eslint-plugin-traceability 1.12.0 → 1.13.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.
package/CHANGELOG.md CHANGED
@@ -1,9 +1,9 @@
1
- # [1.12.0](https://github.com/voder-ai/eslint-plugin-traceability/compare/v1.11.4...v1.12.0) (2025-12-07)
1
+ # [1.13.0](https://github.com/voder-ai/eslint-plugin-traceability/compare/v1.12.1...v1.13.0) (2025-12-07)
2
2
 
3
3
 
4
4
  ### Features
5
5
 
6
- * accept [@supports](https://github.com/supports) annotations on branches as alternative format ([6773a3a](https://github.com/voder-ai/eslint-plugin-traceability/commit/6773a3ae190e9b9adc605c7d17112f44401e5b24))
6
+ * add no-redundant-annotation rule and scope analyzer utilities ([5dfc6a8](https://github.com/voder-ai/eslint-plugin-traceability/commit/5dfc6a897a986ba5999c933e67a3834d9f237c33))
7
7
 
8
8
  # Changelog
9
9
 
package/lib/src/index.js CHANGED
@@ -19,6 +19,7 @@ const RULE_NAMES = [
19
19
  "valid-req-reference",
20
20
  "prefer-implements-annotation",
21
21
  "require-test-traceability",
22
+ "no-redundant-annotation",
22
23
  ];
23
24
  const rules = {};
24
25
  exports.rules = rules;
@@ -156,6 +157,7 @@ const TRACEABILITY_RULE_SEVERITIES = {
156
157
  "traceability/valid-story-reference": "error",
157
158
  "traceability/valid-req-reference": "error",
158
159
  "traceability/require-test-traceability": "error",
160
+ "traceability/no-redundant-annotation": "warn",
159
161
  };
160
162
  /**
161
163
  * @story docs/stories/007.0-DEV-ERROR-REPORTING.story.md
@@ -98,6 +98,46 @@ exports.STORY_PATH = "docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md";
98
98
  * Allowed values for export priority option.
99
99
  */
100
100
  exports.EXPORT_PRIORITY_VALUES = ["all", "exported", "non-exported"];
101
+ /**
102
+ * Safely execute a reporting operation, swallowing unexpected errors so that
103
+ * traceability rules never break ESLint runs. When TRACEABILITY_DEBUG=1 is
104
+ * set in the environment, a diagnostic message is logged to stderr.
105
+ * @supports docs/stories/007.0-DEV-ERROR-REPORTING.story.md REQ-ERROR-RESILIENCE
106
+ */
107
+ function withSafeReporting(label, fn) {
108
+ try {
109
+ fn();
110
+ }
111
+ catch (error) {
112
+ if (process.env.TRACEABILITY_DEBUG === "1") {
113
+ // Debug logging only when explicitly enabled for troubleshooting helper failures.
114
+ console.error(`[traceability] ${label} failed`, error?.message ?? error);
115
+ }
116
+ }
117
+ }
118
+ /**
119
+ * Build the shared ESLint report descriptor for a missing @story annotation.
120
+ * This keeps the core helpers focused on computing names, targets, and
121
+ * templates while centralizing the diagnostic wiring.
122
+ * @supports docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md REQ-ERROR-SPECIFIC
123
+ * @supports docs/stories/007.0-DEV-ERROR-REPORTING.story.md REQ-ERROR-RESILIENCE
124
+ */
125
+ function createMissingStoryReportDescriptor(config) {
126
+ const { nameNode, name, resolvedTarget, effectiveTemplate, allowFix, createFix, } = config;
127
+ const baseFix = createFix(resolvedTarget, effectiveTemplate);
128
+ return {
129
+ node: nameNode,
130
+ messageId: "missingStory",
131
+ data: { name, functionName: name },
132
+ fix: allowFix ? baseFix : undefined,
133
+ suggest: [
134
+ {
135
+ desc: `Add JSDoc @story annotation for function '${name}', e.g., ${effectiveTemplate}`,
136
+ fix: baseFix,
137
+ },
138
+ ],
139
+ };
140
+ }
101
141
  /**
102
142
  * Core helper to report a missing @story annotation for a function-like node.
103
143
  * This reporting utility delegates behavior to injected dependencies so that
@@ -111,7 +151,7 @@ exports.EXPORT_PRIORITY_VALUES = ["all", "exported", "non-exported"];
111
151
  */
112
152
  function coreReportMissing(deps, context, sourceCode, config) {
113
153
  const { node, target: passedTarget, options = {} } = config;
114
- try {
154
+ withSafeReporting("coreReportMissing", () => {
115
155
  if (deps.hasStoryAnnotation(sourceCode, node)) {
116
156
  return;
117
157
  }
@@ -120,30 +160,15 @@ function coreReportMissing(deps, context, sourceCode, config) {
120
160
  const nameNode = deps.getNameNodeForReport(node);
121
161
  const { effectiveTemplate, allowFix } = deps.buildTemplateConfig(options);
122
162
  const name = functionName;
123
- context.report({
124
- node: nameNode,
125
- messageId: "missingStory",
126
- data: { name, functionName: name },
127
- fix: allowFix
128
- ? deps.createAddStoryFix(resolvedTarget, effectiveTemplate)
129
- : undefined,
130
- suggest: [
131
- {
132
- desc: `Add JSDoc @story annotation for function '${name}', e.g., ${effectiveTemplate}`,
133
- fix: deps.createAddStoryFix(resolvedTarget, effectiveTemplate),
134
- },
135
- ],
136
- });
137
- }
138
- catch (error) {
139
- // Intentionally swallow unexpected helper errors so traceability checks never
140
- // break lint runs. When TRACEABILITY_DEBUG=1 is set, log a debug message to
141
- // help diagnose misbehaving helpers in local development without affecting
142
- // normal CI or production usage.
143
- if (process.env.TRACEABILITY_DEBUG === "1") {
144
- console.error("[traceability] coreReportMissing failed for node", error?.message ?? error);
145
- }
146
- }
163
+ context.report(createMissingStoryReportDescriptor({
164
+ nameNode,
165
+ name,
166
+ resolvedTarget,
167
+ effectiveTemplate,
168
+ allowFix,
169
+ createFix: deps.createAddStoryFix,
170
+ }));
171
+ });
147
172
  }
148
173
  /**
149
174
  * Core helper to report a missing @story annotation for a method-like node.
@@ -158,37 +183,21 @@ function coreReportMissing(deps, context, sourceCode, config) {
158
183
  */
159
184
  function coreReportMethod(deps, context, sourceCode, config) {
160
185
  const { node, target: passedTarget, options = {} } = config;
161
- try {
186
+ withSafeReporting("coreReportMethod", () => {
162
187
  if (deps.hasStoryAnnotation(sourceCode, node)) {
163
188
  return;
164
189
  }
165
190
  const resolvedTarget = passedTarget ?? deps.resolveAnnotationTargetNode(sourceCode, node, null);
166
191
  const name = deps.extractName(node);
167
192
  const nameNode = (node.key && node.key.type === "Identifier" && node.key) || node;
168
- const effectiveTemplate = deps.getAnnotationTemplate(options.annotationTemplateOverride);
169
- const allowFix = deps.shouldApplyAutoFix(options.autoFixToggle);
170
- context.report({
171
- node: nameNode,
172
- messageId: "missingStory",
173
- data: { name, functionName: name },
174
- fix: allowFix
175
- ? deps.createMethodFix(resolvedTarget, effectiveTemplate)
176
- : undefined,
177
- suggest: [
178
- {
179
- desc: `Add JSDoc @story annotation for function '${name}', e.g., ${effectiveTemplate}`,
180
- fix: deps.createMethodFix(resolvedTarget, effectiveTemplate),
181
- },
182
- ],
183
- });
184
- }
185
- catch (error) {
186
- // Intentionally swallow unexpected helper errors so traceability checks never
187
- // break lint runs. When TRACEABILITY_DEBUG=1 is set, log a debug message to
188
- // help diagnose misbehaving helpers in local development without affecting
189
- // normal CI or production usage.
190
- if (process.env.TRACEABILITY_DEBUG === "1") {
191
- console.error("[traceability] coreReportMethod failed for node", error?.message ?? error);
192
- }
193
- }
193
+ const { effectiveTemplate, allowFix } = deps.buildTemplateConfig(options);
194
+ context.report(createMissingStoryReportDescriptor({
195
+ nameNode,
196
+ name,
197
+ resolvedTarget,
198
+ effectiveTemplate,
199
+ allowFix,
200
+ createFix: deps.createMethodFix,
201
+ }));
202
+ });
194
203
  }
@@ -0,0 +1,3 @@
1
+ import type { Rule } from "eslint";
2
+ declare const rule: Rule.RuleModule;
3
+ export default rule;
@@ -0,0 +1,235 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const branch_annotation_helpers_1 = require("../utils/branch-annotation-helpers");
4
+ const annotation_scope_analyzer_1 = require("../utils/annotation-scope-analyzer");
5
+ /**
6
+ * ESLint rule to detect redundant traceability annotations on statements
7
+ * that are already covered by their containing scope.
8
+ *
9
+ * This rule focuses on simple, statement-level patterns that the
10
+ * existing branch and function rules already treat as covered by
11
+ * surrounding annotations. It treats redundant annotations as
12
+ * maintainability concerns rather than correctness issues, and is
13
+ * therefore exposed as a warning-level rule by default.
14
+ *
15
+ * @supports docs/stories/027.0-DEV-REDUNDANT-ANNOTATION-DETECTION.story.md REQ-SCOPE-ANALYSIS REQ-DUPLICATION-DETECTION REQ-STATEMENT-SIGNIFICANCE REQ-SAFE-REMOVAL REQ-DIFFERENT-REQUIREMENTS REQ-CONFIGURABLE-STRICTNESS REQ-SCOPE-INHERITANCE
16
+ */
17
+ const DEFAULT_ALWAYS_COVERED_STATEMENTS = [
18
+ "ReturnStatement",
19
+ "VariableDeclaration",
20
+ ];
21
+ const DEFAULT_STRICTNESS = "moderate";
22
+ const DEFAULT_ALLOW_EMPHASIS_DUPLICATION = false;
23
+ const DEFAULT_MAX_SCOPE_DEPTH = 3;
24
+ function normalizeOptions(raw) {
25
+ const strictness = raw && typeof raw.strictness === "string"
26
+ ? raw.strictness
27
+ : DEFAULT_STRICTNESS;
28
+ const allowEmphasisDuplication = typeof raw?.allowEmphasisDuplication === "boolean"
29
+ ? raw.allowEmphasisDuplication
30
+ : DEFAULT_ALLOW_EMPHASIS_DUPLICATION;
31
+ const maxScopeDepth = typeof raw?.maxScopeDepth === "number" && raw.maxScopeDepth > 0
32
+ ? raw.maxScopeDepth
33
+ : DEFAULT_MAX_SCOPE_DEPTH;
34
+ const alwaysCovered = Array.isArray(raw?.alwaysCovered)
35
+ ? raw.alwaysCovered
36
+ : Array.from(DEFAULT_ALWAYS_COVERED_STATEMENTS);
37
+ return {
38
+ strictness,
39
+ allowEmphasisDuplication,
40
+ maxScopeDepth,
41
+ alwaysCovered,
42
+ };
43
+ }
44
+ /**
45
+ * Compute the story/requirement pairs for annotations that apply to the
46
+ * given scope node.
47
+ *
48
+ * For branch scopes we reuse the same comment-gathering helper used by
49
+ * the require-branch-annotation rule so that REQ-SCOPE-INHERITANCE
50
+ * aligns with existing behavior.
51
+ *
52
+ * @supports docs/stories/027.0-DEV-REDUNDANT-ANNOTATION-DETECTION.story.md REQ-SCOPE-ANALYSIS REQ-SCOPE-INHERITANCE
53
+ */
54
+ function getScopePairs(context, scopeNode, parent) {
55
+ const sourceCode = context.getSourceCode();
56
+ // Branch-style scope: use the branch helpers to collect comment text.
57
+ if (branch_annotation_helpers_1.DEFAULT_BRANCH_TYPES.includes(scopeNode.type)) {
58
+ const text = (0, branch_annotation_helpers_1.gatherBranchCommentText)(sourceCode, scopeNode, parent);
59
+ return (0, annotation_scope_analyzer_1.extractStoryReqPairsFromText)(text);
60
+ }
61
+ // Function-like scopes: collect from JSDoc and leading/before comments
62
+ const FUNCTION_LIKE_TYPES = new Set([
63
+ "FunctionDeclaration",
64
+ "FunctionExpression",
65
+ "ArrowFunctionExpression",
66
+ "MethodDefinition",
67
+ "TSDeclareFunction",
68
+ "TSMethodSignature",
69
+ ]);
70
+ const comments = [];
71
+ if (FUNCTION_LIKE_TYPES.has(scopeNode.type)) {
72
+ const jsdoc = sourceCode.getJSDocComment
73
+ ? sourceCode.getJSDocComment(scopeNode)
74
+ : null;
75
+ const before = sourceCode.getCommentsBefore
76
+ ? sourceCode.getCommentsBefore(scopeNode) || []
77
+ : [];
78
+ if (jsdoc) {
79
+ comments.push(jsdoc);
80
+ }
81
+ if (Array.isArray(scopeNode.leadingComments)) {
82
+ comments.push(...scopeNode.leadingComments);
83
+ }
84
+ comments.push(...before);
85
+ return (0, annotation_scope_analyzer_1.extractStoryReqPairsFromComments)(comments);
86
+ }
87
+ // Fallback: inspect JSDoc and leading comments around the scope node.
88
+ const jsdoc = sourceCode.getJSDocComment
89
+ ? sourceCode.getJSDocComment(scopeNode)
90
+ : null;
91
+ const before = sourceCode.getCommentsBefore
92
+ ? sourceCode.getCommentsBefore(scopeNode) || []
93
+ : [];
94
+ if (jsdoc) {
95
+ comments.push(jsdoc);
96
+ }
97
+ if (Array.isArray(scopeNode.leadingComments)) {
98
+ comments.push(...scopeNode.leadingComments);
99
+ }
100
+ comments.push(...before);
101
+ return (0, annotation_scope_analyzer_1.extractStoryReqPairsFromComments)(comments);
102
+ }
103
+ /**
104
+ * Collect the comments directly associated with a statement node.
105
+ *
106
+ * @supports docs/stories/027.0-DEV-REDUNDANT-ANNOTATION-DETECTION.story.md REQ-STATEMENT-SIGNIFICANCE REQ-SCOPE-ANALYSIS
107
+ */
108
+ function getStatementComments(context, node) {
109
+ const sourceCode = context.getSourceCode();
110
+ const comments = [];
111
+ if (sourceCode.getCommentsBefore) {
112
+ comments.push(...(sourceCode.getCommentsBefore(node) || []));
113
+ }
114
+ if (Array.isArray(node.leadingComments)) {
115
+ comments.push(...node.leadingComments);
116
+ }
117
+ return comments;
118
+ }
119
+ /**
120
+ * Debug helper for logging scope-level pairs in TRACEABILITY_DEBUG mode.
121
+ *
122
+ * @supports docs/stories/027.0-DEV-REDUNDANT-ANNOTATION-DETECTION.story.md REQ-REDUNDANCY-PATTERNS
123
+ */
124
+ function debugScopePairs(scopeNode, scopePairs) {
125
+ if (process.env.TRACEABILITY_DEBUG !== "1") {
126
+ return;
127
+ }
128
+ console.log("[no-redundant-annotation] Scope node type=%s pairs=%o", scopeNode && scopeNode.type, Array.from(scopePairs));
129
+ }
130
+ /**
131
+ * Analyze a block's statements and report redundant traceability annotations.
132
+ *
133
+ * This helper encapsulates the iteration and reporting logic so that the
134
+ * BlockStatement visitor remains small and focused on scope setup.
135
+ *
136
+ * @supports docs/stories/027.0-DEV-REDUNDANT-ANNOTATION-DETECTION.story.md REQ-REDUNDANCY-PATTERNS REQ-SAFE-REMOVAL REQ-STATEMENT-SIGNIFICANCE
137
+ */
138
+ function reportRedundantAnnotationsInBlock(context, blockNode, scopePairs, options) {
139
+ const statements = Array.isArray(blockNode.body) ? blockNode.body : [];
140
+ if (statements.length === 0)
141
+ return;
142
+ for (const stmt of statements) {
143
+ if (!(0, annotation_scope_analyzer_1.isStatementEligibleForRedundancy)(stmt, options, branch_annotation_helpers_1.DEFAULT_BRANCH_TYPES)) {
144
+ continue;
145
+ }
146
+ const stmtComments = getStatementComments(context, stmt);
147
+ if (stmtComments.length === 0) {
148
+ continue;
149
+ }
150
+ const stmtPairs = (0, annotation_scope_analyzer_1.extractStoryReqPairsFromComments)(stmtComments);
151
+ if (process.env.TRACEABILITY_DEBUG === "1") {
152
+ console.log("[no-redundant-annotation] Statement type=%s eligible=%s commentCount=%d pairs=%o", stmt && stmt.type, (0, annotation_scope_analyzer_1.isStatementEligibleForRedundancy)(stmt, options, branch_annotation_helpers_1.DEFAULT_BRANCH_TYPES), stmtComments.length, Array.from(stmtPairs));
153
+ }
154
+ if (stmtPairs.size === 0) {
155
+ continue;
156
+ }
157
+ if (!(0, annotation_scope_analyzer_1.arePairsFullyCovered)(stmtPairs, scopePairs)) {
158
+ continue;
159
+ }
160
+ // At this point the statement-level annotations are fully
161
+ // covered by the parent scope and therefore redundant.
162
+ for (const comment of stmtComments) {
163
+ const commentText = typeof comment.value === "string" ? comment.value : "";
164
+ if (!/@story\b|@req\b|@supports\b/.test(commentText)) {
165
+ continue;
166
+ }
167
+ const [removalStart, removalEnd] = (0, annotation_scope_analyzer_1.getCommentRemovalRange)(comment, context.getSourceCode());
168
+ context.report({
169
+ node: stmt,
170
+ messageId: "redundantAnnotation",
171
+ fix(fixer) {
172
+ return fixer.removeRange([removalStart, removalEnd]);
173
+ },
174
+ });
175
+ }
176
+ }
177
+ }
178
+ const rule = {
179
+ meta: {
180
+ type: "suggestion",
181
+ docs: {
182
+ description: "Detect and remove redundant traceability annotations already covered by containing scope",
183
+ recommended: false,
184
+ },
185
+ fixable: "code",
186
+ schema: [
187
+ {
188
+ type: "object",
189
+ properties: {
190
+ strictness: {
191
+ enum: ["strict", "moderate", "permissive"],
192
+ },
193
+ allowEmphasisDuplication: {
194
+ type: "boolean",
195
+ },
196
+ maxScopeDepth: {
197
+ type: "number",
198
+ minimum: 1,
199
+ },
200
+ alwaysCovered: {
201
+ type: "array",
202
+ items: { type: "string" },
203
+ uniqueItems: true,
204
+ },
205
+ },
206
+ additionalProperties: false,
207
+ },
208
+ ],
209
+ messages: {
210
+ /**
211
+ * @supports docs/stories/027.0-DEV-REDUNDANT-ANNOTATION-DETECTION.story.md REQ-CLEAR-MESSAGES REQ-SAFE-REMOVAL
212
+ */
213
+ redundantAnnotation: "Annotation on this statement is redundant; it is already covered by its containing scope.",
214
+ },
215
+ },
216
+ create(context) {
217
+ const options = normalizeOptions(context.options[0]);
218
+ return {
219
+ // @supports docs/stories/027.0-DEV-REDUNDANT-ANNOTATION-DETECTION.story.md REQ-REDUNDANCY-PATTERNS REQ-SAFE-REMOVAL
220
+ BlockStatement(node) {
221
+ const parent = node.parent;
222
+ const scopeNode = parent;
223
+ if (process.env.TRACEABILITY_DEBUG === "1") {
224
+ console.log("[no-redundant-annotation] BlockStatement parent=%s statements=%d", parent && parent.type, Array.isArray(node.body) ? node.body.length : 0);
225
+ }
226
+ const scopePairs = getScopePairs(context, scopeNode, scopeNode?.parent);
227
+ debugScopePairs(scopeNode, scopePairs);
228
+ if (scopePairs.size === 0)
229
+ return;
230
+ reportRedundantAnnotationsInBlock(context, node, scopePairs, options);
231
+ },
232
+ };
233
+ },
234
+ };
235
+ exports.default = rule;
@@ -0,0 +1,107 @@
1
+ import type { Rule } from "eslint";
2
+ /**
3
+ * Shared types and helpers for redundant-annotation detection.
4
+ *
5
+ * These utilities focus on parsing traceability annotations from comment
6
+ * text and computing relationships between "scope" coverage and
7
+ * statement-level annotations. They are intentionally small, pure
8
+ * functions so that the ESLint rule can delegate most of its logic
9
+ * here while keeping its own create/visitor code shallow.
10
+ *
11
+ * @supports docs/stories/027.0-DEV-REDUNDANT-ANNOTATION-DETECTION.story.md REQ-SCOPE-ANALYSIS REQ-DUPLICATION-DETECTION
12
+ */
13
+ export declare const EXPECTED_RANGE_LENGTH = 2;
14
+ export type Strictness = "strict" | "moderate" | "permissive";
15
+ export interface RedundancyRuleOptions {
16
+ strictness: Strictness;
17
+ allowEmphasisDuplication: boolean;
18
+ maxScopeDepth: number;
19
+ alwaysCovered: readonly string[];
20
+ }
21
+ /**
22
+ * Canonical representation of a single story+requirement pair.
23
+ *
24
+ * The key form `"<story>|<req>"` lets us compare pairs across scopes
25
+ * without repeatedly allocating compound objects.
26
+ *
27
+ * @supports docs/stories/027.0-DEV-REDUNDANT-ANNOTATION-DETECTION.story.md REQ-DUPLICATION-DETECTION REQ-DIFFERENT-REQUIREMENTS
28
+ */
29
+ export type StoryReqKey = string;
30
+ /**
31
+ * Build a canonical key for a story/requirement pair.
32
+ *
33
+ * Empty story or requirement components are normalized to the empty
34
+ * string so that comparisons remain stable even when some annotations
35
+ * omit one side (for example, malformed or story-less @req lines).
36
+ *
37
+ * @supports docs/stories/027.0-DEV-REDUNDANT-ANNOTATION-DETECTION.story.md REQ-DUPLICATION-DETECTION REQ-DIFFERENT-REQUIREMENTS
38
+ */
39
+ export declare function toStoryReqKey(storyPath: string | null, reqId: string): StoryReqKey;
40
+ /**
41
+ * Extract story/requirement pairs from a snippet of comment text.
42
+ *
43
+ * Supported patterns:
44
+ * - `@story <path>` followed by one or more `@req <ID>` lines.
45
+ * - `@supports <path> <REQ-ID-1> <REQ-ID-2> ...` where each `REQ-*`
46
+ * token is treated as a separate pair bound to the same story path.
47
+ *
48
+ * The parser is intentionally conservative: it only creates pairs when
49
+ * it can confidently associate a requirement identifier with a story
50
+ * path. This avoids false positives in REQ-DIFFERENT-REQUIREMENTS by
51
+ * ensuring we never conflate different requirement IDs.
52
+ *
53
+ * @supports docs/stories/027.0-DEV-REDUNDANT-ANNOTATION-DETECTION.story.md REQ-SCOPE-ANALYSIS REQ-DUPLICATION-DETECTION REQ-DIFFERENT-REQUIREMENTS
54
+ */
55
+ export declare function extractStoryReqPairsFromText(text: string): Set<StoryReqKey>;
56
+ /**
57
+ * Extract story/requirement pairs from a list of ESLint comment nodes.
58
+ *
59
+ * @supports docs/stories/027.0-DEV-REDUNDANT-ANNOTATION-DETECTION.story.md REQ-SCOPE-ANALYSIS REQ-DUPLICATION-DETECTION
60
+ */
61
+ export declare function extractStoryReqPairsFromComments(comments: any[]): Set<StoryReqKey>;
62
+ /**
63
+ * Determine whether all story/requirement pairs in `child` are already
64
+ * covered by `parent`.
65
+ *
66
+ * This implements the core notion of redundancy: if a statement-level
67
+ * annotation only repeats the exact same story+requirement pairs that
68
+ * are already declared on its containing scope, it does not add any
69
+ * new traceability information.
70
+ *
71
+ * @supports docs/stories/027.0-DEV-REDUNDANT-ANNOTATION-DETECTION.story.md REQ-DUPLICATION-DETECTION REQ-DIFFERENT-REQUIREMENTS REQ-SCOPE-INHERITANCE
72
+ */
73
+ export declare function arePairsFullyCovered(child: Set<StoryReqKey>, parent: Set<StoryReqKey>): boolean;
74
+ /**
75
+ * Decide whether a given statement node type should be considered
76
+ * "simple" or "significant" for redundancy detection, based on the
77
+ * configured strictness and alwaysCovered lists.
78
+ *
79
+ * - In `strict` mode, all non-branch statements are eligible.
80
+ * - In `moderate` mode (default), only statement types listed in
81
+ * `alwaysCovered` plus bare expression statements are treated as
82
+ * candidates for redundancy.
83
+ * - In `permissive` mode, only `alwaysCovered` types are considered.
84
+ *
85
+ * This keeps REQ-STATEMENT-SIGNIFICANCE and REQ-CONFIGURABLE-STRICTNESS
86
+ * aligned with the story's configuration model.
87
+ *
88
+ * @supports docs/stories/027.0-DEV-REDUNDANT-ANNOTATION-DETECTION.story.md REQ-STATEMENT-SIGNIFICANCE REQ-CONFIGURABLE-STRICTNESS
89
+ */
90
+ export declare function isStatementEligibleForRedundancy(node: any, options: RedundancyRuleOptions, branchTypes: readonly string[]): boolean;
91
+ /**
92
+ * Compute the character range that should be removed when auto-fixing a
93
+ * redundant annotation comment.
94
+ *
95
+ * The implementation is conservative to satisfy REQ-SAFE-REMOVAL:
96
+ *
97
+ * - When the comment occupies its own line (only whitespace before the
98
+ * comment token), the removal range is expanded to include that
99
+ * leading whitespace and the trailing newline, so the entire line is
100
+ * removed.
101
+ * - When there is other code before the comment on the same line, only
102
+ * the comment text itself is removed, leaving surrounding code and
103
+ * whitespace intact.
104
+ *
105
+ * @supports docs/stories/027.0-DEV-REDUNDANT-ANNOTATION-DETECTION.story.md REQ-SAFE-REMOVAL
106
+ */
107
+ export declare function getCommentRemovalRange(comment: any, sourceCode: ReturnType<Rule.RuleContext["getSourceCode"]>): [number, number];