@junghanacs/entwurf 0.21.0 → 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.
- package/AGENTS.md +2 -2
- package/BASELINE.md +3 -1
- package/CHANGELOG.md +278 -0
- package/DELIVERY.md +156 -26
- package/README.md +64 -13
- package/VERIFY.md +67 -11
- package/docs/external-mcp-host.md +16 -6
- package/docs/setup-clean-host.md +63 -22
- package/mcp/entwurf-bridge/dist/mcp/entwurf-bridge/src/index.js +40 -16
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/codex-caller-seat.js +174 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/codex-fresh-preflight.js +194 -1
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-runner.js +3 -2
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-send.js +8 -4
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/mux-fresh-call.js +160 -25
- package/mcp/entwurf-bridge/dist/scripts/codex-socket-path.js +30 -0
- package/mcp/entwurf-bridge/src/index.ts +50 -16
- package/mcp/entwurf-bridge/tsconfig.build.json +9 -0
- package/package.json +2 -2
- package/pi-extensions/entwurf-control.ts +4 -4
- package/pi-extensions/lib/codex-caller-seat.ts +204 -0
- package/pi-extensions/lib/codex-fresh-preflight.ts +218 -1
- package/pi-extensions/lib/entwurf-v2-runner.ts +3 -2
- package/pi-extensions/lib/entwurf-v2-send.ts +16 -11
- package/pi-extensions/lib/mux-fresh-call.ts +196 -37
- package/run.sh +127 -3
- package/scripts/check-codex-app-server-launch.ts +445 -0
- package/scripts/check-entwurf-v2-production.ts +42 -1
- package/scripts/check-entwurf-v2-send.ts +26 -7
- package/scripts/check-gate-qualification.ts +4 -2
- package/scripts/check-mux-launch-tmux.ts +331 -35
- package/scripts/codex-app-server-launch.sh +275 -0
- package/scripts/codex-socket-path.ts +33 -0
- package/scripts/codex-terminal-title-config.py +500 -0
- package/scripts/codex_toml_io.py +121 -0
- package/scripts/lib/codex-fresh-live-protocol.ts +11 -3
- package/scripts/lib/codex-fresh-source-receipts.ts +29 -2
- package/scripts/mutants/codex-app-server-launch.json +157 -0
- package/scripts/mutants/codex-caller-seat.json +336 -0
- package/scripts/mutants/codex-native.json +3 -3
- package/scripts/mutants/mux-fresh-call.json +86 -14
- package/scripts/mutants/v2-surface.json +22 -0
- package/scripts/smoke-codex-config-state.sh +192 -3
- package/scripts/smoke-codex-fresh-live.ts +277 -37
- package/scripts/smoke-entwurf-chain-live.ts +50 -0
- package/scripts/smoke-setup-verdict.sh +13 -11
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@ npm package: <https://www.npmjs.com/package/@junghanacs/entwurf>
|
|
|
10
10
|
|
|
11
11
|
Legacy package: [`@junghanacs/pi-shell-acp`](https://www.npmjs.com/package/@junghanacs/pi-shell-acp). `entwurf` is its 0.12+ successor line: the same work renamed around the garden-citizen dispatch substrate rather than the pi adapter.
|
|
12
12
|
|
|
13
|
-
> **Repository shape.** This repo is **entwurf-core (v2 dispatch) + native-harness bridges + a pi adapter + an ACP plugin**. Pi is one adapter, not the project subject. Claude Code, GitHub Copilot CLI, and OMP (`omp`) are shipped self-fetch citizens; Antigravity (`agy`) is shipped native-push. Codex CLI is a **native-push citizen, supported in 0.21.0 on Linux** over the operator-owned app-server: vendor-trusted user-scope birth, strict request-scoped identity, loaded-thread probe, one-shot queue delivery, and visible fresh
|
|
13
|
+
> **Repository shape.** This repo is **entwurf-core (v2 dispatch) + native-harness bridges + a pi adapter + an ACP plugin**. Pi is one adapter, not the project subject. Claude Code, GitHub Copilot CLI, and OMP (`omp`) are shipped self-fetch citizens; Antigravity (`agy`) is shipped native-push. Codex CLI is a **native-push citizen, supported in 0.21.0 on Linux** over the operator-owned app-server: vendor-trusted user-scope birth, strict request-scoped identity, loaded-thread probe, one-shot queue delivery, and visible fresh beside the CALLER's own pane. Unrestricted attached-TUI seat inference is not supported or claimed; #95 D1 retired the fixed `codex` tmux home on 2026-09-16, so the operator seats the app-server wherever they like and a Codex caller opens its sibling beside its own TUI pane. Codex remains native so it keeps its vendor tools, delegation, and work context; this is not another way to buy or expose GPT through ACP.
|
|
14
14
|
|
|
15
15
|
```text
|
|
16
16
|
Claude Code / Copilot / Codex / agy / omp / pi
|
|
@@ -19,7 +19,7 @@ Claude Code / Copilot / Codex / agy / omp / pi
|
|
|
19
19
|
→ control-socket | meta-mailbox | native-push
|
|
20
20
|
```
|
|
21
21
|
|
|
22
|
-
[`entwurf_v2`](#entwurf_v2--canonical-dispatch-verb) is the canonical dispatch surface over *existing* garden citizens — live control-socket send, meta-mailbox enqueue, and native-push into a live Antigravity conversation or loaded Codex thread. It starts no process on any rail. `entwurf_fresh_call` is separate lifecycle. With
|
|
22
|
+
[`entwurf_v2`](#entwurf_v2--canonical-dispatch-verb) is the canonical dispatch surface over *existing* garden citizens — live control-socket send, meta-mailbox enqueue, and native-push into a live Antigravity conversation or loaded Codex thread. It starts no process on any rail. `entwurf_fresh_call` is separate lifecycle. With placement omitted the seat follows the CALLER: a Codex citizen opens beside its own TUI pane, found by the `thread-id` in that pane's terminal title, and 0 or 2+ matching panes reject with no fallback; every other caller opens in its own session. A missing app-server rejects before launch, and Entwurf never creates or supervises it. An explicit `placement.tmuxSession` remains an expert override. Exact 0.153.4 source confirms that arbitrary attached-TUI request→seat inference is unavailable, so Entwurf makes no such claim. Codex has no resume surface.
|
|
23
23
|
|
|
24
24
|
**Garden id is deliberate vocabulary.** It is not a decorative synonym for session id, worker, delegate, or subagent. The unfamiliar word is a guard: each harness keeps its own identity and transcript, while `entwurf` supplies a narrow addressable surface between siblings.
|
|
25
25
|
|
|
@@ -152,6 +152,55 @@ entwurf check-bridge
|
|
|
152
152
|
whatever native harnesses are present. The global install is the easiest path when
|
|
153
153
|
Claude Code's USER-scope MCP registration should work from every cwd.
|
|
154
154
|
|
|
155
|
+
#### Codex: what you still type by hand
|
|
156
|
+
|
|
157
|
+
`setup` writes every byte Entwurf owns, and three things are deliberately left to you. They
|
|
158
|
+
are the only manual steps between a fresh install and calling a Codex sibling, and all three are
|
|
159
|
+
answers only you can give:
|
|
160
|
+
|
|
161
|
+
1. **Trust the birth hook once, in a visible plain Codex.** Answer `Trust all and continue`
|
|
162
|
+
and send one first turn. Nobody can do this for you: the receipt is the vendor's record of
|
|
163
|
+
*your* security decision, and writing it ourselves would turn a prompt into a silent
|
|
164
|
+
install. Until it exists, `setup` and `doctor-codex-birth` stay honestly non-green and say
|
|
165
|
+
exactly this.
|
|
166
|
+
2. **Start the app-server, in a detached tmux session that is NOT where you work.**
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
entwurf codex-app-server
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
One command for the vendor string nobody wants to type. It `exec`s
|
|
173
|
+
`codex app-server --listen unix://<default socket>` right there — so Ctrl-C is yours, and
|
|
174
|
+
Entwurf neither supervises nor restarts it. The session matters because the MCP bridge is
|
|
175
|
+
this server's child and inherits its `TMUX`: that tmux server is the one caller-seat
|
|
176
|
+
lookups read. The launcher prints which seat it got rather than guessing one for you.
|
|
177
|
+
3. **Answer the folder-consent prompt once for each directory you open siblings in.** Codex
|
|
178
|
+
records a direct decision per EXACT directory — not the parent, not the git root — and a
|
|
179
|
+
directory with no answer anywhere opens a consent screen instead of running a first turn.
|
|
180
|
+
`entwurf_fresh_call` prints `codex-launch-cwd-undecided` and opens the window anyway — the
|
|
181
|
+
screen is self-repairing, and answering it once is the whole fix. (Two neighbours differ: a
|
|
182
|
+
project `.codex` layer can consent with no entry at all, and a directory inside an explicitly
|
|
183
|
+
untrusted project is noted as `codex-launch-cwd-untrusted-ancestor` because the vendor answers
|
|
184
|
+
that one with an error naming the repository root instead of a prompt.) You can also do it up
|
|
185
|
+
front:
|
|
186
|
+
|
|
187
|
+
```bash
|
|
188
|
+
codex -C <the directory you will launch siblings in> # answer the prompt, then quit
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
Same reason as step 1: it is your security decision, and writing it ourselves would turn a
|
|
192
|
+
prompt into a silent install.
|
|
193
|
+
|
|
194
|
+
Then `entwurf_fresh_call` with `backend: "codex"` works. Attaching your own visible Codex TUI
|
|
195
|
+
(`codex --remote "unix://$CODEX_HOME/app-server-control/app-server-control.sock"`) is optional
|
|
196
|
+
and independent — a sibling Entwurf opens brings its own.
|
|
197
|
+
|
|
198
|
+
> **Observation, not a verb (#95).** That `--remote` string is exactly as unfriendly as the
|
|
199
|
+
> `--listen` one was, and it has no managed spelling. It is deliberately left alone: it is how
|
|
200
|
+
> an operator attaches their OWN terminal, not how a sibling is opened, and `entwurf_fresh_call`
|
|
201
|
+
> already spells it internally for the siblings it launches. Whether the operator-facing half
|
|
202
|
+
> deserves its own verb is open.
|
|
203
|
+
|
|
155
204
|
### From npm — project-local install
|
|
156
205
|
|
|
157
206
|
```bash
|
|
@@ -259,7 +308,7 @@ repair surface: each unit has its own installer, doctor, and inverse.
|
|
|
259
308
|
- **Antigravity / agy** (Linux CERTIFIED; macOS NOT CERTIFIED — pending physical host) — `install-agy-bridge`, `install-agy-statusline`, `install-agy-hooks`, each with a matching `doctor-agy-*`.
|
|
260
309
|
- **GitHub Copilot CLI** (Linux CERTIFIED; macOS NOT CERTIFIED — pending physical host) — four independent units, four independent failure modes: `install-copilot-bridge` (birth: garden id + who-sent, on the first prompt), `install-copilot-mcp` (the entwurf tool hand, where `entwurf_inbox_read` lives), `install-copilot-receive` (the receiver extension: doorbell + receiver marker), `install-copilot-statusline` (optional for a manual citizen, required for supported fresh) — each with a matching `doctor-copilot-*` and `uninstall-copilot-*`.
|
|
261
310
|
- **OMP (`omp`)** (Linux CERTIFIED; macOS NOT CERTIFIED — pending physical host) — four units, in-process extensions rather than launchers: `install-omp-bridge` (birth: the `mode === "tui"` visible host, its garden id on the status line, and who-sent), `install-omp-mcp` (the omp-native `entwurf-bridge` entry), `install-omp-config` (the one operator setting `tools: xdev: false`, without which the vendor mounts MCP tools as `xd://` devices the model cannot call), `install-omp-receive` (the receiver extension: mailbox watch + announce-only doorbell) — each with a matching `uninstall-omp-*`, and a `doctor-omp-*` for all but the setting, whose runtime axis `doctor-omp-mcp` owns. The setting writer owns exactly the lines it adds and refuses an explicit operator `tools: xdev: true` by name rather than overwriting it.
|
|
262
|
-
- **OpenAI Codex CLI** (supported in 0.21.0 on Linux; macOS NOT CERTIFIED — pending physical host) — `install-codex-birth` publishes the `SessionStart` declaration into `$CODEX_HOME/hooks.json` with its launcher closure under `$XDG_DATA_HOME/entwurf/codex-birth`, all operator-owned; the vendor trust receipt for that declaration is the operator's single answer, and `doctor-codex-birth` reports it as its own axis (present, or red with the exact instruction — never computed, never written); `install-codex-mcp` owns `[mcp_servers.entwurf-bridge]`, including the `env_vars` boundary for `CODEX_HOME`, Entwurf garden/control roots, and the app-server's `TMUX`/`TMUX_PANE`; `install-codex-statusline` owns `thread-title`. Each has a matching doctor and inverse.
|
|
311
|
+
- **OpenAI Codex CLI** (supported in 0.21.0 on Linux; macOS NOT CERTIFIED — pending physical host) — `install-codex-birth` publishes the `SessionStart` declaration into `$CODEX_HOME/hooks.json` with its launcher closure under `$XDG_DATA_HOME/entwurf/codex-birth`, all operator-owned; the vendor trust receipt for that declaration is the operator's single answer, and `doctor-codex-birth` reports it as its own axis (present, or red with the exact instruction — never computed, never written); `install-codex-mcp` owns `[mcp_servers.entwurf-bridge]`, including the `env_vars` boundary for `CODEX_HOME`, Entwurf garden/control roots, and the app-server's `TMUX`/`TMUX_PANE`; `install-codex-statusline` owns `thread-title` in `tui.status_line`; `install-codex-terminal-title` owns `thread-id` in `tui.terminal_title`. Each has a matching doctor and inverse. **One Codex precondition is the vendor's own and no installer supplies it: the directory a sibling starts in must already be answered in this Codex.** A direct consent decision is recorded per exact directory on this rail — not the parent, not the git root — and a directory the vendor has no answer for at all opens a consent screen instead of running a first turn, so `entwurf_fresh_call` prints `codex-launch-cwd-undecided` and opens the window anyway; that screen is self-repairing, and answering it once teaches the vendor the directory for good. **Nothing here refuses a launch** — the note is a diagnostic, and the one caller that treats it as a precondition is `smoke-codex-fresh-live`, where nobody is at the keyboard. Two neighbouring cases read differently: a directory answered `untrusted` is not noted at all (the vendor skips its screen on this rail, so that turn starts), and a directory INSIDE an explicitly untrusted project is noted as `codex-launch-cwd-untrusted-ancestor` because the vendor answers that one with an error naming the repository root rather than a prompt. The check is a narrow local read of the operator's own config, never the vendor's verdict: the vendor also consents through project layers it reads from its app-server, and everything the check cannot see stays silent. The operator owns the app-server and seats it wherever they like; Entwurf never creates or supervises it. Omitted fresh placement follows the CALLER, never the backend being opened: an explicit `placement` always wins, then a Codex CALLER opens beside its own TUI pane — located by the `thread-id` that pane's terminal title carries, with 0 or 2+ matching panes refused and no fallback — then the caller's own session. (#95 first shipped a fixed `codex` home for omitted-placement Codex TARGETS; #95 D1 retired it on 2026-09-16 once the anchor made the room unnecessary.) A pane title is a placement input only, never an address, liveness or delivery fact. Exact 0.153.4 source's missing per-client carrier still bounds placing a sibling beside a TUI whose thread nobody named as unsupported.
|
|
263
312
|
|
|
264
313
|
Run them as `entwurf <command>`. Which unit a doctor's refusal names, and the clean-host
|
|
265
314
|
walk-through for each harness, live in [docs/setup-clean-host.md](./docs/setup-clean-host.md).
|
|
@@ -454,8 +503,8 @@ The Claude ACP backend keeps its native model / API / tools; entwurf shapes only
|
|
|
454
503
|
|
|
455
504
|
Codex is not an ACP backend here. Its native-push lane, supported in 0.21.0, uses `SessionStart`
|
|
456
505
|
for birth, strict request `_meta` for who-sent, and a loaded app-server thread as the live route.
|
|
457
|
-
|
|
458
|
-
|
|
506
|
+
A Codex CALLER with placement omitted opens its sibling beside its own TUI pane, matched by the
|
|
507
|
+
`thread-id` its terminal title carries; a pane whose thread nobody named is never inferred.
|
|
459
508
|
|
|
460
509
|
Antigravity is also not an ACP backend. It is a native-push citizen: `PreInvocation` supplies birth/sender identity, `entwurf_v2` probes and direct-injects replies into the live conversation, and no mailbox/receiver marker is involved.
|
|
461
510
|
|
|
@@ -487,16 +536,18 @@ LIVE=1 AGY_CONVERSATION_ID=<id> ./run.sh smoke-agy-native-push-live
|
|
|
487
536
|
# Codex loaded-thread probe (pre-amendment evidence; not first-release acceptance):
|
|
488
537
|
LIVE=1 CODEX_LIVE_THREAD_ID=<threadId> ./run.sh smoke-codex-native-push-live
|
|
489
538
|
|
|
490
|
-
# Codex
|
|
491
|
-
#
|
|
492
|
-
#
|
|
539
|
+
# Codex caller-seat acceptance. The operator-owned app-server must sit in a session OTHER than
|
|
540
|
+
# the one the Pi/Codex pair runs in — that separation is what tells the caller-pane anchor apart
|
|
541
|
+
# from the app-server's inherited environment. This smoke ALSO needs its one stable launch
|
|
542
|
+
# directory answered once (manual step 3 above); VERIFY.md's Codex section owns that procedure,
|
|
543
|
+
# including how to derive the exact path instead of retyping it. Record all four coordinates:
|
|
493
544
|
LIVE=1 ENTWURF_CODEX_APP_SERVER_PID=<existing-app-server-pid> \
|
|
494
545
|
ENTWURF_CODEX_FRESH_MODEL=<codex-model> \
|
|
495
546
|
ENTWURF_CODEX_FRESH_PI_MODEL=<pi-model> \
|
|
496
547
|
./run.sh smoke-codex-fresh-live
|
|
497
|
-
# This accepts only a real visible Pi → visible Codex → visible Pi run: initial Pi
|
|
498
|
-
# the
|
|
499
|
-
#
|
|
548
|
+
# This accepts only a real visible Pi → visible Codex → visible Pi run: initial Pi in a session
|
|
549
|
+
# other than the app-server's, then Codex and its outbound Pi beside it. A fixture first leg may
|
|
550
|
+
# collect receipts but does not satisfy the topology. No arbitrary attached-TUI parity is claimed.
|
|
500
551
|
|
|
501
552
|
# ACP plugin LIVE acceptance — need the operator's local Claude auth/credit:
|
|
502
553
|
LIVE=1 ./run.sh smoke-acp-socket-citizen-live # turn-free socket citizenship (S1)
|
|
@@ -572,7 +623,7 @@ A two-pane recording covers the pre-0.12 v1 surface end-to-end — sibling resum
|
|
|
572
623
|
|
|
573
624
|
Live peer messaging carries a sender envelope `{ sessionId, agentId, cwd, timestamp, origin?, replyable? }`; `entwurf_self` returns that authoritative envelope for the current pi session or trusted meta-session. Plain external MCP hosts are non-replyable. A garden-native meta-session carries a trusted `meta-session` envelope, but **`replyable` is a fact its own rail decides, not a consequence of being trusted** — a self-fetch citizen needs a live armed receiver, a native-push citizen needs an alive adapter probe, and a pi session needs its control socket. `entwurf_self` also reports which rail a meta-session reply would ride, because a native-push citizen has no mailbox to name. `wants_reply` is an etiquette marker rendered as a `(wants reply)` badge — not a transport contract, no wait, no polling. **v2 never gates on it:** a `wants_reply` from an external/non-replyable caller is passed through and surfaced honestly beside that sender's `replyable: false`, not rejected — the decider routes on target + intent, never on sender replyability. (The retired v1 `entwurf_send` did reject it; that behaviour went with the verb.)
|
|
574
625
|
|
|
575
|
-
In ACP-backed and external native-harness sessions, `entwurf-bridge` exposes seven tools: `entwurf_v2`, `entwurf_peers`, `entwurf_fresh_call`, `entwurf_resume_call`, `entwurf_self`, `entwurf_inbox_read`, and the explicit/manual `entwurf_register_native` fallback. Native pi exposes the shared capability directly through the extension surface (`entwurf_v2`, `entwurf_peers`, `entwurf_fresh_call`, `entwurf_resume_call` tools; the socket-scan `/entwurf-sessions` command is gone — #50 C4). **For garden-id delivery/reply use `entwurf_v2`** — the canonical surface that classifies the target and routes to live-pi / self-fetch meta-mailbox (Claude Code, Copilot, OMP) / native-push (Antigravity, Codex), and refuses a dormant target instead of waking it. **To open a sibling that does not exist yet use `entwurf_fresh_call`** — it launches one of five fixed backends (`pi`, `claude-code`, `copilot`, `omp`, `codex`) as a visible window on the operator's own tmux server, requires an explicit model, accepts one optional literal absolute `cwd` (omit it or pass `""` to use the caller's cwd) and one optional `placement.tmuxSession` naming an EXISTING session on that server to open the sibling in instead of the caller's own (the operator's project seat — nothing is ever created: an absent session is `tmux-session-missing` and a name outside the addressed grammar `[A-Za-z0-9][A-Za-z0-9_-]*` is `tmux-session-name-invalid`, with no window and no session left behind. The two inputs never infer each other, and the positive form of that matters more than the prohibition: **with `cwd` omitted, a seated sibling starts in THIS agent's own working directory**, measured — it does not inherit the target session's path or its active pane, so naming the `org` seat does not put the sibling in the `org` project. Pass `cwd` when you want a different directory. entwurf never resolves that path: it hands the literal string to tmux, so with a SYMLINKED directory the receipt echoes what you asked for while the record, the transcript path, the callback envelope and `pane_current_path` all show the path the OS resolved it to — two spellings of one place, and the resolved one is what `entwurf_resume_call` later reopens), passes the model and selected directory through the runtime's visible launch path, and hands it a first task; a `copilot` launch goes through entwurf's own managed invocation and is refused before any window opens if this host lacks the Copilot birth, MCP, receiver or visible-footer units, and an `omp` launch carries its task in the `--entwurf-bootstrap` payload instead of an argv prompt because that vendor connects its MCP tools after the session has already started. Omitted placement
|
|
626
|
+
In ACP-backed and external native-harness sessions, `entwurf-bridge` exposes seven tools: `entwurf_v2`, `entwurf_peers`, `entwurf_fresh_call`, `entwurf_resume_call`, `entwurf_self`, `entwurf_inbox_read`, and the explicit/manual `entwurf_register_native` fallback. Native pi exposes the shared capability directly through the extension surface (`entwurf_v2`, `entwurf_peers`, `entwurf_fresh_call`, `entwurf_resume_call` tools; the socket-scan `/entwurf-sessions` command is gone — #50 C4). **For garden-id delivery/reply use `entwurf_v2`** — the canonical surface that classifies the target and routes to live-pi / self-fetch meta-mailbox (Claude Code, Copilot, OMP) / native-push (Antigravity, Codex), and refuses a dormant target instead of waking it. **To open a sibling that does not exist yet use `entwurf_fresh_call`** — it launches one of five fixed backends (`pi`, `claude-code`, `copilot`, `omp`, `codex`) as a visible window on the operator's own tmux server, requires an explicit model, accepts one optional literal absolute `cwd` (omit it or pass `""` to use the caller's cwd) and one optional `placement.tmuxSession` naming an EXISTING session on that server to open the sibling in instead of the caller's own (the operator's project seat — nothing is ever created: an absent session is `tmux-session-missing` and a name outside the addressed grammar `[A-Za-z0-9][A-Za-z0-9_-]*` is `tmux-session-name-invalid`, with no window and no session left behind. The two inputs never infer each other, and the positive form of that matters more than the prohibition: **with `cwd` omitted, a seated sibling starts in THIS agent's own working directory**, measured — it does not inherit the target session's path or its active pane, so naming the `org` seat does not put the sibling in the `org` project. Pass `cwd` when you want a different directory. entwurf never resolves that path: it hands the literal string to tmux, so with a SYMLINKED directory the receipt echoes what you asked for while the record, the transcript path, the callback envelope and `pane_current_path` all show the path the OS resolved it to — two spellings of one place, and the resolved one is what `entwurf_resume_call` later reopens), passes the model and selected directory through the runtime's visible launch path, and hands it a first task; a `copilot` launch goes through entwurf's own managed invocation and is refused before any window opens if this host lacks the Copilot birth, MCP, receiver or visible-footer units, and an `omp` launch carries its task in the `--entwurf-bootstrap` payload instead of an argv prompt because that vendor connects its MCP tools after the session has already started. Omitted placement follows the CALLER rather than the backend being opened: a Codex CALLER opens its sibling beside its own TUI pane, located by the `thread-id` that pane's terminal title carries (0 or 2+ matching panes reject with no fallback); every other caller opens in its own seat. (0.21.0 shipped a backend-selected default instead — an omitted-placement `codex` TARGET resolved a fixed operator-owned session named `codex` — and #95 D1 retired it on 2026-09-16.) Any of the five can still be sent to an operator-owned existing seat with an explicit `placement.tmuxSession`, and named `claude`/`pi` seats remain available that way, but they are not omitted-placement defaults. Read tmux here as the VISIBLE LAUNCH SEAT, never as an address: the garden id is the address, and it arrives in the callback envelope. Sibling symmetry means each backend keeps its own transcript, auth, native tools and visibility — not that the transports are made identical. The sibling's first action is a nonce callback whose sender envelope carries its garden id. The launch receipt records the requested model/cwd/seat plus tmux coordinates — including the RESOLVED target session id, which is where the window actually is — and never claims that the runtime accepted them or completed delivery. (The v1 verbs `entwurf` / `entwurf_resume` / `entwurf_send` are gone.) Garden-native operator commands require `--entwurf-control`. There is no spawn target allowlist — the target registry is gone (#50 C3): `entwurf_v2` and `entwurf_resume_call` address an existing record-backed citizen, while `entwurf_fresh_call` takes its explicit backend/model/task and optional cwd directly rather than resolving a model tuple from a registry file. **To reopen a DORMANT pi citizen use `entwurf_resume_call {target}`** — the record supplies transcript, model, provider and cwd, so it takes no prompt, no task and no model override; it runs no turn, returns a LAUNCH receipt and a separate OBSERVATION receipt (only the second says the citizen is back), refuses a non-pi target as `target-not-pi`, and on an unobserved socket leaves the visible window open and releases its lock rather than retrying.
|
|
576
627
|
|
|
577
628
|
### `entwurf_v2` — canonical dispatch verb
|
|
578
629
|
|
|
@@ -591,7 +642,7 @@ In ACP-backed and external native-harness sessions, `entwurf-bridge` exposes sev
|
|
|
591
642
|
|
|
592
643
|
**`entwurf_v2` is the canonical surface for garden-id delivery.** When you have a garden id and want to reach whoever it names — message, reply, or hand-off — `entwurf_v2` is the one surface that reads whether the target is live pi, dormant pi, mailbox-backed Claude Code, Copilot or OMP, or native-push Antigravity or Codex, and routes correctly; *when unsure which transport, use `entwurf_v2`*. This prevents callers from guessing a rail from the shape of an id.
|
|
593
644
|
|
|
594
|
-
What v2 provides is a **deterministic dispatch substrate** that moves the "which transport?" decision out of the fallible caller/model and into the decider, with transport-appropriate locking and an honest reject (no `✓ delivered`, no `.msg` garbage) when a target cannot receive. It still does **not** mint siblings, and it does not relaunch one either: every row above either reaches a citizen that is already running or refuses. Reopening a dormant pi citizen is `entwurf_resume_call`, a separate lifecycle verb that never routes through this decider. Fresh creation is the separate `entwurf_fresh_call` verb. It opens one of five fixed runtimes — Pi, Claude Code, Copilot, OMP or Codex — visibly on the caller's tmux server. Omitting placement
|
|
645
|
+
What v2 provides is a **deterministic dispatch substrate** that moves the "which transport?" decision out of the fallible caller/model and into the decider, with transport-appropriate locking and an honest reject (no `✓ delivered`, no `.msg` garbage) when a target cannot receive. It still does **not** mint siblings, and it does not relaunch one either: every row above either reaches a citizen that is already running or refuses. Reopening a dormant pi citizen is `entwurf_resume_call`, a separate lifecycle verb that never routes through this decider. Fresh creation is the separate `entwurf_fresh_call` verb. It opens one of five fixed runtimes — Pi, Claude Code, Copilot, OMP or Codex — visibly on the caller's tmux server. Omitting placement follows the CALLER rather than the backend being opened: an explicit seat always wins; then a Codex CALLER opens beside its own TUI pane, found by the `thread-id` in that pane's terminal title (0 or 2+ matching panes reject, with no fallback); then the caller's own session. An explicit `placement.tmuxSession` sends ANY of the five to an existing named session instead (see the `entwurf_fresh_call` paragraph above for the owning detail). Each launch carries a required explicit model passed in that runtime's measured CLI dialect and one optional literal absolute `cwd`; omitted or `""` means the caller's cwd. Copilot opens through `entwurf copilot`, never the bare vendor; OMP is the opposite — the bare `omp` runtime with no positional prompt at all, because that vendor connects its MCP tools after the session has started, so the task rides a two-stage `--entwurf-bootstrap` payload the installed birth extension releases once the callback tool has actually answered. It returns only a synchronous launch receipt and lets the sibling report its new address asynchronously through the sender envelope of a nonce callback. Use this cwd input for a new cross-repository sibling; do not resume a dormant citizen as a cwd substitute. The meta-mailbox row requires an **active** self-fetch receiver; native-push requires a record-backed, probe-alive native conversation and never borrows mailbox state. The [mux launch lane](./docs/mux-launch-rail.md) owns placement, fixed-runtime launch, and the two narrow compositions above it (fresh-call and resume-call placement); delivery does not import launch, and mux is not a delivery transport.
|
|
595
646
|
|
|
596
647
|
A live pi target is *reached* over its control socket, but the socket is dispatch-internal transport, never identity (#50 C4). A control socket that no meta-record claims — a pre-record-era resident, an unreadable store, or a stale/planted file — is refused for **every** intent as `record-less-socket`, and the reject names the fix (restart the resident so `session_start` births its record, or quiesce and run the fresh-cut). `entwurf_peers` reports the same state as an aggregated `record-less-socket` diagnostic rather than a peer row.
|
|
597
648
|
|
package/VERIFY.md
CHANGED
|
@@ -71,26 +71,62 @@ Verification here is not a benchmark. In production we exchange short turns and
|
|
|
71
71
|
> The aggregate release gate does not own a live agy conversation id, so agy's real native-push round trip is a separate acceptance axis: three fail-loud doctors plus `LIVE=1 AGY_CONVERSATION_ID=<id> ./run.sh smoke-agy-native-push-live`, followed by a fresh-conversation sender/reply check after package install. Its deterministic install/sender gates are already inside `pnpm run check:full`; do not misreport the aggregate gate as live agy evidence. **Cost fence:** the agy conversation this smoke drives runs on a free account, so open it on `gemini-3.6-flash` — never a Pro tier. The model is the operator's choice at conversation-open time; entwurf never selects it, and no assertion reads it (see the shipped-lane note: model display is not part of the agy contract).
|
|
72
72
|
>
|
|
73
73
|
> Aggregate release-gate does not own a loaded Codex thread. Codex native-push remains an
|
|
74
|
-
> on-demand host axis. The
|
|
75
|
-
>
|
|
76
|
-
>
|
|
77
|
-
>
|
|
78
|
-
>
|
|
79
|
-
>
|
|
80
|
-
> and
|
|
74
|
+
> on-demand host axis. The first-admission acceptance passed on 2026-09-12 under the fixed `codex`
|
|
75
|
+
> home topology #95 D1 later retired; `DELIVERY.md` owns its 57-assertion receipt and digest as
|
|
76
|
+
> history. Re-runs follow the caller-seat topology instead: the operator-owned app-server sits in
|
|
77
|
+
> its own session A, the initial Pi and the Codex it opens both sit in a DIFFERENT session S, and
|
|
78
|
+
> the Codex-opened Pi must land in S. **A ≠ S is a precondition, not a preference** — the
|
|
79
|
+
> app-server's inherited `TMUX` names A, so an outbound Pi in S can only have come from the
|
|
80
|
+
> caller's own pane title, and the receipt must name `codex-title-anchor` as the rule that chose
|
|
81
|
+
> it. Exact `rust-v0.153.4` source still exposes no arbitrary request→attached-TUI-seat value, so
|
|
82
|
+
> placing a sibling beside a TUI whose thread nobody named stays unsupported and unclaimed.
|
|
81
83
|
>
|
|
82
84
|
> The A cell takes no inferred server or models:
|
|
83
85
|
> `LIVE=1 ENTWURF_CODEX_APP_SERVER_PID=<existing-app-server-pid>
|
|
84
86
|
> ENTWURF_CODEX_FRESH_MODEL=<codex-model>
|
|
85
87
|
> ENTWURF_CODEX_FRESH_PI_MODEL=<pi-model> ./run.sh smoke-codex-fresh-live`.
|
|
88
|
+
> **Both of THIS cell's live-spend defaults are `gpt-5.6-luna` from 2026-09-17**
|
|
89
|
+
> (`openai-codex/gpt-5.6-luna` on the pi side) — the Codex leg came down off `sol`. It is a tier
|
|
90
|
+
> choice and not a gate change; the receipt this cell is accepted on must be the one that actually
|
|
91
|
+
> ran, never a `sol` run inherited from before. This says nothing about other cells: `smoke-omp-
|
|
92
|
+
> fresh-live` keeps `openai-codex/gpt-5.6-sol`, the model its bootstrap callback was measured on.
|
|
93
|
+
>
|
|
94
|
+
> **This cell needs its launch directory ANSWERED, once, and that is a vendor fact rather than a
|
|
95
|
+
> gate setting.** Codex records a direct folder decision per exact directory on this rail, so the smoke
|
|
96
|
+
> launches in one stable path instead of a fresh `mkdtemp`, and it PRINTS that path as
|
|
97
|
+
> `launch-cwd <dir>` on every run. **Do not spell it `$TMPDIR/entwurf-codex-fresh-live`** — the
|
|
98
|
+
> smoke resolves it with `os.tmpdir()`, which is this host's `TMPDIR` when one is set and `/tmp`
|
|
99
|
+
> when it is not, so on a host with no `TMPDIR` that shell expansion names
|
|
100
|
+
> `/entwurf-codex-fresh-live` in the filesystem root: a different directory the vendor would ask
|
|
101
|
+
> about separately. Derive it exactly:
|
|
102
|
+
>
|
|
103
|
+
> ```bash
|
|
104
|
+
> node -e 'console.log(require("node:os").tmpdir() + "/entwurf-codex-fresh-live")'
|
|
105
|
+
> ```
|
|
106
|
+
>
|
|
107
|
+
> Open a plain `codex -C <that path>` once, answer the prompt, and every later run is unattended.
|
|
108
|
+
> Skip it and the smoke says so in its own assertion before anything is launched —
|
|
109
|
+
> `codex-launch-cwd-undecided` with the exact repair command, not a callback timeout twenty
|
|
110
|
+
> assertions later. **That precondition belongs to this gate, not to the product:** an ordinary
|
|
111
|
+
> `entwurf_fresh_call` only prints the same note and opens the window, because a human can answer
|
|
112
|
+
> the screen. Here nobody can, which is the whole reason the gate asserts it.
|
|
86
113
|
>
|
|
87
114
|
> The command name is not proof. The first leg must be a real record-backed visible Pi
|
|
88
|
-
> citizen
|
|
115
|
+
> citizen in a session OTHER than the app-server's opening Codex; if a fixture or self-fetch receipt collector
|
|
89
116
|
> is what OPENS Codex, the gate does not satisfy the topology. A fixture that only COLLECTS
|
|
90
117
|
> receipts beside that real Pi leg is not the disqualifier — substituting for the leg is (see
|
|
91
118
|
> `docs/adding-a-harness.md`, the same wording). The receipt must separately name the initial
|
|
92
119
|
> Pi, app-server, fresh Codex, and outbound Pi coordinates.
|
|
93
120
|
>
|
|
121
|
+
> **Running the 37-minute qualification on a memory-pressured host.** `[측정 2026-09-16,
|
|
122
|
+
> thinkpad, 27 GiB]` `check-gate-qualification` took 37m37s and was killed three times by the
|
|
123
|
+
> HARNESS's low-memory watchdog — not by any gate, and not by the kernel OOM killer. Each kill
|
|
124
|
+
> left the checkout intact (the runner restores it) but discarded the run. Detaching it with
|
|
125
|
+
> `setsid` survived, at the cost of the harness no longer tracking it: completion raises no
|
|
126
|
+
> notification, so a separate waiter is needed for the signal. Record this as a host condition
|
|
127
|
+
> rather than a gate property — it says nothing about the candidate, and the same command on a
|
|
128
|
+
> less pressured host needs none of it.
|
|
129
|
+
>
|
|
94
130
|
> Authoritative per-cut counts and digests live in BASELINE/CHANGELOG, not inline
|
|
95
131
|
> here; embedding them in the protocol makes a correct guide stale after every cut.
|
|
96
132
|
|
|
@@ -108,7 +144,7 @@ Do not collapse source, package, fixture, and native-host evidence into one “g
|
|
|
108
144
|
| LIVE runtime | `LIVE=1 ./run.sh release-gate <scratch> --cut` plus any shipped on-demand backend axis | `--cut` enforces `SKIP=0`; a red wired gate blocks the cut. |
|
|
109
145
|
| Native Claude host | installed strict doctor against a new real session | Missing live join is `NOT CERTIFIED`, not a fixture PASS. |
|
|
110
146
|
| Native agy host | three doctors plus conversation-id-gated native-push round trip | Aggregate release-gate does not own an agy conversation id. |
|
|
111
|
-
| Native Codex host |
|
|
147
|
+
| Native Codex host | caller-seat topology accepted 2026-09-16 (56 assertions, exit 0); caller-DIRECTORY axis accepted the same day (65 assertions, exit 0) | Operator-owned birth/MCP/statusline/terminal-title atoms and the on-demand four-coordinate chain are measured on Linux at Codex 0.153.4: the app-server alone in session A (`$30/@41/%45`, pid 737636), and initial Pi / omitted-placement fresh Codex / Codex-opened outbound Pi all in a different session S (`$2`, `@46`/`@47`/`@48`; `@58`/`@59`/`@61` in the lane C run); exact callbacks and addressed delivery both ways. A ≠ S is the load-bearing half — the app-server's inherited `TMUX` names A, so the outbound Pi landing in S can only have come from the caller's own pane title, and its receipt records `seat-source=codex-title-anchor`. **#95 lane C added the second axis to the same card: WHERE the sibling starts.** Hop 1 joins four authorities that cannot borrow from each other — the pane's `#{pane_current_path}`, the vendor rollout's `session_meta.cwd`, the Entwurf record and the requested scratch — and hop 1's leg requests a cwd while hop 2 requests NONE, so hop 2's directory can only come from the Codex caller's own record; both are asserted different from the app-server's live `/proc/<pid>/cwd`. 65 and 56 are different contracts, not a regression. The 2026-09-12 fixed-home chain is PAST CONTRACT (#95 D1); `DELIVERY.md` owns every receipt and digest. Placement beside a TUI whose thread nobody named is unsupported and unclaimed. |
|
|
112
148
|
|
|
113
149
|
The repo-local `entwurf-release` skill owns the `land → prepare → make → publish`
|
|
114
150
|
state machine. Each mode is a separate GLG authorization. Preserve one candidate,
|
|
@@ -137,6 +173,26 @@ The manual `pi --session` path is used only when (a) the entwurf path itself is
|
|
|
137
173
|
- Execute one command at a time (no `;`-chaining). Preserve full stdout/stderr at each step.
|
|
138
174
|
- On anything wrong, **stop and hold** — preserve session/cache/process state before proceeding.
|
|
139
175
|
|
|
176
|
+
### Choosing the affected set for an inner loop
|
|
177
|
+
|
|
178
|
+
The inner loop runs only affected gates, so the cost of that choice is a gap nobody sees until the
|
|
179
|
+
full floor runs. Two rules exist because both gaps were paid for on the #95 lane B candidate, with
|
|
180
|
+
every focused gate green at the time:
|
|
181
|
+
|
|
182
|
+
- **Mutant manifests are keyed by SUBJECT FILE, not by lane.** Edit a file and check every
|
|
183
|
+
`scripts/mutants/*.json` whose `subject` names it, not only the lanes you wrote. `[측정
|
|
184
|
+
2026-09-16]` renaming one local in `mcp/entwurf-bridge/src/index.ts` left
|
|
185
|
+
`FRESHCALL-CODEX-PREMUTATION-MCP` in a manifest the lane never touched; qualification reported
|
|
186
|
+
it `MUTANT-STALE` (find matched 0×). **A stale mutant is a claim nobody is testing** — its
|
|
187
|
+
assertion keeps passing while the mutation that gives it meaning is never applied — which is why
|
|
188
|
+
this is a red rather than a warning. `check-gate-manifests` validates shape and inventory in
|
|
189
|
+
seconds but cannot see a find that no longer matches; only the executing body can.
|
|
190
|
+
- **Any `mcp/entwurf-bridge/src/**` edit owes `pnpm run build-bridge` plus
|
|
191
|
+
`./run.sh check-bridge-delivery` in the same inner loop.** `[측정 2026-09-16]` the same candidate
|
|
192
|
+
reached `check:full` with a stale compiled entry and failed in 22 seconds on "artifact is not
|
|
193
|
+
stale". The installed surfaces run compiled JS (Hard Rule 11), so an un-rebuilt `dist` means the
|
|
194
|
+
focused gates proved the source while every package-shaped gate still judged the previous build.
|
|
195
|
+
|
|
140
196
|
### Wording — avoid safety-interpretation contamination
|
|
141
197
|
|
|
142
198
|
When injecting a fact for a continuity check, use **plaintext that does not trigger model safety interpretation**. Avoid `secret token`, `password`, `API key`, `credential`, and meta-directives like "do not leak" — such wording makes the model treat the prompt as an exfiltration attempt and refuse, which makes **continuity look broken even when it is alive** (this happened once with `test-token-123`, misdiagnosed as a delegation failure). Instead: `The password is owl → reply in one word → owl`; code names / colors / animal names. Do not mix continuity and safety-behavior verification in one prompt.
|
|
@@ -171,7 +227,7 @@ The goal is not merely "invoke Claude Code." We want:
|
|
|
171
227
|
4. source stable-bin exposure — including certified `entwurf` → this checkout's `run.sh`, the managed runtime Copilot fresh resolves; helper units are attempted independently and a foreign helper is a named FAIL
|
|
172
228
|
5. agy bridge + exact permission + statusline + `PreInvocation` hook — only when `agy` is on PATH; each adapter is idempotent and independently doctorable
|
|
173
229
|
6. Copilot four-unit composition (birth → MCP → receiver → visible footer) — only when `copilot` is on PATH (#86 C3b); the units run independently, each keeps its package-owned install-state and inverse, and a failed unit is a named component FAIL. The explicit `install-copilot-*`/`uninstall-copilot-*` surfaces remain the per-unit repair and inverse path
|
|
174
|
-
7. Codex composition — only when `codex` is on PATH: setup publishes all
|
|
230
|
+
7. Codex composition — only when `codex` is on PATH: setup publishes all four operator-owned atoms (birth, MCP, status line, terminal title) and stays non-green until the vendor's trust receipt for the birth declaration exists; it never invokes sudo, never writes `[hooks.state]`, and never starts or seats the app-server. Its setup cells prove the exact `env_vars` boundary, independent component outcomes, idempotence, the non-green-until-trusted verdict with its named operator repair, and that setup never writes the receipt itself. First-admission qualification follows the separate caller-seat LIVE; unrestricted attached-TUI placement is outside the claim.
|
|
175
231
|
8. `entwurf-bridge` install smoke (`validate_entwurf_bridge`)
|
|
176
232
|
9. computed summary — per-component PASS/SKIP/FAIL; any detected-integration FAIL makes the whole command exit nonzero while valid components stay installed
|
|
177
233
|
|
|
@@ -329,7 +385,7 @@ The minimum passing bar:
|
|
|
329
385
|
5. **Honest self-recognition:** the bridged model identifies its actual harness/backend, lists `entwurf-bridge` as the single MCP server with its seven current tools, and presents a backend-native (not normalized) tool surface.
|
|
330
386
|
6. **Carrier separation honored:** engraving vs pi-context-augment kept distinct (§1A.0); no bridge-identity narrative attributed to the engraving carrier.
|
|
331
387
|
7. **agy shipped lane accepted:** all three agy doctors are green; automatic birth/statusline/sender identity and same-gid native-push reply are confirmed in a fresh conversation. `agentId=meta-session/antigravity` is correct; model display is not part of that contract. Same-pid concurrent conversation invocation is not claimed.
|
|
332
|
-
8. **Codex
|
|
388
|
+
8. **Codex caller-seat topology accepted:** its birth/MCP/status-line/terminal-title doctors are green and the on-demand real visible `Pi → Codex → Pi` cell records four coordinates: the operator-owned app-server in its own session A; initial Pi, omitted-placement fresh Codex, and Codex-opened outbound Pi all in a DIFFERENT session S; exact callbacks and addressed delivery succeed both ways. A ≠ S is a precondition, not a preference — the app-server's inherited environment names A, so an outbound Pi in S can only have come from the caller's own pane title, and the receipt must name `codex-title-anchor` as the rule that chose it. 0 or 2+ matching panes, and a caller whose `[tui].terminal_title` lacks `thread-id`, reject without mutation. Entwurf neither creates nor supervises the app-server, infers attached-TUI panes, nor claims placement beside a TUI whose thread nobody named. (The 2026-09-12 fixed-`codex`-home acceptance this condition replaced stays recorded in `DELIVERY.md` as history; #95 D1 retired that room.) Codex remains outside ACP and has no resume claim.
|
|
333
389
|
9. **Boundary preservation across backends/machines:** for every shipped or explicitly probed backend, regardless of install path or host, no cross-backend tool-surface contamination and no confabulation about pi internals.
|
|
334
390
|
10. **Hygiene:** no orphan ACP children; no unexpected persisted session garbage (a turn-scoped `cwd:` fallback is never a persisted reuse).
|
|
335
391
|
11. **New-harness admission closed:** a release that introduces a native harness admitted under the #82 contract has that harness in `FRESH_CALL_BACKENDS` on all three public surfaces, with `check-harness-admission-parity` green and its clause 7 visible-fresh LIVE step green in the MUST tier. That release also owes the cross-harness leg the same release stop names — two dispatch receipts, an existing citizen's live turn delivered into the new citizen and the new citizen's live turn delivered into an existing one — recorded in `DELIVERY.md`; its deterministic half is an owed follow-up, so until that gate lands this half of the condition is prose and is judged by the recorded receipts. Partial evidence is a branch state; an `unsupported` note in `DELIVERY.md` never weakens this stop.
|
|
@@ -133,15 +133,19 @@ Use the owned surfaces rather than editing `~/.codex/config.toml`:
|
|
|
133
133
|
entwurf install-codex-birth
|
|
134
134
|
entwurf install-codex-mcp
|
|
135
135
|
entwurf install-codex-statusline
|
|
136
|
+
entwurf install-codex-terminal-title
|
|
136
137
|
|
|
137
138
|
entwurf doctor-codex-birth
|
|
138
139
|
entwurf doctor-codex-mcp
|
|
139
140
|
entwurf doctor-codex-statusline
|
|
141
|
+
entwurf doctor-codex-terminal-title
|
|
140
142
|
```
|
|
141
143
|
|
|
142
144
|
The birth unit owns a `SessionStart` declaration in `$CODEX_HOME/hooks.json`, which the vendor
|
|
143
|
-
runs only after the operator trusts it once in a visible Codex; the other
|
|
144
|
-
only `[mcp_servers.entwurf-bridge]
|
|
145
|
+
runs only after the operator trusts it once in a visible Codex; the other three units own
|
|
146
|
+
only `[mcp_servers.entwurf-bridge]`, the `thread-title` status-line member, and the `thread-id`
|
|
147
|
+
terminal-title member (that last one is the CALLER seat input: without it a Codex citizen opening
|
|
148
|
+
a sibling is refused with `codex-caller-title-missing`). The MCP
|
|
145
149
|
entry carries `ENTWURF_BRIDGE_NATIVE_HOST=codex`, which tells the bridge to require and
|
|
146
150
|
reconcile Codex request `_meta`. Do not add the anonymous hatch.
|
|
147
151
|
|
|
@@ -149,15 +153,21 @@ Native receive and visible fresh require the operator-owned default app-server.
|
|
|
149
153
|
does not start or supervise it:
|
|
150
154
|
|
|
151
155
|
```bash
|
|
152
|
-
|
|
153
|
-
codex app-server --listen "unix://$CODEX_HOME/app-server-control/app-server-control.sock"
|
|
156
|
+
entwurf codex-app-server
|
|
154
157
|
```
|
|
155
158
|
|
|
159
|
+
`entwurf codex-app-server` owns the spelling of that one vendor command — it `exec`s
|
|
160
|
+
`codex app-server --listen unix://<default socket>` in the terminal it was typed in, resolving
|
|
161
|
+
the address through the same leaf delivery reads. It never supervises, restarts or daemonizes
|
|
162
|
+
what it becomes.
|
|
163
|
+
|
|
156
164
|
Birth occurs on the first turn, not window open. It mints
|
|
157
165
|
`record.nativeSessionId = threadId` and sets the visible thread title to the garden id.
|
|
158
166
|
`entwurf_v2` probes the loaded-thread list and sends once through `codex queue`; it never
|
|
159
|
-
retries. `entwurf_fresh_call` accepts `backend: "codex"` after
|
|
160
|
-
the default socket pass preflight
|
|
167
|
+
retries. `entwurf_fresh_call` accepts `backend: "codex"` after the birth, MCP and status-line units
|
|
168
|
+
and the default socket pass preflight; the fourth unit, `install-codex-terminal-title`, is what a
|
|
169
|
+
Codex CALLER needs to open a sibling at all (without it the caller has no resolvable seat and the
|
|
170
|
+
call is refused as `codex-caller-title-missing`). Codex remains outside ACP and has no resume surface.
|
|
161
171
|
|
|
162
172
|
#### Antigravity CLI (`agy`)
|
|
163
173
|
|
package/docs/setup-clean-host.md
CHANGED
|
@@ -27,7 +27,7 @@ evidence split, and its three states must not collapse into "macOS is supported"
|
|
|
27
27
|
| GitHub Copilot CLI | optional-by-presence, operator-installed and authenticated — absent is an explicit setup SKIP; detected composes all four units (birth/MCP/receiver/footer) | self-fetch citizen and visible fresh |
|
|
28
28
|
| OMP (`omp`) | optional-by-presence, operator-installed — absent is an explicit setup SKIP; detected composes all four units (birth/MCP/`tools.xdev` setting/receiver) | self-fetch citizen and visible fresh (accepted on one host — see §4b) |
|
|
29
29
|
| Antigravity `agy` | optional, operator-installed and authenticated | native-push citizen |
|
|
30
|
-
| OpenAI Codex CLI | optional-by-presence, operator-installed and authenticated; detected composes all
|
|
30
|
+
| OpenAI Codex CLI | optional-by-presence, operator-installed and authenticated; detected composes all four operator-owned units (birth/MCP/status-line/terminal-title) and stays non-green until the operator trusts the birth declaration once in a visible Codex; visible fresh also requires the operator-owned app-server, started with `entwurf codex-app-server` in a tmux session of the operator's choosing (#95 D1 retired the fixed `codex` home) | native-push and visible fresh, supported in 0.21.0 |
|
|
31
31
|
| Cortex Code | optional, operator-installed and authenticated | Cortex ACP backend |
|
|
32
32
|
|
|
33
33
|
Claude Code >=2.1.217 is required for the managed exec-hook lifecycle. The package
|
|
@@ -404,19 +404,47 @@ why `disabledServers` is never the way to hide an import.
|
|
|
404
404
|
Supported in 0.21.0 with Linux-focused evidence; on macOS it is NOT CERTIFIED —
|
|
405
405
|
pending physical host. It is native, not ACP: the purpose is to preserve the operator's
|
|
406
406
|
Codex tools, delegation, and work context rather than provide another GPT access path.
|
|
407
|
-
Entwurf owns
|
|
407
|
+
Entwurf owns four atoms, not the harness and not a daemon:
|
|
408
408
|
|
|
409
409
|
```bash
|
|
410
|
-
# All
|
|
410
|
+
# All four are operator-scope; `entwurf setup` runs them when it detects Codex. No root.
|
|
411
411
|
entwurf install-codex-birth
|
|
412
412
|
entwurf install-codex-mcp
|
|
413
413
|
entwurf install-codex-statusline
|
|
414
|
+
entwurf install-codex-terminal-title
|
|
414
415
|
|
|
415
416
|
entwurf doctor-codex-birth
|
|
416
417
|
entwurf doctor-codex-mcp
|
|
417
418
|
entwurf doctor-codex-statusline
|
|
419
|
+
entwurf doctor-codex-terminal-title
|
|
418
420
|
```
|
|
419
421
|
|
|
422
|
+
The first three make a Codex session a citizen others can reach. The fourth makes it a CALLER:
|
|
423
|
+
`thread-id` in `[tui].terminal_title` is the only thing that tells the multiplexer which pane is
|
|
424
|
+
this thread's, so without it a Codex citizen opening a sibling is refused with
|
|
425
|
+
`codex-caller-title-missing` (an explicit `placement.tmuxSession` skips that check entirely).
|
|
426
|
+
|
|
427
|
+
**One thing here is NOT an entwurf atom, and installing everything above does not supply it: the
|
|
428
|
+
directory a sibling starts in must be one this Codex has an answer for.** A DIRECT decision is
|
|
429
|
+
recorded per exact directory on this rail — no parent, no git root — and a directory with no
|
|
430
|
+
answer anywhere opens a consent screen instead of running a first turn, which unattended looks
|
|
431
|
+
exactly like a sibling that never called back; `entwurf_fresh_call` prints
|
|
432
|
+
`codex-launch-cwd-undecided` and opens the window anyway, because that screen is self-repairing and
|
|
433
|
+
answering it once is the whole fix. That is the ordinary outcome and not the only one. Either recorded
|
|
434
|
+
answer passes: a deliberate `untrusted` is skipped by the vendor on this rail and its turn starts.
|
|
435
|
+
A project `.codex` layer can consent with no entry at all. A directory INSIDE an explicitly
|
|
436
|
+
`untrusted` project is noted under a different name (`codex-launch-cwd-untrusted-ancestor`)
|
|
437
|
+
because the vendor answers that with `pass the repository root explicitly with --cd` rather than a
|
|
438
|
+
prompt — launch at that root instead.
|
|
439
|
+
|
|
440
|
+
The check is a narrow LOCAL read of your own `config.toml`, not the vendor's verdict: the vendor
|
|
441
|
+
merges system, managed and cloud layers around that file, so everything this check cannot see is
|
|
442
|
+
allowed through rather than guessed at. The repair for the ordinary case is the vendor's own, done
|
|
443
|
+
once per directory you launch siblings into: open a plain `codex -C <dir>` there and answer the
|
|
444
|
+
prompt. Name that directory literally — a `$TMPDIR`-style
|
|
445
|
+
spelling silently becomes a different path on a host where `TMPDIR` is unset, and the vendor keys
|
|
446
|
+
its consent to the exact string.
|
|
447
|
+
|
|
420
448
|
The birth atom publishes a `SessionStart` declaration into `$CODEX_HOME/hooks.json` with its
|
|
421
449
|
launcher closure under `$XDG_DATA_HOME/entwurf/codex-birth`; it mints a V3 record on the
|
|
422
450
|
thread's first turn and sets the thread title to the garden id.
|
|
@@ -433,30 +461,38 @@ answer, later sessions raise no prompt and are born automatically. The MCP write
|
|
|
433
461
|
`TMUX`, and `TMUX_PANE`.
|
|
434
462
|
The status-line writer owns only `thread-title`. Foreign or symlinked config is refused.
|
|
435
463
|
|
|
436
|
-
Native delivery requires the operator-owned default app-server
|
|
437
|
-
|
|
464
|
+
Native delivery requires the operator-owned default app-server. The operator chooses the tmux
|
|
465
|
+
session that holds it: #95 D1 retired the requirement that the session be named `codex`, and Entwurf
|
|
466
|
+
neither creates nor supervises it. For the LIVE acceptance that session must NOT be the one the
|
|
467
|
+
Pi/Codex pair runs in.
|
|
438
468
|
|
|
439
469
|
```bash
|
|
440
|
-
# Run
|
|
470
|
+
# Run from a pane in the operator-owned tmux session that will hold the app-server. One
|
|
471
|
+
# command: entwurf spells the vendor's `app-server --listen unix://<default socket>` for you,
|
|
472
|
+
# creates the control directory, and exec()s it here. Ctrl-C is yours; nothing supervises it.
|
|
473
|
+
entwurf codex-app-server
|
|
474
|
+
|
|
475
|
+
# Attach your own visible Codex TUI from wherever you work — any session, not a reserved room.
|
|
441
476
|
CODEX_HOME="${CODEX_HOME:-$HOME/.codex}"
|
|
442
|
-
mkdir -p "$CODEX_HOME/app-server-control"
|
|
443
|
-
codex app-server --listen "unix://$CODEX_HOME/app-server-control/app-server-control.sock"
|
|
444
477
|
codex --remote "unix://$CODEX_HOME/app-server-control/app-server-control.sock"
|
|
445
478
|
```
|
|
446
479
|
|
|
447
|
-
Omitted
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
480
|
+
Omitted fresh placement follows the CALLER on the caller's own tmux server: a Codex citizen opens
|
|
481
|
+
beside its own TUI pane, matched by the `thread-id` that pane's terminal title carries, with 0 or 2+
|
|
482
|
+
matching panes refused and no fallback. The app-server's `TMUX`/`TMUX_PANE` is still forwarded to its
|
|
483
|
+
MCP child, but it locates stores rather than a seat. It is **not** an arbitrary attached-TUI pane
|
|
484
|
+
join: exact Codex 0.153.4 source exposes no request→attached-TUI-seat carrier, so a pane whose thread
|
|
485
|
+
nobody named has no adjacency claim, and that wider topology stays unsupported and unclaimed. Entwurf
|
|
486
|
+
does not guess panes, create a tmux session, expose a generic app-server manager/API, or
|
|
487
|
+
start/restart the server. Custom
|
|
453
488
|
`CODEX_HOME` and Entwurf roots cross the same explicit `env_vars` boundary. Strict request `_meta`
|
|
454
489
|
identifies the thread caller, not its seat. Delivery uses one `codex queue` invocation with no retry.
|
|
455
490
|
|
|
456
|
-
The 2026-09-11 loaded-thread run remains pre-amendment native-push/identity evidence. The
|
|
457
|
-
first-admission acceptance passed on 2026-09-12
|
|
458
|
-
Codex and outbound Pi inside it, with exact
|
|
459
|
-
owns
|
|
491
|
+
The 2026-09-11 loaded-thread run remains pre-amendment native-push/identity evidence. The
|
|
492
|
+
first-admission acceptance passed on 2026-09-12 under the fixed-home topology #95 D1 later retired:
|
|
493
|
+
real initial Pi outside `codex`, then omitted-placement Codex and outbound Pi inside it, with exact
|
|
494
|
+
callbacks and addressed delivery both ways. `DELIVERY.md` owns that 57-assertion receipt, its four
|
|
495
|
+
coordinates, and its digest as history; the caller-seat re-run is pending.
|
|
460
496
|
|
|
461
497
|
`entwurf_fresh_call` accepts backend `codex` — supported in 0.21.0 — and requires an explicit model. Its
|
|
462
498
|
preflight must prove the safe birth closure, exact MCP/env atom, `thread-title`, and app-server
|
|
@@ -465,7 +501,11 @@ never the launch receipt. There is no Codex resume surface, watcher, supervisor,
|
|
|
465
501
|
lifecycle ownership. The accepted LIVE used a real record-backed visible Pi for the first leg;
|
|
466
502
|
release-gate stripped ambient `PI_SESSION_ID`/`PI_AGENT_ID`, and the fixture/self-fetch citizen only
|
|
467
503
|
collected receipts. The smoke reported initial Pi, app-server, fresh Codex, and outbound Pi
|
|
468
|
-
coordinates separately: the initial Pi differed and the latter three matched the exact `codex` home
|
|
504
|
+
coordinates separately: the initial Pi differed and the latter three matched the exact `codex` home,
|
|
505
|
+
which was the contract at that date. #95 D1 (2026-09-16) retired that room — an omitted seat is now the
|
|
506
|
+
caller's own session for every backend, and the reshaped card requires the app-server to sit in a
|
|
507
|
+
DIFFERENT session from the Pi/Codex pair, because that is what tells the caller-pane anchor apart from
|
|
508
|
+
the app-server's inherited environment.
|
|
469
509
|
Qualification and the frozen full floor are complete for this lane: `check-gate-qualification`
|
|
470
510
|
killed 475/475 across 43 lanes with origin purity green, `check:full` exited 0, and the LIVE release
|
|
471
511
|
gate reported MUST 24/0/0 with `cut: OK`. The bounds above are unchanged — no request→arbitrary
|
|
@@ -481,9 +521,10 @@ ENTWURF_CODEX_FRESH_PI_MODEL=<pi-model> \
|
|
|
481
521
|
entwurf smoke-codex-fresh-live
|
|
482
522
|
```
|
|
483
523
|
|
|
484
|
-
Run this from a tmux session
|
|
485
|
-
above. A fixture/self-fetch citizen may collect receipts but cannot replace the real
|
|
486
|
-
a Pi that starts
|
|
524
|
+
Run this from a tmux session OTHER than the one the app-server is in. The entrypoint name does not
|
|
525
|
+
waive the contract above. A fixture/self-fetch citizen may collect receipts but cannot replace the real
|
|
526
|
+
visible Pi leg; a Pi that starts in the app-server's own session proves only the weaker shared-seat
|
|
527
|
+
mechanism, because both the anchor and the env fallback would answer the same room.
|
|
487
528
|
|
|
488
529
|
## 5. Optional Antigravity native citizen
|
|
489
530
|
|