eslint-plugin-traceability 1.8.0 → 1.8.2

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 (110) hide show
  1. package/CHANGELOG.md +5 -5
  2. package/README.md +28 -29
  3. package/SECURITY.md +135 -0
  4. package/lib/src/index.d.ts +6 -35
  5. package/lib/src/index.js +8 -5
  6. package/lib/src/maintenance/cli.js +12 -16
  7. package/lib/src/maintenance/detect.js +28 -1
  8. package/lib/src/rules/helpers/require-story-io.d.ts +2 -2
  9. package/lib/src/rules/helpers/require-story-io.js +13 -13
  10. package/lib/src/rules/helpers/valid-annotation-format-internal.d.ts +2 -2
  11. package/lib/src/rules/helpers/valid-annotation-format-internal.js +3 -3
  12. package/lib/src/rules/helpers/valid-annotation-utils.d.ts +5 -0
  13. package/lib/src/rules/helpers/valid-annotation-utils.js +43 -5
  14. package/lib/src/rules/helpers/valid-implements-utils.d.ts +11 -11
  15. package/lib/src/rules/helpers/valid-implements-utils.js +11 -11
  16. package/lib/src/rules/helpers/valid-story-reference-helpers.js +19 -0
  17. package/lib/src/rules/prefer-implements-annotation.d.ts +7 -7
  18. package/lib/src/rules/prefer-implements-annotation.js +21 -21
  19. package/lib/src/rules/valid-annotation-format.js +50 -24
  20. package/lib/src/rules/valid-req-reference.js +9 -9
  21. package/lib/src/utils/annotation-checker.js +3 -1
  22. package/lib/src/utils/reqAnnotationDetection.d.ts +2 -2
  23. package/lib/src/utils/reqAnnotationDetection.js +28 -28
  24. package/lib/tests/config/flat-config-presets-integration.test.d.ts +1 -0
  25. package/lib/tests/config/flat-config-presets-integration.test.js +75 -0
  26. package/lib/tests/maintenance/batch.test.js +11 -11
  27. package/lib/tests/maintenance/cli.test.js +34 -27
  28. package/lib/tests/maintenance/report.test.js +7 -7
  29. package/lib/tests/plugin-default-export-and-configs.test.js +0 -2
  30. package/lib/tests/rules/prefer-implements-annotation.test.js +48 -15
  31. package/lib/tests/rules/require-branch-annotation.test.js +15 -36
  32. package/lib/tests/rules/require-req-annotation.test.js +31 -104
  33. package/lib/tests/rules/require-story-annotation.test.js +3 -3
  34. package/lib/tests/rules/require-story-io-behavior.test.js +2 -7
  35. package/lib/tests/rules/require-story-io.edgecases.test.js +2 -7
  36. package/lib/tests/rules/require-story-visitors-edgecases.test.js +8 -8
  37. package/lib/tests/rules/valid-annotation-format.test.js +23 -23
  38. package/lib/tests/rules/valid-req-reference.test.js +9 -9
  39. package/lib/tests/rules/valid-story-reference.test.js +4 -43
  40. package/lib/tests/utils/annotation-checker.test.js +2 -6
  41. package/lib/tests/utils/fsTestHelpers.d.ts +7 -0
  42. package/lib/tests/utils/fsTestHelpers.js +26 -0
  43. package/lib/tests/utils/ioTestHelpers.d.ts +7 -0
  44. package/lib/tests/utils/ioTestHelpers.js +24 -0
  45. package/lib/tests/utils/temp-dir-helpers.d.ts +14 -0
  46. package/lib/tests/utils/temp-dir-helpers.js +61 -0
  47. package/package.json +8 -7
  48. package/user-docs/api-reference.md +37 -20
  49. package/user-docs/eslint-9-setup-guide.md +89 -6
  50. package/user-docs/migration-guide.md +37 -21
  51. package/docs/ci-cd-pipeline.md +0 -224
  52. package/docs/cli-integration.md +0 -22
  53. package/docs/code-quality-refactor-opportunities-2025-12-03.md +0 -78
  54. package/docs/config-presets.md +0 -38
  55. package/docs/conventional-commits-guide.md +0 -185
  56. package/docs/custom-rules-development-guide.md +0 -659
  57. package/docs/decisions/0001-allow-dynamic-require-for-built-plugins.md +0 -26
  58. package/docs/decisions/001-typescript-for-eslint-plugin.accepted.md +0 -111
  59. package/docs/decisions/002-jest-for-eslint-testing.accepted.md +0 -137
  60. package/docs/decisions/003-code-quality-ratcheting-plan.md +0 -48
  61. package/docs/decisions/004-automated-version-bumping-for-ci-cd.md +0 -196
  62. package/docs/decisions/005-github-actions-validation-tooling.accepted.md +0 -144
  63. package/docs/decisions/006-semantic-release-for-automated-publishing.accepted.md +0 -227
  64. package/docs/decisions/007-github-releases-over-changelog.accepted.md +0 -216
  65. package/docs/decisions/008-ci-audit-flags.accepted.md +0 -60
  66. package/docs/decisions/009-security-focused-lint-rules.accepted.md +0 -64
  67. package/docs/decisions/010-implements-annotation-for-multi-story-requirements.proposed.md +0 -184
  68. package/docs/decisions/adr-0001-console-usage-for-cli-guards.md +0 -190
  69. package/docs/decisions/adr-accept-dev-dep-risk-glob.md +0 -40
  70. package/docs/decisions/adr-commit-branch-tests.md +0 -54
  71. package/docs/decisions/adr-maintenance-cli-interface.md +0 -140
  72. package/docs/decisions/adr-pre-push-parity.md +0 -112
  73. package/docs/decisions/code-quality-ratcheting-plan.md +0 -53
  74. package/docs/dependency-health.md +0 -238
  75. package/docs/eslint-9-setup-guide.md +0 -517
  76. package/docs/eslint-plugin-development-guide.md +0 -487
  77. package/docs/functionality-coverage-2025-12-03.md +0 -250
  78. package/docs/jest-testing-guide.md +0 -100
  79. package/docs/rules/prefer-implements-annotation.md +0 -219
  80. package/docs/rules/require-branch-annotation.md +0 -71
  81. package/docs/rules/require-req-annotation.md +0 -203
  82. package/docs/rules/require-story-annotation.md +0 -159
  83. package/docs/rules/valid-annotation-format.md +0 -418
  84. package/docs/rules/valid-req-reference.md +0 -153
  85. package/docs/rules/valid-story-reference.md +0 -120
  86. package/docs/security-incidents/2025-11-17-glob-cli-incident.md +0 -45
  87. package/docs/security-incidents/2025-11-18-brace-expansion-redos.md +0 -45
  88. package/docs/security-incidents/2025-11-18-bundled-dev-deps-accepted-risk.md +0 -93
  89. package/docs/security-incidents/2025-11-18-tar-race-condition.md +0 -43
  90. package/docs/security-incidents/2025-12-03-dependency-health-review.md +0 -58
  91. package/docs/security-incidents/SECURITY-INCIDENT-2025-11-18-semantic-release-bundled-npm.known-error.md +0 -104
  92. package/docs/security-incidents/SECURITY-INCIDENT-TEMPLATE.md +0 -37
  93. package/docs/security-incidents/dependency-override-rationale.md +0 -57
  94. package/docs/security-incidents/dev-deps-high.json +0 -116
  95. package/docs/security-incidents/handling-procedure.md +0 -54
  96. package/docs/stories/001.0-DEV-PLUGIN-SETUP.story.md +0 -92
  97. package/docs/stories/002.0-DEV-ESLINT-CONFIG.story.md +0 -82
  98. package/docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md +0 -112
  99. package/docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md +0 -153
  100. package/docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md +0 -138
  101. package/docs/stories/006.0-DEV-FILE-VALIDATION.story.md +0 -144
  102. package/docs/stories/007.0-DEV-ERROR-REPORTING.story.md +0 -163
  103. package/docs/stories/008.0-DEV-AUTO-FIX.story.md +0 -150
  104. package/docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md +0 -117
  105. package/docs/stories/010.0-DEV-DEEP-VALIDATION.story.md +0 -124
  106. package/docs/stories/010.1-DEV-CONFIGURABLE-PATTERNS.story.md +0 -149
  107. package/docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md +0 -216
  108. package/docs/stories/010.3-DEV-MIGRATE-TO-IMPLEMENTS.story.md +0 -236
  109. package/docs/stories/developer-story.map.md +0 -120
  110. package/docs/ts-jest-presets-guide.md +0 -548
@@ -1,418 +0,0 @@
1
- # valid-annotation-format
2
-
3
- Validates that `@story`, `@req`, and `@implements` annotations follow the correct format and syntax rules to ensure traceability annotations are parseable and standardized.
4
-
5
- @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
6
- @story docs/stories/010.1-DEV-CONFIGURABLE-PATTERNS.story.md
7
- @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
8
- @req REQ-FORMAT-SPECIFICATION - Define clear format rules for @story and @req annotations
9
- @req REQ-SYNTAX-VALIDATION - Validate annotation syntax matches specification
10
- @req REQ-PATH-FORMAT - Validate @story paths follow expected patterns
11
- @req REQ-REQ-FORMAT - Validate @req identifiers follow expected patterns
12
-
13
- ## Rule Details
14
-
15
- This rule scans all comments in the source code and validates any lines that contain `@story`, `@req`, or `@implements` annotations. It is designed to be flexible in how it discovers annotations while still enforcing a strict, machine-parseable format.
16
-
17
- Key behaviors:
18
-
19
- - **Flexible parsing**
20
- - Works in line (`// ...`), block (`/* ... */`), and JSDoc (`/** ... */`) comments.
21
- - Annotations can appear anywhere in the comment text, not only at the beginning of the line.
22
- - Multiple annotations can appear in the same comment block or on the same line.
23
-
24
- - **Multiline annotation support**
25
- - Annotation values may be split across multiple lines within the same block or JSDoc comment.
26
- - The rule concatenates all lines that belong to the same annotation, removing all internal whitespace characters (spaces, tabs, and newlines) before validating the final value.
27
- - This allows patterns such as:
28
- ```js
29
- /**
30
- * @story docs/stories/005.0-
31
- * DEV-ANNOTATION-VALIDATION.story.md
32
- */
33
- ```
34
- which will be normalized and validated as
35
- `@story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md`.
36
-
37
- - **`@implements` format support**
38
- - The rule validates `@implements` annotations that associate code with one or more stories and requirements, such as:
39
- ```js
40
- /**
41
- * @implements docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md REQ-FOO REQ-BAR
42
- */
43
- ```
44
- - The story path that appears first in an `@implements` annotation is validated using the same story pattern as `@story`.
45
- - All requirement IDs that follow in the same `@implements` annotation are validated using the same requirement pattern as `@req`.
46
- - This ensures that multi-story / multi-requirement traceability annotations share the same standardized, machine-parseable formats as standalone `@story` and `@req` annotations.
47
-
48
- - **Validated patterns (configurable)**
49
- - The rule validates:
50
- - **Story identifiers**: the value that follows `@story` (and the story path segment of `@implements`)
51
- - **Requirement identifiers**: the value that follows `@req` (and each requirement ID segment of `@implements`)
52
- - Both patterns are configurable via rule options so that teams can align validation with their own conventions while still keeping annotations machine-parseable.
53
-
54
- ## Options
55
-
56
- The rule supports an optional configuration object. The **primary** configuration shape uses nested `story` and `req` objects, which allow you to configure both the validation pattern and the example used in error messages:
57
-
58
- ```jsonc
59
- "valid-annotation-format": [
60
- "error",
61
- {
62
- "story": {
63
- "pattern": "^docs/stories/[0-9]+\\.[0-9]+-DEV-[\\w-]+\\.story\\.md$",
64
- "example": "docs/stories/005.0-DEV-EXAMPLE.story.md"
65
- },
66
- "req": {
67
- "pattern": "^REQ-[A-Z0-9-]+$",
68
- "example": "REQ-EXAMPLE"
69
- }
70
- }
71
- ]
72
- ```
73
-
74
- All configuration fields are optional; when omitted, the rule falls back to the built-in defaults described below.
75
-
76
- The canonical, nested configuration fields are:
77
-
78
- - `story.pattern` / `story.example`
79
- - `req.pattern` / `req.example`
80
-
81
- For convenience, a **flat shorthand** form is also supported using:
82
-
83
- - `storyPathPattern`
84
- - `storyPathExample`
85
- - `requirementIdPattern`
86
- - `requirementIdExample`
87
-
88
- These flat fields map directly onto the canonical nested fields. When both nested (`story` / `req`) and flat shorthand fields are provided for the same value, the **nested configuration takes precedence**.
89
-
90
- ### Nested configuration
91
-
92
- Nested configuration is the recommended form and mirrors how the rule internally organizes its helpers.
93
-
94
- #### `story`
95
-
96
- ```jsonc
97
- {
98
- "story": {
99
- "pattern": "^docs/stories/[0-9]+\\.[0-9]+-DEV-[\\w-]+\\.story\\.md$",
100
- "example": "docs/stories/005.0-DEV-EXAMPLE.story.md",
101
- },
102
- }
103
- ```
104
-
105
- - `story.pattern`
106
- - **Type:** `string` (a JavaScript regular expression source, without surrounding slashes)
107
- - **Purpose:** Defines the allowed format for `@story` values.
108
- - **Default:** (from `getDefaultStoryPattern()`)
109
- ```txt
110
- ^docs/stories/[0-9]+\.[0-9]+-DEV-[\w-]+\.story\.md$
111
- ```
112
- - **Default expectation:**
113
- - Path starts with `docs/stories/`
114
- - Followed by `<major>.<minor>` numeric version segments (e.g. `005.0`)
115
- - Followed by `-DEV-`
116
- - Followed by an uppercase, word-character-and-dash name segment
117
- - Ends with `.story.md`
118
- - You can change this to match your own story path / traceability file layout, for example:
119
- ```jsonc
120
- {
121
- "story": {
122
- "pattern": "^trace/stories/STORY-[0-9]+\\.md$",
123
- "example": "trace/stories/STORY-123.md",
124
- },
125
- }
126
- ```
127
-
128
- - `story.example`
129
- - **Type:** `string`
130
- - **Purpose:** Provides a human-readable example that appears in error messages when a `@story` value does not match `story.pattern`.
131
- - **Default:** (from `getDefaultStoryExample()`)
132
- `docs/stories/005.0-DEV-EXAMPLE.story.md`
133
- - **Behavior:**
134
- - Not used for validation; only for constructing clearer diagnostics.
135
- - Should be a single example that matches `story.pattern`.
136
-
137
- #### `req`
138
-
139
- ```jsonc
140
- {
141
- "req": {
142
- "pattern": "^REQ-[A-Z0-9-]+$",
143
- "example": "REQ-EXAMPLE",
144
- },
145
- }
146
- ```
147
-
148
- - `req.pattern`
149
- - **Type:** `string` (a JavaScript regular expression source, without surrounding slashes)
150
- - **Purpose:** Defines the allowed format for `@req` values.
151
- - **Default:** (from `getDefaultReqPattern()`)
152
- ```txt
153
- ^REQ-[A-Z0-9-]+$
154
- ```
155
- - **Default expectation:**
156
- - Identifier starts with `REQ-`
157
- - Contains only uppercase letters, digits, and dashes.
158
- - You can adapt this to your requirement ID scheme, e.g.:
159
- ```jsonc
160
- {
161
- "req": {
162
- "pattern": "^SYS-[0-9]{4}$",
163
- "example": "SYS-0001",
164
- },
165
- }
166
- ```
167
-
168
- - `req.example`
169
- - **Type:** `string`
170
- - **Purpose:** Provides a human-readable example that appears in error messages when a `@req` value does not match `req.pattern`.
171
- - **Default:** (from `getDefaultReqExample()`)
172
- `REQ-EXAMPLE`
173
- - **Behavior:**
174
- - Not used for validation; only for diagnostics.
175
- - Should be a single example that matches `req.pattern`.
176
-
177
- ### Flat shorthand configuration
178
-
179
- Instead of nested `story` / `req` objects, you can configure the same options directly on the rule options object:
180
-
181
- ```jsonc
182
- "valid-annotation-format": [
183
- "error",
184
- {
185
- "storyPathPattern": "^trace/stories/STORY-[0-9]+\\.md$",
186
- "storyPathExample": "trace/stories/STORY-123.md",
187
- "requirementIdPattern": "^SYS-[0-9]{4}$",
188
- "requirementIdExample": "SYS-0001"
189
- }
190
- ]
191
- ```
192
-
193
- Flat fields map directly to the canonical nested ones:
194
-
195
- - `storyPathPattern` → `story.pattern`
196
- - `storyPathExample` → `story.example`
197
- - `requirementIdPattern` → `req.pattern`
198
- - `requirementIdExample` → `req.example`
199
-
200
- If you specify both nested and flat values for the same option, the **nested `story` / `req` values take precedence** over the flat shorthand.
201
-
202
- ### Invalid configuration
203
-
204
- If any of the pattern fields contain an invalid regular expression source—whether provided via nested `story.pattern` / `req.pattern` or via flat shorthand `storyPathPattern` / `requirementIdPattern`—the rule:
205
-
206
- - Reports an ESLint configuration diagnostic with `messageId: "invalidRuleConfiguration"`.
207
- - Falls back to the **built-in default** story and/or requirement patterns (`getDefaultStoryPattern()` / `getDefaultReqPattern()`) for actual annotation validation, so that rule execution still proceeds with known-good patterns.
208
-
209
- This behavior ensures configuration errors are visible without breaking validation of annotations in your codebase.
210
-
211
- ## Error messages
212
-
213
- The rule reports targeted, specific messages depending on what failed, using the configured patterns and examples where appropriate. Messages are produced via `meta.messages.invalidStoryFormat` / `invalidReqFormat` and the helpers `buildStoryErrorMessage` / `buildReqErrorMessage`.
214
-
215
- Examples of the actual runtime messages:
216
-
217
- - **Missing story value**
218
- - When `@story` is present but no path value is provided:
219
- - `Invalid annotation format: Missing story path for @story annotation. Expected a path like "docs/stories/005.0-DEV-EXAMPLE.story.md".`
220
-
221
- - **Invalid story identifier format**
222
- - When a `@story` value is present but does not match `story.pattern`:
223
- - `Invalid annotation format: Invalid story path "foo/bar.story.md" for @story annotation. Expected a path like "docs/stories/005.0-DEV-EXAMPLE.story.md".`
224
- - If you configure `story.pattern` / `story.example` (or the corresponding flat shorthand fields), the example in the message will change accordingly.
225
-
226
- - **Missing requirement value**
227
- - When `@req` is present but no identifier value is provided:
228
- - `Invalid annotation format: Missing requirement ID for @req annotation. Expected an identifier like "REQ-EXAMPLE".`
229
-
230
- - **Invalid requirement identifier format**
231
- - When a `@req` value is present but does not match `req.pattern`:
232
- - `Invalid annotation format: Invalid requirement ID "Req-foo" for @req annotation. Expected an identifier like "REQ-EXAMPLE" (uppercase letters, numbers, and dashes only).`
233
- - If you configure `req.pattern` / `req.example` (or the corresponding flat shorthand fields), the example in the message will change accordingly.
234
-
235
- Violations always include:
236
-
237
- - The exact line (and, where possible, column) of the offending annotation.
238
- - Whether the problem is with a `@story` value or a `@req` value.
239
- - A short description of what part of the expected format was not met, to make fixes straightforward.
240
-
241
- ## Examples
242
-
243
- #### Correct (default configuration)
244
-
245
- ```js
246
- /**
247
- * @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
248
- * @req REQ-FORMAT-SPECIFICATION
249
- */
250
- function example() {}
251
-
252
- // @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
253
- // @req REQ-SYNTAX-VALIDATION
254
- if (condition) {
255
- /* ... */
256
- }
257
- ```
258
-
259
- #### Correct (custom configuration, nested)
260
-
261
- ```js
262
- /* eslint valid-annotation-format: [
263
- "error",
264
- {
265
- story: {
266
- pattern: "^trace/stories/STORY-[0-9]+\\.md$",
267
- example: "trace/stories/STORY-123.md"
268
- },
269
- req: {
270
- pattern: "^SYS-[0-9]{4}$",
271
- example: "SYS-0001"
272
- }
273
- }
274
- ] */
275
-
276
- /**
277
- * @story trace/stories/STORY-123.md
278
- * @req SYS-0001
279
- */
280
- function configured() {}
281
- ```
282
-
283
- #### Correct (custom configuration, flat shorthand)
284
-
285
- ```js
286
- /* eslint valid-annotation-format: [
287
- "error",
288
- {
289
- storyPathPattern: "^trace/stories/STORY-[0-9]+\\.md$",
290
- storyPathExample: "trace/stories/STORY-123.md",
291
- requirementIdPattern: "^SYS-[0-9]{4}$",
292
- requirementIdExample: "SYS-0001"
293
- }
294
- ] */
295
-
296
- /**
297
- * @story trace/stories/STORY-123.md
298
- * @req SYS-0001
299
- */
300
- function flatConfigured() {}
301
- ```
302
-
303
- #### Incorrect
304
-
305
- ```js
306
- // @story invalid-path
307
- // @req REQ-1234
308
-
309
- // @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story
310
- // @req invalid-req-id
311
-
312
- /**
313
- * @story
314
- * @req REQ-EXAMPLE
315
- */
316
- function badExample() {}
317
- ```
318
-
319
- ## Migration to `@implements`
320
-
321
- The `@implements` annotation is designed to make multi-story integration functions easier to annotate and validate without breaking existing projects. You do **not** need to migrate existing single-story code that already uses `@story` and `@req` correctly, but you can opt in to `@implements` where it adds clarity.
322
-
323
- ### When you can stay with `@story` + `@req`
324
-
325
- Keep using only `@story` + `@req` when:
326
-
327
- - A function or class is tied to a **single** story file.
328
- - All of its requirements live in that same story file.
329
- - You are happy to treat that story as the single source of truth for that code path.
330
-
331
- Example (no migration required):
332
-
333
- ```js
334
- /**
335
- * Calculate age in days since publish date.
336
- * @story docs/stories/002.0-DEV-FETCH-AVAILABLE-VERSIONS.story.md
337
- * @req REQ-AGE-CALC
338
- */
339
- export function calculateAgeInDays(publishDate) {
340
- // ...
341
- }
342
- ```
343
-
344
- ### When to adopt `@implements`
345
-
346
- Use `@implements` when:
347
-
348
- - A function or class **implements requirements from multiple stories**.
349
- - Requirements with the **same ID** are reused in more than one story.
350
- - You want each requirement to be explicitly associated with the story file it comes from.
351
-
352
- With only `@story` + `@req`, multi-story integration code either:
353
-
354
- - Cannot be expressed cleanly at all, or
355
- - Leads to confusing or incorrect deep-validation results when checked by `valid-req-reference`.
356
-
357
- `@implements` solves this by letting you list, on a single line, the story file followed by all requirement IDs from that story that the code implements.
358
-
359
- ### Before: single-story annotations only
360
-
361
- A typical "before" example for integration code might try to overload a single story, or avoid deep validation entirely:
362
-
363
- ```js
364
- /**
365
- * Apply age and security filters to rows.
366
- * @story docs/stories/003.0-DEV-IDENTIFY-OUTDATED.story.md
367
- * @req REQ-AGE-THRESHOLD
368
- * @req REQ-OUTPUT
369
- *
370
- * // Implicitly also implements security checks from another story,
371
- * // but there is no clear or validated link here.
372
- */
373
- export async function applyFilters(rows, options) {
374
- // combined behavior
375
- }
376
- ```
377
-
378
- This passes format validation but does **not** clearly show that some behavior comes from a second story, and deep requirement validation cannot reliably tell which story each requirement belongs to.
379
-
380
- ### After: multi-story `@implements`
381
-
382
- With `@implements`, you keep `@story` + `@req` for the primary story (if you want), and add explicit, multi-story mappings:
383
-
384
- ```js
385
- /**
386
- * Apply age and security filters to rows.
387
- * @story docs/stories/003.0-DEV-IDENTIFY-OUTDATED.story.md
388
- * @req REQ-AGE-THRESHOLD
389
- * @req REQ-OUTPUT
390
- *
391
- * @implements docs/stories/003.0-DEV-IDENTIFY-OUTDATED.story.md REQ-AGE-THRESHOLD REQ-OUTPUT
392
- * @implements docs/stories/004.0-DEV-FILTER-VULNERABLE-VERSIONS.story.md REQ-AUDIT-CHECK REQ-SAFE-ONLY
393
- */
394
- export async function applyFilters(rows, options) {
395
- // combined behavior
396
- }
397
- ```
398
-
399
- In this form:
400
-
401
- - `valid-annotation-format` checks that each `@implements` line uses a valid story path and requirement ID format.
402
- - `valid-req-reference` (see its rule documentation) performs deep validation that every requirement ID listed after `@implements` actually exists in the referenced story file.
403
-
404
- ### Mixed usage during migration
405
-
406
- You can introduce `@implements` **incrementally**:
407
-
408
- 1. Start from working code that already uses `@story` + `@req`.
409
- 2. Add `@implements` lines that group requirements by story file, without removing the original annotations.
410
- 3. Run ESLint with `traceability/valid-annotation-format` and `traceability/valid-req-reference` enabled to confirm there are no new violations.
411
- 4. Optionally, once your team is comfortable, standardize on always using `@implements` for multi-story integration functions.
412
-
413
- Both annotation styles are fully supported:
414
-
415
- - Single-story code can continue to use only `@story` + `@req`.
416
- - Multi-story integration code should prefer `@implements` to get precise, per-story validation.
417
-
418
- For more details on how `@implements` participates in deep requirement checking, including multi-story scenarios and requirement ID scoping, see the `valid-req-reference` rule documentation and Story `docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md`.
@@ -1,153 +0,0 @@
1
- # valid-req-reference
2
-
3
- Enforces that `@req` and `@implements` annotations reference existing requirements in story files and protects against invalid paths.
4
-
5
- @story docs/stories/010.0-DEV-DEEP-VALIDATION.story.md
6
- @req REQ-DEEP-PARSE - Parse story files to extract requirement identifiers
7
- @req REQ-DEEP-MATCH - Validate `@req` references against story file content
8
- @req REQ-DEEP-PATH - Protect against path traversal in story paths
9
- @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
10
- @req REQ-MULTI-IMPLEMENTS - Support `@implements` annotations that point to multiple story files
11
- @req REQ-MULTI-PATHS - Allow each `@implements` to specify its own story path and requirement list
12
- @req REQ-MULTI-UNIQUENESS - Only require requirement IDs to be unique within a single story file
13
-
14
- ## Rule Details
15
-
16
- This rule performs deep validation of `@req` and `@implements` annotations by:
17
-
18
- - Verifying each referenced story file exists and is within the project directory
19
- - Parsing every referenced story file to extract requirement IDs (e.g., `REQ-XXX-YYY`)
20
- - Ensuring each `@req` and `@implements` requirement ID matches an extracted requirement ID in the corresponding story file
21
- - Reporting an `invalidPath` error for paths containing `..` or absolute paths
22
- - Reporting a `reqMissing` error when the requirement ID is not found in the story file
23
- - Treating requirement IDs as scoped to a story file: the same requirement ID may appear in multiple story files without error
24
-
25
- ### Interaction of `@story`/`@req` and `@implements`
26
-
27
- - `@story` sets a default story file path for all subsequent `@req` lines in the same file (until another `@story` is encountered).
28
- - Each `@req` line uses the most recent `@story` path; the rule validates that the requirement exists in that story file.
29
- - Each `@implements` line is self-contained:
30
- - It specifies its own story path (independent of any preceding `@story`).
31
- - It may list one or more requirement IDs that are expected to exist in that specific story file.
32
- - The rule validates each requirement ID from `@implements` against the requirements found in the referenced story file.
33
- - Requirement IDs only need to be unique within a single story file; duplicates across different story files are allowed and validated independently.
34
-
35
- ### Options
36
-
37
- This rule does not accept any options (schema is `[]`).
38
-
39
- ## Examples
40
-
41
- ### Correct: Single Story with `@story` and `@req`
42
-
43
- ```js
44
- // @story docs/stories/001.0-DEV-PLUGIN-SETUP.story.md
45
- // @req REQ-PLUGIN-STRUCTURE
46
- function initPlugin() {}
47
- ```
48
-
49
- ### Correct: Multiple Stories with `@implements`
50
-
51
- ```js
52
- // @implements docs/stories/001.0-DEV-PLUGIN-SETUP.story.md REQ-PLUGIN-STRUCTURE REQ-PLUGIN-INIT
53
- // @implements docs/stories/002.0-DEV-PLUGIN-RUNTIME.story.md REQ-PLUGIN-RUNTIME-START REQ-PLUGIN-RUNTIME-STOP
54
- function initPlugin() {}
55
-
56
- // Same requirement ID reused in another story file is allowed:
57
- // @implements docs/stories/003.0-DEV-ALT-SETUP.story.md REQ-PLUGIN-STRUCTURE
58
- function altInitPlugin() {}
59
- ```
60
-
61
- ### Incorrect: Missing Requirement with `@req`
62
-
63
- ```js
64
- // @story docs/stories/001.0-DEV-PLUGIN-SETUP.story.md
65
- // @req REQ-NON-EXISTENT
66
- function initPlugin() {}
67
- ```
68
-
69
- ### Incorrect: Missing Requirement with `@implements`
70
-
71
- ```js
72
- // @implements docs/stories/001.0-DEV-PLUGIN-SETUP.story.md REQ-NON-EXISTENT
73
- function initPlugin() {}
74
- ```
75
-
76
- ### Incorrect: Path Traversal
77
-
78
- ```js
79
- // @story ../docs/stories/001.0-DEV-PLUGIN-SETUP.story.md
80
- // @req REQ-PLUGIN-STRUCTURE
81
- function initPlugin() {}
82
- ```
83
-
84
- ```js
85
- // @implements ../docs/stories/001.0-DEV-PLUGIN-SETUP.story.md REQ-PLUGIN-STRUCTURE
86
- function initPlugin() {}
87
- ```
88
-
89
- ### Incorrect: Absolute Path
90
-
91
- ```js
92
- // @story /absolute/path/docs/stories/001.0-DEV-PLUGIN-SETUP.story.md
93
- // @req REQ-PLUGIN-STRUCTURE
94
- function initPlugin() {}
95
- ```
96
-
97
- ```js
98
- // @implements /absolute/path/docs/stories/001.0-DEV-PLUGIN-SETUP.story.md REQ-PLUGIN-STRUCTURE
99
- function initPlugin() {}
100
- ```
101
-
102
- ### Migration and multi-story usage
103
-
104
- The `valid-req-reference` rule is fully backward compatible with projects that only use `@story` and `@req`. You can keep your existing deep-validation configuration and gradually adopt `@implements` where it adds clarity.
105
-
106
- #### Before: deep validation with a single story
107
-
108
- In many codebases, deep requirement validation starts with a single story per function:
109
-
110
- ```js
111
- // @story docs/stories/003.0-DEV-IDENTIFY-OUTDATED.story.md
112
- // @req REQ-AGE-THRESHOLD
113
- // @req REQ-OUTPUT
114
- export async function applyFilters(rows, options) {
115
- // combined behavior
116
- }
117
- ```
118
-
119
- `valid-req-reference` resolves the story file, parses its requirement IDs, and verifies that both `REQ-AGE-THRESHOLD` and `REQ-OUTPUT` exist in that file.
120
-
121
- #### After: multi-story deep validation with `@implements`
122
-
123
- When the same function genuinely implements requirements from multiple stories, prefer `@implements` to make that relationship explicit:
124
-
125
- ```js
126
- /**
127
- * Apply age and security filters to rows.
128
- * @story docs/stories/003.0-DEV-IDENTIFY-OUTDATED.story.md
129
- * @req REQ-AGE-THRESHOLD
130
- * @req REQ-OUTPUT
131
- *
132
- * @implements docs/stories/003.0-DEV-IDENTIFY-OUTDATED.story.md REQ-AGE-THRESHOLD REQ-OUTPUT
133
- * @implements docs/stories/004.0-DEV-FILTER-VULNERABLE-VERSIONS.story.md REQ-AUDIT-CHECK REQ-SAFE-ONLY
134
- */
135
- export async function applyFilters(rows, options) {
136
- // combined behavior
137
- }
138
- ```
139
-
140
- In this form:
141
-
142
- - Each `@implements` line is self-contained: it specifies the story file and the list of requirements implemented from that story.
143
- - `valid-req-reference` validates every requirement ID listed after `@implements` against the corresponding story file, using the same parsing and caching logic as for `@req`.
144
- - Requirement IDs only need to be unique within a single story file; you can safely reuse IDs like `REQ-SHARED-ID` in multiple stories and reference each one via its own `@implements` line.
145
-
146
- You can mix `@story`/`@req` and `@implements` in the same file during migration. Start from working `@story`/`@req` annotations, add `@implements` lines for multi-story integration functions, and run ESLint with both `traceability/valid-annotation-format` and `traceability/valid-req-reference` enabled to confirm there are no new violations.
147
-
148
- For more background and examples, see Story `docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md`.
149
-
150
- For more details, see the stories:
151
-
152
- - docs/stories/010.0-DEV-DEEP-VALIDATION.story.md
153
- - docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
@@ -1,120 +0,0 @@
1
- # valid-story-reference
2
-
3
- Validates that `@story` annotation references refer to existing `.story.md` files within the project and prevents invalid path usage.
4
-
5
- @story docs/stories/006.0-DEV-FILE-VALIDATION.story.md
6
- @req REQ-FILE-EXISTENCE - Verify that `@story` file paths reference existing files
7
- @req REQ-PATH-RESOLUTION - Resolve relative paths correctly and enforce configuration
8
- @req REQ-SECURITY-VALIDATION - Prevent path traversal and absolute path usage
9
-
10
- ## Rule Details
11
-
12
- This rule inspects all comment nodes for lines starting with `@story`. It then:
13
-
14
- - Prevents absolute paths unless explicitly allowed
15
- - Prevents path traversal outside the project directory
16
- - Ensures `.story.md` extension when required
17
- - Resolves candidate file locations in configured story directories
18
- - Reports a `fileMissing`, `invalidExtension`, or `invalidPath` error for violations
19
-
20
- ### Boundary & Configuration
21
-
22
- The rule enforces a strict “project boundary” for story references in line with story `006.0-DEV-FILE-VALIDATION`:
23
-
24
- - All candidate filesystem paths are built (from `@story` text, `storyDirectories`, and allowed absolute paths) and then checked against the project root using a `ProjectBoundaryCheckResult`.
25
- - A candidate that the boundary checker classifies as outside the project root is treated as out-of-bounds and is not used for existence/access decisions.
26
- - If **all** resolved candidates are out-of-bounds, the rule reports `invalidPath` even if filesystem queries (were they to be run) would indicate that a file exists at one or more of those locations.
27
- - When **at least one** candidate path is within the project boundary:
28
- - Normal extension rules are applied first (e.g. `.story.md` when `requireStoryExtension` is `true`), and mismatches are reported as `invalidExtension`.
29
- - For in-bounds candidates with acceptable extensions, the rule performs filesystem checks and distinguishes between:
30
- - `fileMissing` – the candidate is within the boundary and has a valid extension, but no file exists at that path.
31
- - `fileAccessError` – the candidate is within the boundary, but the filesystem check fails for reasons other than “file not found” (e.g. permission errors).
32
- - Success – the file exists and is accessible within the project boundary.
33
- - By default, absolute paths are considered outside the project boundary and are rejected as `invalidPath` by the boundary checker unless explicitly allowed by configuration; when allowed, they are still subject to the same boundary classification and existence/access logic as relative candidates.
34
- - Resolution is constrained to candidates that remain within the project root according to `ProjectBoundaryCheckResult`; candidates that would escape the project (e.g. via `..` traversal) are classified as out-of-bounds and contribute to `invalidPath` when no in-bounds alternatives exist.
35
-
36
- These constraints ensure that story references are deterministic, prevent accidental coupling to files outside the intended documentation area, and avoid security issues from arbitrary filesystem traversal.
37
-
38
- ### Options
39
-
40
- Configure rule behavior using an options object that controls how paths are resolved and validated against the project boundary:
41
-
42
- ```json
43
- {
44
- "storyDirectories": ["docs/stories", "stories"],
45
- "allowAbsolutePaths": false,
46
- "requireStoryExtension": true
47
- }
48
- ```
49
-
50
- #### `storyDirectories`
51
-
52
- - Type: `string[]`
53
- - Default: `["docs/stories", "stories"]` (may vary by project)
54
-
55
- Defines the set of directories (relative to the project root) that are considered valid locations for `.story.md` files.
56
-
57
- Behavior and interaction with boundaries:
58
-
59
- - All relative `@story` paths are first normalized and then resolved **within** these directories.
60
- - The rule will only look for matching files under these directories; files with the same name outside them are ignored.
61
- - A path that cannot be mapped into any configured `storyDirectories` without leaving the project boundary is reported as `invalidPath`.
62
- - If a normalized path is inside a `storyDirectories` entry but the target file does not exist, the rule reports `fileMissing`.
63
-
64
- Use this to centrally control where story files are allowed to live (e.g. `docs/stories`, `stories/api`).
65
-
66
- #### `allowAbsolutePaths`
67
-
68
- - Type: `boolean`
69
- - Default: `false`
70
-
71
- Controls whether `@story` can use absolute filesystem paths (e.g. `/full/path/to/story.story.md`).
72
-
73
- Boundary and security implications:
74
-
75
- - When `false`:
76
- - Any absolute path is reported as `invalidPath`.
77
- - All `@story` references must be relative to the project and are resolved only within `storyDirectories`.
78
- - This is the recommended setting to prevent references to files outside the repository or project boundary.
79
- - When `true`:
80
- - Absolute paths are permitted and resolved directly on the filesystem.
81
- - Other checks still apply:
82
- - Path traversal checks still apply (e.g. disallowing obvious escape patterns if the project sets such constraints).
83
- - `requireStoryExtension` still controls which extensions are accepted.
84
- - Use with caution, as it weakens project-boundary guarantees and may introduce portability issues across machines.
85
-
86
- #### `requireStoryExtension`
87
-
88
- - Type: `boolean`
89
- - Default: `true`
90
-
91
- Controls whether `@story` references must end with the `.story.md` extension.
92
-
93
- Extension and boundary interaction:
94
-
95
- - When `true`:
96
- - Only files with a `.story.md` suffix are considered valid story files.
97
- - If a referenced path resolves to a file with the wrong extension (e.g. `.md` without `.story`), the rule reports `invalidExtension`.
98
- - This applies regardless of whether the file exists within the project boundary; a mismatched extension is always flagged as `invalidExtension` (even if the underlying file exists).
99
- - When `false`:
100
- - Other markdown extensions (e.g. `.md`, `.markdown`) may be allowed, subject to the rule’s implementation.
101
- - Path and boundary checks still apply: the file must still be within `storyDirectories` (or be a permitted absolute path), and traversal outside the project boundary is still rejected as `invalidPath`.
102
-
103
- Use `requireStoryExtension: true` to enforce a clear, consistent naming convention for story files and to make it unambiguous which markdown files are intended to be executable stories.
104
-
105
- ### Examples
106
-
107
- #### Correct
108
-
109
- ```js
110
- // @story docs/stories/001.0-DEV-PLUGIN-SETUP.story.md
111
- ```
112
-
113
- #### Incorrect
114
-
115
- ```js
116
- // @story docs/stories/nonexistent.story.md // @story fileMissing
117
- // @story docs/stories/001.0-DEV-PLUGIN-SETUP.md // @story invalidExtension
118
- // @story ../outside.story.md // @story invalidPath
119
- // @story /etc/passwd.story.md // @story invalidPath
120
- ```