Skip to content

Part 7: Plan-Level Specs (Gherkin Features)

TypeScript annotations describe what exists. Gherkin features describe what needs to be built — acceptance criteria, deliverables, and business rules that complement your code annotations.

Quick primer: In Gherkin files, tags before Feature: are metadata (like JSDoc tags). Background: sets up shared context. Rule: blocks define business constraints. Scenario: blocks are individual test cases with Given/When/Then steps.

7.2 Create src/specs/user-registration.feature

Section titled “7.2 Create src/specs/user-registration.feature”

Important: Gherkin features must include the @libar-docs opt-in tag. Without it, the scanner ignores the file entirely — just like TypeScript files.

@libar-docs
@libar-docs-pattern:UserRegistration
@libar-docs-status:roadmap
@libar-docs-core
@libar-docs-phase:1
@libar-docs-release:v0.1.0
@libar-docs-uses:UserService
@libar-docs-implements:UserService
@libar-docs-quarter:Q1-2026
Feature: User Registration
As a new user
I want to register an account
So that I can access the system
Background: Deliverables
Given the following deliverables:
| Deliverable | Status | Location |
| Registration endpoint | Pending | src/sample-sources/user-service.ts |
| Email validation | Pending | src/sample-sources/user-service.ts |
| Duplicate check | Pending | src/sample-sources/user-service.ts |
Rule: Valid registrations create new accounts
**Invariant:** Each email address maps to exactly one user account.
**Rationale:** Prevents account confusion and ensures unique identity.
@happy-path
Scenario: Successful registration with valid email
Given a valid email "alice@example.com"
When the user submits the registration form
Then a new account should be created
And a confirmation email should be sent
@happy-path
Scenario: Registration assigns a unique user ID
Given a valid email "bob@example.com"
When the user submits the registration form
Then the returned user ID should be a valid UUID
And the user should be marked as active
Rule: Invalid input is rejected before account creation
**Invariant:** No user record is created for invalid input.
**Rationale:** Prevents polluting the user store with bad data.
@validation @business-rule
Scenario: Registration fails with empty email
Given an empty email ""
When the user submits the registration form
Then the registration should be rejected
And an error message should indicate the email is invalid
Rule: Duplicate emails are rejected
**Invariant:** Registration with an existing email always fails.
**Rationale:** Enforces unique identity constraint at the application boundary.
@business-rule
Scenario: Registration fails with duplicate email
Given an existing user with email "alice@example.com"
When another user tries to register with "alice@example.com"
Then the registration should be rejected
And an error message should indicate the email is taken

Feature-level tags (before Feature:) use colon syntax — not spaces like TypeScript:

SyntaxContextExample
Space-separatedTypeScript JSDoc@libar-docs-pattern UserService
Colon-separatedGherkin tags@libar-docs-pattern:UserRegistration

Key feature-level tags:

TagPurpose
@libar-docsRequired. Opts the file into scanning.
@libar-docs-pattern:UserRegistrationNames this as a pattern.
@libar-docs-implements:UserServiceLinks this spec to the TypeScript pattern it specifies (dotted arrows in diagrams).
@libar-docs-depends-on:UserRegistrationRoadmap sequencing between specs.

Background: Deliverables — A data table under Background: that tracks deliverables. Each row specifies a deliverable name, its status, and the source file where it will be implemented. These show up in roadmap tracking and pattern detail pages.

Rule: blocks — Gherkin Rule: blocks are extracted as business rules. Add structured annotations inside the rule description:

  • **Invariant:** — The constraint that must hold (extracted verbatim)
  • **Rationale:** — Why the invariant matters
  • Scenarios under the rule are linked as Verified by: entries

Semantic scenario tags — Tags like @happy-path, @validation, @business-rule categorize scenarios for reporting.

Create src/specs/authentication.feature:

@libar-docs
@libar-docs-pattern:Authentication
@libar-docs-status:roadmap
@libar-docs-api
@libar-docs-phase:2
@libar-docs-release:vNEXT
@libar-docs-uses:UserService
@libar-docs-implements:AuthHandler
@libar-docs-depends-on:UserRegistration
@libar-docs-quarter:Q1-2026
Feature: Authentication
As a registered user
I want to log in to my account
So that I can access protected resources
Background: Deliverables
Given the following deliverables:
| Deliverable | Status | Location |
| Login endpoint | Pending | src/sample-sources/auth-handler.ts |
| Session token creation | Pending | src/sample-sources/auth-handler.ts |
Rule: Valid credentials grant access
**Invariant:** A session token is only issued for valid credential pairs.
**Rationale:** Prevents unauthorized access to the system.
@happy-path
Scenario: Successful login with valid credentials
Given a registered user with email "alice@example.com"
When the user submits valid login credentials
Then a session token should be returned
And the session should be marked as active
Rule: Invalid credentials are rejected securely
**Invariant:** Error messages never reveal whether the email or password was wrong.
**Rationale:** Prevents credential enumeration attacks.
@business-rule @validation
Scenario: Login fails with wrong password
Given a registered user with email "alice@example.com"
When the user logs in with an incorrect password
Then authentication should fail
And the error should say "Invalid credentials"

This feature demonstrates cross-pattern traceability:

  • @libar-docs-implements:AuthHandler — links this spec to the TypeScript implementation
  • @libar-docs-depends-on:UserRegistration — sequencing between specs
Terminal window
npm run process:rules
{
"success": true,
"data": {
"productAreas": [
{
"productArea": "Platform",
"ruleCount": 5,
"invariantCount": 5,
"phases": [
{
"phase": "Phase 1",
"features": [
{
"pattern": "UserRegistration",
"source": "src/specs/user-registration.feature",
"rules": [
{
"name": "Valid registrations create new accounts",
"invariant": "Each email address maps to exactly one user account.",
"rationale": "Prevents account confusion and ensures unique identity.",
"verifiedBy": [
"Successful registration with valid email",
"Registration assigns a unique user ID"
],
"scenarioCount": 2
},
{
"name": "Invalid input is rejected before account creation",
"invariant": "No user record is created for invalid input.",
"rationale": "Prevents polluting the user store with bad data.",
"verifiedBy": ["Registration fails with empty email"],
"scenarioCount": 1
},
{
"name": "Duplicate emails are rejected",
"invariant": "Registration with an existing email always fails.",
"rationale": "Enforces unique identity constraint at the application boundary.",
"verifiedBy": ["Registration fails with duplicate email"],
"scenarioCount": 1
}
]
}
]
},
{
"phase": "Phase 2",
"features": [
{
"pattern": "Authentication",
"source": "src/specs/authentication.feature",
"rules": [
{
"name": "Valid credentials grant access",
"invariant": "A session token is only issued for valid credential pairs.",
"rationale": "Prevents unauthorized access to the system.",
"verifiedBy": ["Successful login with valid credentials"],
"scenarioCount": 1
},
{
"name": "Invalid credentials are rejected securely",
"invariant": "Error messages never reveal whether the email or password was wrong.",
"rationale": "Prevents credential enumeration attacks.",
"verifiedBy": ["Login fails with wrong password"],
"scenarioCount": 1
}
]
}
]
}
]
}
],
"totalRules": 5,
"totalInvariants": 5
}
}

What just happened: The system extracted 5 business rules from 2 Gherkin features, each with invariant statements and scenario verification links. Every Rule: block with an **Invariant:** becomes a queryable business rule.

Terminal window
npm run docs:business-rules
Using sources from delivery-process.config.ts...
Scanning source files...
Found 8 patterns
Extracting patterns...
Extracted 8 patterns
Running generator: business-rules
✓ BUSINESS-RULES.md
✓ business-rules/platform.md
✅ Documentation generation complete!
2 files written

BUSINESS-RULES.md shows a summary: “5 rules from 2 features across 1 product area.” The detail page business-rules/platform.md lists every invariant with its rationale and verification scenarios.

Terminal window
npm run process:overview
=== PROGRESS ===
8 patterns (0 completed, 1 active, 7 planned) = 0%
=== ACTIVE PHASES ===
Phase 1: Inception (1 active)
=== BLOCKING ===
UserService blocked by: EventStore
AuthHandler blocked by: UserService
Authentication blocked by: UserRegistration

The Gherkin features added 2 more main patterns (UserRegistration, Authentication). The total is now 8 patterns (not 11 — that count is reached after Part 8 adds the stub file). The blocking analysis now includes spec-level dependencies: Authentication is blocked by UserRegistration.

Terminal window
npm run process:sources
{
"success": true,
"data": {
"types": [
{
"type": "TypeScript (annotated)",
"count": 3,
"locationPattern": "src/sample-sources/**/*.ts",
"files": [
"src/sample-sources/user-service.ts",
"src/sample-sources/event-store.ts",
"src/sample-sources/auth-handler.ts"
]
},
{
"type": "Gherkin (features)",
"count": 2,
"locationPattern": "src/specs/**/*.feature",
"files": [
"src/specs/user-registration.feature",
"src/specs/authentication.feature"
]
}
],
"totalFiles": 5
}
}
  • npm run process:sources shows 3 TypeScript + 2 Gherkin files
  • npm run process:rules returns 5 business rules
  • docs-generated/BUSINESS-RULES.md exists
  • npm run process:overview shows 8 patterns
  • Gherkin features own planning metadata: status, phase, deliverables, business rules
  • TypeScript owns implementation metadata: uses, used-by, shapes, architecture
  • Together they form a complete picture — neither duplicates the other
  • Rule: blocks with **Invariant:**/**Rationale:** become queryable business rules
  • Gherkin tags use colon syntax (@libar-docs-pattern:Name), TypeScript uses spaces