--- id: 04-ci-drift-gate epic: conformance-system-v1 title: CI drift gate — pnpm conformance with cross-feature event closure type: technical-story status: done feature: scripts depends-on: [03-b-ast-eslint-rules] blocks: [05-generator-updates] --- ## Goal `pnpm conformance` aggregates cross-feature checks that no single-file ESLint rule can perform — most importantly, event closure: every event declared in any manifest's `consumes` must have at least one matching `publishes` somewhere in the repo. ## Why Per-file lint can't see cross-feature contracts. Without this gate, a feature can declare it consumes `X` while no feature publishes `X` — silent until the broken handler is exercised in prod. ## Done when - `pnpm conformance` exits 0 when manifests are consistent; non-zero with a clear error message on orphan consumers - Wired into `turbo.json` as the `conformance` task - Wired into `.github/workflows/ci.yml` after `pnpm lint` ## In scope - `scripts/conformance.mjs` — orphan-consumer check - Tests via vitest - turbo.json + CI wiring ## Out of scope - Scaffold drift check (regenerate via `turbo gen feature`, diff against on-disk state) — depends on generator updates landing - Repository write outside use-cases check — separate concern - Reverse check (orphan publishers — events nothing consumes) — many events are intentionally "fire and forget"; not a closure violation ## Tasks - [x] Story scaffold - [x] `scripts/conformance.mjs` implementation - [x] Tests for the script - [x] Wire into root package.json + turbo.json - [x] Wire into ci.yml - [x] Final verification + closeout