@anselmdk/feature-spec-md 0.1.1-rc.6 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/README.md +89 -61
  2. package/SPEC_FORMAT.md +44 -17
  3. package/dist/cli.js +5 -1
  4. package/dist/cli.js.map +1 -1
  5. package/dist/featureSpecs.d.ts +34 -0
  6. package/dist/featureSpecs.d.ts.map +1 -0
  7. package/dist/featureSpecs.js +240 -0
  8. package/dist/featureSpecs.js.map +1 -0
  9. package/dist/filePatterns.d.ts.map +1 -1
  10. package/dist/filePatterns.js +4 -0
  11. package/dist/filePatterns.js.map +1 -1
  12. package/dist/html.d.ts +3 -0
  13. package/dist/html.d.ts.map +1 -1
  14. package/dist/html.js +3 -0
  15. package/dist/html.js.map +1 -1
  16. package/dist/index.d.ts +3 -34
  17. package/dist/index.d.ts.map +1 -1
  18. package/dist/index.js +7 -338
  19. package/dist/index.js.map +1 -1
  20. package/dist/playwright.d.ts.map +1 -1
  21. package/dist/playwright.js +13 -21
  22. package/dist/playwright.js.map +1 -1
  23. package/dist/reportTemplate.d.ts.map +1 -1
  24. package/dist/reportTemplate.js +4 -0
  25. package/dist/reportTemplate.js.map +1 -1
  26. package/dist/screenshots.d.ts.map +1 -1
  27. package/dist/screenshots.js +4 -0
  28. package/dist/screenshots.js.map +1 -1
  29. package/dist/specDocuments.d.ts.map +1 -1
  30. package/dist/specDocuments.js +16 -112
  31. package/dist/specDocuments.js.map +1 -1
  32. package/dist/specMarkdown.d.ts +26 -0
  33. package/dist/specMarkdown.d.ts.map +1 -0
  34. package/dist/specMarkdown.js +126 -0
  35. package/dist/specMarkdown.js.map +1 -0
  36. package/dist/testImplementationReport.d.ts +4 -0
  37. package/dist/testImplementationReport.d.ts.map +1 -1
  38. package/dist/testImplementationReport.js.map +1 -1
  39. package/dist/types.d.ts +4 -0
  40. package/dist/types.d.ts.map +1 -1
  41. package/docs/releasing.md +107 -0
  42. package/docs/spec-driven-flow.md +175 -0
  43. package/package.json +8 -2
package/README.md CHANGED
@@ -1,83 +1,123 @@
1
1
  # feature-spec-md
2
2
 
3
- Markdown feature specs with stable model, rule, and scenario IDs, validation, coverage checks, and generated reports.
3
+ Markdown specs for AI-assisted, testable spec driven development.
4
4
 
5
- The goal is simple:
5
+ The concept is deliberately small:
6
6
 
7
7
  ```txt
8
- human-readable model + feature specs
9
- exact executable tests
10
- generated coverage report
8
+ model + features + stack + design
9
+ -> AI-written executable tests that reference stable spec IDs
10
+ -> validation, coverage, screenshots, and reports
11
11
  ```
12
12
 
13
- The handwritten specs stay clean. Shared domain language lives in `*.model.md` files. User-facing capabilities live in `*.feature.md` files with rules and scenarios. Test mapping is derived from stable IDs instead of being written manually in the spec.
13
+ The specs are meant to be written with an AI before implementation. The tests are meant to be written with an AI from those specs. `feature-spec-md` checks that the Markdown stays structured and that executable tests still cover the model items, rules, and scenarios the specs define.
14
14
 
15
- ## Format
15
+ ## What You Write
16
16
 
17
- Feature Spec Markdown uses ordinary Markdown files:
17
+ Use four ordinary Markdown document types:
18
18
 
19
19
  ```txt
20
- *.model.md
21
- *.feature.md
20
+ *.model.md shared domain vocabulary
21
+ *.feature.md user-facing behavior, rules, and scenarios
22
+ *.stack.md technical platform choices
23
+ *.design.md product, UI, and interaction direction
22
24
  ```
23
25
 
24
- Model files use:
26
+ Each document has frontmatter, a short `## Purpose`, and stable IDs. Tests reference those IDs in titles, tags, annotations, comments, or metadata.
25
27
 
26
- - frontmatter for metadata
27
- - `## Model` for shared domain vocabulary
28
- - optional `## Rules` for global invariants
28
+ ```md
29
+ ### ACCOUNT-ACCESS-S001: Registered person signs in
29
30
 
30
- Feature files use:
31
+ Given a registered person is on the sign-in page
32
+ When they request and open a valid sign-in link
33
+ Then they are signed in
34
+ ```
35
+
36
+ ```ts
37
+ test("ACCOUNT-ACCESS-S001 registered person signs in", async ({ page }) => {
38
+ // Covers ACCOUNT-ACCESS-R001 and ACCOUNT-M001.
39
+ });
40
+ ```
41
+
42
+ See [SPEC_FORMAT.md](SPEC_FORMAT.md) for the exact document format.
43
+
44
+ ## Install
45
+
46
+ ```bash
47
+ npm install -D @anselmdk/feature-spec-md
48
+ ```
31
49
 
32
- - frontmatter for metadata
33
- - optional `model` or `models` references
34
- - `## Rules` for durable product truths
35
- - `## Scenarios` for Given / When / Then examples
50
+ Create starter specs:
36
51
 
37
- See `SPEC_FORMAT.md` and `examples/account-access.feature.md`.
52
+ ```bash
53
+ npx feature-spec-md init --kind model --dir specs
54
+ npx feature-spec-md init --kind feature --dir specs
55
+ npx feature-spec-md init --kind stack --dir specs
56
+ npx feature-spec-md init --kind design --dir specs
57
+ ```
58
+
59
+ ## Workflow
60
+
61
+ 1. Ask an AI to draft or update `*.model.md`, `*.feature.md`, `*.stack.md`, and `*.design.md` files.
62
+ 2. Run `npx feature-spec-md check` until the spec set is valid.
63
+ 3. Ask an AI to write executable tests from the specs, preserving the relevant `-M001`, `-R001`, and `-S001` IDs in the test source.
64
+ 4. Run `npx feature-spec-md coverage` to see which scenarios, rules, and model items have tests.
65
+ 5. Run `npx feature-spec-md report` to generate an HTML implementation report for review or CI artifacts.
66
+
67
+ The longer flow, including AI prompts and CI setup, is in [docs/spec-driven-flow.md](docs/spec-driven-flow.md).
38
68
 
39
69
  ## CLI
40
70
 
41
71
  ```bash
42
- npm install
43
- npm run dev -- check --specs "examples/**/*.feature.md" --tests "tests/**/*.test.ts" --require-scenario-coverage=false
44
- npm run dev -- coverage --specs "specs/**/*.feature.md" --tests "tests/**/*.spec.ts"
45
- npm run dev -- report --specs "examples/**/*.feature.md" --tests "tests/**/*.test.ts"
46
- npm run dev -- report --specs "examples/**/*.feature.md" --tests "tests/**/*.test.ts" --screenshots "test-results/spec-report/screenshots-*.json"
72
+ npx feature-spec-md check
73
+ npx feature-spec-md coverage --fail-on-missing
74
+ npx feature-spec-md report --out test-results/feature-spec-report/index.html
47
75
  ```
48
76
 
49
- The `coverage` command prints a simple terminal report that groups feature
50
- specs by whether all, some, or none of their scenarios have matching test
51
- references. Use `--fail-on-missing` when missing scenario tests should fail CI.
52
-
53
- Playwright screenshot evidence can be shown in the report by passing one or
54
- more screenshot manifest JSON files. Each screenshot should point at the spec
55
- file and line it proves:
56
-
57
- ```json
58
- {
59
- "screenshots": [
60
- {
61
- "specPath": "specs/account-access.feature.md",
62
- "line": 24,
63
- "path": "screenshots/account-s001-line-24.png",
64
- "title": "ACCOUNT-S001:24 Given a registered person is on the sign-in page"
65
- }
66
- ]
67
- }
77
+ By default the CLI scans:
78
+
79
+ ```txt
80
+ specs/**/*.model.md
81
+ specs/**/*.feature.md
82
+ specs/**/*.stack.md
83
+ specs/**/*.design.md
84
+ tests/**/*.spec.ts
85
+ ```
86
+
87
+ Use explicit patterns when your project uses different paths:
88
+
89
+ ```bash
90
+ npx feature-spec-md check \
91
+ --specs "product/**/*.model.md,product/**/*.feature.md,product/**/*.stack.md,product/**/*.design.md" \
92
+ --tests "e2e/**/*.spec.ts"
93
+ ```
94
+
95
+ The `check` command validates spec structure, references between documents, and test coverage. Scenario coverage is required by default when tests are scanned. Use `--require-scenario-coverage=false` while drafting.
96
+
97
+ The `coverage` command prints a terminal implementation report. Use `--fail-on-missing` when missing scenario tests should fail CI.
98
+
99
+ The `report` command writes an HTML report. It can include screenshot evidence from Playwright or another test runner by passing one or more screenshot manifest files:
100
+
101
+ ```bash
102
+ npx feature-spec-md report \
103
+ --screenshots "test-results/spec-report/screenshots-*.json"
68
104
  ```
69
105
 
70
106
  ## Library API
71
107
 
108
+ Most integrations can use the top-level document API:
109
+
72
110
  ```ts
73
111
  import {
74
- checkFeatureSpecs,
75
- parseFeatureSpec,
112
+ checkSpecDocuments,
113
+ parseSpecDocument,
76
114
  renderHtmlReport,
77
- validateFeatureSpec,
78
- } from "feature-spec-md";
115
+ validateSpecDocument,
116
+ } from "@anselmdk/feature-spec-md";
79
117
  ```
80
118
 
119
+ Feature-only helpers such as `parseFeatureSpec` and `checkFeatureSpecs` remain available for compatibility.
120
+
81
121
  ## Development
82
122
 
83
123
  ```bash
@@ -89,16 +129,4 @@ npm run build
89
129
 
90
130
  ## Releases
91
131
 
92
- Stable releases are published to npm by the `Publish to npm` GitHub Actions workflow when a `v*` tag is pushed, or when the workflow is run manually.
93
-
94
- Release candidates are published manually from GitHub Actions:
95
-
96
- 1. Open the `Publish RC to npm` workflow.
97
- 2. Run the workflow from `main`.
98
- 3. The workflow verifies the package, creates a prerelease version using the current package version and GitHub run number, and publishes it with the npm `rc` dist-tag.
99
-
100
- Install the latest release candidate with:
101
-
102
- ```bash
103
- npm install @anselmdk/feature-spec-md@rc
104
- ```
132
+ Release candidates and stable releases are documented in [docs/releasing.md](docs/releasing.md).
package/SPEC_FORMAT.md CHANGED
@@ -1,12 +1,12 @@
1
- # Feature Spec Markdown format
1
+ # Feature Spec Markdown Format
2
2
 
3
- Feature Spec Markdown is a lightweight convention for readable, testable specifications.
3
+ Feature Spec Markdown is a lightweight format for testable specs.
4
4
 
5
- It uses ordinary Markdown and stable IDs instead of a dedicated executable specification language.
5
+ It uses ordinary Markdown plus stable IDs so an AI can write clear product specs, another AI pass can write executable tests from those specs, and tooling can verify that the tests still cover the documented behavior.
6
6
 
7
- ## File types
7
+ ## Document Set
8
8
 
9
- The format has four document types:
9
+ A spec set can contain four document types:
10
10
 
11
11
  ```txt
12
12
  *.model.md
@@ -15,10 +15,19 @@ The format has four document types:
15
15
  *.design.md
16
16
  ```
17
17
 
18
- - `*.model.md` defines shared domain vocabulary.
18
+ - `*.model.md` defines shared domain vocabulary and global domain rules.
19
19
  - `*.feature.md` defines user-facing behavior with rules and scenarios.
20
- - `*.stack.md` defines technical platform choices.
21
- - `*.design.md` defines product, UI, and interaction design direction.
20
+ - `*.stack.md` defines technical platform choices and their rationale.
21
+ - `*.design.md` defines product, UI, layout, visual, and interaction direction.
22
+
23
+ The short version:
24
+
25
+ ```txt
26
+ model tells tests what things mean
27
+ features tell tests what behavior must work
28
+ stack tells implementation what tools and constraints to use
29
+ design tells implementation what experience to create
30
+ ```
22
31
 
23
32
  ## Frontmatter
24
33
 
@@ -45,13 +54,15 @@ Optional fields:
45
54
  - `model`: a single referenced model id, for feature and design files
46
55
  - `models`: comma-separated referenced model ids, for feature and design files
47
56
 
57
+ IDs use uppercase words separated by hyphens. IDs are the contract between specs, tests, reports, and implementation work.
58
+
48
59
  ## Purpose
49
60
 
50
61
  Every model, feature, stack, and design file MUST include a short `## Purpose` section.
51
62
 
52
63
  Purpose explains the document boundary and intent. It SHOULD be one or two short paragraphs and SHOULD NOT contain rules, scenarios, implementation details, or roadmap notes.
53
64
 
54
- ## Model files
65
+ ## Model Files
55
66
 
56
67
  Model files use the `.model.md` suffix.
57
68
 
@@ -79,9 +90,13 @@ Model items use stable `-M001` IDs:
79
90
  A card represents one work item on the board.
80
91
  ```
81
92
 
82
- Model rules use stable `-R001` IDs and should describe global invariants for the domain vocabulary.
93
+ Model rules use stable `-R001` IDs and describe global invariants for the domain vocabulary:
94
+
95
+ ```md
96
+ - KANBAN-R001: A card MUST have one current workflow state.
97
+ ```
83
98
 
84
- ## Feature files
99
+ ## Feature Files
85
100
 
86
101
  Feature files use the `.feature.md` suffix.
87
102
 
@@ -121,7 +136,9 @@ Allowed step keywords:
121
136
  - `And`
122
137
  - `But`
123
138
 
124
- ## Stack files
139
+ Rules state durable product truths. Scenarios show concrete examples that executable tests can implement.
140
+
141
+ ## Stack Files
125
142
 
126
143
  Stack files use the `.stack.md` suffix.
127
144
 
@@ -171,7 +188,7 @@ Define the initial technical stack for implementing the Kanban board.
171
188
  | Testing | Playwright |
172
189
  ```
173
190
 
174
- ## Design files
191
+ ## Design Files
175
192
 
176
193
  Design files use the `.design.md` suffix.
177
194
 
@@ -220,17 +237,19 @@ Define the visual and interaction design direction for the Kanban board.
220
237
  The board should feel lightweight, immediate, and calm.
221
238
  ```
222
239
 
223
- ## Splitting guidance
240
+ ## Splitting Guidance
224
241
 
225
242
  Split model files by coherent domain vocabulary, ownership, or lifecycle.
226
243
 
227
- Feature files SHOULD be split by user capability.
244
+ Split feature files by user capability.
228
245
 
229
- Use stack files for broad technical choices such as framework, language, testing, persistence, and deployment.
246
+ Use stack files for broad technical choices such as framework, language, testing, persistence, deployment, and runtime constraints.
230
247
 
231
248
  Use design files for product/UI direction such as layout, interaction, visual style, and design principles.
232
249
 
233
- ## Test coverage convention
250
+ Keep each document small enough for an AI to read, revise, and use as test-writing context without extra explanation.
251
+
252
+ ## Test Coverage Convention
234
253
 
235
254
  Spec files do not contain test mappings.
236
255
 
@@ -243,3 +262,11 @@ Generated tooling can answer:
243
262
  - Which rules have executable coverage?
244
263
  - Which tests reference deleted or unknown spec IDs?
245
264
  - Which visible flows have screenshots, traces, or other evidence?
265
+
266
+ Example:
267
+
268
+ ```ts
269
+ test("KANBAN-CARD-AUTHORING-S001 user creates a card", async ({ page }) => {
270
+ // Covers KANBAN-M001 and KANBAN-CARD-AUTHORING-R001.
271
+ });
272
+ ```
package/dist/cli.js CHANGED
@@ -1,4 +1,8 @@
1
1
  #!/usr/bin/env node
2
+ /**
3
+ * Command-line interface for initializing, validating, reporting on, and
4
+ * measuring test coverage for Feature Spec Markdown documents.
5
+ */
2
6
  import { mkdir, readFile, writeFile } from "node:fs/promises";
3
7
  import path from "node:path";
4
8
  import { collectSpecScreenshots, renderHtmlReport, writeTextFile, } from "./index.js";
@@ -149,7 +153,7 @@ function printHelp() {
149
153
  Usage:
150
154
  feature-spec-md init [--kind feature|model|stack|design] [--dir specs]
151
155
  feature-spec-md check [--specs "specs/**/*.model.md,specs/**/*.feature.md,specs/**/*.stack.md,specs/**/*.design.md"] [--tests "tests/**/*.spec.ts"]
152
- feature-spec-md coverage [--specs "specs/**/*.feature.md"] [--tests "tests/**/*.spec.ts"] [--fail-on-missing]
156
+ feature-spec-md coverage [--specs "specs/**/*.model.md,specs/**/*.feature.md,specs/**/*.stack.md,specs/**/*.design.md"] [--tests "tests/**/*.spec.ts"] [--fail-on-missing]
153
157
  feature-spec-md report [--specs "specs/**/*.model.md,specs/**/*.feature.md,specs/**/*.stack.md,specs/**/*.design.md"] [--tests "tests/**/*.spec.ts"] [--screenshots "test-results/spec-report/screenshots.json"] [--out test-results/feature-spec-report/index.html]
154
158
 
155
159
  Options:
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ import type { CoverageSummary, FeatureSpec, TestReference, ValidationIssue } from "./types.js";
2
+ /** Parse one feature spec Markdown document into structured metadata, rules, and scenarios. */
3
+ export declare function parseFeatureSpec(source: string, options?: {
4
+ filePath?: string;
5
+ }): FeatureSpec;
6
+ /** Validate spec structure, ID prefixes, duplicate IDs, and required sections. */
7
+ export declare function validateFeatureSpec(spec: FeatureSpec): ValidationIssue[];
8
+ /** Find rule and scenario IDs referenced by one test source file. */
9
+ export declare function parseTestReferences(source: string, filePath?: string): TestReference[];
10
+ /** Expand test file globs and collect all rule and scenario references from them. */
11
+ export declare function collectTestReferences(patterns: string[]): Promise<TestReference[]>;
12
+ /** Build rule and scenario coverage from parsed specs and collected test references. */
13
+ export declare function buildCoverageSummary(specs: FeatureSpec[], references: TestReference[]): CoverageSummary;
14
+ /** Validate coverage expectations and orphaned references. */
15
+ export declare function validateCoverage(coverage: CoverageSummary, options?: {
16
+ requireRuleCoverage?: boolean;
17
+ requireScenarioCoverage?: boolean;
18
+ }): ValidationIssue[];
19
+ /** Load and parse all feature specs matching the provided globs. */
20
+ export declare function loadFeatureSpecs(patterns: string[]): Promise<FeatureSpec[]>;
21
+ /** Run spec validation and optional test coverage validation in one call. */
22
+ export declare function checkFeatureSpecs(options: {
23
+ specs: string[];
24
+ tests?: string[];
25
+ requireRuleCoverage?: boolean;
26
+ requireScenarioCoverage?: boolean;
27
+ }): Promise<{
28
+ specs: FeatureSpec[];
29
+ validationIssues: ValidationIssue[];
30
+ coverage: CoverageSummary | undefined;
31
+ coverageIssues: ValidationIssue[];
32
+ ok: boolean;
33
+ }>;
34
+ //# sourceMappingURL=featureSpecs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"featureSpecs.d.ts","sourceRoot":"","sources":["../src/featureSpecs.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAEV,eAAe,EAGf,WAAW,EAEX,aAAa,EACb,eAAe,EAChB,MAAM,YAAY,CAAC;AAEpB,+FAA+F;AAC/F,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,MAAM,EACd,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,GAClC,WAAW,CAab;AAED,kFAAkF;AAClF,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,WAAW,GAAG,eAAe,EAAE,CA2GxE;AAED,qEAAqE;AACrE,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,MAAM,EACd,QAAQ,SAAa,GACpB,aAAa,EAAE,CAyBjB;AAED,qFAAqF;AACrF,wBAAsB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,EAAE,4BAM7D;AAED,wFAAwF;AACxF,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,WAAW,EAAE,EACpB,UAAU,EAAE,aAAa,EAAE,GAC1B,eAAe,CAkCjB;AAED,8DAA8D;AAC9D,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,eAAe,EACzB,OAAO,GAAE;IACP,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,uBAAuB,CAAC,EAAE,OAAO,CAAC;CAC9B,qBAmDP;AAED,oEAAoE;AACpE,wBAAsB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,0BAQxD;AAED,6EAA6E;AAC7E,wBAAsB,iBAAiB,CAAC,OAAO,EAAE;IAC/C,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,uBAAuB,CAAC,EAAE,OAAO,CAAC;CACnC;;;;;;GAsBA"}
@@ -0,0 +1,240 @@
1
+ /**
2
+ * Feature-spec parsing, validation, test-reference extraction, and coverage
3
+ * checks for `*.feature.md` documents.
4
+ */
5
+ import { readFile } from "node:fs/promises";
6
+ import { expandFilePatterns } from "./filePatterns.js";
7
+ import { dedupeTestReferences, lineForOffset, parseMarkdownDocument, parseRuleItems, ruleIdPattern, scenarioIdPattern, } from "./specMarkdown.js";
8
+ /** Parse one feature spec Markdown document into structured metadata, rules, and scenarios. */
9
+ export function parseFeatureSpec(source, options = {}) {
10
+ const filePath = options.filePath ?? "<inline>";
11
+ const parsed = parseMarkdownDocument(source, "Feature spec");
12
+ return {
13
+ filePath,
14
+ frontmatter: parsed.frontmatter,
15
+ title: parsed.title,
16
+ purpose: parsed.purpose,
17
+ rules: parseRuleItems(parsed.lines, parsed.bodyStartLine),
18
+ scenarios: parseScenarios(parsed.lines, parsed.bodyStartLine),
19
+ source,
20
+ };
21
+ }
22
+ /** Validate spec structure, ID prefixes, duplicate IDs, and required sections. */
23
+ export function validateFeatureSpec(spec) {
24
+ const issues = [];
25
+ const seen = new Map();
26
+ if (!/^[A-Z][A-Z0-9]*(?:-[A-Z0-9]+)*$/.test(spec.frontmatter.id)) {
27
+ issues.push(issue(spec, "invalid-feature-id", "error", `Feature id "${spec.frontmatter.id}" must use uppercase words separated by hyphens.`));
28
+ }
29
+ if (spec.title !== spec.frontmatter.title) {
30
+ issues.push(issue(spec, "title-mismatch", "warning", "The H1 title should match frontmatter.title."));
31
+ }
32
+ if (!spec.purpose) {
33
+ issues.push(issue(spec, "missing-purpose", "error", "Feature spec must include a non-empty ## Purpose section."));
34
+ }
35
+ if (spec.scenarios.length === 0) {
36
+ issues.push(issue(spec, "missing-scenarios", "error", "Feature spec must include at least one scenario."));
37
+ }
38
+ for (const rule of spec.rules) {
39
+ if (!rule.id.startsWith(`${spec.frontmatter.id}-`)) {
40
+ issues.push(issue(spec, "wrong-rule-prefix", "error", `Rule id "${rule.id}" must start with ${spec.frontmatter.id}-.`, rule.line));
41
+ }
42
+ if (!rule.keyword) {
43
+ issues.push(issue(spec, "missing-rule-keyword", "warning", `Rule "${rule.id}" should use MUST, MUST NOT, SHOULD, SHOULD NOT, MAY, or OPTIONAL.`, rule.line));
44
+ }
45
+ registerId(spec, issues, seen, rule.id, rule.line);
46
+ }
47
+ for (const scenario of spec.scenarios) {
48
+ if (!scenario.id.startsWith(`${spec.frontmatter.id}-`)) {
49
+ issues.push(issue(spec, "wrong-scenario-prefix", "error", `Scenario id "${scenario.id}" must start with ${spec.frontmatter.id}-.`, scenario.line));
50
+ }
51
+ for (const keyword of ["Given", "When", "Then"]) {
52
+ if (!scenario.steps.some((step) => step.keyword === keyword)) {
53
+ issues.push(issue(spec, `missing-${keyword.toLowerCase()}`, "warning", `Scenario "${scenario.id}" should include a ${keyword} step.`, scenario.line));
54
+ }
55
+ }
56
+ registerId(spec, issues, seen, scenario.id, scenario.line);
57
+ }
58
+ return issues;
59
+ }
60
+ /** Find rule and scenario IDs referenced by one test source file. */
61
+ export function parseTestReferences(source, filePath = "<inline>") {
62
+ const lines = source.split(/\r?\n/);
63
+ const refs = [];
64
+ for (const match of source.matchAll(scenarioIdPattern)) {
65
+ refs.push({
66
+ id: match[0],
67
+ filePath,
68
+ line: lineForOffset(lines, match.index ?? 0),
69
+ kind: "scenario",
70
+ source: sourceForMatch(source, match.index ?? 0),
71
+ });
72
+ }
73
+ for (const match of source.matchAll(ruleIdPattern)) {
74
+ refs.push({
75
+ id: match[0],
76
+ filePath,
77
+ line: lineForOffset(lines, match.index ?? 0),
78
+ kind: "rule",
79
+ source: sourceForMatch(source, match.index ?? 0),
80
+ });
81
+ }
82
+ return dedupeTestReferences(refs);
83
+ }
84
+ /** Expand test file globs and collect all rule and scenario references from them. */
85
+ export async function collectTestReferences(patterns) {
86
+ const refs = [];
87
+ for (const file of await expandFilePatterns(patterns)) {
88
+ refs.push(...parseTestReferences(await readFile(file, "utf8"), file));
89
+ }
90
+ return refs;
91
+ }
92
+ /** Build rule and scenario coverage from parsed specs and collected test references. */
93
+ export function buildCoverageSummary(specs, references) {
94
+ const scenarios = new Map(specs.flatMap((spec) => spec.scenarios.map((scenario) => [scenario.id, { spec, scenario }])));
95
+ const rules = new Map(specs.flatMap((spec) => spec.rules.map((rule) => [rule.id, { spec, rule }])));
96
+ const scenarioRefs = references.filter((ref) => ref.kind === "scenario");
97
+ const ruleRefs = references.filter((ref) => ref.kind === "rule");
98
+ return {
99
+ scenarioCoverage: Array.from(scenarios.entries()).map(([id, value]) => item(id, value.scenario.title, value.spec.filePath, value.scenario.line, scenarioRefs)),
100
+ ruleCoverage: Array.from(rules.entries()).map(([id, value]) => item(id, value.rule.text, value.spec.filePath, value.rule.line, ruleRefs)),
101
+ orphanScenarioReferences: scenarioRefs.filter((ref) => !scenarios.has(ref.id)),
102
+ orphanRuleReferences: ruleRefs.filter((ref) => !rules.has(ref.id)),
103
+ };
104
+ }
105
+ /** Validate coverage expectations and orphaned references. */
106
+ export function validateCoverage(coverage, options = {}) {
107
+ const issues = [];
108
+ if (options.requireScenarioCoverage ?? true) {
109
+ for (const item of coverage.scenarioCoverage.filter((item) => !item.covered)) {
110
+ issues.push({
111
+ code: "missing-scenario-coverage",
112
+ severity: "error",
113
+ message: `Scenario "${item.id}" has no matching test reference.`,
114
+ filePath: item.filePath,
115
+ line: item.line,
116
+ });
117
+ }
118
+ }
119
+ if (options.requireRuleCoverage ?? false) {
120
+ for (const item of coverage.ruleCoverage.filter((item) => !item.covered)) {
121
+ issues.push({
122
+ code: "missing-rule-coverage",
123
+ severity: "error",
124
+ message: `Rule "${item.id}" has no matching test reference.`,
125
+ filePath: item.filePath,
126
+ line: item.line,
127
+ });
128
+ }
129
+ }
130
+ for (const ref of coverage.orphanScenarioReferences) {
131
+ issues.push({
132
+ code: "orphan-scenario-reference",
133
+ severity: "error",
134
+ message: `Test references unknown scenario "${ref.id}".`,
135
+ filePath: ref.filePath,
136
+ line: ref.line,
137
+ });
138
+ }
139
+ for (const ref of coverage.orphanRuleReferences) {
140
+ issues.push({
141
+ code: "orphan-rule-reference",
142
+ severity: "error",
143
+ message: `Test references unknown rule "${ref.id}".`,
144
+ filePath: ref.filePath,
145
+ line: ref.line,
146
+ });
147
+ }
148
+ return issues;
149
+ }
150
+ /** Load and parse all feature specs matching the provided globs. */
151
+ export async function loadFeatureSpecs(patterns) {
152
+ const specs = [];
153
+ for (const file of await expandFilePatterns(patterns)) {
154
+ specs.push(parseFeatureSpec(await readFile(file, "utf8"), { filePath: file }));
155
+ }
156
+ return specs;
157
+ }
158
+ /** Run spec validation and optional test coverage validation in one call. */
159
+ export async function checkFeatureSpecs(options) {
160
+ const specs = await loadFeatureSpecs(options.specs);
161
+ const validationIssues = specs.flatMap(validateFeatureSpec);
162
+ const coverage = options.tests?.length
163
+ ? buildCoverageSummary(specs, await collectTestReferences(options.tests))
164
+ : undefined;
165
+ const coverageIssues = coverage
166
+ ? validateCoverage(coverage, {
167
+ requireRuleCoverage: options.requireRuleCoverage,
168
+ requireScenarioCoverage: options.requireScenarioCoverage,
169
+ })
170
+ : [];
171
+ return {
172
+ specs,
173
+ validationIssues,
174
+ coverage,
175
+ coverageIssues,
176
+ ok: ![...validationIssues, ...coverageIssues].some((i) => i.severity === "error"),
177
+ };
178
+ }
179
+ function parseScenarios(lines, bodyStartLine) {
180
+ const scenarios = [];
181
+ for (let i = 0; i < lines.length; i += 1) {
182
+ const match = lines[i].match(/^###\s+([A-Z][A-Z0-9]*(?:-[A-Z0-9]+)*-S\d{3}):\s+(.+)$/);
183
+ if (!match)
184
+ continue;
185
+ const steps = [];
186
+ for (let j = i + 1; j < lines.length; j += 1) {
187
+ if (/^##?#\s+/.test(lines[j]))
188
+ break;
189
+ const step = lines[j].trim().match(/^(Given|When|Then|And|But)\s+(.+)$/);
190
+ if (step)
191
+ steps.push({
192
+ keyword: step[1],
193
+ text: step[2].trim(),
194
+ line: bodyStartLine + j,
195
+ });
196
+ }
197
+ scenarios.push({
198
+ id: match[1],
199
+ title: match[2].trim(),
200
+ line: bodyStartLine + i,
201
+ steps,
202
+ });
203
+ }
204
+ return scenarios;
205
+ }
206
+ function issue(spec, code, severity, message, line) {
207
+ return { code, severity, message, filePath: spec.filePath, line };
208
+ }
209
+ function registerId(spec, issues, seen, id, line) {
210
+ const previous = seen.get(id);
211
+ if (previous)
212
+ issues.push(issue(spec, "duplicate-id", "error", `Duplicate id "${id}". First occurrence is on line ${previous}.`, line));
213
+ seen.set(id, line);
214
+ }
215
+ function item(id, title, filePath, line, refs) {
216
+ const references = refs.filter((ref) => ref.id === id);
217
+ return {
218
+ id,
219
+ title,
220
+ filePath,
221
+ line,
222
+ references,
223
+ covered: references.length > 0,
224
+ };
225
+ }
226
+ function sourceForMatch(source, offset) {
227
+ const before = source.slice(Math.max(0, offset - 80), offset);
228
+ const context = source.slice(Math.max(0, offset - 80), offset + 80);
229
+ if (/covers\s*:\s*\[[^\]]*$/.test(before))
230
+ return "covers";
231
+ if (/tag\s*:\s*\[[^\]]*$/.test(before) ||
232
+ /@/.test(source.slice(Math.max(0, offset - 2), offset + 2)))
233
+ return "tag";
234
+ if (/annotation/.test(context))
235
+ return "annotation";
236
+ if (/test|scenario/.test(before.slice(-40)))
237
+ return "title";
238
+ return "free-text";
239
+ }
240
+ //# sourceMappingURL=featureSpecs.js.map