eslint-plugin-traceability 1.8.0 → 1.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. package/CHANGELOG.md +5 -5
  2. package/README.md +28 -29
  3. package/SECURITY.md +135 -0
  4. package/lib/src/index.d.ts +6 -35
  5. package/lib/src/index.js +8 -5
  6. package/lib/src/maintenance/cli.js +12 -16
  7. package/lib/src/maintenance/detect.js +28 -1
  8. package/lib/src/rules/helpers/require-story-io.d.ts +2 -2
  9. package/lib/src/rules/helpers/require-story-io.js +13 -13
  10. package/lib/src/rules/helpers/valid-annotation-format-internal.d.ts +2 -2
  11. package/lib/src/rules/helpers/valid-annotation-format-internal.js +3 -3
  12. package/lib/src/rules/helpers/valid-annotation-utils.d.ts +5 -0
  13. package/lib/src/rules/helpers/valid-annotation-utils.js +43 -5
  14. package/lib/src/rules/helpers/valid-implements-utils.d.ts +11 -11
  15. package/lib/src/rules/helpers/valid-implements-utils.js +11 -11
  16. package/lib/src/rules/helpers/valid-story-reference-helpers.js +19 -0
  17. package/lib/src/rules/prefer-implements-annotation.d.ts +7 -7
  18. package/lib/src/rules/prefer-implements-annotation.js +21 -21
  19. package/lib/src/rules/valid-annotation-format.js +50 -24
  20. package/lib/src/rules/valid-req-reference.js +9 -9
  21. package/lib/src/utils/annotation-checker.js +3 -1
  22. package/lib/src/utils/reqAnnotationDetection.d.ts +2 -2
  23. package/lib/src/utils/reqAnnotationDetection.js +28 -28
  24. package/lib/tests/config/flat-config-presets-integration.test.d.ts +1 -0
  25. package/lib/tests/config/flat-config-presets-integration.test.js +75 -0
  26. package/lib/tests/maintenance/batch.test.js +11 -11
  27. package/lib/tests/maintenance/cli.test.js +34 -27
  28. package/lib/tests/maintenance/report.test.js +7 -7
  29. package/lib/tests/plugin-default-export-and-configs.test.js +0 -2
  30. package/lib/tests/rules/prefer-implements-annotation.test.js +48 -15
  31. package/lib/tests/rules/require-branch-annotation.test.js +15 -36
  32. package/lib/tests/rules/require-req-annotation.test.js +31 -104
  33. package/lib/tests/rules/require-story-annotation.test.js +3 -3
  34. package/lib/tests/rules/require-story-io-behavior.test.js +2 -7
  35. package/lib/tests/rules/require-story-io.edgecases.test.js +2 -7
  36. package/lib/tests/rules/require-story-visitors-edgecases.test.js +8 -8
  37. package/lib/tests/rules/valid-annotation-format.test.js +23 -23
  38. package/lib/tests/rules/valid-req-reference.test.js +9 -9
  39. package/lib/tests/rules/valid-story-reference.test.js +4 -43
  40. package/lib/tests/utils/annotation-checker.test.js +2 -6
  41. package/lib/tests/utils/fsTestHelpers.d.ts +7 -0
  42. package/lib/tests/utils/fsTestHelpers.js +26 -0
  43. package/lib/tests/utils/ioTestHelpers.d.ts +7 -0
  44. package/lib/tests/utils/ioTestHelpers.js +24 -0
  45. package/lib/tests/utils/temp-dir-helpers.d.ts +14 -0
  46. package/lib/tests/utils/temp-dir-helpers.js +61 -0
  47. package/package.json +8 -7
  48. package/user-docs/api-reference.md +37 -20
  49. package/user-docs/eslint-9-setup-guide.md +89 -6
  50. package/user-docs/migration-guide.md +37 -21
  51. package/docs/ci-cd-pipeline.md +0 -224
  52. package/docs/cli-integration.md +0 -22
  53. package/docs/code-quality-refactor-opportunities-2025-12-03.md +0 -78
  54. package/docs/config-presets.md +0 -38
  55. package/docs/conventional-commits-guide.md +0 -185
  56. package/docs/custom-rules-development-guide.md +0 -659
  57. package/docs/decisions/0001-allow-dynamic-require-for-built-plugins.md +0 -26
  58. package/docs/decisions/001-typescript-for-eslint-plugin.accepted.md +0 -111
  59. package/docs/decisions/002-jest-for-eslint-testing.accepted.md +0 -137
  60. package/docs/decisions/003-code-quality-ratcheting-plan.md +0 -48
  61. package/docs/decisions/004-automated-version-bumping-for-ci-cd.md +0 -196
  62. package/docs/decisions/005-github-actions-validation-tooling.accepted.md +0 -144
  63. package/docs/decisions/006-semantic-release-for-automated-publishing.accepted.md +0 -227
  64. package/docs/decisions/007-github-releases-over-changelog.accepted.md +0 -216
  65. package/docs/decisions/008-ci-audit-flags.accepted.md +0 -60
  66. package/docs/decisions/009-security-focused-lint-rules.accepted.md +0 -64
  67. package/docs/decisions/010-implements-annotation-for-multi-story-requirements.proposed.md +0 -184
  68. package/docs/decisions/adr-0001-console-usage-for-cli-guards.md +0 -190
  69. package/docs/decisions/adr-accept-dev-dep-risk-glob.md +0 -40
  70. package/docs/decisions/adr-commit-branch-tests.md +0 -54
  71. package/docs/decisions/adr-maintenance-cli-interface.md +0 -140
  72. package/docs/decisions/adr-pre-push-parity.md +0 -112
  73. package/docs/decisions/code-quality-ratcheting-plan.md +0 -53
  74. package/docs/dependency-health.md +0 -238
  75. package/docs/eslint-9-setup-guide.md +0 -517
  76. package/docs/eslint-plugin-development-guide.md +0 -487
  77. package/docs/functionality-coverage-2025-12-03.md +0 -250
  78. package/docs/jest-testing-guide.md +0 -100
  79. package/docs/rules/prefer-implements-annotation.md +0 -219
  80. package/docs/rules/require-branch-annotation.md +0 -71
  81. package/docs/rules/require-req-annotation.md +0 -203
  82. package/docs/rules/require-story-annotation.md +0 -159
  83. package/docs/rules/valid-annotation-format.md +0 -418
  84. package/docs/rules/valid-req-reference.md +0 -153
  85. package/docs/rules/valid-story-reference.md +0 -120
  86. package/docs/security-incidents/2025-11-17-glob-cli-incident.md +0 -45
  87. package/docs/security-incidents/2025-11-18-brace-expansion-redos.md +0 -45
  88. package/docs/security-incidents/2025-11-18-bundled-dev-deps-accepted-risk.md +0 -93
  89. package/docs/security-incidents/2025-11-18-tar-race-condition.md +0 -43
  90. package/docs/security-incidents/2025-12-03-dependency-health-review.md +0 -58
  91. package/docs/security-incidents/SECURITY-INCIDENT-2025-11-18-semantic-release-bundled-npm.known-error.md +0 -104
  92. package/docs/security-incidents/SECURITY-INCIDENT-TEMPLATE.md +0 -37
  93. package/docs/security-incidents/dependency-override-rationale.md +0 -57
  94. package/docs/security-incidents/dev-deps-high.json +0 -116
  95. package/docs/security-incidents/handling-procedure.md +0 -54
  96. package/docs/stories/001.0-DEV-PLUGIN-SETUP.story.md +0 -92
  97. package/docs/stories/002.0-DEV-ESLINT-CONFIG.story.md +0 -82
  98. package/docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md +0 -112
  99. package/docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md +0 -153
  100. package/docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md +0 -138
  101. package/docs/stories/006.0-DEV-FILE-VALIDATION.story.md +0 -144
  102. package/docs/stories/007.0-DEV-ERROR-REPORTING.story.md +0 -163
  103. package/docs/stories/008.0-DEV-AUTO-FIX.story.md +0 -150
  104. package/docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md +0 -117
  105. package/docs/stories/010.0-DEV-DEEP-VALIDATION.story.md +0 -124
  106. package/docs/stories/010.1-DEV-CONFIGURABLE-PATTERNS.story.md +0 -149
  107. package/docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md +0 -216
  108. package/docs/stories/010.3-DEV-MIGRATE-TO-IMPLEMENTS.story.md +0 -236
  109. package/docs/stories/developer-story.map.md +0 -120
  110. package/docs/ts-jest-presets-guide.md +0 -548
@@ -13,13 +13,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
13
13
  * @req REQ-MAINT-SAFE - Clear exit codes and non-destructive dry-run
14
14
  */
15
15
  const fs_1 = __importDefault(require("fs"));
16
- const os_1 = __importDefault(require("os"));
17
16
  const path_1 = __importDefault(require("path"));
18
17
  const cli_1 = require("../../src/maintenance/cli");
19
- function withTempDir() {
20
- const tmpDir = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), "maint-cli-"));
21
- return tmpDir;
22
- }
18
+ const temp_dir_helpers_1 = require("../utils/temp-dir-helpers");
23
19
  describe("Maintenance CLI (Story 009.0-DEV-MAINTENANCE-TOOLS)", () => {
24
20
  let originalCwd;
25
21
  beforeAll(() => {
@@ -29,7 +25,8 @@ describe("Maintenance CLI (Story 009.0-DEV-MAINTENANCE-TOOLS)", () => {
29
25
  process.chdir(originalCwd);
30
26
  });
31
27
  it("[REQ-MAINT-DETECT] detect exits with code 0 and message when no stale annotations", () => {
32
- const dir = withTempDir();
28
+ const temp = (0, temp_dir_helpers_1.createTempDir)("maint-cli-");
29
+ const dir = temp.dir;
33
30
  process.chdir(dir);
34
31
  const logSpy = jest.spyOn(console, "log").mockImplementation(() => { });
35
32
  const code = (0, cli_1.runMaintenanceCli)(["node", "traceability-maint", "detect"]);
@@ -39,11 +36,12 @@ describe("Maintenance CLI (Story 009.0-DEV-MAINTENANCE-TOOLS)", () => {
39
36
  }
40
37
  finally {
41
38
  logSpy.mockRestore();
42
- fs_1.default.rmSync(dir, { recursive: true, force: true });
39
+ temp.cleanup();
43
40
  }
44
41
  });
45
42
  it("[REQ-MAINT-VERIFY] verify exits with code 0 when annotations valid", () => {
46
- const dir = withTempDir();
43
+ const temp = (0, temp_dir_helpers_1.createTempDir)("maint-cli-");
44
+ const dir = temp.dir;
47
45
  process.chdir(dir);
48
46
  const tsContent = `/**\n * @story my-story.story.md\n */`;
49
47
  fs_1.default.writeFileSync(path_1.default.join(dir, "file.ts"), tsContent, "utf8");
@@ -56,11 +54,12 @@ describe("Maintenance CLI (Story 009.0-DEV-MAINTENANCE-TOOLS)", () => {
56
54
  }
57
55
  finally {
58
56
  logSpy.mockRestore();
59
- fs_1.default.rmSync(dir, { recursive: true, force: true });
57
+ temp.cleanup();
60
58
  }
61
59
  });
62
60
  it("[REQ-MAINT-REPORT] report prints human-readable summary and exits 0", () => {
63
- const dir = withTempDir();
61
+ const temp = (0, temp_dir_helpers_1.createTempDir)("maint-cli-");
62
+ const dir = temp.dir;
64
63
  process.chdir(dir);
65
64
  const tsContent = `/**\n * @story missing.story.md\n */`;
66
65
  fs_1.default.writeFileSync(path_1.default.join(dir, "file.ts"), tsContent, "utf8");
@@ -74,11 +73,12 @@ describe("Maintenance CLI (Story 009.0-DEV-MAINTENANCE-TOOLS)", () => {
74
73
  }
75
74
  finally {
76
75
  logSpy.mockRestore();
77
- fs_1.default.rmSync(dir, { recursive: true, force: true });
76
+ temp.cleanup();
78
77
  }
79
78
  });
80
79
  it("[REQ-MAINT-UPDATE] update performs replacements and exits 0", () => {
81
- const dir = withTempDir();
80
+ const temp = (0, temp_dir_helpers_1.createTempDir)("maint-cli-");
81
+ const dir = temp.dir;
82
82
  process.chdir(dir);
83
83
  const tsContent = `/**\n * @story old.path.md\n */`;
84
84
  fs_1.default.writeFileSync(path_1.default.join(dir, "file.ts"), tsContent, "utf8");
@@ -99,11 +99,12 @@ describe("Maintenance CLI (Story 009.0-DEV-MAINTENANCE-TOOLS)", () => {
99
99
  }
100
100
  finally {
101
101
  logSpy.mockRestore();
102
- fs_1.default.rmSync(dir, { recursive: true, force: true });
102
+ temp.cleanup();
103
103
  }
104
104
  });
105
105
  it("[REQ-MAINT-SAFE] update requires --from and --to and exits 2 when missing", () => {
106
- const dir = withTempDir();
106
+ const temp = (0, temp_dir_helpers_1.createTempDir)("maint-cli-");
107
+ const dir = temp.dir;
107
108
  process.chdir(dir);
108
109
  const errorSpy = jest.spyOn(console, "error").mockImplementation(() => { });
109
110
  const logSpy = jest.spyOn(console, "log").mockImplementation(() => { });
@@ -116,11 +117,12 @@ describe("Maintenance CLI (Story 009.0-DEV-MAINTENANCE-TOOLS)", () => {
116
117
  finally {
117
118
  errorSpy.mockRestore();
118
119
  logSpy.mockRestore();
119
- fs_1.default.rmSync(dir, { recursive: true, force: true });
120
+ temp.cleanup();
120
121
  }
121
122
  });
122
123
  it("[REQ-MAINT-SAFE] dry-run does not modify files and exits 0", () => {
123
- const dir = withTempDir();
124
+ const temp = (0, temp_dir_helpers_1.createTempDir)("maint-cli-");
125
+ const dir = temp.dir;
124
126
  process.chdir(dir);
125
127
  const tsContent = `/**\n * @story old.path.md\n */`;
126
128
  fs_1.default.writeFileSync(path_1.default.join(dir, "file.ts"), tsContent, "utf8");
@@ -142,11 +144,12 @@ describe("Maintenance CLI (Story 009.0-DEV-MAINTENANCE-TOOLS)", () => {
142
144
  }
143
145
  finally {
144
146
  logSpy.mockRestore();
145
- fs_1.default.rmSync(dir, { recursive: true, force: true });
147
+ temp.cleanup();
146
148
  }
147
149
  });
148
150
  it("[REQ-MAINT-SAFE] report exits 2 and prints error on invalid --format value", () => {
149
- const dir = withTempDir();
151
+ const temp = (0, temp_dir_helpers_1.createTempDir)("maint-cli-");
152
+ const dir = temp.dir;
150
153
  process.chdir(dir);
151
154
  const errorSpy = jest.spyOn(console, "error").mockImplementation(() => { });
152
155
  const logSpy = jest.spyOn(console, "log").mockImplementation(() => { });
@@ -167,11 +170,12 @@ describe("Maintenance CLI (Story 009.0-DEV-MAINTENANCE-TOOLS)", () => {
167
170
  finally {
168
171
  errorSpy.mockRestore();
169
172
  logSpy.mockRestore();
170
- fs_1.default.rmSync(dir, { recursive: true, force: true });
173
+ temp.cleanup();
171
174
  }
172
175
  });
173
176
  it("[REQ-MAINT-DETECT] detect supports --json output", () => {
174
- const dir = withTempDir();
177
+ const temp = (0, temp_dir_helpers_1.createTempDir)("maint-cli-");
178
+ const dir = temp.dir;
175
179
  process.chdir(dir);
176
180
  const tsContent = `/**\n * @story stale.story.md\n */`;
177
181
  fs_1.default.writeFileSync(path_1.default.join(dir, "file.ts"), tsContent, "utf8");
@@ -191,11 +195,12 @@ describe("Maintenance CLI (Story 009.0-DEV-MAINTENANCE-TOOLS)", () => {
191
195
  }
192
196
  finally {
193
197
  logSpy.mockRestore();
194
- fs_1.default.rmSync(dir, { recursive: true, force: true });
198
+ temp.cleanup();
195
199
  }
196
200
  });
197
201
  it("[REQ-MAINT-DETECT] detect with non-existent --root exits 0 and reports no stale annotations", () => {
198
- const dir = withTempDir();
202
+ const temp = (0, temp_dir_helpers_1.createTempDir)("maint-cli-");
203
+ const dir = temp.dir;
199
204
  process.chdir(dir);
200
205
  const missingRoot = path_1.default.join(dir, "missing-root");
201
206
  const logSpy = jest.spyOn(console, "log").mockImplementation(() => { });
@@ -212,11 +217,12 @@ describe("Maintenance CLI (Story 009.0-DEV-MAINTENANCE-TOOLS)", () => {
212
217
  }
213
218
  finally {
214
219
  logSpy.mockRestore();
215
- fs_1.default.rmSync(dir, { recursive: true, force: true });
220
+ temp.cleanup();
216
221
  }
217
222
  });
218
223
  it("[REQ-MAINT-SAFE] prints help and exits 0 when no subcommand is provided", () => {
219
- const dir = withTempDir();
224
+ const temp = (0, temp_dir_helpers_1.createTempDir)("maint-cli-");
225
+ const dir = temp.dir;
220
226
  process.chdir(dir);
221
227
  const logSpy = jest.spyOn(console, "log").mockImplementation(() => { });
222
228
  const errorSpy = jest.spyOn(console, "error").mockImplementation(() => { });
@@ -231,11 +237,12 @@ describe("Maintenance CLI (Story 009.0-DEV-MAINTENANCE-TOOLS)", () => {
231
237
  finally {
232
238
  logSpy.mockRestore();
233
239
  errorSpy.mockRestore();
234
- fs_1.default.rmSync(dir, { recursive: true, force: true });
240
+ temp.cleanup();
235
241
  }
236
242
  });
237
243
  it("[REQ-MAINT-SAFE] detect catches filesystem permission errors and exits 2 with prefixed error message", () => {
238
- const dir = withTempDir();
244
+ const temp = (0, temp_dir_helpers_1.createTempDir)("maint-cli-");
245
+ const dir = temp.dir;
239
246
  process.chdir(dir);
240
247
  const errorSpy = jest.spyOn(console, "error").mockImplementation(() => { });
241
248
  const logSpy = jest.spyOn(console, "log").mockImplementation(() => { });
@@ -255,7 +262,7 @@ describe("Maintenance CLI (Story 009.0-DEV-MAINTENANCE-TOOLS)", () => {
255
262
  statSpy.mockRestore();
256
263
  errorSpy.mockRestore();
257
264
  logSpy.mockRestore();
258
- fs_1.default.rmSync(dir, { recursive: true, force: true });
265
+ temp.cleanup();
259
266
  }
260
267
  });
261
268
  });
@@ -41,27 +41,27 @@ Object.defineProperty(exports, "__esModule", { value: true });
41
41
  */
42
42
  const fs = __importStar(require("fs"));
43
43
  const path = __importStar(require("path"));
44
- const os = __importStar(require("os"));
44
+ const temp_dir_helpers_1 = require("../utils/temp-dir-helpers");
45
45
  const report_1 = require("../../src/maintenance/report");
46
46
  describe("generateMaintenanceReport (Story 009.0-DEV-MAINTENANCE-TOOLS)", () => {
47
- let tmpDir;
47
+ let temp;
48
48
  beforeAll(() => {
49
- tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "report-test-"));
49
+ temp = (0, temp_dir_helpers_1.createTempDir)("report-test-");
50
50
  });
51
51
  afterAll(() => {
52
- fs.rmSync(tmpDir, { recursive: true, force: true });
52
+ temp.cleanup();
53
53
  });
54
54
  it("[REQ-MAINT-REPORT] should return empty string when no operations", () => {
55
- const report = (0, report_1.generateMaintenanceReport)(tmpDir);
55
+ const report = (0, report_1.generateMaintenanceReport)(temp.dir);
56
56
  expect(report).toBe("");
57
57
  });
58
58
  it("[REQ-MAINT-REPORT] should report stale story annotation", () => {
59
- const filePath = path.join(tmpDir, "stub.md");
59
+ const filePath = path.join(temp.dir, "stub.md");
60
60
  const content = `/**
61
61
  * @story non-existent.story.md
62
62
  */`;
63
63
  fs.writeFileSync(filePath, content);
64
- const report = (0, report_1.generateMaintenanceReport)(tmpDir);
64
+ const report = (0, report_1.generateMaintenanceReport)(temp.dir);
65
65
  expect(report).toContain("non-existent.story.md");
66
66
  });
67
67
  });
@@ -80,12 +80,10 @@ describe("Plugin Default Export and Configs (Story 001.0-DEV-PLUGIN-SETUP)", ()
80
80
  expect(recommendedRules).toHaveProperty("traceability/require-branch-annotation", "error");
81
81
  expect(recommendedRules).toHaveProperty("traceability/valid-story-reference", "error");
82
82
  expect(recommendedRules).toHaveProperty("traceability/valid-req-reference", "error");
83
- expect(recommendedRules).toHaveProperty("traceability/prefer-implements-annotation", "warn");
84
83
  });
85
84
  it("[REQ-ERROR-SEVERITY] configs.strict uses same severity mapping as recommended", () => {
86
85
  const strictRules = index_1.configs.strict[0].rules;
87
86
  const recommendedRules = index_1.configs.recommended[0].rules;
88
87
  expect(strictRules).toEqual(recommendedRules);
89
- expect(strictRules).toHaveProperty("traceability/prefer-implements-annotation", "warn");
90
88
  });
91
89
  });
@@ -4,20 +4,21 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  /**
7
- * Tests for: docs/stories/010.3-DEV-MIGRATE-TO-IMPLEMENTS.story.md
8
- * @story docs/stories/010.3-DEV-MIGRATE-TO-IMPLEMENTS.story.md
9
- * @req REQ-OPTIONAL-WARNING - Verify rule emits recommendations for legacy @story/@req usage
10
- * @req REQ-MULTI-STORY-DETECT - Verify rule detects multi-story and mixed-annotation patterns
7
+ * Tests for: docs/stories/010.3-DEV-MIGRATE-TO-SUPPORTS.story.md
8
+ * @story docs/stories/010.3-DEV-MIGRATE-TO-SUPPORTS.story.md
9
+ * @req REQ-OPTIONAL-WARNING - Verify rule emits recommendations for legacy @story/@req usage and migration to @supports
10
+ * @req REQ-MULTI-STORY-DETECT - Verify rule detects multi-story and mixed-annotation patterns involving @supports
11
11
  * @req REQ-CONFIG-SEVERITY - Verify rule is disabled by default and can be enabled as warn/error
12
12
  */
13
13
  const eslint_1 = require("eslint");
14
14
  const prefer_implements_annotation_1 = __importDefault(require("../../src/rules/prefer-implements-annotation"));
15
+ const src_1 = require("../../src");
15
16
  const ruleTester = new eslint_1.RuleTester({
16
17
  languageOptions: {
17
18
  parserOptions: { ecmaVersion: 2020, sourceType: "module" },
18
19
  },
19
20
  });
20
- describe("prefer-implements-annotation rule (Story 010.3-DEV-MIGRATE-TO-IMPLEMENTS)", () => {
21
+ describe("prefer-implements-annotation rule (Story 010.3-DEV-MIGRATE-TO-SUPPORTS)", () => {
21
22
  ruleTester.run("prefer-implements-annotation", prefer_implements_annotation_1.default, {
22
23
  valid: [
23
24
  {
@@ -29,25 +30,25 @@ describe("prefer-implements-annotation rule (Story 010.3-DEV-MIGRATE-TO-IMPLEMEN
29
30
  code: `/**\n * @req REQ-ONLY\n */\nfunction onlyReq() {}`,
30
31
  },
31
32
  {
32
- name: "[REQ-BACKWARD-COMP-VALIDATION] comment with @implements only is ignored",
33
- code: `/**\n * @implements docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md REQ-ANNOTATION-REQUIRED\n */\nfunction alreadyImplements() {}`,
33
+ name: "[REQ-BACKWARD-COMP-VALIDATION] comment with @supports only is ignored",
34
+ code: `/**\n * @supports docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md REQ-ANNOTATION-REQUIRED\n */\nfunction alreadyImplements() {}`,
34
35
  },
35
36
  ],
36
37
  invalid: [
37
38
  {
38
39
  name: "[REQ-OPTIONAL-WARNING] single-story @story + @req block triggers preferImplements message",
39
40
  code: `/**\n * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md\n * @req REQ-ANNOTATION-REQUIRED\n */\nfunction legacy() {}`,
40
- output: `/**\n * @implements docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md REQ-ANNOTATION-REQUIRED\n */\nfunction legacy() {}`,
41
+ output: `/**\n * @supports docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md REQ-ANNOTATION-REQUIRED\n */\nfunction legacy() {}`,
41
42
  errors: [{ messageId: "preferImplements" }],
42
43
  },
43
44
  {
44
- name: "[REQ-MULTI-STORY-DETECT] mixed @story/@req and @implements triggers cannotAutoFix",
45
- code: `/**\n * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md\n * @req REQ-ANNOTATION-REQUIRED\n * @implements docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md REQ-ANNOTATION-REQUIRED\n */\nfunction mixed() {}`,
45
+ name: "[REQ-MULTI-STORY-DETECT] mixed @story/@req and @supports triggers cannotAutoFix",
46
+ code: `/**\n * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md\n * @req REQ-ANNOTATION-REQUIRED\n * @supports docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md REQ-ANNOTATION-REQUIRED\n */\nfunction mixed() {}`,
46
47
  errors: [
47
48
  {
48
49
  messageId: "cannotAutoFix",
49
50
  data: {
50
- reason: "comment mixes @story/@req with existing @implements annotations",
51
+ reason: "comment mixes @story/@req with existing @supports annotations",
51
52
  },
52
53
  },
53
54
  ],
@@ -58,15 +59,15 @@ describe("prefer-implements-annotation rule (Story 010.3-DEV-MIGRATE-TO-IMPLEMEN
58
59
  errors: [{ messageId: "multiStoryDetected" }],
59
60
  },
60
61
  {
61
- name: "[REQ-AUTO-FIX] single @story + single @req auto-fixes to single @implements line",
62
+ name: "[REQ-AUTO-FIX] single @story + single @req auto-fixes to single @supports line",
62
63
  code: `/**\n * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md\n * @req REQ-ANNOTATION-REQUIRED\n */\nfunction autoFixSingleReq() {}`,
63
- output: `/**\n * @implements docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md REQ-ANNOTATION-REQUIRED\n */\nfunction autoFixSingleReq() {}`,
64
+ output: `/**\n * @supports docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md REQ-ANNOTATION-REQUIRED\n */\nfunction autoFixSingleReq() {}`,
64
65
  errors: [{ messageId: "preferImplements" }],
65
66
  },
66
67
  {
67
- name: "[REQ-SINGLE-STORY-FIX] single @story with multiple @req lines auto-fixes to single @implements line containing all REQ IDs",
68
+ name: "[REQ-SINGLE-STORY-FIX] single @story with multiple @req lines auto-fixes to single @supports line containing all REQ IDs",
68
69
  code: `/**\n * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md\n * @req REQ-ONE\n * @req REQ-TWO\n * @req REQ-THREE\n */\nfunction autoFixMultiReq() {}`,
69
- output: `/**\n * @implements docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md REQ-ONE REQ-TWO REQ-THREE\n */\nfunction autoFixMultiReq() {}`,
70
+ output: `/**\n * @supports docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md REQ-ONE REQ-TWO REQ-THREE\n */\nfunction autoFixMultiReq() {}`,
70
71
  errors: [{ messageId: "preferImplements" }],
71
72
  },
72
73
  {
@@ -82,3 +83,35 @@ describe("prefer-implements-annotation rule (Story 010.3-DEV-MIGRATE-TO-IMPLEMEN
82
83
  ],
83
84
  });
84
85
  });
86
+ describe("prefer-implements-annotation configuration severity (REQ-CONFIG-SEVERITY)", () => {
87
+ test("rule is disabled by default in recommended and strict presets (not present in preset rule maps)", () => {
88
+ const recommended = src_1.configs.recommended;
89
+ expect(Array.isArray(recommended)).toBe(true);
90
+ const firstConfig = recommended[0];
91
+ expect(firstConfig).toBeDefined();
92
+ const rules = firstConfig.rules || {};
93
+ expect(rules["traceability/prefer-implements-annotation"]).toBeUndefined();
94
+ const strict = src_1.configs.strict;
95
+ expect(Array.isArray(strict)).toBe(true);
96
+ const strictFirstConfig = strict[0];
97
+ expect(strictFirstConfig).toBeDefined();
98
+ const strictRules = strictFirstConfig.rules || {};
99
+ expect(strictRules["traceability/prefer-implements-annotation"]).toBeUndefined();
100
+ });
101
+ test("rule can be configured with severity 'warn' or 'error' in flat config", () => {
102
+ const flatWarnConfig = {
103
+ files: ["**/*.ts"],
104
+ rules: {
105
+ "traceability/prefer-implements-annotation": "warn",
106
+ },
107
+ };
108
+ expect(flatWarnConfig.rules["traceability/prefer-implements-annotation"]).toBe("warn");
109
+ const flatErrorConfig = {
110
+ files: ["**/*.ts"],
111
+ rules: {
112
+ "traceability/prefer-implements-annotation": "error",
113
+ },
114
+ };
115
+ expect(flatErrorConfig.rules["traceability/prefer-implements-annotation"]).toBe("error");
116
+ });
117
+ });
@@ -17,6 +17,10 @@ const require_branch_annotation_1 = __importDefault(require("../../src/rules/req
17
17
  const ruleTester = new eslint_1.RuleTester({
18
18
  languageOptions: { parserOptions: { ecmaVersion: 2020 } },
19
19
  });
20
+ const makeMissingAnnotationErrors = (...missing) => missing.map((item) => ({
21
+ messageId: "missingAnnotation",
22
+ data: { missing: item },
23
+ }));
20
24
  const runRule = (tests) => ruleTester.run("require-branch-annotation", require_branch_annotation_1.default, tests);
21
25
  describe("Require Branch Annotation Rule (Story 004.0-DEV-BRANCH-ANNOTATIONS)", () => {
22
26
  runRule({
@@ -144,10 +148,7 @@ if (condition) {}`,
144
148
  code: `if (condition) {}`,
145
149
  output: `// @story <story-file>.story.md
146
150
  if (condition) {}`,
147
- errors: [
148
- { messageId: "missingAnnotation", data: { missing: "@story" } },
149
- { messageId: "missingAnnotation", data: { missing: "@req" } },
150
- ],
151
+ errors: makeMissingAnnotationErrors("@story", "@req"),
151
152
  },
152
153
  {
153
154
  name: "[REQ-BRANCH-DETECTION] missing @req on for loop when only story present",
@@ -156,7 +157,7 @@ for (let i = 0; i < 5; i++) {}`,
156
157
  output: `// @story docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md
157
158
  // @req <REQ-ID>
158
159
  for (let i = 0; i < 5; i++) {}`,
159
- errors: [{ messageId: "missingAnnotation", data: { missing: "@req" } }],
160
+ errors: makeMissingAnnotationErrors("@req"),
160
161
  },
161
162
  {
162
163
  name: "[REQ-BRANCH-DETECTION] missing @story on while loop when only req present",
@@ -165,9 +166,7 @@ while (true) {}`,
165
166
  output: `// @req REQ-BRANCH-DETECTION
166
167
  // @story <story-file>.story.md
167
168
  while (true) {}`,
168
- errors: [
169
- { messageId: "missingAnnotation", data: { missing: "@story" } },
170
- ],
169
+ errors: makeMissingAnnotationErrors("@story"),
171
170
  },
172
171
  {
173
172
  name: "[REQ-BRANCH-DETECTION] missing annotations on switch-case",
@@ -180,10 +179,7 @@ while (true) {}`,
180
179
  case 'a':
181
180
  break;
182
181
  }`,
183
- errors: [
184
- { messageId: "missingAnnotation", data: { missing: "@story" } },
185
- { messageId: "missingAnnotation", data: { missing: "@req" } },
186
- ],
182
+ errors: makeMissingAnnotationErrors("@story", "@req"),
187
183
  },
188
184
  {
189
185
  name: "[REQ-BRANCH-DETECTION] missing annotations on do-while loop",
@@ -194,10 +190,7 @@ while (true) {}`,
194
190
  do {
195
191
  action();
196
192
  } while (condition);`,
197
- errors: [
198
- { messageId: "missingAnnotation", data: { missing: "@story" } },
199
- { messageId: "missingAnnotation", data: { missing: "@req" } },
200
- ],
193
+ errors: makeMissingAnnotationErrors("@story", "@req"),
201
194
  },
202
195
  {
203
196
  name: "[REQ-BRANCH-DETECTION] missing annotations on for-of loop",
@@ -208,10 +201,7 @@ do {
208
201
  for (const item of items) {
209
202
  process(item);
210
203
  }`,
211
- errors: [
212
- { messageId: "missingAnnotation", data: { missing: "@story" } },
213
- { messageId: "missingAnnotation", data: { missing: "@req" } },
214
- ],
204
+ errors: makeMissingAnnotationErrors("@story", "@req"),
215
205
  },
216
206
  {
217
207
  name: "[REQ-BRANCH-DETECTION] missing annotations on for-in loop",
@@ -222,10 +212,7 @@ for (const item of items) {
222
212
  for (const key in object) {
223
213
  console.log(key);
224
214
  }`,
225
- errors: [
226
- { messageId: "missingAnnotation", data: { missing: "@story" } },
227
- { messageId: "missingAnnotation", data: { missing: "@req" } },
228
- ],
215
+ errors: makeMissingAnnotationErrors("@story", "@req"),
229
216
  },
230
217
  {
231
218
  name: "[REQ-BRANCH-DETECTION] missing annotations on try-catch blocks",
@@ -241,10 +228,8 @@ try {
241
228
  handleError(error);
242
229
  }`,
243
230
  errors: [
244
- { messageId: "missingAnnotation", data: { missing: "@story" } },
245
- { messageId: "missingAnnotation", data: { missing: "@req" } },
246
- { messageId: "missingAnnotation", data: { missing: "@story" } },
247
- { messageId: "missingAnnotation", data: { missing: "@req" } },
231
+ ...makeMissingAnnotationErrors("@story", "@req"),
232
+ ...makeMissingAnnotationErrors("@story", "@req"),
248
233
  ],
249
234
  },
250
235
  {
@@ -260,10 +245,7 @@ try {
260
245
  case 'a':
261
246
  break;
262
247
  }`,
263
- errors: [
264
- { messageId: "missingAnnotation", data: { missing: "@story" } },
265
- { messageId: "missingAnnotation", data: { missing: "@req" } },
266
- ],
248
+ errors: makeMissingAnnotationErrors("@story", "@req"),
267
249
  },
268
250
  {
269
251
  name: "[REQ-CONFIGURABLE-SCOPE] missing annotations on configured branch type ForStatement",
@@ -271,10 +253,7 @@ try {
271
253
  options: [{ branchTypes: ["ForStatement"] }],
272
254
  output: `// @story <story-file>.story.md
273
255
  for (let i = 0; i < 3; i++) {}`,
274
- errors: [
275
- { messageId: "missingAnnotation", data: { missing: "@story" } },
276
- { messageId: "missingAnnotation", data: { missing: "@req" } },
277
- ],
256
+ errors: makeMissingAnnotationErrors("@story", "@req"),
278
257
  },
279
258
  ],
280
259
  });