eslint-plugin-traceability 1.10.1 → 1.11.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 (70) hide show
  1. package/CHANGELOG.md +2 -2
  2. package/README.md +3 -2
  3. package/lib/src/maintenance/cli.js +12 -12
  4. package/lib/src/maintenance/detect.js +19 -19
  5. package/lib/src/maintenance/flags.js +111 -25
  6. package/lib/src/rules/helpers/require-story-core.d.ts +55 -9
  7. package/lib/src/rules/helpers/require-story-core.js +85 -62
  8. package/lib/src/rules/helpers/require-story-helpers.d.ts +27 -48
  9. package/lib/src/rules/helpers/require-story-helpers.js +154 -116
  10. package/lib/src/rules/helpers/require-story-io.js +51 -31
  11. package/lib/src/rules/helpers/require-story-visitors.js +47 -6
  12. package/lib/src/rules/helpers/valid-annotation-format-validators.js +5 -1
  13. package/lib/src/rules/helpers/valid-annotation-options.d.ts +9 -0
  14. package/lib/src/rules/helpers/valid-annotation-options.js +67 -20
  15. package/lib/src/rules/helpers/valid-annotation-utils.js +31 -31
  16. package/lib/src/rules/helpers/valid-story-reference-helpers.js +19 -19
  17. package/lib/src/rules/prefer-implements-annotation.js +29 -1
  18. package/lib/src/rules/require-story-annotation.js +15 -0
  19. package/lib/src/rules/require-test-traceability.js +1 -6
  20. package/lib/src/utils/annotation-checker.js +32 -8
  21. package/lib/src/utils/reqAnnotationDetection.js +36 -22
  22. package/lib/tests/cli-error-handling.test.js +1 -0
  23. package/lib/tests/config/eslint-config-validation.test.d.ts +8 -0
  24. package/lib/tests/config/eslint-config-validation.test.js +8 -0
  25. package/lib/tests/config/flat-config-presets-integration.test.js +1 -3
  26. package/lib/tests/config/require-story-annotation-config.test.d.ts +9 -0
  27. package/lib/tests/config/require-story-annotation-config.test.js +9 -0
  28. package/lib/tests/integration/cli-integration.test.js +9 -1
  29. package/lib/tests/maintenance/batch.test.js +1 -0
  30. package/lib/tests/maintenance/cli.test.js +1 -0
  31. package/lib/tests/maintenance/detect-isolated.test.js +1 -0
  32. package/lib/tests/maintenance/detect.test.js +1 -0
  33. package/lib/tests/maintenance/index.test.js +1 -0
  34. package/lib/tests/maintenance/report.test.js +1 -0
  35. package/lib/tests/maintenance/update-isolated.test.js +1 -0
  36. package/lib/tests/maintenance/update.test.js +1 -0
  37. package/lib/tests/perf/maintenance-cli-large-workspace.test.d.ts +1 -0
  38. package/lib/tests/perf/maintenance-cli-large-workspace.test.js +130 -0
  39. package/lib/tests/perf/maintenance-large-workspace.test.d.ts +1 -0
  40. package/lib/tests/perf/maintenance-large-workspace.test.js +149 -0
  41. package/lib/tests/plugin-default-export-and-configs.test.js +2 -0
  42. package/lib/tests/plugin-setup-error.test.d.ts +1 -0
  43. package/lib/tests/plugin-setup-error.test.js +1 -0
  44. package/lib/tests/plugin-setup.test.js +1 -1
  45. package/lib/tests/rules/auto-fix-behavior-008.test.js +39 -0
  46. package/lib/tests/rules/error-reporting.test.js +1 -0
  47. package/lib/tests/rules/prefer-implements-annotation.test.js +8 -0
  48. package/lib/tests/rules/require-branch-annotation.test.js +2 -0
  49. package/lib/tests/rules/require-story-core-edgecases.test.js +1 -0
  50. package/lib/tests/rules/require-story-core.autofix.test.js +10 -3
  51. package/lib/tests/rules/require-story-core.test.js +14 -7
  52. package/lib/tests/rules/require-story-helpers-edgecases.test.d.ts +1 -0
  53. package/lib/tests/rules/require-story-helpers-edgecases.test.js +2 -1
  54. package/lib/tests/rules/require-story-helpers.test.js +18 -11
  55. package/lib/tests/rules/require-story-io-behavior.test.d.ts +1 -0
  56. package/lib/tests/rules/require-story-io-behavior.test.js +1 -0
  57. package/lib/tests/rules/require-story-io.edgecases.test.d.ts +1 -0
  58. package/lib/tests/rules/require-story-io.edgecases.test.js +1 -0
  59. package/lib/tests/rules/require-story-visitors-edgecases.test.d.ts +1 -0
  60. package/lib/tests/rules/require-story-visitors-edgecases.test.js +1 -0
  61. package/lib/tests/rules/valid-story-reference.test.js +2 -0
  62. package/lib/tests/utils/annotation-checker.test.js +2 -1
  63. package/lib/tests/utils/branch-annotation-helpers.test.js +2 -1
  64. package/lib/tests/utils/require-story-core-test-helpers.d.ts +1 -1
  65. package/lib/tests/utils/require-story-core-test-helpers.js +16 -16
  66. package/lib/tests/utils/temp-dir-helpers.js +1 -1
  67. package/package.json +9 -2
  68. package/user-docs/api-reference.md +123 -12
  69. package/user-docs/examples.md +41 -0
  70. package/user-docs/migration-guide.md +36 -3
@@ -133,42 +133,32 @@ function parentChainHasStory(sourceCode, node) {
133
133
  return false;
134
134
  }
135
135
  /**
136
- * Fallback: inspect text immediately preceding the node in sourceCode.getText to find @story
137
- * Also accepts @supports annotations as satisfying story presence for this rule.
136
+ * Safely compute the starting range index for fallback text scanning.
137
+ * Centralizes guards around sourceCode.getText and node.range structure.
138
138
  * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
139
- * @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
140
- * @req REQ-ANNOTATION-REQUIRED - Provide fallback textual inspection when other heuristics fail
141
- * @req REQ-REQUIRE-ACCEPTS-IMPLEMENTS - Treat @supports annotations as satisfying story presence in fallback checks
139
+ * @req REQ-ANNOTATION-REQUIRED - Centralize guards for fallback range computation
142
140
  */
143
- function fallbackTextBeforeHasStory(sourceCode, node) {
144
- // Skip fallback text inspection when the sourceCode API or node range information is not available.
145
- // @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
146
- // @req REQ-ANNOTATION-REQUIRED - Avoid throwing when source text or range metadata cannot be read
147
- if (typeof sourceCode?.getText !== "function" ||
148
- !Array.isArray((node && node.range) || [])) {
149
- return false;
141
+ function getFallbackRangeStart(sourceCode, node) {
142
+ if (typeof sourceCode?.getText !== "function") {
143
+ return null;
150
144
  }
151
- const range = node.range;
152
- // Guard against malformed range values that cannot provide a numeric start index for slicing.
153
- // @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
154
- // @req REQ-ANNOTATION-REQUIRED - Validate node range structure before computing fallback window
145
+ const range = (node && node.range) || null;
155
146
  if (!Array.isArray(range) || typeof range[0] !== "number") {
156
- return false;
147
+ return null;
157
148
  }
149
+ return range[0];
150
+ }
151
+ /**
152
+ * Safely slice a bounded fallback text window immediately preceding the node start index.
153
+ * Restricts scanning to a fixed-size window and treats IO/slicing failures as non-fatal.
154
+ * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
155
+ * @req REQ-ANNOTATION-REQUIRED - Restrict fallback text scanning to a safe, fixed-size window and handle failures gracefully
156
+ */
157
+ function getFallbackTextWindow(sourceCode, nodeStartIndex) {
158
+ const start = Math.max(0, nodeStartIndex - exports.FALLBACK_WINDOW);
158
159
  try {
159
- // Limit the fallback inspection window to a bounded region immediately preceding the node.
160
- // @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
161
- // @req REQ-ANNOTATION-REQUIRED - Restrict fallback text scanning to a safe, fixed-size window
162
- const start = Math.max(0, range[0] - exports.FALLBACK_WINDOW);
163
- const textBefore = sourceCode.getText().slice(start, range[0]);
164
- // Detect any @story or @supports marker that appears within the bounded fallback window.
165
- // @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
166
- // @req REQ-ANNOTATION-REQUIRED - Recognize story annotations discovered via fallback text scanning
167
- // @req REQ-REQUIRE-ACCEPTS-IMPLEMENTS - Recognize @supports annotations discovered via fallback text scanning
168
- if (typeof textBefore === "string" &&
169
- (textBefore.includes("@story") || textBefore.includes("@supports"))) {
170
- return true;
171
- }
160
+ const textBefore = sourceCode.getText().slice(start, nodeStartIndex);
161
+ return typeof textBefore === "string" ? textBefore : null;
172
162
  }
173
163
  catch {
174
164
  /*
@@ -176,6 +166,36 @@ function fallbackTextBeforeHasStory(sourceCode, node) {
176
166
  * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
177
167
  * @req REQ-ANNOTATION-REQUIRED - Treat fallback text inspection failures as "no annotation" instead of raising
178
168
  */
169
+ return null;
179
170
  }
180
- return false;
171
+ }
172
+ /**
173
+ * Detect whether the provided fallback text window contains a story marker.
174
+ * Recognizes both @story and @supports annotations in the inspected text.
175
+ * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
176
+ * @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
177
+ * @req REQ-ANNOTATION-REQUIRED - Recognize story annotations discovered via fallback text scanning
178
+ * @req REQ-REQUIRE-ACCEPTS-IMPLEMENTS - Recognize @supports annotations discovered via fallback text scanning
179
+ */
180
+ function fallbackTextHasMarker(textBefore) {
181
+ if (typeof textBefore !== "string") {
182
+ return false;
183
+ }
184
+ return textBefore.includes("@story") || textBefore.includes("@supports");
185
+ }
186
+ /**
187
+ * Fallback: inspect text immediately preceding the node in sourceCode.getText to find @story
188
+ * Also accepts @supports annotations as satisfying story presence for this rule.
189
+ * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
190
+ * @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
191
+ * @req REQ-ANNOTATION-REQUIRED - Provide fallback textual inspection when other heuristics fail
192
+ * @req REQ-REQUIRE-ACCEPTS-IMPLEMENTS - Treat @supports annotations as satisfying story presence in fallback checks
193
+ */
194
+ function fallbackTextBeforeHasStory(sourceCode, node) {
195
+ const nodeStartIndex = getFallbackRangeStart(sourceCode, node);
196
+ if (nodeStartIndex === null) {
197
+ return false;
198
+ }
199
+ const textBefore = getFallbackTextWindow(sourceCode, nodeStartIndex);
200
+ return fallbackTextHasMarker(textBefore);
181
201
  }
@@ -27,7 +27,14 @@ function buildFunctionDeclarationVisitor(context, sourceCode, options) {
27
27
  if (!options.shouldProcessNode(node))
28
28
  return;
29
29
  const target = (0, require_story_helpers_1.resolveTargetNode)(sourceCode, node);
30
- (0, require_story_helpers_1.reportMissing)(context, sourceCode, node, target);
30
+ (0, require_story_helpers_1.reportMissing)(context, sourceCode, {
31
+ node,
32
+ target,
33
+ options: {
34
+ annotationTemplateOverride: options.annotationTemplate,
35
+ autoFixToggle: options.autoFix,
36
+ },
37
+ });
31
38
  }
32
39
  return {
33
40
  FunctionDeclaration: handleFunctionDeclaration,
@@ -55,7 +62,14 @@ function buildFunctionExpressionVisitor(context, sourceCode, options) {
55
62
  if (node.parent && node.parent.type === "MethodDefinition")
56
63
  return;
57
64
  const target = (0, require_story_helpers_1.resolveTargetNode)(sourceCode, node);
58
- (0, require_story_helpers_1.reportMissing)(context, sourceCode, node, target);
65
+ (0, require_story_helpers_1.reportMissing)(context, sourceCode, {
66
+ node,
67
+ target,
68
+ options: {
69
+ annotationTemplateOverride: options.annotationTemplate,
70
+ autoFixToggle: options.autoFix,
71
+ },
72
+ });
59
73
  }
60
74
  return {
61
75
  FunctionExpression: handleFunctionExpression,
@@ -76,7 +90,14 @@ function buildArrowFunctionVisitor(context, sourceCode, options) {
76
90
  if (!options.shouldProcessNode(node))
77
91
  return;
78
92
  const target = (0, require_story_helpers_1.resolveTargetNode)(sourceCode, node);
79
- (0, require_story_helpers_1.reportMissing)(context, sourceCode, node, target);
93
+ (0, require_story_helpers_1.reportMissing)(context, sourceCode, {
94
+ node,
95
+ target,
96
+ options: {
97
+ annotationTemplateOverride: options.annotationTemplate,
98
+ autoFixToggle: options.autoFix,
99
+ },
100
+ });
80
101
  }
81
102
  return {
82
103
  ArrowFunctionExpression: handleArrowFunctionExpression,
@@ -96,7 +117,14 @@ function buildTSDeclareFunctionVisitor(context, sourceCode, options) {
96
117
  function handleTSDeclareFunction(node) {
97
118
  if (!options.shouldProcessNode(node))
98
119
  return;
99
- (0, require_story_helpers_1.reportMissing)(context, sourceCode, node, node);
120
+ (0, require_story_helpers_1.reportMissing)(context, sourceCode, {
121
+ node,
122
+ target: node,
123
+ options: {
124
+ annotationTemplateOverride: options.annotationTemplate,
125
+ autoFixToggle: options.autoFix,
126
+ },
127
+ });
100
128
  }
101
129
  return {
102
130
  TSDeclareFunction: handleTSDeclareFunction,
@@ -117,7 +145,14 @@ function buildTSMethodSignatureVisitor(context, sourceCode, options) {
117
145
  if (!options.shouldProcessNode(node))
118
146
  return;
119
147
  const target = (0, require_story_helpers_1.resolveTargetNode)(sourceCode, node);
120
- (0, require_story_helpers_1.reportMissing)(context, sourceCode, node, target);
148
+ (0, require_story_helpers_1.reportMissing)(context, sourceCode, {
149
+ node,
150
+ target,
151
+ options: {
152
+ annotationTemplateOverride: options.methodAnnotationTemplate ?? options.annotationTemplate,
153
+ autoFixToggle: options.autoFix,
154
+ },
155
+ });
121
156
  }
122
157
  return {
123
158
  TSMethodSignature: handleTSMethodSignature,
@@ -137,7 +172,13 @@ function buildMethodDefinitionVisitor(context, sourceCode, options) {
137
172
  function handleMethodDefinition(node) {
138
173
  if (!options.shouldProcessNode(node))
139
174
  return;
140
- (0, require_story_helpers_1.reportMethod)(context, sourceCode, node);
175
+ (0, require_story_helpers_1.reportMethod)(context, sourceCode, {
176
+ node,
177
+ options: {
178
+ annotationTemplateOverride: options.methodAnnotationTemplate ?? options.annotationTemplate,
179
+ autoFixToggle: options.autoFix,
180
+ },
181
+ });
141
182
  }
142
183
  return {
143
184
  MethodDefinition: handleMethodDefinition,
@@ -167,7 +167,11 @@ function validateStoryAnnotation(context, comment, rawValue, options) {
167
167
  // @story docs/stories/008.0-DEV-AUTO-FIX.story.md
168
168
  // @req REQ-AUTOFIX-FORMAT - Apply suffix-only auto-fix when it yields a pattern-compliant path
169
169
  if (fixed && pathPattern.test(fixed)) {
170
- reportInvalidStoryFormatWithFix(context, comment, collapsed, fixed);
170
+ if (options.autoFix !== false) {
171
+ reportInvalidStoryFormatWithFix(context, comment, collapsed, fixed);
172
+ return;
173
+ }
174
+ reportInvalidStoryFormat(context, comment, collapsed, options);
171
175
  return;
172
176
  }
173
177
  reportInvalidStoryFormat(context, comment, collapsed, options);
@@ -53,6 +53,11 @@ export interface AnnotationRuleOptions {
53
53
  * Human-readable example requirement ID used in error messages.
54
54
  */
55
55
  requirementIdExample?: string;
56
+ /**
57
+ * Global toggle for auto-fix behavior in valid-annotation-format.
58
+ * When false, no automatic suffix-normalization fixes are applied.
59
+ */
60
+ autoFix?: boolean;
56
61
  }
57
62
  /**
58
63
  * Resolved, runtime-ready options for the rule.
@@ -62,6 +67,7 @@ export interface ResolvedAnnotationOptions {
62
67
  storyExample: string;
63
68
  reqPattern: RegExp;
64
69
  reqExample: string;
70
+ autoFix: boolean;
65
71
  }
66
72
  export declare function getDefaultReqExample(): string;
67
73
  export declare function getResolvedDefaults(): ResolvedAnnotationOptions;
@@ -113,6 +119,9 @@ export declare function getRuleSchema(): {
113
119
  requirementIdExample: {
114
120
  type: string;
115
121
  };
122
+ autoFix: {
123
+ type: string;
124
+ };
116
125
  };
117
126
  additionalProperties: boolean;
118
127
  }[];
@@ -26,6 +26,7 @@ let resolvedDefaults = {
26
26
  storyExample: getDefaultStoryExample(),
27
27
  reqPattern: getDefaultReqPattern(),
28
28
  reqExample: getDefaultReqExample(),
29
+ autoFix: true,
29
30
  };
30
31
  /**
31
32
  * Collected configuration errors encountered while resolving options.
@@ -104,43 +105,88 @@ function resolveExample(nestedExample, flatExample, defaultExample) {
104
105
  }
105
106
  return defaultExample;
106
107
  }
107
- /**
108
- * Resolve user options into concrete, validated configuration.
109
- * Falls back to existing defaults when options are not provided or invalid.
110
- */
111
- function resolveOptions(rawOptions) {
112
- optionErrors = [];
113
- const user = normalizeUserOptions(rawOptions);
114
- const nestedStoryPattern = user?.story?.pattern;
115
- const flatStoryPattern = user?.storyPathPattern;
116
- const nestedStoryExample = user?.story?.example;
117
- const flatStoryExample = user?.storyPathExample;
118
- const nestedReqPattern = user?.req?.pattern;
119
- const flatReqPattern = user?.requirementIdPattern;
120
- const nestedReqExample = user?.req?.example;
121
- const flatReqExample = user?.requirementIdExample;
122
- const storyPattern = resolvePattern({
108
+ function getUserOptions(rawOptions) {
109
+ return normalizeUserOptions(rawOptions);
110
+ }
111
+ function resolveAutoFixFlag(user) {
112
+ const autoFixFlag = user?.autoFix;
113
+ return typeof autoFixFlag === "boolean" ? autoFixFlag : true;
114
+ }
115
+ function resolveStoryPattern(nestedStoryPattern, flatStoryPattern) {
116
+ return resolvePattern({
123
117
  nestedPattern: nestedStoryPattern,
124
118
  nestedFieldName: "story.pattern",
125
119
  flatPattern: flatStoryPattern,
126
120
  flatFieldName: "storyPathPattern",
127
121
  defaultPattern: getDefaultStoryPattern(),
128
122
  });
129
- const reqPattern = resolvePattern({
123
+ }
124
+ function resolveReqPattern(nestedReqPattern, flatReqPattern) {
125
+ return resolvePattern({
130
126
  nestedPattern: nestedReqPattern,
131
127
  nestedFieldName: "req.pattern",
132
128
  flatPattern: flatReqPattern,
133
129
  flatFieldName: "requirementIdPattern",
134
130
  defaultPattern: getDefaultReqPattern(),
135
131
  });
136
- const storyExample = resolveExample(nestedStoryExample, flatStoryExample, getDefaultStoryExample());
137
- const reqExample = resolveExample(nestedReqExample, flatReqExample, getDefaultReqExample());
138
- resolvedDefaults = {
132
+ }
133
+ function resolveStoryExample(nestedStoryExample, flatStoryExample) {
134
+ return resolveExample(nestedStoryExample, flatStoryExample, getDefaultStoryExample());
135
+ }
136
+ function resolveReqExample(nestedReqExample, flatReqExample) {
137
+ return resolveExample(nestedReqExample, flatReqExample, getDefaultReqExample());
138
+ }
139
+ function getStoryPatternInputs(user) {
140
+ return {
141
+ nestedStoryPattern: user?.story?.pattern,
142
+ flatStoryPattern: user?.storyPathPattern,
143
+ };
144
+ }
145
+ function getStoryExampleInputs(user) {
146
+ return {
147
+ nestedStoryExample: user?.story?.example,
148
+ flatStoryExample: user?.storyPathExample,
149
+ };
150
+ }
151
+ function getReqPatternInputs(user) {
152
+ return {
153
+ nestedReqPattern: user?.req?.pattern,
154
+ flatReqPattern: user?.requirementIdPattern,
155
+ };
156
+ }
157
+ function getReqExampleInputs(user) {
158
+ return {
159
+ nestedReqExample: user?.req?.example,
160
+ flatReqExample: user?.requirementIdExample,
161
+ };
162
+ }
163
+ function resolveOptionsInternal(user) {
164
+ const { nestedStoryPattern, flatStoryPattern } = getStoryPatternInputs(user);
165
+ const { nestedStoryExample, flatStoryExample } = getStoryExampleInputs(user);
166
+ const { nestedReqPattern, flatReqPattern } = getReqPatternInputs(user);
167
+ const { nestedReqExample, flatReqExample } = getReqExampleInputs(user);
168
+ const autoFix = resolveAutoFixFlag(user);
169
+ const storyPattern = resolveStoryPattern(nestedStoryPattern, flatStoryPattern);
170
+ const reqPattern = resolveReqPattern(nestedReqPattern, flatReqPattern);
171
+ const storyExample = resolveStoryExample(nestedStoryExample, flatStoryExample);
172
+ const reqExample = resolveReqExample(nestedReqExample, flatReqExample);
173
+ return {
139
174
  storyPattern,
140
175
  storyExample,
141
176
  reqPattern,
142
177
  reqExample,
178
+ autoFix,
143
179
  };
180
+ }
181
+ /**
182
+ * Resolve user options into concrete, validated configuration.
183
+ * Falls back to existing defaults when options are not provided or invalid.
184
+ */
185
+ function resolveOptions(rawOptions) {
186
+ optionErrors = [];
187
+ const user = getUserOptions(rawOptions);
188
+ const resolved = resolveOptionsInternal(user);
189
+ resolvedDefaults = resolved;
144
190
  return resolvedDefaults;
145
191
  }
146
192
  /**
@@ -171,6 +217,7 @@ function getRuleSchema() {
171
217
  storyPathExample: { type: "string" },
172
218
  requirementIdPattern: { type: "string" },
173
219
  requirementIdExample: { type: "string" },
220
+ autoFix: { type: "boolean" },
174
221
  },
175
222
  additionalProperties: false,
176
223
  },
@@ -39,7 +39,7 @@ exports.STORY_EXAMPLE_PATH = "docs/stories/005.0-DEV-EXAMPLE.story.md";
39
39
  * @req REQ-AUTOFIX-FORMAT - Provide safe, minimal automatic fixes for common format issues
40
40
  */
41
41
  function collapseAnnotationValue(value) {
42
- // @implements docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md REQ-MULTILINE-SUPPORT
42
+ // @supports docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md REQ-MULTILINE-SUPPORT
43
43
  return value.replace(/\s+/g, "");
44
44
  }
45
45
  /**
@@ -62,42 +62,42 @@ function collapseAnnotationValue(value) {
62
62
  */
63
63
  function getFixedStoryPath(original) {
64
64
  // @story docs/stories/010.1-REQ-STORY-PATH-STRICTNESS.story.md | REQ-AUTOFIX-SAFE - Reject auto-fix when the path contains ".." traversal segments to avoid broadening the reference.
65
- // @implements docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md REQ-REQ-FORMAT REQ-ERROR-SPECIFICITY - Enforces correctness of the story identifier by rejecting paths that use unsafe traversal segments.
65
+ // @supports docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md REQ-REQ-FORMAT REQ-ERROR-SPECIFICITY - Enforces correctness of the story identifier by rejecting paths that use unsafe traversal segments.
66
66
  if (original.includes("..")) {
67
- // @implements docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md REQ-REQ-FORMAT
68
- // @implements docs/stories/008.0-DEV-AUTO-FIX.story.md REQ-AUTOFIX-SAFE
69
- // @implements docs/stories/010.1-REQ-STORY-PATH-STRICTNESS.story.md REQ-AUTOFIX-SAFE
67
+ // @supports docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md REQ-REQ-FORMAT
68
+ // @supports docs/stories/008.0-DEV-AUTO-FIX.story.md REQ-AUTOFIX-SAFE
69
+ // @supports docs/stories/010.1-REQ-STORY-PATH-STRICTNESS.story.md REQ-AUTOFIX-SAFE
70
70
  return null;
71
71
  }
72
72
  // @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md | REQ-AUTOFIX-FORMAT - Leave correctly formatted ".story.md" paths unchanged so diagnostics are not hidden by redundant fixes.
73
- // @implements docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md REQ-REQ-FORMAT REQ-ERROR-SPECIFICITY - Enforces correctness of the story identifier by recognizing already valid ".story.md" paths without altering them.
73
+ // @supports docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md REQ-REQ-FORMAT REQ-ERROR-SPECIFICITY - Enforces correctness of the story identifier by recognizing already valid ".story.md" paths without altering them.
74
74
  if (/\.story\.md$/.test(original)) {
75
- // @implements docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md REQ-REQ-FORMAT
76
- // @implements docs/stories/008.0-DEV-AUTO-FIX.story.md REQ-AUTOFIX-FORMAT
77
- // @implements docs/stories/010.1-REQ-STORY-PATH-STRICTNESS.story.md REQ-AUTOFIX-FORMAT
75
+ // @supports docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md REQ-REQ-FORMAT
76
+ // @supports docs/stories/008.0-DEV-AUTO-FIX.story.md REQ-AUTOFIX-FORMAT
77
+ // @supports docs/stories/010.1-REQ-STORY-PATH-STRICTNESS.story.md REQ-AUTOFIX-FORMAT
78
78
  return null;
79
79
  }
80
80
  // @story docs/stories/008.0-DEV-AUTO-FIX.story.md | REQ-AUTOFIX-FORMAT REQ-AUTOFIX-PRESERVE - When ".story" is present but ".md" is missing, append only the extension without altering the base path.
81
- // @implements docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md REQ-REQ-FORMAT REQ-ERROR-SPECIFICITY - Enforces correctness of the story identifier by completing a partially correct ".story" suffix to the canonical ".story.md".
81
+ // @supports docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md REQ-REQ-FORMAT REQ-ERROR-SPECIFICITY - Enforces correctness of the story identifier by completing a partially correct ".story" suffix to the canonical ".story.md".
82
82
  if (/\.story$/.test(original)) {
83
- // @implements docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md REQ-REQ-FORMAT
84
- // @implements docs/stories/008.0-DEV-AUTO-FIX.story.md REQ-AUTOFIX-FORMAT REQ-AUTOFIX-PRESERVE
85
- // @implements docs/stories/010.2-REQ-STORY-PATH-AUTOFIX.story.md REQ-AUTOFIX-FORMAT
83
+ // @supports docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md REQ-REQ-FORMAT
84
+ // @supports docs/stories/008.0-DEV-AUTO-FIX.story.md REQ-AUTOFIX-FORMAT REQ-AUTOFIX-PRESERVE
85
+ // @supports docs/stories/010.2-REQ-STORY-PATH-AUTOFIX.story.md REQ-AUTOFIX-FORMAT
86
86
  return `${original}.md`;
87
87
  }
88
88
  // @story docs/stories/010.2-REQ-STORY-PATH-AUTOFIX.story.md | REQ-AUTOFIX-FORMAT REQ-AUTOFIX-PRESERVE - Normalize plain ".md" doc paths to ".story.md" while keeping the rest of the path intact.
89
- // @implements docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md REQ-REQ-FORMAT REQ-ERROR-SPECIFICITY - Enforces correctness of the story identifier by transforming generic ".md" references into canonical ".story.md" story paths.
89
+ // @supports docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md REQ-REQ-FORMAT REQ-ERROR-SPECIFICITY - Enforces correctness of the story identifier by transforming generic ".md" references into canonical ".story.md" story paths.
90
90
  if (/\.md$/.test(original)) {
91
- // @implements docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md REQ-REQ-FORMAT
92
- // @implements docs/stories/008.0-DEV-AUTO-FIX.story.md REQ-AUTOFIX-FORMAT REQ-AUTOFIX-PRESERVE
93
- // @implements docs/stories/010.2-REQ-STORY-PATH-AUTOFIX.story.md REQ-AUTOFIX-FORMAT
91
+ // @supports docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md REQ-REQ-FORMAT
92
+ // @supports docs/stories/008.0-DEV-AUTO-FIX.story.md REQ-AUTOFIX-FORMAT REQ-AUTOFIX-PRESERVE
93
+ // @supports docs/stories/010.2-REQ-STORY-PATH-AUTOFIX.story.md REQ-AUTOFIX-FORMAT
94
94
  return original.replace(/\.md$/, ".story.md");
95
95
  }
96
96
  // @story docs/stories/010.2-REQ-STORY-PATH-AUTOFIX.story.md | REQ-AUTOFIX-FORMAT REQ-AUTOFIX-PRESERVE REQ-AUTOFIX-SAFE - For bare paths with no extension, append ".story.md" as a canonical story reference without touching the directory.
97
- // @implements docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md REQ-REQ-FORMAT REQ-ERROR-SPECIFICITY - Enforces presence and correctness of the story identifier by supplying the standard ".story.md" suffix when no extension is provided.
98
- // @implements docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md REQ-REQ-FORMAT
99
- // @implements docs/stories/008.0-DEV-AUTO-FIX.story.md REQ-AUTOFIX-FORMAT REQ-AUTOFIX-PRESERVE REQ-AUTOFIX-SAFE
100
- // @implements docs/stories/010.2-REQ-STORY-PATH-AUTOFIX.story.md REQ-AUTOFIX-FORMAT
97
+ // @supports docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md REQ-REQ-FORMAT REQ-ERROR-SPECIFICITY - Enforces presence and correctness of the story identifier by supplying the standard ".story.md" suffix when no extension is provided.
98
+ // @supports docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md REQ-REQ-FORMAT
99
+ // @supports docs/stories/008.0-DEV-AUTO-FIX.story.md REQ-AUTOFIX-FORMAT REQ-AUTOFIX-PRESERVE REQ-AUTOFIX-SAFE
100
+ // @supports docs/stories/010.2-REQ-STORY-PATH-AUTOFIX.story.md REQ-AUTOFIX-FORMAT
101
101
  return `${original}.story.md`;
102
102
  }
103
103
  /**
@@ -112,14 +112,14 @@ function getFixedStoryPath(original) {
112
112
  function buildStoryErrorMessage(kind, value, options) {
113
113
  const example = options.storyExample || exports.STORY_EXAMPLE_PATH;
114
114
  // @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md | REQ-ERROR-SPECIFICITY - Use a dedicated message variant when the @story value is completely missing.
115
- // @implements docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md REQ-REQ-FORMAT REQ-ERROR-SPECIFICITY - Enforces presence of the story identifier by emitting a targeted message when the @story value is absent.
115
+ // @supports docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md REQ-REQ-FORMAT REQ-ERROR-SPECIFICITY - Enforces presence of the story identifier by emitting a targeted message when the @story value is absent.
116
116
  if (kind === "missing") {
117
- // @implements docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md REQ-ERROR-SPECIFICITY
118
- // @implements docs/stories/010.1-REQ-STORY-PATH-STRICTNESS.story.md REQ-ERROR-SPECIFICITY
117
+ // @supports docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md REQ-ERROR-SPECIFICITY
118
+ // @supports docs/stories/010.1-REQ-STORY-PATH-STRICTNESS.story.md REQ-ERROR-SPECIFICITY
119
119
  return `Missing story path for @story annotation. Expected a path like "${example}".`;
120
120
  }
121
- // @implements docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md REQ-ERROR-SPECIFICITY
122
- // @implements docs/stories/010.1-REQ-STORY-PATH-STRICTNESS.story.md REQ-ERROR-SPECIFICITY
121
+ // @supports docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md REQ-ERROR-SPECIFICITY
122
+ // @supports docs/stories/010.1-REQ-STORY-PATH-STRICTNESS.story.md REQ-ERROR-SPECIFICITY
123
123
  return `Invalid story path "${value ?? ""}" for @story annotation. Expected a path like "${example}".`;
124
124
  }
125
125
  /**
@@ -134,13 +134,13 @@ function buildStoryErrorMessage(kind, value, options) {
134
134
  function buildReqErrorMessage(kind, value, options) {
135
135
  const example = options.reqExample || (0, valid_annotation_options_1.getDefaultReqExample)();
136
136
  // @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md | REQ-ERROR-SPECIFICITY - Distinguish a completely missing @req from one that is present but malformed.
137
- // @implements docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md REQ-REQ-FORMAT REQ-ERROR-SPECIFICITY - Enforces presence of the requirement identifier by emitting a specific message when the @req value is missing.
137
+ // @supports docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md REQ-REQ-FORMAT REQ-ERROR-SPECIFICITY - Enforces presence of the requirement identifier by emitting a specific message when the @req value is missing.
138
138
  if (kind === "missing") {
139
- // @implements docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md REQ-ERROR-SPECIFICITY
140
- // @implements docs/stories/010.1-REQ-STORY-PATH-STRICTNESS.story.md REQ-ERROR-SPECIFICITY
139
+ // @supports docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md REQ-ERROR-SPECIFICITY
140
+ // @supports docs/stories/010.1-REQ-STORY-PATH-STRICTNESS.story.md REQ-ERROR-SPECIFICITY
141
141
  return `Missing requirement ID for @req annotation. Expected an identifier like "${example}".`;
142
142
  }
143
- // @implements docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md REQ-ERROR-SPECIFICITY
144
- // @implements docs/stories/010.1-REQ-STORY-PATH-STRICTNESS.story.md REQ-ERROR-SPECIFICITY
143
+ // @supports docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md REQ-ERROR-SPECIFICITY
144
+ // @supports docs/stories/010.1-REQ-STORY-PATH-STRICTNESS.story.md REQ-ERROR-SPECIFICITY
145
145
  return `Invalid requirement ID "${value ?? ""}" for @req annotation. Expected an identifier like "${example}" (uppercase letters, numbers, and dashes only).`;
146
146
  }
@@ -20,14 +20,14 @@ function analyzeCandidateBoundaries(candidates, cwd) {
20
20
  let hasInProjectCandidate = false;
21
21
  let hasOutOfProjectCandidate = false;
22
22
  for (const candidate of candidates) {
23
- // @implements docs/stories/006.0-DEV-FILE-VALIDATION.story.md REQ-PROJECT-BOUNDARY REQ-SECURITY-VALIDATION
23
+ // @supports docs/stories/006.0-DEV-FILE-VALIDATION.story.md REQ-PROJECT-BOUNDARY REQ-SECURITY-VALIDATION
24
24
  const boundary = (0, storyReferenceUtils_1.enforceProjectBoundary)(candidate, cwd);
25
25
  if (boundary.isWithinProject) {
26
- // @implements docs/stories/006.0-DEV-FILE-VALIDATION.story.md REQ-PROJECT-BOUNDARY
26
+ // @supports docs/stories/006.0-DEV-FILE-VALIDATION.story.md REQ-PROJECT-BOUNDARY
27
27
  hasInProjectCandidate = true;
28
28
  }
29
29
  else {
30
- // @implements docs/stories/006.0-DEV-FILE-VALIDATION.story.md REQ-PROJECT-BOUNDARY
30
+ // @supports docs/stories/006.0-DEV-FILE-VALIDATION.story.md REQ-PROJECT-BOUNDARY
31
31
  hasOutOfProjectCandidate = true;
32
32
  }
33
33
  }
@@ -47,12 +47,12 @@ function analyzeCandidateBoundaries(candidates, cwd) {
47
47
  */
48
48
  function handleProjectBoundaryForExistence({ storyPath, commentNode, context, cwd, candidates, existenceResult, reportInvalidPath, }) {
49
49
  if (candidates.length > 0) {
50
- // @implements docs/stories/006.0-DEV-FILE-VALIDATION.story.md REQ-PROJECT-BOUNDARY REQ-SECURITY-VALIDATION
51
- // @implements docs/stories/006.0-DEV-FILE-VALIDATION.story.md REQ-PROJECT-BOUNDARY REQ-SECURITY-VALIDATION
50
+ // @supports docs/stories/006.0-DEV-FILE-VALIDATION.story.md REQ-PROJECT-BOUNDARY REQ-SECURITY-VALIDATION
51
+ // @supports docs/stories/006.0-DEV-FILE-VALIDATION.story.md REQ-PROJECT-BOUNDARY REQ-SECURITY-VALIDATION
52
52
  const { hasInProjectCandidate, hasOutOfProjectCandidate } = analyzeCandidateBoundaries(candidates, cwd);
53
53
  if (hasOutOfProjectCandidate && !hasInProjectCandidate) {
54
- // @implements docs/stories/006.0-DEV-FILE-VALIDATION.story.md REQ-PROJECT-BOUNDARY REQ-SECURITY-VALIDATION
55
- // @implements docs/stories/006.0-DEV-FILE-VALIDATION.story.md REQ-PROJECT-BOUNDARY
54
+ // @supports docs/stories/006.0-DEV-FILE-VALIDATION.story.md REQ-PROJECT-BOUNDARY REQ-SECURITY-VALIDATION
55
+ // @supports docs/stories/006.0-DEV-FILE-VALIDATION.story.md REQ-PROJECT-BOUNDARY
56
56
  reportInvalidPath({ storyPath, commentNode, context });
57
57
  return true;
58
58
  }
@@ -60,12 +60,12 @@ function handleProjectBoundaryForExistence({ storyPath, commentNode, context, cw
60
60
  if (existenceResult &&
61
61
  existenceResult.status === "exists" &&
62
62
  existenceResult.matchedPath) {
63
- // @implements docs/stories/006.0-DEV-FILE-VALIDATION.story.md REQ-PROJECT-BOUNDARY REQ-SECURITY-VALIDATION
64
- // @implements docs/stories/006.0-DEV-FILE-VALIDATION.story.md REQ-PROJECT-BOUNDARY
63
+ // @supports docs/stories/006.0-DEV-FILE-VALIDATION.story.md REQ-PROJECT-BOUNDARY REQ-SECURITY-VALIDATION
64
+ // @supports docs/stories/006.0-DEV-FILE-VALIDATION.story.md REQ-PROJECT-BOUNDARY
65
65
  const boundary = (0, storyReferenceUtils_1.enforceProjectBoundary)(existenceResult.matchedPath, cwd);
66
66
  if (!boundary.isWithinProject) {
67
- // @implements docs/stories/006.0-DEV-FILE-VALIDATION.story.md REQ-PROJECT-BOUNDARY REQ-SECURITY-VALIDATION
68
- // @implements docs/stories/006.0-DEV-FILE-VALIDATION.story.md REQ-PROJECT-BOUNDARY
67
+ // @supports docs/stories/006.0-DEV-FILE-VALIDATION.story.md REQ-PROJECT-BOUNDARY REQ-SECURITY-VALIDATION
68
+ // @supports docs/stories/006.0-DEV-FILE-VALIDATION.story.md REQ-PROJECT-BOUNDARY
69
69
  reportInvalidPath({ storyPath, commentNode, context });
70
70
  return true;
71
71
  }
@@ -83,11 +83,11 @@ function handleProjectBoundaryForExistence({ storyPath, commentNode, context, cw
83
83
  function performSecurityValidations({ storyPath, commentNode, context, cwd, allowAbsolute, reportInvalidPath, }) {
84
84
  // Absolute path check
85
85
  if (path_1.default.isAbsolute(storyPath)) {
86
- // @implements docs/stories/006.0-DEV-FILE-VALIDATION.story.md REQ-PROJECT-BOUNDARY REQ-SECURITY-VALIDATION
87
- // @implements docs/stories/006.0-DEV-FILE-VALIDATION.story.md REQ-SECURITY-VALIDATION
86
+ // @supports docs/stories/006.0-DEV-FILE-VALIDATION.story.md REQ-PROJECT-BOUNDARY REQ-SECURITY-VALIDATION
87
+ // @supports docs/stories/006.0-DEV-FILE-VALIDATION.story.md REQ-SECURITY-VALIDATION
88
88
  if (!allowAbsolute) {
89
- // @implements docs/stories/006.0-DEV-FILE-VALIDATION.story.md REQ-PROJECT-BOUNDARY REQ-SECURITY-VALIDATION
90
- // @implements docs/stories/006.0-DEV-FILE-VALIDATION.story.md REQ-SECURITY-VALIDATION
89
+ // @supports docs/stories/006.0-DEV-FILE-VALIDATION.story.md REQ-PROJECT-BOUNDARY REQ-SECURITY-VALIDATION
90
+ // @supports docs/stories/006.0-DEV-FILE-VALIDATION.story.md REQ-SECURITY-VALIDATION
91
91
  reportInvalidPath({ storyPath, commentNode, context });
92
92
  return false;
93
93
  }
@@ -97,12 +97,12 @@ function performSecurityValidations({ storyPath, commentNode, context, cwd, allo
97
97
  // Path traversal check
98
98
  const containsTraversal = storyPath.includes("..") || /\\|\//.test(storyPath);
99
99
  if (containsTraversal) {
100
- // @implements docs/stories/006.0-DEV-FILE-VALIDATION.story.md REQ-PROJECT-BOUNDARY REQ-SECURITY-VALIDATION
101
- // @implements docs/stories/006.0-DEV-FILE-VALIDATION.story.md REQ-SECURITY-VALIDATION
100
+ // @supports docs/stories/006.0-DEV-FILE-VALIDATION.story.md REQ-PROJECT-BOUNDARY REQ-SECURITY-VALIDATION
101
+ // @supports docs/stories/006.0-DEV-FILE-VALIDATION.story.md REQ-SECURITY-VALIDATION
102
102
  const full = path_1.default.resolve(cwd, path_1.default.normalize(storyPath));
103
103
  if (!full.startsWith(cwd + path_1.default.sep)) {
104
- // @implements docs/stories/006.0-DEV-FILE-VALIDATION.story.md REQ-PROJECT-BOUNDARY REQ-SECURITY-VALIDATION
105
- // @implements docs/stories/006.0-DEV-FILE-VALIDATION.story.md REQ-SECURITY-VALIDATION
104
+ // @supports docs/stories/006.0-DEV-FILE-VALIDATION.story.md REQ-PROJECT-BOUNDARY REQ-SECURITY-VALIDATION
105
+ // @supports docs/stories/006.0-DEV-FILE-VALIDATION.story.md REQ-SECURITY-VALIDATION
106
106
  reportInvalidPath({ storyPath, commentNode, context });
107
107
  return false;
108
108
  }
@@ -12,6 +12,14 @@ const MIN_STORY_TOKENS = 2;
12
12
  const MIN_REQ_TOKENS = MIN_STORY_TOKENS;
13
13
  // Length of the opening "/*" portion of a block comment prefix.
14
14
  const COMMENT_PREFIX_LENGTH = 2;
15
+ /**
16
+ * Collect line indices and metadata for @story and @req annotations within a
17
+ * single block comment. This helper isolates the parsing logic used by the
18
+ * auto-fix path so that complex or ambiguous patterns can be detected and
19
+ * safely rejected.
20
+ *
21
+ * @supports docs/stories/010.3-DEV-MIGRATE-TO-SUPPORTS.story.md REQ-AUTO-FIX REQ-SINGLE-STORY-FIX REQ-VALID-OUTPUT
22
+ */
15
23
  function collectStoryAndReqMetadata(comment) {
16
24
  const rawValue = comment.value || "";
17
25
  const rawLines = rawValue.split(/\r?\n/);
@@ -52,6 +60,13 @@ function collectStoryAndReqMetadata(comment) {
52
60
  });
53
61
  return { storyLineIndices, reqLineIndices, reqIds, storyPath };
54
62
  }
63
+ /**
64
+ * Apply the @supports replacement for simple, single-story legacy blocks,
65
+ * constructing a fixed comment body that preserves existing indentation and
66
+ * prefix formatting while removing the original @story/@req lines.
67
+ *
68
+ * @supports docs/stories/010.3-DEV-MIGRATE-TO-SUPPORTS.story.md REQ-AUTO-FIX REQ-SINGLE-STORY-FIX REQ-PRESERVE-FORMAT REQ-VALID-OUTPUT
69
+ */
55
70
  function applyImplementsReplacement(context, comment, details) {
56
71
  const { storyIdx, allIndicesToRemove, storyPath, reqIds } = details;
57
72
  const rawValue = comment.value || "";
@@ -98,7 +113,7 @@ function applyImplementsReplacement(context, comment, details) {
98
113
  * More complex patterns remain diagnostics-only with no fix to avoid
99
114
  * producing invalid or ambiguous output.
100
115
  *
101
- * @implements docs/stories/010.3-DEV-MIGRATE-TO-SUPPORTS.story.md
116
+ * @supports docs/stories/010.3-DEV-MIGRATE-TO-SUPPORTS.story.md
102
117
  * @req REQ-AUTO-FIX - Provide safe, opt-in auto-fix for simple legacy patterns
103
118
  * @req REQ-SINGLE-STORY-FIX - Restrict auto-fix to single-story, single-path cases
104
119
  * @req REQ-PRESERVE-FORMAT - Preserve original JSDoc indentation and prefix formatting
@@ -126,6 +141,12 @@ function buildImplementsAutoFix(context, comment, storyPaths) {
126
141
  reqIds,
127
142
  });
128
143
  }
144
+ /**
145
+ * Analyze a block comment to detect legacy @story/@req usage, existing
146
+ * @supports lines, and the presence of multiple distinct @story paths.
147
+ *
148
+ * @supports docs/stories/010.3-DEV-MIGRATE-TO-SUPPORTS.story.md REQ-OPTIONAL-WARNING REQ-MULTI-STORY-DETECT
149
+ */
129
150
  function analyzeComment(comment) {
130
151
  const rawLines = (comment.value || "").split(/\r?\n/);
131
152
  let hasStory = false;
@@ -158,6 +179,13 @@ function hasMultipleStories(storyPaths) {
158
179
  // @req REQ-MULTI-STORY-DETECT - Use named threshold constant instead of a magic number
159
180
  return storyPaths.size > MULTI_STORY_THRESHOLD;
160
181
  }
182
+ /**
183
+ * End-to-end processing for a single block comment: classify its
184
+ * traceability annotations, decide whether to report recommendations only
185
+ * or emit an auto-fix, and surface the appropriate message ID.
186
+ *
187
+ * @supports docs/stories/010.3-DEV-MIGRATE-TO-SUPPORTS.story.md REQ-OPTIONAL-WARNING REQ-MULTI-STORY-DETECT REQ-AUTO-FIX REQ-VALID-OUTPUT
188
+ */
161
189
  function processComment(comment, context) {
162
190
  const { hasStory, hasReq, hasImplements, storyPaths } = analyzeComment(comment);
163
191
  if (!hasStory || !hasReq) {