eslint-plugin-traceability 1.8.1 → 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.
- package/CHANGELOG.md +4 -4
- package/README.md +4 -4
- package/SECURITY.md +40 -37
- package/lib/src/maintenance/cli.js +12 -16
- package/lib/src/maintenance/detect.js +28 -1
- package/lib/src/rules/helpers/require-story-io.d.ts +2 -2
- package/lib/src/rules/helpers/require-story-io.js +13 -13
- package/lib/src/rules/helpers/valid-annotation-format-internal.d.ts +2 -2
- package/lib/src/rules/helpers/valid-annotation-format-internal.js +3 -3
- package/lib/src/rules/helpers/valid-annotation-utils.d.ts +5 -0
- package/lib/src/rules/helpers/valid-annotation-utils.js +43 -5
- package/lib/src/rules/helpers/valid-implements-utils.d.ts +11 -11
- package/lib/src/rules/helpers/valid-implements-utils.js +11 -11
- package/lib/src/rules/helpers/valid-story-reference-helpers.js +19 -0
- package/lib/src/rules/prefer-implements-annotation.d.ts +7 -7
- package/lib/src/rules/prefer-implements-annotation.js +21 -21
- package/lib/src/rules/valid-annotation-format.js +50 -24
- package/lib/src/rules/valid-req-reference.js +9 -9
- package/lib/src/utils/annotation-checker.js +3 -1
- package/lib/src/utils/reqAnnotationDetection.d.ts +2 -2
- package/lib/src/utils/reqAnnotationDetection.js +28 -28
- package/lib/tests/maintenance/batch.test.js +11 -11
- package/lib/tests/maintenance/cli.test.js +34 -27
- package/lib/tests/maintenance/report.test.js +7 -7
- package/lib/tests/rules/prefer-implements-annotation.test.js +27 -22
- package/lib/tests/rules/require-branch-annotation.test.js +15 -36
- package/lib/tests/rules/require-req-annotation.test.js +31 -104
- package/lib/tests/rules/require-story-annotation.test.js +3 -3
- package/lib/tests/rules/require-story-io-behavior.test.js +2 -7
- package/lib/tests/rules/require-story-io.edgecases.test.js +2 -7
- package/lib/tests/rules/require-story-visitors-edgecases.test.js +8 -8
- package/lib/tests/rules/valid-annotation-format.test.js +23 -23
- package/lib/tests/rules/valid-req-reference.test.js +9 -9
- package/lib/tests/rules/valid-story-reference.test.js +4 -43
- package/lib/tests/utils/annotation-checker.test.js +2 -6
- package/lib/tests/utils/fsTestHelpers.d.ts +7 -0
- package/lib/tests/utils/fsTestHelpers.js +26 -0
- package/lib/tests/utils/ioTestHelpers.d.ts +7 -0
- package/lib/tests/utils/ioTestHelpers.js +24 -0
- package/lib/tests/utils/temp-dir-helpers.d.ts +14 -0
- package/lib/tests/utils/temp-dir-helpers.js +61 -0
- package/package.json +3 -2
- package/user-docs/api-reference.md +12 -15
- package/user-docs/migration-guide.md +21 -21
package/CHANGELOG.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
## [1.8.
|
|
1
|
+
## [1.8.2](https://github.com/voder-ai/eslint-plugin-traceability/compare/v1.8.1...v1.8.2) (2025-12-04)
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
### Bug Fixes
|
|
5
5
|
|
|
6
|
-
*
|
|
6
|
+
* rename multi-story annotation from [@implements](https://github.com/implements) to [@supports](https://github.com/supports) ([8c5d089](https://github.com/voder-ai/eslint-plugin-traceability/commit/8c5d08997efb4e5ab8f565db462633b15232ded0))
|
|
7
7
|
|
|
8
8
|
# Changelog
|
|
9
9
|
|
|
@@ -75,8 +75,8 @@ The following entries were maintained manually before the adoption of semantic-r
|
|
|
75
75
|
- `valid-annotation-format`
|
|
76
76
|
- `valid-story-reference`
|
|
77
77
|
- `valid-req-reference`
|
|
78
|
-
-
|
|
79
|
-
-
|
|
78
|
+
- Developer documentation for all rules in this repository.
|
|
79
|
+
- Developer documentation for configuration presets in this repository.
|
|
80
80
|
- Example usage in `README.md`.
|
|
81
81
|
- Pre-commit and pre-push hooks with formatting, linting, and tests.
|
|
82
82
|
- Comprehensive tests covering core validation rules.
|
package/README.md
CHANGED
|
@@ -54,7 +54,7 @@ export default [
|
|
|
54
54
|
- `traceability/valid-annotation-format` Enforces correct format of traceability annotations. (See the rule documentation in the plugin's user guide.)
|
|
55
55
|
- `traceability/valid-story-reference` Validates that `@story` references point to existing story files. (See the rule documentation in the plugin's user guide.)
|
|
56
56
|
- `traceability/valid-req-reference` Validates that `@req` references point to existing requirement IDs. (See the rule documentation in the plugin's user guide.)
|
|
57
|
-
- `traceability/prefer-implements-annotation` Recommends migration from legacy `@story`/`@req` annotations to `@
|
|
57
|
+
- `traceability/prefer-implements-annotation` Recommends migration from legacy `@story`/`@req` annotations to `@supports` (opt-in; disabled by default in the presets and must be explicitly enabled). (See the rule documentation in the plugin's user guide.)
|
|
58
58
|
|
|
59
59
|
Configuration options: For detailed per-rule options (such as scopes, branch types, and story directory settings), see the individual rule docs in the plugin's user guide and the consolidated [API Reference](user-docs/api-reference.md).
|
|
60
60
|
|
|
@@ -136,8 +136,8 @@ npx traceability-maint report --root . --format json
|
|
|
136
136
|
# Update references when a story file is renamed
|
|
137
137
|
npx traceability-maint update \
|
|
138
138
|
--root . \
|
|
139
|
-
--from "
|
|
140
|
-
--to "
|
|
139
|
+
--from "stories/feature-authentication.story.md" \
|
|
140
|
+
--to "stories/feature-auth-v2.story.md"
|
|
141
141
|
```
|
|
142
142
|
|
|
143
143
|
For a full description of options and JSON payloads, see the [Maintenance API and CLI](user-docs/api-reference.md#maintenance-api-and-cli) section in the API Reference.
|
|
@@ -195,7 +195,7 @@ These tests verify end-to-end behavior of the plugin via the ESLint CLI.
|
|
|
195
195
|
|
|
196
196
|
## Security and Dependency Health
|
|
197
197
|
|
|
198
|
-
For the canonical, user-facing security policy (including how to report vulnerabilities), see [SECURITY.md](SECURITY.md).
|
|
198
|
+
For the canonical, user-facing security policy (including how to report vulnerabilities), see [SECURITY.md](SECURITY.md). Internal implementation details and deeper discussion live in the project’s internal documentation and decision records, which are intended for maintainers rather than end users.
|
|
199
199
|
|
|
200
200
|
### What end users can expect from production dependencies
|
|
201
201
|
|
package/SECURITY.md
CHANGED
|
@@ -4,6 +4,8 @@ This document describes how security is handled for `eslint-plugin-traceability`
|
|
|
4
4
|
|
|
5
5
|
> This file is **user-facing** documentation. Internal implementation details and deeper discussion live in the project’s internal documentation and decision records.
|
|
6
6
|
|
|
7
|
+
For a consolidated implementation overview of security tooling and checks (maintainer and automated-assessor focused), maintainers can refer to the project's internal security overview documentation; this level of detail is not required for normal end users of the plugin.
|
|
8
|
+
|
|
7
9
|
## Reporting a Vulnerability
|
|
8
10
|
|
|
9
11
|
If you believe you have found a security vulnerability in this project:
|
|
@@ -41,7 +43,9 @@ In other words:
|
|
|
41
43
|
- The published npm package is intended to ship **without known high‑severity vulnerabilities in its production dependencies** at the moment it is released.
|
|
42
44
|
- Dev-only tooling and CI infrastructure are kept separate from what you install via `npm install eslint-plugin-traceability`.
|
|
43
45
|
|
|
44
|
-
For more detail on how these checks are wired into CI,
|
|
46
|
+
For more detail on how these checks are wired into CI, maintainers can refer to the project’s internal dependency health and security documentation; this level of detail is not required for normal end users of the plugin.
|
|
47
|
+
|
|
48
|
+
Maintainer-focused CI/CD security summary: release workflows enforce several security checks end-to-end. For production dependencies, `npm audit --omit=dev --audit-level=high` is **release-blocking** and must report no high-severity issues before publishing proceeds. For broader dependency health, `npm run safety:deps` (dry-aged-deps) and `npm run audit:dev-high` (dev-only `npm audit` with stricter thresholds) are **advisory**: they generate machine-readable reports to guide upgrades and risk review but do not, by themselves, block a release. Secret scanning is performed with `npm run security:secrets` (secretlint); this is treated as **release-blocking** in CI/CD so that accidental credential or secret leaks are caught before artifacts are published.
|
|
45
49
|
|
|
46
50
|
## Dependency Maturity and `dry-aged-deps`
|
|
47
51
|
|
|
@@ -59,71 +63,70 @@ Current high-level policy:
|
|
|
59
63
|
|
|
60
64
|
When `dry-aged-deps` reports that there are **no safe upgrades available** under these thresholds, we may temporarily accept residual risk in dev-only tooling while keeping production dependencies clean and fully audited.
|
|
61
65
|
|
|
62
|
-
For maintainers, the full process is described in the project’s internal dependency health and security guidelines.
|
|
66
|
+
For maintainers, the full process is described in the project’s internal dependency health and security guidelines; end users typically do not need to consult those documents.
|
|
63
67
|
|
|
64
68
|
## Dev-Only Release Tooling Risk (semantic-release / npm / glob / brace-expansion)
|
|
65
69
|
|
|
66
|
-
|
|
70
|
+
This section documents a **historical** dev-only risk in an older semantic-release/npm toolchain that has since been fully resolved by upgrading the release toolchain to a vulnerability-free stack.
|
|
71
|
+
|
|
72
|
+
### What was affected?
|
|
67
73
|
|
|
68
|
-
|
|
74
|
+
During the incident period, the **older** dev dependency stack had the following characteristics:
|
|
69
75
|
|
|
70
|
-
- The dev dependency `@semantic-release/npm@10.0.6`
|
|
71
|
-
- The relevant advisories
|
|
76
|
+
- The dev dependency `@semantic-release/npm@10.0.6` bundled `npm@9.5.0`, which in turn included vulnerable versions of `glob` and `brace-expansion`.
|
|
77
|
+
- The relevant advisories were:
|
|
72
78
|
- `glob` CLI command injection: [GHSA-5j98-mcp5-4vw2](https://github.com/advisories/GHSA-5j98-mcp5-4vw2)
|
|
73
79
|
- `brace-expansion` ReDoS: [GHSA-v6h2-p8h4-qcjw](https://github.com/advisories/GHSA-v6h2-p8h4-qcjw)
|
|
74
|
-
- These vulnerable packages
|
|
80
|
+
- These vulnerable packages existed **only inside the npm binary bundled within `@semantic-release/npm`** and were used solely during automated publishing from CI.
|
|
81
|
+
|
|
82
|
+
The current release toolchain no longer relies on this vulnerable bundled npm stack, and these specific advisories are no longer present in our dev dependencies.
|
|
75
83
|
|
|
76
84
|
### What is _not_ affected?
|
|
77
85
|
|
|
78
|
-
|
|
86
|
+
Throughout the incident period, and continuing after the upgrade:
|
|
87
|
+
|
|
88
|
+
- The published `eslint-plugin-traceability` package has **no runtime dependencies** on any bundled npm or its `glob`/`brace-expansion` copies.
|
|
79
89
|
- End-user projects that install and run `eslint-plugin-traceability` or `traceability-maint` **do not execute** this bundled tooling.
|
|
80
|
-
- `npm audit --omit=dev --audit-level=high`
|
|
90
|
+
- `npm audit --omit=dev --audit-level=high` has continued to report **0 high‑severity vulnerabilities** for the production dependency tree at release time.
|
|
81
91
|
|
|
82
|
-
|
|
92
|
+
These guarantees remain in effect with the updated, vulnerability-free toolchain.
|
|
83
93
|
|
|
84
|
-
|
|
94
|
+
### Historical Risk Acceptance
|
|
85
95
|
|
|
86
|
-
-
|
|
87
|
-
- We therefore treat this as a **known error in dev-only tooling** rather than a production risk.
|
|
96
|
+
While the older `@semantic-release/npm@10.0.6` stack was in use and no safe, `dry-aged-deps`–approved upgrade path existed, this dev-only risk was **explicitly accepted** as a known error under the `dry-aged-deps` policy. It is **no longer** an active known error following the toolchain upgrade.
|
|
88
97
|
|
|
89
|
-
|
|
98
|
+
The full historical record and resolution details are documented in:
|
|
99
|
+
|
|
100
|
+
- A detailed historical incident report in this repository’s internal security incident documentation (maintainer-facing only)
|
|
90
101
|
|
|
91
102
|
### Compensating Controls
|
|
92
103
|
|
|
93
|
-
|
|
104
|
+
While the older toolchain was in place, we applied several compensating controls to tightly contain the dev-only risk. The same general isolation and audit practices continue to apply to the updated, vulnerability-free release toolchain:
|
|
94
105
|
|
|
95
106
|
1. **Environment Isolation**
|
|
96
|
-
- The vulnerable tooling
|
|
97
|
-
- It
|
|
98
|
-
- The job
|
|
107
|
+
- The vulnerable tooling was used **only** in the GitHub Actions CI workflow (`.github/workflows/ci-cd.yml`).
|
|
108
|
+
- It ran in a single, controlled job that executed on pushes to the `main` branch, not for pull requests.
|
|
109
|
+
- The job ran on GitHub-hosted runners and did not have access to internal infrastructure.
|
|
99
110
|
|
|
100
111
|
2. **Least-Privilege Permissions for Release**
|
|
101
|
-
- Workflow-level permissions
|
|
102
|
-
- Elevated permissions (`contents: write`, `issues: write`, `pull-requests: write`, `id-token: write`)
|
|
112
|
+
- Workflow-level permissions defaulted to `contents: read`.
|
|
113
|
+
- Elevated permissions (`contents: write`, `issues: write`, `pull-requests: write`, `id-token: write`) were scoped to the release job/step that ran semantic-release and were not used for general CI tasks.
|
|
103
114
|
|
|
104
115
|
3. **Strict Input Handling**
|
|
105
|
-
- The CI configuration and project scripts **never
|
|
106
|
-
- The semantic-release/npm toolchain
|
|
107
|
-
- Release jobs
|
|
116
|
+
- The CI configuration and project scripts **never invoked the `glob` CLI** with the dangerous `-c/--cmd` flags.
|
|
117
|
+
- The semantic-release/npm toolchain did **not** receive untrusted user input for glob patterns or environment variables.
|
|
118
|
+
- Release jobs operated only on the repository contents of this project plus standard CI-provided environment variables.
|
|
108
119
|
|
|
109
120
|
4. **Audit and Monitoring**
|
|
110
|
-
- Dev-only vulnerabilities
|
|
111
|
-
- `dry-aged-deps` reports (`ci/dry-aged-deps.json`)
|
|
112
|
-
- A nightly `dependency-health` workflow re-
|
|
121
|
+
- Dev-only vulnerabilities were tracked via `npm run audit:dev-high`, which wrote a machine-readable report to `ci/npm-audit.json` for each CI run.
|
|
122
|
+
- `dry-aged-deps` reports (`ci/dry-aged-deps.json`) were stored as CI artifacts to document when no safe upgrade path existed under the configured thresholds.
|
|
123
|
+
- A nightly `dependency-health` workflow re-ran dev-dependency audits to keep this type of risk under continuous review.
|
|
113
124
|
|
|
114
125
|
5. **Guarded semantic-release Invocation (CI-Only)**
|
|
115
|
-
- semantic-release
|
|
116
|
-
- Local developers
|
|
117
|
-
|
|
118
|
-
### Upgrade Plan
|
|
119
|
-
|
|
120
|
-
We intend to migrate away from the affected semantic-release/npm toolchain as soon as a safe, dry‑aged‑deps–approved upgrade path is available:
|
|
121
|
-
|
|
122
|
-
1. Continue monitoring `dry-aged-deps` output for `@semantic-release/npm`, `semantic-release`, and related packages.
|
|
123
|
-
2. When a newer, vulnerability-free version remains stable for at least 7 days and passes our audit checks, update the dev dependencies accordingly.
|
|
124
|
-
3. After migration, convert the existing known-error record into a resolved incident that documents the fix and new baseline.
|
|
126
|
+
- semantic-release was invoked **only from CI**, and guarded to ensure it ran under the intended safe context (GitHub Actions, push to `main`, CI environment).
|
|
127
|
+
- Local developers were not expected to run semantic-release directly; publishing was handled automatically by CI after all quality and security checks passed.
|
|
125
128
|
|
|
126
|
-
|
|
129
|
+
With the upgraded release toolchain, these controls continue to constrain dev-only tooling and help ensure that vulnerabilities in CI infrastructure do not impact the security properties of the published package.
|
|
127
130
|
|
|
128
131
|
---
|
|
129
132
|
|
|
@@ -18,10 +18,8 @@ function runMaintenanceCli(rawArgv) {
|
|
|
18
18
|
const initialNormalized = (0, flags_1.normalizeCliArgs)(rawArgv);
|
|
19
19
|
const { subcommand: command } = initialNormalized;
|
|
20
20
|
if (!command || command === "-h" || command === "--help") {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
* @req REQ-MAINT-SAFE - Handle help requests safely and provide discoverable usage output
|
|
24
|
-
*/
|
|
21
|
+
// @implements docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-SAFE
|
|
22
|
+
// @implements docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-SAFE - Branch to show usage when no command or help flag is provided; handle help requests safely and provide discoverable usage output
|
|
25
23
|
printHelp();
|
|
26
24
|
return commands_1.EXIT_OK;
|
|
27
25
|
}
|
|
@@ -29,37 +27,36 @@ function runMaintenanceCli(rawArgv) {
|
|
|
29
27
|
// receive the subcommand name and its raw argument vector unchanged.
|
|
30
28
|
const normalized = initialNormalized;
|
|
31
29
|
try {
|
|
30
|
+
// @implements docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-SAFE
|
|
31
|
+
// @implements docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-SAFE - Catch unexpected errors and surface concise diagnostics without crashing
|
|
32
32
|
switch (command) {
|
|
33
33
|
case "detect":
|
|
34
|
-
// @
|
|
34
|
+
// @implements docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-DETECT - Branch to dispatch to detection handler when 'detect' is requested; dispatch to detection handler
|
|
35
35
|
return (0, commands_1.handleDetect)(normalized);
|
|
36
36
|
case "verify":
|
|
37
|
-
// @
|
|
37
|
+
// @implements docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-VERIFY - Branch to dispatch to verification handler when 'verify' is requested; dispatch to verification handler
|
|
38
38
|
return (0, commands_1.handleVerify)(normalized);
|
|
39
39
|
case "report":
|
|
40
|
-
// @
|
|
40
|
+
// @implements docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-REPORT - Branch to dispatch to reporting handler when 'report' is requested; dispatch to reporting handler
|
|
41
41
|
return (0, commands_1.handleReport)(normalized);
|
|
42
42
|
case "update": {
|
|
43
|
-
// @
|
|
43
|
+
// @implements docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-UPDATE - Branch to dispatch to update handler when 'update' is requested; dispatch to update handler
|
|
44
44
|
const result = (0, commands_1.handleUpdate)(normalized);
|
|
45
|
-
// @story docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md @req REQ-MAINT-SAFE - Print help on usage errors from update
|
|
46
45
|
if (result === commands_1.EXIT_USAGE) {
|
|
46
|
+
// @implements docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-SAFE - Print help on usage errors from update; help branch for update usage errors
|
|
47
47
|
printHelp();
|
|
48
48
|
}
|
|
49
49
|
return result;
|
|
50
50
|
}
|
|
51
51
|
default:
|
|
52
|
-
// @
|
|
52
|
+
// @implements docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-SAFE - Branch for unknown commands to emit diagnostics and safe usage guidance; handle unknown commands safely with diagnostics
|
|
53
53
|
console.error(`Unknown command: ${command}`);
|
|
54
54
|
printHelp();
|
|
55
55
|
return commands_1.EXIT_USAGE;
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
58
|
catch (error) {
|
|
59
|
-
|
|
60
|
-
* @story docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md
|
|
61
|
-
* @req REQ-MAINT-SAFE - Catch unexpected errors and surface concise diagnostics without crashing
|
|
62
|
-
*/
|
|
59
|
+
// @implements docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-SAFE - Catch-all error branch to prevent crashes and provide concise diagnostics; catch unexpected errors and surface concise diagnostics without crashing
|
|
63
60
|
const message = error instanceof Error
|
|
64
61
|
? error.message
|
|
65
62
|
: "Unknown error in maintenance CLI";
|
|
@@ -73,8 +70,7 @@ function runMaintenanceCli(rawArgv) {
|
|
|
73
70
|
* @req REQ-MAINT-SAFE - Provide discoverable CLI usage information
|
|
74
71
|
*/
|
|
75
72
|
function printHelp() {
|
|
76
|
-
// @
|
|
77
|
-
// @req REQ-MAINT-SAFE - Provide discoverable CLI usage information
|
|
73
|
+
// @implements docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-SAFE - Help branch ensures users can discover maintenance CLI usage safely; provide discoverable CLI usage information
|
|
78
74
|
console.log(`traceability-maint - Traceability annotation maintenance tools
|
|
79
75
|
|
|
80
76
|
Usage:
|
|
@@ -54,6 +54,9 @@ function detectStaleAnnotations(codebasePath) {
|
|
|
54
54
|
// @req REQ-MAINT-DETECT - Return empty result if workspaceRoot does not exist or is not a directory
|
|
55
55
|
if (!fs.existsSync(workspaceRoot) ||
|
|
56
56
|
!fs.statSync(workspaceRoot).isDirectory()) {
|
|
57
|
+
// @implements docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-DETECT
|
|
58
|
+
// @implements docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-SAFE
|
|
59
|
+
// @implements docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-DETECT
|
|
57
60
|
return [];
|
|
58
61
|
}
|
|
59
62
|
const stale = new Set();
|
|
@@ -76,9 +79,12 @@ function processFileForStaleAnnotations(file, workspaceRoot, cwd, stale) {
|
|
|
76
79
|
// @story docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md
|
|
77
80
|
// @req REQ-MAINT-DETECT - Handle file read errors gracefully
|
|
78
81
|
try {
|
|
82
|
+
// @implements docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-DETECT
|
|
79
83
|
content = fs.readFileSync(file, "utf8");
|
|
80
84
|
}
|
|
81
85
|
catch {
|
|
86
|
+
// @implements docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-DETECT
|
|
87
|
+
// @implements docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-SAFE - Swallow file read failures without aborting detection
|
|
82
88
|
return;
|
|
83
89
|
}
|
|
84
90
|
const regex = /@story\s+([^\s]+)/g;
|
|
@@ -97,6 +103,7 @@ function handleStoryMatch(storyPath, workspaceRoot, cwd, stale) {
|
|
|
97
103
|
// @story docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md
|
|
98
104
|
// @req REQ-MAINT-DETECT REQ-SECURITY-VALIDATION - Skip traversal/absolute-unsafe or invalid-extension story paths before any filesystem or boundary checks
|
|
99
105
|
if ((0, storyReferenceUtils_1.isUnsafeStoryPath)(storyPath)) {
|
|
106
|
+
// @implements docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-DETECT
|
|
100
107
|
return;
|
|
101
108
|
}
|
|
102
109
|
// @story docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md
|
|
@@ -108,6 +115,7 @@ function handleStoryMatch(storyPath, workspaceRoot, cwd, stale) {
|
|
|
108
115
|
const inProjectCandidates = getInProjectCandidates(storyProjectCandidate, storyCodebaseCandidate, workspaceRoot);
|
|
109
116
|
// If both candidates are out-of-project, do not mark as stale and skip FS checks
|
|
110
117
|
if (inProjectCandidates.length === 0) {
|
|
118
|
+
// @implements docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-DETECT - No in-project candidates means nothing to check or mark stale
|
|
111
119
|
return;
|
|
112
120
|
}
|
|
113
121
|
// @story docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md
|
|
@@ -116,6 +124,7 @@ function handleStoryMatch(storyPath, workspaceRoot, cwd, stale) {
|
|
|
116
124
|
// @story docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md
|
|
117
125
|
// @req REQ-MAINT-DETECT - Mark story as stale if any in-project candidate exists conceptually but none exist on disk
|
|
118
126
|
if (!anyExists) {
|
|
127
|
+
// @implements docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-DETECT
|
|
119
128
|
stale.add(storyPath);
|
|
120
129
|
}
|
|
121
130
|
}
|
|
@@ -127,18 +136,24 @@ function getInProjectCandidates(storyProjectCandidate, storyCodebaseCandidate, w
|
|
|
127
136
|
let projectBoundary;
|
|
128
137
|
let codebaseBoundary;
|
|
129
138
|
try {
|
|
139
|
+
// @implements docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-DETECT
|
|
130
140
|
projectBoundary = (0, storyReferenceUtils_1.enforceProjectBoundary)(storyProjectCandidate, workspaceRoot);
|
|
131
141
|
}
|
|
132
142
|
catch {
|
|
143
|
+
// @implements docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-DETECT
|
|
144
|
+
// @implements docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-SAFE - Treat boundary enforcement failures as out-of-project
|
|
133
145
|
projectBoundary = {
|
|
134
146
|
isWithinProject: false,
|
|
135
147
|
candidate: storyProjectCandidate,
|
|
136
148
|
};
|
|
137
149
|
}
|
|
138
150
|
try {
|
|
151
|
+
// @implements docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-DETECT
|
|
139
152
|
codebaseBoundary = (0, storyReferenceUtils_1.enforceProjectBoundary)(storyCodebaseCandidate, workspaceRoot);
|
|
140
153
|
}
|
|
141
154
|
catch {
|
|
155
|
+
// @implements docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-DETECT
|
|
156
|
+
// @implements docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-SAFE - Treat boundary enforcement failures as out-of-project
|
|
142
157
|
codebaseBoundary = {
|
|
143
158
|
isWithinProject: false,
|
|
144
159
|
candidate: storyCodebaseCandidate,
|
|
@@ -146,9 +161,11 @@ function getInProjectCandidates(storyProjectCandidate, storyCodebaseCandidate, w
|
|
|
146
161
|
}
|
|
147
162
|
const inProjectCandidates = [];
|
|
148
163
|
if (projectBoundary.isWithinProject) {
|
|
164
|
+
// @implements docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-DETECT - Collect project-relative in-project candidate
|
|
149
165
|
inProjectCandidates.push(projectBoundary.candidate);
|
|
150
166
|
}
|
|
151
167
|
if (codebaseBoundary.isWithinProject) {
|
|
168
|
+
// @implements docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-DETECT - Collect workspace-root-relative in-project candidate
|
|
152
169
|
inProjectCandidates.push(codebaseBoundary.candidate);
|
|
153
170
|
}
|
|
154
171
|
return inProjectCandidates;
|
|
@@ -158,5 +175,15 @@ function getInProjectCandidates(storyProjectCandidate, storyCodebaseCandidate, w
|
|
|
158
175
|
* @req REQ-MAINT-DETECT - Check on-disk existence of in-project candidates
|
|
159
176
|
*/
|
|
160
177
|
function anyInProjectCandidateExists(inProjectCandidates) {
|
|
161
|
-
return inProjectCandidates.some(
|
|
178
|
+
return inProjectCandidates.some(
|
|
179
|
+
/**
|
|
180
|
+
* @implements docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-DETECT REQ-MAINT-SAFE
|
|
181
|
+
*/
|
|
182
|
+
(p) => {
|
|
183
|
+
const exists = fs.existsSync(p);
|
|
184
|
+
if (!exists) {
|
|
185
|
+
// @implements docs/stories/009.0-DEV-MAINTENANCE-TOOLS.story.md REQ-MAINT-SAFE - Safely handle non-existent candidate without throwing
|
|
186
|
+
}
|
|
187
|
+
return exists;
|
|
188
|
+
});
|
|
162
189
|
}
|
|
@@ -29,10 +29,10 @@ export declare function linesBeforeHasStory(sourceCode: any, node: any, lookback
|
|
|
29
29
|
export declare function parentChainHasStory(sourceCode: any, node: any): boolean;
|
|
30
30
|
/**
|
|
31
31
|
* Fallback: inspect text immediately preceding the node in sourceCode.getText to find @story
|
|
32
|
-
* Also accepts @
|
|
32
|
+
* Also accepts @supports annotations as satisfying story presence for this rule.
|
|
33
33
|
* @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
|
|
34
34
|
* @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
|
|
35
35
|
* @req REQ-ANNOTATION-REQUIRED - Provide fallback textual inspection when other heuristics fail
|
|
36
|
-
* @req REQ-REQUIRE-ACCEPTS-IMPLEMENTS - Treat @
|
|
36
|
+
* @req REQ-REQUIRE-ACCEPTS-IMPLEMENTS - Treat @supports annotations as satisfying story presence in fallback checks
|
|
37
37
|
*/
|
|
38
38
|
export declare function fallbackTextBeforeHasStory(sourceCode: any, node: any): boolean;
|
|
@@ -23,17 +23,17 @@ exports.LOOKBACK_LINES = 4;
|
|
|
23
23
|
exports.FALLBACK_WINDOW = 800;
|
|
24
24
|
/**
|
|
25
25
|
* Shared predicate to determine if a given comment node contains an @story marker.
|
|
26
|
-
* Also treats @
|
|
26
|
+
* Also treats @supports annotations as satisfying story presence checks.
|
|
27
27
|
* @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
|
|
28
28
|
* @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
|
|
29
29
|
* @req REQ-ANNOTATION-REQUIRED - Centralize @story detection logic for comment value inspection
|
|
30
|
-
* @req REQ-REQUIRE-ACCEPTS-IMPLEMENTS - Treat @
|
|
30
|
+
* @req REQ-REQUIRE-ACCEPTS-IMPLEMENTS - Treat @supports annotations as satisfying story presence checks
|
|
31
31
|
*/
|
|
32
32
|
function commentContainsStory(comment) {
|
|
33
33
|
if (typeof comment?.value !== "string") {
|
|
34
34
|
return false;
|
|
35
35
|
}
|
|
36
|
-
return (comment.value.includes("@story") || comment.value.includes("@
|
|
36
|
+
return (comment.value.includes("@story") || comment.value.includes("@supports"));
|
|
37
37
|
}
|
|
38
38
|
/**
|
|
39
39
|
* Safely extract the physical source lines array from sourceCode for scanning.
|
|
@@ -61,20 +61,20 @@ function getNodeStartLine(node) {
|
|
|
61
61
|
* Generic helper to scan a range of physical source lines for the presence of an @story marker.
|
|
62
62
|
* @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
|
|
63
63
|
* @req REQ-ANNOTATION-REQUIRED - Reuse line scanning logic for story annotations across helpers
|
|
64
|
-
* @req REQ-REQUIRE-ACCEPTS-IMPLEMENTS - Accept @
|
|
64
|
+
* @req REQ-REQUIRE-ACCEPTS-IMPLEMENTS - Accept @supports annotations as valid markers during line scanning
|
|
65
65
|
*/
|
|
66
66
|
function scanLinesForMarker(lines, from, to) {
|
|
67
|
-
// Walk each physical line in the configured lookback window to search for an inline @story or @
|
|
67
|
+
// Walk each physical line in the configured lookback window to search for an inline @story or @supports marker.
|
|
68
68
|
// @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
|
|
69
69
|
// @req REQ-ANNOTATION-REQUIRED - Scan preceding lines for existing story annotations
|
|
70
70
|
for (let i = from; i < to; i++) {
|
|
71
71
|
const text = lines[i];
|
|
72
|
-
// Treat any line containing "@story" or "@
|
|
72
|
+
// Treat any line containing "@story" or "@supports" as evidence that the function is already annotated.
|
|
73
73
|
// @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
|
|
74
74
|
// @req REQ-ANNOTATION-REQUIRED - Detect explicit @story markers in raw source text
|
|
75
|
-
// @req REQ-REQUIRE-ACCEPTS-IMPLEMENTS - Detect explicit @
|
|
75
|
+
// @req REQ-REQUIRE-ACCEPTS-IMPLEMENTS - Detect explicit @supports markers in raw source text
|
|
76
76
|
if (typeof text === "string" &&
|
|
77
|
-
(text.includes("@story") || text.includes("@
|
|
77
|
+
(text.includes("@story") || text.includes("@supports"))) {
|
|
78
78
|
return true;
|
|
79
79
|
}
|
|
80
80
|
}
|
|
@@ -134,11 +134,11 @@ function parentChainHasStory(sourceCode, node) {
|
|
|
134
134
|
}
|
|
135
135
|
/**
|
|
136
136
|
* Fallback: inspect text immediately preceding the node in sourceCode.getText to find @story
|
|
137
|
-
* Also accepts @
|
|
137
|
+
* Also accepts @supports annotations as satisfying story presence for this rule.
|
|
138
138
|
* @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
|
|
139
139
|
* @story docs/stories/010.2-DEV-MULTI-STORY-SUPPORT.story.md
|
|
140
140
|
* @req REQ-ANNOTATION-REQUIRED - Provide fallback textual inspection when other heuristics fail
|
|
141
|
-
* @req REQ-REQUIRE-ACCEPTS-IMPLEMENTS - Treat @
|
|
141
|
+
* @req REQ-REQUIRE-ACCEPTS-IMPLEMENTS - Treat @supports annotations as satisfying story presence in fallback checks
|
|
142
142
|
*/
|
|
143
143
|
function fallbackTextBeforeHasStory(sourceCode, node) {
|
|
144
144
|
// Skip fallback text inspection when the sourceCode API or node range information is not available.
|
|
@@ -161,12 +161,12 @@ function fallbackTextBeforeHasStory(sourceCode, node) {
|
|
|
161
161
|
// @req REQ-ANNOTATION-REQUIRED - Restrict fallback text scanning to a safe, fixed-size window
|
|
162
162
|
const start = Math.max(0, range[0] - exports.FALLBACK_WINDOW);
|
|
163
163
|
const textBefore = sourceCode.getText().slice(start, range[0]);
|
|
164
|
-
// Detect any @story or @
|
|
164
|
+
// Detect any @story or @supports marker that appears within the bounded fallback window.
|
|
165
165
|
// @story docs/stories/003.0-DEV-FUNCTION-ANNOTATIONS.story.md
|
|
166
166
|
// @req REQ-ANNOTATION-REQUIRED - Recognize story annotations discovered via fallback text scanning
|
|
167
|
-
// @req REQ-REQUIRE-ACCEPTS-IMPLEMENTS - Recognize @
|
|
167
|
+
// @req REQ-REQUIRE-ACCEPTS-IMPLEMENTS - Recognize @supports annotations discovered via fallback text scanning
|
|
168
168
|
if (typeof textBefore === "string" &&
|
|
169
|
-
(textBefore.includes("@story") || textBefore.includes("@
|
|
169
|
+
(textBefore.includes("@story") || textBefore.includes("@supports"))) {
|
|
170
170
|
return true;
|
|
171
171
|
}
|
|
172
172
|
}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* @req REQ-MULTILINE-SUPPORT - Handle annotations split across multiple lines
|
|
8
8
|
* @req REQ-FLEXIBLE-PARSING - Support reasonable variations in whitespace and formatting
|
|
9
9
|
* @req REQ-AUTOFIX-FORMAT - Provide safe, minimal automatic fixes for common format issues
|
|
10
|
-
* @req REQ-
|
|
10
|
+
* @req REQ-SUPPORTS-PARSE - Parse @supports annotations without affecting @story/@req
|
|
11
11
|
* @req REQ-MIXED-SUPPORT - Support mixed @story/@req/@implements usage in comments
|
|
12
12
|
*/
|
|
13
13
|
/**
|
|
@@ -24,7 +24,7 @@ export interface PendingAnnotation {
|
|
|
24
24
|
* This function trims whitespace, keeps any annotation tags that appear
|
|
25
25
|
* later in the line, and supports common JSDoc styles such as leading "*".
|
|
26
26
|
*
|
|
27
|
-
* It detects @story, @req, and @
|
|
27
|
+
* It detects @story, @req, and @supports tags while preserving the rest
|
|
28
28
|
* of the line for downstream logic.
|
|
29
29
|
*/
|
|
30
30
|
export declare function normalizeCommentLine(rawLine: string): string;
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* @req REQ-MULTILINE-SUPPORT - Handle annotations split across multiple lines
|
|
9
9
|
* @req REQ-FLEXIBLE-PARSING - Support reasonable variations in whitespace and formatting
|
|
10
10
|
* @req REQ-AUTOFIX-FORMAT - Provide safe, minimal automatic fixes for common format issues
|
|
11
|
-
* @req REQ-
|
|
11
|
+
* @req REQ-SUPPORTS-PARSE - Parse @supports annotations without affecting @story/@req
|
|
12
12
|
* @req REQ-MIXED-SUPPORT - Support mixed @story/@req/@implements usage in comments
|
|
13
13
|
*/
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
@@ -19,7 +19,7 @@ exports.normalizeCommentLine = normalizeCommentLine;
|
|
|
19
19
|
* This function trims whitespace, keeps any annotation tags that appear
|
|
20
20
|
* later in the line, and supports common JSDoc styles such as leading "*".
|
|
21
21
|
*
|
|
22
|
-
* It detects @story, @req, and @
|
|
22
|
+
* It detects @story, @req, and @supports tags while preserving the rest
|
|
23
23
|
* of the line for downstream logic.
|
|
24
24
|
*/
|
|
25
25
|
function normalizeCommentLine(rawLine) {
|
|
@@ -27,7 +27,7 @@ function normalizeCommentLine(rawLine) {
|
|
|
27
27
|
if (!trimmed) {
|
|
28
28
|
return "";
|
|
29
29
|
}
|
|
30
|
-
const annotationMatch = trimmed.match(/@story\b|@req\b|@
|
|
30
|
+
const annotationMatch = trimmed.match(/@story\b|@req\b|@supports\b/);
|
|
31
31
|
if (!annotationMatch || annotationMatch.index === undefined) {
|
|
32
32
|
const withoutLeadingStar = trimmed.replace(/^\*\s?/, "");
|
|
33
33
|
return withoutLeadingStar;
|
|
@@ -42,7 +42,10 @@ export declare function collapseAnnotationValue(value: string): string;
|
|
|
42
42
|
*
|
|
43
43
|
* Returns the fixed path when safe, or null if no fix should be applied.
|
|
44
44
|
*
|
|
45
|
+
* @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
|
|
45
46
|
* @story docs/stories/008.0-DEV-AUTO-FIX.story.md
|
|
47
|
+
* @story docs/stories/010.1-REQ-STORY-PATH-STRICTNESS.story.md
|
|
48
|
+
* @story docs/stories/010.2-REQ-STORY-PATH-AUTOFIX.story.md
|
|
46
49
|
* @req REQ-AUTOFIX-FORMAT - Provide safe, minimal automatic fixes for common format issues
|
|
47
50
|
* @req REQ-AUTOFIX-SAFE - Auto-fix must be conservative and never broaden the referenced path
|
|
48
51
|
* @req REQ-AUTOFIX-PRESERVE - Preserve surrounding formatting when normalizing story path suffixes
|
|
@@ -53,6 +56,7 @@ export declare function getFixedStoryPath(original: string): string | null;
|
|
|
53
56
|
*
|
|
54
57
|
* @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
|
|
55
58
|
* @story docs/stories/008.0-DEV-AUTO-FIX.story.md
|
|
59
|
+
* @story docs/stories/010.1-REQ-STORY-PATH-STRICTNESS.story.md
|
|
56
60
|
* @req REQ-ERROR-SPECIFICITY - Provide specific error messages for different format violations
|
|
57
61
|
* @req REQ-AUTOFIX-FORMAT - Provide safe, minimal automatic fixes for common format issues
|
|
58
62
|
*/
|
|
@@ -62,6 +66,7 @@ export declare function buildStoryErrorMessage(kind: "missing" | "invalid", valu
|
|
|
62
66
|
*
|
|
63
67
|
* @story docs/stories/005.0-DEV-ANNOTATION-VALIDATION.story.md
|
|
64
68
|
* @story docs/stories/008.0-DEV-AUTO-FIX.story.md
|
|
69
|
+
* @story docs/stories/010.1-REQ-STORY-PATH-STRICTNESS.story.md
|
|
65
70
|
* @req REQ-ERROR-SPECIFICITY - Provide specific error messages for different format violations
|
|
66
71
|
* @req REQ-AUTOFIX-FORMAT - Provide safe, minimal automatic fixes for common format issues
|
|
67
72
|
*/
|