@junghanacs/entwurf 0.41.0 → 0.41.2
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 +1 -1
- package/BASELINE.md +7 -5
- package/CHANGELOG.md +426 -0
- package/DELIVERY.md +37 -20
- package/README.md +83 -81
- package/VERIFY.md +10 -10
- package/demo/README.md +1 -1
- package/docs/acp-backend-rail.md +1 -1
- package/docs/external-mcp-host.md +1 -1
- package/docs/setup-clean-host.md +95 -29
- package/mcp/entwurf-bridge/dist/mcp/entwurf-bridge/src/index.js +14 -11
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-self-address.js +10 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-contract.js +23 -2
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-decider.js +7 -1
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-surface.js +3 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/fresh-call-composition.js +21 -10
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/herdr-fresh-call.js +4 -2
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/meta-sender-identity.js +5 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/mux-fresh-call.js +6 -4
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/native-push/register.js +13 -2
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/omp-fresh-preflight.js +2 -2
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/pi-durable-fresh-preflight.js +1 -1
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/remote-pair-receive.js +25 -17
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/remote-pair-send.js +94 -28
- package/mcp/entwurf-bridge/dist/pi-extensions/meta-bridge-omp.js +57 -7
- package/mcp/entwurf-bridge/dist/pi-extensions/meta-bridge-pi-durable.js +2 -2
- package/mcp/entwurf-bridge/dist/scripts/remote-pair-receive.js +12 -12
- package/mcp/entwurf-bridge/src/index.ts +14 -11
- package/native/win32-x64/entwurf_win.node +0 -0
- package/native/win32-x64/manifest.json +2 -2
- package/package.json +15 -14
- package/pi/pi-durable/carrier/tui.js +18 -3
- package/pi/pi-durable/overlay/upstream-pin.json +12 -12
- package/pi-extensions/lib/acp/backend-adapter.ts +5 -3
- package/pi-extensions/lib/acp/backend.ts +12 -9
- package/pi-extensions/lib/entwurf-self-address.ts +13 -0
- package/pi-extensions/lib/entwurf-v2-contract.ts +25 -2
- package/pi-extensions/lib/entwurf-v2-decider.ts +7 -0
- package/pi-extensions/lib/entwurf-v2-surface.ts +3 -0
- package/pi-extensions/lib/fresh-call-composition.ts +21 -10
- package/pi-extensions/lib/herdr-fresh-call.ts +4 -2
- package/pi-extensions/lib/meta-sender-identity.ts +4 -0
- package/pi-extensions/lib/mux-fresh-call.ts +6 -4
- package/pi-extensions/lib/native-push/register.ts +17 -2
- package/pi-extensions/lib/omp-fresh-preflight.ts +2 -2
- package/pi-extensions/lib/pi-durable-fresh-preflight.ts +1 -1
- package/pi-extensions/lib/remote-pair-receive.ts +27 -19
- package/pi-extensions/lib/remote-pair-send.ts +117 -30
- package/pi-extensions/meta-bridge-omp.ts +69 -7
- package/pi-extensions/meta-bridge-pi-durable.ts +3 -3
- package/run.sh +218 -49
- package/scripts/check-agy-sender-identity.ts +6 -4
- package/scripts/check-entwurf-self-address.ts +34 -3
- package/scripts/check-entwurf-v2-contract.ts +24 -2
- package/scripts/check-entwurf-v2-production.ts +45 -31
- package/scripts/check-gate-qualification.ts +11 -9
- package/scripts/check-native-push-adapter.ts +4 -0
- package/scripts/check-native-push-register.ts +32 -82
- package/scripts/check-remote-pair-installed.ts +2 -2
- package/scripts/dev-bin.sh +112 -1
- package/scripts/lib/chain-terminus-publication.ts +58 -0
- package/scripts/lib/codex-fresh-live-protocol.ts +33 -2
- package/scripts/lib/codex-fresh-source-receipts.ts +112 -3
- package/scripts/lib/pi-durable-fresh-live-receipts.ts +29 -1
- package/scripts/mutants/chain-terminus-publication.json +17 -0
- package/scripts/mutants/codex-native.json +98 -0
- package/scripts/mutants/control-send-receipt.json +3 -3
- package/scripts/mutants/mux-fresh-call.json +16 -1
- package/scripts/mutants/native-push-paused.json +17 -0
- package/scripts/mutants/omp-fresh.json +13 -2
- package/scripts/mutants/pack-install.json +50 -4
- package/scripts/mutants/pi-durable.json +60 -0
- package/scripts/mutants/pi-floor.json +5 -5
- package/scripts/mutants/remote-pair-display.json +3 -3
- package/scripts/mutants/remote-pair-receive.json +41 -11
- package/scripts/mutants/remote-pair-roundtrip.json +67 -7
- package/scripts/mutants/remote-pair-send.json +156 -17
- package/scripts/mutants/remote-pair-transport.json +4 -4
- package/scripts/mutants/source-install.json +66 -0
- package/scripts/remote-pair-receive.ts +12 -12
- package/scripts/smoke-agy-install-state.sh +121 -0
- package/scripts/smoke-agy-native-push-live.ts +13 -0
- package/scripts/smoke-codex-fresh-live.ts +52 -28
- package/scripts/smoke-entwurf-chain-live.ts +7 -9
- package/scripts/smoke-herdr-fresh-call-live.ts +4 -4
- package/scripts/smoke-herdr-plugin-build-live.ts +179 -50
- package/scripts/smoke-herdr-raw-install-live.sh +10 -3
- package/scripts/smoke-mux-lifecycle-live.ts +140 -8
- package/scripts/smoke-omp-fresh-live.ts +5 -4
- package/scripts/smoke-pi-durable-fresh-live.ts +29 -7
- package/scripts/smoke-setup-verdict.sh +27 -2
package/DELIVERY.md
CHANGED
|
@@ -126,7 +126,7 @@ physical host. Do not read a D-level cell as a Darwin receipt.
|
|
|
126
126
|
|---|---|---|---|
|
|
127
127
|
| **pi Entwurf** | shipped | D7; D8 partial | Record-addressed Unix control socket. A record-less socket is diagnostic only and never dispatched. A pi session calls the verbs through Pi's built-in MCP running the compiled `entwurf-bridge` (#125); the receive rail is unchanged. |
|
|
128
128
|
| **Claude Code interactive `>=2.1.306`** | shipped; Linux certified | D6; D7/D8 partial | Per-session mailbox + exec-form `FileChanged`/`asyncRewake`. B2 proved idle wake and same-session continuity on one NixOS host. `>=2.1.306` is the current declared floor; that historical B2 receipt does not certify #147 resume/retire lifecycle on this floor. |
|
|
129
|
-
| **Antigravity / agy** | shipped | D6; D7 partial | Record-backed native-push through LS gRPC `agentapi send-message
|
|
129
|
+
| **Antigravity / agy** | shipped, native push paused (#152) | D6; D7 partial | Record-backed native-push through LS gRPC `agentapi send-message` is paused: on the measured external path the agy LS refused `agentapi` calls for a missing CSRF token (#128; a first-party token path is unmeasured), so dispatch refuses an agy target pre-probe as `native-push-paused`. Birth, statusline and sender identity stay; no mailbox or receiver marker. Admitted before the #82 step 9 contract and not re-evaluated under it, so it is legacy citizen evidence, not a step-9 supported harness: `entwurf_fresh_call` cannot open an agy sibling, and nothing here should be read as claiming visible lifecycle parity. |
|
|
130
130
|
| **Codex CLI app-server citizen** | supported in 0.21.0; caller-seat topology ACCEPTED on Linux at 0.153.4, 2026-09-16 (56 assertions, exit 0), and the caller-DIRECTORY axis ACCEPTED the same day on the same host (#95 lane C, 65 assertions, exit 0; the 2026-09-12 explicit-home acceptance is history — #95 D1 retired that room) | D6 accepted; D7 partial; D8 unproven | Vendor-trusted user-scope `SessionStart` birth plus strict per-request metadata join; no shared-pid sender marker. `thread/loaded/list` probes the operator-owned app-server UDS and one-shot `codex queue` delivers with no retry. The operator owns the app-server and seats it wherever they like; Entwurf creates, moves and supervises none of it. Since #95 lane B a Codex CALLER with no explicit placement opens its sibling beside its OWN pane, located by the `thread-id` its `[tui].terminal_title` writes into the pane title; 0 or 2+ matching panes refuse with no fallback, and the title is a placement input only — never an address, liveness or delivery fact. #95 D1 (2026-09-16) retired the fixed `codex` home that omitted-placement Codex TARGETS used to select, so an omitted seat is the caller's own session for every backend. Exact 0.153.4 source still bounds placing a sibling beside a TUI whose thread nobody named as unsupported; the anchor resolves only the pane showing the caller's own thread. |
|
|
131
131
|
| **Codex embedded TUI** | deferred | D0 partial | At the 2026-09-08 Codex 0.153.4 measurement, standalone mode had no `watchPaths`/`FileChanged`/`asyncRewake` analogue or supported idle receive route. This is dated vendor evidence, not a claim that the current candidate lacks records or fresh. |
|
|
132
132
|
| **Copilot CLI first-party extension** | raw transport probe; superseded by the owned product unit | D7 path observed; D3 control receipt incomplete; D8 unproven | CLI-spawned extension over stdio JSON-RPC; `joinSession()` + documented `fs.watch` → `session.send({mode:"enqueue"})`. Idle wake, exact-marker reply, and completion passed on 2026-08-23 (CLI 1.0.80, L4, one Linux host). Two-process isolation was observed but its decisive B log was not preserved. Kept as the transport receipt the owned receive unit was built on; the shipped unit differs deliberately — it announces the inbox instead of injecting the body. |
|
|
@@ -156,13 +156,24 @@ coalescing bounds, re-arm gaps, and crash/re-poke behavior are measured. A synth
|
|
|
156
156
|
container doctor proves package/oracle shape, not a real Claude wake; a claimed host
|
|
157
157
|
needs the installed strict doctor against a new native session.
|
|
158
158
|
|
|
159
|
-
### Antigravity: native push
|
|
159
|
+
### Antigravity: native push (paused, #152)
|
|
160
160
|
|
|
161
161
|
`PreInvocation` births or reattaches a citizen by native `conversationId` and writes a
|
|
162
|
-
record-backed sender marker.
|
|
163
|
-
|
|
164
|
-
`
|
|
165
|
-
|
|
162
|
+
record-backed sender marker. Native push is paused: on the external send path entwurf
|
|
163
|
+
measured, the agy language server refused the `agentapi` call with `Unauthenticated: missing
|
|
164
|
+
CSRF token` (#128 on agy 1.2.16, again on agy 1.4.6). Whether a first-party own-tool shell
|
|
165
|
+
holds a token entwurf could legitimately present is unmeasured, not ruled out. One fixed constant,
|
|
166
|
+
`NATIVE_PUSH_PAUSED_BACKENDS` in the v2 contract, drives three refusals before any probe:
|
|
167
|
+
`entwurf_v2` rejects an agy target as `native-push-paused` with `observedLiveness: null`, a
|
|
168
|
+
sender's `replyable` is false with a reason naming the pause, and `entwurf_register_native`
|
|
169
|
+
refuses without minting a garden id. Existing records, the backend enum and the record
|
|
170
|
+
reader are unchanged, so agy citizens stay readable and listed. The fresh-cut quiescence
|
|
171
|
+
probe is deliberately not behind the pause: it protects an operator-named archive and keeps
|
|
172
|
+
failing closed. When the pause lifts, the adapter path below applies again: `entwurf_v2`
|
|
173
|
+
probes the live conversation and injects directly through the native adapter, with one
|
|
174
|
+
bounded re-probe retry, and replyability is `record-backed identity ∧ probe-alive`.
|
|
175
|
+
Mailbox state does not exist on this rail, and no rail has resume authority since the
|
|
176
|
+
visible-first cut.
|
|
166
177
|
|
|
167
178
|
The owned bridge, statusline, and hook installers own separate configuration atoms.
|
|
168
179
|
Same-pid concurrent model invocation by multiple conversations is not claimed because
|
|
@@ -587,39 +598,45 @@ same class of capability through the CLI-owned stdio lifecycle. Absence of Claud
|
|
|
587
598
|
`FileChanged` / `asyncRewake` / `watchPaths` therefore means only that Claude's hook
|
|
588
599
|
mechanism cannot be copied — it never proved Copilot had no vendor wake surface.
|
|
589
600
|
|
|
590
|
-
### Remote
|
|
601
|
+
### Remote pairs (#107, #149): declared pairs across hosts
|
|
591
602
|
|
|
592
|
-
This is an opt-in route between
|
|
603
|
+
This is an opt-in route between existing citizens on two hosts. Why it keeps these boundaries, and which questions it leaves open, is [remote-sibling-principles](./docs/remote-sibling-principles.md); this section owns the mechanism. It is off unless the operator places a file on each host. The operator owns the connection between the hosts (a private network such as Tailscale is recommended, not required) and owns SSH authentication and authorization. Entwurf installs, configures, discovers and retries none of it. It adds no relay, listener, queue or registry. One SSH process runs per send and then exits.
|
|
593
604
|
|
|
594
|
-
- **Declaration.** Each host has at most one `entwurf-remote-pair.json`, beside its meta-record store (`remotePairOptInPath()`: the pi agent dir, unless `ENTWURF_META_SESSIONS_DIR` moves the store and the file with it). It
|
|
595
|
-
- `
|
|
605
|
+
- **Declaration (#149).** Each host has at most one `entwurf-remote-pair.json`, beside its meta-record store (`remotePairOptInPath()`: the pi agent dir, unless `ENTWURF_META_SESSIONS_DIR` moves the store and the file with it). It is exactly `{"version": 2, "routes": [...]}` with a non-empty `routes` array. Each route is one exact pair with exactly four keys:
|
|
606
|
+
- `localGardenId`: the citizen on this host.
|
|
607
|
+
- `remoteGardenId`: the citizen on the other host.
|
|
596
608
|
- `ssh`: an existing SSH alias whose characters need no quoting.
|
|
597
|
-
- `localGardenId`.
|
|
598
609
|
- `remotePairFile`: the other host's canonical absolute path to its own file.
|
|
599
610
|
|
|
600
|
-
One
|
|
611
|
+
One route serves both directions of its pair. An absent file means off. A symlink, non-regular, unreadable or invalid file is refused, and so is an empty `routes`. The versionless single-pair object of 0.41 and earlier is refused by name with the manual rewrite (`{"version":2,"routes":[<that object>]}`); there is no legacy reader. The file is read at every send and receive and never cached. The sending bridge and the SSH-spawned receive entry must resolve the same root; an override set in only one of those environments moves the file.
|
|
612
|
+
|
|
613
|
+
The set of routes is checked as a whole, never by position:
|
|
614
|
+
- Allowed: one local citizen paired with several remotes, one remote paired with several locals, and several routes over one alias.
|
|
615
|
+
- Refused: the same exact pair twice; a garden id that is local in one route and remote in another (a route to itself included); one remote garden id with two different aliases or files; one alias with two different `remotePairFile` values, because the receive entry behind that alias accepts only its one canonical file.
|
|
616
|
+
|
|
617
|
+
A broken file is refused at every `entwurf_v2` call of that bridge, local targets included, because the bridge reads the file before it can tell a remote target from a local one. That is not a silent fallback, and it is a cost to keep in mind when editing the file.
|
|
601
618
|
- **Send.** The sending host's `entwurf_v2` reads the file and then decides:
|
|
602
|
-
- A target
|
|
619
|
+
- A target that no route names as remote takes the unchanged local path.
|
|
603
620
|
- A declared target that also has a local trace on this host (a record in any state, or a control socket) is refused as `remote-route-local-conflict`, with zero SSH.
|
|
604
|
-
-
|
|
605
|
-
-
|
|
606
|
-
- **Receive.** The entry accepts only when `--pair` equals its own canonical file path. Canonical means the normalized expected path, not realpath equivalence: a spelling through a symlinked home alias is refused. That file is the entry's whole scope. It
|
|
621
|
+
- Otherwise the route is the one whose two ends are this sender and this target. If there is none, the call is refused as `remote-sender-not-declared`, with zero SSH and no local fallback. That also covers an anonymous sender.
|
|
622
|
+
- That route spawns one `ssh -o BatchMode=yes -o ConnectTimeout=10 <alias> entwurf remote-pair-receive --pair <remotePairFile>`, with no shell and the claim on stdin.
|
|
623
|
+
- **Receive.** The entry accepts only when `--pair` equals its own canonical file path. Canonical means the normalized expected path, not realpath equivalence: a spelling through a symlinked home alias is refused. That file is the entry's whole scope. It looks for the one route whose remote end is the claim's `from` and whose local end is the claim's `target`, comparing both ends in that same route. A `from` that no route declares is refused as `remote-pair-from-mismatch`. A declared `from` addressing anything but its own declared local, including another route's local, is refused as `remote-pair-target-mismatch`. Both refusals come before any mailbox mutation. A match goes to that host's existing local `entwurf_v2` dispatch, so every deliverability rule of the target's rail applies there; the measured pairs used the self-fetch mailbox.
|
|
607
624
|
- **Outcomes.** The send ends in one of three outcomes:
|
|
608
625
|
- the other host's receipt, quoted verbatim (its paths are on the other host). This is either an acceptance or an error the other host reported while processing (`remote-reported-error`, exit 4). That error can follow a write, for example an enqueue whose later step threw and is reported `retry-safe: false`. The sender's header therefore claims neither non-delivery nor safety to resend; the quoted text is the fact;
|
|
609
626
|
- the other host's named refusal, made before any mutation: a pair mismatch (exit 3) or invalid input (exit 2);
|
|
610
627
|
- `remote-indeterminate`.
|
|
611
628
|
|
|
612
629
|
A result is decided only when the exit code and the JSON kind agree. There is no retry. An unanswered message stays unconfirmed. Acceptance is not a read or a reply. Neither side learns whether its last message was read unless the other side answers, and there is no third ACK.
|
|
613
|
-
- **Display.** The received envelope names the remote origin and route, marks the sender's cwd as a sender-host path, and carries `replyable=false`, with "reply route declared, not probed" stated separately. Display is proven only for the self-fetch mailbox body; control-socket and native-push rendering are not proven. `entwurf_callback` stays local.
|
|
614
|
-
- **Trust.** The model is same-operator SSH account trust. The receiving host checks the declared
|
|
630
|
+
- **Display.** The received envelope names the remote origin and the matched route's alias, marks the sender's cwd as a sender-host path, and carries `replyable=false`, with "reply route declared, not probed" stated separately. Display is proven only for the self-fetch mailbox body; control-socket and native-push rendering are not proven. `entwurf_callback` stays local.
|
|
631
|
+
- **Trust.** The model is same-operator SSH account trust. The receiving host checks the declared pairs. It does not cryptographically authenticate a citizen, and the same account could claim any declared id. With several routes, everything allowed to log in to that account is inside this boundary. That includes the keys of every other host. Separating them is the operator's SSH policy; Entwurf adds no per-route or per-key binding.
|
|
615
632
|
- **Visibility.** For the measured self-fetch pair, delivery requires backing records, live owners and armed watches, so an OFF citizen of that rail is undeliverable. Any other target keeps its own rail-specific local admission. The remote route has no launch, fresh or resume path. The code does not check that the target's screen is open: an owner pid, a TTY or a garden-id label is not visibility. Live use addresses only siblings the user has open, and delivery to an active pi-durable can start a turn at once.
|
|
616
633
|
- **SSH side effects.** The transport adds exactly two options, `BatchMode=yes` and `ConnectTimeout=10`. Host-key checking, known-hosts writes and every other SSH policy remain the operator's, as configured. Their side effects on a real exchange were not measured.
|
|
617
634
|
|
|
618
635
|
Evidence:
|
|
619
636
|
|
|
620
|
-
- T1 (executor-in) and T2 (surface-in) are deterministic. T2 drives the real bridge over MCP stdio, and a stand-in `ssh` on PATH runs the other host's real entry under a separate HOME. An installed-tarball gate runs the T2 cells against installed bytes.
|
|
637
|
+
- T1 (executor-in) and T2 (surface-in) are deterministic. T2 drives the real bridge over MCP stdio, and a stand-in `ssh` on PATH runs the other host's real entry under a separate HOME. Since #149 it also runs three hosts, where one host's one file carries three routes: fan-out, a shared remote, both directions, an undeclared sender with zero SSH, and a cross claim refused on the receiving host. An installed-tarball gate runs the T2 cells against installed bytes.
|
|
621
638
|
- One real exchange is recorded in [#107](https://github.com/junghan0611/entwurf/issues/107#issuecomment-6278665895), at branch commit `e632294` on 2026-10-20. A nonce went B→A→B between two already-open pi-durable siblings on two Linux hosts. That is L4: one pair, one run.
|
|
622
|
-
- Unmeasured: an OFF target, negative paths over real SSH, other rails, more than one pair, and non-Linux hosts.
|
|
639
|
+
- Unmeasured: an OFF target, negative paths over real SSH, other rails, more than one pair over real SSH (the #149 schema has no T3 yet, so the #107 exchange does not carry over to it), a sender and a receiver on different releases (the claim, argv, exit codes and receipt are unchanged, so that interoperation is inferred, not measured), and non-Linux hosts.
|
|
623
640
|
|
|
624
641
|
## Recording a new claim
|
|
625
642
|
|
package/README.md
CHANGED
|
@@ -27,11 +27,12 @@ Git branches, worktrees, file isolation and internal delegation remain their con
|
|
|
27
27
|
|---|---|---|---|
|
|
28
28
|
| **Open** a new sibling | `entwurf_fresh_call` | Herdr: a new unfocused tab in the caller's workspace. tmux: a new window in the caller's session, or an existing session named by `placement.tmuxSession`; without an explicit tmux placement, a Codex caller opens beside its own TUI pane | Herdr: pi, Claude Code. tmux: pi, pi-durable, Claude Code, Copilot, OMP, Codex |
|
|
29
29
|
| **Reopen** a dormant sibling | `entwurf_resume_call` | A visible tmux window under the same garden id, with its record selecting the native session (Pi: identity-checked recorded transcript supplies launch metadata; Claude Code: `claude --resume <session id>` at the record cwd); the verb submits no prompt | pi, Claude Code |
|
|
30
|
-
| **Retire** one sibling the human operator explicitly requested
|
|
31
|
-
| **Message** an existing sibling | `entwurf_v2` | Its own rail: control socket, self-fetch mailbox or native push; delivery starts no process | pi, pi-durable, Claude Code, Copilot, OMP, Antigravity,
|
|
30
|
+
| **Retire** one sibling the human operator explicitly requested | `entwurf_retire_call` | One guarded attempt on that target: a Pi shutdown request or Claude Code fd-pinned process termination. Refused/unknown outcomes remain possible; the record stays for guarded same-id return. Not a grant, a roster cleanup or an automatic retry | pi, Claude Code |
|
|
31
|
+
| **Message** an existing sibling | `entwurf_v2` | Its own rail: control socket, self-fetch mailbox or native push; delivery starts no process | pi, pi-durable, Claude Code, Copilot, OMP, Codex (Antigravity native push is paused, #152) |
|
|
32
32
|
|
|
33
|
-
Antigravity is
|
|
34
|
-
launches one.
|
|
33
|
+
Antigravity is a legacy citizen: an existing conversation registers itself, and Entwurf never
|
|
34
|
+
launches one. Its native push is paused (#152): on the measured external path the agy language server
|
|
35
|
+
refused Entwurf's calls for a missing CSRF token, so a message to it is refused as `native-push-paused`. A launch receipt is neither the new sibling's address nor proof that it
|
|
35
36
|
processed its task; the sibling's own callback envelope supplies its garden id.
|
|
36
37
|
|
|
37
38
|
The package holds the v2 core, native-harness bridges, a pi adapter and an ACP
|
|
@@ -54,11 +55,15 @@ ownership of the harness.
|
|
|
54
55
|
|
|
55
56
|
Prerequisites: **Node >=24.0.0**, Python 3 for `setup`, and any harnesses you choose
|
|
56
57
|
to use, already installed and authenticated. Pi is optional-by-presence, supported
|
|
57
|
-
range `>=1.
|
|
58
|
+
range `>=1.4.0 <1.5`; Claude Code supported floor `>=2.1.306` is required for its
|
|
58
59
|
managed exec-hook lifecycle. pnpm is needed only for source-checkout setup.
|
|
59
60
|
|
|
61
|
+
**Release state.** The Pi range above belongs to the published 0.41.1, which ships the Pi
|
|
62
|
+
1.4.0 SDK set. The previous `0.41.0` ships the Pi 1.3.0 set and requires Pi 1.3.x; the README
|
|
63
|
+
inside that tarball and the Herdr snapshot `v2026.10.22-herdr.0.41.0` are its contract.
|
|
64
|
+
|
|
60
65
|
```bash
|
|
61
|
-
npm install -g @junghanacs/entwurf
|
|
66
|
+
npm install -g @junghanacs/entwurf@0.41.1
|
|
62
67
|
entwurf setup /path/to/your-project
|
|
63
68
|
entwurf check-bridge
|
|
64
69
|
```
|
|
@@ -76,27 +81,18 @@ consent: [the operator steps](./docs/setup-clean-host.md#three-operator-decision
|
|
|
76
81
|
Plain MCP registration alone does not make a garden citizen.
|
|
77
82
|
|
|
78
83
|
**Platform bounds:** Linux is the certified harness-rail axis. macOS Entwurf-only
|
|
79
|
-
installation is **CERTIFIED (CI)
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
0.39.0 (published to npm); 0.38.1 and earlier carry no Windows addon. On a GitHub Actions
|
|
87
|
-
`windows-2025` x64 runner with Node 24.18.1 and Pi 1.3.0, a CI-built artifact was installed,
|
|
88
|
-
wired with `pi install`, birthed two Pi citizens and
|
|
89
|
-
delivered one garden-id `entwurf_v2` message between them, with an offline scripted model
|
|
90
|
-
and no vendor turn. Every other native-Windows surface (other harnesses, `setup` and the
|
|
91
|
-
`.sh` bins, fresh/resume, mux/Herdr/ACP, physical machines) is **not declared**; the
|
|
92
|
-
measured scope and its named gaps are in [BASELINE](./BASELINE.md#release-host-baseline).
|
|
93
|
-
The package has no npm `os` restriction; that is not certification of every platform's
|
|
94
|
-
harness rails.
|
|
84
|
+
installation is **CERTIFIED (CI)**; native harness rails, ACP and mux remain
|
|
85
|
+
**NOT CERTIFIED — pending physical host**, so detected wiring can still leave setup non-green.
|
|
86
|
+
WSL2 and native Windows have **experimental CI previews**, not general or physical-host support.
|
|
87
|
+
The native-Windows preview covers Pi citizens and offline scripted delivery, not vendor turns,
|
|
88
|
+
other harnesses, shell entrypoints, fresh/resume, mux, Herdr or ACP.
|
|
89
|
+
[BASELINE](./BASELINE.md#release-host-baseline) owns the version-specific receipts and gaps;
|
|
90
|
+
an npm package without an `os` restriction is not an all-platform certification.
|
|
95
91
|
|
|
96
92
|
### Native durable (pi-durable)
|
|
97
93
|
|
|
98
|
-
**The package supplies Entwurf's owned app/TUI carrier and nine exact Pi 1.
|
|
99
|
-
members
|
|
94
|
+
**The package supplies Entwurf's owned app/TUI carrier and nine exact Pi 1.4.0 SDK
|
|
95
|
+
members** (the previous 0.41.0 supplies the 1.3.0 set). No separate runtime checkout, patch, build or ordinary Pi CLI is needed.
|
|
100
96
|
The launch verifies carrier integrity and every reachable SDK edge's name/version;
|
|
101
97
|
incomplete, drifted or mixed supply refuses by name.
|
|
102
98
|
|
|
@@ -106,7 +102,7 @@ entwurf pi-durable --continue
|
|
|
106
102
|
entwurf pi-durable --resume
|
|
107
103
|
```
|
|
108
104
|
|
|
109
|
-
`--continue` opens this directory's
|
|
105
|
+
`--continue` opens this directory's latest-created durable session. `--resume` lists its
|
|
110
106
|
sessions, most recently written first, by creation time, garden id and last write, and
|
|
111
107
|
reopens the one you choose as it was saved; a cancelled choice opens nothing.
|
|
112
108
|
|
|
@@ -117,11 +113,9 @@ SQLite, transcripts and recovery remain the upstream SDK's:
|
|
|
117
113
|
|
|
118
114
|
Optional `--native-module <absolute file>` initializes trusted ESM before runtime/TUI
|
|
119
115
|
imports, then installs its default native Extension after the contact.
|
|
120
|
-
`--native-module-dir <absolute directory>`
|
|
121
|
-
|
|
122
|
-
background-task badge on screen at any terminal size. No automatic module discovery or
|
|
116
|
+
`--native-module-dir <absolute directory>` loads that named directory's top-level
|
|
117
|
+
`*.extension.mjs` files in byte-wise order instead. No automatic module discovery or
|
|
123
118
|
classic Pi API shim; guards are not a sandbox or rollback.
|
|
124
|
-
Ordinary Pi's known `peerDependencies` warning reflects the separate native SDK supply.
|
|
125
119
|
[Native setup](./docs/setup-clean-host.md#2b-optional-native-durable-pi-durable)
|
|
126
120
|
owns flags, refusals and the extension contract.
|
|
127
121
|
|
|
@@ -131,9 +125,14 @@ owns flags, refusals and the extension contract.
|
|
|
131
125
|
herdr plugin install junghan0611/entwurf/plugins/herdr --yes
|
|
132
126
|
```
|
|
133
127
|
|
|
128
|
+
The [Herdr guide](./plugins/herdr/README.md#from-a-raw-linux-box-in-nine-steps) says which
|
|
129
|
+
runtime each ref installs.
|
|
130
|
+
|
|
134
131
|
This separate path activates Herdr-integrated **pi and Claude Code** with an
|
|
135
132
|
independently locked runtime and an observation pane; it puts no Entwurf bins on PATH.
|
|
136
|
-
An Entwurf release does not automatically re-pin that runtime.
|
|
133
|
+
An Entwurf release does not automatically re-pin that runtime: while 0.41.2 is an unpublished
|
|
134
|
+
candidate, main's lock rides the `herdr-checkout` verification carrier, and the published 0.41.1
|
|
135
|
+
runtime stays pinned by the Herdr tag `v2026.10.23-herdr.0.41.1`.
|
|
137
136
|
[Herdr guide](./plugins/herdr/README.md) owns install, use, evidence and removal;
|
|
138
137
|
the plugin does not own delivery or the Herdr launch rail.
|
|
139
138
|
|
|
@@ -169,7 +168,7 @@ In that **harness session**, ask the agent to:
|
|
|
169
168
|
```
|
|
170
169
|
|
|
171
170
|
These are tools, not shell subcommands. Pi calls
|
|
172
|
-
`mcp__entwurf_bridge__<verb>` through Pi's built-in MCP. The
|
|
171
|
+
`mcp__entwurf_bridge__<verb>` through Pi's built-in MCP. The bridge exposes **nine**
|
|
173
172
|
verbs; a pi citizen has **seven active + two hidden** (`entwurf_inbox_read` and
|
|
174
173
|
`entwurf_register_native`), since it has no mailbox and its own extension births it.
|
|
175
174
|
Those counts describe the source; a running host offers what its already-loaded bridge
|
|
@@ -194,58 +193,61 @@ pi / pi-durable / Claude Code / Copilot / OMP / Antigravity / Codex
|
|
|
194
193
|
→ control-socket | self-fetch mailbox | native-push
|
|
195
194
|
```
|
|
196
195
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
196
|
+
**Delivery starts no process.** A live pi receives over its record-addressed control
|
|
197
|
+
address, an armed self-fetch citizen (Claude Code, Copilot, OMP, pi-durable) through its
|
|
198
|
+
mailbox, a probe-alive native-push citizen (Codex; Antigravity is paused, #152) through its own
|
|
199
|
+
conversation or thread. Anything else is refused honestly, never launched into. The receipt
|
|
200
|
+
is a receiver acceptance boundary, a durable mailbox file or an injection, never completion;
|
|
201
|
+
`wants_reply` is etiquette, not waiting. [DELIVERY](./DELIVERY.md#control-socket-acceptance-boundaries-120)
|
|
202
|
+
owns what each boundary means and each rail's admission.
|
|
203
|
+
|
|
204
|
+
The lifecycle verbs in the [table above](#entwurf) return their own receipts, never a delivery
|
|
205
|
+
result: fresh a launch receipt (the sibling's callback envelope supplies its garden id), resume
|
|
206
|
+
separate LAUNCH and OBSERVATION receipts, retire separate action, record and lock results.
|
|
207
|
+
**Retirement is never autonomous:** it needs the human operator's explicit target and action, and
|
|
208
|
+
no role, caller identity or tool annotation grants it ([DELIVERY](./DELIVERY.md#candidate-lifecycle-commands-are-not-delivery-boundaries-147)).
|
|
209
|
+
|
|
210
|
+
**Mux launches; it never delivers.** Fresh opens in the caller's Herdr workspace or tmux session;
|
|
211
|
+
`placement.tmuxSession` names another existing tmux session and is refused inside Herdr. The model
|
|
212
|
+
is required and `cwd` is a literal absolute directory. Missing or ambiguous context refuses with no
|
|
213
|
+
fallback. Exact argv, identity checks and limits: [tmux launch](./docs/mux-launch-rail.md),
|
|
214
|
+
[Herdr launch](./docs/herdr-launch-rail.md).
|
|
215
|
+
|
|
216
|
+
### Remote siblings — declared pairs across hosts
|
|
217
|
+
|
|
218
|
+
Opt-in. The v2 file below is in main and the 0.41.2 candidate; the published 0.41.1 reads the
|
|
219
|
+
earlier single-pair file. On each host, write `~/.pi/agent/entwurf-remote-pair.json` (mode `0600`; a
|
|
220
|
+
moved agent dir or meta store moves it, see [setup §2c](./docs/setup-clean-host.md#2c-optional-remote-pair-between-two-hosts-107)). The ids
|
|
221
|
+
below are fictional; use your own records' garden ids:
|
|
222
|
+
|
|
223
|
+
```json
|
|
224
|
+
{
|
|
225
|
+
"version": 2,
|
|
226
|
+
"routes": [
|
|
227
|
+
{
|
|
228
|
+
"localGardenId": "20260101T090000-a1b2c3",
|
|
229
|
+
"remoteGardenId": "20260101T091500-d4e5f6",
|
|
230
|
+
"ssh": "host-b",
|
|
231
|
+
"remotePairFile": "/home/me/.pi/agent/entwurf-remote-pair.json"
|
|
232
|
+
}
|
|
233
|
+
]
|
|
234
|
+
}
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
The other host carries the mirror route: the two garden ids swapped, its own SSH alias for this
|
|
238
|
+
host, and this file's canonical absolute path. Then send with the ordinary `entwurf_v2` to the
|
|
239
|
+
remote garden id: one SSH attempt over your own account, checked and dispatched by the host that
|
|
240
|
+
holds the record. There is no peer discovery, wake or retry. Why: [remote sibling principles](./docs/remote-sibling-principles.md);
|
|
241
|
+
contract: [DELIVERY](./DELIVERY.md#remote-pairs-107-149-declared-pairs-across-hosts); steps and the
|
|
242
|
+
hand conversion from the earlier file: [setup §2c](./docs/setup-clean-host.md#2c-optional-remote-pair-between-two-hosts-107).
|
|
203
243
|
|
|
204
|
-
|
|
244
|
+
### Team bookmarks — `team.json`
|
|
205
245
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
the per-target lock; the other rails use their own deliverability/probe evidence.
|
|
212
|
-
|
|
213
|
-
A control-socket `sent` means a turn was triggered, not that the model read the
|
|
214
|
-
text. `queued-steer` and `queued-follow-up` are volatile process queues, with no
|
|
215
|
-
FIFO between them; abort drops them. `accepted-unknown-boundary` is acceptance
|
|
216
|
-
we cannot classify, never a reason to retry. A mailbox enqueue is a durable file
|
|
217
|
-
receipt. `wants_reply` is etiquette, not waiting, polling or ownership; replyability
|
|
218
|
-
is a live rail fact, not merely a consequence of trusted identity.
|
|
219
|
-
[DELIVERY](./DELIVERY.md#control-socket-acceptance-boundaries-120) owns these distinctions.
|
|
220
|
-
|
|
221
|
-
**One declared remote pair (opt-in, #107).** The operator places an `entwurf-remote-pair.json` on each of two hosts. Then `entwurf_v2` to the declared remote garden id runs the other host's `entwurf remote-pair-receive` once over the operator's own SSH, and that host checks the pair and dispatches locally. The trust is the same operator's SSH account. It makes one attempt with no retry, and it uses no relay or listener. [DELIVERY](./DELIVERY.md#remote-pair-107-one-declared-pair-across-two-hosts) owns the contract; [setup](./docs/setup-clean-host.md#2c-optional-remote-pair-between-two-hosts-107) covers the operator steps.
|
|
222
|
-
|
|
223
|
-
**Mux launches; it never delivers.** Inside Herdr, fresh opens a new unfocused tab
|
|
224
|
-
in the caller's workspace, pi/Claude Code only, with no tmux fallback. Outside,
|
|
225
|
-
it opens pi/pi-durable/Claude Code/Copilot/OMP/Codex visibly on the caller's own tmux
|
|
226
|
-
server. pi-durable runs the installed carrier on its exact SDK set described above and
|
|
227
|
-
refuses by name before any window when that verdict is not green; Herdr does not fresh-open it. The model is required; optional `cwd` is a literal
|
|
228
|
-
absolute directory, omitted or empty means the caller's cwd. Optional `placement.tmuxSession` names an existing
|
|
229
|
-
tmux session (not permitted inside Herdr), never a new session or a cwd lookup.
|
|
230
|
-
Omitted tmux placement follows the caller: Codex beside its own TUI pane matched
|
|
231
|
-
by thread-id, everyone else in their own session. Missing/ambiguous context refuses.
|
|
232
|
-
A launch receipt is neither the callback address nor proof of task processing.
|
|
233
|
-
Resume is pi or Claude Code, tmux-only, same id, with the record as the sole address authority.
|
|
234
|
-
For Pi, the record selects the absolute transcript and its native id must match; the transcript's
|
|
235
|
-
session-header cwd and first `model_change` provider/model feed the launch resolver, without a
|
|
236
|
-
record/host cwd fallback. Claude Code uses `claude --resume <record session id>` at the record cwd,
|
|
237
|
-
without restoring model or permission flags;
|
|
238
|
-
a live, unprovably dormant, unbound or other-backend target refuses, and an unobserved window
|
|
239
|
-
stays visible without retry. Claude Code's [V3 arm binding](./docs/adding-a-harness.md#arm-binding-within-v3)
|
|
240
|
-
is a record–receiver witness, not a new identity or an occupancy/permission token.
|
|
241
|
-
Claude Code dormancy is read only from attaches the Entwurf hook committed: a process that
|
|
242
|
-
holds the session without one (hooks disabled, a lock timeout or store failure at its start)
|
|
243
|
-
is invisible to that check.
|
|
244
|
-
Claude Code same-id resume is new in 0.41.0. One dated test-owned
|
|
245
|
-
[Native round trip](./BASELINE.md#147-native-round-trip-2026-10-22-n1) was measured; its
|
|
246
|
-
release acceptance remains separate from the shipped Pi resume history.
|
|
247
|
-
Exact boundaries: [tmux launch](https://github.com/junghan0611/entwurf/blob/main/docs/mux-launch-rail.md)
|
|
248
|
-
and [Herdr launch](https://github.com/junghan0611/entwurf/blob/main/docs/herdr-launch-rail.md).
|
|
246
|
+
A person's bookmark of the siblings one project works with. Copy [`team.json.example`](./team.json.example)
|
|
247
|
+
to an ignored `team.json` at the project root and fill names, backend, garden id, native session id
|
|
248
|
+
and cwd from Entwurf's record facts; read liveness from `entwurf_peers` when you dispatch. Entwurf
|
|
249
|
+
does not load it as runtime configuration, and it is not configuration for identity, routes, roles, permissions or lifecycle. The
|
|
250
|
+
example's own notes say the rest.
|
|
249
251
|
|
|
250
252
|
## Concepts
|
|
251
253
|
|
package/VERIFY.md
CHANGED
|
@@ -6,8 +6,8 @@ and the manual judgements a gate cannot make.
|
|
|
6
6
|
|
|
7
7
|
> **Current surface.** `entwurf-bridge` exposes `entwurf_v2`, `entwurf_peers`,
|
|
8
8
|
> `entwurf_fresh_call`, `entwurf_resume_call`, `entwurf_retire_call` (0.41.0; operator-requested target only), `entwurf_callback`, `entwurf_self`, `entwurf_inbox_read`, and `entwurf_register_native` — nine; a pi citizen offers seven with two hidden, the pi-durable contact eight. The ACP
|
|
9
|
-
> backends are Claude and Snowflake Cortex Code. Antigravity is a separate
|
|
10
|
-
> native
|
|
9
|
+
> backends are Claude and Snowflake Cortex Code. Antigravity is a separate legacy citizen
|
|
10
|
+
> lane whose native push is paused (#152). Codex is a native-push citizen supported in 0.21.0, whose birth hook is
|
|
11
11
|
> operator-owned and gated on one vendor trust receipt, with strict request-scoped identity and
|
|
12
12
|
> visible fresh; it remains outside ACP and has no resume.
|
|
13
13
|
> This banner names the surface, not an acceptance. The 48-assertion / MUST 24/0/0 / 475/475 numbers
|
|
@@ -115,7 +115,7 @@ that moved out of `scripts/` and into a file beside its subject would otherwise
|
|
|
115
115
|
>
|
|
116
116
|
> 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`.
|
|
117
117
|
>
|
|
118
|
-
> **The integrated mux lifecycle IS a release-gate MUST.** `LIVE=1 ./run.sh smoke-mux-lifecycle-live` enters through a real MCP `tools/call` on the shipped bridge launcher and follows one citizen the whole way: fresh-call launch receipt, the sibling's own nonce callback whose SENDER ENVELOPE is the address, an `entwurf_v2` control-socket send that lands in the sibling's own transcript, `entwurf_resume_call` REFUSED while that citizen is live, a stable-handle close proving pane and socket gone with the record preserved, delivery to the dormant citizen refused honestly, then the public `entwurf_resume_call` — whose LAUNCH and OBSERVATION receipts stay apart — and finally an `entwurf_v2` send the resumed session answers with a fact it was told BEFORE the window closed. That last step is the point: a socket answering under the same garden id proves a process stood up at that address, and only the recall proves the same conversation came back. Both pi provider shapes are driven to it (native `openai-codex/gpt-6.1-sol:low` and recorded-ACP `entwurf/claude-sonnet-5-5`, the only shape that re-injects a bridge extension), and the Claude Code cell, whose sibling is still live at that point, has its resume refused as `target-live` with no window opened and no lock residue (the per-gid coordination lock is released). Claude Code's dormant same-id resume (#147 L2) is certified deterministically; its LIVE dormant→resume leg is not in this MUST yet. One test-owned retire→resume round trip outside this gate is recorded as dated n=1 evidence in [BASELINE](./BASELINE.md#147-native-round-trip-2026-10-22-n1); it is not this MUST.
|
|
118
|
+
> **The integrated mux lifecycle IS a release-gate MUST.** `LIVE=1 ./run.sh smoke-mux-lifecycle-live` enters through a real MCP `tools/call` on the shipped bridge launcher and follows one citizen the whole way. The native pi launch names `placement.tmuxSession` (#108): the harness asks tmux itself that the pane sits in a caller-distinct seat session on the same private server, that the caller's windows are unchanged, and that the close later removes only that window and leaves the seat's anchor standing. The steps are: fresh-call launch receipt, the sibling's own nonce callback whose SENDER ENVELOPE is the address, an `entwurf_v2` control-socket send that lands in the sibling's own transcript, `entwurf_resume_call` REFUSED while that citizen is live, a stable-handle close proving pane and socket gone with the record preserved, delivery to the dormant citizen refused honestly, then the public `entwurf_resume_call` — whose LAUNCH and OBSERVATION receipts stay apart — and finally an `entwurf_v2` send the resumed session answers with a fact it was told BEFORE the window closed. That last step is the point: a socket answering under the same garden id proves a process stood up at that address, and only the recall proves the same conversation came back. Both pi provider shapes are driven to it (native `openai-codex/gpt-6.1-sol:low` and recorded-ACP `entwurf/claude-sonnet-5-5`, the only shape that re-injects a bridge extension), and the Claude Code cell, whose sibling is still live at that point, has its resume refused as `target-live` with no window opened and no lock residue (the per-gid coordination lock is released). Claude Code's dormant same-id resume (#147 L2) is certified deterministically; its LIVE dormant→resume leg is not in this MUST yet. One test-owned retire→resume round trip outside this gate is recorded as dated n=1 evidence in [BASELINE](./BASELINE.md#147-native-round-trip-2026-10-22-n1); it is not this MUST.
|
|
119
119
|
>
|
|
120
120
|
> One cell cannot be fully fenced, and that is recorded rather than glossed: the recorded-ACP pi cell runs under the operator's REAL home, because its provider spawns `claude` as a child that authenticates from there — measured, a fenced HOME kills that turn with `Authentication required` before the model is ever reached, and `CLAUDE_CONFIG_DIR` alone does not fix it. That is the same trade `smoke-acp-bundled-mcp-live` already makes. While that cell runs, its citizen's control socket and per-gid lock exist under the operator's own `~/.pi/entwurf-control` and `~/.pi/entwurf-v2-locks`; its meta store stays fixture, so no garden record is minted outside the fixture, and the run ends by proving all six real roots hold the entry sets they started with, with no fixture garden id and no leftover lock.
|
|
121
121
|
>
|
|
@@ -125,7 +125,7 @@ that moved out of `scripts/` and into a file beside its subject would otherwise
|
|
|
125
125
|
|
|
126
126
|
> **A visible-fresh receipt outside the MUST tier is no longer the general rule — it is Copilot's preserved exception.** #87 measured what that wording permitted: because clause 7 sat outside every aggregate, a harness could be admitted as a full D6 citizen that `entwurf_fresh_call` cannot open, carry an honest "not a supported harness" sentence in the delivery matrix, and still pass the whole floor toward a cut. From OMP onward, the FIRST release of a harness admitted under the #82 contract owes its clause 7 receipt as a release-gate MUST step (`smoke-omp-fresh-live`), and the deterministic half of the same rule (`check-harness-admission-parity`, inside `check:full`) makes a citizen backend missing from `FRESH_CALL_BACKENDS` a red floor rather than a documented one. Forward-only by decision: Copilot's row is not reopened. The contract lives in `docs/adding-a-harness.md` step 9, "The release stop"; this note exists so the cost discipline above is not read as permission.
|
|
127
127
|
>
|
|
128
|
-
>
|
|
128
|
+
> agy native push is paused (#152). On the external send path entwurf measured, the agy language server refused the `agentapi` call with `Unauthenticated: missing CSRF token` (#128 on agy 1.2.16, again on agy 1.4.6). Whether a first-party own-tool shell holds a token entwurf could legitimately present is unmeasured; this pause does not claim it is impossible. So `entwurf_v2` refuses an agy target before any probe as `native-push-paused` (`observedLiveness: null`), an agy sender's `replyable` is false by name, and `entwurf_register_native` refuses before probing or minting a garden id. Existing agy records stay readable. The pause itself is proven by the deterministic `[QK:AGY-NATIVE-PUSH-PAUSED]` cell, which needs no agy conversation. Separately, an agy host's retained legacy identity surfaces can be checked by three fail-loud doctors plus a fresh-conversation identity check (birth, statusline, `entwurf_self`) after package install. While the pause holds, that check is an optional diagnostic run on request, not a cut requirement; a green one is neither send evidence nor a certification that the installed agy host works or can receive. `LIVE=1 AGY_CONVERSATION_ID=<id> ./run.sh smoke-agy-native-push-live` exits 2 with `[entwurf:paused]` before it reads the conversation id, and that is not a PASS. The deterministic paused refusal (`[QK:AGY-NATIVE-PUSH-PAUSED]`) and the install/sender gates are inside `pnpm run check:full`; do not misreport the aggregate gate as live agy evidence. **Cost fence:** the pause proof (criterion 7) needs no agy conversation, model or conversation id, and none is opened for `smoke-agy-native-push-live`. The optional retained-identity diagnostic does need one fresh agy conversation after package install; when it is asked for, open it on `gemini-3.6-flash` (free account) — never a Pro tier; entwurf never selects the model, and no assertion reads it (model display is not part of the agy contract). Lifting the pause would be a separate future change with its own positive acceptance; nothing here certifies one.
|
|
129
129
|
>
|
|
130
130
|
> Aggregate release-gate does not own a loaded Codex thread. Codex native-push remains an
|
|
131
131
|
> on-demand host axis. The first-admission acceptance passed on 2026-09-12 under the fixed `codex`
|
|
@@ -211,7 +211,7 @@ Do not collapse source, package, fixture, and native-host evidence into one “g
|
|
|
211
211
|
|
|
212
212
|
**Remote pair (#107).** Each layer of evidence lives in its own place:
|
|
213
213
|
|
|
214
|
-
- **Deterministic.** The cells sit beside the behavior in `pi-extensions/lib/remote-pair-*.test.ts` and are found by `check-tests-beside-behavior`. T1 is executor-in. T2 is surface-in: the real bridge over MCP stdio, with a stand-in `ssh` on PATH that runs the other host's real `run.sh` under a separate HOME. Both are offline.
|
|
214
|
+
- **Deterministic.** The cells sit beside the behavior in `pi-extensions/lib/remote-pair-*.test.ts` and are found by `check-tests-beside-behavior`. Their mutants run through the narrow execution coordinate `check-remote-pair-beside` (rule 3, #149). T1 is executor-in. T2 is surface-in: the real bridge over MCP stdio, with a stand-in `ssh` on PATH that runs the other host's real `run.sh` under a separate HOME. It covers two hosts, and since #149 also three hosts whose one file carries several routes. Both are offline.
|
|
215
215
|
- **Installed bytes.** `./run.sh check-remote-pair-installed --tgz <abs>` is checkout-only and takes a caller-supplied candidate tarball. It installs a sandboxed copy, checks the compiled surface, and runs the T2 cells against the installed subject. It also audits that every module those processes load resolves inside the consumer prefix, with a checkout negative control. Its audit primitive is mutation-qualified as `check-remote-pair-installed-audit` inside `check:vitest`.
|
|
216
216
|
- **Real two-host exchange (T3).** This is opt-in operator LIVE evidence, recorded in [BASELINE](./BASELINE.md) and #107. It is not a release-gate MUST and not a fixture that needs a second machine online. A T1, T2 or installed green does not stand for it, and its record belongs to the commit it ran on; a later SHA does not inherit it. Re-run T3 when any of these change:
|
|
217
217
|
- the SSH argv or quoting;
|
|
@@ -289,7 +289,7 @@ caller/target/cross-harness LIVE are owed by `smoke-pi-durable-fresh-live`, a re
|
|
|
289
289
|
| Native-Windows carrier (carrier-bearing release, from 0.39.0) | one `windows-native-preview.yml` dispatch at the exact prepared SHA after its four `ci.yml` jobs are green: accepted build receipt (two byte-identical MSVC builds, exact import set), `pack` printing `present`, eight Windows addon/citizen cells on the installed tarball; that run's `win32-preview-tgz` is the release candidate, retrieved (never packed locally) with its `SHA256SUMS`, the Windows-printed install hash and the downloaded hash equal, then accepted by the Linux `check-install-container` unchanged (release skill M3w) | Experimental, shape-only, non-voting for `ci.yml`: no vendor turn, x64 runner only, named gaps in BASELINE. The four `ci.yml` jobs (incl. macOS) saw an addon-less pack, so the carrier-bearing tarball's macOS install is unmeasured. Artifacts expire after 14 days; the preserved copy is the durable one. |
|
|
290
290
|
| 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. |
|
|
291
291
|
| Native Claude host | installed strict doctor against a new real session | Missing live join is `NOT CERTIFIED`, not a fixture PASS. |
|
|
292
|
-
| Native agy host |
|
|
292
|
+
| Native agy host | the deterministic pause cell plus the agy record, install and sender gates inside `check:full`; native push is paused (#152), so no round trip is claimed. The three doctors and a fresh-conversation identity check are an optional diagnostic, run on request | Aggregate release-gate does not own an agy conversation id. The paused LIVE smoke exits 2 `[entwurf:paused]`, not a PASS. |
|
|
293
293
|
| 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. |
|
|
294
294
|
|
|
295
295
|
The repo-local `entwurf-release` skill owns the `land → prepare → make → publish`
|
|
@@ -305,7 +305,7 @@ consumer image outside this standing protocol.
|
|
|
305
305
|
|
|
306
306
|
- **Garden-id delivery:** discover a target with `entwurf_peers`, then `entwurf_v2 fire-and-forget` — the intent axis is single-valued since the visible-first cut. It reaches a live pi target over its control socket, a deliverable self-fetch citizen through the mailbox, and a probe-alive native-push conversation by direct injection. A DORMANT in-domain citizen rejects as `dormant-fire-forget-unsupported` — delivery never wakes it; reopen it with the separate lifecycle verb `entwurf_resume_call {target}`, then dispatch again. A wrong target state is rejected, never auto-fixed and never launched into.
|
|
307
307
|
- **ACP continuity:** a direct `pi --provider entwurf --model claude-sonnet-5-5` turn, or the `smoke-acp-session-reuse-live` gate (process-scoped reuse + recall). Multi-turn reuse is proven by that gate, not by any v1 resume tool.
|
|
308
|
-
- **agy citizenship:** in a fresh agy conversation, the first `PreInvocation` must yield a garden id
|
|
308
|
+
- **agy citizenship (optional diagnostic while #152 holds; run on request, not a cut requirement):** in a fresh agy conversation, the first `PreInvocation` must yield a garden id and `entwurf_self` must report `agentId=meta-session/antigravity`. While native push is paused (#152), `replyable` is `false` with a reason naming the pause, and a dispatch to that garden id returns `native-push-paused` with nothing probed or sent. A same-gid reply is not claimed. No mailbox/receiver-marker evidence counts on this rail.
|
|
309
309
|
|
|
310
310
|
### What NOT to do — bypassing the operational path
|
|
311
311
|
|
|
@@ -374,7 +374,7 @@ The goal is not merely "invoke Claude Code." We want:
|
|
|
374
374
|
**One install command to remember: `./run.sh setup <project>`.** It is idempotent — re-run the exact same command whenever anything looks wrong. There is no second install surface to juggle: from a clone `setup` runs the whole floor in order.
|
|
375
375
|
|
|
376
376
|
1. `pnpm install` — installs the pinned development dependencies and builds the bridge (source-checkout bootstrap only; an installed package never runs npm/pnpm inside `node_modules`)
|
|
377
|
-
2. pi wiring → `<project>/.pi/settings.json` + user-scope registration — only when a `pi` inside the supported range (`>=1.
|
|
377
|
+
2. pi wiring → `<project>/.pi/settings.json` + user-scope registration — only when a `pi` inside the supported range (`>=1.4.0 <1.5`) is on PATH; absent pi is an explicit zero-state SKIP, a below-floor pi is a detected FAIL. The user-scope entry is owner-recorded (#86 C2): another root's live-or-missing ownership makes this step a zero-write refusal (setup: pi FAIL) that names `takeover-user-scope`; the install-states bind the exact managed settings path (a drifted/symlinked/corrupt target is a zero-write refusal) and the inverse removes only the recorded owner's exact entry; `doctor-pi-package` reports the verdict
|
|
378
378
|
3. Claude meta-bridge global plugin — only when `claude` is on PATH; otherwise skipped cleanly
|
|
379
379
|
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
|
|
380
380
|
5. agy bridge + exact permission + statusline + `PreInvocation` hook — only when `agy` is on PATH; each adapter is idempotent and independently doctorable
|
|
@@ -437,7 +437,7 @@ addressable sends require `--entwurf-control` (measured 2026-07-24: the same
|
|
|
437
437
|
one-shot with that flag returns its own gid and delivers `entwurf_v2` to a peer
|
|
438
438
|
mailbox with `origin=pi-session`, `replyable=true`).
|
|
439
439
|
|
|
440
|
-
`setup` requires Node 24 and Python 3 (pnpm only on a source checkout — the dependency bootstrap is source-only, and installed mode needs no pnpm); harnesses including pi are optional-by-presence (absent → explicit zero-state SKIP, detected incomplete/below-floor → named FAIL + nonzero result). On a source checkout it runs the frozen `pnpm install` + presence-gated pi project/user wiring + detected Claude wiring + source stable-bin exposure + detected agy wiring + detected Copilot four-unit composition + the v2 install smoke. A green setup certifies that the source-owned `entwurf` symlink targets this checkout and wins PATH resolution; helper units are attempted independently and a foreign helper is a named bins FAIL (nonzero), with harness-specific doctors keeping the per-leaf verdict. A detected `copilot` composes all four native units (birth → MCP → receiver → visible footer) with independent per-unit verdicts (#86 C3b); setup does **not** replace any native-harness doctor. The full aggregate live floor is still `LIVE=1 ./run.sh release-gate <scratch> --cut` — without `--cut` it is a diagnostic pass, not acceptance — with agy
|
|
440
|
+
`setup` requires Node 24 and Python 3 (pnpm only on a source checkout — the dependency bootstrap is source-only, and installed mode needs no pnpm); harnesses including pi are optional-by-presence (absent → explicit zero-state SKIP, detected incomplete/below-floor → named FAIL + nonzero result). On a source checkout it runs the frozen `pnpm install` + presence-gated pi project/user wiring + detected Claude wiring + source stable-bin exposure + detected agy wiring + detected Copilot four-unit composition + the v2 install smoke. A green setup certifies that the source-owned `entwurf` symlink targets this checkout and wins PATH resolution; helper units are attempted independently and a foreign helper is a named bins FAIL (nonzero), with harness-specific doctors keeping the per-leaf verdict. A detected `copilot` composes all four native units (birth → MCP → receiver → visible footer) with independent per-unit verdicts (#86 C3b); setup does **not** replace any native-harness doctor. The full aggregate live floor is still `LIVE=1 ./run.sh release-gate <scratch> --cut` — without `--cut` it is a diagnostic pass, not acceptance — with agy accepted separately under criterion 7: its paused native push by the deterministic `[QK:AGY-NATIVE-PUSH-PAUSED]` cell and its record, install and sender gates, all inside `check:full`; the retained legacy identity check is an optional diagnostic while #152 holds, and no agy round trip is claimed.
|
|
441
441
|
|
|
442
442
|
### 1.4 Cross-install / cross-backend parity (optional, high-value)
|
|
443
443
|
|
|
@@ -545,7 +545,7 @@ The minimum passing bar:
|
|
|
545
545
|
4. **Native-host doctor green where the Claude meta-bridge is claimed:** a new post-install Claude session exists, live evidence is present, and the installed `doctor-meta-bridge` exits 0. `NOT CERTIFIED` is a release failure for that host, not a skip.
|
|
546
546
|
5. **Honest self-recognition:** the bridged model identifies its actual harness/backend, lists `entwurf-bridge` as the single MCP server with the tools its loaded bridge actually lists (the current source lists nine; a pi citizen offers seven with two hidden, the pi-durable contact eight), and presents a backend-native (not normalized) tool surface.
|
|
547
547
|
6. **Carrier separation honored:** engraving vs pi-context-augment kept distinct (§1A.0); no bridge-identity narrative attributed to the engraving carrier.
|
|
548
|
-
7. **agy
|
|
548
|
+
7. **agy native push paused (#152); acceptance is deterministic while the pause holds.** Required for a cut: a dispatch to an agy target returns `native-push-paused` with zero probe, send, adapter or record activity, an agy sender's `replyable` is false by name, and `entwurf_register_native` refuses before probing — proven by the deterministic `[QK:AGY-NATIVE-PUSH-PAUSED]` cell, which needs no agy conversation and is not a live round trip — and the agy record schema and reader stay in place, with the agy install and sender gates green inside `check:full`. A same-gid reply is not claimed while the pause holds. Not required for a cut: the retained legacy identity check (all three agy doctors, plus automatic birth/statusline/sender identity in a fresh conversation, where `agentId=meta-session/antigravity` is correct and model display is not part of it). It is an optional diagnostic run when explicitly asked, it is not send evidence, and a release does not claim that the installed agy host is certified or can receive. An optional diagnostic is not a cut requirement, and neither is a native-push PASS. Lifting the pause needs a separate future change with its own positive acceptance. Same-pid concurrent conversation invocation is not claimed.
|
|
549
549
|
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.
|
|
550
550
|
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.
|
|
551
551
|
10. **Hygiene:** no orphan ACP children; no unexpected persisted session garbage (a turn-scoped `cwd:` fallback is never a persisted reuse).
|
package/demo/README.md
CHANGED
|
@@ -24,7 +24,7 @@ takes/helpers/tests are maintainer tooling and are deliberately excluded from th
|
|
|
24
24
|
|
|
25
25
|
- Node at the `package.json#engines.node` floor; the checkout's pinned dependencies and
|
|
26
26
|
current compiled bridge. After a source/bootstrap change, build with `pnpm run build-bridge`.
|
|
27
|
-
- Pi at the checkout's supported floor (**current floor 1.
|
|
27
|
+
- Pi at the checkout's supported floor (**current floor 1.4.0**); `entwurf` on PATH must be
|
|
28
28
|
the intended source stable-bin. Source evidence is not registry-package acceptance.
|
|
29
29
|
- `herdr` on PATH at exactly `scripts/fixtures/herdr-supply.json#version`; `asciinema` v3.
|
|
30
30
|
- Herdr's existing Pi integration at
|
package/docs/acp-backend-rail.md
CHANGED
|
@@ -135,7 +135,7 @@ Dep bump ledger. 0.38.0 (#145) changes only the pi SDK set; the ACP rows are unc
|
|
|
135
135
|
| Surface | Declaration | Class | What a green actually says |
|
|
136
136
|
|---|---|---|---|
|
|
137
137
|
| Entwurf package | `package.json` `version` | checkout declaration, not a release receipt | names the package label these rows sit beside; whether that label shipped, and with which rows, is CHANGELOG's record |
|
|
138
|
-
| pi runtime | SDK exact `1.
|
|
138
|
+
| pi runtime | SDK exact `1.4.0`, host `>=1.4.0 <1.5` | **exact** oracle + **closed range** | the SDK set this checkout builds, gates and (since #130) ships as exact production dependencies; an ordinary Pi host inside the range is accepted, and the ceiling moves only on measurement. The published 0.36.0 through 0.37.1 shipped the 1.2.0 set (#141); 0.38.0 (#145) is the first to carry 1.3.0. Historical: the published 0.33.0 through 0.35.1 ship SDK exact 1.1.0, 0.32.0 ships SDK exact 1.0.4 with the next-minor ceiling (ROADMAP's 2026-10-07 bump entry), and 0.31.0 declared the earlier peer floor 1.0.2 |
|
|
139
139
|
| ACP wire SDK | `@agentclientprotocol/sdk 1.8.0` | **exact** | the shared wire oracle both adapters speak |
|
|
140
140
|
| Claude ACP adapter | `@agentclientprotocol/claude-agent-acp 0.90.0` | **exact**, bundled | shipped since 0.36.1; resolved before any PATH fallback; declaration is not compatibility evidence |
|
|
141
141
|
| Claude Agent SDK | `0.3.296` (transitive) | **exact** oracle | the runtime risk surface behind the adapter |
|
|
@@ -83,7 +83,7 @@ Emergency/manual workaround when the MCP server environment is wrong but an exis
|
|
|
83
83
|
External/meta-session semantics:
|
|
84
84
|
|
|
85
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.
|
|
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`.
|
|
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, and while its native push is paused (#152) it is `false` by name without a probe; Codex requires a matching request-scoped record and loaded app-server thread. Identity survives either way; only `replyable` drops to `false`.
|
|
87
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.
|
|
88
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.
|
|
89
89
|
|