eslint-plugin-traceability 1.11.0 → 1.11.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 (84) hide show
  1. package/CHANGELOG.md +3 -4
  2. package/README.md +1 -1
  3. package/lib/src/index.d.ts +12 -1
  4. package/lib/src/index.js +43 -6
  5. package/lib/src/maintenance/commands.js +2 -3
  6. package/lib/src/maintenance/flags.js +111 -25
  7. package/lib/src/maintenance/update.js +1 -14
  8. package/lib/src/rules/helpers/require-story-core.d.ts +67 -0
  9. package/lib/src/rules/helpers/require-story-core.js +142 -23
  10. package/lib/src/rules/helpers/require-story-helpers.d.ts +9 -88
  11. package/lib/src/rules/helpers/require-story-helpers.js +118 -166
  12. package/lib/src/rules/helpers/require-story-io.js +51 -31
  13. package/lib/src/rules/helpers/valid-annotation-format-internal.d.ts +12 -13
  14. package/lib/src/rules/helpers/valid-annotation-format-internal.js +21 -16
  15. package/lib/src/rules/helpers/valid-annotation-format-validators.d.ts +29 -3
  16. package/lib/src/rules/helpers/valid-annotation-format-validators.js +29 -3
  17. package/lib/src/rules/helpers/valid-annotation-options.d.ts +3 -0
  18. package/lib/src/rules/helpers/valid-annotation-options.js +64 -21
  19. package/lib/src/rules/helpers/valid-annotation-utils.d.ts +3 -3
  20. package/lib/src/rules/helpers/valid-annotation-utils.js +10 -10
  21. package/lib/src/rules/helpers/valid-req-reference-helpers.d.ts +11 -0
  22. package/lib/src/rules/helpers/valid-req-reference-helpers.js +362 -0
  23. package/lib/src/rules/prefer-implements-annotation.js +7 -7
  24. package/lib/src/rules/require-story-annotation.d.ts +2 -0
  25. package/lib/src/rules/require-story-annotation.js +1 -1
  26. package/lib/src/rules/valid-req-reference.d.ts +4 -0
  27. package/lib/src/rules/valid-req-reference.js +5 -349
  28. package/lib/src/rules/valid-story-reference.d.ts +1 -1
  29. package/lib/src/rules/valid-story-reference.js +17 -10
  30. package/lib/src/utils/annotation-checker.js +31 -7
  31. package/lib/src/utils/branch-annotation-helpers.d.ts +2 -2
  32. package/lib/src/utils/branch-annotation-helpers.js +4 -4
  33. package/lib/src/utils/reqAnnotationDetection.js +36 -22
  34. package/lib/tests/cli-error-handling.test.js +2 -1
  35. package/lib/tests/config/eslint-config-validation.test.d.ts +8 -0
  36. package/lib/tests/config/eslint-config-validation.test.js +81 -0
  37. package/lib/tests/config/flat-config-presets-integration.test.js +1 -3
  38. package/lib/tests/config/require-story-annotation-config.test.d.ts +9 -0
  39. package/lib/tests/config/require-story-annotation-config.test.js +9 -0
  40. package/lib/tests/fixtures/stale/example.js +1 -1
  41. package/lib/tests/fixtures/update/example.js +1 -1
  42. package/lib/tests/integration/cli-integration.test.js +9 -1
  43. package/lib/tests/integration/dogfooding-validation.test.d.ts +1 -0
  44. package/lib/tests/integration/dogfooding-validation.test.js +94 -0
  45. package/lib/tests/maintenance/batch.test.js +1 -0
  46. package/lib/tests/maintenance/cli.test.js +38 -0
  47. package/lib/tests/maintenance/detect-isolated.test.js +6 -5
  48. package/lib/tests/maintenance/detect.test.js +1 -0
  49. package/lib/tests/maintenance/index.test.js +1 -0
  50. package/lib/tests/maintenance/report.test.js +1 -0
  51. package/lib/tests/maintenance/update-isolated.test.js +1 -0
  52. package/lib/tests/maintenance/update.test.js +1 -0
  53. package/lib/tests/perf/maintenance-cli-large-workspace.test.js +18 -0
  54. package/lib/tests/perf/require-branch-annotation-large-file.test.d.ts +1 -0
  55. package/lib/tests/perf/require-branch-annotation-large-file.test.js +67 -0
  56. package/lib/tests/plugin-default-export-and-configs.test.js +2 -0
  57. package/lib/tests/plugin-setup-error.test.d.ts +1 -0
  58. package/lib/tests/plugin-setup-error.test.js +1 -0
  59. package/lib/tests/plugin-setup.test.js +12 -1
  60. package/lib/tests/rules/auto-fix-behavior-008.test.js +16 -0
  61. package/lib/tests/rules/error-reporting.test.js +1 -0
  62. package/lib/tests/rules/prefer-implements-annotation.test.js +8 -0
  63. package/lib/tests/rules/require-branch-annotation.test.js +34 -0
  64. package/lib/tests/rules/require-story-core-edgecases.test.js +1 -0
  65. package/lib/tests/rules/require-story-core.autofix.test.js +1 -0
  66. package/lib/tests/rules/require-story-core.test.js +1 -0
  67. package/lib/tests/rules/require-story-helpers-edgecases.test.d.ts +1 -0
  68. package/lib/tests/rules/require-story-helpers-edgecases.test.js +1 -0
  69. package/lib/tests/rules/require-story-helpers.test.js +4 -3
  70. package/lib/tests/rules/require-story-io-behavior.test.d.ts +1 -0
  71. package/lib/tests/rules/require-story-io-behavior.test.js +1 -0
  72. package/lib/tests/rules/require-story-io.edgecases.test.d.ts +1 -0
  73. package/lib/tests/rules/require-story-io.edgecases.test.js +1 -0
  74. package/lib/tests/rules/require-story-visitors-edgecases.test.d.ts +1 -0
  75. package/lib/tests/rules/require-story-visitors-edgecases.test.js +1 -0
  76. package/lib/tests/rules/valid-annotation-format-internal.test.d.ts +8 -0
  77. package/lib/tests/rules/valid-annotation-format-internal.test.js +47 -0
  78. package/lib/tests/rules/valid-story-reference.test.js +2 -0
  79. package/lib/tests/utils/annotation-checker.test.js +2 -1
  80. package/lib/tests/utils/branch-annotation-helpers.test.js +2 -1
  81. package/package.json +2 -2
  82. package/user-docs/api-reference.md +115 -8
  83. package/user-docs/examples.md +1 -1
  84. package/user-docs/migration-guide.md +35 -2
@@ -1 +1,9 @@
1
+ /**
2
+ * Tests for ESLint config rule schemas.
3
+ *
4
+ * @supports docs/stories/002.0-DEV-ESLINT-CONFIG.story.md
5
+ * @req REQ-RULE-OPTIONS
6
+ * @req REQ-CONFIG-VALIDATION
7
+ * @story docs/stories/002.0-DEV-ESLINT-CONFIG.story.md
8
+ */
1
9
  export {};
@@ -1,9 +1,19 @@
1
1
  "use strict";
2
+ /**
3
+ * Tests for ESLint config rule schemas.
4
+ *
5
+ * @supports docs/stories/002.0-DEV-ESLINT-CONFIG.story.md
6
+ * @req REQ-RULE-OPTIONS
7
+ * @req REQ-CONFIG-VALIDATION
8
+ * @story docs/stories/002.0-DEV-ESLINT-CONFIG.story.md
9
+ */
2
10
  var __importDefault = (this && this.__importDefault) || function (mod) {
3
11
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
12
  };
5
13
  Object.defineProperty(exports, "__esModule", { value: true });
6
14
  const valid_story_reference_1 = __importDefault(require("../../src/rules/valid-story-reference"));
15
+ const use_at_your_own_risk_1 = require("eslint/use-at-your-own-risk");
16
+ const index_1 = __importDefault(require("../../src/index"));
7
17
  /** @story docs/stories/002.0-DEV-ESLINT-CONFIG.story.md */
8
18
  describe("ESLint Configuration Setup (Story 002.0-DEV-ESLINT-CONFIG)", () => {
9
19
  it("[REQ-RULE-OPTIONS] rule meta.schema defines expected properties", () => {
@@ -16,4 +26,75 @@ describe("ESLint Configuration Setup (Story 002.0-DEV-ESLINT-CONFIG)", () => {
16
26
  const schema = valid_story_reference_1.default.meta.schema[0];
17
27
  expect(schema.additionalProperties).toBe(false);
18
28
  });
29
+ it("[REQ-CONFIG-VALIDATION] ESLint throws on unknown rule option", async () => {
30
+ const eslint = new use_at_your_own_risk_1.FlatESLint({
31
+ overrideConfig: [
32
+ {
33
+ plugins: {
34
+ traceability: index_1.default,
35
+ },
36
+ rules: {
37
+ "traceability/valid-story-reference": [
38
+ "error",
39
+ {
40
+ storyDirectories: ["stories"],
41
+ allowAbsolutePaths: false,
42
+ requireStoryExtension: true,
43
+ unknownOptionKey: true,
44
+ },
45
+ ],
46
+ },
47
+ },
48
+ ],
49
+ overrideConfigFile: true,
50
+ ignore: false,
51
+ });
52
+ let caughtError;
53
+ try {
54
+ await eslint.lintText("const x = 1;");
55
+ }
56
+ catch (err) {
57
+ caughtError = err;
58
+ }
59
+ expect(caughtError).toBeInstanceOf(Error);
60
+ const message = String(caughtError.message || caughtError);
61
+ expect(message).toContain("traceability/valid-story-reference");
62
+ expect(message.toLowerCase()).toContain("additional");
63
+ expect(message.toLowerCase()).toContain("unexpected property");
64
+ expect(message).toContain("unknownOptionKey");
65
+ });
66
+ it("[REQ-CONFIG-VALIDATION] ESLint throws on invalid option type", async () => {
67
+ const eslint = new use_at_your_own_risk_1.FlatESLint({
68
+ overrideConfig: [
69
+ {
70
+ plugins: {
71
+ traceability: index_1.default,
72
+ },
73
+ rules: {
74
+ "traceability/valid-story-reference": [
75
+ "error",
76
+ {
77
+ // storyDirectories must be an array, not a string
78
+ storyDirectories: "not-an-array",
79
+ },
80
+ ],
81
+ },
82
+ },
83
+ ],
84
+ overrideConfigFile: true,
85
+ ignore: false,
86
+ });
87
+ let caughtError;
88
+ try {
89
+ await eslint.lintText("const y = 2;");
90
+ }
91
+ catch (err) {
92
+ caughtError = err;
93
+ }
94
+ expect(caughtError).toBeInstanceOf(Error);
95
+ const message = String(caughtError.message || caughtError);
96
+ expect(message).toContain("traceability/valid-story-reference");
97
+ expect(message).toContain("not-an-array");
98
+ expect(message.toLowerCase()).toContain("array");
99
+ });
19
100
  });
@@ -36,9 +36,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
36
36
  /**
37
37
  * Tests for: docs/stories/002.0-DEV-ESLINT-CONFIG.story.md
38
38
  * @story docs/stories/002.0-DEV-ESLINT-CONFIG.story.md
39
- * @req REQ-CONFIG-PRESETS - Validate flat-config presets register traceability plugin and rules
40
- * @req REQ-FLAT-CONFIG - Ensure presets work with ESLint v9 flat config
41
- * @req REQ-PROJECT-INTEGRATION - Support seamless integration via documented preset usage
39
+ * @supports docs/stories/002.0-DEV-ESLINT-CONFIG.story.md REQ-CONFIG-PRESETS REQ-FLAT-CONFIG REQ-PROJECT-INTEGRATION
42
40
  */
43
41
  const use_at_your_own_risk_1 = require("eslint/use-at-your-own-risk");
44
42
  const index_1 = __importStar(require("../../src/index"));
@@ -1 +1,10 @@
1
+ /**
2
+ * Tests for the require-story-annotation rule schema configuration.
3
+ *
4
+ * Verifies that the ESLint rule options for require-story-annotation
5
+ * define the expected schema properties and constraints.
6
+ *
7
+ * @story docs/stories/002.0-DEV-ESLINT-CONFIG.story.md
8
+ * @supports docs/stories/002.0-DEV-ESLINT-CONFIG.story.md REQ-RULE-OPTIONS
9
+ */
1
10
  export {};
@@ -1,4 +1,13 @@
1
1
  "use strict";
2
+ /**
3
+ * Tests for the require-story-annotation rule schema configuration.
4
+ *
5
+ * Verifies that the ESLint rule options for require-story-annotation
6
+ * define the expected schema properties and constraints.
7
+ *
8
+ * @story docs/stories/002.0-DEV-ESLINT-CONFIG.story.md
9
+ * @supports docs/stories/002.0-DEV-ESLINT-CONFIG.story.md REQ-RULE-OPTIONS
10
+ */
2
11
  var __importDefault = (this && this.__importDefault) || function (mod) {
3
12
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
13
  };
@@ -1,3 +1,3 @@
1
1
  "use strict";
2
2
  // Sample code with stale annotation
3
- // @story non-existent.md
3
+ // @story docs/stories/non-existent.story.md
@@ -1,3 +1,3 @@
1
1
  "use strict";
2
2
  // Sample code with annotation to update
3
- // @story old.md
3
+ // @story docs/stories/old.story.md
@@ -3,6 +3,14 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
+ /**
7
+ * Tests for CLI integration of the traceability plugin.
8
+ * Validates that the plugin registers correctly and enforces
9
+ * traceability-related rules when invoked via the ESLint CLI.
10
+ *
11
+ * @supports docs/stories/001.0-DEV-PLUGIN-SETUP.story.md REQ-PLUGIN-STRUCTURE
12
+ * @story docs/stories/001.0-DEV-PLUGIN-SETUP.story.md
13
+ */
6
14
  /**
7
15
  * Tests for CLI integration functionality
8
16
  * @story docs/stories/001.0-DEV-PLUGIN-SETUP.story.md
@@ -10,7 +18,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
10
18
  */
11
19
  const child_process_1 = require("child_process");
12
20
  const path_1 = __importDefault(require("path"));
13
- describe("[docs/stories/001.0-DEV-PLUGIN-SETUP.story.md] CLI Integration (traceability plugin)", () => {
21
+ describe("CLI Integration (Story 001.0-DEV-PLUGIN-SETUP)", () => {
14
22
  const eslintPkgDir = path_1.default.dirname(require.resolve("eslint/package.json"));
15
23
  const eslintCliPath = path_1.default.join(eslintPkgDir, "bin", "eslint.js");
16
24
  const configPath = path_1.default.resolve(__dirname, "../../eslint.config.js");
@@ -0,0 +1,94 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ /**
37
+ * Dogfooding validation integration tests
38
+ * @supports docs/stories/023.0-MAINT-DOGFOODING-VALIDATION.story.md REQ-DOGFOODING-TEST REQ-DOGFOODING-CI
39
+ */
40
+ const path = __importStar(require("path"));
41
+ const child_process_1 = require("child_process");
42
+ /**
43
+ * @supports docs/stories/023.0-MAINT-DOGFOODING-VALIDATION.story.md REQ-DOGFOODING-TEST
44
+ */
45
+ function getTsConfigFromEslintConfig(eslintConfig) {
46
+ const configs = Array.isArray(eslintConfig) ? eslintConfig : [eslintConfig];
47
+ return configs.find((config) => {
48
+ if (!config || !config.files)
49
+ return false;
50
+ const files = config.files;
51
+ return files.includes("**/*.ts") && files.includes("**/*.tsx");
52
+ });
53
+ }
54
+ describe("Dogfooding Validation (Story 023.0-MAINT-DOGFOODING-VALIDATION)", () => {
55
+ it("[REQ-DOGFOODING-TEST] should have traceability/require-story-annotation enabled for TS sources", () => {
56
+ /**
57
+ * @supports docs/stories/023.0-MAINT-DOGFOODING-VALIDATION.story.md REQ-DOGFOODING-TEST
58
+ */
59
+ // Require the project's eslint.config.js and find the TS-specific config
60
+ // that applies to *.ts and *.tsx files.
61
+ const eslintConfig = require("../../eslint.config.js");
62
+ const tsConfig = getTsConfigFromEslintConfig(eslintConfig);
63
+ expect(tsConfig).toBeDefined();
64
+ const rules = tsConfig.rules || {};
65
+ const ruleEntry = rules["traceability/require-story-annotation"];
66
+ expect(ruleEntry).toBeDefined();
67
+ const severity = Array.isArray(ruleEntry) && ruleEntry.length > 0
68
+ ? ruleEntry[0]
69
+ : ruleEntry;
70
+ expect(severity).toBe("error");
71
+ });
72
+ it("[REQ-DOGFOODING-CI] should run traceability/require-story-annotation via ESLint CLI on TS sources", () => {
73
+ /**
74
+ * @supports docs/stories/023.0-MAINT-DOGFOODING-VALIDATION.story.md REQ-DOGFOODING-CI
75
+ */
76
+ const eslintBin = path.resolve(__dirname, "../../node_modules/.bin/eslint");
77
+ const configPath = path.resolve(__dirname, "../../eslint.config.js");
78
+ const tsSnippet = `
79
+ const x: number = 42;
80
+ export function foo() {
81
+ return x;
82
+ }
83
+ `;
84
+ const result = (0, child_process_1.spawnSync)(process.platform === "win32" ? `${eslintBin}.cmd` : eslintBin, ["--config", configPath, "--stdin", "--stdin-filename", "src/dogfood.ts"], {
85
+ encoding: "utf8",
86
+ input: tsSnippet,
87
+ });
88
+ // The snippet intentionally lacks @story annotations, so the rule should
89
+ // report an error for the generated `src/dogfood.ts` virtual file.
90
+ expect(result.status).not.toBe(0);
91
+ expect(result.stdout).toContain("error");
92
+ expect(result.stdout).toContain("src/dogfood.ts");
93
+ });
94
+ });
@@ -38,6 +38,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
38
38
  * @story docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md
39
39
  * @req REQ-MAINT-BATCH - Perform batch updates
40
40
  * @req REQ-MAINT-VERIFY - Verify annotation references
41
+ * @supports docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-BATCH REQ-MAINT-VERIFY
41
42
  */
42
43
  const fs = __importStar(require("fs"));
43
44
  const path = __importStar(require("path"));
@@ -11,6 +11,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
11
11
  * @req REQ-MAINT-REPORT - CLI reporting of stale annotations
12
12
  * @req REQ-MAINT-UPDATE - CLI updating of annotation references
13
13
  * @req REQ-MAINT-SAFE - Clear exit codes and non-destructive dry-run
14
+ * @supports docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-DETECT REQ-MAINT-VERIFY REQ-MAINT-REPORT REQ-MAINT-UPDATE REQ-MAINT-SAFE
14
15
  */
15
16
  const fs_1 = __importDefault(require("fs"));
16
17
  const path_1 = __importDefault(require("path"));
@@ -57,6 +58,26 @@ describe("Maintenance CLI (Story 009.0-DEV-MAINTENANCE-TOOLS)", () => {
57
58
  temp.cleanup();
58
59
  }
59
60
  });
61
+ it("[REQ-MAINT-VERIFY] verify exits with code 1 and prints guidance when annotations are stale or invalid", () => {
62
+ const temp = (0, temp_dir_helpers_1.createTempDir)("maint-cli-");
63
+ const dir = temp.dir;
64
+ process.chdir(dir);
65
+ const tsContent = `/**\n * @story missing.story.md\n */`;
66
+ fs_1.default.writeFileSync(path_1.default.join(dir, "file.ts"), tsContent, "utf8");
67
+ const logSpy = jest.spyOn(console, "log").mockImplementation(() => { });
68
+ const code = (0, cli_1.runMaintenanceCli)(["node", "traceability-maint", "verify"]);
69
+ try {
70
+ expect(code).toBe(1);
71
+ expect(logSpy).toHaveBeenCalledTimes(1);
72
+ const message = String(logSpy.mock.calls[0][0]);
73
+ expect(message).toContain("Stale or invalid traceability annotations detected under");
74
+ expect(message).toContain("Run 'traceability-maint detect' or 'traceability-maint report' for details.");
75
+ }
76
+ finally {
77
+ logSpy.mockRestore();
78
+ temp.cleanup();
79
+ }
80
+ });
60
81
  it("[REQ-MAINT-REPORT] report prints human-readable summary and exits 0", () => {
61
82
  const temp = (0, temp_dir_helpers_1.createTempDir)("maint-cli-");
62
83
  const dir = temp.dir;
@@ -76,6 +97,23 @@ describe("Maintenance CLI (Story 009.0-DEV-MAINTENANCE-TOOLS)", () => {
76
97
  temp.cleanup();
77
98
  }
78
99
  });
100
+ it("[REQ-MAINT-REPORT] report prints 'nothing to report' when no stale annotations exist", () => {
101
+ const temp = (0, temp_dir_helpers_1.createTempDir)("maint-cli-");
102
+ const dir = temp.dir;
103
+ process.chdir(dir);
104
+ const logSpy = jest.spyOn(console, "log").mockImplementation(() => { });
105
+ const code = (0, cli_1.runMaintenanceCli)(["node", "traceability-maint", "report"]);
106
+ try {
107
+ expect(code).toBe(0);
108
+ expect(logSpy).toHaveBeenCalled();
109
+ const allMessages = logSpy.mock.calls.flat().join("\n");
110
+ expect(allMessages).toContain("No stale @story annotations found. Nothing to report.");
111
+ }
112
+ finally {
113
+ logSpy.mockRestore();
114
+ temp.cleanup();
115
+ }
116
+ });
79
117
  it("[REQ-MAINT-UPDATE] update performs replacements and exits 0", () => {
80
118
  const temp = (0, temp_dir_helpers_1.createTempDir)("maint-cli-");
81
119
  const dir = temp.dir;
@@ -37,6 +37,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
37
37
  * Tests for: docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md
38
38
  * @story docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md
39
39
  * @req REQ-MAINT-DETECT - Detect stale annotation references
40
+ * @supports docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-DETECT
40
41
  */
41
42
  const path = __importStar(require("path"));
42
43
  const os = __importStar(require("os"));
@@ -56,26 +57,26 @@ describe("detectStaleAnnotations isolated (Story 009.0-DEV-MAINTENANCE-TOOLS)",
56
57
  const filePath2 = path.join(nestedDir, "file2.ts");
57
58
  const content1 = `
58
59
  /**
59
- * @story stale1.story.md
60
+ * @story docs/stories/non-existent-story.story.md
60
61
  */
61
62
  `;
62
63
  fs.writeFileSync(filePath1, content1, "utf8");
63
64
  const content2 = `
64
65
  /**
65
- * @story stale2.story.md
66
+ * @story docs/stories/another-non-existent.story.md
66
67
  */
67
68
  `;
68
69
  fs.writeFileSync(filePath2, content2, "utf8");
69
70
  const result = (0, detect_1.detectStaleAnnotations)(tmpDir);
70
71
  expect(result).toHaveLength(2);
71
- expect(result).toContain("stale1.story.md");
72
- expect(result).toContain("stale2.story.md");
72
+ expect(result).toContain("docs/stories/non-existent-story.story.md");
73
+ expect(result).toContain("docs/stories/another-non-existent.story.md");
73
74
  }
74
75
  finally {
75
76
  fs.rmSync(tmpDir, { recursive: true, force: true });
76
77
  }
77
78
  });
78
- it("[REQ-MAINT-DETECT] throws error on permission denied", () => {
79
+ it("[REQ-MAINT-DETECT] handles permission denied errors by returning an empty result", () => {
79
80
  const tmpDir2 = fs.mkdtempSync(path.join(os.tmpdir(), "tmp-perm-"));
80
81
  const dir = path.join(tmpDir2, "subdir");
81
82
  fs.mkdirSync(dir);
@@ -7,6 +7,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
7
7
  * Tests for: docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md
8
8
  * @story docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md
9
9
  * @req REQ-MAINT-DETECT - Detect stale annotation references
10
+ * @supports docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-DETECT
10
11
  */
11
12
  const fs_1 = __importDefault(require("fs"));
12
13
  const path_1 = __importDefault(require("path"));
@@ -4,6 +4,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
4
4
  * Tests for: docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md
5
5
  * @story docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md
6
6
  * @req REQ-MAINT-SAFE - Ensure all maintenance tools are exported correctly
7
+ * @supports docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-SAFE REQ-MAINT-DETECT REQ-MAINT-UPDATE REQ-MAINT-BATCH REQ-MAINT-VERIFY REQ-MAINT-REPORT
7
8
  */
8
9
  const maintenance_1 = require("../../src/maintenance");
9
10
  describe("Maintenance Tools Index Exports (Story 009.0-DEV-MAINTENANCE-TOOLS)", () => {
@@ -38,6 +38,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
38
38
  * @story docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md
39
39
  * @req REQ-MAINT-REPORT - Generate maintenance report
40
40
  * @req REQ-MAINT-SAFE - Ensure operations are safe and reversible
41
+ * @supports docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-REPORT REQ-MAINT-SAFE
41
42
  */
42
43
  const fs = __importStar(require("fs"));
43
44
  const path = __importStar(require("path"));
@@ -37,6 +37,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
37
37
  * Tests for: docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md
38
38
  * @story docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md
39
39
  * @req REQ-MAINT-UPDATE - Update annotation references
40
+ * @supports docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-UPDATE
40
41
  */
41
42
  const fs = __importStar(require("fs"));
42
43
  const path = __importStar(require("path"));
@@ -7,6 +7,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
7
7
  * Tests for: docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md
8
8
  * @story docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md
9
9
  * @req REQ-MAINT-UPDATE - Update annotation references
10
+ * @supports docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-UPDATE
10
11
  */
11
12
  const fs_1 = __importDefault(require("fs"));
12
13
  const os_1 = __importDefault(require("os"));
@@ -127,4 +127,22 @@ describe("Maintenance CLI on large workspaces (Story 009.0-DEV-MAINTENANCE-TOOLS
127
127
  expect(typeof payload.report).toBe("string");
128
128
  logSpy.mockRestore();
129
129
  });
130
+ it("[REQ-MAINT-VERIFY] verify completes within a generous time budget and reports stale annotations", () => {
131
+ const logSpy = jest.spyOn(console, "log").mockImplementation(() => { });
132
+ const start = perf_hooks_1.performance.now();
133
+ const exitCode = (0, cli_1.runMaintenanceCli)([
134
+ "node",
135
+ "traceability-maint",
136
+ "verify",
137
+ "--root",
138
+ workspace.root,
139
+ ]);
140
+ const durationMs = perf_hooks_1.performance.now() - start;
141
+ expect(exitCode).toBe(1);
142
+ expect(durationMs).toBeLessThan(5000);
143
+ expect(logSpy).toHaveBeenCalledTimes(1);
144
+ const message = String(logSpy.mock.calls[0][0]);
145
+ expect(message).toContain("Stale or invalid traceability annotations detected under");
146
+ logSpy.mockRestore();
147
+ });
130
148
  });
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ /**
7
+ * Performance tests for require-branch-annotation on large nested-branch files.
8
+ *
9
+ * @supports docs/stories/004.0-DEV-BRANCH-ANNOTATIONS.story.md REQ-PERFORMANCE-OPTIMIZATION REQ-NESTED-HANDLING
10
+ */
11
+ const eslint_1 = require("eslint");
12
+ const perf_hooks_1 = require("perf_hooks");
13
+ const require_branch_annotation_1 = __importDefault(require("../../src/rules/require-branch-annotation"));
14
+ /**
15
+ * Build a large source file containing many nested branch structures
16
+ * (if-statements within if-statements) to exercise the rule at scale.
17
+ *
18
+ * The generated code intentionally omits annotations so that the rule
19
+ * produces diagnostics for both outer and inner branches.
20
+ */
21
+ function buildLargeNestedBranchSource(functionCount, nestingDepth) {
22
+ const lines = [];
23
+ for (let i = 0; i < functionCount; i += 1) {
24
+ lines.push(`function fn_${i}() {`);
25
+ lines.push(" let x = 0;");
26
+ // Create a staircase of nested if-statements.
27
+ for (let depth = 0; depth < nestingDepth; depth += 1) {
28
+ const indent = " ".repeat(depth + 1);
29
+ lines.push(`${indent}if (x > ${depth}) {`);
30
+ }
31
+ const innerIndent = " ".repeat(nestingDepth + 1);
32
+ lines.push(`${innerIndent}if (x % 2 === 0) {`);
33
+ lines.push(`${innerIndent} x++;`);
34
+ lines.push(`${innerIndent}} else {`);
35
+ lines.push(`${innerIndent} x--;`);
36
+ lines.push(`${innerIndent}}`);
37
+ // Close all nested if blocks.
38
+ for (let depth = nestingDepth - 1; depth >= 0; depth -= 1) {
39
+ const indent = " ".repeat(depth + 1);
40
+ lines.push(`${indent}}`);
41
+ }
42
+ lines.push("}");
43
+ }
44
+ return lines.join("\n");
45
+ }
46
+ describe("require-branch-annotation performance on large nested-branch files (Story 004.0-DEV-BRANCH-ANNOTATIONS)", () => {
47
+ const ruleName = "traceability/require-branch-annotation";
48
+ it("[REQ-PERFORMANCE-OPTIMIZATION] analyzes a large nested-branch file within a generous time budget", () => {
49
+ const linter = new eslint_1.Linter({ configType: "eslintrc" });
50
+ linter.defineRule(ruleName, require_branch_annotation_1.default);
51
+ // 200 functions each with several nested branches gives us
52
+ // a substantial number of branch nodes without being extreme.
53
+ const source = buildLargeNestedBranchSource(200, 3);
54
+ const start = perf_hooks_1.performance.now();
55
+ const messages = linter.verify(source, {
56
+ parserOptions: { ecmaVersion: 2020, sourceType: "module" },
57
+ rules: {
58
+ [ruleName]: "error",
59
+ },
60
+ });
61
+ const durationMs = perf_hooks_1.performance.now() - start;
62
+ // Sanity check: we expect diagnostics for many branches.
63
+ expect(messages.length).toBeGreaterThan(0);
64
+ // Guardrail: keep analysis comfortably under ~5 seconds on CI hardware.
65
+ expect(durationMs).toBeLessThan(5000);
66
+ });
67
+ });
@@ -35,6 +35,8 @@ var __importStar = (this && this.__importStar) || (function () {
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  /**
37
37
  * Tests for: docs/stories/001.0-DEV-PLUGIN-SETUP.story.md
38
+ * @supports docs/stories/001.0-DEV-PLUGIN-SETUP.story.md REQ-PLUGIN-STRUCTURE REQ-RULE-REGISTRY REQ-CONFIG-SYSTEM
39
+ * @supports docs/stories/007.0-DEV-ERROR-REPORTING.story.md REQ-ERROR-SEVERITY
38
40
  * @story docs/stories/001.0-DEV-PLUGIN-SETUP.story.md
39
41
  * @story docs/stories/007.0-DEV-ERROR-REPORTING.story.md
40
42
  * @req REQ-PLUGIN-STRUCTURE - Validate plugin default export and configs in src/index.ts
@@ -2,4 +2,5 @@
2
2
  * Tests for: docs/stories/001.0-DEV-PLUGIN-SETUP.story.md
3
3
  * @story docs/stories/001.0-DEV-PLUGIN-SETUP.story.md
4
4
  * @req REQ-ERROR-HANDLING - Gracefully handles plugin loading errors and missing dependencies
5
+ * @supports docs/stories/001.0-DEV-PLUGIN-SETUP.story.md REQ-ERROR-HANDLING
5
6
  */
@@ -3,6 +3,7 @@
3
3
  * Tests for: docs/stories/001.0-DEV-PLUGIN-SETUP.story.md
4
4
  * @story docs/stories/001.0-DEV-PLUGIN-SETUP.story.md
5
5
  * @req REQ-ERROR-HANDLING - Gracefully handles plugin loading errors and missing dependencies
6
+ * @supports docs/stories/001.0-DEV-PLUGIN-SETUP.story.md REQ-ERROR-HANDLING
6
7
  */
7
8
  describe("Traceability ESLint Plugin Error Handling (Story 001.0-DEV-PLUGIN-SETUP)", () => {
8
9
  beforeEach(() => {
@@ -36,7 +36,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
36
36
  /**
37
37
  * Tests for: docs/stories/001.0-DEV-PLUGIN-SETUP.story.md
38
38
  * @story docs/stories/001.0-DEV-PLUGIN-SETUP.story.md
39
- * @req REQ-PLUGIN-STRUCTURE - plugin exports rules and configs
39
+ * @supports docs/stories/001.0-DEV-PLUGIN-SETUP.story.md REQ-PLUGIN-STRUCTURE REQ-NPM-PACKAGE
40
40
  */
41
41
  const index_1 = __importStar(require("../src/index"));
42
42
  describe("Traceability ESLint Plugin (Story 001.0-DEV-PLUGIN-SETUP)", () => {
@@ -48,4 +48,15 @@ describe("Traceability ESLint Plugin (Story 001.0-DEV-PLUGIN-SETUP)", () => {
48
48
  expect(index_1.default.rules).toBe(index_1.rules);
49
49
  expect(index_1.default.configs).toBe(index_1.configs);
50
50
  });
51
+ it("[REQ-PLUGIN-STRUCTURE][REQ-NPM-PACKAGE] plugin exposes meta with name, namespace, and version", () => {
52
+ // Arrange
53
+ const pkg = require("../package.json");
54
+ // Act
55
+ const meta = index_1.default.meta;
56
+ // Assert
57
+ expect(meta).toBeDefined();
58
+ expect(meta.name).toBe(pkg.name);
59
+ expect(meta.version).toBe(pkg.version);
60
+ expect(meta.namespace).toBe("traceability");
61
+ });
51
62
  });
@@ -8,6 +8,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
8
8
  * @story docs/stories/008.0-DEV-AUTO-FIX.story.md
9
9
  * @req REQ-AUTOFIX-MISSING - Verify ESLint --fix automatically adds missing @story annotations to functions
10
10
  * @req REQ-AUTOFIX-FORMAT - Verify ESLint --fix corrects simple annotation format issues for @story annotations
11
+ * @supports docs/stories/008.0-DEV-AUTO-FIX.story.md REQ-AUTOFIX-MISSING REQ-AUTOFIX-FORMAT
11
12
  */
12
13
  const eslint_1 = require("eslint");
13
14
  const require_story_annotation_1 = __importDefault(require("../../src/rules/require-story-annotation"));
@@ -177,6 +178,21 @@ describe("Auto-fix behavior (Story 008.0-DEV-AUTO-FIX)", () => {
177
178
  },
178
179
  ],
179
180
  },
181
+ {
182
+ name: "[REQ-AUTOFIX-SELECTIVE] does not apply suffix fix when autoFix is false",
183
+ code: `// @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story`,
184
+ output: null,
185
+ options: [
186
+ {
187
+ autoFix: false,
188
+ },
189
+ ],
190
+ errors: [
191
+ {
192
+ messageId: "invalidStoryFormat",
193
+ },
194
+ ],
195
+ },
180
196
  ],
181
197
  });
182
198
  });
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  /**
7
7
  * Tests for: docs/stories/007.0-DEV-ERROR-REPORTING.story.md
8
8
  * @story docs/stories/007.0-DEV-ERROR-REPORTING.story.md
9
+ * @supports docs/stories/007.0-DEV-ERROR-REPORTING.story.md REQ-ERROR-SPECIFIC REQ-ERROR-SUGGESTION REQ-ERROR-CONTEXT REQ-ERROR-LOCATION
9
10
  * @req REQ-ERROR-SPECIFIC - Specific details about what annotation is missing or invalid
10
11
  * @req REQ-ERROR-SUGGESTION - Suggest concrete steps to fix the issue
11
12
  * @req REQ-ERROR-CONTEXT - Include relevant context in error messages
@@ -33,6 +33,14 @@ describe("prefer-implements-annotation rule (Story 010.3-DEV-MIGRATE-TO-SUPPORTS
33
33
  name: "[REQ-BACKWARD-COMP-VALIDATION] comment with @supports only is ignored",
34
34
  code: `/**\n * @supports docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md REQ-ANNOTATION-REQUIRED\n */\nfunction alreadyImplements() {}`,
35
35
  },
36
+ {
37
+ name: "[REQ-BACKWARD-COMP-VALIDATION] comment with @story and @supports but no @req is ignored",
38
+ code: `/**\n * @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md\n * @supports docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md REQ-ANNOTATION-REQUIRED\n */\nfunction storyAndSupportsNoReq() {}`,
39
+ },
40
+ {
41
+ name: "[REQ-BACKWARD-COMP-VALIDATION] comment with @req and @supports but no @story is ignored",
42
+ code: `/**\n * @req REQ-ANNOTATION-REQUIRED\n * @supports docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md REQ-ANNOTATION-REQUIRED\n */\nfunction reqAndSupportsNoStory() {}`,
43
+ },
36
44
  ],
37
45
  invalid: [
38
46
  {