@junghanacs/entwurf 0.20.0 → 0.21.0

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.
Files changed (112) hide show
  1. package/AGENTS.md +85 -190
  2. package/BASELINE.md +6 -5
  3. package/CHANGELOG.md +220 -14
  4. package/CONTRIBUTING.md +1 -1
  5. package/DELIVERY.md +202 -60
  6. package/README.md +54 -26
  7. package/VERIFY.md +37 -7
  8. package/docs/acp-backend-rail.md +31 -15
  9. package/docs/external-mcp-host.md +58 -35
  10. package/docs/fresh-cut-policy.md +3 -2
  11. package/docs/setup-clean-host.md +110 -17
  12. package/mcp/entwurf-bridge/dist/mcp/entwurf-bridge/src/index.js +101 -109
  13. package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/backend-adapter.js +2 -2
  14. package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/tool-surface.js +6 -0
  15. package/mcp/entwurf-bridge/dist/pi-extensions/lib/codex-fresh-preflight.js +326 -0
  16. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-fact-provider.js +22 -2
  17. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-peers-render.js +3 -1
  18. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-contract.js +6 -7
  19. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-native-push.js +30 -17
  20. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-production.js +7 -1
  21. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-surface.js +7 -4
  22. package/mcp/entwurf-bridge/dist/pi-extensions/lib/meta-mailbox-body.js +20 -11
  23. package/mcp/entwurf-bridge/dist/pi-extensions/lib/meta-sender-identity.js +227 -1
  24. package/mcp/entwurf-bridge/dist/pi-extensions/lib/mux-fresh-call.js +50 -15
  25. package/mcp/entwurf-bridge/dist/pi-extensions/lib/native-push/adapter.js +10 -7
  26. package/mcp/entwurf-bridge/dist/pi-extensions/lib/native-push/codex-ws-client.js +403 -0
  27. package/mcp/entwurf-bridge/dist/pi-extensions/lib/native-push/register.js +4 -4
  28. package/mcp/entwurf-bridge/dist/pi-extensions/meta-bridge-hook-codex.js +325 -0
  29. package/mcp/entwurf-bridge/dist/scripts/meta-bridge-fresh-cut.js +6 -1
  30. package/mcp/entwurf-bridge/src/index.ts +115 -111
  31. package/mcp/entwurf-bridge/tsconfig.build.json +1 -0
  32. package/package.json +14 -5
  33. package/pi-extensions/entwurf-control.ts +71 -19
  34. package/pi-extensions/lib/acp/acp-client.ts +3 -3
  35. package/pi-extensions/lib/acp/backend-adapter.ts +3 -3
  36. package/pi-extensions/lib/acp/backend.ts +3 -3
  37. package/pi-extensions/lib/acp/event-mapper.ts +4 -4
  38. package/pi-extensions/lib/acp/tool-surface.ts +6 -0
  39. package/pi-extensions/lib/codex-fresh-preflight.ts +363 -0
  40. package/pi-extensions/lib/compaction-send-guard.ts +80 -0
  41. package/pi-extensions/lib/entwurf-fact-provider.ts +29 -3
  42. package/pi-extensions/lib/entwurf-peers-render.ts +4 -1
  43. package/pi-extensions/lib/entwurf-v2-contract.ts +6 -7
  44. package/pi-extensions/lib/entwurf-v2-native-push.ts +35 -18
  45. package/pi-extensions/lib/entwurf-v2-production.ts +10 -3
  46. package/pi-extensions/lib/entwurf-v2-surface.ts +7 -4
  47. package/pi-extensions/lib/meta-mailbox-body.ts +22 -13
  48. package/pi-extensions/lib/meta-sender-identity.ts +305 -0
  49. package/pi-extensions/lib/mux-fresh-call.ts +64 -19
  50. package/pi-extensions/lib/native-push/adapter.ts +21 -24
  51. package/pi-extensions/lib/native-push/codex-ws-client.ts +506 -0
  52. package/pi-extensions/lib/native-push/register.ts +7 -9
  53. package/pi-extensions/meta-bridge-hook-codex.ts +371 -0
  54. package/run.sh +251 -35
  55. package/scripts/check-acp-usage-accounting.ts +9 -9
  56. package/scripts/check-agy-sender-identity.ts +1 -1
  57. package/scripts/check-codex-birth-hook.ts +264 -0
  58. package/scripts/check-codex-bridge-identity.ts +179 -0
  59. package/scripts/check-codex-native-push.ts +386 -0
  60. package/scripts/check-codex-sender-identity.ts +495 -0
  61. package/scripts/check-compaction-send-guard.ts +130 -0
  62. package/scripts/check-copilot-receive-arm.ts +4 -1
  63. package/scripts/check-entwurf-fact-provider.ts +38 -0
  64. package/scripts/check-entwurf-peers-surface.ts +13 -1
  65. package/scripts/check-entwurf-self-address.ts +15 -16
  66. package/scripts/check-entwurf-v2-contract.ts +4 -3
  67. package/scripts/check-entwurf-v2-decider.ts +7 -5
  68. package/scripts/check-entwurf-v2-native-push.ts +35 -7
  69. package/scripts/check-entwurf-v2-production.ts +203 -11
  70. package/scripts/check-entwurf-v2-runner.ts +1 -1
  71. package/scripts/check-entwurf-v2-surface.ts +1 -1
  72. package/scripts/check-gate-qualification.ts +7 -4
  73. package/scripts/check-harness-admission-parity.ts +0 -1
  74. package/scripts/check-install-surface.ts +23 -7
  75. package/scripts/check-mux-launch-tmux.ts +47 -2
  76. package/scripts/check-native-push-adapter.ts +20 -16
  77. package/scripts/check-native-push-register.ts +5 -1
  78. package/scripts/check-release-gate-outcomes.ts +47 -1
  79. package/scripts/check-setup-qualification.sh +3 -1
  80. package/scripts/codex-birth-doctor.sh +276 -0
  81. package/scripts/codex-birth-install.sh +414 -0
  82. package/scripts/codex-birth-uninstall.sh +170 -0
  83. package/scripts/codex-mcp-config.py +435 -0
  84. package/scripts/codex-statusline-config.py +434 -0
  85. package/scripts/codex_toml_io.py +532 -0
  86. package/scripts/lib/codex-fresh-live-protocol.ts +113 -0
  87. package/scripts/lib/codex-fresh-source-receipts.ts +399 -0
  88. package/scripts/lib/launch-receipt-windows.ts +46 -0
  89. package/scripts/lib/tmux-coordinate-row.ts +58 -0
  90. package/scripts/meta-bridge-fresh-cut.ts +6 -1
  91. package/scripts/mutants/codex-native.json +838 -0
  92. package/scripts/mutants/compaction-send-guard.json +103 -0
  93. package/scripts/mutants/entwurf-peers.json +19 -0
  94. package/scripts/mutants/mux-fresh-call.json +19 -8
  95. package/scripts/mutants/omp-fresh.json +6 -4
  96. package/scripts/mutants/pi-package-ownership.json +26 -0
  97. package/scripts/mutants/release-gate.json +13 -0
  98. package/scripts/mutants/v2-surface.json +53 -1
  99. package/scripts/pi_settings_io.py +3 -1
  100. package/scripts/raw-async-delivery/README.md +2 -1
  101. package/scripts/raw-codex-measure/README.md +114 -46
  102. package/scripts/register-pi-package.py +38 -30
  103. package/scripts/register-pi-provider.py +3 -2
  104. package/scripts/smoke-acp-raw-turn-live.ts +1 -1
  105. package/scripts/smoke-agy-native-push-live.ts +3 -1
  106. package/scripts/smoke-codex-birth.sh +347 -0
  107. package/scripts/smoke-codex-config-state.sh +511 -0
  108. package/scripts/smoke-codex-fresh-live.ts +1186 -0
  109. package/scripts/smoke-codex-native-push-live.ts +75 -0
  110. package/scripts/smoke-setup-verdict.sh +123 -10
  111. package/scripts/smoke-user-scope-citizen.sh +199 -71
  112. package/scripts/tsconfig.json +1 -0
package/run.sh CHANGED
@@ -119,8 +119,8 @@ run_vitest() {
119
119
  usage() {
120
120
  cat <<'EOF'
121
121
  Usage:
122
- ./run.sh setup [project-dir] # ONE presence-driven composition (#86): mode-first (source bootstrap only on a checkout), then per-component PASS/SKIP/FAIL for pi (presence+floor)/claude/agy/copilot (all four units: birth→MCP→receiver→footer, independently)/omp (four units: birthMCP→tools.xdev setting→receiver, independently) + stable dev bins + v2 install smoke; absent harness = zero-state SKIP, detected-incomplete = named FAIL + nonzero exit. Never installs a harness or touches credentials
123
- ./run.sh release-gate [project-dir] [--cut] [--allow-skip-gemini] # SINGLE release gate: full static (pnpm run check:full) + the v2-native live gates (v2 matrix-live, check-bridge, doctor-pi-provider, RGG) + the ACP plugin acceptance floor (12 LIVE smokes: socket-citizen/raw-turn/overlay/provider/session-reuse/carrier-augment/memory-containment/rgg/mcp/skill/bundled-mcp/v2-send) + the one surviving axis the aggregate used to omit silently (claude-native-resume; Cortex stays a documented on-demand direct call) + the cross-harness delivery chain (smoke-entwurf-chain-live). TWO-TIER summary: MUST (release-blocking, owns the exit code — "green" applies here) + BEHAVIOR (advisory, non-blocking: RGG positives model-in-loop turn). STEP OUTCOME protocol: every step is INVOKED and reports its own PASS / SKIP (exit 97, a prerequisite it does not have) / FAIL — a skip is never counted as a pass. Without --cut this is the unattended diagnostic (SKIPs reported, exit 0). WITH --cut it is read as release acceptance and ANY MUST SKIP is red, which is what makes "a CUT needs LIVE=1, SKIP=0" executable instead of prose. --allow-skip-gemini accepted-but-ignored (back-compat). final cut authorization is GLG's.
122
+ ./run.sh setup [project-dir] # ONE presence-driven composition (#86): per-component PASS/SKIP/FAIL for pi/claude/agy/copilot/omp/codex + stable dev bins + v2 install smoke. Codex birth/MCP/status-line atoms are composed at user scope; setup stays non-green until the operator trusts the birth declaration once in visible Codex. Absent harness = zero-state SKIP, detected-incomplete = named FAIL + nonzero exit. Never installs a harness, starts a daemon, touches credentials, or writes the vendor trust receipt
123
+ ./run.sh release-gate [project-dir] [--cut] [--allow-skip-gemini] # SINGLE release gate: full static (pnpm run check:full) + the v2-native live gates (v2 matrix-live, check-bridge, doctor-pi-provider, RGG) + the ACP plugin acceptance floor (12 LIVE smokes: socket-citizen/raw-turn/overlay/provider/session-reuse/carrier-augment/memory-containment/rgg/mcp/skill/bundled-mcp/v2-send) + the one surviving axis the aggregate used to omit silently (claude-native-resume; Cortex stays a documented on-demand direct call) + the cross-harness delivery chain (smoke-entwurf-chain-live) + the Codex first-admission Pi -> visible Codex -> visible Pi receipt chain (smoke-codex-fresh-live; initial Pi outside the existing exact `codex` home, explicit operator-owned app-server PID inside it). TWO-TIER summary: MUST (release-blocking, owns the exit code — "green" applies here) + BEHAVIOR (advisory, non-blocking: RGG positives model-in-loop turn). STEP OUTCOME protocol: every step is INVOKED and reports its own PASS / SKIP (exit 97, a prerequisite it does not have) / FAIL — a skip is never counted as a pass. Without --cut this is the unattended diagnostic (SKIPs reported, exit 0). WITH --cut it is read as release acceptance and ANY MUST SKIP is red, which is what makes "a CUT needs LIVE=1, SKIP=0" executable instead of prose. --allow-skip-gemini accepted-but-ignored (back-compat). final cut authorization is GLG's.
124
124
  ./run.sh check-bridge # entwurf-bridge direct MCP smoke + protocol/negative-path test.sh (live substrate = v2 live smokes)
125
125
  ./run.sh check-entwurf-bridge-boot # deterministic gate (5d-5-pre, G1a/G1b/G1e/G1f, IN pnpm run check:full): boot start.sh under strip-types + assert v2 fence graph loads + entwurf_v2 and entwurf_resume_call registered/schema + the tools/list surface is EXACTLY the seven shipped garden verbs; tools/list only, no auth/side-effect
126
126
  ./run.sh check-entwurf-bridge-pi-free # deterministic gate (0.12.1 A, IN pnpm check): static — bridge index eager value-import closure must carry no @earendil-works/pi-* (type-only + dynamic import excluded); proves the meta-bridge boots pi-free
@@ -137,6 +137,13 @@ Usage:
137
137
  ./run.sh check-copilot-statusline # #82 Copilot custom-footer renderer. session_id → ready/?/gid + rail `cop`; exit 0. IN pnpm check. No Copilot, no model turn
138
138
  ./run.sh check-entwurf-capabilities # deterministic gate (0.11 Stage 0 step 3C): backend capability registry (pi/entwurf-capabilities.json) — coverage==META_CITIZEN_BACKENDS + agrees with live META_BACKEND_DESCRIPTORS + strict keyset, no API
139
139
  ./run.sh check-omp-fresh-preflight # #87 C: the OMP fresh preflight reproduces omp_agent_dir and the tools.xdev read in TS (it runs from two emit depths and cannot call a sibling script). This drives the SHIPPED shell/python leaves over the same inputs — refusals included — and requires the TS half to agree, so the reproduction cannot silently drift from the installer's own oracle
140
+ ./run.sh check-codex-sender-identity # Codex tools/call _meta triple agreement + addressable record join + cross-carrier conflict refusal
141
+ ./run.sh check-codex-bridge-identity # real MCP bridge request identity for entwurf_self/v2/fresh; malformed or conflicting _meta fails loud
142
+ ./run.sh check-codex-native-push # app-server WebSocket-over-UDS probe + measured one-shot codex queue delivery; no replay
143
+ ./run.sh check-codex-birth-hook # sandboxed payload + declaration: first-turn V3 birth, one record per thread, top-level event predicate, no markers
144
+ ./run.sh check-codex-fresh-preflight # pre-mutation birth/MCP/thread-title/default-app-server readiness
145
+ ./run.sh smoke-codex-config-state # sandboxed MCP + status-line ownership/install/doctor/inverse lifecycle
146
+ ./run.sh smoke-codex-birth # sandboxed birth unit: exact trust identity, the vendor trust receipt read (never written), config.toml untouched, foreign/edited/symlink refusals, drift-refusing inverse
140
147
  ./run.sh check-harness-admission-parity # #87 C: the EDGE the two closed parity loops never had. Every citizen backend is fresh-openable or a declared pre-#82 legacy admission whose exception a reader finds in DELIVERY.md — so a post-contract harness that mints records but cannot be opened by entwurf_fresh_call blocks the release package instead of only carrying an `unsupported` note (docs/adding-a-harness.md step 9)
141
148
  ./run.sh check-capability-bundle-reach # deterministic gate (IN pnpm check): re-ask EVERY shipped copy of meta-session (source + bridge bundle emit) whether metaCapabilitiesFilePath() reaches the registry — the artifact-depth check the source-path gates cannot make; needs a built dist, missing dist FAILS
142
149
  ./run.sh smoke-pi-attach # deterministic gate (#50 C2 checkpoint + C3 ACP tail): a pi session attaches as a V3 meta-record citizen (backend:"pi"), the gardenId is the RECORD's not pi's session id, the control socket is keyed on it, a re-open ATTACHES to the same address (never a second mint), the BUILT DIST ENTRY driven over MCP stdio lists the citizen + delivers entwurf_v2 to that socket with an RPC ack, and the ACP identity chain lands a send AS the host record (enrichMcpServersWithEnvelope env → bridge sender = host gardenId). mkdtemp-isolated; the live store is never read
@@ -149,12 +156,13 @@ Usage:
149
156
  ./run.sh check-meta-capability-source # deterministic gate (0.11 Stage 0 step 3D-3): capability-source cut-over — mint/parse read wakeMode/deliveryLevel from the registry (metaCapabilityFor, registry-driven via injection), not META_BACKEND_DESCRIPTORS; behaviour-preserving (registry ≡ const); the record.delivery slot 3D-3 preserved was deleted by 3D-4, no API
150
157
  ./run.sh check-socket-probe # deterministic gate (0.11 Stage 0, F3): three-valued control-socket liveness (alive|dead|indeterminate) — GC reclaims dead only, indeterminate survives; pure classify + 2-socket integration, no API
151
158
  ./run.sh check-project-trust-handler # deterministic gate (0.11 Stage 0, Trust 2층): project_trust handler — decideProjectTrust matrix (escape=inherited-false+interactive+trust-here→{yes,remember:true}; non-interactive→undecided; never undefined) + adapter single-writer, fake prompt, no UI
152
- ./run.sh check-entwurf-v2-contract # deterministic gate (0.11 Stage 0 step 4-pre, 동결결정 10 + Fable R1-R5): FROZEN entwurf_v2 contract — R1 control-socket domain (currently pi; claude/codex/agy=unsupported, not folded), 3-cell intent×liveness table (single verdict, 1 allow/2 reject after the visible-first cut), N1 indeterminate-no-spawn, R3 table↔receipt round-trip, R5 taxonomy, schema↔types drift; pure, no API
159
+ ./run.sh check-entwurf-v2-contract # FROZEN entwurf_v2 contract — control-socket liveness domain is currently pi; self-fetch/native-push citizens are out of that domain, not globally unsupported; pure, no IO
153
160
  ./run.sh check-entwurf-v2-lock # deterministic gate (0.11 Stage 0 step 5a, 버킷 B F2): per-gid dispatch LOCK primitive — openSync wx atomic acquire, second-acquire=target-locked conflict (holder JSON for human cleanup), nonce-owned release (successor survives late release), stale reclaim same-host+ESRCH-only (EPERM/remote/alive/unknown fail-closed), empty/corrupt=conflict not auto-deleted, F2-P1 malformed gid throws; real temp dir, deps injected
154
161
  ./run.sh check-entwurf-v2-decider # deterministic gate (0.11 Stage 0 step 5b): PURE dispatch decider decideDispatch — frozen 7-step order over injected fakes, lock acquire+release tracked so reject⇒no-plan-no-lock proven; pre-probe rejects observedLiveness=null, send/resume execute keep lock + mailbox no-lock (?7), resume plan no mode/provider/model, invalid gid throws (F2-P1); pure, no IO
155
162
  ./run.sh check-entwurf-v2-matrix # deterministic gate (0.11 Stage 0 step 5d-5 a): REACHABILITY + LOCK SSOT table — drives REAL decideDispatch over fakes, fixes every (target kind → transport → lock class) cell as one table (control-socket/meta-mailbox/native-push + bad-target/conflict/locked/undeliverable/dormant/indeterminate rejects), coverage pass fails on a dropped cell; thin coverage not a decider re-impl; pure, no IO
156
163
  ./run.sh check-entwurf-v2-release # deterministic gate (0.11 Stage 0 step 5c-1): PURE release-policy reducer (decideReleasePolicy + reduceRelease) — the spawn-observation policy and its four release events went with the transport in the visible-first cut, so the shipped machine is meta-mailbox=never release (no lock) + control-socket=release once on send-final; decideReleasePolicy still enforces the lock-nullness invariant; pure, no IO
157
164
  ./run.sh check-entwurf-v2-send # deterministic gate (0.11 Stage 0 step 5c-2a): control-socket SEND hand (executeControlSocketSend) wiring transport IO onto the 5c-1 reducer — ack→sent, in-band reject→rejected (no fallback), dead→same-lock one-shot re-resolve (control retry / mailbox enqueue), indeterminate→failed+rethrow with NO fallback (no double-delivery); release exactly once, releaseLock throw never masks the send error; IO-via-dep
165
+ ./run.sh check-compaction-send-guard # deterministic gate (#111): control-socket send during Pi compaction — event-armed refuse compacting, quiet unknown non-idle refuse busy (ctx.signal is not isStreaming); no pi.sendMessage, no delivered:true; idle/live-run steer/followUp preserved; pure, no IO
158
166
  ./run.sh check-entwurf-v2-send-fallback # deterministic gate (0.11 Stage 0 step 5c-2b): same-lock re-resolve RESOLVER (resolveDeadControlSendFallback) — fire-and-forget re-resolve: alive→control retry, dead→reject (nothing is ever launched), indeterminate→reject, unsupported+deliverable→mailbox plan, undeliverable/bad-target/conflict→reject; resolver never releases, mis-wire fails loud, inspect/probe throws propagate; no IO (fakes)
159
167
  ./run.sh check-entwurf-v2-runner # deterministic gate (0.11 Stage 0 step 5d-1): execute-router (executeDispatch) routing an already-decided DispatchDecision to its 5c transport hand → one outcome-rich EntwurfV2RunResult. reject→rejected (no hand) / control/mailbox→matching hand with decision.lock verbatim / N3 rejectReason carried / N1 SendDeliveredReleaseFailedError→execution-failed{finalizedOutcome,releaseFailed,retrySafe:false}; fake hands, no IO
160
168
  ./run.sh check-entwurf-v2-mailbox # deterministic gate (0.11 Stage 0 step 5c-4, LAST 5c transport slice): ENQUEUE-ONLY meta-mailbox SEND body (executeMetaMailboxSend) + production sendViaMailbox adapter — sender→formatMetaMailboxBody with plan.wantsReply threaded (divergence from legacy hard false), sender absent→raw plan.message, enqueue opts EXACTLY {gardenId,body,sessionsDir,mailboxDir}, enqueue throw PROPAGATES (no success:false fold — mailbox has no in-band refuse); adapter NEVER touches lock (release is the hand's job); source guard: no release/routing seam
@@ -166,12 +174,14 @@ Usage:
166
174
  ./run.sh check-resume-launch-identity # deterministic gate for resume-launch-identity.ts, the record-authoritative launch-identity leaf preserved through the visible-first cut (spawn-bg and all its callers are gone; this leaf answers "which being is this, and which conversation is theirs"). Temp meta-store fixture: gardenId→record.transcriptPath happy path with header cwd/provider/model; C3 integrity (header id ≠ record.nativeSessionId → refused, never resumed); #52 ADDRESSABLE read (a gid that no longer holds its nativeSessionId alone is refused from EITHER side — the plain targeted read would resume one transcript twice under two locks); cause fidelity per impossible resume incl. the F7 pin (recorded-but-deleted transcript → MISSING, not "no recorded model"); header↔gate SSOT. No spawn/socket/timer
167
175
  ./run.sh smoke-entwurf-v2-matrix-live # LIVE sentinel (0.11 Stage 0 step 5d-5, D4-b) — OUT of pnpm check, needs LIVE=1. Drives REAL production runEntwurfV2 deps over REAL OS objects, 4 cells: C1 control-socket (real pi --entwurf-control resident → RPC send → lock acquire→release ×1), C1b record-less socket (#50 C4: live record-less pi → EVERY intent rejected pre-probe record-less-socket, no lock, rendered hint names record authority + fresh-cut), C2 meta-mailbox deliverable (armed self-fetch citizen → real .msg enqueue, lock-free), C3 meta-mailbox guard (no armed receiver → reject, no garbage). Model-in-loop OUT (transport/lock/enqueue gate, GPT Q2); negative/timeout stay deterministic. Model: ENTWURF_LIVE_TARGET=<provider>/<model> (default openai-codex/gpt-5.6-luna). LIVE=1 ./run.sh smoke-entwurf-v2-matrix-live
168
176
  ./run.sh smoke-agy-native-push-live # 봉인 8 LIVE acceptance for the native-push (agy) rail — OUT of pnpm check, needs LIVE=1 + AGY_CONVERSATION_ID (a live agy conversation). Drives the REAL antigravity adapter + register core + runEntwurfV2 (production deps): doctor-static preflight (dangling→FAIL, the ③ gate), probe route, register create/attach idempotency, fire→native-push delivered, post-send re-probe (D7 partial), bogus-conv→native-push-probe-indeterminate. Meta-store isolated to a temp dir (only the agy round-trip is real; no real-store residue). COST FENCE: open that agy conversation on gemini-3.6-flash (free account) — never a Pro tier; entwurf never selects the agy model and no assertion reads it. LIVE=1 AGY_CONVERSATION_ID=<convId> ./run.sh smoke-agy-native-push-live
177
+ ./run.sh smoke-codex-native-push-live # on-demand LIVE acceptance for Codex native-push — needs LIVE=1 + CODEX_LIVE_THREAD_ID loaded by a visible TUI attached to the default app-server; probes the real UDS, routes through production runEntwurfV2, queues one exact-token turn without retry, re-probes, and leaves the visible TUI to show the model result
178
+ ./run.sh smoke-codex-fresh-live # #95 first-admission RELEASE MUST: record-backed receipt fixture -> PUBLIC initial Pi fresh/callback OUTSIDE the Codex home -> Pi PUBLIC omitted-placement Codex fresh/exact callback INSIDE exact existing `codex` -> Pi-to-Codex addressed native-push v2 -> Codex PUBLIC omitted-placement fresh Pi/exact callback IN that home -> Codex final v2 evidence. Needs LIVE=1 + ENTWURF_CODEX_APP_SERVER_PID + ENTWURF_CODEX_FRESH_MODEL + ENTWURF_CODEX_FRESH_PI_MODEL. The PID names the operator-owned app-server inside `codex`; the smoke checks/prints all four coordinates, never starts/stops/guesses the app-server/session, never reads screen text, interrupts only its exact still-running Codex turn, and cleans only receipt-named window ids
169
179
  ./run.sh smoke-mux-lifecycle-live # RELEASE MUST integrated LIVE lifecycle acceptance for mux, through the REAL MCP surface — OUT of pnpm check, needs LIVE=1 and spends model turns (two pi siblings: native + recorded-ACP provider, each resumed once; one Claude Code sibling). tools/call fresh_call -> nonce callback sender envelope -> v2 control send landing in the sibling's own transcript -> resume_call REFUSED while live (window count unchanged) -> stable-handle close (pane gone, socket dead, record kept) -> dormant delivery refused honestly -> public entwurf_resume_call with LAUNCH and OBSERVATION receipts kept apart, same-gid socket alive, zero new citizens, zero lock residue, resumed pane_start_path == RECORD cwd (separate tmux query), transcript byte-identical across the resume -> v2 recall of the pre-close fact. claude-code resume refused target-not-pi, no window opened and no lock residue. LIVE=1 ./run.sh smoke-mux-lifecycle-live
170
180
  ./run.sh check-entwurf-facts # deterministic gate (0.11 Stage 0 step 4, fact-provider slice 1+2): PURE PeerFact core + resolveFactList union — R1 out-of-domain→unsupported, R3b socket-domain 4-value, facts-only keyset; union: PeerFact + RecordLessSocketFact by gardenId (#50 C4: record-less socket = diagnostic subject, gid+liveness only), dormant→dead, F3 indeterminate preserved, out-of-socket-domain+socket fail-loud; pure, no IO
171
181
  ./run.sh check-socket-discovery # deterministic gate (0.11 Stage 0 step 4, fact-provider slice 3): SOCKET-axis scanSocketProbes — probes (dir sockets) ∪ (in-domain citizen canonical paths) 3-valued; dormant citizen no-file → dead (resumable, not unprobed), stall → indeterminate (F3), dir hygiene/dedup/missing-dir + e2e → resolveFactList; readdir/probe injected, no IO
172
182
  ./run.sh check-meta-facts # deterministic gate for the meta-facts projection (#65): drives the REAL CLI — full-record join, parse-before-uniqueness, no-winner duplicates, drift/symlink/invalid-UTF-8 defects in-band, deterministic bytes, exit contract 0/2/3, dispatch+emit reachability
173
183
  ./run.sh check-meta-listing # deterministic gate: META-STORE facts axis — kind-carrying entries; non-regular records are never read, parse/drift become diagnostics, duplicate nativeSessionId quarantines every rival but not unrelated citizens; strict throws / collect partial; pure injected IO
174
- ./run.sh check-entwurf-fact-provider # deterministic gate (0.11 Stage 0 step 4, fact-provider slice 4b): ASSEMBLY listEntwurfFacts — listAllMetaIdentities→scanSocketProbes→pre-quarantine out-of-socket-domain/socket conflicts→resolveFactList(clean)→{facts,diagnostics}; C-원칙: expected corruption (parse/collision)→diagnostics (listing survives), impossible invariant (dup/unprobed)→throw; collision quarantines BOTH PeerFact+socket; deps injected, no IO
184
+ ./run.sh check-entwurf-fact-provider # deterministic gate (0.11 Stage 0 step 4, fact-provider slice 4b): ASSEMBLY listEntwurfFacts — full-store parse/probe/quarantine/resolve stays intact; #112 bounds expensive receiver/transcript observation to the newest 32 rendered rows while older machine rows say unobserved; Q112 pins full payload + exact observer budget; C-원칙 keeps corruption diagnostic and impossible wiring loud; deps injected, no IO
175
185
  ./run.sh check-entwurf-peers-surface # deterministic gate (0.11 Stage 0 step 4, fact-provider slice 4c): MCP entwurf_peers RENDER renderEntwurfPeers (#50 C4) — payload keyset exactly {peers, diagnostics}; FORBIDDEN keys sessions/socketOnly/controlDir/socketPath/count + no .sock in text (socket is transport, never identity); record-less socket = aggregated record-less-socket diagnostic (F8, liveness-keyed message, alive names fresh-cut); NO verb-routing key (JSON deep scan) NOR word (text), diagnostics both surfaces, empty→(none), unsupported shown; WIRING guard: both surfaces call provider+render, getLiveSessions + /entwurf-sessions gone; facts fabricated, no IO
176
186
  ./run.sh check-entwurf-self-address # deterministic gate (SE-1/SE-2 slice 1): self-addressability honesty predicate computeSelfAddressability — pi replyable ⟺ live socket; meta splits by RAIL: self-fetch ⟺ recordBacked ∧ ownerAlive ∧ watchArmed (regression-proof record-present rows), native-push ⟺ recordBacked ∧ probeAlive (separate axis — no mailbox fact may rescue or sink it), unsupplied rail fail-closed; SOURCE GUARD buildStrictPiSenderEnvelope drops hardcoded replyable:true + existsSync-probes socket, entwurf_self renders alive vs expected AND renders the meta rail per-rail (mailbox only inside the self-fetch branch; native-push denies an inbox and gates injection on the probe)
177
187
  ./run.sh check-entwurf-deliverability # deterministic gate (SE-1/SE-2 slice 2c): conversational-mailbox deliverability predicate — computeMetaReceiverActive (recordBacked ∧ ownerAlive ∧ watchArmed) + mailboxConversationalDeliverable (self-fetch AND active); direct-inject pi refused (SE-1), self-fetch dead/unarmed refused (SE-2); self-address shares the same atom
@@ -201,7 +211,7 @@ Usage:
201
211
  ./run.sh smoke-copilot-mcp-state # Copilot MCP install/doctor/inverse regression. Offline/deterministic
202
212
  ./run.sh smoke-omp-bridge-state # OMP birth-extension install/doctor/inverse regression: placement, stale-writer detection, honest inverse, no-state refusal (structurally VALID as well as foreign), symlink refusal, ambiguous-agent-dir refusal (ledger M6), and a poisoned PI_CODING_AGENT_DIR that attracts no artifact. Fully sandboxed HOME/PI/XDG. Offline/deterministic
203
213
  ./run.sh smoke-agy-hooks-state # agy PreInvocation birth/sender hook install/doctor/inverse + direct stdin→meta-record regression. Offline/deterministic
204
- ./run.sh smoke-user-scope-citizen # 0.12.6 install-boundary: pi packages[] registration SSOT (register-pi-package.py) — idempotent + preserves unrelated + remove symmetry + fails loud, and the #86 C2 explicit ownership cells (project scope still normalizes ITS OWN stale entries; user scope refuses other owners and only takeover-user-scope moves the shared entry). Offline/hermetic (deps: bash+python3)
214
+ ./run.sh smoke-user-scope-citizen # 0.12.6 install-boundary: pi packages[] registration SSOT (register-pi-package.py) — idempotent + preserves unrelated + remove symmetry + fails loud, and the #86 C2 explicit ownership cells (project scope still normalizes ITS OWN stale entries; user scope refuses other owners and only takeover-user-scope moves the shared entry), plus the #110 self-checkout cells: the real `run.sh install <checkout>` drive leaves the tracked .pi/settings.json byte-identical, writes NO project-scope packages[] self-registration, and REPORTS the skip; cell 15 boots the pinned devDep pi against two stand-in checkouts and requires the --entwurf-control collision to APPEAR before asserting its absence. Offline/hermetic (deps: bash+python3+pinned pi)
205
215
  ./run.sh smoke-meta-prune # 1.0.0 meta-bridge Phase 4: listing-only store janitor regression gate — classify keep/orphan/stale/ambiguous, delete nothing. Offline/deterministic (deps: bash+node)
206
216
  ./run.sh smoke-meta-keyset-guard # 0.10.0 meta-bridge: keyset-owner guard regression — check-keyset-overlap + managed-keys SSOT (disjoint passes, collisions fail). Offline/hermetic (deps: bash+python3)
207
217
  ./run.sh check-meta-manifest-schema # 0.12.2 meta-bridge: CLI-version-INDEPENDENT static guard — plugin manifests pinned to the minimal keyset that validates on the lowest supported Claude (closed-schema regression that broke 0.12.1 install on floor) + desired_mcp installed-vs-clone dual-mode. Offline (deps: python3)
@@ -240,6 +250,15 @@ Usage:
240
250
  ./run.sh uninstall-agy-bridge # 봉인 7: honest inverse of install-agy-bridge from install-state (restore preimage / remove key; refuse if config became a symlink)
241
251
  ./run.sh probe-bridge-command <cmd> [args...] # #81: BOOT the given bridge invocation and require the entwurf MCP tool surface back. `--invocation-json '{"command":"…","args":[],"env":{}}'` preserves a harness config exactly. It waits for a valid initialize response, then sends initialized + tools/list only (no tools/call, lock, record, or delivery). exit 0 = it serves the bridge; 1 = it does not. The pi/agy doctors use this leaf.
242
252
  ./run.sh doctor-agy-bridge # fail-loud doctor: MCP config + exact permission rule + state + live probe label
253
+ ./run.sh install-codex-birth # publish $CODEX_HOME/hooks.json + the helper closure; no root, and the operator trusts the declaration once in their own Codex
254
+ ./run.sh uninstall-codex-birth # exact inverse from the install-state digest inventory; refuses drifted bytes instead of deleting them
255
+ ./run.sh doctor-codex-birth # RUNTIME + UNIT bytes/ownership + the vendor TRUST receipt for this declaration (--unit-only skips the receipt; sandboxes only)
256
+ ./run.sh install-codex-mcp # own only [mcp_servers.entwurf-bridge] in the user's Codex config
257
+ ./run.sh uninstall-codex-mcp # remove/restore only the recorded MCP atom
258
+ ./run.sh doctor-codex-mcp # effective config + ownership + actual bridge boot
259
+ ./run.sh install-codex-statusline # add thread-title to tui.status_line for visible garden ids
260
+ ./run.sh uninstall-codex-statusline # remove only the recorded status-line atom
261
+ ./run.sh doctor-codex-statusline # effective visible-identity config + ownership verdict
243
262
  ./run.sh install-agy-statusline # own the agy statusLine subtree with bare entwurf-agy-statusline; preserve unrelated settings
244
263
  ./run.sh uninstall-agy-statusline # honest inverse from statusline install-state
245
264
  ./run.sh doctor-agy-statusline # fail-loud statusLine config/bin/state doctor + honest live SKIP
@@ -537,7 +556,28 @@ install_local_package() {
537
556
  mkdir -p "$project_dir/.pi"
538
557
  # packages[] registration via the shared SSOT — same is_entwurf_source
539
558
  # predicate + idempotency as user-scope and remove (not a substring match).
540
- python3 "$REPO_DIR/scripts/register-pi-package.py" "$project_dir/.pi/settings.json" "$REPO_DIR"
559
+ #
560
+ # #110: entwurf's OWN checkout is the one project that never gets this entry.
561
+ # pi's package identity is `local:<resolved path>` and dedupePackages collapses
562
+ # only EQUAL identities (package-manager.js getPackageIdentity/dedupePackages),
563
+ # so a project entry naming this checkout and the user-scope entry naming the
564
+ # installed root are the same package only while they are the same directory.
565
+ # In a SECOND checkout (worktree, release scratch) they differ, pi keeps both,
566
+ # and both copies of pi-extensions/entwurf-control.ts declare
567
+ # `--entwurf-control`: detectExtensionConflicts gives the flag to whoever
568
+ # registered first — project scope — and fails the INSTALLED extension while
569
+ # printing the installed path as the loser, which reads exactly backwards.
570
+ # user-scope already owns self-registration from any cwd, so the project entry
571
+ # was only ever a duplicate of it; dropping it removes the second identity in
572
+ # written bytes, the way the tracked `.pi/settings.json` no longer carries one.
573
+ # A FOREIGN project still gets its entry — that is what `entwurf install
574
+ # <project>` is for, and an npm consumer's REPO_DIR is inside node_modules, so
575
+ # it can never take this branch.
576
+ if [ "$(cd -P -- "$project_dir" 2>/dev/null && pwd)" = "$REPO_DIR" ]; then
577
+ echo "install: project-scope packages[] skipped — entwurf's own checkout self-registers through user scope (#110)"
578
+ else
579
+ python3 "$REPO_DIR/scripts/register-pi-package.py" "$project_dir/.pi/settings.json" "$REPO_DIR"
580
+ fi
541
581
  # entwurfProvider.mcpServers.entwurf-bridge (project scope — checkout-local, NO state; #46
542
582
  # Task 2) via the shared register-pi-provider SSOT: normalize the command to the bare stable
543
583
  # bin `entwurf-bridge` (ownership-classified: absent/managed-current/managed-legacy adopt, a
@@ -560,6 +600,38 @@ install_local_package() {
560
600
  _pi_package_state() { echo "${XDG_DATA_HOME:-$HOME/.local/share}/entwurf/pi-package/install-state.json"; }
561
601
  _pi_provider_state() { echo "${XDG_DATA_HOME:-$HOME/.local/share}/entwurf/pi-provider/install-state.json"; }
562
602
 
603
+ _codex_home() { echo "${CODEX_HOME:-$HOME/.codex}"; }
604
+ _codex_config() { echo "$(_codex_home)/config.toml"; }
605
+ _codex_mcp_state() { echo "${XDG_DATA_HOME:-$HOME/.local/share}/entwurf/codex-mcp/install-state.json"; }
606
+ _codex_statusline_state() { echo "${XDG_DATA_HOME:-$HOME/.local/share}/entwurf/codex-statusline/install-state.json"; }
607
+
608
+ codex_mcp() {
609
+ local verb="$1" config state
610
+ config="$(_codex_config)"
611
+ state="$(_codex_mcp_state)"
612
+ case "$verb" in
613
+ install) python3 "$REPO_DIR/scripts/codex-mcp-config.py" install "$config" "entwurf-bridge" "$state" ;;
614
+ uninstall) python3 "$REPO_DIR/scripts/codex-mcp-config.py" uninstall "$state" ;;
615
+ doctor)
616
+ python3 "$REPO_DIR/scripts/codex-mcp-config.py" doctor-static "$config" "entwurf-bridge" "$state"
617
+ local invocation
618
+ invocation="$(python3 "$REPO_DIR/scripts/codex-mcp-config.py" doctor-invocation "$config")"
619
+ run_ts scripts/probe-bridge-command.ts --invocation-json "$invocation"
620
+ ;;
621
+ esac
622
+ }
623
+
624
+ codex_statusline() {
625
+ local verb="$1" config state
626
+ config="$(_codex_config)"
627
+ state="$(_codex_statusline_state)"
628
+ case "$verb" in
629
+ install) python3 "$REPO_DIR/scripts/codex-statusline-config.py" install "$config" "$state" ;;
630
+ uninstall) python3 "$REPO_DIR/scripts/codex-statusline-config.py" uninstall "$state" ;;
631
+ doctor) python3 "$REPO_DIR/scripts/codex-statusline-config.py" doctor-static "$config" "$state" ;;
632
+ esac
633
+ }
634
+
563
635
  register_user_scope_citizen() {
564
636
  local agent_dir="${PI_CODING_AGENT_DIR:-$HOME/.pi/agent}"
565
637
  # Shared idempotent implementation (also driven by smoke-user-scope-citizen).
@@ -707,8 +779,11 @@ remove_local_package() {
707
779
  # install, so remove never over-deletes a look-alike repo (entwurf-notes, …)
708
780
  # that install would never have registered. Same rule, one shape further (#53 B):
709
781
  # register only ever WRITES the absolute path, so a settings-RELATIVE entry naming
710
- # this repo (the committed `".."` in <repo>/.pi/settings.json) is source, not
711
- # install state — remove leaves it and says so instead of editing tracked bytes.
782
+ # this repo is source an operator hand-wrote, not install state — remove leaves it
783
+ # and says so instead of editing bytes it never authored. entwurf's own checkout
784
+ # no longer ships such an entry (#110: user scope owns self-registration), so for
785
+ # THIS repo the call is a reported no-op; the predicate stays because the shape is
786
+ # still writable by hand and still must not be over-deleted.
712
787
  python3 "$REPO_DIR/scripts/register-pi-package.py" "$project_dir/.pi/settings.json" "$REPO_DIR" --remove
713
788
  # entwurfProvider.mcpServers.entwurf-bridge cleanup (project scope) via the shared SSOT: strip
714
789
  # our-managed shapes (the bare stable bin AND the legacy repo start.sh path — a true user
@@ -807,6 +882,18 @@ smoke_omp_fresh_live() {
807
882
  run_ts scripts/smoke-omp-fresh-live.ts
808
883
  }
809
884
 
885
+ smoke_codex_fresh_live() {
886
+ # #95 first-admission RELEASE MUST. The explicit operator-owned app-server PID is the
887
+ # seat authority: validate its Codex app-server argv and /proc TMUX/TMUX_PANE, then use
888
+ # that exact environment for the record-backed receipt fixture and stable-id cleanup.
889
+ # Through public MCP calls: fixture opens visible Pi; Pi opens visible Codex, correlates
890
+ # its exact callback and addresses it through native-push v2; Codex opens visible Pi,
891
+ # correlates that callback, and sends final evidence. The four actor/seat coordinates
892
+ # travel in receipts and Codex/app-server-MCP/outbound-Pi sessions must be identical.
893
+ # LIVE!=1 is the only host-prerequisite SKIP after admission; missing facts FAIL.
894
+ run_ts scripts/smoke-codex-fresh-live.ts
895
+ }
896
+
810
897
  check_omp_fresh_preflight() {
811
898
  # #87 Bundle C. The OMP fresh preflight REPRODUCES two resolvers that already exist
812
899
  # in this repo in another language — omp_agent_dir (scripts/omp-bridge-oracle.sh) and
@@ -1195,6 +1282,14 @@ check_entwurf_control_rpc() {
1195
1282
  run_ts scripts/check-entwurf-control-rpc.ts
1196
1283
  }
1197
1284
 
1285
+ check_compaction_send_guard() {
1286
+ # Deterministic gate for #111: a compacting Pi citizen is neither idle nor an ordinary
1287
+ # streaming turn. Event-armed refuse is `compacting`; quiet unknown non-idle is `busy`
1288
+ # (`ctx.signal` is not isStreaming). Either token: no pi.sendMessage, no delivered:true.
1289
+ # Pure admission predicate + resident wiring/event source guard. No IO.
1290
+ run_ts scripts/check-compaction-send-guard.ts
1291
+ }
1292
+
1198
1293
  check_entwurf_resume_args() {
1199
1294
  # Deterministic gate for the resume-argv SSOT (buildResumePiArgs). S1 replaced the headless
1200
1295
  # shape with the VISIBLE one, measured against the runtime before the consumer was written:
@@ -1309,14 +1404,17 @@ check_mux_fresh_call() {
1309
1404
  # could not see: real bridge boot → tools/list schema/description, Rust-regex-family pattern
1310
1405
  # validity on every emitted pattern (the #62 escape), and the schema riding the actual
1311
1406
  # anthropic-messages request body. No fake tmux: the real-window axis is smoke-mux-fresh-call-live.
1312
- # copilot-fresh-preflight rides here rather than in its own gate: it exists only as
1313
- # freshCall's pre-mutation branch (#82 RAIL 9), and splitting it would let the two halves
1314
- # of one refusal be certified in different runs.
1407
+ # copilot-fresh-preflight and codex-fresh-preflight ride here rather than in
1408
+ # separate gates: each exists only as freshCall's pre-mutation branch, and
1409
+ # splitting one would let the two halves of a refusal be certified separately.
1315
1410
  # omp-fresh-bootstrap.contract rides here for the same reason and one more: it is the ONLY
1316
1411
  # place the launcher's payload and the installed birth extension's decoder are read together.
1317
1412
  # They ship in different directories and cannot import each other (#87 Bundle C), so this
1318
1413
  # lane is what keeps a deliberate duplication from becoming drift.
1319
- run_vitest test/mux-fresh-call.test.ts test/copilot-fresh-preflight.test.ts test/fresh-call-surfaces.contract.test.ts test/fresh-call-provider.contract.test.ts test/omp-fresh-bootstrap.contract.test.ts
1414
+ # tmux-coordinate-row rides here because it is the parse every tmux COORDINATE in this lane's
1415
+ # LIVE siblings depends on, and because it is the only half of a measured LIVE failure that a
1416
+ # deterministic gate can own: the row is the input, so no tmux and no model turn is needed.
1417
+ run_vitest test/mux-fresh-call.test.ts test/copilot-fresh-preflight.test.ts test/codex-fresh-preflight.test.ts test/fresh-call-surfaces.contract.test.ts test/fresh-call-provider.contract.test.ts test/omp-fresh-bootstrap.contract.test.ts test/tmux-coordinate-row.test.ts test/codex-fresh-live-protocol.test.ts
1320
1418
  }
1321
1419
 
1322
1420
  smoke_mux_fresh_call_live() {
@@ -1677,11 +1775,13 @@ check_entwurf_fact_provider() {
1677
1775
  # Deterministic gate for 0.11 Stage 0 step 4 (fact-provider slice 4b): the
1678
1776
  # ASSEMBLY layer listEntwurfFacts. listAllMetaIdentities → scanSocketProbes →
1679
1777
  # pre-quarantine out-of-socket-domain/socket conflicts → resolveFactList(clean) →
1680
- # {facts, diagnostics}. Throw-vs-diagnostics policy (GPT힣 C-원칙): expected
1681
- # corruption (parse failure / gardenId↔socket collision) diagnostics, listing
1682
- # survives; impossible wiring invariant (resolveFactList duplicate/unprobed)
1683
- # throw, never swallowed. A collision quarantines BOTH the PeerFact and the
1684
- # socket (gid is the universal address). meta + socket deps injected, no IO.
1778
+ # {facts, diagnostics}. #112 keeps the whole-store parse/probe/diagnostic/payload
1779
+ # while running expensive receiver/transcript observation for exactly the newest
1780
+ # 32 human-rendered rows; older payload rows say unobserved. Q112 pins that budget.
1781
+ # Throw-vs-diagnostics policy (GPT힣 C-원칙): expected corruption (parse failure /
1782
+ # gardenId↔socket collision) diagnostics, listing survives; impossible wiring
1783
+ # invariant (resolveFactList duplicate/unprobed) → throw, never swallowed. A
1784
+ # collision quarantines BOTH the PeerFact and socket. deps injected, no IO.
1685
1785
  run_ts scripts/check-entwurf-fact-provider.ts
1686
1786
  }
1687
1787
 
@@ -1883,7 +1983,7 @@ assert.equal(peerDepTui, expectedPeer,
1883
1983
  // read, and sat two bumps stale (`>=0.84.3 <0.85` at a 0.85.1 pin) precisely
1884
1984
  // because it was outside this list. Its declaration is a plain range, so the
1885
1985
  // range scan below binds it — no new PROSE_DECL is needed for it.
1886
- const BASELINE_DOCS = ['AGENTS.md', 'README.md', 'ROADMAP.md', 'docs/setup-clean-host.md', 'demo/README.md', 'VERIFY.md'];
1986
+ const BASELINE_DOCS = ['README.md', 'ROADMAP.md', 'docs/setup-clean-host.md', 'demo/README.md', 'VERIFY.md'];
1887
1987
  let rangeDecls = 0, exactDecls = 0;
1888
1988
  for (const file of BASELINE_DOCS) {
1889
1989
  const text = readFileSync(file, 'utf8');
@@ -1908,7 +2008,6 @@ const PROSE_DECLS = [
1908
2008
  ['demo/README.md', /current floor (\d+\.\d+\.\d+)/, 'current floor <version>'],
1909
2009
  ['ROADMAP.md', /\bpi (\d+\.\d+\.\d+) fence\b/, 'pi <version> fence'],
1910
2010
  ['ROADMAP.md', /floor = \*\*(\d+\.\d+\.\d+)\*\*/, 'floor = **<version>**'],
1911
- ['AGENTS.md', /devDep exact `(\d+\.\d+\.\d+)`/, 'devDep exact `<version>`'],
1912
2011
  ];
1913
2012
  for (const [file, re, shape] of PROSE_DECLS) {
1914
2013
  const m = readFileSync(file, 'utf8').match(re);
@@ -2060,7 +2159,7 @@ check_claude_floor_coherence() {
2060
2159
  # DERIVE it at runtime through scripts/meta-bridge-claude-floor.sh, so they carry no
2061
2160
  # literal to drift — that shared file is itself part of the contract and is asserted
2062
2161
  # below. What remains are the human-facing declarations (the launcher's refusal
2063
- # message, docs, AGENTS) plus every fake `claude` CLI stub the gates spawn: a stub
2162
+ # message and docs) plus every fake `claude` CLI stub the gates spawn: a stub
2064
2163
  # that reports a version BELOW the floor would make the doctor legitimately refuse
2065
2164
  # its own fixture, so those are bound too — as `>=`, not equality, since a stub may
2066
2165
  # honestly claim a newer version.
@@ -2100,7 +2199,6 @@ const SITES = [
2100
2199
  ['pi/meta-bridge/entwurf-meta-receive/scripts/hook-launch.sh', /Claude Code >= (\d+\.\d+\.\d+)/g, 1, 'launcher refusal message'],
2101
2200
  ['scripts/meta-bridge-claude-floor.sh', /THE FLOOR IS (\d+\.\d+\.\d+)/g, 1, 'floor helper rationale header'],
2102
2201
  ['docs/setup-clean-host.md', /`>=(\d+\.\d+\.\d+)`\*\* — the exec-form hook floor/g, 1, 'clean-host pin matrix'],
2103
- ['AGENTS.md', /Claude Code `>=(\d+\.\d+\.\d+)`/g, 1, 'AGENTS rule 14'],
2104
2202
  ['README.md', /supported floor `>=(\d+\.\d+\.\d+)`/g, 1, 'README doctor description'],
2105
2203
  ['scripts/check-hook-launch-topology.ts', /\/(\d+)\\\.(\d+)\\\.(\d+)\//g, 1, 'topology gate floor assertion'],
2106
2204
  ];
@@ -3764,10 +3862,10 @@ sys.exit(0 if any(isinstance(s,str) and s.rstrip('/')== '$npm2_pkg' for s in src
3764
3862
  # SAME installed candidate, on a FRESH sandbox home with every harness absent.
3765
3863
  # This is the first living consumer of package-installed `entwurf setup`:
3766
3864
  # mode=installed decided by name before anything else, NO pnpm bootstrap inside
3767
- # node_modules, pi/claude/agy/copilot/omp explicit zero-state SKIPs, stable bins
3768
- # PASS as npm-provided, core bridge boundary PASS, computed green, zero
3769
- # harness/auth writes. Harness probes are pinned absent via the FIVE
3770
- # PI_BIN/CLAUDE_BIN/AGY_BIN/COPILOT_BIN/OMP_BIN seams so the gate host's real
3865
+ # node_modules, every harness explicit zero-state SKIP, stable bins PASS as
3866
+ # npm-provided, core bridge boundary PASS, computed green, zero
3867
+ # harness/auth writes. Harness probes are pinned absent via the SIX
3868
+ # PI_BIN/CLAUDE_BIN/AGY_BIN/COPILOT_BIN/OMP_BIN/CODEX_BIN seams so the gate host's real
3771
3869
  # harnesses never leak in.
3772
3870
  #
3773
3871
  # OMP_BIN was MISSING here from the 0.16.0 OMP admission until 2026-09-06, while
@@ -3779,7 +3877,7 @@ sys.exit(0 if any(isinstance(s,str) and s.rstrip('/')== '$npm2_pkg' for s in src
3779
3877
  # ambiguous. Nothing was wrong with the product; the fixture was. It stayed
3780
3878
  # invisible because CI runners carry no omp AND `release_gate()` does not run
3781
3879
  # this gate (it is prepublishOnly + the CI install-surface job). The skip probe
3782
- # below now requires FIVE zero-states, so a sixth harness cannot be admitted
3880
+ # below now requires SIX zero-states, so a seventh harness cannot be admitted
3783
3881
  # while this seam is quietly left behind again.
3784
3882
  local setup_home="$npm_tmp/setuphome" setup_proj="$npm_tmp/setupproj" setup_out setup_rc setup_auth
3785
3883
  mkdir -p "$setup_home/.pi/agent" "$setup_proj"
@@ -3791,7 +3889,7 @@ sys.exit(0 if any(isinstance(s,str) and s.rstrip('/')== '$npm2_pkg' for s in src
3791
3889
  setup_out=$(HOME="$setup_home" XDG_DATA_HOME="$setup_home/.local/share" XDG_STATE_HOME="$setup_home/.local/state" \
3792
3890
  XDG_CACHE_HOME="$setup_home/.cache" XDG_CONFIG_HOME="$setup_home/.config" PI_CODING_AGENT_DIR="$setup_home/.pi/agent" \
3793
3891
  PI_BIN="$npm_tmp/definitely-absent" CLAUDE_BIN="$npm_tmp/definitely-absent" AGY_BIN="$npm_tmp/definitely-absent" \
3794
- COPILOT_BIN="$npm_tmp/definitely-absent" OMP_BIN="$npm_tmp/definitely-absent" \
3892
+ COPILOT_BIN="$npm_tmp/definitely-absent" OMP_BIN="$npm_tmp/definitely-absent" CODEX_BIN="$npm_tmp/definitely-absent" \
3795
3893
  "$npmroot/node_modules/.bin/entwurf" setup "$setup_proj" 2>&1)
3796
3894
  setup_rc=$?
3797
3895
  set -e
@@ -3811,9 +3909,9 @@ sys.exit(0 if any(isinstance(s,str) and s.rstrip('/')== '$npm2_pkg' for s in src
3811
3909
  return 1
3812
3910
  fi
3813
3911
  local skip_probe
3814
- for skip_probe in "pi: SKIP" "claude: SKIP" "agy: SKIP" "copilot: SKIP" "omp: SKIP"; do
3912
+ for skip_probe in "pi: SKIP" "claude: SKIP" "agy: SKIP" "copilot: SKIP" "omp: SKIP" "codex: SKIP"; do
3815
3913
  if ! grep -q "$skip_probe" <<<"$setup_out"; then
3816
- fail "[check-pack-install] installed all-absent setup missing explicit zero-state '$skip_probe':"
3914
+ fail "[QK:CODEX-PACK-SETUP-ZERO-STATE] [check-pack-install] installed all-absent setup missing explicit zero-state '$skip_probe':"
3817
3915
  echo "$setup_out" | tail -25 | sed 's/^/ /' >&2
3818
3916
  return 1
3819
3917
  fi
@@ -3828,7 +3926,7 @@ sys.exit(0 if any(isinstance(s,str) and s.rstrip('/')== '$npm2_pkg' for s in src
3828
3926
  echo "$setup_out" | tail -25 | sed 's/^/ /' >&2
3829
3927
  return 1
3830
3928
  fi
3831
- if [ -e "$setup_proj/.pi" ] || [ -e "$setup_home/.pi/agent/settings.json" ] || [ -e "$setup_home/.gemini" ] || [ -e "$setup_home/.copilot" ]; then
3929
+ if [ -e "$setup_proj/.pi" ] || [ -e "$setup_home/.pi/agent/settings.json" ] || [ -e "$setup_home/.gemini" ] || [ -e "$setup_home/.copilot" ] || [ -e "$setup_home/.codex" ]; then
3832
3930
  fail "[check-pack-install] installed all-absent setup wrote harness state (must be zero-write)"
3833
3931
  return 1
3834
3932
  fi
@@ -3836,7 +3934,7 @@ sys.exit(0 if any(isinstance(s,str) and s.rstrip('/')== '$npm2_pkg' for s in src
3836
3934
  fail "[check-pack-install] installed setup touched the credential store (byte drift or .bak)"
3837
3935
  return 1
3838
3936
  fi
3839
- echo "[check-pack-install] installed all-absent setup pass (mode-first, no bootstrap, 5x SKIP, bins npm-provided, core PASS, computed green, zero writes)"
3937
+ echo "[check-pack-install] installed all-absent setup pass (mode-first, no bootstrap, 6x SKIP, bins npm-provided, core PASS, computed green, zero writes)"
3840
3938
 
3841
3939
  # Installed copilot-present aggregate `setup` row (#86 C3b): the SAME consumer
3842
3940
  # bin on a fresh sandbox home, with the shared fake vendor
@@ -3862,7 +3960,7 @@ sys.exit(0 if any(isinstance(s,str) and s.rstrip('/')== '$npm2_pkg' for s in src
3862
3960
  setup_out=$(HOME="$cop_setup_home" XDG_DATA_HOME="$cop_setup_home/.local/share" XDG_STATE_HOME="$cop_setup_home/.local/state" \
3863
3961
  XDG_CACHE_HOME="$cop_setup_home/.cache" XDG_CONFIG_HOME="$cop_setup_home/.config" PI_CODING_AGENT_DIR="$cop_setup_home/.pi/agent" \
3864
3962
  PI_BIN="$npm_tmp/definitely-absent" CLAUDE_BIN="$npm_tmp/definitely-absent" AGY_BIN="$npm_tmp/definitely-absent" \
3865
- OMP_BIN="$npm_tmp/definitely-absent" \
3963
+ OMP_BIN="$npm_tmp/definitely-absent" CODEX_BIN="$npm_tmp/definitely-absent" \
3866
3964
  COPILOT_BIN="$cop_fake/copilot" PATH="$cop_fake:$npmroot/node_modules/.bin:$PATH" \
3867
3965
  "$npmroot/node_modules/.bin/entwurf" setup "$cop_setup_proj" 2>&1)
3868
3966
  setup_rc=$?
@@ -5179,6 +5277,43 @@ setup_all() {
5179
5277
  fi
5180
5278
  fi
5181
5279
 
5280
+
5281
+ # ── codex ── Entwurf owns three configuration atoms, never the harness, its app-server, or
5282
+ # the operator's trust decision. The birth unit is now setup's to install: its paths are the
5283
+ # operator's own, so nothing here needs root — but the VENDOR still has to agree to run the
5284
+ # hook, and only the operator can answer that, once, in their own visible Codex. So a first
5285
+ # setup on a Codex host writes the bytes and is honestly NON-GREEN until that answer exists.
5286
+ # It never writes `[hooks.state]`: forging the receipt in a file we already own would turn a
5287
+ # security prompt into a silent install.
5288
+ local codex_rc
5289
+ if ! command -v "${CODEX_BIN:-codex}" >/dev/null 2>&1; then
5290
+ setup_result codex SKIP "codex not on PATH — zero Codex wiring written"
5291
+ else
5292
+ section "codex units (native harness detected: OpenAI Codex CLI)"
5293
+ codex_rc=0; (cd "$REPO_DIR" && bash scripts/codex-birth-install.sh) || codex_rc=$?
5294
+ if [ "$codex_rc" -ne 0 ]; then
5295
+ setup_result codex-birth FAIL "detected codex, but publishing the SessionStart birth unit did not complete (see above)"
5296
+ else
5297
+ codex_rc=0; (cd "$REPO_DIR" && bash scripts/codex-birth-doctor.sh) >/dev/null || codex_rc=$?
5298
+ if [ "$codex_rc" -eq 0 ]; then
5299
+ setup_harness_result codex-birth "SessionStart birth unit published and trusted by the vendor" "./run.sh doctor-codex-birth"
5300
+ else
5301
+ setup_result codex-birth FAIL "the birth unit is published, but the vendor has recorded no trust receipt for it yet, so the hook is declared and never runs. Open a visible plain Codex, answer its prompt with 'Trust all and continue', send one first turn, then re-run setup (or './run.sh doctor-codex-birth')"
5302
+ fi
5303
+ fi
5304
+ codex_rc=0; codex_mcp install || codex_rc=$?
5305
+ if [ "$codex_rc" -eq 0 ]; then
5306
+ setup_harness_result codex-mcp "MCP server registered" "./run.sh doctor-codex-mcp"
5307
+ else
5308
+ setup_result codex-mcp FAIL "detected codex, but the MCP registration did not complete (see above)"
5309
+ fi
5310
+ codex_rc=0; codex_statusline install || codex_rc=$?
5311
+ if [ "$codex_rc" -eq 0 ]; then
5312
+ setup_harness_result codex-statusline "thread-title visible identity enabled" "./run.sh doctor-codex-statusline"
5313
+ else
5314
+ setup_result codex-statusline FAIL "detected codex, but the visible-identity setting did not complete (see above)"
5315
+ fi
5316
+ fi
5182
5317
  # ── core bridge boundary ── deterministic preflight lives in `pnpm run
5183
5318
  # check:full`; live substrate acceptance lives in `LIVE=1 ./run.sh
5184
5319
  # release-gate <scratch> --cut`. Setup is the install path, so it verifies the
@@ -5683,6 +5818,14 @@ release_gate() {
5683
5818
  # applied from omp onward; it does not retroactively redesign Copilot's operator-metered
5684
5819
  # exclusion.
5685
5820
  run_live_step "smoke-omp-fresh-live (#87 bundle C: entwurf_fresh_call opens omp, exact nonce callback, addressed receive)" gate bash "$self" smoke-omp-fresh-live
5821
+ # #95 first-admission LIVE acceptance. Unlike the older on-demand loaded-thread smoke,
5822
+ # this gate owns all three windows it opens and therefore needs no pre-existing TUI/thread id.
5823
+ # It still does not own the app-server: ENTWURF_CODEX_APP_SERVER_PID explicitly names the
5824
+ # operator-started process, whose /proc TMUX/TMUX_PANE is the only seat authority.
5825
+ # A real initial Pi opens Codex, correlates its callback and addresses it through native-push;
5826
+ # Codex then opens the outbound Pi and correlates that callback. Actor coordinates travel,
5827
+ # and fresh Codex == app-server/MCP inherited session == outbound Pi is a hard check.
5828
+ run_live_step "smoke-codex-fresh-live (#95: visible Pi -> visible Codex/callback/addressed v2 -> visible Pi/callback)" gate bash "$self" smoke-codex-fresh-live
5686
5829
 
5687
5830
  # 4. BEHAVIOR lane (advisory, non-blocking). Model-in-loop gates that probe
5688
5831
  # whether the model AUTONOMOUSLY drives the MCP entwurf surface. These never
@@ -5775,9 +5918,11 @@ case "$cmd" in
5775
5918
  check_shell_quote
5776
5919
  ;;
5777
5920
  check-install-surface)
5778
- # 0.12.7 — structural half of the node_modules strip-types fence: run_ts is the only
5779
- # crossing, every operator subcommand has a compiled twin, bin wrappers branch, dev
5780
- # gates stay out of the tarball, and offline smokes never touch the real $HOME.
5921
+ # structural half of the node_modules strip-types fence: run_ts is the only crossing,
5922
+ # every operator subcommand has a compiled twin, bin wrappers branch, dev gates stay out
5923
+ # of the tarball, offline smokes carry no OBVIOUS write to the real $HOME (a static
5924
+ # tripwire, never a sandbox proof), and S7 pins the candidate .pi/settings.json — the
5925
+ # shared release skill, the stable bridge bin, and NO packages key (#110).
5781
5926
  # check-pack-install owns the dynamic half (it drives the installed commands).
5782
5927
  run_ts scripts/check-install-surface.ts
5783
5928
  ;;
@@ -5895,6 +6040,9 @@ case "$cmd" in
5895
6040
  check-entwurf-control-rpc)
5896
6041
  check_entwurf_control_rpc
5897
6042
  ;;
6043
+ check-compaction-send-guard)
6044
+ check_compaction_send_guard
6045
+ ;;
5898
6046
  check-entwurf-v2-production)
5899
6047
  check_entwurf_v2_production
5900
6048
  ;;
@@ -6222,6 +6370,16 @@ case "$cmd" in
6222
6370
  # LIVE!=1. doctor-static preflight FAILs before the agy bridge is wired (③).
6223
6371
  run_ts scripts/smoke-agy-native-push-live.ts
6224
6372
  ;;
6373
+ smoke-codex-native-push-live)
6374
+ # Codex native-push LIVE acceptance. The operator owns both the app-server and visible
6375
+ # attached TUI; this command only proves the shipped record/probe/delivery composition.
6376
+ run_ts scripts/smoke-codex-native-push-live.ts
6377
+ ;;
6378
+ smoke-codex-fresh-live)
6379
+ # First-admission LIVE acceptance: real visible Pi -> Codex -> Pi, using only the
6380
+ # operator-owned app-server explicitly named by ENTWURF_CODEX_APP_SERVER_PID.
6381
+ run_ts scripts/smoke-codex-fresh-live.ts
6382
+ ;;
6225
6383
  smoke-user-scope-citizen)
6226
6384
  # 0.12.6 install-boundary gate: register-pi-package.py is the shared
6227
6385
  # packages[] SSOT for project/user install and remove; user scope makes
@@ -6428,6 +6586,36 @@ case "$cmd" in
6428
6586
  shift || true
6429
6587
  run_ts scripts/omp-receive-facts.ts "$@"
6430
6588
  ;;
6589
+ install-codex-birth)
6590
+ shift || true
6591
+ (cd "$REPO_DIR" && bash scripts/codex-birth-install.sh "$@")
6592
+ ;;
6593
+ uninstall-codex-birth)
6594
+ shift || true
6595
+ (cd "$REPO_DIR" && bash scripts/codex-birth-uninstall.sh "$@")
6596
+ ;;
6597
+ doctor-codex-birth)
6598
+ shift || true
6599
+ (cd "$REPO_DIR" && bash scripts/codex-birth-doctor.sh "$@")
6600
+ ;;
6601
+ install-codex-mcp)
6602
+ codex_mcp install
6603
+ ;;
6604
+ uninstall-codex-mcp)
6605
+ codex_mcp uninstall
6606
+ ;;
6607
+ doctor-codex-mcp)
6608
+ codex_mcp doctor
6609
+ ;;
6610
+ install-codex-statusline)
6611
+ codex_statusline install
6612
+ ;;
6613
+ uninstall-codex-statusline)
6614
+ codex_statusline uninstall
6615
+ ;;
6616
+ doctor-codex-statusline)
6617
+ codex_statusline doctor
6618
+ ;;
6431
6619
  copilot)
6432
6620
  # #82 RAIL 7: the managed launch. `exec` and NO subshell/cd on purpose — the vendor
6433
6621
  # must inherit this terminal exactly: the caller's cwd, this pid, this tty, and its own
@@ -6719,6 +6907,34 @@ case "$cmd" in
6719
6907
  # Hermetic (mkdtemp worlds + the store env seam); no model, no network.
6720
6908
  (cd "$REPO_DIR" && bash scripts/check-fresh-cut-gate.sh "$@")
6721
6909
  ;;
6910
+ check-codex-sender-identity)
6911
+ shift || true
6912
+ run_ts scripts/check-codex-sender-identity.ts "$@"
6913
+ ;;
6914
+ check-codex-bridge-identity)
6915
+ shift || true
6916
+ run_ts scripts/check-codex-bridge-identity.ts "$@"
6917
+ ;;
6918
+ check-codex-native-push)
6919
+ shift || true
6920
+ run_ts scripts/check-codex-native-push.ts "$@"
6921
+ ;;
6922
+ check-codex-birth-hook)
6923
+ shift || true
6924
+ run_ts scripts/check-codex-birth-hook.ts "$@"
6925
+ ;;
6926
+ check-codex-fresh-preflight)
6927
+ shift || true
6928
+ (cd "$REPO_DIR" && pnpm exec vitest run test/codex-fresh-preflight.test.ts "$@")
6929
+ ;;
6930
+ smoke-codex-config-state)
6931
+ shift || true
6932
+ (cd "$REPO_DIR" && bash scripts/smoke-codex-config-state.sh "$@")
6933
+ ;;
6934
+ smoke-codex-birth)
6935
+ shift || true
6936
+ (cd "$REPO_DIR" && bash scripts/smoke-codex-birth.sh "$@")
6937
+ ;;
6722
6938
  check-pack)
6723
6939
  check_pack
6724
6940
  ;;
@@ -72,7 +72,7 @@ interface WireUsage {
72
72
  * One `_meta.quota.token_count` row. The field NAMES deliberately differ from
73
73
  * `PromptResponse.usage`: cache reads are `cachedInputTokens` here because the
74
74
  * shape is shared with codex-acp, and `cachedWriteTokens` is Claude's extra
75
- * sibling (read at claude-agent-acp 0.75.1 `dist/acp-agent.js:6493-6502`).
75
+ * sibling (read at claude-agent-acp 0.76.0 `dist/acp-agent.js:6566-6575`).
76
76
  * Reading a quota row with the `usage` field names silently yields zeros, so the
77
77
  * fixture below spells the vendor's names out rather than reusing `WireUsage`.
78
78
  */
@@ -137,7 +137,7 @@ function makeFakeChild() {
137
137
  *
138
138
  * Each `prompt` call consumes the next TurnScript: it first pushes that turn's
139
139
  * `usage_update` notification (the wire the running cost total actually arrives
140
- * on — read at claude-agent-acp 0.75.1 `dist/acp-agent.js:3467-3482`), then answers the
140
+ * on — read at claude-agent-acp 0.76.0 `dist/acp-agent.js:3471-3486`), then answers the
141
141
  * prompt with that turn's `PromptResponse.usage` (the wire the turn aggregate
142
142
  * arrives on). Both orderings are the real one: the notification precedes the
143
143
  * response, because the SDK emits it from the `result` message that ENDS the turn.
@@ -393,8 +393,8 @@ try {
393
393
  // AGENT LOOP only. `_meta.quota.model_usage` comes from `result.modelUsage` and
394
394
  // also counts Task subagents, sidechains and INTERNAL CALLS SUCH AS COMPACTION;
395
395
  // the vendor states its rows "can total more than `token_count`" and are "the
396
- // fuller picture, not a decomposition of it" (read at claude-agent-acp 0.75.1
397
- // `dist/acp-agent.js:6468-6474`).
396
+ // fuller picture, not a decomposition of it" (read at claude-agent-acp 0.76.0
397
+ // `dist/acp-agent.js:6541-6547`).
398
398
  //
399
399
  // The wide one is required, not merely nicer, because the DENOMINATOR already
400
400
  // has that scope: turn cost is the adjacent diff of the backend's running total,
@@ -608,8 +608,8 @@ try {
608
608
  // identity that produces the bound is a property of the MAIN AGENT LOOP's
609
609
  // cache breakpoints. `_meta.quota.model_usage` is a WIDER scope: the vendor
610
610
  // states those rows also count Task subagents, sidechains, and INTERNAL
611
- // CALLS SUCH AS COMPACTION (read at claude-agent-acp 0.75.1
612
- // `dist/acp-agent.js:6465-6485`). Mixing the two scopes inflates the bound
611
+ // CALLS SUCH AS COMPACTION (read at claude-agent-acp 0.76.0
612
+ // `dist/acp-agent.js:6538-6558`). Mixing the two scopes inflates the bound
613
613
  // through both remaining terms that mention cacheWrite:
614
614
  // max(0, occupancy − cacheWrite) shrinks as wide cacheWrite grows, so
615
615
  // less is subtracted; min(rawBound, cacheWrite) rises with it.
@@ -749,9 +749,9 @@ try {
749
749
  // CELL 3 — a turn with NO cost notification holds the baseline.
750
750
  //
751
751
  // Measured upstream: the result-path `usage_update` carries cost (read at
752
- // claude-agent-acp 0.75.1 `dist/acp-agent.js:3467-3482`), while other
752
+ // claude-agent-acp 0.76.0 `dist/acp-agent.js:3471-3486`), while other
753
753
  // `usage_update` paths can carry `used` without cost (for example the
754
- // rate-limit path at `:4269-4277`). A live thinkpad ledger shows such turns
754
+ // rate-limit path at `:4273-4281`). A live thinkpad ledger shows such turns
755
755
  // really occur. The honest handling is to HOLD the baseline so the amount lands
756
756
  // in the NEXT diff: misattributed by turn, exact by session. Rebaselining to 0
757
757
  // there would double-count the whole prefix.
@@ -831,7 +831,7 @@ try {
831
831
  // CELL 4 — a DECREASING session total is never silently absorbed.
832
832
  //
833
833
  // `conversation_reset` switches the session to a fresh transcript (read at
834
- // claude-agent-acp 0.75.1 `dist/acp-agent.js:4282-4289`), but whether that
834
+ // claude-agent-acp 0.76.0 `dist/acp-agent.js:4286-4293`), but whether that
835
835
  // changes `total_cost_usd` is an SDK-internal value we cannot observe here.
836
836
  // A diff can therefore go negative in a session we are still holding.
837
837
  // Absorbing it quietly would both misreport the turn and destroy the only
@@ -466,7 +466,7 @@ try {
466
466
  "probe alive → replyable true",
467
467
  (await probeNativeSenderAlive(
468
468
  identity,
469
- adapterWith(async () => ({ status: "alive", route: { lsAddress: "127.0.0.1:1" } })),
469
+ adapterWith(async () => ({ status: "alive", route: { backend: "antigravity", lsAddress: "127.0.0.1:1" } })),
470
470
  )) === true,
471
471
  );
472
472