@junghanacs/entwurf 0.30.0 → 0.30.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,123 @@ All notable changes to this project will be documented here. Format follows [Kee
4
4
 
5
5
  ## Unreleased
6
6
 
7
+ ## 0.30.1 - 2026-10-04
8
+
9
+ ### Fixed
10
+
11
+ - **Claude incomplete foreground-tool failures no longer invite a whole-prompt replay (#127).**
12
+ Only a non-aborted prompt rejection decoded as the root ACP SDK's `RequestError`, code
13
+ `-32603`, vendor `errorKind: incomplete_tool_call`, receives an authored unsafe-to-replay
14
+ verdict. Some tools may already have run. The original rejection and stderr/lifecycle/launch
15
+ evidence stay in existing diagnostics rather than tainting pi's retry/overflow classifier.
16
+ FIRST failure, abort precedence, exactly one error seal, and new/reuse session drop remain;
17
+ other failures and Cortex retain their legacy paths. This is not a global retry disable or
18
+ a claim about vendor-internal retries or real incomplete-tool frequency.
19
+ - **Herdr's existing plugin/runtime consumption boundary is aligned (#126).** The follow-up to
20
+ 0.30.0 aligned the plugin manifest with its locked runtime, re-pinned the published npm bytes,
21
+ and measured Herdr 0.9.3 supply against the unchanged 0.9.0 admission floor. Sandbox path
22
+ normalization and the Pi-native callback oracle were corrected. Operating Herdr/config stayed
23
+ unchanged; no permanent plugin/package equality policy was introduced.
24
+ - **Herdr build verification covers both carriers and the real default lock reader.** Explicit
25
+ npm and checkout fixtures assert exact sequential acquisition counts and final artifact identity.
26
+ The uninjected default reader is checked against an independent committed JSON oracle, with a
27
+ dedicated wrong-directory mutant. This changes proof, not the production build/runtime/activation.
28
+ - **Published support, source observations and execution evidence stay distinct (#127).** The
29
+ ACP rail and dependency ledger separate conditional settings, capability-gated and ambient
30
+ changes from runtime certification, stock auth messages from sealed legacy stderr tails,
31
+ upstream comments from unread SDK internals, and native/platform/loader contexts. The typed
32
+ rejection row points only to its deterministic gate: normal raw/reuse LIVE turns do not induce
33
+ that rejection.
34
+
35
+ ### Changed
36
+
37
+ - **Claude ACP closure:** adapter `0.79.0` → `0.85.1`, shared direct/child ACP wire SDK `1.4.0` →
38
+ `1.6.0`, Agent SDK `0.3.274` → `0.3.286`; the existing shared-wire contract is retained.
39
+ New reachable upstream behaviour is classified in the ACP rail and ROADMAP ledger, not
40
+ silently adopted as new Entwurf policy. Pi 1.0's range and public bridge verbs are unchanged.
41
+ - **Unpublished Herdr verification candidate:** plugin metadata is 0.30.1 and uses the closed `herdr-checkout`
42
+ verification carrier while 0.30.1 is unpublished. No npm integrity is fabricated. After
43
+ publication, re-pin only the measured registry artifact and re-prove that acquisition source;
44
+ switching an installed source still requires explicit deactivate/activate.
45
+
46
+ ### Verification
47
+
48
+ - Implementation/source review, related MUST LIVE and pre-version landing receipts are in
49
+ [#127](https://github.com/junghan0611/entwurf/issues/127), with #126's Herdr acceptance separate.
50
+ Main landing `f24820c` passed required four CI jobs, full411s and qualification841/841.
51
+ These are pre-version receipts, not this release's exact-SHA acceptance.
52
+ - The gate-only amendment landed separately at `61dbef7`: independent review closed with
53
+ zero Blockers/Defects; the frozen 0.30.0 source passed local qualification **842/842** and
54
+ full645s. Its [exact-SHA CI](https://github.com/junghan0611/entwurf/actions/runs/37181269379)
55
+ passed all four required jobs, full361s and qualification842/842. These are pre-version
56
+ implementation receipts, not 0.30.1 release acceptance. The original npm-only/version-pinned
57
+ HPB failure, old wrong-directory mutant survival, inventory RED, PARTIAL13/13 and PARTIAL14/14
58
+ receipts remain separate history; offline four positive/two negative controls are not registry
59
+ proof (`~/tmp/e0301-prepare/gate-fix/implementation-floor/`).
60
+ - **Fresh 0.30.1 working-candidate floor:** P4 full641s, followed by
61
+ `LIVE=1 ./run.sh release-gate /tmp/entwurf-release-gate-0.30.1.SLE9uA --cut`:
62
+ **MUST PASS=24 FAIL=0 SKIP=0**, **BEHAVIOR PASS=1 FAIL=0 SKIP=0**, exit0/cutOK;
63
+ its own full645s and qualification **842/842** passed. The versioned candidate on
64
+ `61dbef7` stayed frozen through both runs (seal
65
+ `541cc5a368315e6ecac4548a3492ce73024c6fe9dc5613077e14898d4fd76879`).
66
+ Complete log: `/tmp/entwurf-release-gate-0.30.1.SLE9uA/release-gate.log`, SHA256
67
+ `ea070c2ea019c05766e40e3442d1a31d68894e6cb3da7f7bbb80460c782bc020`.
68
+ - **Native Codex caller-seat acceptance:** 65 assertions, native `gpt-5.6-luna` and
69
+ Pi `openai-codex/gpt-6-luna`; personally launched app-server PID2143836 in session
70
+ A `$309` and initial Pi/fresh Codex/outbound Pi all in S `$316`, windows
71
+ `@810/@811/@812`. The outbound receipt names `codex-title-anchor`; both exact
72
+ source-role audits completed3/3. The complete pre-cleanup evidence is retained at
73
+ `.probe-artifacts/codex-fresh-live-fypXrI/`; decoded decisive receipts accompany
74
+ `~/tmp/e0301-prepare/fresh-prepare/p5-accepted.json`.
75
+ - **On-demand long-turn:** Sonnet5 completed the original three sequential240s
76
+ foreground waits in736147ms, with exactly one cold ACP bootstrap and no reported
77
+ retry/timeout; the same candidate seal remained MATCH. This normal-turn receipt does
78
+ not induce `incomplete_tool_call` or certify vendor-internal retry frequency.
79
+ - **Immediate P9:** census113 unused roots, four owned/aged/unused roots444K and one
80
+ reparented GLG Emacs daemon. All four roots were archived before four-fence-checked
81
+ reclamation; no process was killed. Post-census reports110 unused/zero reclaimable
82
+ roots, with Emacs untouched. Archive SHA256
83
+ `3132ab92d26702a5b1e3ddf9298e7e77c6abe63c276625ac2cd93b12558ad19a`;
84
+ per-root fences and before/after observations are in
85
+ `~/tmp/e0301-prepare/fresh-prepare/p9-reclaim/receipt.json`.
86
+ - **Claude checkout-provenance certification of the candidate tree:** the local
87
+ owner-managed installer was refreshed from this working0.30.1 checkout, then a NEW
88
+ native Claude2.1.289 session was opened without sending a prompt. Install finished
89
+ `2026-10-04T10:20:16.328Z`; fresh garden `20261004T192019-c50e8a` was created at
90
+ `10:20:19.625Z`. Exact launched PID3397500/startKey `linux:284430975` joins both
91
+ markers, its unique V3 record and registered bridge child3397563. Registry-authoritative
92
+ cached plugin0.1.0 and stable XDG assembly match the checkout's hook/writer/session-id/
93
+ registry bytes; plugin0.1.0 is not the root package version. Strict doctor exit0:
94
+ three live owner joins, source=assembled=installed writer `8558bb3a5aa8`, registry
95
+ `277c8dc2a183`, store v1=0/v2=0/v3=1476, PASS. The targeted leaf completed27s;
96
+ **full/BODY/P5 reruns=0**, leaf seal
97
+ `1f754eeb12ff800b2d7c1122d2b8fad030c9311e1784aab67166916ad3eb5364` MATCH.
98
+ Receipt `~/tmp/e0301-prepare/fresh-prepare/criterion4.json` SHA256
99
+ `0af3edc3d913eae2157d8229f7408c670f47b8bdd6363ff8e1f1c8a51e2b1fe6`;
100
+ doctor log SHA256 `6922043ddb500c34edaba5f70115d910cbdbfe7a1d01f8ff9ab28f2b3cf9a83f`.
101
+ This satisfies the pre-commit fresh-native criterion in dev-clone mode, not emitted-JS
102
+ package/bin-shim, final-artifact, registry, model-turn or fresh delivery/reply acceptance.
103
+ - **Explicit one-cut agy exception, authorized by GLG (2026-10-04):**
104
+ “명시 예외 승인 npm publish 전까지 쭉 가줘” authorizes this0.30.1 cut to proceed
105
+ without criterion7 acceptance. **agy native-push is NOT CERTIFIED on the1.2 line**:
106
+ measured on oracle/agy1.2.16, the answering LS port requires a CSRF nonce the external
107
+ sender does not legitimately hold; authenticated current host/CID join and same-gid
108
+ round-trip are absent. HTTP200/grpc16 `missing CSRF token` is not successful delivery.
109
+ The requirement's introduction date is unknown. The checked adapter/imprint/raw-send/
110
+ LIVE/pin subjects are unchanged from0.30.0; last native acceptance remains1.1.0
111
+ (2026-07-14), `PIN_AGY_MINOR=1.1` and DELIVERY's certification row are unchanged.
112
+ Follow-up [#128](https://github.com/junghan0611/entwurf/issues/128) is prerequisite
113
+ research: own tool-shell key presence/current CID/garden first, no nonce extraction,
114
+ discovery/cache/manager or unapproved implementation. This exception does not label
115
+ agy green, modify the permanent floor, or authorize npm publication.
116
+ - **No duplicate local floor:** GLG requested retaining the existing green evidence
117
+ and resolving only the two native leaves. P4/full, qualification BODY, P5 and duplicate
118
+ local M0/full are not restarted for the post-verdict evidence/policy prose. Exact
119
+ prepared-SHA CI (four required jobs plus its qualification BODY) and one preserved
120
+ final-artifact consumer acceptance remain separate mandatory make evidence. The
121
+ working-candidate receipts above do not certify those later axes or registry bytes;
122
+ verification prose added after each frozen verdict is not the bytes that run consumed.
123
+
7
124
  ## 0.30.0 - 2026-10-02
8
125
 
9
126
  ### Changed
package/README.md CHANGED
@@ -184,7 +184,12 @@ compiled bridge after record birth, under the citizen's identity, and Pi supervi
184
184
  that MCP child. **MCP is ingress; pi receive remains the record-addressed Unix
185
185
  control socket.** Garden identity and dispatch stay Entwurf's. What `pi-durable`
186
186
  or Pi codemode should mean here remains undecided: this cut neither adopts nor
187
- certifies them. Herdr 0.9.3 is a later lane, not a companion upgrade.
187
+ certifies them. The 0.30.0 follow-up separately measured Herdr supply 0.9.3 and
188
+ re-pinned the plugin runtime to published npm 0.30.0 with manifest 0.30.0; its
189
+ public consumer and CI receipts live in the #126 thread. That dated alignment is
190
+ not a permanent version-equality rule. Unpublished 0.30.1 uses the closed
191
+ `herdr-checkout` verification carrier; the [plugin source contract](./plugins/herdr/README.md)
192
+ owns the measured npm re-pin and source-specific proof owed after publication.
188
193
 
189
194
  Entwurf is the 0.12+ successor to
190
195
  [`@junghanacs/pi-shell-acp`](https://www.npmjs.com/package/@junghanacs/pi-shell-acp):
package/VERIFY.md CHANGED
@@ -96,7 +96,7 @@ that moved out of `scripts/` and into a file beside its subject would otherwise
96
96
 
97
97
  > **The herdr rail is an on-demand axis until it has run.** `smoke-herdr-fresh-call-live` opens a real caller inside a real herdr pane, has it invoke the PUBLIC `entwurf_fresh_call`, and reads the child's nonce callback — the one axis no deterministic gate reaches, since the rail is selected from the caller's own process environment. **The release aggregate does not yet require the herdr rail**: herdr is optional (a cut on a host without it must not block), and first evidence precedes promotion — a rail whose first acceptance run is days old has not earned a place in the gate that blocks every cut. Run it directly with `LIVE=1 ./run.sh smoke-herdr-fresh-call-live` whenever a cut changes herdr rail code, and promote it to a MUST only after the axis has a run history to point at. Without `LIVE=1`, or without herdr/pi/claude on PATH, it reports protocol SKIP — never a pass.
98
98
 
99
- > **The plugin's FIRST USER PATH is its own on-demand axis, and an npm acquisition receipt holds only for the ref it ran on (#118 홉 1).** Every other herdr gate proves the plugin against a source this repository controls: `check-herdr-plugin-build` drives a stub, and `smoke-herdr-plugin-build-live` redirects the product remote to a local bare clone with git `insteadOf` and asserts `identity.kind === "herdr-checkout"`. npm has been the production authority class since `dd84ac0`, though the lock committed in this checkout is the `herdr-checkout` verification carrier set for the 0.25.1 candidate window on 2026-09-30 (`8b92e09`). The re-proof this same document demands above (§ source switch) has one dated npm acquisition row — 2026-09-23, public `main` = `113ca53`, plugin 0.5.0, `kind=npm @junghanacs/entwurf@0.25.0`, PASS (CHANGELOG 0.25.1, "Public-remote first-user path") — and no run against this checkout's carrier or the current candidate. `LIVE=1 ./run.sh smoke-herdr-raw-install-live [--ref <REF>]` fills that row in a clean `node:24` container with no socket, config, cache or repo mounted, no `insteadOf`, the public remote spelled as a user would type it, and pi / herdr / Claude Code installed in the IMAGE BUILD as scaffolding (Hard Rule 17 — the product face begins at `herdr integration install pi`). herdr itself comes from `scripts/install-herdr-ci.sh` against `scripts/fixtures/herdr-supply.json`, so no version or digest lives here. `--ref` defaults to `main` rather than a pinned SHA because receipt (a) is defined as what a user gets today; the resolved commit is printed, so the receipt stays exact. It takes a REMOTE REF and not an arbitrary commit — measured on herdr 0.9.1, `--ref <sha>` fails as `couldn't find remote ref`, because the checkout fetches by name — so a candidate is addressed by its branch and pinned by the resolved commit the gate prints. **Its first question is a measurement, not an assumption:** herdr registers a plugin through a running server and falls back to `persist_plugin_offline` only on a connection error (`src/cli/plugin.rs:914-981,1016-1021` @ `c77af189`, 0.9.0 source), and a container has no server. The 2026-09-23 PASS installed with no server on the 0.9.1 binary `113ca53`'s `herdr-supply.json` pins, so that install path worked on 0.9.1 — inferred from the PASS, not a direct observation of the fallback branch; a later "no" on another herdr is #118 investigation output rather than a completion. `--yes` is always passed so a non-interactive exit 2 (`:189`) can never be misread as a missing server. **At a ref whose lock names `herdr-checkout` the acquisition axis reports a NAMED SKIP rather than a red, and the rest of the journey still runs.** That lock is the candidate carrier a cut legitimately sits on — 0.23.0 was tagged that way and `dd84ac0` pinned npm afterwards — so a red there would report a broken install when what is true is that the ref does not lock npm. What is asserted at BOTH lock kinds is that the runtime came from the source the ref locks, and the package name is read from the checkout manifest rather than the lock, because a `herdr-checkout` lock carries no `name` (`herdr-runtime.mjs:317-328`) and joining `undefined` throws instead of failing an assertion. **Cells [6]–[9] carry it past the install into the USE path (#118 H1-4/H1-5).** An install is not a use, so the same container then runs what those bytes became: the compiled bridge entry, the three `REQUIRED_BINS` as executables, and a real `entwurf check-bridge` printing its exact eight-verb set — the installed-runtime row below, measured rather than inferred from the bootstrap's refusal to install without them. Then the pi that this plugin WIRED is started with no `-e` and no `--no-extensions`, because the subject is the wiring and not this checkout: one `get_state` in `--mode rpc` costs zero tokens and needs no provider argument (a bogus `--provider` exits 1 before session_start; omitting the pair lets pi resolve the default the extension itself registers), and while that session is alive the cell reads its V3 record, its gardenId-keyed control socket, and the installed `entwurf peer-facts` reporting it `alive`. The plugin's own status fan is then driven with NEITHER side stubbed, in both states a user can be in: with no herdr server it must name `herdr-agent-list-failed` and go red rather than draw an empty table, and under a headless `herdr server` — scaffolding for that second read only, stopped again before the teardown — it exits 0 and COUNTS the citizen as `unobserved` instead of dropping it. Last, the shipped `entwurf herdr-plugin-deactivate` runs from the installed bin, deletes the runtime it is executing from, and the plugin reinstalls onto the host it fully left — the deactivation row, on the npm source. **What these cells still do not close:** swap / torn-swap recovery on the npm source, the package-consumer proof, and the fan drawing a citizen as a ROW — that needs `placement.kind === "herdr-pane"`, so the pi session must live in a herdr pane a session reference joins, and a headless container has no panes. **This smoke carries no test beside behavior and no mutant**, and that is a property of its class rather than an omission: it is a real-live container journey whose oracle is the container's own end state, so there is no pure seam to assert against and nothing a source mutation could kill deterministically. Like the other two herdr LIVE lanes it is **on-demand and not an aggregate MUST** — herdr is optional, a cut on a host without it must not block, and first evidence precedes promotion. Without `LIVE=1`, or without a usable Docker daemon, it reports a named SKIP; `ENTWURF_REQUIRE_DOCKER=1` turns the Docker absence red. Neither skip is a pass.
99
+ > **The plugin's FIRST USER PATH is its own on-demand axis, and an npm acquisition receipt holds only for the ref it ran on (#118 홉 1).** Every other herdr gate proves the plugin against a source this repository controls: `check-herdr-plugin-build` drives a stub, and `smoke-herdr-plugin-build-live` redirects the product remote to a local bare clone with git `insteadOf` and asserts `identity.kind === "herdr-checkout"`. npm has been the production authority class since `dd84ac0`; the 0.30.0 follow-up re-pinned `@junghanacs/entwurf@0.30.0` with its published sha512 after the candidate windows rode `herdr-checkout` (`8b92e09`). The **unpublished 0.30.1 preparation** now uses that closed verification carrier again, without inventing a registry integrity. The source re-proof has separate dated npm rows: 2026-09-23 public `main=113ca53`, plugin 0.5.0, runtime 0.25.0 (CHANGELOG 0.25.1), and the 0.30.0 re-pin accepted at public `506c46f` in the [#126 thread](https://github.com/junghan0611/entwurf/issues/126). Neither certifies a future 0.30.1 registry acquisition. After publication and a measured re-pin, that source owes its own public-ref run; checkout candidate evidence does not transfer. `LIVE=1 ./run.sh smoke-herdr-raw-install-live [--ref <REF>]` fills that row in a clean `node:24` container with no socket, config, cache or repo mounted, no `insteadOf`, the public remote spelled as a user would type it, and pi / herdr / Claude Code installed in the IMAGE BUILD as scaffolding (Hard Rule 17 — the product face begins at `herdr integration install pi`). herdr itself comes from `scripts/install-herdr-ci.sh` against `scripts/fixtures/herdr-supply.json`, so no version or digest lives here. `--ref` defaults to `main` rather than a pinned SHA because receipt (a) is defined as what a user gets today; the resolved commit is printed, so the receipt stays exact. It takes a REMOTE REF and not an arbitrary commit — measured on herdr 0.9.1, `--ref <sha>` fails as `couldn't find remote ref`, because the checkout fetches by name — so a candidate is addressed by its branch and pinned by the resolved commit the gate prints. **Its first question is a measurement, not an assumption:** herdr registers a plugin through a running server and falls back to `persist_plugin_offline` only on a connection error (`src/cli/plugin.rs:914-981,1016-1021` @ `c77af189`, 0.9.0 source), and a container has no server. The 2026-09-23 PASS installed with no server on the 0.9.1 binary `113ca53`'s `herdr-supply.json` pins, so that install path worked on 0.9.1 — inferred from the PASS, not a direct observation of the fallback branch; a later "no" on another herdr is #118 investigation output rather than a completion. `--yes` is always passed so a non-interactive exit 2 (`:189`) can never be misread as a missing server. **At a ref whose lock names `herdr-checkout` the acquisition axis reports a NAMED SKIP rather than a red, and the rest of the journey still runs.** That lock is the candidate carrier a cut legitimately sits on — 0.23.0 was tagged that way and `dd84ac0` pinned npm afterwards — so a red there would report a broken install when what is true is that the ref does not lock npm. What is asserted at BOTH lock kinds is that the runtime came from the source the ref locks, and the package name is read from the checkout manifest rather than the lock, because a `herdr-checkout` lock carries no `name` (`herdr-runtime.mjs:317-328`) and joining `undefined` throws instead of failing an assertion. **Cells [6]–[9] carry it past the install into the USE path (#118 H1-4/H1-5).** An install is not a use, so the same container then runs what those bytes became: the compiled bridge entry, the three `REQUIRED_BINS` as executables, and a real `entwurf check-bridge` printing its exact eight-verb set — the installed-runtime row below, measured rather than inferred from the bootstrap's refusal to install without them. Then the pi that this plugin WIRED is started with no `-e` and no `--no-extensions`, because the subject is the wiring and not this checkout: one `get_state` in `--mode rpc` costs zero tokens and needs no provider argument (a bogus `--provider` exits 1 before session_start; omitting the pair lets pi resolve the default the extension itself registers), and while that session is alive the cell reads its V3 record, its gardenId-keyed control socket, and the installed `entwurf peer-facts` reporting it `alive`. The plugin's own status fan is then driven with NEITHER side stubbed, in both states a user can be in: with no herdr server it must name `herdr-agent-list-failed` and go red rather than draw an empty table, and under a headless `herdr server` — scaffolding for that second read only, stopped again before the teardown — it exits 0 and COUNTS the citizen as `unobserved` instead of dropping it. Last, the shipped `entwurf herdr-plugin-deactivate` runs from the installed bin, deletes the runtime it is executing from, and the plugin reinstalls onto the host it fully left — the deactivation row, on the npm source. **What these cells still do not close:** swap / torn-swap recovery on the npm source, the package-consumer proof, and the fan drawing a citizen as a ROW — that needs `placement.kind === "herdr-pane"`, so the pi session must live in a herdr pane a session reference joins, and a headless container has no panes. **This smoke carries no test beside behavior and no mutant**, and that is a property of its class rather than an omission: it is a real-live container journey whose oracle is the container's own end state, so there is no pure seam to assert against and nothing a source mutation could kill deterministically. Like the other two herdr LIVE lanes it is **on-demand and not an aggregate MUST** — herdr is optional, a cut on a host without it must not block, and first evidence precedes promotion. Without `LIVE=1`, or without a usable Docker daemon, it reports a named SKIP; `ENTWURF_REQUIRE_DOCKER=1` turns the Docker absence red. Neither skip is a pass.
100
100
 
101
101
  > **The Herdr plugin's install-time build has two axes, and one of them needs the network (#116 M3-b3).** `check-herdr-plugin-build` is deterministic and rides `check:hermetic`: the listing reaches the runner through its own spawn seam (there is no fake `herdr` binary in this repo), and the one cell that drives the REAL binary reports a named SKIP on a host without it — `ENTWURF_REQUIRE_HERDR=1` makes that absence red. What it cannot have is the real acquisition: `npm pack git+https://github.com/junghan0611/entwurf.git#<full sha>` builds the bridge through `prepare`, which installs devDependencies from the registry. So `LIVE=1 ./run.sh smoke-herdr-plugin-build-live` owns that axis, and its cells are **two kinds of evidence, labelled as such**: three are the real Herdr journey — a real `herdr plugin install` driving the real `[[build]]`, with the UNCHANGED product remote redirected to a local bare clone by git `insteadOf` (the one substitution, which is what makes an unpushed candidate testable) — covering remote-commit available, the `{pi}` → `{pi, claude-code}` reinstall with OpenCode as a zero-write negative control and exactly ONE counted vendor MCP owner entry, and the post-build Herdr-commit gap (where the ledger has already rebound to the gap commit, so the honest assertion is that transition and not unchanged bytes). The fourth cell — remote-commit **unavailable** — calls the shipped runtime leaf directly with the fixed product argv and is therefore **acquisition-leaf evidence, not a journey**; routing it through Herdr would add a second explanation for one red. Its oracle is the honest one too: active runtime, ledger and harness bytes preserved, while the journal is left as the certified `installing` retry authority the transaction deliberately writes before acquiring, and a normal install afterwards is green. Its Claude cell is where the vendor question the deterministic gates refuse to claim gets answered — whether Claude Code accepts an ABSOLUTE executable as an MCP command — and it reports a named skip on a host without `claude`, never a pass. Like the herdr fresh-call rail, this smoke is **on-demand and not an aggregate MUST**. The release aggregate does not require the plugin build smoke: herdr is optional, a cut on a host without it must not block, and first evidence precedes promotion.
102
102
 
@@ -251,8 +251,10 @@ every focused gate green at the time:
251
251
  `FRESHCALL-CODEX-PREMUTATION-MCP` in a manifest the lane never touched; qualification reported
252
252
  it `MUTANT-STALE` (find matched 0×). **A stale mutant is a claim nobody is testing** — its
253
253
  assertion keeps passing while the mutation that gives it meaning is never applied — which is why
254
- this is a red rather than a warning. `check-gate-manifests` validates shape and inventory in
255
- seconds but cannot see a find that no longer matches; only the executing body can.
254
+ this is a red rather than a warning. `check-gate-manifests` also reads each subject and
255
+ validates the joined `find` exactly once (`[QK:MUTANT-FIND-MATCHES-SUBJECT]`): #127's moved
256
+ reuse-catch anchor was caught by this HEAD. It executes zero committed mutants. Only the
257
+ qualification body proves that a matching corruption is killed at its claimed signature.
256
258
  - **Any `mcp/entwurf-bridge/src/**` edit owes `pnpm run build-bridge` plus
257
259
  `./run.sh check-bridge-delivery` in the same inner loop.** `[측정 2026-09-16]` the same candidate
258
260
  reached `check:full` with a stale compiled entry and failed in 22 seconds on "artifact is not
@@ -398,6 +400,7 @@ The single-turn / multi-turn / cross-process / persistence-boundary / shutdown i
398
400
  | Lifecycle policy — turn-scoped is always new; process-scoped may reuse only the live in-memory session; persisted records are not a resume/load path today | `check-acp-session-store`, `check-acp-session-reuse` |
399
401
  | Tool-call / event mapping | `check-acp-event-mapper`, `smoke-acp-provider-live` |
400
402
  | Prompt lifecycle — no wall-clock cutoff on a running turn; abort ends it by ACP `session/cancel` with bounded cleanup; a child death is reported with exit status + stderr on new AND reuse turns; our prompt-phase error text is not classified transient by pi | `check-acp-prompt-lifecycle`, `check-probe-ordering` |
403
+ | Claude typed `incomplete_tool_call` prompt failure — real pi retry/overflow classifiers reject the fixed verdict, raw evidence stays in existing diagnostics, exactly one seal and drop on new/reuse; other failures retain the legacy stderr path, cancel stays separate. A deterministic receipt does not certify the target vendor runtime. No LIVE gate induces this rejection; the raw-turn and reuse smokes certify only their own rows above. | `check-acp-vendor-rejection` (four mutants) |
401
404
  | Operator mcpServers / skills reach the live session | `smoke-acp-mcp-live`, `smoke-acp-skill-live`, `check-acp-config` |
402
405
  | Overlay isolation + memory containment | `check-acp-overlay`, `smoke-acp-memory-containment-live`, `check-acp-tool-surface` |
403
406
 
@@ -48,6 +48,7 @@ Source of truth: `pi-extensions/lib/acp/backend-adapter.ts`.
48
48
  | `buildSessionMeta` | Build optional `newSession._meta` from the already-loaded carrier. |
49
49
  | `enforceModel` | Make the requested native model authoritative before the prompt. |
50
50
  | `configSignatureFields` | Return a stable primitive map whose changes invalidate reuse. |
51
+ | `readPromptRejection?` | Name a backend-owned typed prompt rejection and its fixed app-policy verdict; absent means the existing verbatim-first failure path. |
51
52
 
52
53
  `backend.ts` resolves the adapter once at turn entry. Common config never branches on
53
54
  backend-specific keys; `adapterSettings` remains opaque until handed back to its owner.
@@ -85,21 +86,58 @@ turned one long turn into four in 0.13.0. Gates: `check-acp-prompt-lifecycle` (b
85
86
  with pi's own classifier as the oracle), `check-probe-ordering` (no production prompt
86
87
  cutoff in source).
87
88
 
89
+ ### Typed Claude prompt rejection — no automatic whole-prompt replay (#127)
90
+
91
+ Claude ACP 0.85.1 can reject a prompt with the wire SDK's `RequestError`, code `-32603`,
92
+ `data.errorKind === "incomplete_tool_call"`, after marking unfinished foreground tools
93
+ failed. Some tools may already have run. This is an RPC failure, not a successful
94
+ `stopReason`, and a failed tool notification alone is not a turn settlement. The code
95
+ is JSON-RPC's internal error; the kind is Claude's extension vocabulary, not a standard
96
+ retry permission. AIR session-failure metadata remains capability-gated off by
97
+ `clientCapabilities: {}`.
98
+
99
+ Only the Claude adapter declares `readPromptRejection`. The common loop passes the
100
+ once-resolved adapter at the two prompt catches, not at bootstrap failures, and skips
101
+ this reader when the operator's abort wins. A recognized rejection seals once as
102
+ `error/error`, drops the new or reused session under the existing policy, and sets a
103
+ fixed semantic `errorMessage` which pi's real retry **and** overflow classifiers must
104
+ reject. No global retry disable, substring sanitizer, or new failure ledger is added.
105
+
106
+ The first raw failure remains in pi's existing `AssistantMessage.diagnostics`: original
107
+ name/message/stack/code, serialized data, and applicable stderr tail, lifecycle, launch
108
+ signal and overflow hint. None is remixed into the semantic verdict. Actual wire data
109
+ is parsed JSON; a hand-built non-wire value that cannot serialize gets an explicit
110
+ unserializable marker rather than escaping before the stream seal. Assistant content,
111
+ context replay and reuse signatures remain unchanged; persisted message JSON gains
112
+ these diagnostics. Cortex, other error kinds/codes, preprompt failures and aborts keep
113
+ the existing path. No warm reuse after this failure is newly claimed.
114
+
115
+ `check-acp-vendor-rejection` owns the source-adjacent adapter/pipeline assertions and
116
+ four defect mutants. Its real classifier oracles, raw positive controls, failed-tool
117
+ ordering, exact-once seal/drop, raw evidence and separate cooperative-cancel cells do
118
+ not certify a real vendor runtime. Target closure installation, focused receipts,
119
+ qualification, full floor and LIVE compatibility remain separate in the
120
+ [#127 thread](https://github.com/junghan0611/entwurf/issues/127).
121
+
88
122
  ## Support contract (#81)
89
123
 
90
124
  What "supported" means here, per declaration class. The classes are kept apart on purpose: one
91
125
  undifferentiated "supported" column is what let a Claude PASS read as if it also certified Cortex.
92
126
  The rows are THIS checkout's declarations, not a shipped release: what a published version carried
93
127
  is that version's CHANGELOG entry, and a candidate's own evidence is its issue thread, LIVE receipts
94
- and exact-SHA CI (#125 for the Pi 1.0 candidate).
128
+ and exact-SHA CI. The pi row shipped in 0.30.0 (#125). The ACP wire SDK, Claude adapter and Claude
129
+ Agent SDK rows are the unpublished #127 candidate: published 0.30.0 still declares
130
+ `claude-agent-acp 0.79.0` and `@agentclientprotocol/sdk 1.4.0`
131
+ (`[측정 2026-10-04]` `npm view @junghanacs/entwurf@0.30.0`; per-bump record in the ROADMAP Dep bump
132
+ ledger, 2026-10-03).
95
133
 
96
134
  | Surface | Declaration | Class | What a green actually says |
97
135
  |---|---|---|---|
98
136
  | Entwurf package | `package.json` `version` | checkout declaration, not a release receipt | names the package label these rows sit beside; whether that label shipped, and with which rows, is CHANGELOG's record |
99
- | pi runtime | devDep exact `1.0.0`, peer `>=1.0.0 <1.1` | **exact** oracle + **closed range** | the source pin this checkout builds and gates against; hosts inside the range are accepted, and the ceiling moves only on measurement. Its 1.0.0 evidence is #125's branch receipts, not a shipped release |
100
- | ACP wire SDK | `@agentclientprotocol/sdk 1.4.0` | **exact** | the shared wire oracle both adapters speak |
101
- | Claude ACP adapter | `@agentclientprotocol/claude-agent-acp 0.79.0` | **exact**, bundled | the adapter we ship and certify; resolved before any PATH fallback |
102
- | Claude Agent SDK | `0.3.274` (transitive) | **exact** oracle | the runtime risk surface behind the adapter |
137
+ | pi runtime | devDep exact `1.0.0`, peer `>=1.0.0 <1.1` | **exact** oracle + **closed range** | the source pin this checkout builds and gates against; hosts inside the range are accepted, and the ceiling moves only on measurement. Shipped in 0.30.0 (`[측정 2026-10-04]` npm `latest` `0.30.0` declares this same peer range); its acceptance record is CHANGELOG 0.30.0, with #125 as the branch evidence |
138
+ | ACP wire SDK | `@agentclientprotocol/sdk 1.6.0` | **exact** | the shared wire oracle both adapters speak |
139
+ | Claude ACP adapter | `@agentclientprotocol/claude-agent-acp 0.85.1` | **exact**, bundled | checkout candidate; resolved before any PATH fallback; declaration is not compatibility evidence |
140
+ | Claude Agent SDK | `0.3.286` (transitive) | **exact** oracle | the runtime risk surface behind the adapter |
103
141
  | Anthropic SDK | `0.100.1` | **exact**, peer-resolution only | satisfies the Agent SDK peer floor (0.93.0+); never an API client here (gate L4) |
104
142
  | Claude Code runtime | `>=2.1.217` (`entwurf.claudeCodeFloor`) | **floor** | below it, hook args are silently dropped; entwurf enforces this itself |
105
143
  | Node | `>=24` (`engines.node`) | **floor** | single axis, derived everywhere else |
@@ -181,7 +219,7 @@ different reasons, and collapsing them would hide a real risk**:
181
219
  imported nowhere: `[측정 2026-09-18]` `git grep -c` over `pi-extensions/`, `test/`, `scripts/`,
182
220
  `mcp/` is **0**. We never imported the adapter as a library at all — we spawn its binary.
183
221
  - **0.77.0's `allowDangerouslySkipPermissions` opt-out is a new lever we deliberately do not
184
- pull, and our effective permission mode is unchanged.** 0.76.0 sent
222
+ pull, and at 0.79.0 our effective permission mode was unchanged.** 0.76.0 sent
185
223
  `allowDangerouslySkipPermissions: ALLOW_BYPASS` unconditionally and computed
186
224
  `initialPermissionMode = creationOpts.permissionMode ?? resolvePermissionMode(settings…)`.
187
225
  0.79.0 computes `allowBypass = ALLOW_BYPASS && sessionMeta?.claudeCode?.options?.allowDangerouslySkipPermissions !== false`
@@ -192,7 +230,9 @@ different reasons, and collapsing them would hide a real risk**:
192
230
  versions (`dist/permissions/modes.js` 직독). Our overlay pins
193
231
  `permissions.defaultMode: "bypassPermissions"` (`overlay.ts:122`), which resolves the same
194
232
  under both. The lever now EXISTS for a host that wants a non-bypass sibling; declaring it is a
195
- separate axis, not a one-line flip, and nothing in this bump takes it.
233
+ separate axis, not a one-line flip, and nothing in this bump takes it. That is the 0.79.0
234
+ measurement; at 0.85.1 project or managed settings can drop bypass (the 0.79.0 → 0.85.1 entry
235
+ below).
196
236
  - **0.77.0's system-reminder strip never touches our first-user-message augment.**
197
237
  `INJECTED_CONTEXT_MARKERS = ["system-reminder"]` joins the local-command markers in
198
238
  `stripMarkerTags`, and `stripLocalCommandMetadata` has exactly two call sites
@@ -247,6 +287,51 @@ different reasons, and collapsing them would hide a real risk**:
247
287
  is byte-identical (`md5 cfd031d0…` both versions). The `settings`-as-STRING-PATH branch our
248
288
  call-site contract names is still on the unconditional `session/new` path
249
289
  (`dist/acp-agent.js:6010-6012`).
290
+ - **0.79.0 → 0.85.1 (#127) is a source classification, not a runtime certification.**
291
+ `[측정 2026-10-03, tag v0.85.1 = 686c0c99, src and dist read directly; nothing executed]`.
292
+ Coordinates and evidence classes are in the ROADMAP Dep bump entry for 2026-10-03. The typed
293
+ rejection above is the only failure-policy change Entwurf wrote for this bump. The reachable
294
+ upstream changes below are classified, not new Entwurf contracts:
295
+ - **Permission mode is conditional (#1165, reachable on a host/project basis).** The adapter
296
+ builds its own `SettingsManager(params.cwd, …)` over `claudeConfigDir/settings.json`, the
297
+ cwd's `.claude/settings.json` and `settings.local.json`, and the managed path, then calls
298
+ `resolveSettings({ cwd })` without a `settingSources` argument. That call flow is separate
299
+ from our SDK query's `settingSources: []` (`tool-surface.ts:152`). When those settings carry
300
+ `disableBypassPermissionsMode: "disable"`, `allowBypass` drops, more permission requests reach
301
+ our approve-all-by-kind handler, and `[permission:approved]` notices appear. The SDK's
302
+ default `resolveSettings` source set is UNREAD, and no gate covers this case.
303
+ - **Reachable, classified from source only.**
304
+ - `createSession` starts a background `getContextUsage()` refresh (call at tag `85:8317`), and
305
+ does so again after a model change. Upstream comments (`85:8301`, `85:8350-8352`) describe
306
+ SDK control requests as serialized. The SDK internals are UNREAD, so whether our bounded
307
+ set-model and early-abort grace queue behind it is a lead, and its timing is UNRUN.
308
+ - A turn made only of `<synthetic>` frames emits no `usage_update` (#1132), and our
309
+ keep-prior occupancy seals the prior value.
310
+ - `tool_call_update` omits unchanged fields for every client (#1153). No producer was found
311
+ that sends a pre-terminal `rawOutput.isError`, so a tool failure still arrives as
312
+ `status: "failed"`.
313
+ - #1216's `teardownSession` stops awaiting the interrupt reply. It is reached only
314
+ indirectly, through dispose on stdin EOF or a signal: we never send `session/close`, and
315
+ our `session/cancel` still awaits.
316
+ - A sign-out during an unsettled turn fails it with `RequestError.authRequired()` and no
317
+ extra message (#1179, tag `85:4420`). The stock text is the SDK default
318
+ `Authentication required` (sdk 1.6.0 `jsonrpc.js:1032-1033`), and that message ALONE is not
319
+ transient to pi's real classifier. The sealed legacy `errorMessage` also carries the
320
+ stderr tail, lifecycle and hint (`backend.ts:1463-1490`), and a tail can still match the
321
+ retry pattern. `[측정 2026-10-04, coordinator classifier probe]`: `"Authentication required"`
322
+ → `retryable: false`; the same text plus `\n--- backend stderr (tail) ---\n500` →
323
+ `retryable: true`. #127 does not sanitize this path and certifies no auth runtime.
324
+ - The nine other vendor `internalError` sites keep the same legacy path. Their
325
+ `Internal error:` prefix can match the classifier, and that pre-existing policy is
326
+ unchanged here. This is a source and classifier reading, not a frequency claim and not a
327
+ complete-callee identity claim.
328
+ - **Capability-gated:** `notice`, the one new `sessionUpdate` literal (17 → 18 kinds).
329
+ - **Ambient only:** `CLAUDE_AGENT_ACP_EXPERIMENTAL_V2=1` selects a v1/v2 router. Entwurf
330
+ neither sets nor scrubs it, and the router's v1 path for a v1 client is UNRUN.
331
+ - **Entry identity is not settlement identity.** `newSession`, `prompt`,
332
+ `setSessionConfigOption`, `requestPermission`, `closeSession`, `sessionUsage`,
333
+ `turnQuotaMeta` and `quotaTokenCount` are byte-identical as extracted entry bodies. Turn
334
+ settlement moved inside the changed `runConsumer`.
250
335
 
251
336
  - **The one 0.73.0 → 0.75.1 change that DOES reach us:** context compaction is now surfaced as a
252
337
  synthetic ACP tool lifecycle (0.75.0, #991) — a `tool_call` with `kind: "think"`, title
@@ -373,10 +458,15 @@ caller-session `_meta`, and cross-machine certification.
373
458
 
374
459
  A backend can return `newSession` before its declared MCP server is callable. This was
375
460
  observed intermittently on the Claude rail and directly on Cortex's private `mcp.json`
376
- path. Neither `claude-agent-acp` 0.79.0 nor the Cortex landing adds a client-side
377
- readiness fence over a session's declared MCP servers, and entwurf's common loop
378
- calls `mcpServerStatus()` nowhere.
379
- (Re-measured at the 0.76.0 → 0.79.0 bump, not inherited — the previous bump's argument is
461
+ path. Entwurf's common loop calls `mcpServerStatus()` nowhere; it does not add a
462
+ client-side readiness fence over a session's declared MCP servers. The historical
463
+ 0.76.0 → 0.79.0 source measurement below is dated evidence, not a current-version
464
+ runtime claim. For the 0.85.1 candidate, #127 re-measured the MCP-authentication
465
+ window as byte-identical and ran the MCP/self/send LIVE samples; neither source
466
+ identity nor green samples establish the cause of the intermittent readiness gap
467
+ or close #72. Changed SDK internals and session initialization remain separate
468
+ risk surfaces.
469
+ (Historical measurement at the 0.76.0 → 0.79.0 bump, not inherited — the previous bump's argument is
380
470
  not reused, the way the 0.75.1 → 0.76.0 entry did not reuse 0.73.0 → 0.75.1's.
381
471
  `mcpServerStatus` call sites in `src/acp-agent.ts` are **2 at v0.76.0 and 2 at v0.79.0**
382
472
  `[측정 2026-09-18, upstream v0.79.0/src/acp-agent.ts read directly, grep -n]`; they first
@@ -392,7 +482,8 @@ empty) and the region moved +11 while the file shrank 10,405 → 10,329 lines. T
392
482
  agent-picker removal, the 0.78.0 compaction/checkpoint/AIR work and the 0.79.0 permission
393
483
  presentation touch no part of this path, so the other reachable-surface findings stand as
394
484
  re-measured in the capability-posture section above.
395
- This bump changes no readiness behavior and closes no part of #72.)
485
+ That historical source measurement closes no part of #72; it is not a claim that
486
+ all effective readiness behavior stayed unchanged in the 0.85.1 runtime.)
396
487
 
397
488
  ### 11-7-a/b. Instrument and first measurement
398
489
 
@@ -500,12 +591,14 @@ nothing here is promised and then quietly not delivered. `[QK:PI-QUEUE-NO-MIDFLI
500
591
  while a turn is in flight never joins that turn and arrives as a later one. The delay's SIZE is
501
592
  that turn's duration, which the same cell fixes by construction.
502
593
 
503
- **Forwarding is not adopted here.** `[측정 2026-09-22]` standard ACP has no steer method at all —
504
- enumerating the installed `@agentclientprotocol/sdk`'s `AGENT_METHODS` and `CLIENT_METHODS` yields
505
- zero entries containing `steer`. What exists is a PRIVATE vendor extension,
506
- `_session/steering` in `@agentclientprotocol/claude-agent-acp@0.79.0`
507
- (`dist/acp-agent.js:163`, handler at `:7792`), client→agent, advertised through
508
- `InitializeResponse._meta.steering.supported`. Our client sends `initialize`, `session/new`,
594
+ **Forwarding is not adopted here.** The dated `[측정 2026-09-22]` SDK-method enumeration
595
+ and private-extension observation below belong to the then-installed oracle, not a
596
+ 0.85.1 runtime measurement: `AGENT_METHODS` and `CLIENT_METHODS` contained zero entries
597
+ matching `steer`; `_session/steering` was a PRIVATE vendor extension in
598
+ `@agentclientprotocol/claude-agent-acp@0.79.0` (`dist/acp-agent.js:163`, handler at `:7792`),
599
+ client→agent, advertised through `InitializeResponse._meta.steering.supported`.
600
+ The current boundary is our client's lack of a steering path, not a claim that the
601
+ old vendor coordinates describe the current pin. Our client sends `initialize`, `session/new`,
509
602
  `session/prompt`, `session/set_config_option` and `session/cancel` (`acp-client.ts:138-149`) and
510
603
  has no steering path at all.
511
604
 
@@ -20,6 +20,7 @@
20
20
  import { createRequire } from "node:module";
21
21
  import { homedir } from "node:os";
22
22
  import { fileURLToPath } from "node:url";
23
+ import { RequestError } from "@agentclientprotocol/sdk";
23
24
  import { enrichMcpServersWithEnvelope } from "./config.js";
24
25
  import { loadEngraving } from "./engraving.js";
25
26
  import { CORTEX_MODEL_PREFIX, curatedClaudeModels, curatedCortexModels, SUPPORTED_ANTHROPIC_MODEL_IDS, SUPPORTED_CORTEX_MODEL_IDS, } from "./models.js";
@@ -34,10 +35,46 @@ import { buildClaudeSessionMeta } from "./tool-surface.js";
34
35
  function shellQuote(value) {
35
36
  return `'${value.replace(/'/g, `'\\''`)}'`;
36
37
  }
38
+ /** The pi diagnostic type under which backend.ts preserves a recognised rejection verbatim. */
39
+ export const ACP_PROMPT_REJECTION_DIAGNOSTIC = "entwurf_acp_prompt_rejection";
40
+ /** The verdict for claude-agent-acp's `incomplete_tool_call` (#127; upstream #1212, 0.85.0+). */
41
+ export const INCOMPLETE_TOOL_CALL_VERDICT = "ACP turn failed because foreground tool results are incomplete. Some tools may already have run; automatic replay is unsafe. Inspect the preserved vendor rejection before continuing.";
37
42
  // ---------------------------------------------------------------------------
38
43
  // claude adapter — the first implementation (the rail's reference backend)
39
44
  // ---------------------------------------------------------------------------
40
45
  const SUPPORTED_CLAUDE_IDS = new Set(SUPPORTED_ANTHROPIC_MODEL_IDS);
46
+ /** JSON-RPC internal error — the code claude-agent-acp rejects an unfinished-tool turn with. */
47
+ const JSONRPC_INTERNAL_ERROR = -32603;
48
+ /** claude-agent-acp's own kind for a turn that hit `end_turn` with foreground tools unanswered
49
+ * (read at v0.85.1 `src/acp-agent.ts:4286-4340`, `errorKindData("incomplete_tool_call")`). */
50
+ const CLAUDE_INCOMPLETE_TOOL_CALL = "incomplete_tool_call";
51
+ /**
52
+ * The claude reader for `readPromptRejection` (#127). Only the root wire SDK's own decode
53
+ * counts: its client rejects a JSON-RPC error response with `new RequestError(code, message,
54
+ * data)`, `data` being the parsed `error.data` (sdk 1.4.0 `dist/jsonrpc.js:842-844`). A
55
+ * look-alike object, another code, or another `errorKind` is not this failure and keeps the
56
+ * verbatim-first path.
57
+ */
58
+ function readClaudePromptRejection(err) {
59
+ if (!(err instanceof RequestError) || err.code !== JSONRPC_INTERNAL_ERROR)
60
+ return undefined;
61
+ const data = err.data;
62
+ if (typeof data !== "object" || data === null || !("errorKind" in data))
63
+ return undefined;
64
+ if (data.errorKind !== CLAUDE_INCOMPLETE_TOOL_CALL)
65
+ return undefined;
66
+ return { errorKind: CLAUDE_INCOMPLETE_TOOL_CALL, verdict: INCOMPLETE_TOOL_CALL_VERDICT, dataJson: jsonText(data) };
67
+ }
68
+ /** Evidence text for a wire value. A value that came off the wire serializes; one that cannot
69
+ * (a hand-built fake) is named as such, so recording evidence never breaks the turn's seal. */
70
+ function jsonText(value) {
71
+ try {
72
+ return JSON.stringify(value) ?? String(value);
73
+ }
74
+ catch (err) {
75
+ return `[unserializable: ${err instanceof Error ? err.name : typeof err}]`;
76
+ }
77
+ }
41
78
  /**
42
79
  * Resolve the claude launch — an ENTWURF-OWNED launcher, or the env override for debug.
43
80
  * This is the single source for the claude launch spec; backend.ts holds no private copy.
@@ -139,6 +176,9 @@ export const claudeAdapter = {
139
176
  // The token partition is NOT declared here: ACP's only token carrier is a
140
177
  // per-turn round-trip aggregate, which is not what pi's four fields mean.
141
178
  sealsTurnAccounting: true,
179
+ // #127: claude-agent-acp 0.85.0+ rejects an `end_turn` with unanswered foreground tools as
180
+ // -32603 `incomplete_tool_call` (upstream #1212). Its raw text reads as transient to pi.
181
+ readPromptRejection: readClaudePromptRejection,
142
182
  };
143
183
  // ---------------------------------------------------------------------------
144
184
  // Registry — modelId → adapter
@@ -285,6 +325,9 @@ export const cortexAdapter = {
285
325
  // backend.ts seals nothing for a backend without this flag, so cortex's
286
326
  // emitted usage is byte-identical to what it was before #93. Declaring this
287
327
  // with a guess would mint exactly the silent misaccounting #93 exists to end.
328
+ // NO readPromptRejection — also deliberate. Nobody has read which failures cortex's ACP
329
+ // server rejects a prompt with, so every cortex failure keeps the verbatim-first
330
+ // errorMessage. Borrowing claude's `incomplete_tool_call` reader would be a guess.
288
331
  };
289
332
  const ADAPTERS = [claudeAdapter, cortexAdapter];
290
333
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@junghanacs/entwurf",
3
- "version": "0.30.0",
3
+ "version": "0.30.1",
4
4
  "description": "Garden-citizen dispatch substrate and meta-bridge for pi, Claude Code, Copilot CLI, OMP, Antigravity, and Codex CLI; Claude and Snowflake Cortex Code ACP backends.",
5
5
  "license": "MIT",
6
6
  "author": {
@@ -84,8 +84,8 @@
84
84
  "claudeCodeFloor": ">=2.1.217"
85
85
  },
86
86
  "dependencies": {
87
- "@agentclientprotocol/claude-agent-acp": "0.79.0",
88
- "@agentclientprotocol/sdk": "1.4.0",
87
+ "@agentclientprotocol/claude-agent-acp": "0.85.1",
88
+ "@agentclientprotocol/sdk": "1.6.0",
89
89
  "@anthropic-ai/sdk": "0.100.1",
90
90
  "@modelcontextprotocol/sdk": "^1.12.1",
91
91
  "smol-toml": "^1.8.0",
@@ -177,7 +177,7 @@
177
177
  "check:core": "pnpm run check:toolchain && pnpm run check:vitest && pnpm run check:contracts",
178
178
  "check:toolchain": "pnpm lint && pnpm typecheck",
179
179
  "check:vitest": "./run.sh check-mux-fresh-call && ./run.sh check-acp-sdk-surface && ./run.sh check-pi-queue-oracle && ./run.sh check-tests-beside-behavior",
180
- "check:contracts": "./run.sh check-shell-quote && ./run.sh check-entwurf-session-identity && ./run.sh check-meta-session && ./run.sh check-meta-v3-record && ./run.sh check-mailbox-receipt-state && ./run.sh check-entwurf-capabilities && ./run.sh check-harness-admission-parity && ./run.sh check-capability-bundle-reach && ./run.sh check-meta-mailbox-state-write && ./run.sh check-meta-receiver-marker && ./run.sh check-meta-capability-source && ./run.sh check-model-lock && ./run.sh check-dep-versions && ./run.sh check-node-floor-coherence && ./run.sh check-claude-floor-coherence && ./run.sh check-copilot-statusline && ./run.sh check-pi-import-surface && ./run.sh check-env-namespace && ./run.sh check-pi-runtime-version && ./run.sh check-pi-preflight && ./run.sh check-project-trust-handler && ./run.sh check-entwurf-v2-contract && ./run.sh check-entwurf-v2-send && ./run.sh check-entwurf-v2-runner && ./run.sh check-control-send-receipt && ./run.sh check-compaction-send-guard && ./run.sh check-entwurf-resume-args && ./run.sh check-entwurf-v2-native-push && ./run.sh check-entwurf-v2-production && ./run.sh check-entwurf-v2-surface && ./run.sh check-entwurf-bridge-pi-free && ./run.sh check-resume-launch-identity && ./run.sh check-mux-placement && ./run.sh check-mux-launch && ./run.sh check-mux-resume-call && ./run.sh check-mux-parent-artifact && ./run.sh check-mux-launcher-fence && ./run.sh check-entwurf-v2-visible-resume && ./run.sh check-control-socket-path && ./run.sh check-meta-listing && ./run.sh check-entwurf-fact-provider && ./run.sh check-herdr-placement && ./run.sh check-herdr-fresh-call && ./run.sh check-herdr-supply && ./run.sh check-fresh-call-dispatch && ./run.sh check-entwurf-peers-surface && ./run.sh check-entwurf-self-address && ./run.sh check-entwurf-deliverability && ./run.sh check-native-push-adapter && ./run.sh check-native-push-register && ./run.sh check-auth-boundary && ./run.sh check-acp-overlay && ./run.sh check-acp-tool-surface && ./run.sh check-acp-event-mapper && ./run.sh check-acp-prompt-builder && ./run.sh check-acp-config && ./run.sh check-acp-session-store && ./run.sh check-acp-carrier-augment && ./run.sh check-acp-foreground-only && ./run.sh check-codex-sender-identity && ./run.sh check-codex-native-push",
180
+ "check:contracts": "./run.sh check-shell-quote && ./run.sh check-entwurf-session-identity && ./run.sh check-meta-session && ./run.sh check-meta-v3-record && ./run.sh check-mailbox-receipt-state && ./run.sh check-entwurf-capabilities && ./run.sh check-harness-admission-parity && ./run.sh check-capability-bundle-reach && ./run.sh check-meta-mailbox-state-write && ./run.sh check-meta-receiver-marker && ./run.sh check-meta-capability-source && ./run.sh check-model-lock && ./run.sh check-dep-versions && ./run.sh check-node-floor-coherence && ./run.sh check-claude-floor-coherence && ./run.sh check-copilot-statusline && ./run.sh check-pi-import-surface && ./run.sh check-env-namespace && ./run.sh check-pi-runtime-version && ./run.sh check-pi-preflight && ./run.sh check-project-trust-handler && ./run.sh check-entwurf-v2-contract && ./run.sh check-entwurf-v2-send && ./run.sh check-entwurf-v2-runner && ./run.sh check-control-send-receipt && ./run.sh check-compaction-send-guard && ./run.sh check-entwurf-resume-args && ./run.sh check-entwurf-v2-native-push && ./run.sh check-entwurf-v2-production && ./run.sh check-entwurf-v2-surface && ./run.sh check-entwurf-bridge-pi-free && ./run.sh check-resume-launch-identity && ./run.sh check-mux-placement && ./run.sh check-mux-launch && ./run.sh check-mux-resume-call && ./run.sh check-mux-parent-artifact && ./run.sh check-mux-launcher-fence && ./run.sh check-entwurf-v2-visible-resume && ./run.sh check-control-socket-path && ./run.sh check-meta-listing && ./run.sh check-entwurf-fact-provider && ./run.sh check-herdr-placement && ./run.sh check-herdr-fresh-call && ./run.sh check-herdr-supply && ./run.sh check-fresh-call-dispatch && ./run.sh check-entwurf-peers-surface && ./run.sh check-entwurf-self-address && ./run.sh check-entwurf-deliverability && ./run.sh check-native-push-adapter && ./run.sh check-native-push-register && ./run.sh check-auth-boundary && ./run.sh check-acp-overlay && ./run.sh check-acp-tool-surface && ./run.sh check-acp-event-mapper && ./run.sh check-acp-prompt-builder && ./run.sh check-acp-config && ./run.sh check-acp-session-store && ./run.sh check-acp-carrier-augment && ./run.sh check-acp-foreground-only && ./run.sh check-acp-vendor-rejection && ./run.sh check-codex-sender-identity && ./run.sh check-codex-native-push",
181
181
  "check:hermetic": "./run.sh check-bridge-delivery && ./run.sh smoke-pi-attach && ./run.sh check-fresh-cut-gate && ./run.sh check-hook-launch-topology && ./run.sh check-meta-hook-session-switch && ./run.sh check-copilot-birth-hook && ./run.sh check-copilot-receive-arm && ./run.sh check-copilot-launch && ./run.sh check-pi-launch && ./run.sh check-codex-app-server-launch && ./run.sh check-meta-identity-consumers && ./run.sh check-socket-probe && ./run.sh smoke-meta-honesty && ./run.sh check-meta-doctor-oracle && ./run.sh check-agy-permission-matrix && ./run.sh smoke-agy-statusline-state && ./run.sh smoke-copilot-statusline-state && ./run.sh smoke-copilot-mcp-state && ./run.sh smoke-omp-bridge-state && ./run.sh smoke-omp-mcp-state && ./run.sh smoke-omp-receive-state && ./run.sh check-omp-birth-hook && ./run.sh check-omp-receive-arm && ./run.sh check-omp-fresh-preflight && ./run.sh smoke-agy-hooks-state && ./run.sh smoke-pi-provider-state && ./run.sh smoke-user-scope-citizen && ./run.sh smoke-meta-prune && ./run.sh smoke-meta-keyset-guard && ./run.sh check-meta-manifest-schema && ./run.sh check-entwurf-control-rpc && ./run.sh check-entwurf-bridge-boot && ./run.sh check-probe-bridge-command && ./run.sh check-meta-facts && ./run.sh check-peer-facts && ./run.sh check-herdr-plugin && ./run.sh check-herdr-plugin-profile && ./run.sh check-herdr-plugin-build && ./run.sh check-herdr-runtime-bootstrap && ./run.sh check-herdr-activation && ./run.sh check-agy-sender-identity && ./run.sh check-acp-provider-surface && ./run.sh check-acp-stop-reason && ./run.sh check-acp-prompt-lifecycle && ./run.sh check-acp-usage-accounting && ./run.sh check-acp-launch-namespace && ./run.sh check-acp-stream-hooks && ./run.sh check-acp-backend-preflight && ./run.sh check-acp-session-reuse && ./run.sh check-typing-call-fence && ./run.sh check-gate-manifests && ./run.sh check-release-gate-outcomes && ./run.sh check-probe-ordering && ./run.sh check-probe-cli-shim && ./run.sh check-acp-cortex && ./run.sh check-codex-bridge-identity && ./run.sh check-pi-bridge-sender && ./run.sh check-pi-mcp-register && ./run.sh check-pi-mcp-bridge && ./run.sh check-codex-birth-hook && ./run.sh smoke-codex-config-state && ./run.sh smoke-codex-birth && ./run.sh check-herdr-sandbox",
182
182
  "check:package": "./run.sh check-install-surface && ./run.sh smoke-meta-install-state && ./run.sh smoke-agy-install-state && ./run.sh smoke-setup-verdict && ./run.sh check-pack-pin-matcher && ./run.sh check-pack-consumer-roots && ./run.sh check-package-source-routing && ./run.sh check-install-preflight && ./run.sh check-pack"
183
183
  },