@junghanacs/entwurf 0.21.0 → 0.23.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 +3 -2
- package/BASELINE.md +3 -1
- package/CHANGELOG.md +658 -0
- package/DELIVERY.md +167 -29
- package/README.md +104 -14
- package/VERIFY.md +73 -11
- package/docs/external-mcp-host.md +16 -6
- package/docs/setup-clean-host.md +73 -22
- package/mcp/entwurf-bridge/dist/mcp/entwurf-bridge/src/index.js +54 -31
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/codex-caller-seat.js +174 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/codex-declaration.js +609 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/codex-fresh-preflight.js +289 -57
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-control-rpc.js +40 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-fact-provider.js +6 -2
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-facts.js +7 -1
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-peer-observe.js +67 -2
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-peers-render.js +9 -1
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-self-address.js +24 -0
- 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/fresh-call-composition.js +423 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/fresh-call-dispatch.js +98 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/herdr-fresh-call.js +1018 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/herdr-placement.js +213 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/mux-fresh-call.js +171 -263
- package/mcp/entwurf-bridge/dist/scripts/codex-socket-path.js +30 -0
- package/mcp/entwurf-bridge/dist/scripts/peer-facts.js +109 -0
- package/mcp/entwurf-bridge/src/index.ts +65 -31
- package/mcp/entwurf-bridge/tsconfig.build.json +10 -0
- package/package.json +4 -4
- package/pi-extensions/entwurf-control.ts +91 -52
- package/pi-extensions/lib/codex-caller-seat.ts +204 -0
- package/pi-extensions/lib/codex-declaration.js +612 -0
- package/pi-extensions/lib/codex-fresh-preflight.ts +325 -52
- package/pi-extensions/lib/entwurf-control-rpc.ts +46 -0
- package/pi-extensions/lib/entwurf-fact-provider.ts +13 -2
- package/pi-extensions/lib/entwurf-facts.ts +14 -1
- package/pi-extensions/lib/entwurf-peer-observe.ts +72 -2
- package/pi-extensions/lib/entwurf-peers-render.ts +9 -1
- package/pi-extensions/lib/entwurf-self-address.ts +33 -0
- package/pi-extensions/lib/entwurf-v2-runner.ts +3 -2
- package/pi-extensions/lib/entwurf-v2-send.ts +16 -11
- package/pi-extensions/lib/fresh-call-composition.ts +493 -0
- package/pi-extensions/lib/fresh-call-dispatch.ts +142 -0
- package/pi-extensions/lib/herdr-fresh-call.ts +1378 -0
- package/pi-extensions/lib/herdr-placement.ts +276 -0
- package/pi-extensions/lib/mux-fresh-call.ts +257 -304
- package/run.sh +522 -10
- package/scripts/check-codex-app-server-launch.ts +445 -0
- package/scripts/check-codex-birth-hook.ts +187 -0
- package/scripts/check-entwurf-control-rpc.ts +138 -0
- package/scripts/check-entwurf-fact-provider.ts +4 -2
- package/scripts/check-entwurf-facts.ts +12 -6
- package/scripts/check-entwurf-peers-surface.ts +3 -1
- package/scripts/check-entwurf-self-address.ts +68 -0
- package/scripts/check-entwurf-v2-production.ts +42 -1
- package/scripts/check-entwurf-v2-send.ts +26 -7
- package/scripts/check-fresh-call-dispatch.ts +301 -0
- package/scripts/check-gate-qualification.ts +19 -5
- package/scripts/check-herdr-activation.ts +1293 -0
- package/scripts/check-herdr-fresh-call.ts +1250 -0
- package/scripts/check-herdr-placement.ts +383 -0
- package/scripts/check-herdr-plugin-build.ts +842 -0
- package/scripts/check-herdr-plugin-profile.ts +371 -0
- package/scripts/check-herdr-plugin.ts +523 -0
- package/scripts/check-herdr-runtime-bootstrap.ts +1264 -0
- package/scripts/check-herdr-sandbox.ts +456 -0
- package/scripts/check-herdr-supply.ts +184 -0
- package/scripts/check-mux-launch-tmux.ts +331 -35
- package/scripts/check-mux-launcher-fence.ts +138 -0
- package/scripts/check-peer-facts.ts +331 -0
- package/scripts/check-release-gate-outcomes.ts +12 -0
- package/scripts/check-typing-call-fence.ts +296 -0
- package/scripts/codex-app-server-launch.sh +275 -0
- package/scripts/codex-birth-doctor.sh +159 -16
- package/scripts/codex-birth-install.sh +178 -72
- package/scripts/codex-birth-uninstall.sh +162 -7
- 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/fixtures/herdr-supply.json +29 -0
- package/scripts/herdr-activation.mjs +536 -0
- package/scripts/herdr-plugin-activate.mjs +270 -0
- package/scripts/herdr-plugin-deactivate.mjs +193 -0
- package/scripts/herdr-runtime.mjs +1203 -0
- package/scripts/install-herdr-ci.sh +96 -0
- package/scripts/lib/codex-fresh-live-protocol.ts +13 -5
- package/scripts/lib/codex-fresh-source-receipts.ts +29 -2
- package/scripts/lib/tree-digest.ts +97 -0
- package/scripts/meta-bridge-install.sh +19 -2
- package/scripts/meta-bridge-state.py +167 -11
- package/scripts/mutants/codex-app-server-launch.json +157 -0
- package/scripts/mutants/codex-caller-seat.json +334 -0
- package/scripts/mutants/codex-native.json +195 -29
- package/scripts/mutants/control-socket-disconnect.json +57 -0
- package/scripts/mutants/fresh-call-dispatch.json +154 -0
- package/scripts/mutants/herdr-activation.json +305 -0
- package/scripts/mutants/herdr-fresh-call.json +448 -0
- package/scripts/mutants/herdr-placement.json +161 -0
- package/scripts/mutants/herdr-plugin-build.json +160 -0
- package/scripts/mutants/herdr-plugin-profile.json +202 -0
- package/scripts/mutants/herdr-plugin.json +133 -0
- package/scripts/mutants/herdr-runtime-bootstrap.json +411 -0
- package/scripts/mutants/herdr-supply.json +133 -0
- package/scripts/mutants/meta-hook-session-switch.json +2 -2
- package/scripts/mutants/mux-fresh-call.json +184 -25
- package/scripts/mutants/mux-launcher-fence.json +13 -0
- package/scripts/mutants/omp-fresh.json +4 -4
- package/scripts/mutants/peer-facts.json +98 -0
- package/scripts/mutants/self-address.json +33 -0
- package/scripts/mutants/typing-call-fence.json +17 -0
- package/scripts/mutants/v2-surface.json +22 -0
- package/scripts/peer-facts.ts +120 -0
- package/scripts/register-pi-package.py +8 -0
- package/scripts/register-pi-provider.py +88 -12
- package/scripts/smoke-codex-birth.sh +281 -12
- 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-herdr-fresh-call-live.ts +1027 -0
- package/scripts/smoke-herdr-plugin-build-live.ts +543 -0
- package/scripts/smoke-setup-verdict.sh +13 -11
package/VERIFY.md
CHANGED
|
@@ -54,6 +54,12 @@ Verification here is not a benchmark. In production we exchange short turns and
|
|
|
54
54
|
- **Every MUST step is invoked and reports its own outcome.** Prerequisite-bearing LIVE MUST steps ride the P1 STEP OUTCOME protocol (`scripts/lib/step-outcome.sh`): exit 0 = PASS, exit 97 = SKIP (a prerequisite the step does not have, printed as an `[entwurf:skip]` line), anything else = FAIL. The two non-skippable static steps (`pnpm run check:full`, `check-gate-qualification`) are inline: 0 = PASS, nonzero = FAIL, no SKIP arm — they have no prerequisite to decline. A skip is never counted as a pass — that hole is what let a cortex-less host read as cortex acceptance. Without `--cut` this is the unattended diagnostic: SKIPs are reported and the run still exits 0. **`--cut` makes it acceptance and any MUST SKIP is red**, which is how "a real cut needs `LIVE=1` with `SKIP=0`" stopped being prose. A green MUST gate is **necessary, not sufficient** — GLG authorizes the cut.
|
|
55
55
|
- **When cost-bearing MUST gates run (fixed 2026-07-23, the F6/F7 lesson):** a commit that touches a rail a MUST-tier live gate covers runs that gate **before cross-review is requested** — never parked behind "run it at approval time". Deferring a wired gate to a human decision is what let F6/F7 ship reviewed-and-approved; the wiring exists so the verdict never depends on who pressed enter. "배선이 없어 못 한 것은 OK, 배선이 있는데 안 돌린 것은 우리가 남긴 구멍이다." Model-in-loop cost is spent via the subscription-backed `entwurf` provider where the gate allows it, a free-tier native model otherwise; cost is a reason to pick the cheap target, not to skip the gate.
|
|
56
56
|
|
|
57
|
+
> **The herdr rail is an on-demand axis until it has run.** `smoke-herdr-fresh-call-live` opens a real caller inside a real herdr pane, has it invoke the PUBLIC `entwurf_fresh_call`, and reads the child's nonce callback — the one axis no deterministic gate reaches, since the rail is selected from the caller's own process environment. **The release aggregate does not yet require the herdr rail**: herdr is optional (a cut on a host without it must not block), and first evidence precedes promotion — a rail whose first acceptance run is days old has not earned a place in the gate that blocks every cut. Run it directly with `LIVE=1 ./run.sh smoke-herdr-fresh-call-live` whenever a cut changes herdr rail code, and promote it to a MUST only after the axis has a run history to point at. Without `LIVE=1`, or without herdr/pi/claude on PATH, it reports protocol SKIP — never a pass.
|
|
58
|
+
|
|
59
|
+
> **The Herdr plugin's install-time build has two axes, and one of them needs the network (#116 M3-b3).** `check-herdr-plugin-build` is deterministic and rides `check:hermetic`: the listing reaches the runner through its own spawn seam (there is no fake `herdr` binary in this repo), and the one cell that drives the REAL binary reports a named SKIP on a host without it — `ENTWURF_REQUIRE_HERDR=1` makes that absence red. What it cannot have is the real acquisition: `npm pack git+https://github.com/junghan0611/entwurf.git#<full sha>` builds the bridge through `prepare`, which installs devDependencies from the registry. So `LIVE=1 ./run.sh smoke-herdr-plugin-build-live` owns that axis, and its cells are **two kinds of evidence, labelled as such**: three are the real Herdr journey — a real `herdr plugin install` driving the real `[[build]]`, with the UNCHANGED product remote redirected to a local bare clone by git `insteadOf` (the one substitution, which is what makes an unpushed candidate testable) — covering remote-commit available, the `{pi}` → `{pi, claude-code}` reinstall with OpenCode as a zero-write negative control and exactly ONE counted vendor MCP owner entry, and the post-build Herdr-commit gap (where the ledger has already rebound to the gap commit, so the honest assertion is that transition and not unchanged bytes). The fourth cell — remote-commit **unavailable** — calls the shipped runtime leaf directly with the fixed product argv and is therefore **acquisition-leaf evidence, not a journey**; routing it through Herdr would add a second explanation for one red. Its oracle is the honest one too: active runtime, ledger and harness bytes preserved, while the journal is left as the certified `installing` retry authority the transaction deliberately writes before acquiring, and a normal install afterwards is green. Its Claude cell is where the vendor question the deterministic gates refuse to claim gets answered — whether Claude Code accepts an ABSOLUTE executable as an MCP command — and it reports a named skip on a host without `claude`, never a pass. Like the herdr fresh-call rail, this smoke is **on-demand and not an aggregate MUST**. The release aggregate does not require the plugin build smoke: herdr is optional, a cut on a host without it must not block, and first evidence precedes promotion.
|
|
60
|
+
|
|
61
|
+
> **Switching the plugin's production acquisition source is a re-proof, not a configuration change (#116 M3-b3).** `runtime-lock.json` carries a closed `source` discriminant: `npm` (exact `name@version` plus the published sha512, compared against the tarball's own bytes) is the production authority, and `herdr-checkout` (the exact commit Herdr checked out, from the fixed remote) is a VERIFICATION-ONLY carrier that exists so a candidate needs no npm release. Evidence does not transfer between them. Before a release that moves the production source — to npm, or to a GitHub Release tarball plus its sha512 — the exact acquisition and integrity comparison, the installed runtime (name@version, compiled entry, three executable bins, a real `check-bridge`), the swap and torn-swap recovery, activation and deactivation, and the package-consumer proof must all be re-run against THAT source. A candidate proof green on the checkout carrier says nothing about a registry artifact, and the reverse is equally true. **Records from before this contract are refused, not migrated:** a v1 runtime journal or activation ledger fails certification by name, and the way forward on such a host is an explicit `entwurf herdr-plugin-deactivate` (or a hand-cleared runtime/ledger state) before the next install — there is no reader that accepts both schemas, because the older shape cannot say WHICH artifact it was.
|
|
62
|
+
|
|
57
63
|
> **Cortex is an on-demand axis, not an aggregate one.** Its rail needs an external Snowflake connection and login that the host owns, not the repo — so wiring it into the aggregate would block every cut taken on a host without that account. **The release aggregate does not re-certify Cortex**; `LIVE=1 ENTWURF_ACP_CORTEX_CONNECTION=<conn> ./run.sh smoke-acp-cortex-live` stays a required direct call whenever a cut changes Cortex rail code or an operator elects to certify that host. Its honest-skip behaviour is unchanged: run it without the connection and it reports protocol SKIP, never a pass. **Operator-host limit (2026-08-20, measured):** the source-owning host has no Snowflake subscription, and Cortex Code offers no free tier that a live turn can reach — a direct attempt confirmed it. So on this host `smoke-acp-cortex-live` can only ever report protocol SKIP, and no LIVE Cortex evidence can be produced here until Snowflake supplies a free-usable route or the account is subscribed. Record that SKIP as an absent axis; never soften it into a pass, and never substitute a different backend as Cortex evidence.
|
|
58
64
|
>
|
|
59
65
|
> A cut that touches the prompt-lifecycle contract (no wall clock on a running turn) owes one long-turn acceptance the aggregate floor is too short to carry: `LIVE=1 ./run.sh smoke-acp-long-turn-live` drives a real turn whose tool work outlasts the retired 600s cutoff and requires exactly one cold ACP bootstrap in the transcript. It takes >12 minutes by construction and is on-demand, not part of `release-gate`.
|
|
@@ -71,26 +77,62 @@ Verification here is not a benchmark. In production we exchange short turns and
|
|
|
71
77
|
> 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
78
|
>
|
|
73
79
|
> 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
|
|
80
|
+
> on-demand host axis. The first-admission acceptance passed on 2026-09-12 under the fixed `codex`
|
|
81
|
+
> home topology #95 D1 later retired; `DELIVERY.md` owns its 57-assertion receipt and digest as
|
|
82
|
+
> history. Re-runs follow the caller-seat topology instead: the operator-owned app-server sits in
|
|
83
|
+
> its own session A, the initial Pi and the Codex it opens both sit in a DIFFERENT session S, and
|
|
84
|
+
> the Codex-opened Pi must land in S. **A ≠ S is a precondition, not a preference** — the
|
|
85
|
+
> app-server's inherited `TMUX` names A, so an outbound Pi in S can only have come from the
|
|
86
|
+
> caller's own pane title, and the receipt must name `codex-title-anchor` as the rule that chose
|
|
87
|
+
> it. Exact `rust-v0.153.4` source still exposes no arbitrary request→attached-TUI-seat value, so
|
|
88
|
+
> placing a sibling beside a TUI whose thread nobody named stays unsupported and unclaimed.
|
|
81
89
|
>
|
|
82
90
|
> The A cell takes no inferred server or models:
|
|
83
91
|
> `LIVE=1 ENTWURF_CODEX_APP_SERVER_PID=<existing-app-server-pid>
|
|
84
92
|
> ENTWURF_CODEX_FRESH_MODEL=<codex-model>
|
|
85
93
|
> ENTWURF_CODEX_FRESH_PI_MODEL=<pi-model> ./run.sh smoke-codex-fresh-live`.
|
|
94
|
+
> **Both of THIS cell's live-spend defaults are `gpt-5.6-luna` from 2026-09-17**
|
|
95
|
+
> (`openai-codex/gpt-5.6-luna` on the pi side) — the Codex leg came down off `sol`. It is a tier
|
|
96
|
+
> choice and not a gate change; the receipt this cell is accepted on must be the one that actually
|
|
97
|
+
> ran, never a `sol` run inherited from before. This says nothing about other cells: `smoke-omp-
|
|
98
|
+
> fresh-live` keeps `openai-codex/gpt-5.6-sol`, the model its bootstrap callback was measured on.
|
|
99
|
+
>
|
|
100
|
+
> **This cell needs its launch directory ANSWERED, once, and that is a vendor fact rather than a
|
|
101
|
+
> gate setting.** Codex records a direct folder decision per exact directory on this rail, so the smoke
|
|
102
|
+
> launches in one stable path instead of a fresh `mkdtemp`, and it PRINTS that path as
|
|
103
|
+
> `launch-cwd <dir>` on every run. **Do not spell it `$TMPDIR/entwurf-codex-fresh-live`** — the
|
|
104
|
+
> smoke resolves it with `os.tmpdir()`, which is this host's `TMPDIR` when one is set and `/tmp`
|
|
105
|
+
> when it is not, so on a host with no `TMPDIR` that shell expansion names
|
|
106
|
+
> `/entwurf-codex-fresh-live` in the filesystem root: a different directory the vendor would ask
|
|
107
|
+
> about separately. Derive it exactly:
|
|
108
|
+
>
|
|
109
|
+
> ```bash
|
|
110
|
+
> node -e 'console.log(require("node:os").tmpdir() + "/entwurf-codex-fresh-live")'
|
|
111
|
+
> ```
|
|
112
|
+
>
|
|
113
|
+
> Open a plain `codex -C <that path>` once, answer the prompt, and every later run is unattended.
|
|
114
|
+
> Skip it and the smoke says so in its own assertion before anything is launched —
|
|
115
|
+
> `codex-launch-cwd-undecided` with the exact repair command, not a callback timeout twenty
|
|
116
|
+
> assertions later. **That precondition belongs to this gate, not to the product:** an ordinary
|
|
117
|
+
> `entwurf_fresh_call` only prints the same note and opens the window, because a human can answer
|
|
118
|
+
> the screen. Here nobody can, which is the whole reason the gate asserts it.
|
|
86
119
|
>
|
|
87
120
|
> The command name is not proof. The first leg must be a real record-backed visible Pi
|
|
88
|
-
> citizen
|
|
121
|
+
> citizen in a session OTHER than the app-server's opening Codex; if a fixture or self-fetch receipt collector
|
|
89
122
|
> is what OPENS Codex, the gate does not satisfy the topology. A fixture that only COLLECTS
|
|
90
123
|
> receipts beside that real Pi leg is not the disqualifier — substituting for the leg is (see
|
|
91
124
|
> `docs/adding-a-harness.md`, the same wording). The receipt must separately name the initial
|
|
92
125
|
> Pi, app-server, fresh Codex, and outbound Pi coordinates.
|
|
93
126
|
>
|
|
127
|
+
> **Running the 37-minute qualification on a memory-pressured host.** `[측정 2026-09-16,
|
|
128
|
+
> thinkpad, 27 GiB]` `check-gate-qualification` took 37m37s and was killed three times by the
|
|
129
|
+
> HARNESS's low-memory watchdog — not by any gate, and not by the kernel OOM killer. Each kill
|
|
130
|
+
> left the checkout intact (the runner restores it) but discarded the run. Detaching it with
|
|
131
|
+
> `setsid` survived, at the cost of the harness no longer tracking it: completion raises no
|
|
132
|
+
> notification, so a separate waiter is needed for the signal. Record this as a host condition
|
|
133
|
+
> rather than a gate property — it says nothing about the candidate, and the same command on a
|
|
134
|
+
> less pressured host needs none of it.
|
|
135
|
+
>
|
|
94
136
|
> Authoritative per-cut counts and digests live in BASELINE/CHANGELOG, not inline
|
|
95
137
|
> here; embedding them in the protocol makes a correct guide stale after every cut.
|
|
96
138
|
|
|
@@ -108,7 +150,7 @@ Do not collapse source, package, fixture, and native-host evidence into one “g
|
|
|
108
150
|
| 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
151
|
| Native Claude host | installed strict doctor against a new real session | Missing live join is `NOT CERTIFIED`, not a fixture PASS. |
|
|
110
152
|
| 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 |
|
|
153
|
+
| 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
154
|
|
|
113
155
|
The repo-local `entwurf-release` skill owns the `land → prepare → make → publish`
|
|
114
156
|
state machine. Each mode is a separate GLG authorization. Preserve one candidate,
|
|
@@ -137,6 +179,26 @@ The manual `pi --session` path is used only when (a) the entwurf path itself is
|
|
|
137
179
|
- Execute one command at a time (no `;`-chaining). Preserve full stdout/stderr at each step.
|
|
138
180
|
- On anything wrong, **stop and hold** — preserve session/cache/process state before proceeding.
|
|
139
181
|
|
|
182
|
+
### Choosing the affected set for an inner loop
|
|
183
|
+
|
|
184
|
+
The inner loop runs only affected gates, so the cost of that choice is a gap nobody sees until the
|
|
185
|
+
full floor runs. Two rules exist because both gaps were paid for on the #95 lane B candidate, with
|
|
186
|
+
every focused gate green at the time:
|
|
187
|
+
|
|
188
|
+
- **Mutant manifests are keyed by SUBJECT FILE, not by lane.** Edit a file and check every
|
|
189
|
+
`scripts/mutants/*.json` whose `subject` names it, not only the lanes you wrote. `[측정
|
|
190
|
+
2026-09-16]` renaming one local in `mcp/entwurf-bridge/src/index.ts` left
|
|
191
|
+
`FRESHCALL-CODEX-PREMUTATION-MCP` in a manifest the lane never touched; qualification reported
|
|
192
|
+
it `MUTANT-STALE` (find matched 0×). **A stale mutant is a claim nobody is testing** — its
|
|
193
|
+
assertion keeps passing while the mutation that gives it meaning is never applied — which is why
|
|
194
|
+
this is a red rather than a warning. `check-gate-manifests` validates shape and inventory in
|
|
195
|
+
seconds but cannot see a find that no longer matches; only the executing body can.
|
|
196
|
+
- **Any `mcp/entwurf-bridge/src/**` edit owes `pnpm run build-bridge` plus
|
|
197
|
+
`./run.sh check-bridge-delivery` in the same inner loop.** `[측정 2026-09-16]` the same candidate
|
|
198
|
+
reached `check:full` with a stale compiled entry and failed in 22 seconds on "artifact is not
|
|
199
|
+
stale". The installed surfaces run compiled JS (Hard Rule 11), so an un-rebuilt `dist` means the
|
|
200
|
+
focused gates proved the source while every package-shaped gate still judged the previous build.
|
|
201
|
+
|
|
140
202
|
### Wording — avoid safety-interpretation contamination
|
|
141
203
|
|
|
142
204
|
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 +233,7 @@ The goal is not merely "invoke Claude Code." We want:
|
|
|
171
233
|
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
234
|
5. agy bridge + exact permission + statusline + `PreInvocation` hook — only when `agy` is on PATH; each adapter is idempotent and independently doctorable
|
|
173
235
|
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
|
|
236
|
+
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
237
|
8. `entwurf-bridge` install smoke (`validate_entwurf_bridge`)
|
|
176
238
|
9. computed summary — per-component PASS/SKIP/FAIL; any detected-integration FAIL makes the whole command exit nonzero while valid components stay installed
|
|
177
239
|
|
|
@@ -329,7 +391,7 @@ The minimum passing bar:
|
|
|
329
391
|
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
392
|
6. **Carrier separation honored:** engraving vs pi-context-augment kept distinct (§1A.0); no bridge-identity narrative attributed to the engraving carrier.
|
|
331
393
|
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
|
|
394
|
+
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
395
|
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
396
|
10. **Hygiene:** no orphan ACP children; no unexpected persisted session garbage (a turn-scoped `cwd:` fallback is never a persisted reuse).
|
|
335
397
|
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,23 +404,58 @@ 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.
|
|
423
451
|
|
|
452
|
+
It owns that ONE declaration, not the file. Another integration may declare its own
|
|
453
|
+
`SessionStart` group in the same hooks.json — Herdr's official Codex integration does — and the
|
|
454
|
+
vendor runs both, because trust is keyed per declaration. So installing into a file somebody else
|
|
455
|
+
already wrote is an append, not a refusal; uninstalling takes out only entwurf's group and leaves
|
|
456
|
+
every neighbouring byte where it was; and `doctor-codex-birth` lists the neighbours in their own
|
|
457
|
+
FOREIGN section, certifying none of them and letting none of them move its verdict.
|
|
458
|
+
|
|
424
459
|
**One step is the operator's, and no command can do it for them.** The vendor will not run a
|
|
425
460
|
user-layer hook until it has been trusted once: open a visible plain Codex, answer
|
|
426
461
|
`Trust all and continue`, then send one first turn. `doctor-codex-birth` reads the receipt the
|
|
@@ -433,30 +468,41 @@ answer, later sessions raise no prompt and are born automatically. The MCP write
|
|
|
433
468
|
`TMUX`, and `TMUX_PANE`.
|
|
434
469
|
The status-line writer owns only `thread-title`. Foreign or symlinked config is refused.
|
|
435
470
|
|
|
436
|
-
Native delivery requires the operator-owned default app-server
|
|
437
|
-
|
|
471
|
+
Native delivery requires the operator-owned default app-server. The operator chooses the tmux
|
|
472
|
+
session that holds it: #95 D1 retired the requirement that the session be named `codex`, and Entwurf
|
|
473
|
+
neither creates nor supervises it. For the LIVE acceptance that session must NOT be the one the
|
|
474
|
+
Pi/Codex pair runs in.
|
|
438
475
|
|
|
439
476
|
```bash
|
|
440
|
-
# Run
|
|
477
|
+
# Run from a pane in the operator-owned tmux session that will hold the app-server. One
|
|
478
|
+
# command: entwurf spells the vendor's `app-server --listen unix://<default socket>` for you,
|
|
479
|
+
# creates the control directory, and exec()s it here. Ctrl-C is yours; nothing supervises it.
|
|
480
|
+
entwurf codex-app-server
|
|
481
|
+
|
|
482
|
+
# Attach your own visible Codex TUI from wherever you work — any session, not a reserved room.
|
|
441
483
|
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
484
|
codex --remote "unix://$CODEX_HOME/app-server-control/app-server-control.sock"
|
|
445
485
|
```
|
|
446
486
|
|
|
447
|
-
Omitted
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
487
|
+
Omitted fresh placement follows the CALLER on the caller's own tmux server: a Codex citizen opens
|
|
488
|
+
beside its own TUI pane, matched by the `thread-id` that pane's terminal title carries, with 0 or 2+
|
|
489
|
+
matching panes refused and no fallback. The app-server's `TMUX`/`TMUX_PANE` is still forwarded to its
|
|
490
|
+
MCP child, but it locates stores rather than a seat. It is **not** an arbitrary attached-TUI pane
|
|
491
|
+
join: exact Codex 0.153.4 source exposes no request→attached-TUI-seat carrier, so a pane whose thread
|
|
492
|
+
nobody named has no adjacency claim, and that wider topology stays unsupported and unclaimed. Entwurf
|
|
493
|
+
does not guess panes, create a tmux session, expose a generic app-server manager/API, or
|
|
494
|
+
start/restart the server. Custom
|
|
453
495
|
`CODEX_HOME` and Entwurf roots cross the same explicit `env_vars` boundary. Strict request `_meta`
|
|
454
496
|
identifies the thread caller, not its seat. Delivery uses one `codex queue` invocation with no retry.
|
|
455
497
|
|
|
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
|
|
498
|
+
The 2026-09-11 loaded-thread run remains pre-amendment native-push/identity evidence. The
|
|
499
|
+
first-admission acceptance passed on 2026-09-12 under the fixed-home topology #95 D1 later retired:
|
|
500
|
+
real initial Pi outside `codex`, then omitted-placement Codex and outbound Pi inside it, with exact
|
|
501
|
+
callbacks and addressed delivery both ways. `DELIVERY.md` owns that 57-assertion receipt, its four
|
|
502
|
+
coordinates, and its digest as history. The caller-seat re-run is **accepted** — 2026-09-16, 56
|
|
503
|
+
assertions for the seat topology and 65 for the caller-DIRECTORY axis, both exit 0 — and it, not the
|
|
504
|
+
fixed home, is the current contract (`DELIVERY.md`, `BASELINE.md:37-38`, `VERIFY.md` release
|
|
505
|
+
acceptance).
|
|
460
506
|
|
|
461
507
|
`entwurf_fresh_call` accepts backend `codex` — supported in 0.21.0 — and requires an explicit model. Its
|
|
462
508
|
preflight must prove the safe birth closure, exact MCP/env atom, `thread-title`, and app-server
|
|
@@ -465,7 +511,11 @@ never the launch receipt. There is no Codex resume surface, watcher, supervisor,
|
|
|
465
511
|
lifecycle ownership. The accepted LIVE used a real record-backed visible Pi for the first leg;
|
|
466
512
|
release-gate stripped ambient `PI_SESSION_ID`/`PI_AGENT_ID`, and the fixture/self-fetch citizen only
|
|
467
513
|
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
|
|
514
|
+
coordinates separately: the initial Pi differed and the latter three matched the exact `codex` home,
|
|
515
|
+
which was the contract at that date. #95 D1 (2026-09-16) retired that room — an omitted seat is now the
|
|
516
|
+
caller's own session for every backend, and the reshaped card requires the app-server to sit in a
|
|
517
|
+
DIFFERENT session from the Pi/Codex pair, because that is what tells the caller-pane anchor apart from
|
|
518
|
+
the app-server's inherited environment.
|
|
469
519
|
Qualification and the frozen full floor are complete for this lane: `check-gate-qualification`
|
|
470
520
|
killed 475/475 across 43 lanes with origin purity green, `check:full` exited 0, and the LIVE release
|
|
471
521
|
gate reported MUST 24/0/0 with `cut: OK`. The bounds above are unchanged — no request→arbitrary
|
|
@@ -481,9 +531,10 @@ ENTWURF_CODEX_FRESH_PI_MODEL=<pi-model> \
|
|
|
481
531
|
entwurf smoke-codex-fresh-live
|
|
482
532
|
```
|
|
483
533
|
|
|
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
|
|
534
|
+
Run this from a tmux session OTHER than the one the app-server is in. The entrypoint name does not
|
|
535
|
+
waive the contract above. A fixture/self-fetch citizen may collect receipts but cannot replace the real
|
|
536
|
+
visible Pi leg; a Pi that starts in the app-server's own session proves only the weaker shared-seat
|
|
537
|
+
mechanism, because both the anchor and the env fallback would answer the same room.
|
|
487
538
|
|
|
488
539
|
## 5. Optional Antigravity native citizen
|
|
489
540
|
|
|
@@ -64,7 +64,6 @@ import * as process from "node:process";
|
|
|
64
64
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
65
65
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
66
66
|
import { z } from "zod";
|
|
67
|
-
import { codexFreshPreflight } from "../../../pi-extensions/lib/codex-fresh-preflight.js";
|
|
68
67
|
import { controlSocketPathIn, defaultControlSocketDir } from "../../../pi-extensions/lib/control-socket-path.js";
|
|
69
68
|
import { resolveMailboxReceiverFacts } from "../../../pi-extensions/lib/entwurf-deliverability.js";
|
|
70
69
|
import { listEntwurfFacts } from "../../../pi-extensions/lib/entwurf-fact-provider.js";
|
|
@@ -74,9 +73,9 @@ import { nativePushSupported } from "../../../pi-extensions/lib/entwurf-v2-contr
|
|
|
74
73
|
import { resolveMailboxWakeModeCapability } from "../../../pi-extensions/lib/entwurf-v2-decider.js";
|
|
75
74
|
import { runAndRenderEntwurfV2FromSurface } from "../../../pi-extensions/lib/entwurf-v2-surface.js";
|
|
76
75
|
import { makeVisibleResumeDeps, renderVisibleResume, visibleResume, } from "../../../pi-extensions/lib/entwurf-v2-visible-resume.js";
|
|
76
|
+
import { dispatchFreshCall, renderDispatchedFreshCall } from "../../../pi-extensions/lib/fresh-call-dispatch.js";
|
|
77
77
|
import { probeNativeSenderAlive, reconcileSenderIdentityClaims, resolveCodexRequestSenderIdentity, resolveTrustedMetaSenderIdentity, } from "../../../pi-extensions/lib/meta-sender-identity.js";
|
|
78
78
|
import { applyOmpBridgeChildRootPolicy, defaultMetaMailboxDir, defaultMetaSessionsDir, makeStoreRecordReader, readActiveStoreEntries, readMetaInbox, readMetaReceiverMarker, readMetaSenderMarker, requireBackend, } from "../../../pi-extensions/lib/meta-session.js";
|
|
79
|
-
import { freshCall, renderFreshCall } from "../../../pi-extensions/lib/mux-fresh-call.js";
|
|
80
79
|
import { RESUME_CALL_REJECT_HINT, resumeCall } from "../../../pi-extensions/lib/mux-resume-call.js";
|
|
81
80
|
import { registerNativeConversation } from "../../../pi-extensions/lib/native-push/register.js";
|
|
82
81
|
const HOME = os.homedir();
|
|
@@ -270,7 +269,7 @@ async function resolveAuthoritativeSender(context, cwd = process.cwd()) {
|
|
|
270
269
|
if (!selected)
|
|
271
270
|
return null;
|
|
272
271
|
if (codex && codex.identity.gardenId === selected.id) {
|
|
273
|
-
return buildMetaSenderEnvelope(codex.identity, codex.identity.cwd);
|
|
272
|
+
return { ...(await buildMetaSenderEnvelope(codex.identity, codex.identity.cwd)), codexThreadId: codex.threadId };
|
|
274
273
|
}
|
|
275
274
|
if (marker && marker.identity.gardenId === selected.id) {
|
|
276
275
|
return buildMetaSenderEnvelope(marker.identity, marker.marker.cwd || cwd, marker);
|
|
@@ -598,29 +597,29 @@ server.tool("entwurf_register_native", "Register an ALREADY-RUNNING native conve
|
|
|
598
597
|
// its own garden id, a fresh cell answered with its uuidv7 `PI_SESSION_ID` value read out of the
|
|
599
598
|
// environment by an MCP server it had spawned itself — confidently, and wrong. A sibling launched
|
|
600
599
|
// against that answer would call home to a garden id nobody holds.
|
|
601
|
-
server.tool("entwurf_fresh_call", "Open ONE fresh visible sibling
|
|
602
|
-
"
|
|
603
|
-
"
|
|
604
|
-
"
|
|
605
|
-
"
|
|
606
|
-
"
|
|
607
|
-
"
|
|
608
|
-
"
|
|
609
|
-
"
|
|
610
|
-
"
|
|
611
|
-
"
|
|
612
|
-
"
|
|
613
|
-
"
|
|
614
|
-
"
|
|
615
|
-
"
|
|
616
|
-
"
|
|
617
|
-
"
|
|
618
|
-
"
|
|
619
|
-
"
|
|
620
|
-
"
|
|
600
|
+
server.tool("entwurf_fresh_call", "Open ONE fresh visible sibling beside you and hand it a first task. WHERE it opens is decided by where THIS " +
|
|
601
|
+
"agent runs, never by a parameter: inside herdr (HERDR_ENV=1) it opens a NEW UNFOCUSED TAB in your own " +
|
|
602
|
+
"herdr workspace, pi and claude-code " +
|
|
603
|
+
"only; everywhere else it opens in the operator's tmux with all five backends (pi, " +
|
|
604
|
+
"claude-code, copilot, omp, codex). No fallback: an incomplete herdr context is refused by name, " +
|
|
605
|
+
"never by a tmux window you cannot see from herdr. The sibling's FIRST action " +
|
|
606
|
+
"is a callback to you carrying a nonce, whose sender envelope is its garden id — that is how you learn the " +
|
|
607
|
+
"address of a thing that did not exist a moment ago. This returns a LAUNCH receipt (the owner's " +
|
|
608
|
+
"coordinates plus that nonce): it does NOT mean the runtime started, the first turn ran, or " +
|
|
609
|
+
"the task was delivered. Those coordinates are a VIEW, never an address — a herdr tab/pane id can change under " +
|
|
610
|
+
"a running sibling. Nothing polls. " +
|
|
611
|
+
"For EXISTING citizens use entwurf_v2 — this tool only creates, and entwurf_peers only reports. Model is REQUIRED and passed to the chosen " +
|
|
612
|
+
"runtime CLI (`provider/model` for pi, an id/alias for Claude Code, a pattern for the rest). On tmux, copilot/omp/codex are refused BEFORE any window " +
|
|
613
|
+
"opens when their birth, MCP, receive or visible-identity units are absent, and codex also needs the " +
|
|
614
|
+
"operator-owned app-server socket entwurf never starts; in herdr they are refused by name first. " +
|
|
615
|
+
"Optional placement.tmuxSession is a TMUX-ONLY seat naming ONE EXISTING session and " +
|
|
616
|
+
"ALWAYS wins; omitted, the seat follows the CALLER, never the backend opened: a CODEX CALLER opens beside " +
|
|
617
|
+
"its own TUI pane, every other caller in its own session; a missing one is tmux-session-missing and " +
|
|
618
|
+
"NOTHING is created. In herdr the field is refused by name. Do not put secrets in the task — model and task argv are visible to same-user " +
|
|
619
|
+
"processes.", {
|
|
621
620
|
backend: z
|
|
622
621
|
.enum(["pi", "claude-code", "copilot", "omp", "codex"])
|
|
623
|
-
.describe("Which fixed runtime to open. Only these five; there is no arbitrary command."),
|
|
622
|
+
.describe("Which fixed runtime to open. Only these five, and only pi/claude-code when this agent runs inside herdr; there is no arbitrary command."),
|
|
624
623
|
model: z
|
|
625
624
|
.string()
|
|
626
625
|
.min(1)
|
|
@@ -643,7 +642,7 @@ server.tool("entwurf_fresh_call", "Open ONE fresh visible sibling in the operato
|
|
|
643
642
|
cwd: z
|
|
644
643
|
.string()
|
|
645
644
|
.optional()
|
|
646
|
-
.describe("Optional literal ABSOLUTE path of an existing directory to start the sibling in (cross-repo fresh). Omit or pass \"\" to start
|
|
645
|
+
.describe("Optional literal ABSOLUTE path of an existing directory to start the sibling in (cross-repo fresh). Omit or pass \"\" to start where the CALLER is — this agent's own cwd on BOTH rails, or for a Codex caller its own record directory, because this bridge is the app-server's child and that process's directory is not the caller's. Taken exactly as given: no trim, no realpath, no project-name resolution. '#' is refused on the tmux rail only, because tmux format-expands a start directory; inside herdr it is an ordinary path character. The receipt echoes the directory that was REQUESTED or the caller record it came from, never an observation of where the pane landed."),
|
|
647
646
|
placement: z
|
|
648
647
|
.object({
|
|
649
648
|
tmuxSession: z
|
|
@@ -651,12 +650,26 @@ server.tool("entwurf_fresh_call", "Open ONE fresh visible sibling in the operato
|
|
|
651
650
|
.describe("EXACT name of an EXISTING session on this agent's own tmux server. Nothing is created: an absent session is refused as tmux-session-missing, and a name outside [A-Za-z0-9][A-Za-z0-9_-]* as tmux-session-name-invalid."),
|
|
652
651
|
})
|
|
653
652
|
.optional()
|
|
654
|
-
.describe("Optional expert seat override: open the sibling in ONE EXISTING tmux session of this agent's own server. When omitted, Codex
|
|
653
|
+
.describe("Optional expert seat override, TMUX ONLY: open the sibling in ONE EXISTING tmux session of this agent's own server, and it always wins. When omitted the seat follows the CALLER, never the backend being opened: a Codex CALLER opens beside its own TUI pane, matched by thread-id in that pane's terminal title (0 or 2+ matching panes refuse, never fall back); every other caller opens in its own session. A pane title is a placement input only — never an address, liveness or delivery fact. Nothing is ever created. Inside herdr this field is refused by name — placement there belongs to herdr, and a tmux session name would silently place the sibling somewhere else. Independent of cwd; neither is inferred from the other. The receipt reports the selected name (absent for the caller-pane rule, which observed a session rather than requesting a name), its source, and resolved target session id."),
|
|
655
654
|
}, async ({ backend, model, task, cwd, placement }, extra) => {
|
|
656
655
|
let callerGardenId = null;
|
|
656
|
+
// Present exactly when the reconciled caller is a record-backed codex citizen. Its ONLY
|
|
657
|
+
// consumer is the placement below: a Codex caller with no explicit seat opens its sibling
|
|
658
|
+
// beside its own TUI pane, found by that thread's terminal title (#95 lane B).
|
|
659
|
+
let callerNativeSessionId;
|
|
660
|
+
// The SAME citizen's record cwd, set under the SAME condition and carrying no further
|
|
661
|
+
// authority: `envelope.cwd` is only the record's directory when the selected sender is
|
|
662
|
+
// that codex citizen, so the two are derived from one fact rather than read separately.
|
|
663
|
+
// It exists because `process.cwd()` here is the operator-owned APP-SERVER's directory —
|
|
664
|
+
// this bridge is its MCP child — so a codex caller that names no cwd would otherwise open
|
|
665
|
+
// every sibling in the app-server's repo (#95 lane C §1). The composition consults it only
|
|
666
|
+
// when the tool call requested no cwd of its own.
|
|
667
|
+
let callerCwd;
|
|
657
668
|
try {
|
|
658
669
|
const self = await buildAuthoritativeSelfEnvelope({ requestMeta: extra._meta });
|
|
659
670
|
callerGardenId = self.envelope.sessionId;
|
|
671
|
+
callerNativeSessionId = self.codexThreadId;
|
|
672
|
+
callerCwd = self.codexThreadId === undefined ? undefined : self.envelope.cwd;
|
|
660
673
|
}
|
|
661
674
|
catch (err) {
|
|
662
675
|
// ONE error is a legitimate answer here: this host has no authoritative identity at all
|
|
@@ -671,11 +684,21 @@ server.tool("entwurf_fresh_call", "Open ONE fresh visible sibling in the operato
|
|
|
671
684
|
callerGardenId = null;
|
|
672
685
|
}
|
|
673
686
|
try {
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
const
|
|
687
|
+
// Rail choice, Codex preflight ordering and rendering all live in the composition root,
|
|
688
|
+
// so this surface and pi's own cannot drift apart on any of them. The two caller-derived
|
|
689
|
+
// codex inputs ride with the request: the root consults them only on the tmux rail,
|
|
690
|
+
// which is where #95 lane B/C measured them.
|
|
691
|
+
const dispatched = await dispatchFreshCall({
|
|
692
|
+
backend,
|
|
693
|
+
model,
|
|
694
|
+
task,
|
|
695
|
+
cwd,
|
|
696
|
+
placement,
|
|
697
|
+
callerGardenId,
|
|
698
|
+
callerNativeSessionId,
|
|
699
|
+
callerCwd,
|
|
700
|
+
});
|
|
701
|
+
const rendered = renderDispatchedFreshCall(dispatched);
|
|
679
702
|
return rendered.isError ? textErr(rendered.text) : textOk(rendered.text);
|
|
680
703
|
}
|
|
681
704
|
catch (err) {
|