Skip to content

P5F-1: disk-backed per-project trust reader (pure extraction) [codex APPROVED] - #145

Merged
lantiscooperdev merged 2 commits into
mainfrom
feat/p5f-1-trust-reader
Jul 5, 2026
Merged

P5F-1: disk-backed per-project trust reader (pure extraction) [codex APPROVED]#145
lantiscooperdev merged 2 commits into
mainfrom
feat/p5f-1-trust-reader

Conversation

@lantisprime

Copy link
Copy Markdown
Owner

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

  1. e51139d — P5F-1: disk-backed per-project trust reader (pure extraction) — initial slice (3 files)
  2. 226c9ab — P5F-1 fix: fail-closed on malformed project trust key (codex P1 blocker) — R2 re-review fix

Files changed (2 net)

File Change
agents/lib/bg-trust.ts NEW: resolveProjectRoot, projectRootSha256, sha256Hex, readOrCreateProjectTrustKey, readProjectTrustStore (+ catch-block fail-closed fix in readProjectTrustStore)
agents/test/test-bg-trust.mjs NEW: 13 tests (12 original + 1 added in fix for malformed-key regression guard)
agents/test/run-bg-trust-tests.sh NEW: slice runner

Review status: ✅ codex APPROVED (cmux consensus, R2)

This slice went through the cmux-orchestrated coder↔reviewer consensus loop:

  • R1 review (codex, gpt-5.5 high): 1 P1_BLOCKER found — readProjectTrustStore rethrew malformed-key failures instead of failing closed to the untrusted union (violated REQ-1/REQ-3). All other gates passed (12/12 tests, BREAK=1 non-zero, INV-2 grep empty, REQ-10 MAC-before-root).
  • Consensus discussion: fix shape pinned (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.
  • R2 re-review (codex): 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.sh13/13 passing
  • BREAK=1 node agents/test/test-bg-trust.mjs → exit 1, 12/13 (foreign-root discriminating test, expected)
  • INV-2 grep (resolveTrustedHome|readOrCreateSessionMacKey|getBgStateDir|getBgSessionMacPath) agents/lib/bg-trust.ts → empty
  • REQ-11 imports → only node:* + ./bg-state.ts
  • "refusing symlinked" → exactly 2 hits (L77 throw + L177 catch carve-out)
  • Diff scope → only the 2 expected files

Invariants honored

  • REQ-1/REQ-3 (fail-closed): trust-content failures return {ok:false, reason:...}, never throw — the ONLY intentional throw is the REQ-2 symlink carve-out.
  • REQ-2 (symlink boundary): assertNoSymlinkLocal throw preserved at L77, re-thrown from the catch via message-prefix match.
  • REQ-7 (key isolation): project trust key distinct from session MAC key.
  • REQ-10 (timing-oracle mitigation): verifyBgPayloadMac (MAC) executes BEFORE projectRootSha256 compare, in source order.
  • REQ-11 (no third-party deps): only node:* + ./bg-state.
  • INV-2 (forbidden imports): no resolveTrustedHome/readOrCreateSessionMacKey/getBgStateDir/getBgSessionMacPath references.

Plan reference

agents/docs/P5F_DISK_BACKED_TRUST_READER_PLAN.md — Appendix B step 1.1 (verbatim bg-trust.ts source) + step 1.2 (test contract).

Charlton D. Ho and others added 2 commits July 5, 2026 20:00
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
lantiscooperdev merged commit 08a9be6 into main Jul 5, 2026
1 check passed
@lantisprime
lantisprime deleted the feat/p5f-1-trust-reader branch July 5, 2026 13:00
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants