@junghanacs/entwurf 0.14.2 → 0.15.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/AGENTS.md +9 -5
  2. package/CHANGELOG.md +149 -0
  3. package/DELIVERY.md +72 -59
  4. package/README.md +120 -20
  5. package/VERIFY.md +10 -7
  6. package/demo/README.md +1 -1
  7. package/docs/acp-backend-rail.md +2 -2
  8. package/docs/external-mcp-host.md +26 -4
  9. package/docs/setup-clean-host.md +118 -18
  10. package/mcp/entwurf-bridge/dist/mcp/entwurf-bridge/src/index.js +8 -6
  11. package/mcp/entwurf-bridge/dist/pi-extensions/entwurf-capabilities.json +1 -0
  12. package/mcp/entwurf-bridge/dist/pi-extensions/lib/copilot-fresh-preflight.js +253 -0
  13. package/mcp/entwurf-bridge/dist/pi-extensions/lib/meta-sender-identity.js +10 -2
  14. package/mcp/entwurf-bridge/dist/pi-extensions/lib/meta-session.js +102 -28
  15. package/mcp/entwurf-bridge/dist/pi-extensions/lib/mux-fresh-call.js +77 -11
  16. package/mcp/entwurf-bridge/dist/pi-extensions/meta-bridge-hook-copilot.js +288 -0
  17. package/mcp/entwurf-bridge/src/index.ts +10 -6
  18. package/mcp/entwurf-bridge/tsconfig.build.json +2 -1
  19. package/package.json +22 -11
  20. package/pi/copilot-receive/entwurf-receive/extension.mjs +323 -0
  21. package/pi/entwurf-capabilities.json +1 -0
  22. package/pi/meta-bridge-copilot/.claude-plugin/marketplace.json +11 -0
  23. package/pi/meta-bridge-copilot/entwurf-meta-receive-copilot/.claude-plugin/plugin.json +5 -0
  24. package/pi/meta-bridge-copilot/entwurf-meta-receive-copilot/hooks/hooks.json +7 -0
  25. package/pi/meta-bridge-copilot/entwurf-meta-receive-copilot/scripts/copilot-hook-launch.sh +85 -0
  26. package/pi-extensions/entwurf-control.ts +17 -8
  27. package/pi-extensions/lib/copilot-fresh-preflight.ts +251 -0
  28. package/pi-extensions/lib/meta-sender-identity.ts +10 -2
  29. package/pi-extensions/lib/meta-session.ts +102 -29
  30. package/pi-extensions/lib/mux-fresh-call.ts +82 -11
  31. package/pi-extensions/meta-bridge-hook-copilot.ts +323 -0
  32. package/run.sh +1079 -186
  33. package/scripts/agy-bridge.sh +4 -2
  34. package/scripts/check-copilot-birth-hook.ts +1174 -0
  35. package/scripts/check-copilot-launch.ts +395 -0
  36. package/scripts/check-copilot-receive-arm.ts +734 -0
  37. package/scripts/check-copilot-statusline.ts +122 -0
  38. package/scripts/check-entwurf-capabilities.ts +25 -6
  39. package/scripts/check-fresh-cut-gate.sh +104 -27
  40. package/scripts/check-gate-qualification.ts +10 -1
  41. package/scripts/check-install-container.sh +2 -2
  42. package/scripts/check-install-surface.ts +1 -1
  43. package/scripts/check-meta-capability-source.ts +89 -5
  44. package/scripts/check-meta-doctor-oracle.sh +26 -0
  45. package/scripts/check-meta-manifest-schema.py +38 -0
  46. package/scripts/check-meta-receiver-marker.ts +25 -2
  47. package/scripts/check-meta-session.ts +32 -0
  48. package/scripts/check-setup-qualification.sh +126 -0
  49. package/scripts/copilot-bridge-doctor.sh +242 -0
  50. package/scripts/copilot-bridge-install.sh +384 -0
  51. package/scripts/copilot-bridge-oracle.sh +223 -0
  52. package/scripts/copilot-bridge-uninstall.sh +137 -0
  53. package/scripts/copilot-launch.sh +236 -0
  54. package/scripts/copilot-mcp-bridge.sh +177 -0
  55. package/scripts/copilot-mcp-config.py +221 -0
  56. package/scripts/copilot-receive-bridge.sh +483 -0
  57. package/scripts/copilot-statusline-bridge.sh +149 -0
  58. package/scripts/copilot-statusline-config.py +201 -0
  59. package/scripts/copilot-statusline.sh +68 -0
  60. package/scripts/dev-bin.sh +53 -9
  61. package/scripts/fake-copilot-vendor.sh +67 -0
  62. package/scripts/meta-bridge-hook-log.sh +17 -5
  63. package/scripts/mutants/capability-cache.json +64 -0
  64. package/scripts/mutants/copilot-birth.json +244 -0
  65. package/scripts/mutants/copilot-launch.json +187 -0
  66. package/scripts/mutants/copilot-receive.json +263 -0
  67. package/scripts/mutants/fresh-cut.json +17 -0
  68. package/scripts/mutants/mux-fresh-call.json +216 -2
  69. package/scripts/mutants/pack-install.json +17 -0
  70. package/scripts/mutants/pi-package-ownership.json +92 -0
  71. package/scripts/mutants/setup-verdict.json +148 -0
  72. package/scripts/mutants/source-install.json +32 -0
  73. package/scripts/pi_settings_io.py +22 -0
  74. package/scripts/raw-async-delivery/README.md +170 -125
  75. package/scripts/raw-async-delivery/copilot-enqueue-addressed.sh +35 -0
  76. package/scripts/raw-async-delivery/copilot-extension-receive/extension.mjs +123 -0
  77. package/scripts/register-pi-package.py +525 -7
  78. package/scripts/register-pi-provider.py +162 -12
  79. package/scripts/smoke-agy-install-state.sh +86 -30
  80. package/scripts/smoke-agy-statusline-state.sh +9 -6
  81. package/scripts/smoke-copilot-mcp-state.sh +153 -0
  82. package/scripts/smoke-copilot-statusline-state.sh +131 -0
  83. package/scripts/smoke-mux-fresh-call-live.ts +2 -0
  84. package/scripts/smoke-mux-lifecycle-live.ts +3 -1
  85. package/scripts/smoke-setup-verdict.sh +291 -0
  86. package/scripts/smoke-user-scope-citizen.sh +388 -1
  87. package/scripts/tsconfig.json +1 -0
package/README.md CHANGED
@@ -10,10 +10,10 @@ npm package: <https://www.npmjs.com/package/@junghanacs/entwurf>
10
10
 
11
11
  Legacy package: [`@junghanacs/pi-shell-acp`](https://www.npmjs.com/package/@junghanacs/pi-shell-acp). `entwurf` is its 0.12+ successor line: the same work renamed around the garden-citizen dispatch substrate rather than the pi adapter.
12
12
 
13
- > **Repository shape.** This repo is **entwurf-core (v2 dispatch) + native-harness bridges + a pi adapter + an ACP plugin**. Pi is one supported harness adapter — important because it supplies control sockets and hosts the ACP plugin today — but it is not the project subject. Claude Code is shipped as a mailbox-backed meta-session; Antigravity (`agy`) is shipped as a native-push citizen with automatic `PreInvocation` birth, ambient garden-id status, and a managed MCP/permission install surface. Codex has a launch-mode-specific verified delivery probe documented in [DELIVERY.md](./DELIVERY.md), but no managed native-citizen install lane yet. The ACP plugin ships two backends through one adapter rail: Claude (the reference) and Snowflake Cortex Code (landed in 0.13.0 under the measured contract in [docs/acp-backend-rail.md](./docs/acp-backend-rail.md#cortex-code-audit-d1d10)).
13
+ > **Repository shape.** This repo is **entwurf-core (v2 dispatch) + native-harness bridges + a pi adapter + an ACP plugin**. Pi is one supported harness adapter — important because it supplies control sockets and hosts the ACP plugin today — but it is not the project subject. Claude Code and GitHub Copilot CLI are shipped as mailbox-backed self-fetch meta-sessions; Antigravity (`agy`) is shipped as a native-push citizen with automatic `PreInvocation` birth, ambient garden-id status, and a managed MCP/permission install surface. Codex has a launch-mode-specific verified delivery probe documented in [DELIVERY.md](./DELIVERY.md), but no managed native-citizen install lane yet. The ACP plugin ships two backends through one adapter rail: Claude (the reference) and Snowflake Cortex Code (landed in 0.13.0 under the measured contract in [docs/acp-backend-rail.md](./docs/acp-backend-rail.md#cortex-code-audit-d1d10)).
14
14
 
15
15
  ```text
16
- Claude Code / Codex / agy / pi
16
+ Claude Code / Copilot / Codex / agy / pi
17
17
  → garden id
18
18
  → entwurf_v2
19
19
  → control-socket | meta-mailbox | native-push
@@ -44,6 +44,12 @@ native Claude Code
44
44
  → entwurf_self | entwurf_v2 | entwurf_inbox_read
45
45
  ```
46
46
 
47
+ Copilot uses another self-fetch rail. Its birth hook mints the V3 record on the first prompt,
48
+ the MCP hand supplies `entwurf_inbox_read`, and a first-party extension arms the receiver marker
49
+ and rings a doorbell for queued bodies. Supported launch is `entwurf copilot`; visible fresh uses
50
+ that same managed invocation so extension scan, receiver preconditions, model, and permission
51
+ policy are present before the sibling calls back with its garden id.
52
+
47
53
  Antigravity uses a separate shipped rail. Its `PreInvocation` hook births or re-attaches the conversation by native `conversationId`, writes a record-backed sender marker, and leaves delivery to the live native LS gRPC route. There is no mailbox or receiver marker on this rail: `entwurf_v2` probes the conversation and direct-injects with native-push.
48
54
 
49
55
  ```text
@@ -53,7 +59,10 @@ native Antigravity / agy
53
59
  ↔ entwurf_v2 native-push
54
60
  ```
55
61
 
56
- Claude's `install-meta-bridge` and agy's `install-agy-{bridge,statusline,hooks}` are distinct managed install surfaces because their lifecycle and delivery transports are genuinely different. Codex remains verified probe evidence, not a shipped managed native-citizen lane; see [DELIVERY.md](./DELIVERY.md).
62
+ Claude's `install-meta-bridge`, Copilot's four `install-copilot-*` surfaces, and agy's
63
+ `install-agy-{bridge,statusline,hooks}` are distinct because their lifecycle and delivery
64
+ transports are genuinely different. Codex remains verified probe evidence, not a shipped managed
65
+ native-citizen lane; see [DELIVERY.md](./DELIVERY.md).
57
66
 
58
67
  > **Direction.** Inverse of [`pi-acp`](https://github.com/svkozak/pi-acp). `pi-acp` lets external ACP clients talk *to* pi; `entwurf` lets garden citizens talk across harness boundaries — with pi as one adapter, not the center.
59
68
 
@@ -62,9 +71,9 @@ Claude's `install-meta-bridge` and agy's `install-agy-{bridge,statusline,hooks}`
62
71
  A few words that look unusual for a coding tool.
63
72
 
64
73
  - **Entwurf** (기투, projection-of-self) — sibling sessions with their own runtime boundary. Not "delegate," not "worker," not "sub-agent." Opening a visible sibling (`entwurf_fresh_call`), live peer messaging (`entwurf_v2`) and reopening a dormant one (`entwurf_resume_call`) are first-class; the hidden background resume that preceded the last of those was withdrawn under the visible-first rule.
65
- - **Garden / garden id** — the garden is the shared address space where independent harness sessions become citizens without losing their own runtime or transcript. A garden id is the stable address of one such citizen (for pi, a garden-native session id like `YYYYMMDDTHHMMSS-<6hex>`; for native harnesses, a meta-session id minted from an authoritative lifecycle hook — Claude `SessionStart`, agy `PreInvocation`). It is not a worker name and not proof that pi owns the session. The same-looking id may name a live control socket, a dormant pi record, a mailbox-backed native session, or a native-push conversation, so callers discover facts with `entwurf_peers` and deliver with `entwurf_v2` instead of choosing a transport by hand.
74
+ - **Garden / garden id** — the garden is the shared address space where independent harness sessions become citizens without losing their own runtime or transcript. A garden id is the stable address of one such citizen (for pi, a garden-native session id like `YYYYMMDDTHHMMSS-<6hex>`; for native harnesses, a meta-session id minted from an authoritative lifecycle hook — Claude `SessionStart`, Copilot's first-prompt birth hook, agy `PreInvocation`). It is not a worker name and not proof that pi owns the session. The same-looking id may name a live control socket, a dormant pi record, a mailbox-backed native session, or a native-push conversation, so callers discover facts with `entwurf_peers` and deliver with `entwurf_v2` instead of choosing a transport by hand.
66
75
  - **Engraving** — optional short operator text delivered through each backend's native identity carrier. Not a giant hidden prompt, not a tool catalog.
67
- - **MCP** — in this repo, MCP is just the transport by which ACP-backed sessions receive pi capabilities that native pi exposes directly as extensions. It is not a general MCP platform. Explicit `entwurfProvider.mcpServers` only; no ambient `~/.mcp.json` scanning, no automatic retrieval. The same `entwurf-bridge` entry can also be wired into another host's MCP catalog (Claude Code, Codex, Antigravity, …) when the operator chooses. `entwurf_self` returns an authoritative pi-session or trusted meta-session identity envelope; `entwurf_v2` requires an authoritative sender by default (#50 C4) — a plain external MCP host with no identity lane is refused unless the operator explicitly wires the documented anonymous hatch, and even then it is never replyable.
76
+ - **MCP** — in this repo, MCP is just the transport by which ACP-backed sessions receive pi capabilities that native pi exposes directly as extensions. It is not a general MCP platform. Explicit `entwurfProvider.mcpServers` only; no ambient `~/.mcp.json` scanning, no automatic retrieval. The same `entwurf-bridge` entry can also be wired into another host's MCP catalog (Claude Code, Copilot, Codex, Antigravity, …) when the operator chooses. `entwurf_self` returns an authoritative pi-session or trusted meta-session identity envelope; `entwurf_v2` requires an authoritative sender by default (#50 C4) — a plain external MCP host with no identity lane is refused unless the operator explicitly wires the documented anonymous hatch, and even then it is never replyable.
68
77
  - **Session persistence** — re-attaches pi to the same remote ACP session. Does not hydrate backend transcripts into pi history.
69
78
 
70
79
  ## Install
@@ -74,19 +83,24 @@ A few words that look unusual for a coding tool.
74
83
  adapter that hosts the ACP plugin and live control-socket surface, but the base
75
84
  install is **not** `pi install npm:...` anymore.
76
85
 
77
- The package exposes five bins:
86
+ The package exposes six bins:
78
87
 
79
88
  - `entwurf` → `run.sh` (installer, checks, native-bridge doctors/installers)
80
89
  - `entwurf-bridge` → the MCP stdio launcher (`mcp/entwurf-bridge/start.sh`)
81
90
  - `entwurf-statusline` → the Claude Code statusline renderer (`scripts/meta-bridge-statusline.sh`)
82
91
  - `entwurf-agy-statusline` → the Antigravity garden-id statusline renderer (`scripts/agy-statusline.sh`)
83
92
  - `entwurf-agy-imprint` → the Antigravity `PreInvocation` birth/sender hook (`scripts/agy-imprint.sh`)
93
+ - `entwurf-copilot-statusline` → the Copilot CLI garden-id footer renderer (`scripts/copilot-statusline.sh`)
84
94
 
85
95
  The bridge/renderers/hook use stable bin names so package upgrades do not bake versioned package-store paths into native-harness settings.
86
96
 
87
- The bridge does not provide backend credentials, tokens, or subscription access,
88
- and does not bypass any backend auth. Whatever the operator's local `claude` /
89
- `codex` / `agy` / pi runtime already trusts is what entwurf can use.
97
+ Installing Entwurf installs **Entwurf only**: its package bytes, six bins, bridge, and
98
+ integration artifacts. It does not install `pi`, Claude Code, Copilot CLI, Codex, agy, Cortex,
99
+ or any other harness runtime. Those are operator choices and may all be absent. The bridge also
100
+ does not provide credentials, tokens, subscription access, or an auth bypass; whatever an
101
+ operator-installed harness already trusts is what Entwurf can use. `setup` is composition, not
102
+ recruitment: it may wire a harness that is present, but never downloads one to make a matrix cell
103
+ look complete.
90
104
 
91
105
  ### From npm — user/global install
92
106
 
@@ -137,12 +151,43 @@ or at the package launcher directly:
137
151
  ```bash
138
152
  git clone https://github.com/junghan0611/entwurf ~/repos/gh/entwurf
139
153
  cd ~/repos/gh/entwurf
140
- pnpm install
141
154
 
142
- ./run.sh install /path/to/your-project
155
+ ./run.sh setup /path/to/your-project
143
156
  ./run.sh check-bridge
144
157
  ```
145
158
 
159
+ The full source setup requires Node 24, pnpm, and Python 3 on PATH. Harnesses are
160
+ optional-by-presence: `setup` runs the frozen dependency install, then composes what the operator
161
+ already installed — a compatible `pi` (`>=0.84.3 <0.85`), Claude Code, agy, and the Copilot CLI
162
+ each get their wiring completed when detected, an absent harness is an explicit zero-state SKIP,
163
+ and a detected harness that cannot be completed (including a below-floor `pi`) is a named FAIL
164
+ that makes setup exit nonzero. `setup` never installs a harness binary or touches a credential
165
+ store. It also exposes stable commands under `~/.local/bin`, including `entwurf` → this
166
+ checkout's `run.sh`, so managed Copilot fresh does not depend on an unrelated global npm/pnpm
167
+ installation. A detected `copilot` composes all four native units (birth → MCP → receiver →
168
+ visible footer) independently; the explicit `install-copilot-*` surfaces below remain the
169
+ per-unit repair and inverse path. Package consumers
170
+ run the same `entwurf setup <project>` through their npm-provided bin: installed mode is decided
171
+ by name first, skips the source-only pnpm bootstrap entirely, and reports the stable commands as
172
+ already provided by npm bin linking; `entwurf install` remains the narrower pi-wiring repair
173
+ leaf.
174
+
175
+ The pi user-scope registration is ONE shared entry with a recorded owner (#86 C2): installing
176
+ from a second checkout or npm root does not silently steal it — normal `install`/`setup` refuse
177
+ (zero settings writes, live or missing owner alike), `entwurf takeover-user-scope` is the
178
+ operator-explicit move (old→new reported), `entwurf doctor-pi-package` names the ownership
179
+ verdict (including `missing-owner` for a moved/deleted root), and `entwurf remove-user-scope`
180
+ is same-owner-only with a reported orphan cleanup when entry, package state and provider
181
+ installerRoot all name the same missing root. User-scope operations are atomic across the
182
+ package and provider halves (read-only preflights first — a refusal on one side writes nothing
183
+ on the other); a takeover over an operator's provider override reports a split verdict (package
184
+ moved, override preserved unowned) and a legacy unattributed provider state must be adopted by
185
+ a same-root `setup`/`install` before it can be removed. Both halves' install-states also record
186
+ exactly WHICH settings file they manage (`managedSettingsPath`): an operation targeting a
187
+ different, symlinked or unparseable file is a zero-write refusal before either half goes green,
188
+ and the owned/orphan inverse removes only the recorded owner's exact `packages[]` entry —
189
+ 0 or 2+ exact entries refuse, so an npm spec or another `.../entwurf` path is never collateral.
190
+
146
191
  A development clone runs the bridge source through Node's strip-types path;
147
192
  an npm-installed package runs the prebuilt JS under `mcp/entwurf-bridge/dist/`
148
193
  because Node refuses to strip `.ts` files under `node_modules`. The dev launcher's
@@ -154,7 +199,7 @@ gitignored and may be absent or stale immediately after a checkout or pull.
154
199
  ### Pi adapter / ACP plugin lane
155
200
 
156
201
  To use the `entwurf` provider inside pi, install a compatible pi binary
157
- separately (`@earendil-works/pi-coding-agent >=0.84.2 <0.85`). Then point pi at
202
+ separately (`@earendil-works/pi-coding-agent >=0.84.3 <0.85`). Then point pi at
158
203
  the npm-installed package or development clone:
159
204
 
160
205
  ```bash
@@ -169,8 +214,10 @@ For daily operator sessions, launch pi with `--entwurf-control` — no id
169
214
  injection; the meta-record mints the garden address (see [Garden launcher](#garden-launcher)). Older pi
170
215
  versions may silently miss the provider/extension surface, so treat the pi floor
171
216
  as release-critical for the ACP/plugin lane. A host that only uses
172
- `entwurf-bridge` from Claude Code / Codex / Antigravity does not need pi at all for
173
- delivery: no `entwurf_v2` rail launches a pi process.
217
+ `entwurf-bridge` from Claude Code / Copilot / Codex / Antigravity does not need pi at all for
218
+ delivery: no `entwurf_v2` rail launches a pi process. That external-only shape works with the same
219
+ `setup` command: pi is optional-by-presence there, so a pi-less host simply gets an explicit pi
220
+ SKIP while the detected harnesses are composed.
174
221
 
175
222
  ### Native harness install and doctors
176
223
 
@@ -190,12 +237,65 @@ entwurf install-agy-hooks
190
237
  entwurf doctor-agy-bridge
191
238
  entwurf doctor-agy-statusline
192
239
  entwurf doctor-agy-hooks
240
+
241
+ # GitHub Copilot CLI — four independent surfaces, four independent failure modes
242
+ # (`setup` composes all four when `copilot` is on PATH; these remain the per-unit repair/inverse surfaces)
243
+ entwurf install-copilot-bridge # birth: garden id + who-sent, on the first prompt (owns an install-state; inverse: uninstall-copilot-bridge)
244
+ entwurf install-copilot-mcp # the entwurf tool hand (entwurf_inbox_read lives here)
245
+ entwurf install-copilot-receive # the receiver extension: doorbell + receiver marker
246
+ entwurf install-copilot-statusline # optional for a manual citizen; required for supported fresh
247
+ entwurf doctor-copilot-bridge
248
+ entwurf doctor-copilot-mcp
249
+ entwurf doctor-copilot-receive
250
+ entwurf doctor-copilot-statusline
193
251
  ```
194
252
 
253
+ #### Launching Copilot as a garden citizen — `entwurf copilot`
254
+
255
+ Copilot only scans for extensions when its CLI is started with
256
+ `COPILOT_CLI_ENABLED_FEATURE_FLAGS=EXTENSIONS`, and when that flag is absent it skips the
257
+ scan **silently** — no error, no log line, no receiver. entwurf does not own your shell
258
+ and writes nothing to your rc files, so it owns one invocation instead:
259
+
260
+ ```bash
261
+ entwurf copilot # managed launch, in this terminal
262
+ entwurf copilot -p "…" --model gpt-5.4
263
+ copilot # the plain vendor CLI, untouched
264
+ ```
265
+
266
+ `entwurf copilot` execs the vendor CLI in your current terminal — same cwd, same pid,
267
+ same exit status, no tmux window and no new citizen (a Copilot session is still born on
268
+ its first prompt). Before it launches it verifies that the receiver unit it is about to
269
+ promise is actually installed, and refuses with `entwurf install-copilot-receive` if it
270
+ is not, rather than starting a session that can never be delivered to.
271
+
272
+ **Running it is your consent to its profile.** For that one invocation it adds:
273
+
274
+ | Injected | When |
275
+ |---|---|
276
+ | `COPILOT_CLI_ENABLED_FEATURE_FLAGS=EXTENSIONS` | always — your other feature-flag tokens are preserved, in order, deduplicated |
277
+ | `--model auto` | only when you passed no `--model` |
278
+ | `--yolo` | only when you passed no explicit permission or surface policy flag |
279
+
280
+ The escape hatch is simply to state your own policy: any of `--yolo`, `--allow-all`,
281
+ `--allow-all-tools`, `--allow-all-paths`, `--allow-all-urls`, `--allow-tool`,
282
+ `--deny-tool`, `--allow-url`, `--deny-url`, `--available-tools` or `--excluded-tools`
283
+ suppresses the injected `--yolo`. The narrowing flags are in that list on purpose — adding
284
+ `--yolo` beside your `--allow-url=…` would silently widen exactly what you were
285
+ restricting. `--allow-all-mcp-server-instructions` (prompt content, not authorization) and
286
+ `--autopilot` (a mode) are deliberately not policy flags. Everything you pass is forwarded
287
+ byte-identically, injected defaults land before any `--` terminator, and nothing after the
288
+ terminator is read as policy. Nothing is written to disk; run plain `copilot` and none of
289
+ this applies.
290
+
291
+ Why `--yolo` is the default: the managed lane exists so a sibling can wake an idle session,
292
+ and draining the mailbox with `entwurf_inbox_read` costs two interactive approvals under
293
+ the default permission prompts — which an idle, unattended session is not there to answer.
294
+
195
295
  Claude Code uses the supported floor `>=2.1.217`; older versions silently discard the
196
296
  exec-hook `args`, so install and doctor fail loud rather than falling back. After any
197
- upgrade, rerun the installer for the native harness you use and restart its existing
198
- processes. A claimed Claude host is certified only when a **new** session using the
297
+ upgrade, rerun every owned installer for the native harness you use (all four for Copilot) and
298
+ restart its existing processes. A claimed Claude host is certified only when a **new** session using the
199
299
  installed artifact makes `doctor-meta-bridge` exit 0 with the live owner join.
200
300
 
201
301
  Linux is the only currently certified Claude meta-bridge axis. New macOS wiring is
@@ -309,7 +409,7 @@ Reference shape lives in [`pi/settings.reference.json`](./pi/settings.reference.
309
409
  ### External MCP registration
310
410
 
311
411
  `entwurf-bridge` can also be registered in a separate MCP-aware harness (Claude Code,
312
- Codex CLI, Antigravity). Two shapes exist and they are not interchangeable:
412
+ Copilot CLI, Codex CLI, Antigravity). Two shapes exist and they are not interchangeable:
313
413
 
314
414
  - **plain external MCP host** — no garden meta-record or sender marker. It can read the
315
415
  surfaces, but `entwurf_v2` is **refused by default**: there is no authoritative sender.
@@ -427,7 +527,7 @@ A two-pane recording covers the pre-0.12 v1 surface end-to-end — sibling resum
427
527
 
428
528
  Live peer messaging carries a sender envelope `{ sessionId, agentId, cwd, timestamp, origin?, replyable? }`; `entwurf_self` returns that authoritative envelope for the current pi session or trusted meta-session. Plain external MCP hosts are non-replyable. A garden-native meta-session carries a trusted `meta-session` envelope, but **`replyable` is a fact its own rail decides, not a consequence of being trusted** — a self-fetch citizen needs a live armed receiver, a native-push citizen needs an alive adapter probe, and a pi session needs its control socket. `entwurf_self` also reports which rail a meta-session reply would ride, because a native-push citizen has no mailbox to name. `wants_reply` is an etiquette marker rendered as a `(wants reply)` badge — not a transport contract, no wait, no polling. **v2 never gates on it:** a `wants_reply` from an external/non-replyable caller is passed through and surfaced honestly beside that sender's `replyable: false`, not rejected — the decider routes on target + intent, never on sender replyability. (The retired v1 `entwurf_send` did reject it; that behaviour went with the verb.)
429
529
 
430
- In ACP-backed and external native-harness sessions, `entwurf-bridge` exposes seven tools: `entwurf_v2`, `entwurf_peers`, `entwurf_fresh_call`, `entwurf_resume_call`, `entwurf_self`, `entwurf_inbox_read`, and the explicit/manual `entwurf_register_native` fallback. Native pi exposes the shared capability directly through the extension surface (`entwurf_v2`, `entwurf_peers`, `entwurf_fresh_call`, `entwurf_resume_call` tools; the socket-scan `/entwurf-sessions` command is gone — #50 C4). **For garden-id delivery/reply use `entwurf_v2`** — the canonical surface that classifies the target and routes to live-pi / Claude-Code-meta-mailbox / Antigravity-native-push, and refuses a dormant target instead of waking it. **To open a sibling that does not exist yet use `entwurf_fresh_call`** — it launches one fixed backend (`pi` or `claude-code`) as a visible window in the operator's own tmux session, requires an explicit model, accepts one optional literal absolute `cwd` (omit it or pass `""` to use the caller's cwd), passes the model and selected directory through the runtime's visible launch path, and hands it a first task; the sibling's first action is a nonce callback whose sender envelope carries its garden id. The launch receipt records the requested model/cwd plus tmux coordinates and never claims that the runtime accepted them or completed delivery. (The v1 verbs `entwurf` / `entwurf_resume` / `entwurf_send` are gone.) Garden-native operator commands require `--entwurf-control`. There is no spawn target allowlist — the target registry is gone (#50 C3): `entwurf_v2` and `entwurf_resume_call` address an existing record-backed citizen, while `entwurf_fresh_call` takes its explicit backend/model/task and optional cwd directly rather than resolving a model tuple from a registry file. **To reopen a DORMANT pi citizen use `entwurf_resume_call {target}`** — the record supplies transcript, model, provider and cwd, so it takes no prompt, no task and no model override; it runs no turn, returns a LAUNCH receipt and a separate OBSERVATION receipt (only the second says the citizen is back), refuses a non-pi target as `target-not-pi`, and on an unobserved socket leaves the visible window open and releases its lock rather than retrying.
530
+ In ACP-backed and external native-harness sessions, `entwurf-bridge` exposes seven tools: `entwurf_v2`, `entwurf_peers`, `entwurf_fresh_call`, `entwurf_resume_call`, `entwurf_self`, `entwurf_inbox_read`, and the explicit/manual `entwurf_register_native` fallback. Native pi exposes the shared capability directly through the extension surface (`entwurf_v2`, `entwurf_peers`, `entwurf_fresh_call`, `entwurf_resume_call` tools; the socket-scan `/entwurf-sessions` command is gone — #50 C4). **For garden-id delivery/reply use `entwurf_v2`** — the canonical surface that classifies the target and routes to live-pi / Claude-or-Copilot-meta-mailbox / Antigravity-native-push, and refuses a dormant target instead of waking it. **To open a sibling that does not exist yet use `entwurf_fresh_call`** — it launches one fixed backend (`pi`, `claude-code`, or `copilot`) as a visible window in the operator's own tmux session, requires an explicit model, accepts one optional literal absolute `cwd` (omit it or pass `""` to use the caller's cwd), passes the model and selected directory through the runtime's visible launch path, and hands it a first task; a `copilot` launch goes through entwurf's own managed invocation and is refused before any window opens if this host lacks the Copilot birth, MCP, receiver or visible-footer units. The sibling's first action is a nonce callback whose sender envelope carries its garden id. The launch receipt records the requested model/cwd plus tmux coordinates and never claims that the runtime accepted them or completed delivery. (The v1 verbs `entwurf` / `entwurf_resume` / `entwurf_send` are gone.) Garden-native operator commands require `--entwurf-control`. There is no spawn target allowlist — the target registry is gone (#50 C3): `entwurf_v2` and `entwurf_resume_call` address an existing record-backed citizen, while `entwurf_fresh_call` takes its explicit backend/model/task and optional cwd directly rather than resolving a model tuple from a registry file. **To reopen a DORMANT pi citizen use `entwurf_resume_call {target}`** — the record supplies transcript, model, provider and cwd, so it takes no prompt, no task and no model override; it runs no turn, returns a LAUNCH receipt and a separate OBSERVATION receipt (only the second says the citizen is back), refuses a non-pi target as `target-not-pi`, and on an unobserved socket leaves the visible window open and releases its lock rather than retrying.
431
531
 
432
532
  ### `entwurf_v2` — canonical dispatch verb
433
533
 
@@ -444,9 +544,9 @@ In ACP-backed and external native-harness sessions, `entwurf-bridge` exposes sev
444
544
  | dead / indeterminate native-push conversation | fire-and-forget | **reject** (`native-push-target-dead` / `native-push-probe-indeterminate`) |
445
545
  | record-less control socket (no meta-record) | any | **reject** (`record-less-socket` — pre-probe; diagnostic state, #50 C4) |
446
546
 
447
- **`entwurf_v2` is the canonical surface for garden-id delivery.** When you have a garden id and want to reach whoever it names — message, reply, or hand-off — `entwurf_v2` is the one surface that reads whether the target is live pi, dormant pi, mailbox-backed Claude Code, or native-push Antigravity and routes correctly; *when unsure which transport, use `entwurf_v2`*. This prevents callers from guessing a rail from the shape of an id.
547
+ **`entwurf_v2` is the canonical surface for garden-id delivery.** When you have a garden id and want to reach whoever it names — message, reply, or hand-off — `entwurf_v2` is the one surface that reads whether the target is live pi, dormant pi, mailbox-backed Claude Code or Copilot, or native-push Antigravity and routes correctly; *when unsure which transport, use `entwurf_v2`*. This prevents callers from guessing a rail from the shape of an id.
448
548
 
449
- What v2 provides is a **deterministic dispatch substrate** that moves the "which transport?" decision out of the fallible caller/model and into the decider, with transport-appropriate locking and an honest reject (no `✓ delivered`, no `.msg` garbage) when a target cannot receive. It still does **not** mint siblings, and it does not relaunch one either: every row above either reaches a citizen that is already running or refuses. Reopening a dormant pi citizen is `entwurf_resume_call`, a separate lifecycle verb that never routes through this decider. Fresh creation is the separate `entwurf_fresh_call` verb. It opens one fixed Pi or Claude Code runtime visibly in the caller's tmux session with a required explicit model passed in that runtime's measured CLI dialect and one optional literal absolute `cwd`; omitted or `""` means the caller's cwd. It returns only a synchronous launch receipt and lets the sibling report its new address asynchronously through the sender envelope of a nonce callback. Use this cwd input for a new cross-repository sibling; do not resume a dormant citizen as a cwd substitute. The meta-mailbox row requires an **active** self-fetch receiver; native-push requires a record-backed, probe-alive native conversation and never borrows mailbox state. The [mux launch lane](./docs/mux-launch-rail.md) owns placement, fixed-runtime launch, and the two narrow compositions above it (fresh-call and resume-call placement); delivery does not import launch, and mux is not a delivery transport.
549
+ What v2 provides is a **deterministic dispatch substrate** that moves the "which transport?" decision out of the fallible caller/model and into the decider, with transport-appropriate locking and an honest reject (no `✓ delivered`, no `.msg` garbage) when a target cannot receive. It still does **not** mint siblings, and it does not relaunch one either: every row above either reaches a citizen that is already running or refuses. Reopening a dormant pi citizen is `entwurf_resume_call`, a separate lifecycle verb that never routes through this decider. Fresh creation is the separate `entwurf_fresh_call` verb. It opens one fixed Pi, Claude Code, or Copilot runtime visibly in the caller's tmux session with a required explicit model passed in that runtime's measured CLI dialect and one optional literal absolute `cwd`; omitted or `""` means the caller's cwd. Copilot opens through `entwurf copilot`, never the bare vendor. It returns only a synchronous launch receipt and lets the sibling report its new address asynchronously through the sender envelope of a nonce callback. Use this cwd input for a new cross-repository sibling; do not resume a dormant citizen as a cwd substitute. The meta-mailbox row requires an **active** self-fetch receiver; native-push requires a record-backed, probe-alive native conversation and never borrows mailbox state. The [mux launch lane](./docs/mux-launch-rail.md) owns placement, fixed-runtime launch, and the two narrow compositions above it (fresh-call and resume-call placement); delivery does not import launch, and mux is not a delivery transport.
450
550
 
451
551
  A live pi target is *reached* over its control socket, but the socket is dispatch-internal transport, never identity (#50 C4). A control socket that no meta-record claims — a pre-record-era resident, an unreadable store, or a stale/planted file — is refused for **every** intent as `record-less-socket`, and the reject names the fix (restart the resident so `session_start` births its record, or quiesce and run the fresh-cut). `entwurf_peers` reports the same state as an aggregated `record-less-socket` diagnostic rather than a peer row.
452
552
 
package/VERIFY.md CHANGED
@@ -58,7 +58,7 @@ Verification here is not a benchmark. In production we exchange short turns and
58
58
  >
59
59
  > Be honest about its cost, because it is a MUST: it spends real model turns on the operator's configured runtimes (two pi siblings, each resumed once, plus one Claude Code sibling), it READS the operator's real runtime config, and the siblings' native session transcripts stay in the real pi agent dir — they are not cleaned up, and they are part of the evidence a resume was real. The fence is per-axis rather than absolute: the four meta roots (records, mailbox, receivers, senders) are fixture for every cell, so no garden record is ever minted outside it; the native pi cell's control socket and v2 lock are fixture too; the ACP pi cell's are REAL and transient (see the paragraph above); and the Claude cell runs its runtime under the real HOME while its mailbox and meta stay fixture. The run ends by proving the operator's SIX real roots — the four meta roots plus `~/.pi/entwurf-control` and `~/.pi/entwurf-v2-locks` — hold the entry sets they started with, with no fixture garden id anywhere, and that proof now runs on the failure path as well as the green one. Missing prerequisites decline with the protocol SKIP (97), which `release-gate --cut` refuses rather than rounding up.
60
60
  >
61
- > **Fresh-call LIVE is on-demand, not part of `release-gate`.** `LIVE=1 ./run.sh smoke-mux-fresh-call-live` opens configured Pi and Claude Code siblings, spends two model turns, and preserves their native transcripts; run it directly whenever a cut changes mux fresh-call/launch behavior. Its private tmux servers and fixture-bound entwurf write axes make it deterministic about garden-record/socket residue, but those real native turns should not appear unexpectedly in every aggregate cut.
61
+ > **Fresh-call LIVE is on-demand, not part of `release-gate`.** `LIVE=1 ./run.sh smoke-mux-fresh-call-live` opens configured Pi and Claude Code siblings, spends two model turns, and preserves their native transcripts; run it directly whenever a cut changes mux fresh-call/launch behavior. Its private tmux servers and fixture-bound entwurf write axes make it deterministic about garden-record/socket residue, but those real native turns should not appear unexpectedly in every aggregate cut. **Both this smoke and `smoke-mux-lifecycle-live` enumerate `pi` and `claude-code` only — that exclusion is intentional.** A Copilot visible-fresh (step 9 clause 7) was accepted on 2026-08-25 as an operator-metered LIVE (receipts in `DELIVERY.md`'s Copilot matrix row and the issue #82 thread). It still spends Copilot premium quota, still needs its own grant to re-run, and is never folded into those loops or into the release-gate MUST tier.
62
62
  >
63
63
  > 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).
64
64
  >
@@ -134,11 +134,14 @@ The goal is not merely "invoke Claude Code." We want:
134
134
 
135
135
  **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.
136
136
 
137
- 1. `pnpm install` — bundles pi (a dev/peer dependency; no separate `pi install` step) and builds the bridge
138
- 2. project wiring → `<project>/.pi/settings.json` `entwurfProvider.mcpServers.entwurf-bridge`
137
+ 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`)
138
+ 2. pi wiring → `<project>/.pi/settings.json` + user-scope registration — only when a `pi` inside the supported range (`>=0.84.3 <0.85`) 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
139
139
  3. Claude meta-bridge global plugin — only when `claude` is on PATH; otherwise skipped cleanly
140
- 4. agy bridge + exact permission + statusline + `PreInvocation` hook only when `agy` is on PATH; each adapter is idempotent and independently doctorable
141
- 5. `entwurf-bridge` install smoke (`validate_entwurf_bridge`)
140
+ 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
141
+ 5. agy bridge + exact permission + statusline + `PreInvocation` hook only when `agy` is on PATH; each adapter is idempotent and independently doctorable
142
+ 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
143
+ 7. `entwurf-bridge` install smoke (`validate_entwurf_bridge`)
144
+ 8. computed summary — per-component PASS/SKIP/FAIL; any detected-integration FAIL makes the whole command exit nonzero while valid components stay installed
142
145
 
143
146
  ```bash
144
147
  git clone https://github.com/junghan0611/entwurf /path/to/entwurf && cd $_
@@ -146,7 +149,7 @@ git clone https://github.com/junghan0611/entwurf /path/to/entwurf && cd $_
146
149
  # re-run the SAME command any time to repair a broken install
147
150
  ```
148
151
 
149
- Expected tail: `DONE: entwurf setup (pi adapter + detected native bridges + v2 install smoke) green.` On a host with `claude`, verify `./run.sh doctor-meta-bridge`. On a host with `agy`, verify all three: `doctor-agy-bridge`, `doctor-agy-statusline`, and `doctor-agy-hooks`. Setup keeps optional-harness failures non-fatal so pi/Claude hosts are not bricked; the doctors are the fail-loud acceptance surface.
152
+ Expected tail on a fully green host: `DONE: entwurf setup — result: green (computed from the component outcomes above).` The summary above it lists every component as PASS/SKIP/FAIL; a detected harness that could not be completed is named FAIL and the command exits nonzero (`result: NON-GREEN (FAIL: …)`) while every valid component stays installed — re-running the same `setup` is the repair action. On a host with `claude`, verify `./run.sh doctor-meta-bridge`. On a host with `agy`, verify all three: `doctor-agy-bridge`, `doctor-agy-statusline`, and `doctor-agy-hooks`. After adding a backend to `META_BACKENDS`, re-run the sibling install then the doctor a green checkout with a stale deployed plugin is a silent write-stop on that rail. Each harness doctor remains the fail-loud per-leaf acceptance surface.
150
153
 
151
154
  The wiring / meta-bridge / smoke steps are internal building blocks of `setup` (`install_local_package`, `scripts/meta-bridge-install.sh`, `validate_entwurf_bridge`) — call `setup`, never the parts. Consumers who `npm install @junghanacs/entwurf` get the obvious npm surface; that path is not the developer concern here.
152
155
 
@@ -173,7 +176,7 @@ addressable sends require `--entwurf-control` (measured 2026-07-24: the same
173
176
  one-shot with that flag returns its own gid and delivers `entwurf_v2` to a peer
174
177
  mailbox with `origin=pi-session`, `replyable=true`).
175
178
 
176
- `setup` runs `pnpm install` + project/user-scope install + detected native-harness wiring (Claude and/or agy) + the v2 install smoke. A green setup proves the required core path and reports optional-harness degradation; it does **not** replace the native-harness doctors. 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's conversation-id-gated round trip verified separately.
179
+ `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's conversation-id-gated round trip verified separately.
177
180
 
178
181
  ### 1.4 Cross-install / cross-backend parity (optional, high-value)
179
182
 
package/demo/README.md CHANGED
@@ -139,7 +139,7 @@ SCENE_DELAY=30 FINAL_PAUSE=10 bash demo.sh
139
139
 
140
140
  ## Prerequisites
141
141
 
142
- - `pi` on PATH (current floor 0.84.2)
142
+ - `pi` on PATH (current floor 0.84.3)
143
143
  - `entwurf` provider configured + auth ready for the selected sender/peer models
144
144
  - `asciinema` installed
145
145
  - `agg` installed (optional — only for GIF conversion)
@@ -92,8 +92,8 @@ undifferentiated "supported" column is what let a Claude PASS read as if it also
92
92
 
93
93
  | Surface | Declaration | Class | What a green actually says |
94
94
  |---|---|---|---|
95
- | Entwurf package | `0.14.1` | shipped baseline | the package contract these rows belong to |
96
- | pi runtime | devDep exact `0.84.2`, peer `>=0.84.2 <0.85` | **exact** oracle + **closed range** | built and certified against 0.84.2; hosts inside the range are accepted, and the ceiling moves only on measurement |
95
+ | Entwurf package | `0.15.0` | shipped baseline | the package contract these rows belong to |
96
+ | pi runtime | devDep exact `0.84.3`, peer `>=0.84.3 <0.85` | **exact** oracle + **closed range** | built and certified against 0.84.3; hosts inside the range are accepted, and the ceiling moves only on measurement |
97
97
  | ACP wire SDK | `@agentclientprotocol/sdk 1.3.0` | **exact** | the shared wire oracle both adapters speak |
98
98
  | Claude ACP adapter | `@agentclientprotocol/claude-agent-acp 0.70.0` | **exact**, bundled | the adapter we ship and certify; resolved before any PATH fallback |
99
99
  | Claude Agent SDK | `0.3.232` (transitive) | **exact** oracle | the runtime risk surface behind the adapter |
@@ -7,17 +7,39 @@ the one-line registration; everything an operator needs to actually wire a host
7
7
  `entwurf-bridge` can also be registered in a separate MCP-aware harness (Claude Code, Codex CLI, Antigravity/`agy`, …). That host does **not** become a pi session and does **not** need to be ACP-backed. There are now two honest cases:
8
8
 
9
9
  - **plain external MCP host**: no garden meta-record / sender marker. It can call the read surfaces (`entwurf_peers`, `entwurf_inbox_read`), but `entwurf_v2` sends are **refused by default** (#50 C4: "if we don't know who sent it, we don't send it"). The operator may wire the explicit hatch below; the send then goes out external/non-replyable.
10
- - **garden-native native session**: a trusted lifecycle hook minted a garden id and sender marker — `SessionStart` for Claude Code, `PreInvocation` for agy. It is not a pi control-socket session, but it can be replyable by garden id when its own mailbox/probe rail says so.
10
+ - **garden-native native session**: a trusted lifecycle hook minted a garden id and sender marker — `SessionStart` for Claude Code, `PreInvocation` for agy, `userPromptSubmitted`/`sessionStart` for GitHub Copilot CLI. It is not a pi control-socket session, but it can be replyable by garden id when its own mailbox/probe rail says so.
11
11
 
12
- **Which verb an external agent should reach for:** to deliver to / reply to a garden id, use **`entwurf_v2`** it is the canonical delivery surface and the only one that reads whether the target is live pi, dormant pi, mailbox-backed Claude Code, or native-push Antigravity and routes correctly. Discover targets with `entwurf_peers`, confirm your own identity with `entwurf_self`, drain a mailbox with `entwurf_inbox_read`, and use `entwurf_register_native` only as the explicit/manual fallback for binding an already-running agy conversation (normal agy birth is automatic through the installed hook). Open a NEW sibling with **`entwurf_fresh_call {backend, model, task, cwd?}`**, and reopen a DORMANT pi citizen under its own garden id with **`entwurf_resume_call {target}`**. Fresh call accepts one optional literal absolute `cwd`: omit it or pass `""` for the caller's cwd; otherwise it must name an existing directory and may not contain `#`. Use that input for a new cross-repository sibling resume preserves a dormant Pi citizen's recorded continuity and is not a cwd substitute. Fresh call needs its selected runtime (`pi` or `claude`) on the server's PATH; resume call always needs `pi`. Both start a runtime, while delivery does not. (The old v1 verbs `entwurf` / `entwurf_resume` / `entwurf_send` are gone.)
12
+ **Being garden-native is not the same as being replyable, and Copilot is the case that separates them.** Its hook writes a sender marker, so an `entwurf_v2` send carries its own garden id and the receiver learns who wrote measured 2026-08-21 on Copilot CLI 1.0.80, where a live send arrived under its own garden id with `origin: "meta-session"` and `replyable: false`. Replyability arrived later and through a different process: a first-party extension (`run.sh install-copilot-receive`) that the CLI forks, which binds to the same V3 record and writes a receiver marker owned by its own pid (#82 RAIL 5). So a Copilot citizen is `replyable: true` exactly while that extension is armed for it, and `replyable: false` honestly, with a real garden identity when it is not installed, not launched with `COPILOT_CLI_ENABLED_FEATURE_FLAGS=EXTENSIONS` (which is what `entwurf copilot` sets for one invocation), not yet born, or gone. Read the two facts off different rails: the sender marker answers *who sent this*; the receiver marker answers *can a reply land*. The onboarding obligations are in [`adding-a-harness.md`](./adding-a-harness.md) step 7, and the evidence boundary the managed LIVE acceptance that moved receive to D6, and what D7-partial / D3-pending still exclude is in [`DELIVERY.md`](../DELIVERY.md).
13
13
 
14
- Observed: Claude Code, Codex CLI, and Antigravity CLI all reach the read surfaces through this MCP bridge from a plain external host `entwurf_peers` is a pure fact projection, while `entwurf_inbox_read` is a **mutating drain** (it archives the messages and stamps the read-receipt), so "read" here does not mean side-effect-free; **sending** needs an identity lane. Claude becomes symmetric/replyable through its mailbox-backed meta-session; agy becomes symmetric/replyable through its record-backed sender marker plus live native-push probe. Codex has no managed citizen lifecycle yet, so a Codex host cannot send without the explicit anonymous hatch below.
14
+ **Which verb an external agent should reach for:** to deliver to / reply to a garden id, use **`entwurf_v2`** — it is the canonical delivery surface and the only one that reads whether the target is live pi, dormant pi, mailbox-backed Claude Code, or native-push Antigravity and routes correctly. Discover targets with `entwurf_peers`, confirm your own identity with `entwurf_self`, drain a mailbox with `entwurf_inbox_read`, and use `entwurf_register_native` only as the explicit/manual fallback for binding an already-running agy conversation (normal agy birth is automatic through the installed hook). Open a NEW sibling with **`entwurf_fresh_call {backend, model, task, cwd?}`**, and reopen a DORMANT pi citizen under its own garden id with **`entwurf_resume_call {target}`**. Fresh call accepts one optional literal absolute `cwd`: omit it or pass `""` for the caller's cwd; otherwise it must name an existing directory and may not contain `#`. Use that input for a new cross-repository sibling resume preserves a dormant Pi citizen's recorded continuity and is not a cwd substitute. Fresh call needs its selected runtime on the server's PATH (`pi`, `claude`, or `entwurf` — Copilot opens through the managed `entwurf copilot` invocation, never the bare vendor); resume call always needs `pi`. Both start a runtime, while delivery does not. (The old v1 verbs `entwurf` / `entwurf_resume` / `entwurf_send` are gone.)
15
+
16
+ Observed: Claude Code, Codex CLI, Antigravity CLI and GitHub Copilot CLI all reach the read surfaces through this MCP bridge from a plain external host — `entwurf_peers` is a pure fact projection, while `entwurf_inbox_read` is a **mutating drain** (it archives the messages and stamps the read-receipt), so "read" here does not mean side-effect-free; **sending** needs an identity lane. Claude and Copilot become symmetric/replyable through a mailbox-backed meta-session — Claude's watch armed by its own hook, Copilot's by the forked extension it installs; agy becomes symmetric/replyable through its record-backed sender marker plus live native-push probe. Codex has no managed citizen lifecycle yet, so a Codex host cannot send without the explicit anonymous hatch below.
15
17
 
16
18
  Prerequisites on the host running the external MCP client:
17
19
 
18
20
  - A live pi session launched with `--entwurf-control` populates `~/.pi/entwurf-control/<gardenId>.sock` — the key is the **record's** garden id, never a transcript/session id (`PI_SESSION_ID` only carries the id record birth already established). Required for `entwurf_v2` control-socket dispatch and `entwurf_peers`.
19
21
 
20
- > **PATH boundary.** MCP servers are often launched by GUI/editor daemons and may not inherit the interactive shell's PATH. No `entwurf_v2` rail launches a process, so this does not affect delivery — but `entwurf_fresh_call` and `entwurf_resume_call` do open a fixed runtime. If that runtime works in your terminal but an external-host call fails with `spawn pi ENOENT` or `spawn claude ENOENT`, pass a full PATH in the MCP server `env`, set `ENTWURF_BRIDGE_ENV_FILE` to a small shell file that exports PATH, or point the host at a wrapper that can find the runtime. `start.sh` sources only the explicit `ENTWURF_BRIDGE_ENV_FILE`; it never reads personal dotfiles automatically.
22
+ > **PATH boundary.** MCP servers are often launched by GUI/editor daemons and may not inherit the interactive shell's PATH. No `entwurf_v2` rail launches a process, so this does not affect delivery — but `entwurf_fresh_call` and `entwurf_resume_call` do open a fixed runtime. If that runtime works in your terminal but an external-host call fails with `spawn pi ENOENT`, `spawn claude ENOENT`, or `spawn entwurf ENOENT`, pass a full PATH in the MCP server `env`, set `ENTWURF_BRIDGE_ENV_FILE` to a small shell file that exports PATH, or point the host at a wrapper that can find the runtime. `start.sh` sources only the explicit `ENTWURF_BRIDGE_ENV_FILE`; it never reads personal dotfiles automatically.
23
+
24
+ > **Identity-carrier boundary.** A complete `PI_SESSION_ID` + `PI_AGENT_ID` pair wins before a
25
+ > native sender marker in the bridge's authoritative-self resolution. Those variables are correct
26
+ > inside the pi process that planted them from record birth; they are foreign identity in another
27
+ > native harness. Starting that harness from a pi citizen's bash can otherwise make the new host —
28
+ > and any internal agents borrowing its MCP manager — speak under the parent pi garden id. Every
29
+ > managed native launcher therefore removes both variables before exec and lets the launched
30
+ > harness establish its own identity through its trusted birth marker. Clearing only one is not a
31
+ > repair: an incomplete pair merely changes the failure wording, while retaining either carrier
32
+ > invites future partial-reader drift. An unmanaged launch that inherits them is unsupported. A
33
+ > native-harness admission made under this contract must add a doctor cell that names this
34
+ > contamination rather than silently preferring the marker.
35
+ >
36
+ > **Certified so far: Copilot only** (#82 RAIL 9). `scripts/copilot-launch.sh` unsets both before
37
+ > `exec`, `check-copilot-launch` asserts their absence from the vendor's OWN report of its
38
+ > environment (and that clearing one alone does not pass), and `doctor-copilot-receive` reports any
39
+ > live Copilot CLI still carrying either as RED on its own axis — a session can be perfectly armed
40
+ > and still speak under the parent's garden id, so the flag verdict must not absorb it. The other
41
+ > native launchers have NOT been certified against this failure mode; do not read Copilot's cells
42
+ > as coverage for them.
21
43
 
22
44
  Example env file:
23
45