eslint-plugin-traceability 1.7.0 → 1.8.0

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 (107) hide show
  1. package/CHANGELOG.md +82 -0
  2. package/README.md +73 -32
  3. package/docs/ci-cd-pipeline.md +224 -0
  4. package/docs/cli-integration.md +22 -0
  5. package/docs/code-quality-refactor-opportunities-2025-12-03.md +78 -0
  6. package/docs/config-presets.md +38 -0
  7. package/docs/conventional-commits-guide.md +185 -0
  8. package/docs/custom-rules-development-guide.md +659 -0
  9. package/docs/decisions/0001-allow-dynamic-require-for-built-plugins.md +26 -0
  10. package/docs/decisions/001-typescript-for-eslint-plugin.accepted.md +111 -0
  11. package/docs/decisions/002-jest-for-eslint-testing.accepted.md +137 -0
  12. package/docs/decisions/003-code-quality-ratcheting-plan.md +48 -0
  13. package/docs/decisions/004-automated-version-bumping-for-ci-cd.md +196 -0
  14. package/docs/decisions/005-github-actions-validation-tooling.accepted.md +144 -0
  15. package/docs/decisions/006-semantic-release-for-automated-publishing.accepted.md +227 -0
  16. package/docs/decisions/007-github-releases-over-changelog.accepted.md +216 -0
  17. package/docs/decisions/008-ci-audit-flags.accepted.md +60 -0
  18. package/docs/decisions/009-security-focused-lint-rules.accepted.md +64 -0
  19. package/docs/decisions/010-implements-annotation-for-multi-story-requirements.proposed.md +184 -0
  20. package/docs/decisions/adr-0001-console-usage-for-cli-guards.md +190 -0
  21. package/docs/decisions/adr-accept-dev-dep-risk-glob.md +40 -0
  22. package/docs/decisions/adr-commit-branch-tests.md +54 -0
  23. package/docs/decisions/adr-maintenance-cli-interface.md +140 -0
  24. package/docs/decisions/adr-pre-push-parity.md +112 -0
  25. package/docs/decisions/code-quality-ratcheting-plan.md +53 -0
  26. package/docs/dependency-health.md +238 -0
  27. package/docs/eslint-9-setup-guide.md +517 -0
  28. package/docs/eslint-plugin-development-guide.md +487 -0
  29. package/docs/functionality-coverage-2025-12-03.md +250 -0
  30. package/docs/jest-testing-guide.md +100 -0
  31. package/docs/rules/prefer-implements-annotation.md +219 -0
  32. package/docs/rules/require-branch-annotation.md +71 -0
  33. package/docs/rules/require-req-annotation.md +203 -0
  34. package/docs/rules/require-story-annotation.md +159 -0
  35. package/docs/rules/valid-annotation-format.md +418 -0
  36. package/docs/rules/valid-req-reference.md +153 -0
  37. package/docs/rules/valid-story-reference.md +120 -0
  38. package/docs/security-incidents/2025-11-17-glob-cli-incident.md +45 -0
  39. package/docs/security-incidents/2025-11-18-brace-expansion-redos.md +45 -0
  40. package/docs/security-incidents/2025-11-18-bundled-dev-deps-accepted-risk.md +93 -0
  41. package/docs/security-incidents/2025-11-18-tar-race-condition.md +43 -0
  42. package/docs/security-incidents/2025-12-03-dependency-health-review.md +58 -0
  43. package/docs/security-incidents/SECURITY-INCIDENT-2025-11-18-semantic-release-bundled-npm.known-error.md +104 -0
  44. package/docs/security-incidents/SECURITY-INCIDENT-TEMPLATE.md +37 -0
  45. package/docs/security-incidents/dependency-override-rationale.md +57 -0
  46. package/docs/security-incidents/dev-deps-high.json +116 -0
  47. package/docs/security-incidents/handling-procedure.md +54 -0
  48. package/docs/stories/001.0-DEV-PLUGIN-SETUP.story.md +92 -0
  49. package/docs/stories/002.0-DEV-ESLINT-CONFIG.story.md +82 -0
  50. package/docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md +112 -0
  51. package/docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md +153 -0
  52. package/docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md +138 -0
  53. package/docs/stories/006.0-DEV-FILE-VALIDATION.story.md +144 -0
  54. package/docs/stories/007.0-DEV-ERROR-REPORTING.story.md +163 -0
  55. package/docs/stories/008.0-DEV-AUTO-FIX.story.md +150 -0
  56. package/docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md +117 -0
  57. package/docs/stories/010.0-DEV-DEEP-VALIDATION.story.md +124 -0
  58. package/docs/stories/010.1-DEV-CONFIGURABLE-PATTERNS.story.md +149 -0
  59. package/docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md +216 -0
  60. package/docs/stories/010.3-DEV-MIGRATE-TO-IMPLEMENTS.story.md +236 -0
  61. package/docs/stories/developer-story.map.md +120 -0
  62. package/docs/ts-jest-presets-guide.md +548 -0
  63. package/lib/src/index.d.ts +2 -2
  64. package/lib/src/index.js +2 -0
  65. package/lib/src/maintenance/batch.d.ts +5 -0
  66. package/lib/src/maintenance/batch.js +5 -0
  67. package/lib/src/maintenance/cli.js +34 -212
  68. package/lib/src/maintenance/commands.d.ts +32 -0
  69. package/lib/src/maintenance/commands.js +139 -0
  70. package/lib/src/maintenance/detect.d.ts +2 -0
  71. package/lib/src/maintenance/detect.js +4 -0
  72. package/lib/src/maintenance/flags.d.ts +99 -0
  73. package/lib/src/maintenance/flags.js +121 -0
  74. package/lib/src/maintenance/report.d.ts +2 -0
  75. package/lib/src/maintenance/report.js +2 -0
  76. package/lib/src/maintenance/update.d.ts +4 -0
  77. package/lib/src/maintenance/update.js +4 -0
  78. package/lib/src/rules/helpers/require-story-io.d.ts +3 -0
  79. package/lib/src/rules/helpers/require-story-io.js +20 -6
  80. package/lib/src/rules/helpers/valid-annotation-format-internal.d.ts +30 -0
  81. package/lib/src/rules/helpers/valid-annotation-format-internal.js +36 -0
  82. package/lib/src/rules/helpers/valid-annotation-options.js +15 -4
  83. package/lib/src/rules/helpers/valid-annotation-utils.js +5 -0
  84. package/lib/src/rules/helpers/valid-implements-utils.d.ts +75 -0
  85. package/lib/src/rules/helpers/valid-implements-utils.js +149 -0
  86. package/lib/src/rules/helpers/valid-story-reference-helpers.d.ts +3 -4
  87. package/lib/src/rules/prefer-implements-annotation.d.ts +39 -0
  88. package/lib/src/rules/prefer-implements-annotation.js +276 -0
  89. package/lib/src/rules/valid-annotation-format.js +87 -28
  90. package/lib/src/rules/valid-req-reference.js +71 -0
  91. package/lib/src/utils/reqAnnotationDetection.d.ts +4 -1
  92. package/lib/src/utils/reqAnnotationDetection.js +43 -15
  93. package/lib/tests/maintenance/cli.test.js +89 -0
  94. package/lib/tests/plugin-default-export-and-configs.test.js +3 -0
  95. package/lib/tests/rules/prefer-implements-annotation.test.d.ts +1 -0
  96. package/lib/tests/rules/prefer-implements-annotation.test.js +84 -0
  97. package/lib/tests/rules/require-req-annotation.test.js +8 -1
  98. package/lib/tests/rules/require-story-annotation.test.js +9 -4
  99. package/lib/tests/rules/valid-annotation-format.test.js +78 -0
  100. package/lib/tests/rules/valid-req-reference.test.js +34 -0
  101. package/lib/tests/utils/ts-language-options.d.ts +1 -7
  102. package/lib/tests/utils/ts-language-options.js +8 -5
  103. package/package.json +7 -3
  104. package/user-docs/api-reference.md +507 -0
  105. package/user-docs/eslint-9-setup-guide.md +639 -0
  106. package/user-docs/examples.md +74 -0
  107. package/user-docs/migration-guide.md +158 -0
@@ -0,0 +1,39 @@
1
+ /**
2
+ * ESLint rule implementation for preferring the consolidated `@implements`
3
+ * annotation over legacy combinations of `@story` and `@req` within JSDoc
4
+ * block comments. This module provides:
5
+ *
6
+ * - Detection of legacy `@story` + `@req` patterns.
7
+ * - Identification of multi-story comment blocks that are not safely
8
+ * auto-fixable.
9
+ * - A conservative auto-fix that rewrites simple, single-story patterns into
10
+ * a single `@implements` annotation while preserving formatting.
11
+ *
12
+ * The rule is intended as an **optional migration aid** to help projects
13
+ * gradually move to the newer `@implements` format without breaking existing
14
+ * traceability links.
15
+ *
16
+ * @story docs/stories/010.3-DEV-MIGRATE-TO-IMPLEMENTS.story.md
17
+ * @req REQ-OPTIONAL-WARNING - Emit configurable recommendation diagnostics for legacy @story/@req usage
18
+ * @req REQ-MULTI-STORY-DETECT - Detect multi-story patterns that cannot be auto-fixed
19
+ * @req REQ-SINGLE-STORY-FIX - Restrict auto-fix to single-story, single-path cases
20
+ * @req REQ-PRESERVE-FORMAT - Preserve original JSDoc indentation and prefix formatting
21
+ * @req REQ-VALID-OUTPUT - Avoid emitting auto-fixes for complex or ambiguous patterns
22
+ * @req REQ-BACKWARD-COMP-VALIDATION - Keep legacy @story/@req annotations valid when the rule is disabled
23
+ * @req REQ-AUTO-FIX - Provide safe, opt-in auto-fix for simple legacy patterns
24
+ */
25
+ import type { Rule } from "eslint";
26
+ /**
27
+ * ESLint rule: prefer-implements-annotation
28
+ *
29
+ * Recommend migrating from legacy `@story` + `@req` annotations to the
30
+ * newer `@implements` format. This rule is **disabled by default** and
31
+ * is intended as an optional, opt-in migration aid.
32
+ *
33
+ * @story docs/stories/010.3-DEV-MIGRATE-TO-IMPLEMENTS.story.md
34
+ * @req REQ-OPTIONAL-WARNING - Emit configurable recommendation diagnostics for legacy @story/@req usage
35
+ * @req REQ-MULTI-STORY-DETECT - Detect multi-story patterns that cannot be auto-fixed
36
+ * @req REQ-BACKWARD-COMP-VALIDATION - Keep legacy @story/@req annotations valid when the rule is disabled
37
+ */
38
+ declare const preferImplementsAnnotationRule: Rule.RuleModule;
39
+ export default preferImplementsAnnotationRule;
@@ -0,0 +1,276 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const valid_annotation_format_internal_1 = require("./helpers/valid-annotation-format-internal");
4
+ // Maximum number of distinct @story paths allowed before treating as "multi-story".
5
+ // @req REQ-MULTI-STORY-DETECT - Centralized threshold constant for detecting multi-story patterns
6
+ const MULTI_STORY_THRESHOLD = 1;
7
+ // Minimum number of tokens required for a valid @story annotation line.
8
+ // @story docs/stories/010.3-DEV-MIGRATE-TO-IMPLEMENTS.story.md
9
+ // @req REQ-MULTI-STORY-DETECT
10
+ const MIN_STORY_TOKENS = 2;
11
+ // Minimum number of tokens required for a valid @req annotation line, aligned with story tokens.
12
+ const MIN_REQ_TOKENS = MIN_STORY_TOKENS;
13
+ // Length of the opening "/*" portion of a block comment prefix.
14
+ const COMMENT_PREFIX_LENGTH = 2;
15
+ function collectStoryAndReqMetadata(comment) {
16
+ const rawValue = comment.value || "";
17
+ const rawLines = rawValue.split(/\r?\n/);
18
+ const storyLineIndices = [];
19
+ const reqLineIndices = [];
20
+ const reqIds = [];
21
+ let storyPath = null;
22
+ rawLines.forEach((rawLine, index) => {
23
+ const normalized = (0, valid_annotation_format_internal_1.normalizeCommentLine)(rawLine);
24
+ if (!normalized)
25
+ return;
26
+ if (/^@implements\b/.test(normalized)) {
27
+ // Mixed @implements usage should have been filtered out earlier
28
+ return;
29
+ }
30
+ if (/^@story\b/.test(normalized)) {
31
+ const parts = normalized.split(/\s+/);
32
+ if (parts.length === MIN_STORY_TOKENS) {
33
+ storyLineIndices.push(index);
34
+ storyPath = parts[1];
35
+ }
36
+ else {
37
+ storyPath = null;
38
+ }
39
+ return;
40
+ }
41
+ if (/^@req\b/.test(normalized)) {
42
+ const parts = normalized.split(/\s+/);
43
+ if (parts.length === MIN_REQ_TOKENS) {
44
+ reqLineIndices.push(index);
45
+ reqIds.push(parts[1]);
46
+ }
47
+ else {
48
+ // Complex @req form; bail out entirely.
49
+ storyPath = null;
50
+ }
51
+ }
52
+ });
53
+ return { storyLineIndices, reqLineIndices, reqIds, storyPath };
54
+ }
55
+ function applyImplementsReplacement(context, comment, details) {
56
+ const { storyIdx, allIndicesToRemove, storyPath, reqIds } = details;
57
+ const rawValue = comment.value || "";
58
+ const rawLines = rawValue.split(/\r?\n/);
59
+ const implAnnotation = `@implements ${storyPath} ${reqIds.join(" ")}`;
60
+ // Determine the leading prefix (indentation and `*`) from the original @story line
61
+ const storyRawLine = rawLines[storyIdx];
62
+ const prefixMatch = storyRawLine.match(/^(\s*\*?\s*)/);
63
+ const linePrefix = prefixMatch ? prefixMatch[1] : "";
64
+ const implementsLine = `${linePrefix}${implAnnotation}`;
65
+ const fixedLines = [];
66
+ rawLines.forEach((line, index) => {
67
+ if (index === storyIdx) {
68
+ fixedLines.push(implementsLine);
69
+ return;
70
+ }
71
+ if (allIndicesToRemove.has(index)) {
72
+ return;
73
+ }
74
+ fixedLines.push(line);
75
+ });
76
+ const fixedValue = fixedLines.join("\n");
77
+ const sourceCode = context.getSourceCode();
78
+ return (fixer) => fixer.replaceTextRange([comment.range[0], comment.range[1]], sourceCode.text.slice(comment.range[0], comment.range[0] + COMMENT_PREFIX_LENGTH) +
79
+ fixedValue +
80
+ "*/");
81
+ }
82
+ /**
83
+ * Build an ESLint auto-fix for simple single-story `@story` + `@req` JSDoc
84
+ * blocks, converting them to a single `@implements` annotation while
85
+ * preserving the original comment formatting.
86
+ *
87
+ * The fixer is intentionally conservative and only activates when:
88
+ * - There is exactly one distinct `@story` path.
89
+ * - Exactly one `@story` line is present.
90
+ * - At least one `@req` line is present.
91
+ * - Each `@req` line has the simple form `@req <REQ-ID>` (no extra tokens).
92
+ *
93
+ * When applicable, the fix:
94
+ * - Removes the original `@story` and `@req` lines.
95
+ * - Inserts a single `@implements` line in their place, preserving the
96
+ * original leading comment prefix (indentation and `*` markers).
97
+ *
98
+ * More complex patterns remain diagnostics-only with no fix to avoid
99
+ * producing invalid or ambiguous output.
100
+ *
101
+ * @implements docs/stories/010.3-DEV-MIGRATE-TO-IMPLEMENTS.story.md
102
+ * @req REQ-AUTO-FIX - Provide safe, opt-in auto-fix for simple legacy patterns
103
+ * @req REQ-SINGLE-STORY-FIX - Restrict auto-fix to single-story, single-path cases
104
+ * @req REQ-PRESERVE-FORMAT - Preserve original JSDoc indentation and prefix formatting
105
+ * @req REQ-VALID-OUTPUT - Avoid emitting auto-fixes for complex or ambiguous patterns
106
+ */
107
+ function buildImplementsAutoFix(context, comment, storyPaths) {
108
+ if (storyPaths.size !== 1)
109
+ return null;
110
+ const { storyLineIndices, reqLineIndices, reqIds, storyPath } = collectStoryAndReqMetadata(comment);
111
+ if (storyPaths.size !== 1 ||
112
+ storyLineIndices.length !== 1 ||
113
+ reqLineIndices.length < 1 ||
114
+ storyPath === null) {
115
+ return null;
116
+ }
117
+ const storyIdx = storyLineIndices[0];
118
+ const allIndicesToRemove = new Set([
119
+ ...storyLineIndices,
120
+ ...reqLineIndices,
121
+ ]);
122
+ return applyImplementsReplacement(context, comment, {
123
+ storyIdx,
124
+ allIndicesToRemove,
125
+ storyPath,
126
+ reqIds,
127
+ });
128
+ }
129
+ function analyzeComment(comment) {
130
+ const rawLines = (comment.value || "").split(/\r?\n/);
131
+ let hasStory = false;
132
+ let hasReq = false;
133
+ let hasImplements = false;
134
+ const storyPaths = new Set();
135
+ rawLines.forEach((rawLine) => {
136
+ const normalized = (0, valid_annotation_format_internal_1.normalizeCommentLine)(rawLine);
137
+ if (!normalized)
138
+ return;
139
+ if (/^@implements\b/.test(normalized)) {
140
+ hasImplements = true;
141
+ return;
142
+ }
143
+ if (/^@story\b/.test(normalized)) {
144
+ hasStory = true;
145
+ const parts = normalized.split(/\s+/);
146
+ if (parts.length >= MIN_STORY_TOKENS) {
147
+ storyPaths.add(parts[1]);
148
+ }
149
+ return;
150
+ }
151
+ if (/^@req\b/.test(normalized)) {
152
+ hasReq = true;
153
+ }
154
+ });
155
+ return { hasStory, hasReq, hasImplements, storyPaths };
156
+ }
157
+ function hasMultipleStories(storyPaths) {
158
+ // @req REQ-MULTI-STORY-DETECT - Use named threshold constant instead of a magic number
159
+ return storyPaths.size > MULTI_STORY_THRESHOLD;
160
+ }
161
+ function processComment(comment, context) {
162
+ const { hasStory, hasReq, hasImplements, storyPaths } = analyzeComment(comment);
163
+ if (!hasStory || !hasReq) {
164
+ return;
165
+ }
166
+ if (hasImplements) {
167
+ context.report({
168
+ node: comment,
169
+ messageId: "cannotAutoFix",
170
+ data: {
171
+ reason: "comment mixes @story/@req with existing @implements annotations",
172
+ },
173
+ });
174
+ return;
175
+ }
176
+ if (hasMultipleStories(storyPaths)) {
177
+ context.report({
178
+ node: comment,
179
+ messageId: "multiStoryDetected",
180
+ });
181
+ return;
182
+ }
183
+ const fix = buildImplementsAutoFix(context, comment, storyPaths);
184
+ context.report({
185
+ node: comment,
186
+ messageId: "preferImplements",
187
+ fix: fix ?? undefined,
188
+ });
189
+ }
190
+ /**
191
+ * ESLint rule: prefer-implements-annotation
192
+ *
193
+ * Recommend migrating from legacy `@story` + `@req` annotations to the
194
+ * newer `@implements` format. This rule is **disabled by default** and
195
+ * is intended as an optional, opt-in migration aid.
196
+ *
197
+ * @story docs/stories/010.3-DEV-MIGRATE-TO-IMPLEMENTS.story.md
198
+ * @req REQ-OPTIONAL-WARNING - Emit configurable recommendation diagnostics for legacy @story/@req usage
199
+ * @req REQ-MULTI-STORY-DETECT - Detect multi-story patterns that cannot be auto-fixed
200
+ * @req REQ-BACKWARD-COMP-VALIDATION - Keep legacy @story/@req annotations valid when the rule is disabled
201
+ */
202
+ const preferImplementsAnnotationRule = {
203
+ meta: {
204
+ type: "suggestion",
205
+ docs: {
206
+ description: "Recommend using @implements instead of legacy @story + @req annotations (optional migration rule)",
207
+ recommended: false,
208
+ },
209
+ // Auto-fix support will be wired in a later iteration; the rule starts as
210
+ // a recommendation-only warning with no code modifications.
211
+ fixable: "code",
212
+ messages: {
213
+ /**
214
+ * Recommend migrating simple, single-story @story + @req blocks to a
215
+ * single @implements line. Auto-fix is provided where safe in a
216
+ * follow-up iteration.
217
+ *
218
+ * @story docs/stories/010.3-DEV-MIGRATE-TO-IMPLEMENTS.story.md
219
+ * @req REQ-OPTIONAL-WARNING
220
+ */
221
+ preferImplements: "Consider using @implements instead of @story + @req for clearer traceability. Run ESLint with --fix to auto-convert.",
222
+ /**
223
+ * Report situations where the rule detects a legacy annotation pattern
224
+ * but cannot safely provide an automatic fix. The `reason` field gives
225
+ * a short, human-readable explanation to guide manual migration.
226
+ *
227
+ * @story docs/stories/010.3-DEV-MIGRATE-TO-IMPLEMENTS.story.md
228
+ * @req REQ-MULTI-STORY-DETECT
229
+ */
230
+ cannotAutoFix: "Cannot auto-fix: {{reason}}. Manual migration to @implements required.",
231
+ /**
232
+ * Specialized message for the most common non-fixable case where more
233
+ * than one @story annotation appears in the same block, indicating a
234
+ * likely multi-story integration that must be converted manually.
235
+ *
236
+ * @story docs/stories/010.3-DEV-MIGRATE-TO-IMPLEMENTS.story.md
237
+ * @req REQ-MULTI-STORY-DETECT
238
+ */
239
+ multiStoryDetected: "Multiple @story annotations detected in the same comment block. Manually convert to separate @implements lines.",
240
+ },
241
+ schema: [],
242
+ },
243
+ /**
244
+ * Rule entrypoint.
245
+ *
246
+ * This initial implementation focuses on **detection and messaging only**:
247
+ * it surfaces recommendations when legacy `@story` + `@req` combinations are
248
+ * present but does not yet perform automatic code modifications.
249
+ *
250
+ * @story docs/stories/010.3-DEV-MIGRATE-TO-IMPLEMENTS.story.md
251
+ * @req REQ-OPTIONAL-WARNING
252
+ * @req REQ-MULTI-STORY-DETECT
253
+ */
254
+ create(context) {
255
+ const sourceCode = context.getSourceCode();
256
+ return {
257
+ /**
258
+ * Program-level visitor that scans all comments for legacy
259
+ * `@story` + `@req` usage and emits recommendation diagnostics.
260
+ *
261
+ * @story docs/stories/010.3-DEV-MIGRATE-TO-IMPLEMENTS.story.md
262
+ * @req REQ-OPTIONAL-WARNING - Emit recommendations when legacy annotations are detected
263
+ * @req REQ-MULTI-STORY-DETECT - Detect multi-story and mixed annotation patterns
264
+ */
265
+ Program() {
266
+ const comments = sourceCode.getAllComments() || [];
267
+ comments
268
+ .filter((comment) => comment.type === "Block")
269
+ .forEach((comment) => {
270
+ processComment(comment, context);
271
+ });
272
+ },
273
+ };
274
+ },
275
+ };
276
+ exports.default = preferImplementsAnnotationRule;
@@ -2,34 +2,14 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const valid_annotation_options_1 = require("./helpers/valid-annotation-options");
4
4
  const valid_annotation_utils_1 = require("./helpers/valid-annotation-utils");
5
- /**
6
- * Normalize a raw comment line to make annotation parsing more robust.
7
- *
8
- * This function trims whitespace, keeps any annotation tags that appear
9
- * later in the line, and supports common JSDoc styles such as leading "*".
10
- *
11
- * @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
12
- * @story docs/stories/008.0-DEV-AUTO-FIX.story.md
13
- * @req REQ-FLEXIBLE-PARSING - Support reasonable variations in whitespace and formatting
14
- * @req REQ-AUTOFIX-FORMAT - Provide safe, minimal automatic fixes for common format issues
15
- */
16
- function normalizeCommentLine(rawLine) {
17
- const trimmed = rawLine.trim();
18
- if (!trimmed) {
19
- return "";
20
- }
21
- const annotationMatch = trimmed.match(/@story\b|@req\b/);
22
- if (!annotationMatch || annotationMatch.index === undefined) {
23
- const withoutLeadingStar = trimmed.replace(/^\*\s?/, "");
24
- return withoutLeadingStar;
25
- }
26
- return trimmed.slice(annotationMatch.index);
27
- }
5
+ const valid_implements_utils_1 = require("./helpers/valid-implements-utils");
6
+ const valid_annotation_format_internal_1 = require("./helpers/valid-annotation-format-internal");
28
7
  /**
29
8
  * Report an invalid @story annotation without applying a fix.
30
9
  *
31
10
  * @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
32
11
  * @story docs/stories/008.0-DEV-AUTO-FIX.story.md
12
+ * @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
33
13
  * @req REQ-AUTOFIX-FORMAT - Provide safe, minimal automatic fixes for common format issues
34
14
  */
35
15
  function reportInvalidStoryFormat(context, comment, collapsed, options) {
@@ -91,6 +71,7 @@ function createStoryFix(context, comment, fixed) {
91
71
  *
92
72
  * @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
93
73
  * @story docs/stories/008.0-DEV-AUTO-FIX.story.md
74
+ * @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
94
75
  * @req REQ-PATH-FORMAT - Validate @story paths follow expected patterns
95
76
  * @req REQ-AUTOFIX-FORMAT - Provide safe, minimal automatic fixes for common format issues
96
77
  * @req REQ-AUTOFIX-SAFE - Auto-fix must be conservative and avoid changing semantics
@@ -117,11 +98,13 @@ function reportInvalidStoryFormatWithFix(context, comment, collapsed, fixed) {
117
98
  *
118
99
  * @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
119
100
  * @story docs/stories/008.0-DEV-AUTO-FIX.story.md
101
+ * @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
120
102
  * @req REQ-PATH-FORMAT - Validate @story paths follow expected patterns
121
103
  * @req REQ-ERROR-SPECIFICITY - Provide specific error messages for different format violations
122
104
  * @req REQ-AUTOFIX-FORMAT - Provide safe, minimal automatic fixes for common format issues
123
105
  * @req REQ-REGEX-VALIDATION - Validate configurable story regex patterns and fall back safely
124
106
  * @req REQ-BACKWARD-COMP - Preserve behavior when invalid regex config is supplied
107
+ * @req REQ-MIXED-SUPPORT - Support mixed @story/@req/@implements usage in comments
125
108
  */
126
109
  function validateStoryAnnotation(context, comment, rawValue, options) {
127
110
  const trimmed = rawValue.trim();
@@ -154,10 +137,12 @@ function validateStoryAnnotation(context, comment, rawValue, options) {
154
137
  *
155
138
  * @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
156
139
  * @story docs/stories/008.0-DEV-AUTO-FIX.story.md
140
+ * @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
157
141
  * @req REQ-REQ-FORMAT - Validate @req identifiers follow expected patterns
158
142
  * @req REQ-ERROR-SPECIFICITY - Provide specific error messages for different format violations
159
143
  * @req REQ-REGEX-VALIDATION - Validate configurable requirement regex patterns and fall back safely
160
144
  * @req REQ-BACKWARD-COMP - Preserve behavior when invalid regex config is supplied
145
+ * @req REQ-MIXED-SUPPORT - Support mixed @story/@req/@implements usage in comments
161
146
  */
162
147
  function validateReqAnnotation(context, comment, rawValue, options) {
163
148
  const trimmed = rawValue.trim();
@@ -179,13 +164,47 @@ function validateReqAnnotation(context, comment, rawValue, options) {
179
164
  });
180
165
  }
181
166
  }
167
+ /**
168
+ * Validate an @implements annotation value and report detailed errors when needed.
169
+ *
170
+ * Expected format:
171
+ * @implements <storyPath> <REQ-ID> [<REQ-ID> ...]
172
+ *
173
+ * Validation rules:
174
+ * - Value must include at least a story path and one requirement ID.
175
+ * - Story path must match the same storyPattern used for @story (no auto-fix).
176
+ * - Each subsequent token must match reqPattern and is validated individually.
177
+ *
178
+ * Story path issues are reported with "invalidImplementsFormat" and
179
+ * requirement ID issues reuse the existing "invalidReqFormat" message.
180
+ *
181
+ * @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
182
+ * @req REQ-IMPLEMENTS-PARSE - Parse @implements annotations without affecting @story/@req
183
+ * @req REQ-FORMAT-VALIDATION - Validate @implements story path and requirement IDs
184
+ * @req REQ-MIXED-SUPPORT - Support mixed @story/@req/@implements usage in comments
185
+ */
186
+ function validateImplementsAnnotation(context, comment, rawValue, options) {
187
+ const deps = {
188
+ MIN_IMPLEMENTS_TOKENS: valid_implements_utils_1.MIN_IMPLEMENTS_TOKENS,
189
+ reportMissingImplementsReqIds: valid_implements_utils_1.reportMissingImplementsReqIds,
190
+ reportMissingImplementsValue: valid_implements_utils_1.reportMissingImplementsValue,
191
+ reportInvalidImplementsReqId: valid_implements_utils_1.reportInvalidImplementsReqId,
192
+ reportInvalidImplementsStoryPath: valid_implements_utils_1.reportInvalidImplementsStoryPath,
193
+ };
194
+ (0, valid_implements_utils_1.validateImplementsAnnotationHelper)(deps, context, comment, {
195
+ rawValue,
196
+ options,
197
+ });
198
+ }
182
199
  /**
183
200
  * Finalize and validate the currently pending annotation, if any.
184
201
  *
185
202
  * @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
186
203
  * @story docs/stories/008.0-DEV-AUTO-FIX.story.md
204
+ * @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
187
205
  * @req REQ-SYNTAX-VALIDATION - Validate annotation syntax matches specification
188
206
  * @req REQ-AUTOFIX-FORMAT - Provide safe, minimal automatic fixes for common format issues
207
+ * @req REQ-MIXED-SUPPORT - Support mixed @story/@req/@implements usage in comments
189
208
  */
190
209
  function finalizePendingAnnotation(context, comment, options, pending) {
191
210
  if (!pending) {
@@ -193,8 +212,10 @@ function finalizePendingAnnotation(context, comment, options, pending) {
193
212
  }
194
213
  // @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
195
214
  // @story docs/stories/008.0-DEV-AUTO-FIX.story.md
215
+ // @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
196
216
  // @req REQ-SYNTAX-VALIDATION - Dispatch validation based on annotation type
197
217
  // @req REQ-AUTOFIX-FORMAT - Provide safe, minimal automatic fixes for common format issues
218
+ // @req REQ-MIXED-SUPPORT - Support mixed @story/@req/@implements usage in comments
198
219
  if (pending.type === "story") {
199
220
  validateStoryAnnotation(context, comment, pending.value, options);
200
221
  }
@@ -208,9 +229,13 @@ function finalizePendingAnnotation(context, comment, options, pending) {
208
229
  *
209
230
  * @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
210
231
  * @story docs/stories/008.0-DEV-AUTO-FIX.story.md
232
+ * @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
211
233
  * @req REQ-SYNTAX-VALIDATION - Start new pending annotation when a tag is found
212
234
  * @req REQ-MULTILINE-SUPPORT - Treat subsequent lines as continuation for pending annotation
213
235
  * @req REQ-AUTOFIX-FORMAT - Provide safe, minimal automatic fixes for common format issues
236
+ * @req REQ-IMPLEMENTS-PARSE - Parse @implements annotations without affecting @story/@req
237
+ * @req REQ-FORMAT-VALIDATION - Validate @implements story path and requirement IDs
238
+ * @req REQ-MIXED-SUPPORT - Support mixed @story/@req/@implements usage in comments
214
239
  */
215
240
  function processCommentLine({ normalized, pending, context, comment, options, }) {
216
241
  if (!normalized) {
@@ -218,10 +243,19 @@ function processCommentLine({ normalized, pending, context, comment, options, })
218
243
  }
219
244
  const isStory = /@story\b/.test(normalized);
220
245
  const isReq = /@req\b/.test(normalized);
246
+ const isImplements = /@implements\b/.test(normalized);
247
+ // Handle @implements as an immediate, single-line annotation
248
+ if (isImplements) {
249
+ const implementsValue = normalized.replace(/^@implements\b/, "").trim();
250
+ validateImplementsAnnotation(context, comment, implementsValue, options);
251
+ return pending;
252
+ }
221
253
  // @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
222
254
  // @story docs/stories/008.0-DEV-AUTO-FIX.story.md
255
+ // @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
223
256
  // @req REQ-SYNTAX-VALIDATION - Start new pending annotation when a tag is found
224
257
  // @req REQ-AUTOFIX-FORMAT - Provide safe, minimal automatic fixes for common format issues
258
+ // @req REQ-MIXED-SUPPORT - Support mixed @story/@req/@implements usage in comments
225
259
  if (isStory || isReq) {
226
260
  finalizePendingAnnotation(context, comment, options, pending);
227
261
  const value = normalized.replace(/^@story\b|^@req\b/, "").trim();
@@ -233,8 +267,10 @@ function processCommentLine({ normalized, pending, context, comment, options, })
233
267
  }
234
268
  // @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
235
269
  // @story docs/stories/008.0-DEV-AUTO-FIX.story.md
270
+ // @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
236
271
  // @req REQ-MULTILINE-SUPPORT - Treat subsequent lines as continuation for pending annotation
237
272
  // @req REQ-AUTOFIX-FORMAT - Provide safe, minimal automatic fixes for common format issues
273
+ // @req REQ-MIXED-SUPPORT - Support mixed @story/@req/@implements usage in comments
238
274
  if (pending) {
239
275
  const continuation = normalized.trim();
240
276
  if (!continuation) {
@@ -252,23 +288,30 @@ function processCommentLine({ normalized, pending, context, comment, options, })
252
288
  return pending;
253
289
  }
254
290
  /**
255
- * Process a single comment node and validate any @story/@req annotations it contains.
291
+ * Process a single comment node and validate any @story/@req/@implements annotations it contains.
256
292
  *
257
- * Supports annotations whose values span multiple lines within the same
293
+ * Supports @story and @req annotations whose values span multiple lines within the same
258
294
  * comment block, collapsing whitespace so that the logical value can be
259
295
  * validated against the configured patterns.
260
296
  *
297
+ * @implements annotations are validated immediately per-line and are not
298
+ * accumulated into pending multi-line state.
299
+ *
261
300
  * @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
262
301
  * @story docs/stories/008.0-DEV-AUTO-FIX.story.md
302
+ * @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
263
303
  * @req REQ-MULTILINE-SUPPORT - Handle annotations split across multiple lines
264
304
  * @req REQ-FLEXIBLE-PARSING - Support reasonable variations in whitespace and formatting
265
305
  * @req REQ-AUTOFIX-FORMAT - Provide safe, minimal automatic fixes for common format issues
306
+ * @req REQ-IMPLEMENTS-PARSE - Parse @implements annotations without affecting @story/@req
307
+ * @req REQ-FORMAT-VALIDATION - Validate @implements story path and requirement IDs
308
+ * @req REQ-MIXED-SUPPORT - Support mixed @story/@req/@implements usage in comments
266
309
  */
267
310
  function processComment(context, comment, options) {
268
311
  const rawLines = (comment.value || "").split(/\r?\n/);
269
312
  let pending = null;
270
313
  rawLines.forEach((rawLine) => {
271
- const normalized = normalizeCommentLine(rawLine);
314
+ const normalized = (0, valid_annotation_format_internal_1.normalizeCommentLine)(rawLine);
272
315
  pending = processCommentLine({
273
316
  normalized,
274
317
  pending,
@@ -283,7 +326,7 @@ exports.default = {
283
326
  meta: {
284
327
  type: "problem",
285
328
  docs: {
286
- description: "Validate format and syntax of @story and @req annotations",
329
+ description: "Validate format and syntax of @story, @req, and @implements annotations",
287
330
  recommended: "error",
288
331
  },
289
332
  messages: {
@@ -301,6 +344,14 @@ exports.default = {
301
344
  * @req REQ-ERROR-CONSISTENCY - Use shared "Invalid annotation format: {{details}}." message pattern across rules
302
345
  */
303
346
  invalidReqFormat: "Invalid annotation format: {{details}}.",
347
+ /**
348
+ * @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
349
+ * @req REQ-ERROR-SPECIFIC - Provide specific details about invalid @implements annotation format
350
+ * @req REQ-ERROR-CONTEXT - Include human-readable details about the expected @implements annotation format
351
+ * @req REQ-ERROR-CONSISTENCY - Use shared "Invalid annotation format: {{details}}." message pattern across rules
352
+ * @req REQ-FORMAT-VALIDATION - Validate @implements story path and requirement IDs
353
+ */
354
+ invalidImplementsFormat: "Invalid annotation format: {{details}}.",
304
355
  /**
305
356
  * @story docs/stories/010.1-DEV-CONFIGURABLE-PATTERNS.story.md
306
357
  * @req REQ-REGEX-VALIDATION - Surface configuration errors for invalid regex patterns
@@ -326,11 +377,15 @@ exports.default = {
326
377
  * @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
327
378
  * @story docs/stories/008.0-DEV-AUTO-FIX.story.md
328
379
  * @story docs/stories/010.1-DEV-CONFIGURABLE-PATTERNS.story.md
380
+ * @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
329
381
  * @req REQ-SYNTAX-VALIDATION - Ensure rule create function validates annotations syntax
330
382
  * @req REQ-FORMAT-SPECIFICATION - Implement formatting checks per specification
331
383
  * @req REQ-AUTOFIX-FORMAT - Provide safe, minimal automatic fixes for common format issues
332
384
  * @req REQ-REGEX-VALIDATION - Derive validation regexes from shared options helper
333
385
  * @req REQ-BACKWARD-COMP - Fall back to default patterns and continue validation on config errors
386
+ * @req REQ-IMPLEMENTS-PARSE - Parse @implements annotations without affecting @story/@req
387
+ * @req REQ-FORMAT-VALIDATION - Validate @implements story path and requirement IDs
388
+ * @req REQ-MIXED-SUPPORT - Support mixed @story/@req/@implements usage in comments
334
389
  */
335
390
  create(context) {
336
391
  const sourceCode = context.getSourceCode();
@@ -338,16 +393,20 @@ exports.default = {
338
393
  const optionErrors = (0, valid_annotation_options_1.getOptionErrors)();
339
394
  return {
340
395
  /**
341
- * Program-level handler that inspects all comments for @story and @req tags
396
+ * Program-level handler that inspects all comments for @story, @req, and @implements tags
342
397
  *
343
398
  * @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
344
399
  * @story docs/stories/008.0-DEV-AUTO-FIX.story.md
345
400
  * @story docs/stories/010.1-DEV-CONFIGURABLE-PATTERNS.story.md
401
+ * @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
346
402
  * @req REQ-PATH-FORMAT - Validate @story paths follow expected patterns
347
403
  * @req REQ-REQ-FORMAT - Validate @req identifiers follow expected patterns
348
404
  * @req REQ-AUTOFIX-FORMAT - Provide safe, minimal automatic fixes for common format issues
349
405
  * @req REQ-REGEX-VALIDATION - Surface regex configuration errors without blocking validation
350
406
  * @req REQ-BACKWARD-COMP - Continue validating comments using default patterns on error
407
+ * @req REQ-IMPLEMENTS-PARSE - Parse @implements annotations without affecting @story/@req
408
+ * @req REQ-FORMAT-VALIDATION - Validate @implements story path and requirement IDs
409
+ * @req REQ-MIXED-SUPPORT - Support mixed @story/@req/@implements usage in comments
351
410
  */
352
411
  Program(node) {
353
412
  if (optionErrors && optionErrors.length > 0) {