diff --git a/docs/remediation/TASKS.md b/docs/remediation/TASKS.md index c68ce93f..1f7d5dd1 100644 --- a/docs/remediation/TASKS.md +++ b/docs/remediation/TASKS.md @@ -93,6 +93,52 @@ and must not be cited as merge evidence. Rely on reviewer clearance + real CI. Three independent live instances in a single session — format gate, agent context reset, queue guard — is the class confirmed, not anecdote. +### D-18 — two pre-registered criteria were mutually unsatisfiable, discoverable only at implementation + +Implementing D-17's fix surfaced a conflict **between** pre-registered criteria: + +- **AC2** (as written) — reject symlinked generated state. +- **AC4** — the canonical `pnpm -w build` succeeds and leaves no residue. + +Verified independently rather than taken on report: `apps/web/next.config.ts:4` sets +`output: 'standalone'`, and the built tree contains **42 legitimate pnpm dependency symlinks** under +`.next/standalone/node_modules`. A blanket descendant-symlink rejection makes the canonical build fail +its own preflight with exit 43. **AC2 read literally is unsatisfiable alongside AC4 under this +configuration**, and nothing short of building the tree would have revealed it. + +**Third distinct failure mode of a pre-registered check set**, completing the chain: + +| finding | a pre-registered check set can be… | +| ------- | ------------------------------------------------------------------ | +| D-8 | **wrong** — a check that does not test what it claims | +| D-17 | **incomplete** — green while a criterion's requirement is untested | +| D-18 | **internally inconsistent** — two criteria that cannot both hold | + +The implementing seat escalated instead of silently picking a winner. That matters: **quietly resolving +a conflict between pre-registered criteria destroys the point of pre-registering them** — the registration +exists so that changes of meaning are auditable rather than absorbed. + +**Resolution (orchestrator ruling).** Approved a **build-certified symlink manifest**: `.next` itself is +still rejected as a symlink; descendants are rejected unless _exactly_ certified by a manifest the build +publishes atomically. Strictly **stronger** than blanket rejection — it also catches a **retargeted** +symlink, which blanket rejection cannot distinguish from a legitimate one. + +**AC2 restated (recorded, not absorbed).** _Generated state must reject `.next` itself being a symlink, +and must reject any descendant symlink not exactly certified by the build manifest — added, removed, +retargeted, or manifest-tampered all fail with exit 43._ + +**Hardening required before this counts.** The manifest is itself generated state, so **a manifest +writable by whoever plants a rogue symlink certifies the attack** — that is the one way this design +fails. It must sit inside the same ownership/fingerprint envelope, published atomically via the existing +marker mechanism, with negative controls **observed red first** for: added, removed, retargeted, +**manifest-tampered**, plus a positive control that the canonical build passes. The tampered-manifest +control is non-negotiable; without it, integrity is a claim rather than a property. + +**Requirement on RM-02 (fourth clause).** The registry must detect **conflicts between registered +criteria**, not only wrongness and coverage. Two criteria that cannot simultaneously hold is a registry +defect discoverable by construction — and when a criterion is restated, the registry must retain the +original text, the restatement, and the reason, so the evolution stays auditable. + ### D-17 — a pre-registered criterion passed a green suite without being satisfied `rev-974` returned **CHANGES REQUESTED** on PR #1027 with one blocking finding, and it is the sharpest