# Task 9 quality audit — final 5 **Scope:** the two blocking findings from `task9-quality-audit-final4.md` — unbound production module graph at manual serve, and commit-addressed snapshots accepted without content identity at render. Manual acceptance remains **PENDING**; no `VERDICT.md` was created. ## Verdict: APPROVED for the two final integrity blockers ### 1. Manual serve binds the complete `backend/dist` module graph, not only `server.js` `prepare` now builds a post-build manifest of every regular `backend/dist` file (relative path, size, SHA-256, device, inode) and writes it as an exclusive `0600` record (`installation/runtime/backend-dist.manifest.json`) inside the owned root; `ownership.json` records that record's path/device/inode/size/SHA-256. `serve` revalidates the manifest record identity and bytes, revalidates every distribution file against it (no-follow, single inode, size and digest), and refuses before spawning. The manifest descriptor is passed to the child on fd 4 together with the entrypoint on fd 3. The immutable preload parses the manifest, verifies the entrypoint cross-digest, reads and hash-verifies **every** file at startup, caches the verified bytes, and its load hook serves **only** those cached bytes for any import below `backend/dist` (entry URL still served from the bound fd-3 bytes). A same-path regular replacement of any imported dependency is therefore refused before `RUNNING` (serve-time validation), refused at child startup (startup verification), or rendered harmless (cached bytes), and the parent revalidates the full manifest at `RUNNING` publication and at `stop`. ### 2. Renderer binds snapshot content to its commit identity The generated render command validates the bounded saved read/publish revisions, the commit-addressed owned snapshot path, the installed Git HEAD, and the bounded `snapshot.json` manifest of that commit: `head` equals the commit, `files[.yaml]` is the SHA-256 of the snapshot bytes, the manifest revision binds commit/blob/snapshot path, the saved revision blob equals the manifest blob, and `git rev-parse :workspaces/.yaml` plus `git hash-object` of the snapshot bytes both equal that blob. It passes the expected digest as `--snapshot-sha256`. The renderer re-reads the bounded `snapshot.json` (`head`, `files[.yaml]` must equal the carried digest), opens the snapshot once with no-follow semantics and bounded reads, renders only the digest-verified bytes, re-verifies around lease publication, releases the lease in `finally`, and publishes no output on any refusal. ## Deterministic regressions added - static regular replacement of an imported production dependency after `prepare` is refused, no marker, no accepted PID record, no orphan; - deterministic dependency check/load swap (`beforeSpawn` rename) is refused by the child's startup verification, no marker, no PID record, no orphan; - after `RUNNING`, a same-path regular dependency replacement is never executed: the loader serves the verified cached bytes (health-visible source stays the original) and the marker is absent; - renderer refuses a same-path regular snapshot byte replacement against the carried digest and manifest, with lease release and no output; - renderer refuses manifest `head`, `files` digest, expected-digest, missing, and malformed cases, with lease release and no output; - wrapper refuses missing manifest, manifest head/digest/revision tampering, saved-revision blob mismatch, Git blob mismatch, and snapshot-vs-Git-bytes mismatch, and passes the exact `--snapshot-sha256` on the valid path (stub renderer records arguments). ## Verification - `bash scripts/test-p1-manual-acceptance.sh` (backend build + both suites): **59 tests, 59 pass, 0 fail**; no `8791/8792` listener and no `--p1-manual-nonce` process remain. - `npx tsc --noEmit -p .` (backend): PASS. - Real-repository `prepare` + `cleanup` cycle: 39 distribution files bound, entrypoint cross-digest verified, owned root fully removed afterwards. - Diff check: only the seven Task 9 paths are touched; no Task 8 file was modified. - This report and the implementation contain no fixture secret or canary values. Manual acceptance remains **PENDING** by design; the walkthrough and human verdict are unchanged.