@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.
- package/AGENTS.md +85 -190
- package/BASELINE.md +6 -3
- package/CHANGELOG.md +473 -14
- package/CONTRIBUTING.md +1 -1
- package/DELIVERY.md +332 -60
- package/README.md +100 -22
- package/VERIFY.md +93 -7
- package/docs/acp-backend-rail.md +0 -1
- package/docs/external-mcp-host.md +64 -33
- package/docs/setup-clean-host.md +151 -17
- package/mcp/entwurf-bridge/dist/mcp/entwurf-bridge/src/index.js +130 -114
- 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 +519 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-fact-provider.js +22 -2
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-peers-render.js +3 -1
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-contract.js +6 -7
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-native-push.js +30 -17
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-production.js +7 -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/entwurf-v2-surface.js +7 -4
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/meta-mailbox-body.js +20 -11
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/meta-sender-identity.js +227 -1
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/mux-fresh-call.js +189 -19
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/native-push/adapter.js +10 -7
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/native-push/codex-ws-client.js +403 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/native-push/register.js +4 -4
- package/mcp/entwurf-bridge/dist/pi-extensions/meta-bridge-hook-codex.js +325 -0
- package/mcp/entwurf-bridge/dist/scripts/codex-socket-path.js +30 -0
- package/mcp/entwurf-bridge/dist/scripts/meta-bridge-fresh-cut.js +6 -1
- package/mcp/entwurf-bridge/src/index.ts +154 -116
- package/mcp/entwurf-bridge/tsconfig.build.json +10 -0
- package/package.json +13 -4
- package/pi-extensions/entwurf-control.ts +71 -19
- package/pi-extensions/lib/codex-caller-seat.ts +204 -0
- package/pi-extensions/lib/codex-fresh-preflight.ts +580 -0
- package/pi-extensions/lib/compaction-send-guard.ts +80 -0
- package/pi-extensions/lib/entwurf-fact-provider.ts +29 -3
- package/pi-extensions/lib/entwurf-peers-render.ts +4 -1
- package/pi-extensions/lib/entwurf-v2-contract.ts +6 -7
- package/pi-extensions/lib/entwurf-v2-native-push.ts +35 -18
- package/pi-extensions/lib/entwurf-v2-production.ts +10 -3
- package/pi-extensions/lib/entwurf-v2-runner.ts +3 -2
- package/pi-extensions/lib/entwurf-v2-send.ts +16 -11
- package/pi-extensions/lib/entwurf-v2-surface.ts +7 -4
- package/pi-extensions/lib/meta-mailbox-body.ts +22 -13
- package/pi-extensions/lib/meta-sender-identity.ts +305 -0
- package/pi-extensions/lib/mux-fresh-call.ts +233 -29
- package/pi-extensions/lib/native-push/adapter.ts +21 -24
- package/pi-extensions/lib/native-push/codex-ws-client.ts +506 -0
- package/pi-extensions/lib/native-push/register.ts +7 -9
- package/pi-extensions/meta-bridge-hook-codex.ts +371 -0
- package/run.sh +342 -28
- package/scripts/check-agy-sender-identity.ts +1 -1
- package/scripts/check-codex-app-server-launch.ts +445 -0
- package/scripts/check-codex-birth-hook.ts +264 -0
- package/scripts/check-codex-bridge-identity.ts +179 -0
- package/scripts/check-codex-native-push.ts +386 -0
- package/scripts/check-codex-sender-identity.ts +495 -0
- package/scripts/check-compaction-send-guard.ts +130 -0
- package/scripts/check-copilot-receive-arm.ts +4 -1
- package/scripts/check-entwurf-fact-provider.ts +38 -0
- package/scripts/check-entwurf-peers-surface.ts +13 -1
- package/scripts/check-entwurf-self-address.ts +15 -16
- package/scripts/check-entwurf-v2-contract.ts +4 -3
- package/scripts/check-entwurf-v2-decider.ts +7 -5
- package/scripts/check-entwurf-v2-native-push.ts +35 -7
- package/scripts/check-entwurf-v2-production.ts +245 -12
- package/scripts/check-entwurf-v2-runner.ts +1 -1
- package/scripts/check-entwurf-v2-send.ts +26 -7
- package/scripts/check-entwurf-v2-surface.ts +1 -1
- package/scripts/check-gate-qualification.ts +8 -3
- package/scripts/check-harness-admission-parity.ts +0 -1
- package/scripts/check-mux-launch-tmux.ts +345 -4
- package/scripts/check-native-push-adapter.ts +20 -16
- package/scripts/check-native-push-register.ts +5 -1
- package/scripts/check-release-gate-outcomes.ts +47 -1
- package/scripts/check-setup-qualification.sh +3 -1
- package/scripts/codex-app-server-launch.sh +275 -0
- package/scripts/codex-birth-doctor.sh +276 -0
- package/scripts/codex-birth-install.sh +414 -0
- package/scripts/codex-birth-uninstall.sh +170 -0
- package/scripts/codex-mcp-config.py +435 -0
- package/scripts/codex-socket-path.ts +33 -0
- package/scripts/codex-statusline-config.py +434 -0
- package/scripts/codex-terminal-title-config.py +500 -0
- package/scripts/codex_toml_io.py +653 -0
- package/scripts/lib/codex-fresh-live-protocol.ts +121 -0
- package/scripts/lib/codex-fresh-source-receipts.ts +426 -0
- package/scripts/lib/launch-receipt-windows.ts +46 -0
- package/scripts/lib/tmux-coordinate-row.ts +58 -0
- package/scripts/meta-bridge-fresh-cut.ts +6 -1
- 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 +838 -0
- package/scripts/mutants/compaction-send-guard.json +103 -0
- package/scripts/mutants/entwurf-peers.json +19 -0
- package/scripts/mutants/mux-fresh-call.json +93 -10
- package/scripts/mutants/omp-fresh.json +6 -4
- package/scripts/mutants/release-gate.json +13 -0
- package/scripts/mutants/v2-surface.json +75 -1
- package/scripts/raw-async-delivery/README.md +2 -1
- package/scripts/raw-codex-measure/README.md +114 -46
- package/scripts/smoke-agy-native-push-live.ts +3 -1
- package/scripts/smoke-codex-birth.sh +347 -0
- package/scripts/smoke-codex-config-state.sh +700 -0
- package/scripts/smoke-codex-fresh-live.ts +1426 -0
- package/scripts/smoke-codex-native-push-live.ts +75 -0
- package/scripts/smoke-entwurf-chain-live.ts +50 -0
- package/scripts/smoke-setup-verdict.sh +125 -10
- package/scripts/tsconfig.json +1 -0
|
@@ -6,47 +6,45 @@ the one-line registration; everything an operator needs to actually wire a host
|
|
|
6
6
|
|
|
7
7
|
`entwurf-bridge` can also be registered in a separate MCP-aware harness (Claude Code, Codex CLI, Antigravity/`agy`, GitHub Copilot CLI, OMP, …). That host does **not** become a pi session and does **not** need to be ACP-backed. There are now two honest cases:
|
|
8
8
|
|
|
9
|
-
- **plain external MCP host**: no garden meta-record
|
|
10
|
-
- **garden-native native session**: a trusted lifecycle
|
|
9
|
+
- **plain external MCP host**: no garden meta-record or authoritative identity carrier. It can call the read surfaces (`entwurf_peers`, `entwurf_inbox_read`), but `entwurf_v2` sends are **refused by default** (#50 C4: "if we don't know who sent it, we don't send it"). The operator may wire the explicit hatch below; the send then goes out external/non-replyable.
|
|
10
|
+
- **garden-native native session**: a trusted lifecycle event minted a garden id — `SessionStart` for Claude Code and Codex, `PreInvocation` for agy, `userPromptSubmitted`/`sessionStart` for GitHub Copilot CLI, and for OMP an in-process extension on `session_start`/`session_switch` that mints only a `mode === "tui"` host. Claude/Copilot/agy/OMP then use record-backed process markers; Codex instead carries its matching native `threadId` on every MCP request. It is not a pi control-socket session, but it can be replyable by garden id when its own mailbox/probe rail says so.
|
|
11
11
|
|
|
12
12
|
**Being garden-native is not the same as being replyable, and Copilot is the case that separates them.** Its hook writes a sender marker, so an `entwurf_v2` send carries its own garden id and the receiver learns who wrote — measured 2026-08-21 on Copilot CLI 1.0.80, where a live send arrived under its own garden id with `origin: "meta-session"` and `replyable: false`. Replyability arrived later and through a different process: a first-party extension (`run.sh install-copilot-receive`) that the CLI forks, which binds to the same V3 record and writes a receiver marker owned by its own pid (#82 RAIL 5). So a Copilot citizen is `replyable: true` exactly while that extension is armed for it, and `replyable: false` — honestly, with a real garden identity — when it is not installed, not launched with `COPILOT_CLI_ENABLED_FEATURE_FLAGS=EXTENSIONS` (which is what `entwurf copilot` sets for one invocation), not yet born, or gone. Read the two facts off different rails: the sender marker answers *who sent this*; the receiver marker answers *can a reply land*. The onboarding obligations are in [`adding-a-harness.md`](./adding-a-harness.md) step 7, and the evidence boundary — the managed LIVE acceptance that moved receive to D6, and what D7-partial / D3-pending still exclude — is in [`DELIVERY.md`](../DELIVERY.md).
|
|
13
13
|
|
|
14
|
-
**Which verb an external agent should reach for:** to deliver to / reply to a garden id, use **`entwurf_v2`** — it is the canonical delivery surface and the only one that reads whether the target is live pi, dormant pi, a mailbox-backed self-fetch citizen (Claude Code, Copilot, OMP), or native-push Antigravity and routes correctly. Discover targets with `entwurf_peers`, confirm your own identity with `entwurf_self`, drain a mailbox with `entwurf_inbox_read`, and use `entwurf_register_native` only as the explicit/manual fallback for binding an already-running agy conversation
|
|
14
|
+
**Which verb an external agent should reach for:** to deliver to / reply to a garden id, use **`entwurf_v2`** — it is the canonical delivery surface and the only one that reads whether the target is live pi, dormant pi, a mailbox-backed self-fetch citizen (Claude Code, Copilot, OMP), or native-push Antigravity/Codex and routes correctly. Discover targets with `entwurf_peers`, confirm your own identity with `entwurf_self`, drain a mailbox with `entwurf_inbox_read`, and use `entwurf_register_native` only as the explicit/manual fallback for binding an already-running agy conversation. Open a NEW sibling with **`entwurf_fresh_call {backend, model, task, cwd?, placement?}`**, and reopen a DORMANT pi citizen under the same id with **`entwurf_resume_call {target}`**.
|
|
15
15
|
|
|
16
|
-
Observed: Claude Code, Codex CLI, Antigravity CLI, GitHub Copilot CLI and OMP all reach
|
|
16
|
+
Observed: Claude Code, Codex CLI, Antigravity CLI, GitHub Copilot CLI and OMP all reach this MCP bridge. Claude, Copilot and OMP become symmetric/replyable through mailbox-backed meta-sessions; agy and Codex use native-push. Codex's identity is request-scoped `_meta.threadId`, and its replyability requires that exact record plus a loaded app-server thread. Plain external hosts still need an identity lane to send.
|
|
17
17
|
|
|
18
18
|
Prerequisites on the host running the external MCP client:
|
|
19
19
|
|
|
20
20
|
- A live pi session launched with `--entwurf-control` populates `~/.pi/entwurf-control/<gardenId>.sock` — the key is the **record's** garden id, never a transcript/session id (`PI_SESSION_ID` only carries the id record birth already established). Required for `entwurf_v2` control-socket dispatch and `entwurf_peers`.
|
|
21
21
|
|
|
22
|
-
> **PATH boundary.** MCP servers are often launched by GUI/editor daemons and may not inherit the interactive shell's PATH. No `entwurf_v2` rail launches a process, so this does not affect delivery — but `entwurf_fresh_call` and `entwurf_resume_call` do open a fixed runtime. If that runtime works in your terminal but an external-host call fails with `spawn pi ENOENT`, `spawn claude ENOENT`, `spawn entwurf ENOENT`, or `spawn
|
|
23
|
-
|
|
24
|
-
> **Identity-carrier boundary.**
|
|
25
|
-
>
|
|
26
|
-
>
|
|
27
|
-
>
|
|
28
|
-
>
|
|
29
|
-
> managed
|
|
30
|
-
> harness establish its own identity
|
|
31
|
-
>
|
|
32
|
-
>
|
|
33
|
-
>
|
|
34
|
-
> contamination rather than silently preferring the marker.
|
|
22
|
+
> **PATH boundary.** MCP servers are often launched by GUI/editor daemons and may not inherit the interactive shell's PATH. No `entwurf_v2` rail launches a process, so this does not affect delivery — but `entwurf_fresh_call` and `entwurf_resume_call` do open a fixed runtime. If that runtime works in your terminal but an external-host call fails with `spawn pi ENOENT`, `spawn claude ENOENT`, `spawn entwurf ENOENT`, `spawn omp ENOENT`, or `spawn codex ENOENT`, pass a full PATH in the MCP server `env`, set `ENTWURF_BRIDGE_ENV_FILE` to a small shell file that exports PATH, or point the host at a wrapper that can find the runtime. `start.sh` sources only the explicit `ENTWURF_BRIDGE_ENV_FILE`; it never reads personal dotfiles automatically.
|
|
23
|
+
|
|
24
|
+
> **Identity-carrier boundary.** The bridge reconciles every authoritative claim available to
|
|
25
|
+
> the request: the complete `PI_SESSION_ID` + `PI_AGENT_ID` pair, a native sender marker, and
|
|
26
|
+
> Codex request `_meta`. Zero claims is refused; multiple claims must name the same garden id or
|
|
27
|
+
> the call throws. Nothing silently wins. Those pi variables are correct only inside the pi
|
|
28
|
+
> process that planted them from record birth; they are foreign identity in another native
|
|
29
|
+
> harness. Every managed fresh launch therefore empties both variables at the tmux seam and lets
|
|
30
|
+
> the launched harness establish its own identity. Clearing only one is not a repair: an
|
|
31
|
+
> incomplete pair is itself invalid, while retaining either carrier invites future reader drift.
|
|
32
|
+
> An unmanaged launch that inherits them is unsupported. A native-harness admission must add a
|
|
33
|
+
> doctor or request-level contract that names contamination rather than trusting precedence.
|
|
35
34
|
>
|
|
36
|
-
> **
|
|
37
|
-
>
|
|
38
|
-
>
|
|
39
|
-
>
|
|
40
|
-
>
|
|
41
|
-
>
|
|
42
|
-
> as coverage for them.
|
|
35
|
+
> **The defenses are layered.** Copilot's managed launcher unsets both carriers before `exec`,
|
|
36
|
+
> and `doctor-copilot-receive` detects a contaminated live CLI. The shared fresh-call tmux seam
|
|
37
|
+
> empties them for every backend. OMP retains its process-environment doctor for manually opened
|
|
38
|
+
> sessions. Codex's request-level reconciler adds a different last line: a foreign pi claim and
|
|
39
|
+
> `_meta.threadId` claim cannot coexist under different garden ids. These are complementary;
|
|
40
|
+
> no backend's cell is evidence for another's unmanaged launch.
|
|
43
41
|
>
|
|
44
42
|
> **OMP has both halves, and the strip one is not an omp launcher** (#87 Bundle C). entwurf still
|
|
45
43
|
> owns no managed omp invocation — the bare vendor runtime IS this harness's clause 1 answer, and
|
|
46
44
|
> nothing here argues for a wrapper. The strip sits one level up instead, at the shared launch seam
|
|
47
45
|
> every `entwurf_fresh_call` backend passes through: `SCRUBBED_INHERITED_ENV` empties
|
|
48
46
|
> `PI_SESSION_ID` and `PI_AGENT_ID` on the `new-window` argv itself
|
|
49
|
-
> (`pi-extensions/lib/mux-fresh-call.ts
|
|
47
|
+
> (`pi-extensions/lib/mux-fresh-call.ts`), for all five backends rather than only the one whose
|
|
50
48
|
> measurement surfaced it, because the leak is a property of tmux and not of a vendor. `-e VAR=`
|
|
51
49
|
> sets the variable empty rather than unsetting it — tmux has no per-window unset — and every
|
|
52
50
|
> carrier reader trims and tests truthiness, so empty and absent are the same answer by
|
|
@@ -78,16 +76,16 @@ Then add it to the external MCP config:
|
|
|
78
76
|
}
|
|
79
77
|
```
|
|
80
78
|
|
|
81
|
-
**Anonymous sender hatch (explicit, documented — never a default).** The bridge refuses an `entwurf_v2` send when the process has neither pi-session env
|
|
79
|
+
**Anonymous sender hatch (explicit, documented — never a default).** The bridge refuses an `entwurf_v2` send when the process has neither pi-session env nor a trusted native identity claim. A deliberately-anonymous plain external host may opt out by adding `"ENTWURF_BRIDGE_ALLOW_ANONYMOUS_SENDER": "1"` to the MCP server `env`. The cost is honest and fixed: the send lands with `origin: "external-mcp"`, `replyable: false`, and `wants_reply: true` stays pointless. A managed Codex citizen does **not** use this hatch; request `_meta` supplies its authoritative identity.
|
|
82
80
|
|
|
83
81
|
Emergency/manual workaround when the MCP server environment is wrong but an existing entwurf session must be resumed: run `pi --session /path/to/entwurf.jsonl ...` from an interactive shell whose PATH is known-good. Treat this as a debug escape hatch, not a replacement for fixing the MCP launch environment.
|
|
84
82
|
|
|
85
83
|
External/meta-session semantics:
|
|
86
84
|
|
|
87
85
|
- `entwurf_v2` from a plain external host is **refused by default** (no authoritative sender — #50 C4). With the explicit `ENTWURF_BRIDGE_ALLOW_ANONYMOUS_SENDER=1` hatch it delivers with `origin: "external-mcp"` / `replyable: false`; there is still no reply address.
|
|
88
|
-
- `entwurf_v2` from a trusted meta-session delivers with `origin: "meta-session"`, and `replyable` is **derived from that sender's own rail — not granted by being trusted**: a self-fetch sender (Claude Code, Copilot, OMP) is replyable only while its receiver is live and armed
|
|
86
|
+
- `entwurf_v2` from a trusted meta-session delivers with `origin: "meta-session"`, and `replyable` is **derived from that sender's own rail — not granted by being trusted**: a self-fetch sender (Claude Code, Copilot, OMP) is replyable only while its receiver is live and armed; Antigravity requires a live native probe; Codex requires a matching request-scoped record and loaded app-server thread. Identity survives either way; only `replyable` drops to `false`.
|
|
89
87
|
- `entwurf_v2` never launches a process, so no delivery path needs `pi` on PATH. A dormant pi target is refused as `dormant-fire-forget-unsupported`: the hidden background resume that used to answer there was withdrawn under the visible-first rule, and re-opening the session is the separate `entwurf_resume_call` verb — which DOES need `pi` on PATH, because it starts one.
|
|
90
|
-
- `entwurf_self` returns the same authoritative identity for pi sessions **and** trusted meta-sessions. A plain external host with no pi env
|
|
88
|
+
- `entwurf_self` returns the same authoritative identity for pi sessions **and** trusted meta-sessions. A plain external host with no pi env, native marker, or Codex request claim still fails because there is no reply address to report.
|
|
91
89
|
|
|
92
90
|
#### Claude Code
|
|
93
91
|
|
|
@@ -129,14 +127,47 @@ Claude Code reads `~/.mcp.json` in addition to `~/.claude.json`'s top-level `mcp
|
|
|
129
127
|
|
|
130
128
|
#### Codex CLI
|
|
131
129
|
|
|
132
|
-
|
|
130
|
+
Use the owned surfaces rather than editing `~/.codex/config.toml`:
|
|
133
131
|
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
|
|
132
|
+
```bash
|
|
133
|
+
entwurf install-codex-birth
|
|
134
|
+
entwurf install-codex-mcp
|
|
135
|
+
entwurf install-codex-statusline
|
|
136
|
+
entwurf install-codex-terminal-title
|
|
137
|
+
|
|
138
|
+
entwurf doctor-codex-birth
|
|
139
|
+
entwurf doctor-codex-mcp
|
|
140
|
+
entwurf doctor-codex-statusline
|
|
141
|
+
entwurf doctor-codex-terminal-title
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
The birth unit owns a `SessionStart` declaration in `$CODEX_HOME/hooks.json`, which the vendor
|
|
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
|
|
149
|
+
entry carries `ENTWURF_BRIDGE_NATIVE_HOST=codex`, which tells the bridge to require and
|
|
150
|
+
reconcile Codex request `_meta`. Do not add the anonymous hatch.
|
|
151
|
+
|
|
152
|
+
Native receive and visible fresh require the operator-owned default app-server. Entwurf
|
|
153
|
+
does not start or supervise it:
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
entwurf codex-app-server
|
|
137
157
|
```
|
|
138
158
|
|
|
139
|
-
|
|
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
|
+
|
|
164
|
+
Birth occurs on the first turn, not window open. It mints
|
|
165
|
+
`record.nativeSessionId = threadId` and sets the visible thread title to the garden id.
|
|
166
|
+
`entwurf_v2` probes the loaded-thread list and sends once through `codex queue`; it never
|
|
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.
|
|
140
171
|
|
|
141
172
|
#### Antigravity CLI (`agy`)
|
|
142
173
|
|
package/docs/setup-clean-host.md
CHANGED
|
@@ -8,8 +8,8 @@ evidence split, and its three states must not collapse into "macOS is supported"
|
|
|
8
8
|
(`macos-install-surface`). Weaker than a physical-host doctor green. §1 is
|
|
9
9
|
this surface.
|
|
10
10
|
- **NOT CERTIFIED — pending physical host** — **on macOS**, every garden-native
|
|
11
|
-
harness rail in §2–§6 (pi, Claude, Copilot, OMP, agy), marker join,
|
|
12
|
-
mux. Wiring may be written; that is not a rail receipt. The remaining
|
|
11
|
+
harness rail in §2–§6 (pi, Claude, Copilot, OMP, agy, Codex), marker/request join,
|
|
12
|
+
ACP turn, mux. Wiring may be written; that is not a rail receipt. The remaining
|
|
13
13
|
unmeasured Darwin doctor reason is per-process environment DISCOVERY
|
|
14
14
|
(start-key is portable).
|
|
15
15
|
- **UNSUPPORTED** — native Windows only. macOS is never this.
|
|
@@ -20,13 +20,14 @@ evidence split, and its three states must not collapse into "macOS is supported"
|
|
|
20
20
|
|---|---|---|
|
|
21
21
|
| Node | **`>=24.0.0`** | package and bridge runtime |
|
|
22
22
|
| npm/pnpm | npm is bundled with Node; pnpm is required for source setup | package or source installation |
|
|
23
|
-
| Python 3 | required by `setup`/`install` (project-path normalization + settings writers); `--help`/`check-bridge` stay Python-free | pi/Claude/agy/Copilot wiring writers |
|
|
23
|
+
| Python 3 | required by `setup`/`install` (project-path normalization + settings writers); `--help`/`check-bridge` stay Python-free | pi/Claude/agy/Copilot/Codex wiring writers |
|
|
24
24
|
| entwurf | global/project-local `@junghanacs/entwurf`, or a source checkout | operator command and garden capability |
|
|
25
25
|
| pi | optional-by-presence, `>=0.85.1 <0.86` — absent is an explicit setup SKIP, below-floor is a named FAIL | ACP provider, control sockets |
|
|
26
26
|
| Claude Code | optional, **`>=2.1.217`** — the exec-form hook floor | Claude ACP auth/runtime and mailbox-backed native citizen |
|
|
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 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 |
|
|
30
31
|
| Cortex Code | optional, operator-installed and authenticated | Cortex ACP backend |
|
|
31
32
|
|
|
32
33
|
Claude Code >=2.1.217 is required for the managed exec-hook lifecycle. The package
|
|
@@ -52,9 +53,9 @@ harness. The wiring is written; the row is FAIL:
|
|
|
52
53
|
> platform. Tracking: #78.
|
|
53
54
|
|
|
54
55
|
That FAIL is honest: setup runs no doctor, and no physical-host rail
|
|
55
|
-
receipt exists — it is not an install failure. An Entwurf-only host
|
|
56
|
-
harness on PATH
|
|
57
|
-
`result: green`), which is what CERTIFIED (CI)
|
|
56
|
+
receipt exists — it is not an install failure. An Entwurf-only host with no
|
|
57
|
+
harness on PATH stays green (six SKIP, `bins: PASS`, `core: PASS`,
|
|
58
|
+
`result: green`), which is what CERTIFIED (CI) observes.
|
|
58
59
|
|
|
59
60
|
A physical Mac can send host facts without installing entwurf or logging
|
|
60
61
|
into a harness: `scripts/raw-macos-measure/probe.sh` (`/bin/sh`, ~3.5s,
|
|
@@ -91,10 +92,10 @@ npx entwurf check-bridge
|
|
|
91
92
|
`check-bridge` is auth-free. It proves the installed prebuilt MCP server boots and
|
|
92
93
|
lists the seven garden tools; it does not prove a backend model turn or native hook.
|
|
93
94
|
|
|
94
|
-
Neither npm form installs a harness runtime. `pi`, Claude Code, Copilot CLI, agy,
|
|
95
|
-
authentication remain operator-owned optional prerequisites for the
|
|
96
|
-
all may be absent on an Entwurf-only host. A source checkout's
|
|
97
|
-
for building and testing this repo, not a transitive product installation promise.
|
|
95
|
+
Neither npm form installs a harness runtime. `pi`, Claude Code, Copilot CLI, Codex CLI, agy,
|
|
96
|
+
Cortex, and their authentication remain operator-owned optional prerequisites for the
|
|
97
|
+
integrations that use them; all may be absent on an Entwurf-only host. A source checkout's
|
|
98
|
+
pinned Pi development packages are for building and testing this repo, not a transitive product installation promise.
|
|
98
99
|
|
|
99
100
|
Maintainers using a source checkout do not install a second global entwurf package. Full source
|
|
100
101
|
setup currently requires Node 24, pnpm, and Python 3 on PATH; every harness — including pi — is
|
|
@@ -109,12 +110,14 @@ cd ~/repos/gh/entwurf
|
|
|
109
110
|
|
|
110
111
|
This owns `~/.local/bin/entwurf` as a symlink to that checkout's `run.sh` and fails if the
|
|
111
112
|
link is foreign, outside PATH, or shadowed by another command. It detects and wires
|
|
112
|
-
pi/Claude/agy/Copilot/OMP by presence and prints a computed per-component
|
|
113
|
-
detected harness that cannot be completed makes setup exit nonzero.
|
|
114
|
-
composes all four native units
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
113
|
+
pi/Claude/agy/Copilot/OMP/Codex by presence and prints a computed per-component
|
|
114
|
+
PASS/SKIP/FAIL summary — a detected harness that cannot be completed makes setup exit nonzero.
|
|
115
|
+
A detected `copilot` composes all four native units, and a detected `omp` composes its own four.
|
|
116
|
+
A detected Codex install is a supported 0.21.0 path: setup composes its user-scope birth,
|
|
117
|
+
MCP, and status-line atoms but never escalates or starts the app-server. It reports independent
|
|
118
|
+
component outcomes, preserves exact env forwarding and idempotence, and returns a named
|
|
119
|
+
`codex-birth FAIL` until the vendor trust receipt exists.
|
|
120
|
+
§4, §4b, and §4c keep the explicit per-unit install/doctor/inverse surfaces.
|
|
118
121
|
|
|
119
122
|
### 1.1 User-scope ownership (one shared registration, one recorded owner)
|
|
120
123
|
|
|
@@ -396,6 +399,133 @@ The MCP entry deliberately uses the same server key as any Claude Code import so
|
|
|
396
399
|
SHADOWS it; see [`external-mcp-host.md`](./external-mcp-host.md) for why that key is pinned and
|
|
397
400
|
why `disabledServers` is never the way to hide an import.
|
|
398
401
|
|
|
402
|
+
## 4c. Optional OpenAI Codex CLI native citizen
|
|
403
|
+
|
|
404
|
+
Supported in 0.21.0 with Linux-focused evidence; on macOS it is NOT CERTIFIED —
|
|
405
|
+
pending physical host. It is native, not ACP: the purpose is to preserve the operator's
|
|
406
|
+
Codex tools, delegation, and work context rather than provide another GPT access path.
|
|
407
|
+
Entwurf owns four atoms, not the harness and not a daemon:
|
|
408
|
+
|
|
409
|
+
```bash
|
|
410
|
+
# All four are operator-scope; `entwurf setup` runs them when it detects Codex. No root.
|
|
411
|
+
entwurf install-codex-birth
|
|
412
|
+
entwurf install-codex-mcp
|
|
413
|
+
entwurf install-codex-statusline
|
|
414
|
+
entwurf install-codex-terminal-title
|
|
415
|
+
|
|
416
|
+
entwurf doctor-codex-birth
|
|
417
|
+
entwurf doctor-codex-mcp
|
|
418
|
+
entwurf doctor-codex-statusline
|
|
419
|
+
entwurf doctor-codex-terminal-title
|
|
420
|
+
```
|
|
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
|
+
|
|
448
|
+
The birth atom publishes a `SessionStart` declaration into `$CODEX_HOME/hooks.json` with its
|
|
449
|
+
launcher closure under `$XDG_DATA_HOME/entwurf/codex-birth`; it mints a V3 record on the
|
|
450
|
+
thread's first turn and sets the thread title to the garden id.
|
|
451
|
+
|
|
452
|
+
**One step is the operator's, and no command can do it for them.** The vendor will not run a
|
|
453
|
+
user-layer hook until it has been trusted once: open a visible plain Codex, answer
|
|
454
|
+
`Trust all and continue`, then send one first turn. `doctor-codex-birth` reads the receipt the
|
|
455
|
+
vendor writes for that declaration and is RED without it — entwurf never writes, pre-seeds or
|
|
456
|
+
recomputes that value, because it is the operator's security decision, not ours. After the one
|
|
457
|
+
answer, later sessions raise no prompt and are born automatically. The MCP writer owns
|
|
458
|
+
`[mcp_servers.entwurf-bridge]` in `$CODEX_HOME/config.toml`. Its exact `env_vars` allowlist is
|
|
459
|
+
`CODEX_HOME`, `ENTWURF_DIR`, `PI_CODING_AGENT_DIR`, `ENTWURF_META_SESSIONS_DIR`,
|
|
460
|
+
`ENTWURF_META_MAILBOX_DIR`, `ENTWURF_META_SENDERS_DIR`, `ENTWURF_META_RECEIVERS_DIR`,
|
|
461
|
+
`TMUX`, and `TMUX_PANE`.
|
|
462
|
+
The status-line writer owns only `thread-title`. Foreign or symlinked config is refused.
|
|
463
|
+
|
|
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.
|
|
468
|
+
|
|
469
|
+
```bash
|
|
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.
|
|
476
|
+
CODEX_HOME="${CODEX_HOME:-$HOME/.codex}"
|
|
477
|
+
codex --remote "unix://$CODEX_HOME/app-server-control/app-server-control.sock"
|
|
478
|
+
```
|
|
479
|
+
|
|
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
|
|
488
|
+
`CODEX_HOME` and Entwurf roots cross the same explicit `env_vars` boundary. Strict request `_meta`
|
|
489
|
+
identifies the thread caller, not its seat. Delivery uses one `codex queue` invocation with no retry.
|
|
490
|
+
|
|
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.
|
|
496
|
+
|
|
497
|
+
`entwurf_fresh_call` accepts backend `codex` — supported in 0.21.0 — and requires an explicit model. Its
|
|
498
|
+
preflight must prove the safe birth closure, exact MCP/env atom, `thread-title`, and app-server
|
|
499
|
+
socket before tmux mutation. The sibling's garden id comes from the callback sender envelope,
|
|
500
|
+
never the launch receipt. There is no Codex resume surface, watcher, supervisor, or app-server
|
|
501
|
+
lifecycle ownership. The accepted LIVE used a real record-backed visible Pi for the first leg;
|
|
502
|
+
release-gate stripped ambient `PI_SESSION_ID`/`PI_AGENT_ID`, and the fixture/self-fetch citizen only
|
|
503
|
+
collected receipts. The smoke reported initial Pi, app-server, fresh Codex, and outbound Pi
|
|
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.
|
|
509
|
+
Qualification and the frozen full floor are complete for this lane: `check-gate-qualification`
|
|
510
|
+
killed 475/475 across 43 lanes with origin purity green, `check:full` exited 0, and the LIVE release
|
|
511
|
+
gate reported MUST 24/0/0 with `cut: OK`. The bounds above are unchanged — no request→arbitrary
|
|
512
|
+
attached-TUI seat join, no resume surface, and macOS remains NOT CERTIFIED pending a physical host.
|
|
513
|
+
|
|
514
|
+
Oracle runs the first-admission LIVE with explicit ownership/model selectors:
|
|
515
|
+
|
|
516
|
+
```bash
|
|
517
|
+
LIVE=1 \
|
|
518
|
+
ENTWURF_CODEX_APP_SERVER_PID=<existing-app-server-pid> \
|
|
519
|
+
ENTWURF_CODEX_FRESH_MODEL=<codex-model> \
|
|
520
|
+
ENTWURF_CODEX_FRESH_PI_MODEL=<pi-model> \
|
|
521
|
+
entwurf smoke-codex-fresh-live
|
|
522
|
+
```
|
|
523
|
+
|
|
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.
|
|
528
|
+
|
|
399
529
|
## 5. Optional Antigravity native citizen
|
|
400
530
|
|
|
401
531
|
This rail is CERTIFIED on Linux; on macOS it is NOT CERTIFIED — pending
|
|
@@ -464,7 +594,7 @@ rail receipt. Doctors refuse on Darwin until a physical-host receipt
|
|
|
464
594
|
exists.
|
|
465
595
|
|
|
466
596
|
If install or doctor reports an unreadable/old active citizen generation, do not edit
|
|
467
|
-
records by hand. Close pi, Claude, Copilot,
|
|
597
|
+
records by hand. Close pi, Claude, Copilot, OMP, agy, and Codex sessions first, run
|
|
468
598
|
`entwurf meta-bridge-fresh-cut`, and read its exit status before any install. Then choose the
|
|
469
599
|
installation mode you actually own:
|
|
470
600
|
|
|
@@ -496,6 +626,7 @@ PASS/SKIP/FAIL summary. The complete quiescence, archive, and exit-code contract
|
|
|
496
626
|
- `scripts/raw-macos-measure/probe.sh`: physical-Darwin host facts (~3.5s,
|
|
497
627
|
no install, no login). Measurement, not a certification path.
|
|
498
628
|
- `doctor-meta-bridge`: one installed real Claude host, only with a new live session.
|
|
629
|
+
- `doctor-codex-{birth,mcp,statusline}` plus `LIVE=1 CODEX_LIVE_THREAD_ID=<id> entwurf smoke-codex-native-push-live`: one installed Codex host; the aggregate does not own a loaded thread.
|
|
499
630
|
- `LIVE=1 entwurf release-gate /path/to/scratch --cut`: aggregate runtime acceptance (`--cut` makes any MUST SKIP red; without it the run is a diagnostic pass).
|
|
500
631
|
|
|
501
632
|
Keep these verdicts separate. Current protocol is [VERIFY.md](../VERIFY.md); recorded
|
|
@@ -519,6 +650,9 @@ entwurf uninstall-copilot-bridge
|
|
|
519
650
|
entwurf uninstall-agy-hooks
|
|
520
651
|
entwurf uninstall-agy-statusline
|
|
521
652
|
entwurf uninstall-agy-bridge
|
|
653
|
+
entwurf uninstall-codex-statusline
|
|
654
|
+
entwurf uninstall-codex-mcp
|
|
655
|
+
entwurf uninstall-codex-birth
|
|
522
656
|
entwurf remove ~/entwurf-smoke
|
|
523
657
|
# only when no other project uses the shared user-scope pi registration:
|
|
524
658
|
entwurf remove-user-scope
|