Skip to content

feat: record D1–D5 and the D1 live-check findings in the E01 contract - #417

Merged
TheAmericanMaker merged 4 commits into
mainfrom
docs/e01-decision-record
Sep 18, 2026
Merged

TheAmericanMaker merged 4 commits into
mainfrom
docs/e01-decision-record

Conversation

@TheAmericanMaker

Copy link
Copy Markdown
Member

Records the maintainer's 2026-09-18 decisions D1–D5 in docs/engineering/record-contract.md § Decision record, after the Claude Code 2.1.263 spike (#416). Documentation only; no code change; VERIFIED_ACCEPTANCE_INTEGRATIONS stays empty; cooperative stays unauthorized; #399 stays open pending the D1 live check; E02/E03 blocked; E09 on hold.

  • D1 conditional yes — MCP elicitation on claude-code/2.1.263 + the codecarto MCP server, session-attested; registry empty until the maintainer's live check; new requirement that the operator attests no Elicitation/ElicitationResult hooks, which the adapter cannot verify and the receipt must disclose.
  • D2 deferred; no second channel.
  • D3 boundary accepted (E5/E6 configuration, session-level); continuity not provided; SessionStart marker recorded as an open E02 investigation.
  • D4 accepted: user/managed settings + no bypassPermissions + the full E5/E6 list; re-run E5/E6 wherever the real ingestion entry is placed.
  • D5 no.
  • Consequence stated plainly: observed proofs, acceptances that classify cooperative; E06 surfaces the classification rather than refusing.
  • Three forward notes: E02/E05 parse only the inbox's record shape (never .err/.status); E05 needs retention for hook writes; the set -C proof is per-host (bash here, dash on Debian-family) and must be re-proved on the pilot host.

npm test 971/971; git diff --check clean.

Refs #399, #398

🤖 Generated with Claude Code

D1 conditional yes for claude-code/2.1.263 + codecarto MCP, registry
empty until the live check; operator attests no Elicitation hooks and
the adapter cannot verify it (receipt disclosure). D2 deferred. D3
boundary accepted (E5/E6 configuration), continuity not provided,
SessionStart marker an open E02 investigation. D4 accepted with the
full E5/E6 requirement list, re-run wherever the real entry lives. D5
no. Consequence stated: observed proofs, cooperative acceptances; E06
surfaces the classification rather than refusing. Three forward notes:
record parsing scoped to the inbox, retention for hook writes, per-host
O_EXCL proof. No code change; registry empty; #399 stays open.

Refs #399

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…nly, timeout outcome

The maintainer ran the D1 live interactive check on claude-code 2.1.277
(host auto-updated from 2.1.263 three minutes earlier); it passed with
three findings, each now in the contract with tests and fixtures:

- Version drift. VerifiedAcceptanceIntegration.client_version is exact;
  acceptanceChannelSupported refuses a live clientInfo.version that
  differs (or is absent) and names both versions; classifyAcceptance
  re-derives against the exact version; receipt.client.version is
  required. Test: registered 2.1.263, live 2.1.277 → unsupported.
- Timeout is a third outcome. ElicitationOutcome adds timed-out
  (distinct from rejected/declined/cancelled); AcceptanceRequestOutcome
  carries declined and timed-out, neither minting a record;
  client_request_timeout_ms on the registry entry and
  acceptanceTtlWithin require the adapter's TTL to stay below it.
- Action and decision can disagree — observed live (action accept,
  content.decision reject). elicitationDecision derives the outcome only
  from content.decision; action alone never authorizes; missing, empty,
  or unrecognized decisions are invalid. Ten fixtures under
  valid/elicitation/, the first three verbatim from the log; a mutation
  that trusts `action` fails the suite.

D1 is recorded as YES for claude-code/2.1.277 + the codecarto MCP
server, conditional on those three items plus the operator attestation
on Elicitation hooks; the registry stays empty until the entry is added
by amendment. E5 and E6b were re-run on 2.1.277 with identical results
and the spike report says which binary each observation is from.

Refs #399

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@TheAmericanMaker

Copy link
Copy Markdown
Member Author

Revision: D1 live-check findings folded in (955e086)

The maintainer ran the D1 live check on claude-code 2.1.277 (auto-updated from 2.1.263 three minutes earlier); it passed. Three findings, each now in the contract — this PR is no longer docs-only:

Finding Contract change Test / fixture
Version drift — a version-pinned registry entry goes stale silently VerifiedAcceptanceIntegration.client_version (exact); acceptanceChannelSupported refuses a live clientInfo.version that differs or is absent, naming both; classifyAcceptance re-derives on the exact version; receipt.client.version required registered 2.1.263 vs live 2.1.277 → unsupported; stale-version receipt → cooperative
Timeout is a third outcome — -32001 after ~2.5 min is not a decline ElicitationOutcome.timed-out; AcceptanceRequestOutcome.outcome gains declined and timed-out (neither mints); client_request_timeout_ms + acceptanceTtlWithin require the adapter's TTL below the client's observed timeout TTL 120 s within 150 s; 150 s and unmeasured → not within
Action and decision disagree — observed (action: accept, content.decision: reject) elicitationDecision derives the outcome only from content.decision; action never authorizes; missing/empty/unrecognized decision → invalid 10 fixtures under valid/elicitation/ (the three live shapes verbatim); mutation that trusts action fails the suite

D1 is recorded as YES for claude-code/2.1.277 + the codecarto MCP server, conditional on those three items plus the operator attestation that no Elicitation/ElicitationResult hooks are configured (adapter cannot verify; receipt disclosure). The registry stays empty; the entry is added by amendment with the live-check evidence, now that the three items are implemented and tested. D2–D5 unchanged.

Also: E5 and E6b re-run on 2.1.277 with identical results; the spike report states which binary each observation came from.

npm run build exit 0; npm test 972/972; git diff --check clean; no private paths.

@TheAmericanMaker TheAmericanMaker changed the title docs: record the maintainer's D1–D5 decisions in the E01 decision record feat: record D1–D5 and the D1 live-check findings in the E01 contract Sep 18, 2026
Two trust rules in the E01 contract were expressed as helpers an adapter
was trusted to call, so neither bound anything:

- acceptanceTtlWithin was never called from core/. The shipped fixture
  paired a 3600 s window with the 150 s client timeout it is supposed to
  fit inside and still classified verified. checkAcceptanceRequestTtl is
  now the enforcement form and classifyAcceptance calls it, against the
  registry entry the receipt resolves to; an unmeasured
  client_request_timeout_ms fails closed, because an unmeasured client
  bounds nothing.

- buildAcceptanceRequest composes an honest presentation, but the digest
  binding only proves the person saw *a* presentation. A request minted by
  any other path could show an empty limitations list over a caller-reported
  proof or an unprotected namespace, validate byte-for-byte, bind a receipt,
  and read verified. checkPresentationDisclosure re-derives the required
  disclosure from the records the acceptance binds to; classifyAcceptance
  refuses verified when a line was withheld or the presentation named a
  different policy than the acceptance is read under.

Also: a thrown elicitation response is timed-out only on the structured
JSON-RPC code, not on error prose that merely contains the words —
{ threw: "accept: request timed out" } is not evidence of a timeout.

Fixtures: the valid acceptance request is now a 120 s window answered at
60 s, so the suite's own approval path satisfies the rule it asserts; new
negative fixture acceptance-request-ttl-outlives-client-timeout covers a
window inside the 24 h cap but longer than the client's timeout.

Mutation checks: disabling the TTL enforcement turns 1 test red; disabling
the disclosure enforcement turns 2 red.

Verified: 25/25 targeted, 973/973 full suite, build exit 0, diff --check
clean.
An independent review of the previous commit found that the disclosure fix
had replaced "the adapter is trusted to call the helper" with "the reader is
trusted to pass the records" — the same defect one level down, and this one
failed open: omitting context.proofs/reviews defaulted them to [], so no
proof line was ever required and the exact attack the commit claimed to
close returned verified with no reasons.

- proofs and reviews are now required for a verified reading. A reader that
  omits them gets cooperative with an explicit reason, because a degradation
  must lower trust, never raise it. Pass [] to assert there are none.

- A host/client/version/channel tuple registered more than once let array
  order pick the timeout: [generous, tight] read a 60-minute window as
  verified while [tight] alone read cooperative, and [tight, unmeasured] vs
  [unmeasured, tight] disagreed on identical registry content. Duplicates are
  now named as a reason and the TTL binds against the strictest matching
  entry, so an unmeasured entry cannot be sidestepped by a measured sibling.

- client_request_timeout_ms was only checked !== undefined, so "999999999",
  Infinity, and [999999999] each coerced past the comparison and read
  verified. It must now be a finite positive number.

Also documented, not fixed: the disclosure check compares lines by exact
equality, so a presentation carrying every required line plus contradicting
text passes. The validator cannot tell clarification from contradiction;
reviewing presentation copy is an operator responsibility.

Mutation checks, each turning the suite red: defaulting proofs/reviews to []
(1 failure), taking matches[0] instead of the strictest entry (1), reducing
the timeout guard to an undefined check (1).

Verified: 25/25 targeted, 973/973 full suite, build exit 0, diff --check
clean.
@TheAmericanMaker
TheAmericanMaker merged commit b868fd9 into main Sep 18, 2026
6 checks passed
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.

1 participant