docs(coverage): sync docs to shipped state + wire sandcastle prompts

Closes the staleness gap after the 10-commit coverage epic shipped.

Doc sync (item 1 from the user's choice):
  - CLAUDE.md Quick Start: adds pnpm coverage:aggregate / coverage:diff
    / mutate to the command listing
  - CLAUDE.md: new "Sibling architecture: coverage (ADR-020)" section
    after the conformance gate table — captures the 4-layer table +
    points at docs/guides/coverage.md + ADR-020 + says agents must run
    coverage:diff before reporting complete
  - AGENTS.md preamble: now lists coverage as a parallel multi-latency
    quality system alongside conformance, with the same gate / latency
    framing
  - PRD frontmatter: status draft -> shipped + shipped date +
    shipping-commits list (all 10 SHAs anchoring the trace)
  - PRD findings table: each row gets a Resolution column citing the
    commit that closed it; conclusion text updated to past tense
  - ADR-020 implementation phasing: rewritten as a status table with
    each step linked to the commit that shipped it + Boot-time
    assertFeatureConformance explicitly marked Deferred with rationale
  - docs/guides/coverage.md: removed "Boot wiring lands in the next
    story" line; replaced with the deferral rationale + clarified
    that two readers (vitest, coverage:diff) consume the manifest

Sandcastle prompts (item 2 from the user's choice):
  - .sandcastle/implementer.prompt.md: new "Coverage gates" section
    after the conformance-gates list, requiring `pnpm test --coverage`,
    `pnpm coverage:aggregate`, and `pnpm coverage:diff` to all pass
    before reporting `complete`. Machine-readable JSON shape of
    coverage:diff documented (status / uncovered[] / kind enum), with
    explicit instructions on how to interpret each kind. Allowlist
    expansion requires justification + test.
  - .sandcastle/reviewer.prompt.md: AC coverage relabeled to "AC
    coverage (acceptance criteria, not test coverage)" to disambiguate;
    new check #7 "Coverage gates (ADR-020)" requiring CI's
    Coverage — diff (L1) step green + per-layer thresholds met +
    no silent allowlist expansion + manifest band drift detection.

Effect: future agent runs through sandcastle now treat coverage as a
first-class blocking gate, parallel to conformance. PRs no longer
discover coverage failures only via CI; the implementer is required
to check before reporting done, and the reviewer is required to
verify.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-13 16:47:16 +02:00
parent bf0b049583
commit fc27eef6eb
8 changed files with 82 additions and 17 deletions

View File

@@ -90,16 +90,22 @@ This eliminates the duplication that caused the `@repo/media` drift and centrali
## Implementation phasing
In order (each landable independently):
Shipped as a single epic over 10 commits on 2026-05-13. Per-step state:
1. **L0 unification** — fix `@repo/media` (missing dep + missing config block) and `@repo/navigation` (real test gaps) so every feature passes its declared bands today.
2. **Manifest schema** — extend `feature.manifest.ts` shape (Zod schema in `core-shared/conformance/`) with the `coverage:` section.
3. **Vitest auto-derive**`vitest.config.ts` per feature imports the manifest and emits `coverage.thresholds`. Eliminates duplication.
4. **L1 diff coverage**`scripts/coverage/diff.mjs` + `pnpm coverage:diff` script + CI gate.
5. **L2 aggregate**`scripts/coverage/aggregate.mjs` + `pnpm coverage:aggregate` + summary.json + merge-to-main workflow.
6. **L3 mutation testing** — Stryker setup + `pnpm mutate` + nightly GH Action.
7. **Boot-time `assertFeatureConformance`** — coverage band read against lcov.
8. **Docs + generator + hook rollout** — this ADR (now landing), `docs/guides/coverage.md`, glossary entries, `pnpm turbo gen feature` template update, `.claude/hooks/prompt-context.sh` keyword group.
| # | Step | Commit | Status |
| --- | ----------------------------------------------------------------- | --------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 1 | Manifest schema + helper + auth proof-of-concept | `f7baa8b` | ✅ Shipped |
| 2 | Vitest auto-derive (helper + `DEFAULT_COVERAGE_BANDS`) | `f7baa8b` + rollouts | ✅ Shipped (all 5 features wired) |
| 3 | L1 diff coverage (`scripts/coverage/diff.mjs`) | `412d994` | ✅ Shipped |
| 4 | L2 aggregate (`scripts/coverage/aggregate.mjs` + `summary.json`) | `bd5a077` | ✅ Shipped |
| 5 | CI integration (validate gate + snapshot workflow) | `39e33eb` | ✅ Shipped |
| 6 | Helper rollout to blog + marketing-pages | `15db9c4` | ✅ Shipped |
| 7 | Docs + generator + hook rollout | `4dce1df` + `f4254aa` | ✅ Shipped |
| 8 | L3 mutation testing (Stryker + nightly Action) | `6428f10` | ✅ Shipped (auth proof-of-concept; other features can add `stryker.config.json` by `extends: "@repo/core-testing/stryker.base.json"`) |
| 9 | L0 unification (close test gaps in nav + media + marketing-pages) | `bf0b049` | ✅ Shipped — all 5 features hit declared bands |
| 10 | Boot-time `assertFeatureConformance` coverage check | — | ⏸ Deferred. Duplicates L0's structural enforcement when both readers derive from the same manifest source of truth; the drift it was supposed to catch is mechanically impossible. Revisit if a concrete need emerges. |
Repo-wide state at shipping (`coverage/summary.json`): statements 95.87% / branches 88.91% / functions 100% / lines 95.87%. All five features pass their declared 100%/100%/95%/100% bands on entities/use-cases/controllers.
## Related

View File

@@ -44,11 +44,12 @@ export const myFeatureManifest = defineFeature({
} as const);
```
Three readers pick this up:
Two readers pick this up today:
1. **`vitest.config.ts`** — uses `vitestThresholdsFromBands(DEFAULT_COVERAGE_BANDS)` from `@repo/core-shared/conformance/coverage`. Most features import `DEFAULT_COVERAGE_BANDS` directly (the manifest's `coverage` section matches the defaults). For features with custom bands, override at the vitest config too.
2. **`assertFeatureConformance`** — at app boot, reads the manifest's bands and asserts the produced lcov meets them. _(Boot wiring lands in the next story.)_
3. **`pnpm coverage:diff`** — uses the bands for per-path expectations against the merged lcov.
2. **`pnpm coverage:diff`** — uses the bands for per-path expectations against the merged lcov.
(A third reader, a boot-time `assertFeatureConformance` coverage check, was specified in the PRD and explicitly deferred per ADR-020 — when both readers above derive from the same manifest, the drift it was supposed to catch is mechanically impossible. The manifest's `coverage:` field remains the declarative source of truth regardless of how many readers consume it.)
**Edit the manifest. The other readers pick up the change.**

View File

@@ -1,5 +1,5 @@
{
"updated_at": "2026-05-13T11:37:22.767Z",
"updated_at": "2026-05-13T14:47:21.408Z",
"epics": {
"template-reset-v1": {
"status": "done",

View File

@@ -2,10 +2,22 @@
id: 2026-05-13-coverage-architecture
title: Agent-first coverage architecture (4 layers + manifest-driven thresholds)
type: prd
status: draft
status: shipped
author: danijel
elicitation-session: brainstorm-2026-05-13
created: 2026-05-13
shipped: 2026-05-13
shipping-commits:
- 7eb783a (PRD)
- 4dce1df (ADR-020 + glossary + hook)
- f7baa8b (manifest schema + helper + auth)
- 412d994 (L1 coverage:diff)
- bd5a077 (L2 coverage:aggregate)
- 39e33eb (CI integration)
- 15db9c4 (helper rollout blog + marketing-pages)
- f4254aa (cookbook guide + generator)
- 6428f10 (L3 Stryker mutation)
- bf0b049 (L0 unification — all 5 features green)
---
## Problem