---
name: traceable-reqs-test-ci
description: traceable-reqs (BigscreenVR/traceable-reqs) test/CI characteristics and the four traps that cost real cycles — golden-fixture coupling, self-dogfood scanning of test fixture strings, rustfmt instability, tree-sitter-powershell recovery.
metadata:
  type: project
---

`traceable-reqs` is the Rust CLI behind spt-core's `REQ-*` traceability contract. Its crate lives in `rust/`; the NLSpec (`SPEC.md`) is the source of truth and wins over `README.md`.

**CI gates (`.github/workflows/ci.yml`, ubuntu-latest, `working-directory: rust`):** `cargo fmt --check`, then `cargo clippy --all-targets -- -D warnings`, then `cargo test`. A second `skill-lint` job validates every `SKILL.md` frontmatter as YAML. No coverage gate, no OS matrix — Windows only gets exercised by the release build.

**Traps measured the hard way (2026-08-18, PR #19):**

1. **Golden fixtures are coupled and hand-formatted.** Touching anything under `example/` means updating BOTH `example/expected.json` and `example/expected-change-compile.json`. Do NOT regenerate them with `json.dumps` — the committed style is compact inline objects, and a re-dump turns a 3-line change into 136. Hand-edit. Only `code`, `requirementId`, `stage`, `path`, `line` are stable keys (per `example/README.md`); `expected.json` deliberately omits `severity`, so a whole-file compare will "fail" on a file that is actually correct.
2. **The repo scans itself, including test fixture strings.** `rust/traceable-reqs.toml` has `roots = ["src", "tests"]`, and `traceable-reqs check` from `rust/` must stay at 0 findings. A tag-shaped token inside a Rust string literal becomes real evidence when a `\`-continued string's next physical line starts with `//` — the line scanner sees a comment. Write such fixtures on one physical line, and never put a literal `[stage->REQ-X]` in prose or a doc comment.
3. **rustfmt oscillates** on a long string literal inside a `match` arm's `return Err(...)`: `cargo fmt` and `cargo fmt --check` flip between two forms forever, which reads as a broken toolchain and would fail CI either way. Extract the message into a helper fn.
4. **`tree-sitter-powershell` has poor real-world coverage** — 23 ERROR nodes on a 400-line production script. Any analysis built on those parse trees must check `root_node().has_error()` and skip, or it will report confident nonsense about correctly written code.
5. **A per-language node-kind table is only as complete as its probe set** (PR #19 blind review, 2026-08-19). I built `symbols.rs` item tables from the shapes I tested; one reviewer probe session found 10 uncovered declaration classes (Rust top-level `macro_invocation`, `foreign_mod_item`; C/C++ `declaration`, `type_definition`, `alias_declaration`, `preproc_def`, `linkage_specification`, anonymous namespace, template header spans; Python module-level control flow; TS `abstract_class_declaration`/`internal_module`/`ambient_declaration`), each a false positive against the SPEC's own "any declaration or statement" wording. Before shipping such a table, enumerate the grammar's top-level node kinds (`node-types.json` or an s-expression dump over a kitchen-sink file) and dispose of every one explicitly — a `_ => {}` arm is a silent claim about the whole rest of the grammar.

**Why this matters:** spt-core's whole `REQ-*` discipline is enforced by this binary, so a rule added here changes what every spt repo's `check` accepts.

**How to apply:** before any behavior change here, run `cd rust && cargo build && target/debug/traceable-reqs check` (self-dogfood must stay 0 findings) and `cd example && ../rust/target/debug/traceable-reqs check --json` diffed against the golden on stable keys only. Measure consumer impact by running the built binary read-only from a big consumer repo's root (spt-core carried ~8,000 tags) before defaulting anything ON. See [[spt-test-conventions]] for spt-core's own crate test conventions.
