P5F-1: disk-backed per-project trust reader (pure extraction) [codex APPROVED] - #145
Merged
Conversation
Implements P5F-1 per the accepted P5F plan (PR #143, 0ca3ee6). Zero production callers — pure extraction. The trust module is imported only by tests until P5F-3 wires /agents bg + preflight. Files: - agents/lib/bg-trust.ts (new): resolveProjectRoot, projectRootSha256, sha256Hex, readOrCreateProjectTrustKey, readProjectTrustKey, readProjectTrustStore. Local no-follow helpers inlined to honor INV-2 (private bg-state.ts assertNoSymlink/readUtf8FileNoSymlink not imported). Sign/verify/keyGenId reuse bg-state.ts primitives. - agents/test/test-bg-trust.mjs (new): 12 tests covering all 9 reader states (A-H + I) plus REQ-7 key isolation, REQ-10 both-checks, and the projectTrustStore_symlink red-then-green guard. - agents/test/run-bg-trust-tests.sh (new): slice runner. Verified: - bash agents/test/run-bg-trust-tests.sh -> 12/12 passing - BREAK=1 forces foreign-root fixture to non-discriminating -> exit 1 - REQ-11 grep: only node:* + ./bg-state imports (no third-party) - INV-2 grep: empty (no resolveTrustedHome/readOrCreateSessionMacKey/ getBgStateDir/getBgSessionMacPath references) - REQ-10 ordering: verifyBgPayloadMac (L179) precedes projectRootSha256 compare (L182) - MAC before root, source order. - Existing bg-state + P5E1 suites still green (pure extraction). Note on the plan's verbatim bg-trust.ts source: the docstring comment on readOrCreateProjectTrustKey originally referenced readOrCreateSessionMacKey verbatim, which the INV-2 grep would pick up as a false positive. Reworded to "the session-MAC primitive at bg-state.ts:167-189" - semantic content preserved (line citation intact), forbidden substring removed. All other source matches the Appendix B step 1.1 verbatim block. Co-Authored-By: Claude <noreply@anthropic.com>
Addresses P1_BLOCKER from codex review of e51139d: readProjectTrustStore rethrew malformed-key failures instead of failing closed to the untrusted union, violating REQ-1/REQ-3. Only REQ-2 symlink carve-out may throw. Fix (consensus-reached with codex reviewer, gpt-5.5 high, surface:7): agents/lib/bg-trust.ts (readProjectTrustStore catch block, ~L175-178): - KEEP: ENOENT maps to {ok:false, reason:"forged"} (State E, key absent) - NEW: re-throw errors whose .message starts with "refusing symlinked " (the REQ-2 symlink security boundary, thrown by assertNoSymlinkLocal at L77) - NEW: all other non-ENOENT failures (malformed key content from parseTrustMac, EACCES/EPERM, anything else) map to {ok:false, reason:"forged"} (fail closed) - No custom error class (keeps pure-extraction slice API surface minimal) agents/test/test-bg-trust.mjs (+1 test, suite now 13): - testReadTrustStore_rejectsMalformedKey: writes "zzzz" to .pi/trust/.trust.mac (non-hex, triggers parseTrustMac throw at L139), asserts readProjectTrustStore returns {ok:false, reason:"forged"} via explicit try/catch that fails-on-throw (assert.fail if it throws). NO BREAK=1 shim per Q3 consensus — the try/catch directly covers the regression without test-only production code paths. Verified (run by orchestrator): - bash agents/test/run-bg-trust-tests.sh -> 13/13 passing - BREAK=1 node agents/test/test-bg-trust.mjs -> exit 1 (foreign-root discriminating test still works; new test has no BREAK shim, unaffected) - grep INV-2 (resolveTrustedHome|readOrCreateSessionMacKey|getBgStateDir| getBgSessionMacPath) agents/lib/bg-trust.ts -> empty - grep REQ-11 ^import -> only node:* + ./bg-state.ts - grep "refusing symlinked" -> exactly 2 hits (L77 throw + L177 carve-out) - git status -> only the 2 expected files modified Scope: catch-block discrimination + one test ONLY. No refactor, no API change. No production caller (still pure extraction until P5F-3). Co-Authored-By: Claude <noreply@anthropic.com>
lantiscooperdev
approved these changes
Jul 5, 2026
lantiscooperdev
pushed a commit
that referenced
this pull request
Jul 5, 2026
…, 08a9be6) (#146) P5F-1 disk-backed per-project trust reader merged via PR #145 (squash `08a9be6`). Updates the two workplan pointer files to the new canonical episode head `20260705-130253-post-merge-sync-p5f-1-trust-reader-compl-a5b3` (supersedes the P5F-plan-accepted head `20260704-141949-...a0b1`). Changes: - WORKPLAN.md — current-state block: P5F-1 COMPLETE (#145, 08a9be6); P5F-2 writer+resolver step-tables (author + plan-review) is next. Chain head, revises, tags, summary all updated to the new episode. Follow-up bullet flipped from "P5F-1 (NEXT)" to "P5F-1 COMPLETE" + new "P5F-2 (NEXT)". - agents/P3_IMPLEMENTATION_SLICES.md — P5F section: P5F-1 marked SHIPPED (with the R1→R2 consensus fix trail + 13/13 tests + INV/REQ evidence), P5F-2 marked NEXT (step-tables deferred by design — now to be authored + plan-reviewed since the reader held up under the 13 P5F-1 tests), P5F-3 deferral note dropped from post-P5F-1-review to post-P5F-2-review. Also documents the closure of stale PR #140 (workplan-sync after P5b-2) which was CONFLICTING/DIRTY and fully superseded by #138/#142/#143/#144. The episodic memory episode is canonical; these markdown files are convenience references synced per project-runbook:workplan-update.md. Co-authored-by: Charlton D. Ho <dev@znp.pw> Co-authored-by: Claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements P5F-1 — pure extraction of the disk-backed per-project trust reader primitives. Per the accepted P5F plan (#143,
0ca3ee6). Zero production callers — the trust module is imported only by tests until P5F-3 wires/agents bg+ preflight.Commits
e51139d— P5F-1: disk-backed per-project trust reader (pure extraction) — initial slice (3 files)226c9ab— P5F-1 fix: fail-closed on malformed project trust key (codex P1 blocker) — R2 re-review fixFiles changed (2 net)
agents/lib/bg-trust.tsresolveProjectRoot,projectRootSha256,sha256Hex,readOrCreateProjectTrustKey,readProjectTrustStore(+ catch-block fail-closed fix inreadProjectTrustStore)agents/test/test-bg-trust.mjsagents/test/run-bg-trust-tests.shReview status: ✅ codex APPROVED (cmux consensus, R2)
This slice went through the cmux-orchestrated coder↔reviewer consensus loop:
readProjectTrustStorerethrew malformed-key failures instead of failing closed to the untrusted union (violated REQ-1/REQ-3). All other gates passed (12/12 tests,BREAK=1non-zero, INV-2 grep empty, REQ-10 MAC-before-root).CONSENSUS: agreed) — message-prefix carve-out for the REQ-2 symlink security boundary; map all other non-ENOENT key-read failures (malformed content, EACCES/EPERM) →{ok:false, reason:"forged"}; one new red-then-green test with explicit try/catch fail-on-throw, no BREAK shim; scope limited to catch-block + one test.VERDICT: approve— fix matches consensus, all gates green, regression guard confirmed real.Verification (independently re-run by codex in R2)
bash agents/test/run-bg-trust-tests.sh→ 13/13 passingBREAK=1 node agents/test/test-bg-trust.mjs→ exit 1, 12/13 (foreign-root discriminating test, expected)resolveTrustedHome|readOrCreateSessionMacKey|getBgStateDir|getBgSessionMacPath)agents/lib/bg-trust.ts→ emptynode:*+./bg-state.ts"refusing symlinked"→ exactly 2 hits (L77 throw + L177 catch carve-out)Invariants honored
{ok:false, reason:...}, never throw — the ONLY intentional throw is the REQ-2 symlink carve-out.assertNoSymlinkLocalthrow preserved at L77, re-thrown from the catch via message-prefix match.verifyBgPayloadMac(MAC) executes BEFOREprojectRootSha256compare, in source order.node:*+./bg-state.resolveTrustedHome/readOrCreateSessionMacKey/getBgStateDir/getBgSessionMacPathreferences.Plan reference
agents/docs/P5F_DISK_BACKED_TRUST_READER_PLAN.md— Appendix B step 1.1 (verbatimbg-trust.tssource) + step 1.2 (test contract).