# MVP-TASK-039 — Automate scenario/test ID validation

**Status:** Implemented  
**Parent:** Epic-wide traceability convention

## Objective and required behavior

After scenario evidence exists, add a small non-invasive checker for duplicate requirement IDs,
tests referencing nonexistent scenarios, and scenarios missing required evidence declarations or
references.

## Scope, areas, and non-goals

Expected existing scripts/tests/CI quality infrastructure and a generated/reportable index. Parse
stable documented/test syntax with fixtures. Do not infer semantic adequacy, require browser E2E for
every scenario, or create a second test runner/documentation framework.

## Evidence and acceptance

Fixtures cover valid, duplicate, dangling-test, and missing-evidence cases. Accept when the checker
is deterministic/offline, joins the appropriate existing quality gate without weakening it,
documentation/traceability are current, and inherited DoD passes. Promote from Idea only after test
reference conventions have proven stable.

## Implementation record

`scripts/traceability-check.mjs` is a deterministic, offline checker over tracked files only. It
parses the stable documented/test syntax: story scenario headings (`### MVP-…-SNN`) with their
`- **Evidence:**` bullets, journey headings (`# MVP-JRN-NNN`), the scenario-to-evidence index tables
in each `docs/specs/*/traceability.md` (expanding compact `S01–S02` ranges and `S05–S06`
continuation tokens), and bracketed `[MVP-…]` IDs in tracked test files. It rejects duplicate
scenario/journey definitions, test-declared IDs that name no defined scenario or journey, scenarios
without a non-empty evidence declaration, scenarios absent from the index, index rows referencing
undefined scenarios, scenarios covered by more than one index row, index rows with empty or
`Planned` references, and unparsable index tokens. `--report` prints a generated per-scenario index
(evidence/index/test-file counts) without writing any file, so no second documentation surface is
maintained.

The checker deliberately judges no semantics: a scenario counts as referenced when the index names
it and as evidenced when a test declares its ID; whether the evidence proves the scenario remains a
review responsibility, and no scenario is required to have browser E2E. `pnpm traceability:check`
joins `pnpm quality` after `repository:check` and before `docs:check`; the gate was already green on
the corpus when the check was added, so nothing was weakened or exempted. Fixture coverage lives in
`tests/ci/traceability.test.mjs` (valid, duplicate, dangling-test, missing-evidence, index-row, and
report-determinism cases plus a real-corpus run, an exclusion-scope assertion, and gate-wiring
assertions), and `tests/ci/documentation-gates.test.mjs` additionally requires the new command
inside `quality`.

`tests/ci/traceability.test.mjs` is the single documented exclusion from the tracked test-file scan.
Its fixtures intentionally declare synthetic IDs (`MVP-DEMO-001-S01`, `MVP-JRN-901`) that no story
defines, which is exactly what the dangling-ID and index-row fixtures must exercise; scanning it
would make the real-corpus assertion unsatisfiable. Every other tracked test file must still name
defined IDs, and a test pins that scope so the exclusion cannot silently widen.
