Repository navigation
[E01] Freeze v1 engineering record and approval contracts #399
Description
Activity
Candidate: PR #414, reviewed commit
dcf964f9733deb79844872a52efe81d5d3071dbd(branchfeat/e01-engineering-contract, fromee091ac). Not merged; no tag, release, or version bump.Changed paths:
core/engineering/{types,ids,digest,validation,index}.ts;core/index.ts(+1 export);docs/engineering/record-contract.md(rewritten as the normative contract),docs/engineering/README.md,docs/engineering/implementation-plan.md(E01 status line);tests/engineering-contract.test.mjs;tests/fixtures/engineering/v1/(generator, 22 valid, 97 invalid). Nothing under.codecarto/,mcp-server/, orextensions/changed.Verification on
dcf964f(Linux, Node v22.22.3):- RED: with no
core/engineering/index.ts, the targeted test failedERR_MODULE_NOT_FOUND(0/1, exit 1). Three mutation checks (disable unknown-field, trust every channel, skip digest recompute) each turned the suite red (2/2/1 failures) and were reverted. - GREEN:
node --experimental-strip-types --disable-warning=ExperimentalWarning --test tests/engineering-contract.test.mjs→ 18 pass, 0 fail.npm run build→ exit 0.npm test→ 966 pass, 0 fail (baseline 948 atee091ac).git diff --checkclean.
Independent review: two fresh-context, findings-only rounds. Round 1 on
aff4d98: 1 blocking (the operation API had no action creating the candidate snapshot thatrecord-proof/record-reviewbind to), plusunknown-fieldbypass via prototype names (in→Object.hasOwn), no authority rule for caller-spelledcollector, ambiguous consumed-nonce semantics, unvalidated receipt inputs, trusted channel acceptingauthenticated: none, and shapes downstream would have invented.143fb76closes all eleven (addscapture-candidate, adapter-setattested_by,checkCandidateFreshness,ChangeResults/StatusResult/CheckResult, transition tables, 24 h TTL cap, 15 fixtures). Round 2 on143fb76re-probed each item against live code: no blocking or should-fix findings; two doc nits fixed indcf964f.What the contract settles: IDs
<prefix>_<24 hex>; repo-relative path grammar; canonical JSON + sha256 with the exact digest inputs (snapshot{coverage, manifest, repository}, attempt inputs{brief_digest, plan_digest, references}, presentation);schema_version: 1only, unknown fields refused; every enum spelling; required records for acceptance; the ninecodecarto_changeactions with argument, result, and error shapes; the receipt path (core-issuedacr_request with single-use nonce → host-controlled channel → adapter-minted approval), its rejection table, and what each channel authenticates (a host session, never a person).Unresolved / limitations (honestly stated in the contract):
- Maintainer decision needed (contract § Decision record): is MCP elicitation — which trusts the connected client to have shown the form — an acceptable pilot channel? The SDK server side (
elicitInput, gated on the client'selicitation.formcapability) is verified; no client was verified. If no, E06/E07 stay blocked on MCP; Pi'shost-nativechannel is unaffected. - Same-user filesystem: an adapter-minted approval and a hand-written request+approval pair are byte-indistinguishable at rest. The Pi guard (unchanged, per EV-D03) allows agent writes under
.codecarto/, which includes.codecarto/engineering/; E08's host policy must deny that or treat at-rest records as cooperative. - On MCP every proof is caller-attested (the server never runs commands); the presentation always discloses this. Only the candidate snapshot is required to be adapter-captured.
Baseline follow-ups: #412 reproduces at this baseline with the injected
GIT_CONFIG_COUNTenv (recorded on #412; not touched here). This environment has noGIT_CONFIG_*set, so the counts above are unaffected. #395's Windows-CI policy unchanged.Next eligible: E09 (#407) is independent and open. E02 (#400) and E03 (#401) become eligible once #414 merges; both consume
core/engineering/index.tsas merged and coordinate their singlecore/index.tsedit. E06/E07 additionally wait on item 1 above. Suggest leaving this issue open until the maintainer records that decision — schema fixtures alone were not meant to close it.- RED: with no
- added a commit that references this issue
on Sep 18, 2026 Merged: #414 squashed onto
mainas65c9fa3316ab29d2c0149404d0c8a0d901d97d9d(on top of #413). Post-merge CI run 35309859616 completed successfully for that exact commit — Node 22, Node 24, and the Windows job all passed (Windows: 966 tests, 962 pass, 0 fail, 4 pre-existing POSIX-only skips).E02 (#400) and E03 (#401) are now eligible and consume
core/engineering/index.tsas merged. E06/E07 still wait on the MCP-elicitation channel decision recorded indocs/engineering/record-contract.md§ Decision record; that decision is what closes this issue.Sequencing correction (maintainer): merging #414 does not satisfy the E01 gate. E02 (#400) and E03 (#401) remain blocked until this issue is fully resolved, merged, and closed; my earlier "now eligible" line above is withdrawn. E09 (#407) is on explicit maintainer hold and is not authorized to start regardless of its dependency status.
The contract as merged is a candidate contract; E01 acceptance pending — the "frozen" wording in the docs and code comments is premature and will be corrected in a follow-up PR that also (a) separates caller-reported proof claims from observed evidence so a collector label alone cannot discharge an obligation, (b) keeps MCP elicitation a conditional proposal tied to a verified host/client integration check, and (c) separates approval-channel trust from at-rest record trust. That PR will not be merged and no dependent work will advance until the maintainer has reviewed its handoff.
Corrective candidate: PR #415, reviewed commit
92b034a0ec4b5915ea6f3c6ea61118d380bf933f(branchfix/e01-contract-review, frommain=65c9fa3). Not merged; awaiting maintainer review of this handoff. E02/E03 remain blocked until E01 is resolved, merged, and closed; E09 remains on explicit maintainer hold.Changed paths:
core/engineering/types.ts,core/engineering/validation.ts,docs/engineering/record-contract.md,docs/engineering/README.md,docs/engineering/implementation-plan.md,tests/engineering-contract.test.mjs,tests/fixtures/engineering/v1/(generator; +2 valid, +14 negative → 132 fixtures). Nothing under.codecarto/,mcp-server/,extensions/,core/synthesis.ts,core/status.ts, orcore/index.ts.What it does (per the four demands):
- Status — "frozen" replaced with "candidate contract; E01 acceptance pending" in the contract, README, plan, and code comments; README now states E02/E03 wait on closure and E09 is on hold.
- Claims vs observations — claim (
collector/result/…) / attestation (provenance.attested_by∈ adapter | host-tool-result | caller, adapter-set) / derived authority (proofAuthority) kept apart; a caller-reported record isclaimedwhatever label it carries (tested for every collector value);proofDischargesrefuses claims under the defaultverifiedpolicy; thehost-tool-resultpath is specified (host-side entry unreachable by the model's tools, config outside agent-writable roots, host payload supplies command/cwd/exit/tool_call_id; pairing rules enforced) without CodeCartographer executing anything;cooperativeis a separate operator-set policy, never request-selectable, labelled everywhere, not authorized for the pilot. - Conditional channel —
VERIFIED_ACCEPTANCE_INTEGRATIONS(empty) is the sole source of support;acceptanceChannelSupportedrefuses every pair not in it regardless of advertised capability; the integration check's required observations are documented; unverified →needs-human-acceptance; claim is session-attested. - Channel vs at-rest trust — approvals carry
assurance+storage.boundaryat mint, receipts carryclient;classifyAcceptanceisverifiedonly with a bound receipt, registry membership now,verifiedpolicy, and a reader-side host-enforced boundary whoseenforced_sinceprecedesdecided_at; a forged record cannot be laundered by enabling a boundary later; a record cannot label itselfverifiedoverboundary: none; same-user caveat kept, not used to satisfy the stronger requirement; no credential system; Pi guards untouched.
Verification on
92b034a(Linux, Node v22.22.3): targeted 21 pass / 0 fail; mutation (proofAuthoritytrusting the label) → 2 failures, reverted;npm run buildexit 0;npm test969 pass / 0 fail;git diff --checkclean; staged content scanned for credentials/home paths — none.Independent review: Round A on
b787ca1(findings-only): demands 1 and 3 met, 2 and 4 partial — eleven concrete findings (model-reachablehost-tool-result, boundary-toggle laundering, self-reportedverified_integration, unenforced pairing rules, table drift, wording). All folded into92b034aexcept the maintainer questions below. Round B on92b034a: all nine tightenings verified closed by probe; no findings; confirmed nothing classifiesverifiedagainst the contract's own empty registry.Decisions that remain yours (contract § Decision record) — none are implied by this PR:
- D1 MCP elicitation on a specific verified host/client pair — acceptable pilot channel? Which pair is integration-checked first, by whom?
- D2 Pi
host-nativeon the same terms? - D3 Storage-boundary mechanism for the pilot host; where the boundary declaration and
enforced_sincelive so agent tools cannot write them; must the boundary predate the namespace's first write? - D4 Observed-evidence path: must E05's ingestion entry be demonstrably unreachable by the model's tools, and its hook configuration outside agent-writable roots, to count as
host-tool-result? - D5 Is
cooperativepermitted at all for the pilot; under it, do honestagent-claimedrecords discharge equally with relabelled ones?
Until D1–D4 have answers, no acceptance can be
verifiedon any host, and E01 stays open by design. I will not merge #415 or advance dependent work.CI on #415 at
92b034a: test (22), test (24), test-windows, CodeQL, and both Analyze jobs all pass. PR remains unmerged pending your review.Counterexample addressed. PR #415 now at
5a8ade3fafa097958da5168a4eff73f56ffe4ed7(commits9f0ba64fix,5a8ade3consistency). Still not merged; #399 stays open; E02/E03 blocked; E09 on hold.Reproduced first: against
92b034a, a request/approval pair forged underboundary: nonewithissued_at/responded_at/decided_atchosen to post-date a later protection epoch, plus a supported-integration fixture, returned{"class":"verified"}. The epoch comparison readdecided_atfrom the writable record.Fix, within the constraints: no additional timestamp or flag inside the record set is trusted.
CurrentStorage(the reader's out-of-namespace context) gainsprotection∈continuous-since-initialization|enabled-after-initialization|interrupted|imported-history. Only the first makes any record eligible forverified; the others and an omitted/unknown value classifycooperative. Nothing in any record or request can carry it (unknown-fieldon all nine actions; no record shape has it). Thedecided_at/enforced_sincecomparison is demoted to a consistency check that can only lower trust.Policy stated (contract § Protection before authoritative initialization): the boundary must be enforced before the namespace's first write and never lapse; existing unprotected records never become
verifiedbecause protection is enabled later; an interruption is permanent for that namespace; imported/restored/hand-edited history is untrusted; the way to an authoritative namespace is a fresh one initialized under protection. The remaining mechanism — where the initialization marker,enforced_since, and lapse history live so agent tools cannot write them; how a lapse is detected; how a fresh namespace is initialized — is recorded under D3 as a host mechanism, not solved by the epoch check.D4 as requirements:
HostCapabilities.tool_result_path∈protected|unprotected|none;attestationForHostObservationyieldshost-tool-resultonly forprotected(entry unreachable by the model's tools and configuration outside agent-writable roots), otherwisecaller. The same protection-history rule is stated to govern proof files, as an E06 requirement.Registry still empty;
cooperativestill unauthorized for the pilot; no E02/E06/E07 code.Verification on
5a8ade3(Linux, Node v22.22.3): focused regression — the exact counterexample under each protection history and under omitted/invalid values — 23 targeted pass / 0 fail (RED against the previous logic: 2 failures with the protection check disabled);npm run buildexit 0;npm test971 pass / 0 fail;git diff --checkand hygiene scan clean.Independent review of this exact counterexample (on
9f0ba64): reproduced it against the old validator across everyprotectionvalue (allverifiedbefore; all butcontinuous-since-initializationcooperativeafter, includingundefined/null/"bogus"); confirmed the fix trusts nothing inside the record set, that the epoch comparison is now trust-lowering only, that D3/D4 are recorded as required, and that no E02/E06/E07 logic exists. Verdict: counterexample closed within the constraints. Five consistency items (theACCEPTANCE_REQUIRED_RECORDStext and the doc's acceptance table still described the pre-fix rule; the proof-file corollary was prose-only; two wording/typing nits) are folded into5a8ade3.What the code proves vs what the host must enforce (contract § of that name):
- Code proves: no request can carry a decision, attestation, policy, capability, boundary, protection history, or tool-result path; a caller-reported proof is
claimedunder every label; the registry is empty and refuses every pair;classifyAcceptanceisverifiedonly with a bound receipt, current registry membership, theverifiedpolicy, andcontinuous-since-initialization— the forged pre-dated pair, enabled-later, interrupted, imported, and omitted histories all classifycooperative; a record cannot self-labelverifiedovernone/false; presentations disclose claimed proofs, weak candidates, the cooperative policy, and an unprotected boundary. - Host must enforce (outside the validators' reach): supplying those values from configuration the model cannot write; actually denying agent writes to the namespace; initializing it only after protection and recording the marker/lapse history where agent tools cannot write; reporting
protectiontruthfully; aprotectedtool-result path; running the integration check; showing the presentation unaltered; and (E06) applying the protection history to proof files.
Remaining decisions (unchanged, D1–D5), with D3 now carrying the protection-history mechanism and D4 carrying the demonstration of unreachability. Awaiting your review; I will not merge, close, or advance dependent work.
- Code proves: no request can carry a decision, attestation, policy, capability, boundary, protection history, or tool-result path; a caller-reported proof is
CI on #415 at
5a8ade3: test (22), test (24), test-windows, CodeQL, both Analyze jobs pass. Unmerged, awaiting review.- added a commit that references this issue
on Sep 18, 2026 Merged: #415 squashed onto
mainas54a76dc852dd85b65cf560d52e80fa61853d2038after confirming exact-head checks green at5a8ade3. Post-merge CI run 35315528306 completed successfully for that commit: test (22), test (24), test-windows all success.Scope of the approval (maintainer): the corrective candidate code only. This is not closure of E01 and not a claim that any host integration is verified. #399 stays open; E02/E03 remain blocked; E09 remains on hold;
VERIFIED_ACCEPTANCE_INTEGRATIONSremains empty;cooperativeremains unauthorized for the pilot.Next (maintainer direction): no further expansion of the abstract contract. A bounded feasibility spike against ONE concrete host/client configuration (MCP elicitation as the candidate channel, session-attested), using disposable fixtures, changing no global host configuration, with experimental code kept out of production and no automatic registry entry or issue closure. Results will be reported here.
Feasibility spike — one host, one client. PR #416 at
b36828fd5dedd6089f47ee0e5475852b215ee8f1(reportdocs/engineering/spike-claude-code-2026-09-18.md, fixturesspikes/claude-code-2.1.263/). Not merged. Registers nothing; registry empty;cooperativeunauthorized; no E02/E05/E06/E07 code; #399 open; E02/E03 blocked; E09 on hold.Host inspected: Claude Code 2.1.263, Linux (Fedora, kernel 7.2.4), bubblewrap 0.12.0 + socat present, unprivileged userns OK. MCP client info
{ name: "claude-code", version: "2.1.263" }. All runs headless in a disposable directory via--settings <file>/--mcp-config … --strict-mcp-config; no global configuration touched; no external provider spend (seven short Sonnet runs on the existing subscription).Mechanisms tested and actual results:
- Elicitation — client declares
elicitation.formeven headless; headlesselicitInputreturned{ action: "cancel" }(~3 s), nothing minted. Interactive dialog exists (bundle strings). AnElicitationhook can answer for the user (bundle hook-output schema) — not exercised; hook configuration is therefore part of the boundary. Live human interaction was not performed; the exact steps (accept, reject, cancel/Esc, stale; mismatch is protocol-level) are in the report. - Namespace —
permissions.denyonWrite/Editalone was bypassed bypython3 -c "open(…).write()"(file on disk). Withsandbox.enabled+filesystem.denyWrite, redirect/python/cp/mkdir all failedEROFSwhile a write one directory up succeeded — OS-enforced. Config files (.claudesettings,.claude/hooks,.mcp.json,~/.claude) are sandbox-protected with no exemption and permission-layer protected forWrite/Edit— except inbypassPermissions. No host mechanism attests protection continuity: every acceptance on this host classifiescooperativeunder the contract until D3 settles a mechanism. - Tool results —
PostToolUseandPostToolUseFailurehooks run outside the sandbox and deliveredtool_use_id,tool_input.command,tool_response.{stdout,stderr}/error: "Exit code N\n…"into thedenyWriteinbox; the model's Bash could neither write the inbox nor make the hook script write (EROFS). Limitations: failures firePostToolUseFailure(notPostToolUse); exit code arrives as text; success carries no exit-code field. - Honest unsupported — headless →
cancel; unregistered pair →needs-human-acceptance; missing sandbox deps → hard failure withfailIfUnavailable: true(docs, not exercised); unprotected hook path →caller; unknown history →cooperative.
Smallest feasible pilot configuration (proposal): user-level or managed settings only, never
bypassPermissions: sandbox on (allowUnsandboxedCommands: false,failIfUnavailable: true,denyWriteon the namespace),Write/Editdeny rules,PostToolUse+PostToolUseFailurehooks (matcherBash) into a host-side ingestion entry writing to the protected inbox, noElicitationhooks, interactive sessions for acceptance. Yields per sessionstorage_boundary: host-enforced,tool_result_path: protected,human_acceptance: mcp-elicitation(unverified); does not yieldprotection: continuous-since-initialization.Decisions this leaves with you:
- D1 — run the live check (steps in the report) at an interactive session; I did not simulate it.
- D3 — accept or reject the candidate continuity mechanism (operator enables the configuration before initialization; adapter writes a hook-delivered init marker into the protected namespace; a
SessionStarthook in the same protected source re-checks each session; any gap = permanentinterrupted). Without it,verifiedis unreachable on this host. - D4 — accept "user/managed settings + no
bypassPermissions" as the definition of protected configuration. - D5 — untouched.
Not covered: macOS/WSL2 (docs only), Windows native, other MCP clients, the desktop app's dialog. Awaiting your review of #416; nothing advances.
- Elicitation — client declares
- added a commit that references this issue
on Sep 18, 2026 Spike revision — E5 recorded. PR #416 now at
6b34c17f2e570f1a579be9d3279abf3eb8e36b0f(not merged). The first pilot configuration left the ingestion hook script writable from the sandbox (maintainer finding); it is withdrawn. Corrected configuration puts the hook directory and the settings file indenyWriteexplicitly with matching deny rules, and the hook is hardened (absolute interpreter, fixedPATH, absolute command paths, nothing sourced).E5 results (observed): append/replace/create/chmod/symlink on the hook →
Read-only file system; rename its directory →Device or resource busy; overwrite the settings file → read-only;/usr/bin,/usr/bin/sh, and everyPATHdirectory → refused; script and settings hashes unchanged; hook fired for all 13 calls. One finding: the hook directory's parent was writable (it sits under Claude Code's per-user temp root, which the sandbox allows) — so a hook is protected only where an explicitdenyWriteor a host-protected location covers it, never by being "outside the working directory".Claims now:
tool_result_path: protectedonly for the E5 configuration;storage_boundary: host-enforcedper session;protection: continuous-since-initializationstill not provided. D3 candidate reworked so the initialization marker is written only by a script that is itself protected; whether aSessionStarthook can see enough of the effective settings to re-check per session is an E02 investigation. D1 live check still owed. Registry empty; cooperative unauthorized; E02/E03 blocked; E09 on hold.- added a commit that references this issue
on Sep 18, 2026 Revision: E6 (every path the unsandboxed hook writes)
Second finding of the same class: the E5 hook still wrote
hook-err.logandhook-status.loginto the parent directory E5 item 7 proved writable; the hook runs outside the sandbox, so a planted symlink turned its own redirect into a write primitive. The E5 hook is withdrawn.E6a — the primitive, against the E5 hook (separate disposable root):
Planted symlink Observed hook-err.log → hooks/observe.shon the next Bash call the hook truncated its own script to 0 bytes — ingestion denial of service hook-status.log → <namespace>/inbox/forged.json(alone)inbox/forged.jsoncreated inside the evidence namespace with threehook-wrote-0lines — attacker-chosen path, written by the trusted processCorrected hook: every write (payload, stderr, status) inside the namespace; one fresh uniquely named file per event;
set -Cso each>isO_CREAT|O_EXCLand refuses any symlink, dangling or not (proved locally under this host's/usr/bin/sh); no>>anywhere; absolute interpreter, fixedPATH, absolute commands, nothing sourced.E6b — against the corrected hook (observed): symlinks at
inbox/planted.json,hook-logs,hook-logs/planted.err→Read-only file system; symlinks at the old log paths planted (parent writable) but never opened — targets untouched; a symlink from the writable cwd into the namespace is refused on the resolved path (confirmed directly withbwrap --ro-bind); script hash intact; 10 inbox files + 20 log files, all hook-written and uniquely named; zero symlinks inside the namespace.Requirement, generalized (report § requirement 3, § D4, pilot configuration, E05 note):
tool_result_path: protectedrequires that every path the unsandboxed hook reads, executes, and writes is protected — the script, its directory, its interpreter, everyPATHdirectory, the settings naming it, and every file it writes: payload, error log, status log, temp and lock files — written as fresh uniquely named files withO_CREAT|O_EXCL(orO_NOFOLLOWfixed paths), never through a symlink it did not create. The real E05 ingestion entry has the same exposure for its own error/status output and is bound by the same list.Head:
50b45b3.npm run buildexit 0;npm test971/971;npm pack --dry-runshows zerospikes//docs/entries;git diff --checkclean; no private paths. Registry empty;cooperativeunauthorized; no E02/E05/E06/E07 code; #399 open; E02/E03 blocked; E09 on hold. Not merged.- added a commit that references this issue
on Sep 18, 2026 Merged: #416 squashed onto
mainasddb4f36f01bdf902a93d7c154fa033465a242cd0after confirming exact-head checks green at50b45b3. Post-merge CI run 35320767000 completed successfully for that commit: test (22), test (24), test-windows all success. The spike registers nothing;VERIFIED_ACCEPTANCE_INTEGRATIONSstays empty.Maintainer decisions recorded (to be written into the contract's decision record by a follow-up PR): D1 conditional yes — MCP elicitation on the named pair claude-code/2.1.263 + codecarto MCP server, registry empty until the maintainer runs the live check; operator attests no Elicitation/ElicitationResult hooks, adapter cannot verify this, goes in the receipt disclosure. D2 deferred. D3 boundary accepted (E5/E6 configuration, session-level); continuity NOT provided; SessionStart marker is an open E02 investigation. D4 accepted with the full E5/E6 requirement list; re-run E5/E6 wherever the real ingestion entry is placed. D5 no — cooperative stays unauthorized. Consequence: the pilot runs with observed proofs and acceptances that classify
cooperative; E06 surfaces that classification rather than refusing. #399 stays open pending the D1 live check; E02/E03 blocked; E09 on hold.Decision record PR: #417 at
5ab5612bfdd45a5501c5b7a321f6529fa8762dfd(docs only, not merged) writes D1–D5 intorecord-contract.md§ Decision record with the stated consequence (observed proofs, cooperative acceptances; E06 surfaces the classification) and the three forward notes (inbox-only record parsing, hook-write retention, per-host O_EXCL proof). Stopping here: #399 stays open for the D1 live check; E02/E03 blocked; E09 on hold; registry empty; no E02/E05/E06/E07 code.- added a commit that references this issue
on Sep 18, 2026 D1 live check recorded. Log verified on this machine (5 lines,
sha256 cc4bb6651fae…; the 2.1.277 binary landed at 21:28:47Z, one minute before the firstinitialize). PR #417 now at955e086298919fce9ca526f02dc05c5e936fb196and is no longer docs-only: it adds the version-match check (client_versionexact; live mismatch →needs-human-acceptance),elicitationDecision(decision fromcontent.decisiononly;actionnever authorizes; missing/unknown →invalid),timed-outas its own outcome withacceptanceTtlWithinagainst the client's observed timeout, ten elicitation fixtures (the three live shapes verbatim), and a contract test for each. 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;VERIFIED_ACCEPTANCE_INTEGRATIONSstays empty until the entry is added by amendment with the evidence. E5/E6b re-run on 2.1.277: identical results. D2–D5 unchanged. 972 tests green. Not merged; per your direction #399 can close once #417 lands, after which E02/E03 unblock and E09 stays on hold.E01 closed
#417merged asb868fd9; post-merge CI green on that exact commit (Node 22, Node 24, Windows, CodeQL ×2).The D1 live check was performed by the maintainer at an interactive Claude Code session, and its findings are in the contract. The client identified as 2.1.277, not the 2.1.263 the spike was titled for — the host auto-updated three minutes before the first elicitation. All four required observations were obtained, screen paired against the server log:
observed presentation message shown verbatim with a required Decisionfieldaccept action: accept,content.decision: acceptreject action: accept,content.decision: rejectcancel action: decline, no contenttimeout MCP error -32001after ~2.5 min, nothing mintedThree findings came out of that run and are now enforced in code, not documented as advice: exact-version matching (a self-updating client silently staleing a registry entry),
timed-outas an outcome distinct fromdeclined, and the decision deriving only fromcontent.decision— line 4 above is a real rejection that an adapter readingactionwould have minted as an approval.Reviewing those changes then surfaced a recurring defect class: a trust rule expressed as a helper that nothing calls. Four instances closed:
acceptanceTtlWithinwas dead code; the shipped fixture paired a 3600 s window with the 150 s client timeout it must fit inside and still readverified. Now enforced inclassifyAcceptance.buildAcceptanceRequestcomposes an honest presentation, but nothing checked one. A request minted another way could show emptylimitationsover a caller-reported proof and readverified. Now re-derived and enforced.- The first disclosure fix then replaced "trust the adapter" with "trust the reader" — omitting
proofs/reviewsdefaulted them to[]and reopened the same bypass. They are now required for averifiedreading; omitting them degrades tocooperativewith an explicit reason. - Duplicate registry tuples let array order pick the timeout, and
client_request_timeout_mswas never type-checked ("999999999",Infinity,[999999999]all coerced past the comparison). Both closed.
Each has a mutation check that turns the suite red when the enforcement is removed. 973/973 tests pass.
Decisions recorded (D1–D5): D1 conditional yes for
claude-code/2.1.277+ the codecarto MCP server, pending registration; D2 Pihost-nativedeferred; D3 boundary accepted, continuity not provided by this host; D4 accepted with the E5/E6 requirement list; D5cooperativeunauthorized.The honest consequence: with D3 unsolved and D5 no, the pilot runs with genuinely observed proofs and acceptances that classify
cooperative.VERIFIED_ACCEPTANCE_INTEGRATIONSstays empty. That is the contract working as designed, not a gap — E06 must surface the classification rather than refuse to proceed.Known limitation, documented not enforced: the disclosure check compares lines by exact equality, so a presentation carrying every required line plus contradicting text passes. Reviewing presentation copy is an operator responsibility.
E02 (#400) and E03 (#401) are now unblocked. E09 (#407) remains on maintainer hold.
Context and scope
Part of the incremental CodeCartographer engineering evolution. Planned, not shipped. Read the agent handoff, vision/decisions, record contract, and implementation plan. Documentation baseline PR #410 is merged at
bbdf6b1a8b3bc348aa9e8f20a409c66df087af13. Follow this issue's prerequisite gates before starting.Host-executed/framework-tracked; preserve analysis state ABI, both Pi analysis guards, and current synthesis confirmation gates. Ordinary in-place changes accept zero external references. No implicit source execution, provider spend, GitHub write, release/deployment, or private-data publication. E01 owns schema/API/authority decisions; dependent workers consume its merged contract rather than inventing another.
Tracking: #398
Blocked by: None; ready after the documentation PR is merged.
Objective: give downstream agents one reviewed, executable contract, including an honest host-approval boundary.
Depends on: none. Read:
docs/engineering/record-contract.md,core/types.ts,core/utils.ts,core/status.ts,core/secrets.ts,core/synthesis.ts, existing guard and closure tests.Files: create
core/engineering/types.ts,core/engineering/validation.ts,core/engineering/index.ts,tests/engineering-contract.test.mjs,tests/fixtures/engineering/v1/; updatedocs/engineering/record-contract.mdand the minimalcore/index.tsexport when needed.Steps:
Acceptance: a downstream implementer can consume the merged schemas and public operation signatures without inventing fields; malformed data is rejected deterministically; no
approve: trueagent payload becomes human acceptance. If the trusted receipt design remains unresolved, keep E01 open and explicitly block E06/E07. Schema fixtures alone do not close the issue.Out of scope: storage, command execution, MCP registration, UI, auto-approval, or a custom credential system.
Required verification and handoff
npm run build,npm test, andgit diff --check; record actual results, not historical counts.