Repository navigation
fix: E01 contract review — claims vs observations, conditional channel, at-rest trust - #415
Merged
Merged
Conversation
…l, at-rest trust Corrects the candidate contract merged in #414 on the maintainer's sequencing and trust findings. E01 (#399) is not closed by that merge; E02/E03 stay blocked and E09 stays on hold. - Status: "frozen" becomes "candidate contract; E01 acceptance pending" in the contract, README, implementation plan, and code comments; the decision record now lists the five questions (D1–D5) that keep #399 open. - Claims vs observations: attestation is three-way (adapter, host-tool-result, caller). proofAuthority derives observed/claimed from collector AND attestation, so relabelling a caller-reported record host-observed never elevates it; proofDischarges refuses claimed proofs under the default `verified` policy. The host-tool-result path (a host hook delivering the tool's actual exit code/output to a host-side ingestion entry, E05) is specified without CodeCartographer executing anything. Claimed records are retained and disclosed. - Assurance policy: `verified` is the only meaning of verified; `cooperative` is a separate operator-set policy, never selectable by a request, labelled on every result, and not authorized for the pilot. - Conditional channel: VERIFIED_ACCEPTANCE_INTEGRATIONS (empty) is the only source of support; acceptanceChannelSupported refuses any host/client pair not in it, whatever the client advertises; the integration check's four required observations are documented; receipts carry verified_integration. - Channel vs at-rest trust: approvals carry assurance and the storage boundary at mint; classifyAcceptance is verified only with a bound receipt, verified integration, verified policy, and a host-enforced boundary at mint and now — a real UI decision in an agent-writable namespace is cooperative. A record may not label itself verified over an unprotected boundary. Pi guards unchanged; no credential system. Fixtures: +2 valid (caller-reported proof, cooperative approval), +7 negative; tests +3 (21 total); full suite 969 pass. Refs #399 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ndidate Independent review of b787ca1 found the tightening below; all folded in with fixtures and tests. Two of its questions belong to the maintainer and are recorded as D3/D4/D5 sub-questions in the decision record. - host-tool-result is pinned: only a host-side ingestion entry the model's tools cannot reach, configured where agent tools cannot write, qualifies; the record requires tool_call_id from the host payload and a host-observed collector; the adapter never attests a host-observed check; a snapshot is never a tool result. - The receipt names the client; classifyAcceptance re-derives the integration from the current registry (de-verification downgrades old approvals) and takes CurrentStorage with enforced_since — an approval decided before the boundary was continuously enforced stays cooperative however its storage field is spelled, so enabling a boundary later cannot launder a forged record. Malformed storage context fails closed. - Presentation discloses a candidate that could not bind a verified acceptance; reviewer-separation wording names the review's author, not the host; provenance.source is documented as part of the claim; channel comments carry the "until registered" qualifier; doc field tables match the code again. Refs #399 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ments The maintainer's counterexample: while the namespace is unprotected, an agent pre-writes a request/approval pair whose issued/decided times post-date the epoch at which protection will later be enabled. Against 92b034a classifyAcceptance returned verified, because the epoch check compared the trusted enforced_since with decided_at — a timestamp inside the forged record. Reproduced, then fixed without trusting any other timestamp or flag in the writable record set: - CurrentStorage gains `protection`, the host's out-of-namespace account of the whole history: continuous-since-initialization | enabled-after-initialization | interrupted | imported-history. Only the first makes any record eligible for verified; the others, and an omitted value, classify cooperative. The decided_at/enforced_since comparison is demoted to a consistency check that can only lower trust. - The contract states the initial policy — protection before the namespace's first write, never lapsed; existing unprotected records never become verified; an interruption is permanent; imported history is untrusted — and records the remaining mechanism (where the initialization marker and lapse history live, how a lapse is detected, how a fresh namespace is initialized) as D3, not solved by the epoch. - D4's unreachable ingestion entry and protected configuration are requirements: HostCapabilities.tool_result_path (protected | unprotected | none) and attestationForHostObservation, which yields host-tool-result only for protected. The same protection-history rule is stated to govern proof files. - A "what the code proves / what the host must enforce" table. Tests: the exact counterexample under each protection history (RED against the previous logic: 2 failures with the check disabled), and the tool-result-path rule. 23 targeted, 971 full suite. Refs #399 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…tion-history rule (review consistency items) ACCEPTANCE_REQUIRED_RECORDS and the doc's acceptance table stated the pre-fix "host-enforced now" rule; both now name continuous-since-initialization. The proof-file corollary is recorded as an E06 requirement in the proves/enforces table; the declaration-provenance sentence names protection and tool_result_path; an undeclared protection history gets an honest reason; TOOL_RESULT_PATHS is pinned; attestationForHostObservation accepts an undeclared path as the doc says. Refs #399 Co-Authored-By: Claude Opus 5 <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.
Corrective follow-up to #414 for E01 (#399), per the maintainer's review. Do not merge until the maintainer has reviewed the handoff on #399. Merging #414 did not close E01; E02/E03 remain blocked and E09 stays on hold.
What this changes
1. Status. "Frozen" → "candidate contract; E01 acceptance pending" in the contract, README, implementation plan, and code comments. The decision record now lists five questions (D1–D5) the maintainer must answer before #399 closes.
2. Claims vs observations. Three layers are kept apart on every proof: the claim (
collector,result,exit_code,check— caller text on the tool path), the attestation (provenance.attested_by∈adapter|host-tool-result|caller, adapter-set,unknown-fieldon every input), and the derived authority (proofAuthority→observedonly with an observed collector and an observing attestation). Relabelling a caller-reported recordhost-observednever elevates it — the test pins this for every collector value.proofDischargesrefuses claimed proofs under the defaultverifiedpolicy. The qualifyinghost-tool-resultpath is specified without CodeCartographer executing anything: a host-side ingestion entry the model's tools cannot reach, configured where agent tools cannot write; the host payload supplies command/cwd/exit code/output digest/tool_call_id; pairing rules are enforced (host-tool-result⇒host-observed+tool_call_id;adapternever attestshost-observed; snapshots neverhost-tool-result). Claimed records are retained and disclosed.cooperativeis a separate, operator-set, request-unselectable policy, labelled on every result and not authorized for the pilot (D5).3. MCP elicitation stays conditional.
VERIFIED_ACCEPTANCE_INTEGRATIONS(empty) is the only source of support;acceptanceChannelSupportedrefuses any host/client pair not in it, whatever the client advertises or the adapter asserts. The integration check's four required observations (presentation, acceptance, rejection/cancellation, stale/mismatched response) are documented. Unverified paths stop atneeds-human-acceptance. The claim is session-attested, not human identity. Nothing here approves the channel (D1/D2).4. Channel trust ≠ at-rest trust. Approvals carry
assuranceandstorage.boundaryat mint; receipts carryclient.classifyAcceptanceisverifiedonly with a bound receipt, the(host, client, channel)triple in the current registry, theverifiedpolicy, and a reader-sideCurrentStorage { host-enforced, enforced_since }whose epoch precedesdecided_at. A real UI decision in an agent-writable namespace iscooperative; a forged record spelledhost-enforced/verified_integration: truestays cooperative under boundarynoneor a later epoch (so enabling a boundary later cannot launder it); a record may not label itselfverifiedoverstorage.boundary: none. The same-user-filesystem caveat is kept and is not used to satisfy the stronger requirement. No credential/token/signature system; Pi guards untouched.Verification (on 92b034a, Linux, Node v22.22.3)
node --test tests/engineering-contract.test.mjsproofAuthoritytrusts the collector labelnpm run buildnpm testgit diff --check; hygiene scanFixtures: +2 valid (caller-reported proof, cooperative approval), +14 negative (132 total); tests +3.
Independent review
Round A (on b787ca1, opus, findings-only): met demands 1 and 3; 2 and 4 partial — eleven findings with concrete triggers (host-tool-result reachable by the model, boundary-toggle laundering, self-reported
verified_integration, unenforced pairing rules, doc/code table drift, wording). All folded into 92b034a except two that are the maintainer's to answer and are now D3/D4/D5 sub-questions. Round B (on 92b034a, sonnet): all nine tightenings verified closed by probe; no findings; nothing classifiesverifiedagainst the contract's own empty registry.Open for the maintainer (contract § Decision record)
D1 MCP elicitation on a named verified host/client pair? D2 Pi
host-native? D3 storage-boundary mechanism, where its declaration andenforced_sincelive, boundary-before-first-write? D4 observed-evidence path: must E05's entry be demonstrably unreachable by the model and its hook config outside agent-writable roots? D5 iscooperativepermitted at all, and do honestagent-claimedrecords discharge equally there?Refs #399, #398
🤖 Generated with Claude Code