Trace linter
scripts/trace-lint.mjs validates the whole ID graph on every push and
every PR. Link rot is the killer — 80%-accurate traceability is worse than
none — so it has run in CI since the first commit.
Failure conditions
Section titled “Failure conditions”- A
FEAT-*markdown file pastproposedwith zero code-tag references. - A code tag whose ID doesn’t resolve to a markdown file.
- Any
depends_on/decisions/affects/sub_feature_of/superseded_byedge pointing at a missing ID. - A feature
status: builtwhose ID doesn’t appear in both code and tests. - The same ID declared more than once within a single collection.
Running locally
Section titled “Running locally”pnpm trace-lintExit code 0 with a summary line on success; exit code 1 with one line per
violation otherwise. Output is plain text, one ✗ per error.
States & edge cases
Section titled “States & edge cases”- Unparseable frontmatter is itself a lint error, not a silent skip.
- Regex literals don’t count as tags — the scanner requires a full
FEAT-nnnnform;FEAT-\d{4}in a pattern never matches. - Test detection is by path:
*.test.*,*.spec.*,tests/,__tests__/. - The grep-based v1 is deliberate; a v2 may be SHACL over a generated graph, but a convention without a linter is a suggestion, so grep shipped first.