@junghanacs/entwurf 0.20.1 → 0.22.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 (111) hide show
  1. package/AGENTS.md +85 -190
  2. package/BASELINE.md +6 -3
  3. package/CHANGELOG.md +473 -14
  4. package/CONTRIBUTING.md +1 -1
  5. package/DELIVERY.md +332 -60
  6. package/README.md +100 -22
  7. package/VERIFY.md +93 -7
  8. package/docs/acp-backend-rail.md +0 -1
  9. package/docs/external-mcp-host.md +64 -33
  10. package/docs/setup-clean-host.md +151 -17
  11. package/mcp/entwurf-bridge/dist/mcp/entwurf-bridge/src/index.js +130 -114
  12. package/mcp/entwurf-bridge/dist/pi-extensions/lib/codex-caller-seat.js +174 -0
  13. package/mcp/entwurf-bridge/dist/pi-extensions/lib/codex-fresh-preflight.js +519 -0
  14. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-fact-provider.js +22 -2
  15. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-peers-render.js +3 -1
  16. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-contract.js +6 -7
  17. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-native-push.js +30 -17
  18. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-production.js +7 -1
  19. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-runner.js +3 -2
  20. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-send.js +8 -4
  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 +189 -19
  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/codex-socket-path.js +30 -0
  30. package/mcp/entwurf-bridge/dist/scripts/meta-bridge-fresh-cut.js +6 -1
  31. package/mcp/entwurf-bridge/src/index.ts +154 -116
  32. package/mcp/entwurf-bridge/tsconfig.build.json +10 -0
  33. package/package.json +13 -4
  34. package/pi-extensions/entwurf-control.ts +71 -19
  35. package/pi-extensions/lib/codex-caller-seat.ts +204 -0
  36. package/pi-extensions/lib/codex-fresh-preflight.ts +580 -0
  37. package/pi-extensions/lib/compaction-send-guard.ts +80 -0
  38. package/pi-extensions/lib/entwurf-fact-provider.ts +29 -3
  39. package/pi-extensions/lib/entwurf-peers-render.ts +4 -1
  40. package/pi-extensions/lib/entwurf-v2-contract.ts +6 -7
  41. package/pi-extensions/lib/entwurf-v2-native-push.ts +35 -18
  42. package/pi-extensions/lib/entwurf-v2-production.ts +10 -3
  43. package/pi-extensions/lib/entwurf-v2-runner.ts +3 -2
  44. package/pi-extensions/lib/entwurf-v2-send.ts +16 -11
  45. package/pi-extensions/lib/entwurf-v2-surface.ts +7 -4
  46. package/pi-extensions/lib/meta-mailbox-body.ts +22 -13
  47. package/pi-extensions/lib/meta-sender-identity.ts +305 -0
  48. package/pi-extensions/lib/mux-fresh-call.ts +233 -29
  49. package/pi-extensions/lib/native-push/adapter.ts +21 -24
  50. package/pi-extensions/lib/native-push/codex-ws-client.ts +506 -0
  51. package/pi-extensions/lib/native-push/register.ts +7 -9
  52. package/pi-extensions/meta-bridge-hook-codex.ts +371 -0
  53. package/run.sh +342 -28
  54. package/scripts/check-agy-sender-identity.ts +1 -1
  55. package/scripts/check-codex-app-server-launch.ts +445 -0
  56. package/scripts/check-codex-birth-hook.ts +264 -0
  57. package/scripts/check-codex-bridge-identity.ts +179 -0
  58. package/scripts/check-codex-native-push.ts +386 -0
  59. package/scripts/check-codex-sender-identity.ts +495 -0
  60. package/scripts/check-compaction-send-guard.ts +130 -0
  61. package/scripts/check-copilot-receive-arm.ts +4 -1
  62. package/scripts/check-entwurf-fact-provider.ts +38 -0
  63. package/scripts/check-entwurf-peers-surface.ts +13 -1
  64. package/scripts/check-entwurf-self-address.ts +15 -16
  65. package/scripts/check-entwurf-v2-contract.ts +4 -3
  66. package/scripts/check-entwurf-v2-decider.ts +7 -5
  67. package/scripts/check-entwurf-v2-native-push.ts +35 -7
  68. package/scripts/check-entwurf-v2-production.ts +245 -12
  69. package/scripts/check-entwurf-v2-runner.ts +1 -1
  70. package/scripts/check-entwurf-v2-send.ts +26 -7
  71. package/scripts/check-entwurf-v2-surface.ts +1 -1
  72. package/scripts/check-gate-qualification.ts +8 -3
  73. package/scripts/check-harness-admission-parity.ts +0 -1
  74. package/scripts/check-mux-launch-tmux.ts +345 -4
  75. package/scripts/check-native-push-adapter.ts +20 -16
  76. package/scripts/check-native-push-register.ts +5 -1
  77. package/scripts/check-release-gate-outcomes.ts +47 -1
  78. package/scripts/check-setup-qualification.sh +3 -1
  79. package/scripts/codex-app-server-launch.sh +275 -0
  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-socket-path.ts +33 -0
  85. package/scripts/codex-statusline-config.py +434 -0
  86. package/scripts/codex-terminal-title-config.py +500 -0
  87. package/scripts/codex_toml_io.py +653 -0
  88. package/scripts/lib/codex-fresh-live-protocol.ts +121 -0
  89. package/scripts/lib/codex-fresh-source-receipts.ts +426 -0
  90. package/scripts/lib/launch-receipt-windows.ts +46 -0
  91. package/scripts/lib/tmux-coordinate-row.ts +58 -0
  92. package/scripts/meta-bridge-fresh-cut.ts +6 -1
  93. package/scripts/mutants/codex-app-server-launch.json +157 -0
  94. package/scripts/mutants/codex-caller-seat.json +336 -0
  95. package/scripts/mutants/codex-native.json +838 -0
  96. package/scripts/mutants/compaction-send-guard.json +103 -0
  97. package/scripts/mutants/entwurf-peers.json +19 -0
  98. package/scripts/mutants/mux-fresh-call.json +93 -10
  99. package/scripts/mutants/omp-fresh.json +6 -4
  100. package/scripts/mutants/release-gate.json +13 -0
  101. package/scripts/mutants/v2-surface.json +75 -1
  102. package/scripts/raw-async-delivery/README.md +2 -1
  103. package/scripts/raw-codex-measure/README.md +114 -46
  104. package/scripts/smoke-agy-native-push-live.ts +3 -1
  105. package/scripts/smoke-codex-birth.sh +347 -0
  106. package/scripts/smoke-codex-config-state.sh +700 -0
  107. package/scripts/smoke-codex-fresh-live.ts +1426 -0
  108. package/scripts/smoke-codex-native-push-live.ts +75 -0
  109. package/scripts/smoke-entwurf-chain-live.ts +50 -0
  110. package/scripts/smoke-setup-verdict.sh +125 -10
  111. 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 Pi -> visible Codex -> visible Pi receipt chain (smoke-codex-fresh-live; initial Pi and the Codex it opens in one session S, explicit operator-owned app-server PID in a DIFFERENT session — that separation is the lane B claim). 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
@@ -134,9 +134,17 @@ Usage:
134
134
  ./run.sh check-omp-birth-hook # #87 gate: drives the real OMP assembler into a temp dir, imports the ASSEMBLED index.ts into a MOCK omp host and fires session_start/session_switch. tui mints one backend:"omp" record + a sender marker keyed to the host's OWN pid (the one-process join) + the garden id on the status line; print/rpc/json mint NOTHING (hasUI is true on the rpc rows, as in the vendor); switch attaches on the same native id and mints the replacement on a new one; the CERTIFIED receiver reader still finds no marker. Also the four-root joint binding: extension and omp-labeled bridge child resolve the same sessions/mailbox/senders/receivers bundle against test-built literals (never the production resolver as its own oracle), under a poisoned PI_CODING_AGENT_DIR and under four distinct ENTWURF_META_* overrides; the override grammar is absolute-or-~ and both halves plus the doctor refuse anything else by name; and a drifted provenance label selects no root policy at all. Hermetic; no omp, no model turn
135
135
  ./run.sh check-copilot-receive-arm # #82 RAIL 5 gate: the REAL receiver installer + the REAL extension.mjs forked with a stubbed SDK. Arms only after birth, marker owned by the WATCHER pid, self-fetch dispatch answer, doorbell carries the garden id and NOT the body, id-drift/foreign-parent refusals. Hermetic; no Copilot, no model turn
136
136
  ./run.sh check-copilot-launch # #82 RAIL 7 gate: the MANAGED launch `entwurf copilot`, driven through its public address against a FAKE VENDOR on a sandbox PATH. Receiver precondition refusals, EXTENSIONS token + operator token preservation, injected defaults before the `--` terminator, byte-identical argv, the 11 explicit permission/surface policy overrides that suppress `--yolo`, exec (not fork) pid identity, exit passthrough, recursion refusal. Hermetic; no Copilot, no model turn
137
+ ./run.sh check-codex-app-server-launch # #95 gate: the MANAGED app-server launch `entwurf codex-app-server`, driven through its public address against a FAKE VENDOR on a sandbox PATH. The injected `app-server --listen unix://<socket>` required to equal the product's own resolveCodexDefaultSocketPath over an env matrix that includes the inputs a bash transcription was MEASURED to diverge on (BOM-only CODEX_HOME, trailing slash, `..`), non-absolute and control-character addresses refused before any write, byte-identical operator argv, exec (not fork) parent identity, exit passthrough, live-socket and indeterminate-socket and second---listen and recursion refusals, dead-socket launch-over, the tmux fact line, and both identity carriers stripped. Hermetic; no Codex, no app-server, no model turn
137
138
  ./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
139
  ./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
140
  ./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
141
+ ./run.sh check-codex-sender-identity # Codex tools/call _meta triple agreement + addressable record join + cross-carrier conflict refusal
142
+ ./run.sh check-codex-bridge-identity # real MCP bridge request identity for entwurf_self/v2/fresh; malformed or conflicting _meta fails loud
143
+ ./run.sh check-codex-native-push # app-server WebSocket-over-UDS probe + measured one-shot codex queue delivery; no replay
144
+ ./run.sh check-codex-birth-hook # sandboxed payload + declaration: first-turn V3 birth, one record per thread, top-level event predicate, no markers
145
+ ./run.sh check-codex-fresh-preflight # pre-mutation birth/MCP/thread-title/default-app-server readiness
146
+ ./run.sh smoke-codex-config-state # sandboxed MCP + status-line ownership/install/doctor/inverse lifecycle
147
+ ./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
148
  ./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
149
  ./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
150
  ./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 +157,13 @@ Usage:
149
157
  ./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
158
  ./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
159
  ./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
160
+ ./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
161
  ./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
162
  ./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
163
  ./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
164
  ./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
165
  ./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
166
+ ./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
167
  ./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
168
  ./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
169
  ./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 +175,14 @@ Usage:
166
175
  ./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
176
  ./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
177
  ./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
178
+ ./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
179
+ ./run.sh smoke-codex-fresh-live # #95 RELEASE MUST: record-backed receipt fixture -> PUBLIC initial Pi fresh/callback in a session S that is NOT the app-server's -> Pi PUBLIC omitted-placement Codex fresh/exact callback, also in S -> Pi-to-Codex addressed native-push v2 -> Codex PUBLIC omitted-placement AND omitted-cwd fresh Pi/exact callback IN S beside its own TUI (lane B claim: the app-server env names another session, so S can only come from the caller's pane title; lane C claim: no cwd was asked for, so the directory can only come from the Codex caller's own record — and the Codex thread's own rollout session_meta.cwd, its pane and its record must all be the requested scratch, not the app-server's repo) -> Codex final v2 evidence. Needs LIVE=1 + ENTWURF_CODEX_APP_SERVER_PID + ENTWURF_CODEX_FRESH_MODEL + ENTWURF_CODEX_FRESH_PI_MODEL, and its ONE stable launch directory answered 'Trust' once in a plain codex -C there: codex records its direct consent per exact directory, so a directory with no answer at all opens a consent screen instead of a first turn. Only THIS gate treats that as a precondition and fails up front as codex-launch-cwd-undecided rather than timing out on a callback — an ordinary entwurf_fresh_call only prints the note and opens the window, because a human can answer the screen. The smoke PRINTS that directory as 'launch-cwd <dir>' and repeats it in the failure as a ready-to-run repair command — do not retype it from memory, and do not write it as \$TMPDIR/... (os.tmpdir() falls back to /tmp when TMPDIR is unset, so that spelling names a different directory in the filesystem root). The PID names the operator-owned app-server, which must sit in a DIFFERENT session from S; 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
180
  ./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
181
  ./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
182
  ./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
183
  ./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
184
  ./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
185
+ ./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
186
  ./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
187
  ./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
188
  ./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
@@ -240,6 +251,20 @@ Usage:
240
251
  ./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
252
  ./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
253
  ./run.sh doctor-agy-bridge # fail-loud doctor: MCP config + exact permission rule + state + live probe label
254
+ ./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
255
+ ./run.sh uninstall-codex-birth # exact inverse from the install-state digest inventory; refuses drifted bytes instead of deleting them
256
+ ./run.sh doctor-codex-birth # RUNTIME + UNIT bytes/ownership + the vendor TRUST receipt for this declaration (--unit-only skips the receipt; sandboxes only)
257
+ ./run.sh install-codex-mcp # own only [mcp_servers.entwurf-bridge] in the user's Codex config
258
+ ./run.sh uninstall-codex-mcp # remove/restore only the recorded MCP atom
259
+ ./run.sh doctor-codex-mcp # effective config + ownership + actual bridge boot
260
+ ./run.sh install-codex-statusline # add thread-title to tui.status_line for visible garden ids
261
+ ./run.sh uninstall-codex-statusline # remove only the recorded status-line atom
262
+ ./run.sh doctor-codex-statusline # effective visible-identity config + ownership verdict
263
+ ./run.sh install-codex-terminal-title # add thread-id to tui.terminal_title so the multiplexer reports the caller's thread in #{pane_title} (#95 placement input, never an address)
264
+ ./run.sh uninstall-codex-terminal-title # remove only the recorded terminal-title atom
265
+ ./run.sh doctor-codex-terminal-title # effective caller-seat config + ownership verdict
266
+ ./run.sh codex-socket-path # #95 INTERNAL (the launcher asks this; you do not need it): print the Codex app-server default control-socket path for this environment, from the SAME resolver delivery and preflight read
267
+ ./run.sh codex-app-server [args...] # #95: the ONE COMMAND that starts the operator's Codex app-server. exec()s `codex app-server --listen "unix://$CODEX_HOME/app-server-control/app-server-control.sock"` in THIS terminal (cwd/pid/exit are the vendor's, Ctrl-C is yours) — entwurf owns the SPELLING of that address, never the process's lifecycle: no supervisor, no restart, no daemon, no pid file. It does no path arithmetic: the address comes from `codex-socket-path`, the one leaf every other Codex surface reads, and a resolved address that is not absolute or carries a control character is refused before anything is created. Your arguments are forwarded byte-identical after it; a second --listen is refused by name. Refuses on a live socket (names the /proc owner it can read) or an indeterminate one, and reports which tmux server this seat gives caller-seat lookups. Run it in a DETACHED tmux session other than the one you work in
243
268
  ./run.sh install-agy-statusline # own the agy statusLine subtree with bare entwurf-agy-statusline; preserve unrelated settings
244
269
  ./run.sh uninstall-agy-statusline # honest inverse from statusline install-state
245
270
  ./run.sh doctor-agy-statusline # fail-loud statusLine config/bin/state doctor + honest live SKIP
@@ -581,6 +606,56 @@ install_local_package() {
581
606
  _pi_package_state() { echo "${XDG_DATA_HOME:-$HOME/.local/share}/entwurf/pi-package/install-state.json"; }
582
607
  _pi_provider_state() { echo "${XDG_DATA_HOME:-$HOME/.local/share}/entwurf/pi-provider/install-state.json"; }
583
608
 
609
+ _codex_home() { echo "${CODEX_HOME:-$HOME/.codex}"; }
610
+ _codex_config() { echo "$(_codex_home)/config.toml"; }
611
+ _codex_mcp_state() { echo "${XDG_DATA_HOME:-$HOME/.local/share}/entwurf/codex-mcp/install-state.json"; }
612
+ _codex_statusline_state() { echo "${XDG_DATA_HOME:-$HOME/.local/share}/entwurf/codex-statusline/install-state.json"; }
613
+ _codex_terminal_title_state() { echo "${XDG_DATA_HOME:-$HOME/.local/share}/entwurf/codex-terminal-title/install-state.json"; }
614
+
615
+ codex_mcp() {
616
+ local verb="$1" config state
617
+ config="$(_codex_config)"
618
+ state="$(_codex_mcp_state)"
619
+ case "$verb" in
620
+ install) python3 "$REPO_DIR/scripts/codex-mcp-config.py" install "$config" "entwurf-bridge" "$state" ;;
621
+ uninstall) python3 "$REPO_DIR/scripts/codex-mcp-config.py" uninstall "$state" ;;
622
+ doctor)
623
+ python3 "$REPO_DIR/scripts/codex-mcp-config.py" doctor-static "$config" "entwurf-bridge" "$state"
624
+ local invocation
625
+ invocation="$(python3 "$REPO_DIR/scripts/codex-mcp-config.py" doctor-invocation "$config")"
626
+ run_ts scripts/probe-bridge-command.ts --invocation-json "$invocation"
627
+ ;;
628
+ esac
629
+ }
630
+
631
+ codex_statusline() {
632
+ local verb="$1" config state
633
+ config="$(_codex_config)"
634
+ state="$(_codex_statusline_state)"
635
+ case "$verb" in
636
+ install) python3 "$REPO_DIR/scripts/codex-statusline-config.py" install "$config" "$state" ;;
637
+ uninstall) python3 "$REPO_DIR/scripts/codex-statusline-config.py" uninstall "$state" ;;
638
+ doctor) python3 "$REPO_DIR/scripts/codex-statusline-config.py" doctor-static "$config" "$state" ;;
639
+ esac
640
+ }
641
+
642
+ # #95 lane B: the SECOND visible-identity atom, and a different axis from the
643
+ # status line. `status_line`'s thread-title is what a HUMAN reads in the TUI;
644
+ # `terminal_title`'s thread-id is what the MULTIPLEXER reports back as
645
+ # `#{pane_title}`, which is the only value a Codex caller's `_meta.threadId`
646
+ # can be matched against to find the pane it is sitting in. Placement input
647
+ # only — never an address, delivery or liveness fact.
648
+ codex_terminal_title() {
649
+ local verb="$1" config state
650
+ config="$(_codex_config)"
651
+ state="$(_codex_terminal_title_state)"
652
+ case "$verb" in
653
+ install) python3 "$REPO_DIR/scripts/codex-terminal-title-config.py" install "$config" "$state" ;;
654
+ uninstall) python3 "$REPO_DIR/scripts/codex-terminal-title-config.py" uninstall "$state" ;;
655
+ doctor) python3 "$REPO_DIR/scripts/codex-terminal-title-config.py" doctor-static "$config" "$state" ;;
656
+ esac
657
+ }
658
+
584
659
  register_user_scope_citizen() {
585
660
  local agent_dir="${PI_CODING_AGENT_DIR:-$HOME/.pi/agent}"
586
661
  # Shared idempotent implementation (also driven by smoke-user-scope-citizen).
@@ -831,6 +906,18 @@ smoke_omp_fresh_live() {
831
906
  run_ts scripts/smoke-omp-fresh-live.ts
832
907
  }
833
908
 
909
+ smoke_codex_fresh_live() {
910
+ # #95 first-admission RELEASE MUST. The explicit operator-owned app-server PID is the
911
+ # seat authority: validate its Codex app-server argv and /proc TMUX/TMUX_PANE, then use
912
+ # that exact environment for the record-backed receipt fixture and stable-id cleanup.
913
+ # Through public MCP calls: fixture opens visible Pi; Pi opens visible Codex, correlates
914
+ # its exact callback and addresses it through native-push v2; Codex opens visible Pi,
915
+ # correlates that callback, and sends final evidence. The four actor/seat coordinates
916
+ # travel in receipts and Codex/app-server-MCP/outbound-Pi sessions must be identical.
917
+ # LIVE!=1 is the only host-prerequisite SKIP after admission; missing facts FAIL.
918
+ run_ts scripts/smoke-codex-fresh-live.ts
919
+ }
920
+
834
921
  check_omp_fresh_preflight() {
835
922
  # #87 Bundle C. The OMP fresh preflight REPRODUCES two resolvers that already exist
836
923
  # in this repo in another language — omp_agent_dir (scripts/omp-bridge-oracle.sh) and
@@ -963,6 +1050,14 @@ check_copilot_launch() {
963
1050
  run_ts scripts/check-copilot-launch.ts
964
1051
  }
965
1052
 
1053
+ check_codex_app_server_launch() {
1054
+ # #95: the managed app-server SPELLING, proved against a fake vendor. There is no second
1055
+ # spelling of the address to compare any more — the launcher asks `codex-socket-path` — so
1056
+ # these cells are a WIRING oracle, and they carry the hostile inputs a transcription was
1057
+ # measured to diverge on so that re-deriving the path here goes red rather than passing.
1058
+ run_ts scripts/check-codex-app-server-launch.ts
1059
+ }
1060
+
966
1061
  check_copilot_receive_arm() {
967
1062
  # #82 RAIL 5 gate: drives the REAL receiver installer into a temp extensions dir, then
968
1063
  # forks the REAL extension.mjs with the SDK specifier resolved by a loader hook (the
@@ -1219,6 +1314,14 @@ check_entwurf_control_rpc() {
1219
1314
  run_ts scripts/check-entwurf-control-rpc.ts
1220
1315
  }
1221
1316
 
1317
+ check_compaction_send_guard() {
1318
+ # Deterministic gate for #111: a compacting Pi citizen is neither idle nor an ordinary
1319
+ # streaming turn. Event-armed refuse is `compacting`; quiet unknown non-idle is `busy`
1320
+ # (`ctx.signal` is not isStreaming). Either token: no pi.sendMessage, no delivered:true.
1321
+ # Pure admission predicate + resident wiring/event source guard. No IO.
1322
+ run_ts scripts/check-compaction-send-guard.ts
1323
+ }
1324
+
1222
1325
  check_entwurf_resume_args() {
1223
1326
  # Deterministic gate for the resume-argv SSOT (buildResumePiArgs). S1 replaced the headless
1224
1327
  # shape with the VISIBLE one, measured against the runtime before the consumer was written:
@@ -1333,14 +1436,20 @@ check_mux_fresh_call() {
1333
1436
  # could not see: real bridge boot → tools/list schema/description, Rust-regex-family pattern
1334
1437
  # validity on every emitted pattern (the #62 escape), and the schema riding the actual
1335
1438
  # anthropic-messages request body. No fake tmux: the real-window axis is smoke-mux-fresh-call-live.
1336
- # copilot-fresh-preflight rides here rather than in its own gate: it exists only as
1337
- # freshCall's pre-mutation branch (#82 RAIL 9), and splitting it would let the two halves
1338
- # of one refusal be certified in different runs.
1439
+ # copilot-fresh-preflight and codex-fresh-preflight ride here rather than in
1440
+ # separate gates: each exists only as freshCall's pre-mutation branch, and
1441
+ # splitting one would let the two halves of a refusal be certified separately.
1339
1442
  # omp-fresh-bootstrap.contract rides here for the same reason and one more: it is the ONLY
1340
1443
  # place the launcher's payload and the installed birth extension's decoder are read together.
1341
1444
  # They ship in different directories and cannot import each other (#87 Bundle C), so this
1342
1445
  # lane is what keeps a deliberate duplication from becoming drift.
1343
- 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
1446
+ # tmux-coordinate-row rides here because it is the parse every tmux COORDINATE in this lane's
1447
+ # LIVE siblings depends on, and because it is the only half of a measured LIVE failure that a
1448
+ # deterministic gate can own: the row is the input, so no tmux and no model turn is needed.
1449
+ # codex-caller-seat rides here for the same reason as the two preflights: the leaf exists
1450
+ # only as freshCall's Codex placement input (#95 lane B), so certifying it apart from the
1451
+ # composition it feeds would let the two halves of one seat decision drift.
1452
+ 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 test/codex-caller-seat.test.ts
1344
1453
  }
1345
1454
 
1346
1455
  smoke_mux_fresh_call_live() {
@@ -1701,11 +1810,13 @@ check_entwurf_fact_provider() {
1701
1810
  # Deterministic gate for 0.11 Stage 0 step 4 (fact-provider slice 4b): the
1702
1811
  # ASSEMBLY layer listEntwurfFacts. listAllMetaIdentities → scanSocketProbes →
1703
1812
  # pre-quarantine out-of-socket-domain/socket conflicts → resolveFactList(clean) →
1704
- # {facts, diagnostics}. Throw-vs-diagnostics policy (GPT힣 C-원칙): expected
1705
- # corruption (parse failure / gardenId↔socket collision) diagnostics, listing
1706
- # survives; impossible wiring invariant (resolveFactList duplicate/unprobed)
1707
- # throw, never swallowed. A collision quarantines BOTH the PeerFact and the
1708
- # socket (gid is the universal address). meta + socket deps injected, no IO.
1813
+ # {facts, diagnostics}. #112 keeps the whole-store parse/probe/diagnostic/payload
1814
+ # while running expensive receiver/transcript observation for exactly the newest
1815
+ # 32 human-rendered rows; older payload rows say unobserved. Q112 pins that budget.
1816
+ # Throw-vs-diagnostics policy (GPT힣 C-원칙): expected corruption (parse failure /
1817
+ # gardenId↔socket collision) diagnostics, listing survives; impossible wiring
1818
+ # invariant (resolveFactList duplicate/unprobed) → throw, never swallowed. A
1819
+ # collision quarantines BOTH the PeerFact and socket. deps injected, no IO.
1709
1820
  run_ts scripts/check-entwurf-fact-provider.ts
1710
1821
  }
1711
1822
 
@@ -1907,7 +2018,7 @@ assert.equal(peerDepTui, expectedPeer,
1907
2018
  // read, and sat two bumps stale (`>=0.84.3 <0.85` at a 0.85.1 pin) precisely
1908
2019
  // because it was outside this list. Its declaration is a plain range, so the
1909
2020
  // range scan below binds it — no new PROSE_DECL is needed for it.
1910
- const BASELINE_DOCS = ['AGENTS.md', 'README.md', 'ROADMAP.md', 'docs/setup-clean-host.md', 'demo/README.md', 'VERIFY.md'];
2021
+ const BASELINE_DOCS = ['README.md', 'ROADMAP.md', 'docs/setup-clean-host.md', 'demo/README.md', 'VERIFY.md'];
1911
2022
  let rangeDecls = 0, exactDecls = 0;
1912
2023
  for (const file of BASELINE_DOCS) {
1913
2024
  const text = readFileSync(file, 'utf8');
@@ -1932,7 +2043,6 @@ const PROSE_DECLS = [
1932
2043
  ['demo/README.md', /current floor (\d+\.\d+\.\d+)/, 'current floor <version>'],
1933
2044
  ['ROADMAP.md', /\bpi (\d+\.\d+\.\d+) fence\b/, 'pi <version> fence'],
1934
2045
  ['ROADMAP.md', /floor = \*\*(\d+\.\d+\.\d+)\*\*/, 'floor = **<version>**'],
1935
- ['AGENTS.md', /devDep exact `(\d+\.\d+\.\d+)`/, 'devDep exact `<version>`'],
1936
2046
  ];
1937
2047
  for (const [file, re, shape] of PROSE_DECLS) {
1938
2048
  const m = readFileSync(file, 'utf8').match(re);
@@ -2084,7 +2194,7 @@ check_claude_floor_coherence() {
2084
2194
  # DERIVE it at runtime through scripts/meta-bridge-claude-floor.sh, so they carry no
2085
2195
  # literal to drift — that shared file is itself part of the contract and is asserted
2086
2196
  # below. What remains are the human-facing declarations (the launcher's refusal
2087
- # message, docs, AGENTS) plus every fake `claude` CLI stub the gates spawn: a stub
2197
+ # message and docs) plus every fake `claude` CLI stub the gates spawn: a stub
2088
2198
  # that reports a version BELOW the floor would make the doctor legitimately refuse
2089
2199
  # its own fixture, so those are bound too — as `>=`, not equality, since a stub may
2090
2200
  # honestly claim a newer version.
@@ -2124,7 +2234,6 @@ const SITES = [
2124
2234
  ['pi/meta-bridge/entwurf-meta-receive/scripts/hook-launch.sh', /Claude Code >= (\d+\.\d+\.\d+)/g, 1, 'launcher refusal message'],
2125
2235
  ['scripts/meta-bridge-claude-floor.sh', /THE FLOOR IS (\d+\.\d+\.\d+)/g, 1, 'floor helper rationale header'],
2126
2236
  ['docs/setup-clean-host.md', /`>=(\d+\.\d+\.\d+)`\*\* — the exec-form hook floor/g, 1, 'clean-host pin matrix'],
2127
- ['AGENTS.md', /Claude Code `>=(\d+\.\d+\.\d+)`/g, 1, 'AGENTS rule 14'],
2128
2237
  ['README.md', /supported floor `>=(\d+\.\d+\.\d+)`/g, 1, 'README doctor description'],
2129
2238
  ['scripts/check-hook-launch-topology.ts', /\/(\d+)\\\.(\d+)\\\.(\d+)\//g, 1, 'topology gate floor assertion'],
2130
2239
  ];
@@ -3788,10 +3897,10 @@ sys.exit(0 if any(isinstance(s,str) and s.rstrip('/')== '$npm2_pkg' for s in src
3788
3897
  # SAME installed candidate, on a FRESH sandbox home with every harness absent.
3789
3898
  # This is the first living consumer of package-installed `entwurf setup`:
3790
3899
  # mode=installed decided by name before anything else, NO pnpm bootstrap inside
3791
- # node_modules, pi/claude/agy/copilot/omp explicit zero-state SKIPs, stable bins
3792
- # PASS as npm-provided, core bridge boundary PASS, computed green, zero
3793
- # harness/auth writes. Harness probes are pinned absent via the FIVE
3794
- # PI_BIN/CLAUDE_BIN/AGY_BIN/COPILOT_BIN/OMP_BIN seams so the gate host's real
3900
+ # node_modules, every harness explicit zero-state SKIP, stable bins PASS as
3901
+ # npm-provided, core bridge boundary PASS, computed green, zero
3902
+ # harness/auth writes. Harness probes are pinned absent via the SIX
3903
+ # PI_BIN/CLAUDE_BIN/AGY_BIN/COPILOT_BIN/OMP_BIN/CODEX_BIN seams so the gate host's real
3795
3904
  # harnesses never leak in.
3796
3905
  #
3797
3906
  # OMP_BIN was MISSING here from the 0.16.0 OMP admission until 2026-09-06, while
@@ -3803,7 +3912,7 @@ sys.exit(0 if any(isinstance(s,str) and s.rstrip('/')== '$npm2_pkg' for s in src
3803
3912
  # ambiguous. Nothing was wrong with the product; the fixture was. It stayed
3804
3913
  # invisible because CI runners carry no omp AND `release_gate()` does not run
3805
3914
  # this gate (it is prepublishOnly + the CI install-surface job). The skip probe
3806
- # below now requires FIVE zero-states, so a sixth harness cannot be admitted
3915
+ # below now requires SIX zero-states, so a seventh harness cannot be admitted
3807
3916
  # while this seam is quietly left behind again.
3808
3917
  local setup_home="$npm_tmp/setuphome" setup_proj="$npm_tmp/setupproj" setup_out setup_rc setup_auth
3809
3918
  mkdir -p "$setup_home/.pi/agent" "$setup_proj"
@@ -3815,7 +3924,7 @@ sys.exit(0 if any(isinstance(s,str) and s.rstrip('/')== '$npm2_pkg' for s in src
3815
3924
  setup_out=$(HOME="$setup_home" XDG_DATA_HOME="$setup_home/.local/share" XDG_STATE_HOME="$setup_home/.local/state" \
3816
3925
  XDG_CACHE_HOME="$setup_home/.cache" XDG_CONFIG_HOME="$setup_home/.config" PI_CODING_AGENT_DIR="$setup_home/.pi/agent" \
3817
3926
  PI_BIN="$npm_tmp/definitely-absent" CLAUDE_BIN="$npm_tmp/definitely-absent" AGY_BIN="$npm_tmp/definitely-absent" \
3818
- COPILOT_BIN="$npm_tmp/definitely-absent" OMP_BIN="$npm_tmp/definitely-absent" \
3927
+ COPILOT_BIN="$npm_tmp/definitely-absent" OMP_BIN="$npm_tmp/definitely-absent" CODEX_BIN="$npm_tmp/definitely-absent" \
3819
3928
  "$npmroot/node_modules/.bin/entwurf" setup "$setup_proj" 2>&1)
3820
3929
  setup_rc=$?
3821
3930
  set -e
@@ -3835,9 +3944,9 @@ sys.exit(0 if any(isinstance(s,str) and s.rstrip('/')== '$npm2_pkg' for s in src
3835
3944
  return 1
3836
3945
  fi
3837
3946
  local skip_probe
3838
- for skip_probe in "pi: SKIP" "claude: SKIP" "agy: SKIP" "copilot: SKIP" "omp: SKIP"; do
3947
+ for skip_probe in "pi: SKIP" "claude: SKIP" "agy: SKIP" "copilot: SKIP" "omp: SKIP" "codex: SKIP"; do
3839
3948
  if ! grep -q "$skip_probe" <<<"$setup_out"; then
3840
- fail "[check-pack-install] installed all-absent setup missing explicit zero-state '$skip_probe':"
3949
+ fail "[QK:CODEX-PACK-SETUP-ZERO-STATE] [check-pack-install] installed all-absent setup missing explicit zero-state '$skip_probe':"
3841
3950
  echo "$setup_out" | tail -25 | sed 's/^/ /' >&2
3842
3951
  return 1
3843
3952
  fi
@@ -3852,7 +3961,7 @@ sys.exit(0 if any(isinstance(s,str) and s.rstrip('/')== '$npm2_pkg' for s in src
3852
3961
  echo "$setup_out" | tail -25 | sed 's/^/ /' >&2
3853
3962
  return 1
3854
3963
  fi
3855
- if [ -e "$setup_proj/.pi" ] || [ -e "$setup_home/.pi/agent/settings.json" ] || [ -e "$setup_home/.gemini" ] || [ -e "$setup_home/.copilot" ]; then
3964
+ 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
3856
3965
  fail "[check-pack-install] installed all-absent setup wrote harness state (must be zero-write)"
3857
3966
  return 1
3858
3967
  fi
@@ -3860,7 +3969,7 @@ sys.exit(0 if any(isinstance(s,str) and s.rstrip('/')== '$npm2_pkg' for s in src
3860
3969
  fail "[check-pack-install] installed setup touched the credential store (byte drift or .bak)"
3861
3970
  return 1
3862
3971
  fi
3863
- echo "[check-pack-install] installed all-absent setup pass (mode-first, no bootstrap, 5x SKIP, bins npm-provided, core PASS, computed green, zero writes)"
3972
+ echo "[check-pack-install] installed all-absent setup pass (mode-first, no bootstrap, 6x SKIP, bins npm-provided, core PASS, computed green, zero writes)"
3864
3973
 
3865
3974
  # Installed copilot-present aggregate `setup` row (#86 C3b): the SAME consumer
3866
3975
  # bin on a fresh sandbox home, with the shared fake vendor
@@ -3886,7 +3995,7 @@ sys.exit(0 if any(isinstance(s,str) and s.rstrip('/')== '$npm2_pkg' for s in src
3886
3995
  setup_out=$(HOME="$cop_setup_home" XDG_DATA_HOME="$cop_setup_home/.local/share" XDG_STATE_HOME="$cop_setup_home/.local/state" \
3887
3996
  XDG_CACHE_HOME="$cop_setup_home/.cache" XDG_CONFIG_HOME="$cop_setup_home/.config" PI_CODING_AGENT_DIR="$cop_setup_home/.pi/agent" \
3888
3997
  PI_BIN="$npm_tmp/definitely-absent" CLAUDE_BIN="$npm_tmp/definitely-absent" AGY_BIN="$npm_tmp/definitely-absent" \
3889
- OMP_BIN="$npm_tmp/definitely-absent" \
3998
+ OMP_BIN="$npm_tmp/definitely-absent" CODEX_BIN="$npm_tmp/definitely-absent" \
3890
3999
  COPILOT_BIN="$cop_fake/copilot" PATH="$cop_fake:$npmroot/node_modules/.bin:$PATH" \
3891
4000
  "$npmroot/node_modules/.bin/entwurf" setup "$cop_setup_proj" 2>&1)
3892
4001
  setup_rc=$?
@@ -4424,6 +4533,57 @@ JS
4424
4533
  fi
4425
4534
  echo "[check-pack-install] installed 'entwurf copilot' reached the vendor with the scan flag and the managed argv"
4426
4535
 
4536
+ # #95 — the managed app-server SPELLING, from the INSTALLED package. Same packaging risk as
4537
+ # the copilot cell above and the same reason a file-list assertion cannot stand in for it:
4538
+ # `scripts/codex-app-server-launch.sh` has to be in the tarball AND resolve its siblings
4539
+ # correctly once it lives under node_modules. What makes this cell worth its seconds is the
4540
+ # ADDRESS: this verb exists so a consumer never types that socket path, so the one thing an
4541
+ # installed run must prove is that the path it hands the vendor is the one the product's own
4542
+ # resolver computes — read here from the INSTALLED tree, never from this checkout.
4543
+ local cx_launch_home="$npm_tmp/codex-appserver-home" cx_launch_bin="$npm_tmp/codex-appserver-bin"
4544
+ mkdir -p "$cx_launch_home" "$cx_launch_bin"
4545
+ {
4546
+ printf '#!/usr/bin/env bash\n'
4547
+ printf 'for a in "$@"; do printf "ARG<%%s>\\n" "$a"; done\n'
4548
+ } > "$cx_launch_bin/codex"
4549
+ chmod +x "$cx_launch_bin/codex"
4550
+ # The expectation is read from the INSTALLED COMPILED resolver, not from this checkout and
4551
+ # not transcribed here: compiled JS is what an installed consumer surface reaches (Hard Rule
4552
+ # 11), so this asks the packed tree itself what address it believes in.
4553
+ local cx_resolver="$npm_pkg/mcp/entwurf-bridge/dist/pi-extensions/lib/native-push/codex-ws-client.js" cx_want
4554
+ if [ ! -f "$cx_resolver" ]; then
4555
+ fail "[check-pack-install] the tarball carries no compiled Codex socket resolver at $cx_resolver"
4556
+ return 1
4557
+ fi
4558
+ if ! cx_want=$(CODEX_HOME="$cx_launch_home/.codex" node --input-type=module -e "
4559
+ import { resolveCodexDefaultSocketPath } from '$cx_resolver';
4560
+ process.stdout.write(resolveCodexDefaultSocketPath(process.env));
4561
+ " 2>&1); then
4562
+ fail "[check-pack-install] the INSTALLED compiled socket resolver would not load:"
4563
+ echo "$cx_want" | tail -4 | sed 's/^/ /' >&2
4564
+ return 1
4565
+ fi
4566
+ if ! op_out=$(env -u ENTWURF_CODEX_APP_SERVER_ACTIVE -u TMUX \
4567
+ HOME="$cx_launch_home" CODEX_HOME="$cx_launch_home/.codex" PATH="$cx_launch_bin:$PATH" \
4568
+ "$installed_entwurf" codex-app-server --config probe=1 2>&1); then
4569
+ fail "[check-pack-install] installed 'entwurf codex-app-server' FAILED to reach the vendor:"
4570
+ echo "$op_out" | tail -8 | sed 's/^/ /' >&2
4571
+ return 1
4572
+ fi
4573
+ for want in "ARG<app-server>" "ARG<--listen>" "ARG<unix://$cx_want>" "ARG<--config>" "ARG<probe=1>"; do
4574
+ if ! printf '%s' "$op_out" | grep -qF "$want"; then
4575
+ fail "[check-pack-install] installed 'entwurf codex-app-server' did not hand the vendor the resolved address and argv (missing $want):"
4576
+ echo "$op_out" | tail -8 | sed 's/^/ /' >&2
4577
+ return 1
4578
+ fi
4579
+ done
4580
+ if printf '%s' "$op_out" | grep -qF 'ARG<codex-app-server>'; then
4581
+ fail "[check-pack-install] installed 'entwurf codex-app-server' leaked the dispatcher verb into the vendor's argv:"
4582
+ echo "$op_out" | tail -8 | sed 's/^/ /' >&2
4583
+ return 1
4584
+ fi
4585
+ echo "[check-pack-install] installed 'entwurf codex-app-server' reached the vendor at the INSTALLED resolver's address ($cx_want)"
4586
+
4427
4587
  # The generation verb must reach its verdict from under node_modules. On the
4428
4588
  # sandbox 0-record agent dir it archives nothing and opens a fresh generation.
4429
4589
  # The exit code alone can't tell a verdict from a fence crash, so read the line.
@@ -5203,6 +5363,49 @@ setup_all() {
5203
5363
  fi
5204
5364
  fi
5205
5365
 
5366
+
5367
+ # ── codex ── Entwurf owns three configuration atoms, never the harness, its app-server, or
5368
+ # the operator's trust decision. The birth unit is now setup's to install: its paths are the
5369
+ # operator's own, so nothing here needs root — but the VENDOR still has to agree to run the
5370
+ # hook, and only the operator can answer that, once, in their own visible Codex. So a first
5371
+ # setup on a Codex host writes the bytes and is honestly NON-GREEN until that answer exists.
5372
+ # It never writes `[hooks.state]`: forging the receipt in a file we already own would turn a
5373
+ # security prompt into a silent install.
5374
+ local codex_rc
5375
+ if ! command -v "${CODEX_BIN:-codex}" >/dev/null 2>&1; then
5376
+ setup_result codex SKIP "codex not on PATH — zero Codex wiring written"
5377
+ else
5378
+ section "codex units (native harness detected: OpenAI Codex CLI)"
5379
+ codex_rc=0; (cd "$REPO_DIR" && bash scripts/codex-birth-install.sh) || codex_rc=$?
5380
+ if [ "$codex_rc" -ne 0 ]; then
5381
+ setup_result codex-birth FAIL "detected codex, but publishing the SessionStart birth unit did not complete (see above)"
5382
+ else
5383
+ codex_rc=0; (cd "$REPO_DIR" && bash scripts/codex-birth-doctor.sh) >/dev/null || codex_rc=$?
5384
+ if [ "$codex_rc" -eq 0 ]; then
5385
+ setup_harness_result codex-birth "SessionStart birth unit published and trusted by the vendor" "./run.sh doctor-codex-birth"
5386
+ else
5387
+ 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')"
5388
+ fi
5389
+ fi
5390
+ codex_rc=0; codex_mcp install || codex_rc=$?
5391
+ if [ "$codex_rc" -eq 0 ]; then
5392
+ setup_harness_result codex-mcp "MCP server registered" "./run.sh doctor-codex-mcp"
5393
+ else
5394
+ setup_result codex-mcp FAIL "detected codex, but the MCP registration did not complete (see above)"
5395
+ fi
5396
+ codex_rc=0; codex_statusline install || codex_rc=$?
5397
+ if [ "$codex_rc" -eq 0 ]; then
5398
+ setup_harness_result codex-statusline "thread-title visible identity enabled" "./run.sh doctor-codex-statusline"
5399
+ else
5400
+ setup_result codex-statusline FAIL "detected codex, but the visible-identity setting did not complete (see above)"
5401
+ fi
5402
+ codex_rc=0; codex_terminal_title install || codex_rc=$?
5403
+ if [ "$codex_rc" -eq 0 ]; then
5404
+ setup_harness_result codex-terminal-title "thread-id terminal title enabled (caller-seat placement input)" "./run.sh doctor-codex-terminal-title"
5405
+ else
5406
+ setup_result codex-terminal-title FAIL "detected codex, but the terminal-title setting did not complete (see above)"
5407
+ fi
5408
+ fi
5206
5409
  # ── core bridge boundary ── deterministic preflight lives in `pnpm run
5207
5410
  # check:full`; live substrate acceptance lives in `LIVE=1 ./run.sh
5208
5411
  # release-gate <scratch> --cut`. Setup is the install path, so it verifies the
@@ -5707,6 +5910,14 @@ release_gate() {
5707
5910
  # applied from omp onward; it does not retroactively redesign Copilot's operator-metered
5708
5911
  # exclusion.
5709
5912
  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
5913
+ # #95 first-admission LIVE acceptance. Unlike the older on-demand loaded-thread smoke,
5914
+ # this gate owns all three windows it opens and therefore needs no pre-existing TUI/thread id.
5915
+ # It still does not own the app-server: ENTWURF_CODEX_APP_SERVER_PID explicitly names the
5916
+ # operator-started process, whose /proc TMUX/TMUX_PANE is the only seat authority.
5917
+ # A real initial Pi opens Codex, correlates its callback and addresses it through native-push;
5918
+ # Codex then opens the outbound Pi and correlates that callback. Actor coordinates travel,
5919
+ # and fresh Codex == app-server/MCP inherited session == outbound Pi is a hard check.
5920
+ 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
5710
5921
 
5711
5922
  # 4. BEHAVIOR lane (advisory, non-blocking). Model-in-loop gates that probe
5712
5923
  # whether the model AUTONOMOUSLY drives the MCP entwurf surface. These never
@@ -5873,6 +6084,9 @@ case "$cmd" in
5873
6084
  check-copilot-statusline)
5874
6085
  check_copilot_statusline
5875
6086
  ;;
6087
+ check-codex-app-server-launch)
6088
+ check_codex_app_server_launch
6089
+ ;;
5876
6090
  check-copilot-launch)
5877
6091
  check_copilot_launch
5878
6092
  ;;
@@ -5921,6 +6135,9 @@ case "$cmd" in
5921
6135
  check-entwurf-control-rpc)
5922
6136
  check_entwurf_control_rpc
5923
6137
  ;;
6138
+ check-compaction-send-guard)
6139
+ check_compaction_send_guard
6140
+ ;;
5924
6141
  check-entwurf-v2-production)
5925
6142
  check_entwurf_v2_production
5926
6143
  ;;
@@ -6248,6 +6465,16 @@ case "$cmd" in
6248
6465
  # LIVE!=1. doctor-static preflight FAILs before the agy bridge is wired (③).
6249
6466
  run_ts scripts/smoke-agy-native-push-live.ts
6250
6467
  ;;
6468
+ smoke-codex-native-push-live)
6469
+ # Codex native-push LIVE acceptance. The operator owns both the app-server and visible
6470
+ # attached TUI; this command only proves the shipped record/probe/delivery composition.
6471
+ run_ts scripts/smoke-codex-native-push-live.ts
6472
+ ;;
6473
+ smoke-codex-fresh-live)
6474
+ # First-admission LIVE acceptance: real visible Pi -> Codex -> Pi, using only the
6475
+ # operator-owned app-server explicitly named by ENTWURF_CODEX_APP_SERVER_PID.
6476
+ run_ts scripts/smoke-codex-fresh-live.ts
6477
+ ;;
6251
6478
  smoke-user-scope-citizen)
6252
6479
  # 0.12.6 install-boundary gate: register-pi-package.py is the shared
6253
6480
  # packages[] SSOT for project/user install and remove; user scope makes
@@ -6454,6 +6681,65 @@ case "$cmd" in
6454
6681
  shift || true
6455
6682
  run_ts scripts/omp-receive-facts.ts "$@"
6456
6683
  ;;
6684
+ install-codex-birth)
6685
+ shift || true
6686
+ (cd "$REPO_DIR" && bash scripts/codex-birth-install.sh "$@")
6687
+ ;;
6688
+ uninstall-codex-birth)
6689
+ shift || true
6690
+ (cd "$REPO_DIR" && bash scripts/codex-birth-uninstall.sh "$@")
6691
+ ;;
6692
+ doctor-codex-birth)
6693
+ shift || true
6694
+ (cd "$REPO_DIR" && bash scripts/codex-birth-doctor.sh "$@")
6695
+ ;;
6696
+ install-codex-mcp)
6697
+ codex_mcp install
6698
+ ;;
6699
+ uninstall-codex-mcp)
6700
+ codex_mcp uninstall
6701
+ ;;
6702
+ doctor-codex-mcp)
6703
+ codex_mcp doctor
6704
+ ;;
6705
+ install-codex-statusline)
6706
+ codex_statusline install
6707
+ ;;
6708
+ uninstall-codex-statusline)
6709
+ codex_statusline uninstall
6710
+ ;;
6711
+ doctor-codex-statusline)
6712
+ codex_statusline doctor
6713
+ ;;
6714
+ install-codex-terminal-title)
6715
+ codex_terminal_title install
6716
+ ;;
6717
+ uninstall-codex-terminal-title)
6718
+ codex_terminal_title uninstall
6719
+ ;;
6720
+ doctor-codex-terminal-title)
6721
+ codex_terminal_title doctor
6722
+ ;;
6723
+ codex-socket-path)
6724
+ # #95 INTERNAL: print the Codex app-server default control-socket path for this
6725
+ # environment. It exists because `entwurf codex-app-server` is bash and cannot import
6726
+ # `resolveCodexDefaultSocketPath`; re-deriving that path in bash was MEASURED to diverge
6727
+ # (BOM-only CODEX_HOME, path normalization), so the launcher asks instead of transcribing.
6728
+ # `run_ts` is the crossing (compiled twin when installed, strip-types in a clone), which is
6729
+ # the whole reason this sits in the dispatcher rather than inside the leaf.
6730
+ shift || true
6731
+ run_ts scripts/codex-socket-path.ts "$@"
6732
+ ;;
6733
+ codex-app-server)
6734
+ # #95: the managed app-server SPELLING. Same shape and same reasons as the `copilot`
6735
+ # branch below — `exec`, no subshell and no cd, because this operates on the operator's
6736
+ # own session rather than on the repo, and the `shift` keeps the dispatcher verb out of
6737
+ # the vendor's argv. It is NOT the same KIND of thing: `entwurf copilot` becomes a
6738
+ # sibling-bearing CLI, while this becomes the long-lived server those siblings' MCP
6739
+ # children hang off. Neither one supervises what it becomes.
6740
+ shift || true
6741
+ exec bash "$REPO_DIR/scripts/codex-app-server-launch.sh" "$@"
6742
+ ;;
6457
6743
  copilot)
6458
6744
  # #82 RAIL 7: the managed launch. `exec` and NO subshell/cd on purpose — the vendor
6459
6745
  # must inherit this terminal exactly: the caller's cwd, this pid, this tty, and its own
@@ -6745,6 +7031,34 @@ case "$cmd" in
6745
7031
  # Hermetic (mkdtemp worlds + the store env seam); no model, no network.
6746
7032
  (cd "$REPO_DIR" && bash scripts/check-fresh-cut-gate.sh "$@")
6747
7033
  ;;
7034
+ check-codex-sender-identity)
7035
+ shift || true
7036
+ run_ts scripts/check-codex-sender-identity.ts "$@"
7037
+ ;;
7038
+ check-codex-bridge-identity)
7039
+ shift || true
7040
+ run_ts scripts/check-codex-bridge-identity.ts "$@"
7041
+ ;;
7042
+ check-codex-native-push)
7043
+ shift || true
7044
+ run_ts scripts/check-codex-native-push.ts "$@"
7045
+ ;;
7046
+ check-codex-birth-hook)
7047
+ shift || true
7048
+ run_ts scripts/check-codex-birth-hook.ts "$@"
7049
+ ;;
7050
+ check-codex-fresh-preflight)
7051
+ shift || true
7052
+ (cd "$REPO_DIR" && pnpm exec vitest run test/codex-fresh-preflight.test.ts "$@")
7053
+ ;;
7054
+ smoke-codex-config-state)
7055
+ shift || true
7056
+ (cd "$REPO_DIR" && bash scripts/smoke-codex-config-state.sh "$@")
7057
+ ;;
7058
+ smoke-codex-birth)
7059
+ shift || true
7060
+ (cd "$REPO_DIR" && bash scripts/smoke-codex-birth.sh "$@")
7061
+ ;;
6748
7062
  check-pack)
6749
7063
  check_pack
6750
7064
  ;;