@junghanacs/entwurf 0.15.0 → 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.
package/AGENTS.md CHANGED
@@ -51,6 +51,7 @@ For agents that own this repo: invariant principles and reproducible verificatio
51
51
  14. **Native hook ownership is structural.** Claude hooks use the shipped exec-form launcher and provenance token; marker writers/readers share `isPlausibleOwnerPid`; no shell-form fallback, ancestry guess, or retired pid carrier. entwurf requires Claude Code `>=2.1.217` and enforces that floor itself because upstream gives no fail-loud — an older Claude validates the exec manifest, then drops `args` at runtime and reports success. The number is derived from `package.json` `entwurf.claudeCodeFloor`, never retyped as a second source. Currently certified axis is Linux desktop/workstation. Source/gates: `hook-launch.sh`, `meta-session.ts`, `check-hook-launch-topology`, `check-claude-floor-coherence`, `check-meta-doctor-oracle`.
52
52
  15. **Crash, don't warn.** Bad config/path/model/store state throws. Empty catches are only for bounded environment probing; operator diagnostics go to stderr.
53
53
  16. **mux is launch, never delivery.** A tmux window/pane handle is an ephemeral operator view, not an address: it mints no garden id, stores no record, and reports no liveness. Screen text and keystrokes are never an `entwurf_v2` receipt. Mux owns only placement and fixed-runtime launch; fresh identity correlation stays in callback envelopes, while same-id resume identity/liveness/locking stays on the v2 side of an injected launch seam.
54
+ 17. **Entwurf installs itself; setup composes what the operator already chose.** Package installation and source bootstrap supply Entwurf's own bytes, bins, dependencies and development fixtures only. They never install a harness binary, subscription, credential or login — `pi` has no privileged exception. `setup` detects already-present harnesses by capability and completes each detected integration; absence is an explicit zero-state SKIP, while a detected-but-incomplete integration is named non-green rather than cosmetic success. Source-only pinned Pi dev dependencies are build/test fixtures, not a product promise that neutral npm consumers receive Pi. Installation portability and rail/runtime support are separate evidence axes: Linux proof never certifies macOS or native Windows, and WSL counts as Linux rather than Windows.
54
55
 
55
56
  Detailed incident histories belong in CHANGELOG/issues/BASELINE and source-adjacent comments, not in this prompt. When a concise rule and old archaeology disagree, verify source + gate and repair the stale prose.
56
57
 
package/CHANGELOG.md CHANGED
@@ -4,6 +4,76 @@ All notable changes to this project will be documented here. Format follows [Kee
4
4
 
5
5
  ## Unreleased
6
6
 
7
+ ## 0.15.1 — 2026-08-27
8
+
9
+ This patch is the Linux install-honesty floor for #86: Entwurf installs itself only,
10
+ `setup` composes harnesses the operator already chose, and a detected integration that
11
+ cannot be completed makes the command non-green. It is not a new garden capability.
12
+ macOS and native-Windows implementation move to #78; this cut makes no cross-platform
13
+ support claim.
14
+
15
+ ### Changed
16
+
17
+ - **`setup` is presence-driven composition, not recruitment.** Mode is a named
18
+ source-versus-installed branch before any prerequisite check or write. Each detected
19
+ harness (pi, Claude Code, Copilot, agy) is completed independently; absence is
20
+ zero-state SKIP; below-floor or incomplete is named FAIL and a nonzero exit. The old
21
+ unconditional `DONE ... green` is gone — the summary is computed from component
22
+ outcomes. Pi is optional-by-presence with the package-derived floor `>=0.84.3 <0.85`.
23
+ A detected Copilot composes all four native units (birth → MCP → receiver → visible
24
+ footer). Installed-package setup never runs npm/pnpm inside `node_modules`. (#86)
25
+ - **The shared Pi user-scope registration has a recorded owner.** Silent
26
+ last-writer-wins normalization is retired. A second root's install/setup/remove refuses
27
+ with zero settings writes and names `takeover-user-scope`; only that explicit operator
28
+ verb moves the entry (old→new reported). The inverse removes only the recorded owner's
29
+ exact entry. (#86 C2)
30
+ - **Setup no longer mutates credential stores.** The unconditional `sync_auth` path that
31
+ copied the Anthropic OAuth object to an `entwurf` alias is removed. Hosts that already
32
+ ran the legacy path keep the alias and `auth.json.bak` as a documented manual cleanup,
33
+ not an automated one. (#86 A5)
34
+
35
+ ### Added
36
+
37
+ - **Copilot birth has a package-owned inverse.** `uninstall-copilot-bridge` uses the
38
+ qualified `plugin@marketplace` identity, runs a read-only ownership/safety preflight
39
+ before any vendor write, and refuses to remove an unproven foreign unit. Installer,
40
+ inverse, and doctor share one structural oracle. (#86 C3a)
41
+ - **Packed-consumer and all-absent setup rows are first-class fixtures.**
42
+ `smoke-setup-verdict` and the installed `entwurf setup` path inside `check-pack-install`
43
+ prove absence, independence, false-success refusal, and the named installed-versus-source
44
+ branch without a model turn. That npm-consumer harness is the Linux install evidence
45
+ #78 waits on. (#86)
46
+
47
+ ### Fixed
48
+
49
+ - **Source setup now exposes the `entwurf` operator command Copilot fresh requires.**
50
+ `./run.sh setup` manages `~/.local/bin/entwurf` as an ownership-checked symlink to the
51
+ current checkout's `run.sh`, with per-bin state and an honest inverse. npm consumers
52
+ already received this command through package bin linking; the missing source half caused
53
+ `entwurf_fresh_call {backend:"copilot"}` to reject `runtime-unresolved` unless an unrelated
54
+ global package happened to mask the defect. Source setup now fails loud when that
55
+ operator path is foreign, outside PATH, or shadowed; helper units are attempted
56
+ independently and a foreign helper is a named FAIL, never a warning followed by cosmetic
57
+ success. The managed `entwurf copilot` boundary and every fresh/preflight/delivery
58
+ contract are unchanged. Discovered while rechecking #82. (#86)
59
+
60
+ ### Verification
61
+
62
+ - **`pnpm run check:full`** — PASS, exit 0, 252s standalone / 247s inside the gate
63
+ on the 0.15.1 versioned tree (HEAD `4a23af1` + uncommitted changelog/version).
64
+ - **`LIVE=1 ./run.sh release-gate /tmp/entwurf-release-gate-0.15.1.hAAn7U --cut`** —
65
+ **MUST PASS=21 FAIL=0 SKIP=0, BEHAVIOR PASS=1 FAIL=0 SKIP=0, `cut: OK`.**
66
+ `check-gate-qualification` inside the gate killed **277/277** mutants across 33
67
+ lanes. Log: `/tmp/entwurf-release-gate-0.15.1.hAAn7U/release-gate.log`
68
+ (SHA-256 `aa1364922301e685c650670dd6556a3c27feacdf1481d850d8e2ef56ef5cb23e`).
69
+
70
+ ### Notes
71
+
72
+ - #78 owns macOS and native-Windows implementation. WSL remains Linux evidence.
73
+ - Native-harness doctors stay the per-leaf live-certification surface. Setup writes
74
+ artifacts; a new session is still required before `doctor-meta-bridge` / Copilot receive
75
+ can go green on a host that already had processes open.
76
+
7
77
  ## 0.15.0 — 2026-08-25
8
78
 
9
79
  This release admits Copilot CLI as a full self-fetch garden citizen — birth, an owned
package/DELIVERY.md CHANGED
@@ -88,7 +88,7 @@ can be owned without being supported; a supported harness still has to walk
88
88
  | **Codex app-server-backed TUI** | verified probe | D7; D8 unproven | WebSocket-over-UDS `turn/start` into a live `threadId`; status events expose completion. No owned native-citizen install/invocation lane. |
89
89
  | **Codex embedded TUI** | deferred | D0 partial | No supported receive socket/hook on the measured standalone shape. |
90
90
  | **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. |
91
- | **Copilot CLI garden citizen** | branch candidate; send + receive + visible fresh accepted on one host | D6; D7 partial; D3 pending; D8 unproven | Birth, garden id, MCP hand and record-backed sender identity are accepted; the RECEIVER is an installed first-party extension that binds to the V3 record, writes a receiver marker owned by the WATCHER pid, and rings a doorbell the model drains with `entwurf_inbox_read`. `wakeMode` is `self-fetch`, so dispatch reaches the mailbox rail: armed → delivered, unarmed/stale → the honest `mailbox-undeliverable` refusal. **D6 is the owned-invocation LIVE acceptance of 2026-08-23** — garden `20260823T181316-d9f6ba`, native `20fe30c8-b2bc-4600-91a0-8a409131be51`, CLI 1.0.80: receive log `joined`→`armed`→`doorbell fresh=1`→`rang`, mailbox `lastEnqueuedAt 09:23:41.235Z` / `lastReadAt 09:23:56.480Z`, and a model reply on the same record/native/gid chain. **Visible fresh (step 9 clause 7) is a separate LIVE, 2026-08-25** — launch window `@89`/`%89` nonce `mux-fresh-call-690529ae99f99faa2252aefb`; exact-callback garden `20260825T085721-f68be0`; one `entwurf_v2` → `meta-mailbox → enqueued`; same garden `lastReadAt 2026-08-24T23:57:47.784Z` plus same-gid reply; GLG saw footer garden id and a healthy multi-turn window. Those rows stay unmerged. D7 is PARTIAL: reply and read receipt were observed, the completion taxonomy and long-haul operation were not. D3 (second-session isolation of an owned invocation) is PENDING — observed once, decisive log lost to scratch cleanup. Evidence level L4: one host. Launch through the owned invocation `entwurf copilot`, which sets `COPILOT_CLI_ENABLED_FEATURE_FLAGS=EXTENSIONS` for that one process; `doctor-copilot-receive` reads live CLI environments because a session launched without it is silently inert. Visible fresh is operator-metered and is not a release-gate MUST. |
91
+ | **Copilot CLI garden citizen** | shipped in 0.15.0; send + receive + visible fresh accepted on one host | D6; D7 partial; D3 pending; D8 unproven | Birth, garden id, MCP hand and record-backed sender identity are accepted; the RECEIVER is an installed first-party extension that binds to the V3 record, writes a receiver marker owned by the WATCHER pid, and rings a doorbell the model drains with `entwurf_inbox_read`. `wakeMode` is `self-fetch`, so dispatch reaches the mailbox rail: armed → delivered, unarmed/stale → the honest `mailbox-undeliverable` refusal. **D6 is the owned-invocation LIVE acceptance of 2026-08-23** — garden `20260823T181316-d9f6ba`, native `20fe30c8-b2bc-4600-91a0-8a409131be51`, CLI 1.0.80: receive log `joined`→`armed`→`doorbell fresh=1`→`rang`, mailbox `lastEnqueuedAt 09:23:41.235Z` / `lastReadAt 09:23:56.480Z`, and a model reply on the same record/native/gid chain. **Visible fresh (step 9 clause 7) is a separate LIVE, 2026-08-25** — launch window `@89`/`%89` nonce `mux-fresh-call-690529ae99f99faa2252aefb`; exact-callback garden `20260825T085721-f68be0`; one `entwurf_v2` → `meta-mailbox → enqueued`; same garden `lastReadAt 2026-08-24T23:57:47.784Z` plus same-gid reply; GLG saw footer garden id and a healthy multi-turn window. Those rows stay unmerged. D7 is PARTIAL: reply and read receipt were observed, the completion taxonomy and long-haul operation were not. D3 (second-session isolation of an owned invocation) is PENDING — observed once, decisive log lost to scratch cleanup. Evidence level L4: one host. Launch through the owned invocation `entwurf copilot`, which sets `COPILOT_CLI_ENABLED_FEATURE_FLAGS=EXTENSIONS` for that one process; `doctor-copilot-receive` reads live CLI environments because a session launched without it is silently inert. Visible fresh is operator-metered and is not a release-gate MUST. |
92
92
  | **Copilot CLI TUI+server** — *withdrawn lane, kept as evidence* | rejected | D7; D8 unproven | Older official-SDK probe over hidden `--ui-server`; idle enqueue worked, but loopback RPC authentication was not established. The bundled extension supersedes this candidate without reviving it. |
93
93
  | **ACP Claude / Cortex** | shipped runtime, outside this matrix | — | ACP sessions are children launched by entwurf's pi adapter, not already-running native sessions to wake. |
94
94
 
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
@@ -85,9 +94,13 @@ The package exposes six bins:
85
94
 
86
95
  The bridge/renderers/hook use stable bin names so package upgrades do not bake versioned package-store paths into native-harness settings.
87
96
 
88
- The bridge does not provide backend credentials, tokens, or subscription access,
89
- and does not bypass any backend auth. Whatever the operator's local `claude` /
90
- `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.
91
104
 
92
105
  ### From npm — user/global install
93
106
 
@@ -138,12 +151,43 @@ or at the package launcher directly:
138
151
  ```bash
139
152
  git clone https://github.com/junghan0611/entwurf ~/repos/gh/entwurf
140
153
  cd ~/repos/gh/entwurf
141
- pnpm install
142
154
 
143
- ./run.sh install /path/to/your-project
155
+ ./run.sh setup /path/to/your-project
144
156
  ./run.sh check-bridge
145
157
  ```
146
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
+
147
191
  A development clone runs the bridge source through Node's strip-types path;
148
192
  an npm-installed package runs the prebuilt JS under `mcp/entwurf-bridge/dist/`
149
193
  because Node refuses to strip `.ts` files under `node_modules`. The dev launcher's
@@ -170,8 +214,10 @@ For daily operator sessions, launch pi with `--entwurf-control` — no id
170
214
  injection; the meta-record mints the garden address (see [Garden launcher](#garden-launcher)). Older pi
171
215
  versions may silently miss the provider/extension surface, so treat the pi floor
172
216
  as release-critical for the ACP/plugin lane. A host that only uses
173
- `entwurf-bridge` from Claude Code / Codex / Antigravity does not need pi at all for
174
- 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.
175
221
 
176
222
  ### Native harness install and doctors
177
223
 
@@ -193,12 +239,15 @@ entwurf doctor-agy-statusline
193
239
  entwurf doctor-agy-hooks
194
240
 
195
241
  # GitHub Copilot CLI — four independent surfaces, four independent failure modes
196
- entwurf install-copilot-bridge # birth: garden id + who-sent, on the first prompt
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)
197
244
  entwurf install-copilot-mcp # the entwurf tool hand (entwurf_inbox_read lives here)
198
245
  entwurf install-copilot-receive # the receiver extension: doorbell + receiver marker
199
246
  entwurf install-copilot-statusline # optional for a manual citizen; required for supported fresh
200
247
  entwurf doctor-copilot-bridge
248
+ entwurf doctor-copilot-mcp
201
249
  entwurf doctor-copilot-receive
250
+ entwurf doctor-copilot-statusline
202
251
  ```
203
252
 
204
253
  #### Launching Copilot as a garden citizen — `entwurf copilot`
@@ -245,8 +294,8 @@ the default permission prompts — which an idle, unattended session is not ther
245
294
 
246
295
  Claude Code uses the supported floor `>=2.1.217`; older versions silently discard the
247
296
  exec-hook `args`, so install and doctor fail loud rather than falling back. After any
248
- upgrade, rerun the installer for the native harness you use and restart its existing
249
- 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
250
299
  installed artifact makes `doctor-meta-bridge` exit 0 with the live owner join.
251
300
 
252
301
  Linux is the only currently certified Claude meta-bridge axis. New macOS wiring is
@@ -360,7 +409,7 @@ Reference shape lives in [`pi/settings.reference.json`](./pi/settings.reference.
360
409
  ### External MCP registration
361
410
 
362
411
  `entwurf-bridge` can also be registered in a separate MCP-aware harness (Claude Code,
363
- 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:
364
413
 
365
414
  - **plain external MCP host** — no garden meta-record or sender marker. It can read the
366
415
  surfaces, but `entwurf_v2` is **refused by default**: there is no authoritative sender.
@@ -478,7 +527,7 @@ A two-pane recording covers the pre-0.12 v1 surface end-to-end — sibling resum
478
527
 
479
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.)
480
529
 
481
- 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`, `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.
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.
482
531
 
483
532
  ### `entwurf_v2` — canonical dispatch verb
484
533
 
@@ -495,7 +544,7 @@ In ACP-backed and external native-harness sessions, `entwurf-bridge` exposes sev
495
544
  | dead / indeterminate native-push conversation | fire-and-forget | **reject** (`native-push-target-dead` / `native-push-probe-indeterminate`) |
496
545
  | record-less control socket (no meta-record) | any | **reject** (`record-less-socket` — pre-probe; diagnostic state, #50 C4) |
497
546
 
498
- **`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.
499
548
 
500
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.
501
550
 
package/VERIFY.md CHANGED
@@ -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`. 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. 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
 
@@ -92,7 +92,7 @@ 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 |
95
+ | Entwurf package | `0.15.0` | shipped baseline | the package contract these rows belong to |
96
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 |
@@ -9,10 +9,12 @@ only on Linux because its strict live-owner join uses `/proc`.
9
9
  | Component | Requirement | Needed for |
10
10
  |---|---|---|
11
11
  | Node | **`>=24.0.0`** | package and bridge runtime |
12
- | npm | bundled with Node | package installation |
13
- | entwurf | `@junghanacs/entwurf` | all lanes |
14
- | pi | optional, `@earendil-works/pi-coding-agent >=0.84.3 <0.85` | ACP provider, control sockets |
12
+ | npm/pnpm | npm is bundled with Node; pnpm is required for source setup | package or source installation |
13
+ | Python 3 | required by `setup`/`install` (project-path normalization + settings writers); `--help`/`check-bridge` stay Python-free | pi/Claude/agy/Copilot wiring writers |
14
+ | entwurf | global/project-local `@junghanacs/entwurf`, or a source checkout | operator command and garden capability |
15
+ | pi | optional-by-presence, `>=0.84.3 <0.85` — absent is an explicit setup SKIP, below-floor is a named FAIL | ACP provider, control sockets |
15
16
  | Claude Code | optional, **`>=2.1.217`** — the exec-form hook floor | Claude ACP auth/runtime and mailbox-backed native citizen |
17
+ | GitHub Copilot CLI | optional-by-presence, operator-installed and authenticated — absent is an explicit setup SKIP; detected composes all four units (birth/MCP/receiver/footer) | self-fetch citizen and visible fresh |
16
18
  | Antigravity `agy` | optional, operator-installed and authenticated | native-push citizen |
17
19
  | Cortex Code | optional, operator-installed and authenticated | Cortex ACP backend |
18
20
 
@@ -51,6 +53,56 @@ npx entwurf check-bridge
51
53
  `check-bridge` is auth-free. It proves the installed prebuilt MCP server boots and
52
54
  lists the seven garden tools; it does not prove a backend model turn or native hook.
53
55
 
56
+ Neither npm form installs a harness runtime. `pi`, Claude Code, Copilot CLI, agy, Cortex and their
57
+ authentication remain operator-owned optional prerequisites for the integrations that use them;
58
+ all may be absent on an Entwurf-only host. A source checkout's pinned Pi development packages are
59
+ for building and testing this repo, not a transitive product installation promise.
60
+
61
+ Maintainers using a source checkout do not install a second global entwurf package. Full source
62
+ setup currently requires Node 24, pnpm, and Python 3 on PATH; every harness — including pi — is
63
+ optional-by-presence (absent → explicit SKIP, detected but below the supported floor → named FAIL
64
+ with a nonzero setup result):
65
+
66
+ ```bash
67
+ git clone https://github.com/junghan0611/entwurf ~/repos/gh/entwurf
68
+ cd ~/repos/gh/entwurf
69
+ ./run.sh setup /path/to/consumer-project
70
+ ```
71
+
72
+ This owns `~/.local/bin/entwurf` as a symlink to that checkout's `run.sh` and fails if the
73
+ link is foreign, outside PATH, or shadowed by another command. It detects and wires
74
+ pi/Claude/agy/Copilot by presence and prints a computed per-component PASS/SKIP/FAIL summary — a
75
+ detected harness that cannot be completed makes setup exit nonzero. A detected `copilot`
76
+ composes all four native units (birth → MCP → receiver → visible footer) with independent
77
+ per-unit verdicts (#86 C3b); §4 keeps the explicit per-unit install/doctor/inverse surfaces for
78
+ repair.
79
+
80
+ ### 1.1 User-scope ownership (one shared registration, one recorded owner)
81
+
82
+ The GLOBAL pi user-scope registration (`~/.pi/agent/settings.json` `packages[]` plus the
83
+ `entwurfProvider.mcpServers.entwurf-bridge` key) is ONE shared entry across every install root, and
84
+ it carries a recorded owner: `packageRoot` in `$XDG_DATA_HOME/entwurf/pi-package/install-state.json`
85
+ and `installerRoot` in the provider install-state. Normal `install`/`setup` from a different root —
86
+ whether the recorded owner is live or missing — refuses with zero settings bytes written; the only
87
+ writer that moves the shared entry is the operator-explicit `entwurf takeover-user-scope`
88
+ (old→new reported). Every user-scope operation is ATOMIC across the two halves: both ownership
89
+ preflights run read-only first, so a refusal on either side leaves the other byte-identical. A
90
+ takeover over an operator's own provider override is a SPLIT verdict — the package owner moves,
91
+ the override is preserved and stays unowned (its stale ownership state is cleared), never a false
92
+ "both owned". A LEGACY provider state (no `installerRoot`) accepts no inverse: run `setup`/`install`
93
+ from the owning root first (named adoption), then remove. Both install-states also bind the exact
94
+ settings file they manage (`managedSettingsPath`): pointing an operation at a different, symlinked
95
+ or unparseable file refuses with zero writes before either half proceeds, and the owned/orphan
96
+ inverse removes only the recorded owner's exact `packages[]` entry (0 or 2+ exact entries refuse).
97
+ `entwurf doctor-pi-package` names the package-side verdict including the
98
+ packageRoot↔installerRoot coupling mismatch and a package/provider managed-path mismatch.
99
+
100
+ | Root shape | Package root written | Stable commands from | Project write | User write | Takeover trigger | Inverse | Stale/moved verdict |
101
+ |---|---|---|---|---|---|---|---|
102
+ | source checkout | the checkout dir | `dev-bin` symlinks (`setup`) | `<project>/.pi/settings.json` | shared entry + owner state | `takeover-user-scope` from the new checkout | `remove` (project) / `remove-user-scope` (global, same-owner-only) | `doctor-pi-package` → `missing-owner`; normal install still refuses |
103
+ | global npm (`npm i -g`) | the global `node_modules/@junghanacs/entwurf` | npm bin linking | same | same shared entry | same explicit action | same; a LIVE foreign owner always refuses | same |
104
+ | project-local npm | that project's `node_modules/@junghanacs/entwurf` | `node_modules/.bin` | same | same shared entry | same explicit action | same | same; a deleted root becomes the aligned `remove-user-scope` orphan cleanup (entry + package state + provider installerRoot must all name that missing root) |
105
+
54
106
  ## 2. Optional pi adapter / ACP plugin
55
107
 
56
108
  Install the exact release floor, then wire the project:
@@ -109,7 +161,31 @@ New macOS wiring is refused because the live join is not instrumented there. Dar
109
161
  uninstall remains available for cleaning an older managed install; this is an evidence
110
162
  boundary, not a permanent impossibility claim.
111
163
 
112
- ## 4. Optional Antigravity native citizen
164
+ ## 4. Optional GitHub Copilot CLI native citizen
165
+
166
+ Copilot has four independently owned surfaces. `setup` composes all four when `copilot` is on
167
+ PATH (#86 C3b); the commands below are the per-unit repair, doctor, and inverse surfaces. All
168
+ four must be green for supported visible fresh; a manual citizen may omit the footer, but fresh
169
+ refuses before opening a window when any required surface is absent.
170
+
171
+ ```bash
172
+ entwurf install-copilot-bridge
173
+ entwurf install-copilot-mcp
174
+ entwurf install-copilot-receive
175
+ entwurf install-copilot-statusline
176
+
177
+ entwurf doctor-copilot-bridge
178
+ entwurf doctor-copilot-mcp
179
+ entwurf doctor-copilot-receive
180
+ entwurf doctor-copilot-statusline
181
+ ```
182
+
183
+ Launch the supported invocation with `entwurf copilot`, not bare `copilot`. It enables extension
184
+ scanning for that process, checks the receiver, removes inherited pi identity carriers, and owns
185
+ the model/permission defaults. Birth occurs on the first prompt. `entwurf_fresh_call` uses this
186
+ same managed invocation and requires the birth, MCP, receiver, and visible-identity preflight.
187
+
188
+ ## 5. Optional Antigravity native citizen
113
189
 
114
190
  Install the three independently owned surfaces:
115
191
 
@@ -134,7 +210,7 @@ Real native-push acceptance needs an already-running conversation:
134
210
  LIVE=1 AGY_CONVERSATION_ID=<id> entwurf smoke-agy-native-push-live
135
211
  ```
136
212
 
137
- ## 5. Optional ACP backend turns
213
+ ## 6. Optional ACP backend turns
138
214
 
139
215
  Claude uses the operator's existing local Claude authentication:
140
216
 
@@ -153,25 +229,34 @@ LIVE=1 ENTWURF_ACP_CORTEX_CONNECTION=<conn> \
153
229
  The aggregate release gate is Claude-backed and does not run Cortex automatically.
154
230
  Its silence is not a Cortex PASS.
155
231
 
156
- ## 6. Upgrade and repair
232
+ ## 7. Upgrade and repair
157
233
 
158
234
  After upgrading the package, rerun the managed installers for every native harness
159
235
  in use and restart their existing processes. Native plugin caches are not live-reload
160
236
  safe across launch-contract changes.
161
237
 
162
238
  If install or doctor reports an unreadable/old active citizen generation, do not edit
163
- records by hand:
239
+ records by hand. Close pi, Claude, Copilot, and agy sessions first, run
240
+ `entwurf meta-bridge-fresh-cut`, and read its exit status before any install. Then choose the
241
+ installation mode you actually own:
164
242
 
165
243
  ```bash
166
- # close pi, Claude, and agy sessions first
167
- entwurf meta-bridge-fresh-cut
168
- entwurf setup ~/entwurf-smoke
244
+ # npm package consumer
245
+ entwurf install ~/entwurf-smoke
246
+ entwurf install-meta-bridge
247
+ # `entwurf setup` re-composes the four Copilot units when `copilot` is on PATH
248
+
249
+ # source maintainer — from the checkout
250
+ ./run.sh setup ~/entwurf-smoke
169
251
  ```
170
252
 
171
- Read the cut's exit status before chaining setup. The complete quiescence, archive,
172
- and exit-code contract is [fresh-cut-policy.md](./fresh-cut-policy.md).
253
+ The package-installed `entwurf setup` is the same consumer command in installed mode: it names
254
+ that mode first, never runs npm/pnpm inside `node_modules` (the frozen pnpm bootstrap is
255
+ source-checkout-only), and composes the detected harnesses with the same per-component
256
+ PASS/SKIP/FAIL summary. The complete quiescence, archive, and exit-code contract is
257
+ [fresh-cut-policy.md](./fresh-cut-policy.md).
173
258
 
174
- ## 7. Release acceptance versus host acceptance
259
+ ## 8. Release acceptance versus host acceptance
175
260
 
176
261
  - `entwurf check-bridge`: installed MCP bytes boot; no backend auth.
177
262
  - `pnpm check` / `pnpm run check:full`: tiered source deterministic floors (everyday
@@ -190,12 +275,27 @@ Run only the surfaces this host owns:
190
275
 
191
276
  ```bash
192
277
  entwurf uninstall-meta-bridge
278
+ entwurf uninstall-copilot-statusline
279
+ entwurf uninstall-copilot-receive
280
+ entwurf uninstall-copilot-mcp
281
+ entwurf uninstall-copilot-bridge
193
282
  entwurf uninstall-agy-hooks
194
283
  entwurf uninstall-agy-statusline
195
284
  entwurf uninstall-agy-bridge
196
- entwurf uninstall ~/entwurf-smoke
285
+ entwurf remove ~/entwurf-smoke
286
+ # only when no other project uses the shared user-scope pi registration:
287
+ entwurf remove-user-scope
197
288
  npm uninstall -g @junghanacs/entwurf
198
289
  ```
199
290
 
200
- Each managed surface has an honest inverse and preserves unrelated native-harness
201
- configuration.
291
+ The package `uninstall-*`/`remove` surfaces preserve unrelated native-harness configuration.
292
+ Copilot birth now has a package-owned inverse: `uninstall-copilot-bridge` removes exactly what
293
+ its install-state (`$XDG_DATA_HOME/entwurf/copilot-bridge/install-state.json`) records — the
294
+ qualified plugin, the local marketplace registration when it is owned and still at the recorded
295
+ path, and the recorded assembly — never with `--force`, never a bare plugin name that could
296
+ match somebody else's unit, and never the stale Claude unit. The complete ownership preflight
297
+ runs read-only before the first vendor write, so a marketplace under our name at another path,
298
+ a registration the state does not own, or a failing vendor list (UNKNOWN, never absence)
299
+ refuses the whole inverse with zero writes; the state is deleted last, so a partial failure
300
+ keeps a rerun-repair authority.
301
+ A legacy no-state installation is adopted by re-running `install-copilot-bridge` first.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@junghanacs/entwurf",
3
- "version": "0.15.0",
3
+ "version": "0.15.1",
4
4
  "description": "Garden-citizen dispatch substrate and meta-bridge for Claude Code, Codex, Antigravity, and pi harnesses.",
5
5
  "license": "MIT",
6
6
  "author": {
@@ -156,7 +156,7 @@
156
156
  "check:vitest": "./run.sh check-mux-fresh-call && ./run.sh check-acp-sdk-surface",
157
157
  "check:contracts": "./run.sh check-shell-quote && ./run.sh check-entwurf-session-identity && ./run.sh check-meta-session && ./run.sh check-meta-v3-record && ./run.sh check-mailbox-receipt-state && ./run.sh check-entwurf-capabilities && ./run.sh check-capability-bundle-reach && ./run.sh check-meta-mailbox-state-write && ./run.sh check-meta-receiver-marker && ./run.sh check-meta-capability-source && ./run.sh check-model-lock && ./run.sh check-dep-versions && ./run.sh check-node-floor-coherence && ./run.sh check-claude-floor-coherence && ./run.sh check-copilot-statusline && ./run.sh check-pi-import-surface && ./run.sh check-env-namespace && ./run.sh check-pi-runtime-version && ./run.sh check-pi-preflight && ./run.sh check-project-trust-handler && ./run.sh check-entwurf-v2-contract && ./run.sh check-entwurf-v2-lock && ./run.sh check-entwurf-v2-decider && ./run.sh check-entwurf-v2-matrix && ./run.sh check-entwurf-v2-release && ./run.sh check-entwurf-v2-send && ./run.sh check-entwurf-v2-send-fallback && ./run.sh check-entwurf-v2-mailbox && ./run.sh check-entwurf-v2-native-push && ./run.sh check-entwurf-v2-runner && ./run.sh check-entwurf-v2-production && ./run.sh check-entwurf-v2-surface && ./run.sh check-entwurf-bridge-pi-free && ./run.sh check-entwurf-resume-args && ./run.sh check-resume-launch-identity && ./run.sh check-mux-placement && ./run.sh check-mux-launch && ./run.sh check-mux-resume-call && ./run.sh check-mux-parent-artifact && ./run.sh check-mux-launcher-fence && ./run.sh check-entwurf-v2-visible-resume && ./run.sh check-entwurf-facts && ./run.sh check-control-socket-path && ./run.sh check-socket-discovery && ./run.sh check-meta-listing && ./run.sh check-entwurf-fact-provider && ./run.sh check-entwurf-peers-surface && ./run.sh check-entwurf-self-address && ./run.sh check-entwurf-deliverability && ./run.sh check-native-push-adapter && ./run.sh check-native-push-register && ./run.sh check-auth-boundary && ./run.sh check-acp-overlay && ./run.sh check-acp-tool-surface && ./run.sh check-acp-event-mapper && ./run.sh check-acp-prompt-builder && ./run.sh check-acp-config && ./run.sh check-acp-session-store && ./run.sh check-acp-carrier-augment",
158
158
  "check:hermetic": "./run.sh check-bridge-delivery && ./run.sh smoke-pi-attach && ./run.sh check-fresh-cut-gate && ./run.sh check-hook-launch-topology && ./run.sh check-copilot-birth-hook && ./run.sh check-copilot-receive-arm && ./run.sh check-copilot-launch && ./run.sh check-meta-identity-consumers && ./run.sh check-socket-probe && ./run.sh smoke-meta-honesty && ./run.sh check-meta-doctor-oracle && ./run.sh check-agy-permission-matrix && ./run.sh smoke-agy-statusline-state && ./run.sh smoke-copilot-statusline-state && ./run.sh smoke-copilot-mcp-state && ./run.sh smoke-agy-hooks-state && ./run.sh smoke-pi-provider-state && ./run.sh smoke-user-scope-citizen && ./run.sh smoke-meta-prune && ./run.sh smoke-meta-keyset-guard && ./run.sh check-meta-manifest-schema && ./run.sh check-entwurf-control-rpc && ./run.sh check-entwurf-bridge-boot && ./run.sh check-probe-bridge-command && ./run.sh check-meta-facts && ./run.sh check-agy-sender-identity && ./run.sh check-acp-provider-surface && ./run.sh check-acp-stop-reason && ./run.sh check-acp-prompt-lifecycle && ./run.sh check-acp-stream-hooks && ./run.sh check-acp-backend-preflight && ./run.sh check-acp-session-reuse && ./run.sh check-release-gate-outcomes && ./run.sh check-probe-ordering && ./run.sh check-probe-cli-shim && ./run.sh check-acp-cortex",
159
- "check:package": "./run.sh check-install-surface && ./run.sh smoke-meta-install-state && ./run.sh smoke-agy-install-state && ./run.sh check-pack-pin-matcher && ./run.sh check-package-source-routing && ./run.sh check-install-preflight && ./run.sh check-pack"
159
+ "check:package": "./run.sh check-install-surface && ./run.sh smoke-meta-install-state && ./run.sh smoke-agy-install-state && ./run.sh smoke-setup-verdict && ./run.sh check-pack-pin-matcher && ./run.sh check-package-source-routing && ./run.sh check-install-preflight && ./run.sh check-pack"
160
160
  },
161
161
  "pi": {
162
162
  "extensions": [