@junghanacs/entwurf 0.15.1 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/CHANGELOG.md +229 -0
  2. package/DELIVERY.md +3 -2
  3. package/README.md +70 -87
  4. package/VERIFY.md +4 -1
  5. package/docs/acp-backend-rail.md +1 -1
  6. package/docs/external-mcp-host.md +147 -9
  7. package/docs/setup-clean-host.md +105 -0
  8. package/mcp/entwurf-bridge/dist/mcp/entwurf-bridge/src/index.js +65 -20
  9. package/mcp/entwurf-bridge/dist/pi-extensions/entwurf-capabilities.json +1 -0
  10. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-self-address.js +17 -6
  11. package/mcp/entwurf-bridge/dist/pi-extensions/lib/meta-sender-identity.js +1 -1
  12. package/mcp/entwurf-bridge/dist/pi-extensions/lib/meta-session.js +172 -5
  13. package/mcp/entwurf-bridge/dist/pi-extensions/lib/mux-fresh-call.js +152 -17
  14. package/mcp/entwurf-bridge/dist/pi-extensions/lib/omp-fresh-preflight.js +271 -0
  15. package/mcp/entwurf-bridge/dist/pi-extensions/meta-bridge-omp.js +988 -0
  16. package/mcp/entwurf-bridge/dist/pi-extensions/meta-bridge-receive-omp.js +574 -0
  17. package/mcp/entwurf-bridge/dist/scripts/omp-receive-facts.js +84 -0
  18. package/mcp/entwurf-bridge/src/index.ts +65 -19
  19. package/mcp/entwurf-bridge/tsconfig.build.json +19 -3
  20. package/mcp/tsconfig.json +10 -0
  21. package/package.json +13 -3
  22. package/pi/entwurf-capabilities.json +1 -0
  23. package/pi/meta-bridge-omp/entwurf-meta-omp/package.json +7 -0
  24. package/pi/omp-receive/entwurf-receive-omp/package.json +7 -0
  25. package/pi-extensions/entwurf-control.ts +12 -9
  26. package/pi-extensions/lib/entwurf-self-address.ts +18 -7
  27. package/pi-extensions/lib/meta-sender-identity.ts +1 -1
  28. package/pi-extensions/lib/meta-session.ts +219 -5
  29. package/pi-extensions/lib/mux-fresh-call.ts +171 -17
  30. package/pi-extensions/lib/omp-fresh-preflight.ts +275 -0
  31. package/pi-extensions/meta-bridge-omp.ts +1244 -0
  32. package/pi-extensions/meta-bridge-receive-omp.ts +666 -0
  33. package/run.sh +296 -4
  34. package/scripts/check-entwurf-self-address.ts +31 -0
  35. package/scripts/check-gate-qualification.ts +4 -1
  36. package/scripts/check-harness-admission-parity.ts +143 -0
  37. package/scripts/check-meta-doctor-oracle.sh +20 -0
  38. package/scripts/check-omp-birth-hook.ts +1049 -0
  39. package/scripts/check-omp-fresh-preflight.ts +181 -0
  40. package/scripts/check-omp-receive-arm.ts +516 -0
  41. package/scripts/inventory-verification-surface.ts +1 -1
  42. package/scripts/meta-bridge-hook-log.sh +9 -1
  43. package/scripts/mutants/copilot-birth.json +7 -5
  44. package/scripts/mutants/mux-fresh-call.json +22 -22
  45. package/scripts/mutants/omp-birth.json +173 -0
  46. package/scripts/mutants/omp-fresh.json +300 -0
  47. package/scripts/mutants/omp-receive.json +135 -0
  48. package/scripts/mutants/self-address.json +34 -0
  49. package/scripts/omp-bridge-doctor.sh +315 -0
  50. package/scripts/omp-bridge-install.sh +221 -0
  51. package/scripts/omp-bridge-oracle.sh +154 -0
  52. package/scripts/omp-bridge-uninstall.sh +57 -0
  53. package/scripts/omp-mcp-bridge.sh +320 -0
  54. package/scripts/omp-mcp-config.py +392 -0
  55. package/scripts/omp-receive-doctor.sh +246 -0
  56. package/scripts/omp-receive-facts.ts +106 -0
  57. package/scripts/omp-receive-install.sh +228 -0
  58. package/scripts/omp-receive-uninstall.sh +60 -0
  59. package/scripts/omp-tool-surface.py +349 -0
  60. package/scripts/raw-omp-measure/README.md +420 -0
  61. package/scripts/raw-omp-measure/probe-extension.ts +76 -0
  62. package/scripts/raw-omp-measure/probe-receive-surface.ts +250 -0
  63. package/scripts/raw-omp-measure/source-audit.md +414 -0
  64. package/scripts/smoke-omp-bridge-state.sh +221 -0
  65. package/scripts/smoke-omp-fresh-live.ts +497 -0
  66. package/scripts/smoke-omp-mcp-state.sh +327 -0
  67. package/scripts/smoke-omp-receive-live.ts +340 -0
  68. package/scripts/smoke-omp-receive-state.sh +196 -0
  69. package/scripts/tsconfig.json +2 -0
package/CHANGELOG.md CHANGED
@@ -4,6 +4,235 @@ All notable changes to this project will be documented here. Format follows [Kee
4
4
 
5
5
  ## Unreleased
6
6
 
7
+ ## 0.16.0 - 2026-08-31
8
+
9
+ This release admits **OMP (`omp`) as the fifth garden backend** and closes the admission
10
+ contract that let a citizen ship without the legs that make it reachable. Bundles A, B and C
11
+ are one arc — birth and identity, addressed receive, visible fresh — and the durable half is
12
+ the general rule they forced: from OMP onward, the FIRST release of a harness admitted under
13
+ the #82 contract owes its clause 7 visible-fresh receipt as a release-gate MUST step, its
14
+ citizen↔fresh parity edge inside `pnpm run check:full`, and two cross-harness dispatch
15
+ receipts. Every OMP LIVE claim below is one Linux host, one model, one accepted run; that
16
+ limit is stated once in Notes and is not softened anywhere above it. (#87)
17
+
18
+ ### Added
19
+
20
+ - **OMP is an addressable garden citizen.** `omp` enters the backend registry (`wakeMode`
21
+ `self-fetch`, `deliveryLevel` `D6`, `nativeIdLabel` `sessionId`) and
22
+ `pi-extensions/meta-bridge-omp.ts` mints it: an omp hook IS an in-process extension, so
23
+ birth, the scope fence, the status line and the sender marker are one module. Both birth
24
+ edges are bound — `session_switch` is where `/new`, fork and in-TUI resume re-fire — and
25
+ every mode but `mode === "tui"` is refused before any write. The fence is the vendor's own
26
+ mode and nothing else: `hasUI` is true under rpc/rpc-ui/ACP, so a fence built on it would
27
+ admit exactly the sessions this refuses. A task subagent mints nothing. The step-6 join is
28
+ one process, so the marker is keyed to `process.pid`, never `process.ppid`. Gates:
29
+ `check-omp-birth-hook`, `smoke-omp-bridge-state`, `scripts/mutants/omp-birth.json`. (#87)
30
+ - **An omp-native MCP hand that shadows the borrowed Claude import.** The writer targets omp's
31
+ own `<omp agent dir>/mcp.json` in the vendor's own writer shape, and the server key is a
32
+ PINNED LITERAL `entwurf-bridge` — that pin is the entire mechanism. On a host that ever used
33
+ Claude Code, omp already imports an `entwurf-bridge` server labeled
34
+ `external-mcp/claude-code`; native provider priority beats the import and dedupe is
35
+ first-wins on the key with `equivalent()` never consulted, so a byte-identical key suppresses
36
+ the import while a different key would load BOTH and one of them would keep introducing an
37
+ omp session as Claude Code. `disabledServers` is never the hide-import tool — suppression is
38
+ by name and a suppressed item still claims the key — so the writer refuses to install under a
39
+ denylist of its own key and the doctor is red while one exists. `doctor-omp-mcp` adds an
40
+ EFFECTIVE-source read (native-wins / import-wins / both-suppressed / no-entry) and labels it
41
+ what it is: a configuration read of vendor precedence, never a runtime receipt. Gate:
42
+ `smoke-omp-mcp-state`. (#87)
43
+ - **OMP receives addressed messages — the garden is no longer one-way for it.**
44
+ `pi-extensions/meta-bridge-receive-omp.ts` joins the citizen in the SAME process, holds an
45
+ `fs.watch` on its mailbox signal, and rings an announce-only doorbell through the vendor's
46
+ `pi.sendUserMessage` — measured to live on the factory object rather than the event ctx, and
47
+ measured to start a turn on an idle host with zero typing. The model drains with
48
+ `entwurf_inbox_read`, and that read is the receipt. The `/new` unarm is rail-specific: the
49
+ watch lives inside the operator's TUI, where pid plus start-key cannot see a citizen change
50
+ underneath a living process, so the old garden id is explicitly unarmed and the replacement
51
+ armed. Bounded arm defer, watch-error, vanished-signal and overlapping-edge paths are all
52
+ fail-closed. Install, uninstall and doctor surfaces ship with it, plus
53
+ `check-omp-receive-arm` with `scripts/mutants/omp-receive.json`, `smoke-omp-receive-state`
54
+ and `smoke-omp-receive-live`. (#87)
55
+ - **`entwurf_fresh_call` opens omp on all three public surfaces.** A five-axis pre-mutation
56
+ preflight decides the launch before the tmux window exists, so a missing prerequisite is a
57
+ named refusal instead of a dead window; the fifth axis is omp-specific (`tools.xdev !==
58
+ true`). There is NO positional prompt. The fixed registered flag `--entwurf-bootstrap`
59
+ carries a closed `{v,target,nonce,task}` grammar — an unknown key is a refusal — and the
60
+ installed birth extension runs a two-stage bootstrap: bounded readiness polling over both
61
+ public tool snapshots, a callback-ONLY prompt, then the task released only by the exact
62
+ successful `tool_result` (stored `toolCallId`, tool name, target, nonce and
63
+ `isError === false` all matching) and delivered at that same session's next `turn_end`. That
64
+ shape is a measurement, not a preference: the positional candidate opened its window, minted
65
+ its citizen, received the framing byte-identical, and answered the literal text `ACK` with
66
+ zero tool calls — the vendor's interactive UI defers MCP discovery while the positional
67
+ `initialMessage` prompts straight after `mode.init()`, so the turn began roughly 830ms before
68
+ the tool it was told to call existed. (#87)
69
+ - **`check-harness-admission-parity` — the missing edge between two closed loops.** Every
70
+ backend in `META_CITIZEN_BACKENDS` must appear in `FRESH_CALL_BACKENDS` or be a declared
71
+ pre-#82 legacy admission whose exception a reader can find in `DELIVERY.md`. Registry↔citizens
72
+ and surfaces↔fresh-set each already had a guard, but no file imported both constants, which is
73
+ how omp could pass the entire floor as a D6 citizen that `entwurf_fresh_call` cannot open. The
74
+ gate went red on `Unaccounted: omp` the moment it was added and green when Bundle C closed it.
75
+ It runs inside `check:contracts`, and therefore inside `pnpm run check:full`. (#87)
76
+ - **Two OMP LIVE steps in the release-gate MUST tier.** `smoke-omp-fresh-live` is clause 7's
77
+ worked instance. `smoke-omp-receive-live` reads the capability registry and decides its own
78
+ outcome — no drainable mailbox is a protocol SKIP, which `--cut` reads as red; a registry
79
+ claiming a receive rail with no acceptance body here is a FAIL. Neither is a hardcoded pass,
80
+ and the receive step was wired while omp was still outbound-only, so the one-way boundary
81
+ stopped being prose a cut could pass over in silence. (#87)
82
+ - **`docs/adding-a-harness.md` gains the shipped map and the release stop.** A comparative table
83
+ of the five admitted backends — lineage, receive rail, callback dialect, fresh launch form —
84
+ sits at the top so the variety is expected rather than re-derived at each admission. It pins
85
+ two lessons: lineage does not choose the rail (omp is pi underneath and rides Claude's mailbox
86
+ shape, because the rail follows the measured wake surface; what ancestry does instead is
87
+ concentrate the danger, since a shared env vocabulary is exactly where a fork splits a store),
88
+ and the doorbell doctrine — a hard bridge of measured dialects, explicit doorbells and honest
89
+ rejects is what makes the thing on the other side a peer rather than a disposable worker. The
90
+ release stop then states both executable halves of the admission rule plus the cross-harness
91
+ leg. (#87)
92
+
93
+ ### Changed
94
+
95
+ - **`setup` is the install story on all three entry shapes.** npm global, npm project-local and
96
+ source clone now lead with `entwurf setup <project>`; `entwurf install <project>` is stated as
97
+ the narrower pi-wiring repair leaf that composes no harness. The 14-line native-harness paste
98
+ that read as the install recipe is now "repair and doctors" — one paragraph plus the command
99
+ names, with every per-unit installer, doctor and inverse retained, because a single broken unit
100
+ must still be redoable alone. The user-scope ownership matrix and the manual MCP registration
101
+ paths moved to the documents that already own them. Stale live claim corrected: after an
102
+ upgrade the operator was told to rerun every owned installer; `setup` re-composes them.
103
+ `package.json` gains the `copilot` keyword, shipped since 0.15.0 but unlisted. (#86)
104
+ - **OMP meta roots resolve as one indivisible bundle, never through `piAgentDir()`.** omp is a pi
105
+ fork and inherits pi's env vocabulary, so `PI_CODING_AGENT_DIR` means "pi's persistence root"
106
+ to entwurf and "my agent dir" to the vendor — a plain `omp --profile work` sets it. One pure
107
+ resolver leaf now serves both consumers, the in-process birth extension and the omp-labeled
108
+ bridge child, so agreement is by construction rather than two places computing the same thing.
109
+ A relative override fails closed instead of letting cwd become an authority: measured, the same
110
+ relative value resolved to two different stores because the extension's cwd is wherever the
111
+ operator launched omp while the doctor's is the repository. Installers refuse rather than guess
112
+ under an inherited `PI_CODING_AGENT_DIR` / `PI_CONFIG_DIR` / `PI_PROFILE`. (#87)
113
+ - **`PI_SESSION_ID` and `PI_AGENT_ID` are scrubbed at the launch seam for every backend.**
114
+ Measured on the operator host: a tmux server env carrying `PI_SESSION_ID` was inherited
115
+ verbatim by a new pane, so a sibling's bridge child could phone home under another citizen's
116
+ identity. The scrub is at the seam, not per backend. (#87)
117
+ - **The deterministic floor composes the omp gates.** `check:hermetic` gains
118
+ `smoke-omp-bridge-state`, `smoke-omp-mcp-state`, `smoke-omp-receive-state`,
119
+ `check-omp-receive-arm` and `check-omp-fresh-preflight`; `check:contracts` gains
120
+ `check-harness-admission-parity`. Both tiers still exclude the separately scheduled
121
+ `check-gate-qualification`, and `setup` still does not compose omp. (#87)
122
+ - **`docs/adding-a-harness.md` step 5 now says to measure the invocation FORM, not only the tool
123
+ name.** omp mounts MCP tools as `xd://` virtual devices by default (`tools.xdev`), so a correct
124
+ tool NAME is still not a callable function: the model must read the device for a schema that
125
+ `tools.xdevDocs` keeps off-prompt, then write JSON to the same path to execute. Measured on omp
126
+ 18.0.0, a plain send under that default listed peers and then reported delivery with nothing
127
+ enqueued, and the mount also hid omp's own lsp, debug, browser and ast_edit — 11 devices in
128
+ all. `tools.xdev: false` restores every enabled tool top-level and leaves plan mode's
129
+ `xd://propose` finalization intact. (#87)
130
+
131
+ ### Fixed
132
+
133
+ - **The bridge boot entry was in no typecheck program.** `mcp/tsconfig.json` extended the root
134
+ config without re-declaring `exclude`, so the inherited `"mcp"` resolved to `<repo>/mcp` and
135
+ filtered out everything its own `include` added; `mcp/entwurf-bridge/src/index.ts` — the
136
+ 785-line bridge boot entry — was the only tracked `.ts` in the repo in no `tsc --noEmit`
137
+ program at all, and nothing imports it, so nothing rescued it through the import graph. Orphan
138
+ census 1 line before, 0 after; the mcp program's repo-file count moved 27 → 54, all product
139
+ sources. The emit path is untouched, so there is no product runtime change.
140
+ - **`entwurf_self` stopped rendering a mailbox nothing drains.** `metaDeliveryDomain` was derived
141
+ as native-push or self-fetch, so an omp citizen was handed a `mailboxPath` with no drainer. It
142
+ now has three values: `nativePushSupported` → native-push, else the decider's mailbox seam →
143
+ self-fetch, else none. Dispatch and `wakeMode` are untouched. (#87)
144
+ - **`doctor-omp-mcp` reads `tools.xdev` on the runtime axis.** An absent file or key applies the
145
+ vendor default (xdev on, empty inline allowlist) and is RED while the native hand is the
146
+ effective source; `xdev: false` is ok and a covering `xdevInlineDevices` glob is
147
+ ok-with-note. Ownership stays a separate question, and the smoke never touches the host
148
+ `config.yml`. (#87)
149
+ - **The bootstrap epoch now ends before the birth edge can bail.** Epoch termination depended on
150
+ reaching `startOmpBootstrap`, so a later birth edge that bailed early — a refused envelope or a
151
+ throwing upsert — skipped the invalidation, and on a same-id resume both handler fences still
152
+ passed, leaving the earlier defect shape alive in a narrow window. One rule, one owner:
153
+ `endBootstrapEpochOnLaterEdge` runs right after the mode fence, before the envelope is read,
154
+ and the consumed branch delegates to the same helper. Zero new state, zero new exports. (#87)
155
+ - **The omp-fresh qualification manifests were settled by measurement.** The first standalone
156
+ qualification run on the Bundle C bytes caught what static exactness could not: four mutants
157
+ whose claim and signature named different cells, one QK token minted twice in its gate source,
158
+ twelve find hunks left stale by the bundle's own refactor and the amendment's rewiring, and one
159
+ kill-site guess that died at an anonymous cell. Every repair was adjudicated by
160
+ apply/gate/revert measurement rather than by guess, and kill sites the floor disputed were
161
+ moved to where the kill actually lands. Separately, two mutants had been added to the
162
+ self-address lane without extending `EXPECTED_LANE_MUTANTS` — exactly the drift that assertion
163
+ exists to catch; the declared contract now says 5. (#87)
164
+ - **Two packaging omissions in the omp units.** `pi/meta-bridge-omp/entwurf-meta-omp/package.json`
165
+ and `pi/omp-receive/entwurf-receive-omp/package.json` were absent from `files[]`, so
166
+ `install-omp-receive` died in the installed package while passing from a checkout. (#87)
167
+ - **`check-pack-install` pins the transitive `@earendil-works/pi-telemetry` to 0.84.3.** Upstream
168
+ published the 0.84.4 family on 2026-08-28; `pi-agent-core@0.84.3` and `pi-ai@0.84.3` both carry
169
+ a `^0.84.3` caret on it, so the lockfile-less fresh-temp install floated to 0.84.4 and the leak
170
+ assertion failed closed in the `install-surface` CI job. The pin keeps the verified 0.84.3
171
+ constellation ours to hold; the leak assertion still guards every other pi package and any
172
+ future closure growth. The 0.84.4 bump itself remains a separate hard-cut lane.
173
+ - **The omp doctor treated Bundle C's empty tmux scrub as inherited identity.** `tmux -e NAME=`
174
+ writes `PI_SESSION_ID=` / `PI_AGENT_ID=` present-but-empty; authoritative readers trim and
175
+ require truthy values, so empty and absent are the same answer. The doctor was presence-testing
176
+ those names, which turned `check:full` red whenever a visible fresh omp citizen was alive.
177
+ It now flags only a nonblank value, and the hermetic smoke hands it fixture pids rather than
178
+ the host `pgrep`. (#87)
179
+
180
+ ### Verification
181
+
182
+ Each receipt carries its own scope; none of them is transferable to another commit or host.
183
+
184
+ - **`./run.sh check-gate-qualification`** — **324/324 KILLED, NOT KILLED 0**, standalone on the
185
+ Bundle C candidate (`fd5e462`), independently re-verified by a second model on the gate
186
+ predicate plus a full-lane rescan and a 3/3 sample re-kill.
187
+ - **`pnpm run check:full`** — exit 0 in 430s on that same candidate, after the `build-bridge`
188
+ refresh the stale-dist tripwire demanded.
189
+ - **Clause 7 LIVE** — `smoke-omp-fresh-live` green 2026-08-30, 21 assertions, oracle / omp 18.0.0
190
+ / `openai-codex/gpt-5.6-sol`. The sibling's garden `20260830T192913-df52b9` came from the
191
+ CALLBACK's sender envelope and never from the launch receipt; exactly one new citizen;
192
+ `lastEnqueuedAt 10:29:19.179Z` / `lastReadAt 10:29:24.673Z`, with the drain visible in that
193
+ session's own transcript for its own garden id. It took two runs: the first went red on
194
+ stage-two delivery alone, which is the argument for making it a MUST.
195
+ - **Receive acceptance (D6)** — 2026-08-30, oracle, omp 18.0.0: garden `20260830T140819-116f6a`,
196
+ `lastEnqueuedAt 05:08:20.555Z` / `lastReadAt 05:08:23.958Z`. D3 isolation is PROVEN, not
197
+ pending: with two live armed omp citizens, one addressed enqueue rang exactly one doorbell
198
+ while the sibling persisted no transcript and kept an empty mailbox.
199
+ - **Cross-harness leg** — 2026-08-31, the first bidirectional live-turn roundtrip on the committed
200
+ bytes: a claude-code citizen opened a fresh omp sibling through the public surface (callback
201
+ sender garden `20260831T124226-eac41a`), dispatched an addressed `entwurf_v2` into it, and the
202
+ omp citizen's own live turn drained the doorbell and replied into the claude mailbox.
203
+ - **`LIVE=1 ./run.sh release-gate <scratch> --cut`** — **MUST PASS=23 FAIL=0 SKIP=0,
204
+ BEHAVIOR PASS=1 FAIL=0 SKIP=0, `cut: OK`.** `check-gate-qualification` inside the gate
205
+ killed **325/325** mutants across 36 lanes. Log:
206
+ `/tmp/entwurf-release-gate-0.16.0.run2.DfMtRj/release-gate.log` (SHA-256
207
+ `2df4f6d4bddd563431ca41d2a43074f2cb6ccf9926ec6e8c7323de1a6ddf8192`).
208
+ A first attempt on the same versioned tree (scratch `.../entwurf-release-gate-0.16.0.AyHcIJ`)
209
+ hit one MUST FAIL in `smoke-entwurf-v2-matrix-live` C1b: the hidden-store resident did not
210
+ birth a record inside the 30s boot window. The same smoke rerun standalone passed 17 checks
211
+ in 4.9s; the rerun cut above is the acceptance. `pnpm run check:full` on the versioned tree
212
+ was exit 0 in 432s before P5.
213
+
214
+ ### Notes
215
+
216
+ - **Evidence limit.** Every OMP LIVE claim above is one Linux host (oracle, ARM), one model, one
217
+ accepted run. Multi-host, multi-model and repeated fresh calls inside one process are not
218
+ claimed and are not evidenced here.
219
+ - **Operator prerequisite.** `tools: xdev: false` in `~/.omp/agent/config.yml`. Under the vendor
220
+ default the doorbell would name a tool the model cannot call, so both the fresh preflight and
221
+ the LIVE smokes check it as a precondition. `setup` does not compose omp.
222
+ - Measured against **omp 18.0.0** while the vendor announces 18.0.11. The `mode === "tui"`
223
+ discriminator, the `xd://` behaviour and the five measurement cells recorded in
224
+ `scripts/raw-omp-measure/README.md` §M7 are re-measurement targets on upgrade.
225
+ - Copilot's operator-metered clause 7 exception is preserved, not reopened. The release stop
226
+ applies from OMP onward, forward-only.
227
+ - The cross-harness leg's deterministic half — every post-contract citizen backend having a wired
228
+ cross-harness LIVE step or a declared metered exception — is an owed follow-up. Until that gate
229
+ lands the rule is prose, and `docs/adding-a-harness.md` says so in place.
230
+ - Pi `0.84.4` remains a separate hard-cut lane.
231
+ - #90 is measured but unfixed: installed Claude Code sends `model` as a STRING on the interactive
232
+ `SessionStart` envelope and omits it entirely in print mode, while the reader accepts only the
233
+ object shape, so claude-code records carry no model. Widening the reader and pinning both shapes
234
+ in a fixture is a separate lane.
235
+
7
236
  ## 0.15.1 — 2026-08-27
8
237
 
9
238
  This patch is the Linux install-honesty floor for #86: Entwurf installs itself only,
package/DELIVERY.md CHANGED
@@ -84,11 +84,12 @@ can be owned without being supported; a supported harness still has to walk
84
84
  |---|---|---|---|
85
85
  | **pi native Entwurf** | shipped | D7; D8 partial | Record-addressed Unix control socket. A record-less socket is diagnostic only and never dispatched. |
86
86
  | **Claude Code interactive `>=2.1.217`** | shipped; Linux certified | D6; D7/D8 partial | Per-session mailbox + exec-form `FileChanged`/`asyncRewake`. B2 proved idle wake and same-session continuity on one NixOS host. |
87
- | **Antigravity / agy** | shipped | D6; D7 partial | Record-backed native-push through LS gRPC `agentapi send-message`; no mailbox or receiver marker. |
87
+ | **Antigravity / agy** | shipped | D6; D7 partial | Record-backed native-push through LS gRPC `agentapi send-message`; no mailbox or receiver marker. Admitted before the #82 step 9 contract and not re-evaluated under it, so it is legacy citizen evidence, not a step-9 supported harness: `entwurf_fresh_call` cannot open an agy sibling, and nothing here should be read as claiming visible lifecycle parity. |
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
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
+ | **OMP (`omp`) garden citizen** | shipped in 0.16.0; send + receive + visible fresh accepted on one host | D6; D3 proven; D7 partial; D8 partial | Two in-process extensions in the operator's own TUI: birth mints the `mode === "tui"` host (subagents mint nothing) and names it as sender; the RECEIVER unit joins that citizen in the SAME process, holds an `fs.watch` on its mailbox signal, and rings an announce-only doorbell through the vendor's `pi.sendUserMessage` — measured to be on the FACTORY object, not the event ctx, and measured to start a turn on an idle host with zero typing (`agent_start` +31ms). `wakeMode` is `self-fetch`: the model drains with `entwurf_inbox_read` and THAT read is the receipt. **D6 is the LIVE acceptance of 2026-08-30** (oracle, omp 18.0.0) — garden `20260830T140819-116f6a`, `lastEnqueuedAt 05:08:20.555Z` / `lastReadAt 05:08:23.958Z`, and the citizen's own transcript carrying `mcp__entwurf_bridge_entwurf_inbox_read` for its own garden id. **D3 is PROVEN, not pending**: with two live omp citizens armed, one addressed enqueue rang exactly one doorbell and the sibling persisted no transcript and kept an empty mailbox. D7 is PARTIAL (`lastReadAt` needs no scraping; the reply does). D8 is PARTIAL — dedupe and every stale-handling path are implemented and hermetically pinned, ordering/loop-guard/crash-recovery are not. The `/new` unarm is the rail-specific one: the watch lives in the operator's TUI, so pid + start-key cannot see a citizen change underneath a living process, and without an explicit unarm the previous garden id would keep reading deliverable. Requires `tools: xdev: false` in `~/.omp/agent/config.yml` — the vendor default hides MCP tool schemas from the prompt, so the doorbell would name a tool the model cannot call. The decisive receipt lines — the ordering probe, the D6 chain, the D3 isolation and the `/new` unarm — are pasted into `scripts/raw-omp-measure/README.md` §M7 rather than left in a host-local `/tmp` path. Evidence level L4: one Linux host, ARM. **Visible fresh (step 9) is ACCEPTED — the clause 7 LIVE went green on 2026-08-30:** `entwurf_fresh_call` opens omp on all three public surfaces through the bare `omp` runtime with an explicit `--approval-mode yolo` width and the `mcp__entwurf_bridge_entwurf_v` callback name, behind a five-axis pre-mutation preflight whose fifth axis is omp-specific (`tools.xdev !== true`, without which the model cannot call the callback tool at all). **The first turn is a TWO-STAGE BOOTSTRAP rather than a positional prompt, and that is a measured correction, not a preference.** `[LIVE 2026-08-30]` the positional-prompt candidate opened its window and minted its citizen (garden `20260830T181342-452167`, native `01a051f2-3107-7147-8806-fa2a6f527610`), delivered the byte-identical framing as a user message at `09:13:42.413Z`, and the model answered the literal text `ACK` at `09:13:47.105Z` with ZERO tool calls; the caller timed out at 240s. `[source]` the interactive UI defers MCP discovery (`sdk.ts:1847-1855`, `:1881-1905`) while the positional `initialMessage` prompts straight after `mode.init()` (`main.ts:540-565`), and `[측정]` an observer on the same runtime saw `turn_start` at +654ms with the entwurf tools absent and the callback tool present only at +1484ms. So the launcher now carries `{v,target,nonce,task}` on the fixed registered flag `--entwurf-bootstrap`, and the in-process birth extension polls `getAllTools`(`source:"mcp"`) AND `getActiveTools` for the exact callback name, sends a callback-ONLY prompt, arms the task only on a `tool_result` whose stored `toolCallId`, tool name, target, nonce and `isError === false` all match, and DELIVERS it at the next `turn_end` of that same session. `[LIVE 2026-08-30]` that last boundary is itself a measured correction: a first attempt sent the task from inside the `tool_result` handler with an explicit `deliverAs: "followUp"`, the hook log showed the full chain (`bootstrap-armed` → `bootstrap-ready` +440ms → `bootstrap-callback-observed` → `bootstrap-released`), and the task still never appeared in the session — `[source]` an explicit `deliverAs` queues without starting a turn in either state, while the omitted form starts one when idle (`agent-session.ts:6511-6513`), which the same transcript confirmed three seconds later when the Bundle B doorbell's omitted-option send landed and started a turn. `[측정 2026-08-30]` the callback-only half is what was proven to work: model `openai-codex/gpt-5.6-sol`, tool live at +1105ms, prompt injected at +1107ms, and the sibling calling `mcp__entwurf_bridge_entwurf_v` with the exact nonce (`omp-cb-btkvva4r87` → `20260830T184054-1aa1f2`, `meta-mailbox → enqueued`). **The clause 7 acceptance, `smoke-omp-fresh-live` (release-gate MUST), 2026-08-30, 21 assertions, omp 18.0.0 / `openai-codex/gpt-5.6-sol`:** launch through `tools/call entwurf_fresh_call` with nonce `mux-fresh-call-34faea228f3c3638684b40ff`; sibling garden `20260830T192913-df52b9` taken from the CALLBACK's sender envelope and never from the launch receipt; exactly one new citizen (§3.5); addressed `entwurf_v2` → `meta-mailbox → enqueued`; `lastEnqueuedAt 10:29:19.179Z` / `lastReadAt 10:29:24.673Z`; the drain visible in that session's own transcript for its own garden id. **Both bootstrap stages are in that transcript in order** — the callback-only prompt at offset 970, the caller's task at 4732, no four-backend positional framing anywhere, and an assistant message after the task proving it started a turn rather than sitting in a queue. The unit's own log carries the state machine: `bootstrap-armed 10:29:13.399Z` → `bootstrap-ready 10:29:13.819Z` (+420ms) → `bootstrap-callback-observed 10:29:18.663Z` (`toolCallId=call_KZJtkxfzy1IEE4ejKutCPWcm|fc_0858…`) → `bootstrap-released 10:29:18.731Z` → `bootstrap-task-sent 10:29:18.759Z`. Evidence level L4: one Linux host, ARM, one run. `check-harness-admission-parity` keeps the deterministic half of the admission from being a matter of memory. What this does NOT establish: multi-host, multi-model, or repeated fresh calls in one process. **Cross-harness leg, 2026-08-31 (first bidirectional live-turn roundtrip on the committed bytes + operator redeploy):** a claude-code citizen (`20260831T090152-f998ac`) opened a fresh omp sibling through the public surface (nonce `mux-fresh-call-807cb693b1913ce98ffdbfd1`, callback sender garden `20260831T124226-eac41a`), dispatched an addressed `entwurf_v2` → `meta-mailbox → enqueued`, and the omp citizen's own live turn drained and replied into the claude mailbox — its self-report (doorbell → `entwurf_inbox_read`; callback-only first user message, task second) agrees with every receipt on the chain. |
92
93
  | **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
94
  | **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
95
 
@@ -137,7 +138,7 @@ native citizen or ACP backend. `turn/steer` is active-turn steering, not idle wa
137
138
 
138
139
  ### Copilot CLI: one citizen, two rails, one pending receipt
139
140
 
140
- The branch product owns the native citizen's birth, garden id, statusline, MCP hand,
141
+ The shipped product owns the native citizen's birth, garden id, statusline, MCP hand,
141
142
  outbound sender identity — and, since RAIL 5, the receiver. A real Copilot CLI 1.0.80
142
143
  session minted a V3 record and sent under that record-backed garden id on 2026-08-21.
143
144
  That proved who SENDS. Whether a reply LANDS is a different fact on a different process,
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 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)).
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. OMP (`omp`) is a self-fetch citizen on the same rail as Claude and Copilot, opened by `entwurf_fresh_call` and accepted under the step 9 visible-fresh contract on 2026-08-30 — its first turn is a two-stage in-process bootstrap rather than an argv prompt, because the vendor connects its MCP tools in the background after the session starts. 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 / Copilot / Codex / agy / pi
16
+ Claude Code / Copilot / Codex / agy / omp / pi
17
17
  → garden id
18
18
  → entwurf_v2
19
19
  → control-socket | meta-mailbox | native-push
@@ -59,10 +59,11 @@ native Antigravity / agy
59
59
  ↔ entwurf_v2 native-push
60
60
  ```
61
61
 
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).
62
+ Claude's `install-meta-bridge`, Copilot's four `install-copilot-*` surfaces, agy's
63
+ `install-agy-{bridge,statusline,hooks}` and OMP's three `install-omp-{bridge,mcp,receive}` units
64
+ (each with its own `doctor-omp-*`) are distinct because their lifecycle and delivery transports are
65
+ genuinely different. Codex remains verified probe evidence, not a shipped managed native-citizen
66
+ lane; see [DELIVERY.md](./DELIVERY.md).
66
67
 
67
68
  > **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.
68
69
 
@@ -71,17 +72,20 @@ native-citizen lane; see [DELIVERY.md](./DELIVERY.md).
71
72
  A few words that look unusual for a coding tool.
72
73
 
73
74
  - **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.
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.
75
+ - **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`, and for OMP an in-process extension bound to both session edges that mints only the visible `mode === "tui"` host). 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.
75
76
  - **Engraving** — optional short operator text delivered through each backend's native identity carrier. Not a giant hidden prompt, not a tool catalog.
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.
77
+ - **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, OMP, …) 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.
77
78
  - **Session persistence** — re-attaches pi to the same remote ACP session. Does not hydrate backend transcripts into pi history.
78
79
 
79
80
  ## Install
80
81
 
81
- `entwurf` is a neutral npm package first. Install the package with `npm` (or
82
- `pnpm`/`yarn`) and then wire the harness you want to use. Pi is still the
83
- adapter that hosts the ACP plugin and live control-socket surface, but the base
84
- install is **not** `pi install npm:...` anymore.
82
+ `entwurf` is a neutral npm package first. Get the package, then run **`entwurf setup
83
+ <project>`** one command, the same front door from an npm global install, an npm
84
+ project-local install, or a source checkout. It composes every harness it finds on the
85
+ host and reports each one PASS / SKIP / FAIL. You are not meant to assemble the parts by
86
+ hand; the per-harness installers further down are the repair surface for when one unit
87
+ needs to be redone alone. Pi is still the adapter that hosts the ACP plugin and live
88
+ control-socket surface, but the base install is **not** `pi install npm:...` anymore.
85
89
 
86
90
  The package exposes six bins:
87
91
 
@@ -95,7 +99,7 @@ The package exposes six bins:
95
99
  The bridge/renderers/hook use stable bin names so package upgrades do not bake versioned package-store paths into native-harness settings.
96
100
 
97
101
  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,
102
+ integration artifacts. It does not install `pi`, Claude Code, Copilot CLI, Codex, agy, omp, Cortex,
99
103
  or any other harness runtime. Those are operator choices and may all be absent. The bridge also
100
104
  does not provide credentials, tokens, subscription access, or an auth bypass; whatever an
101
105
  operator-installed harness already trusts is what Entwurf can use. `setup` is composition, not
@@ -107,15 +111,12 @@ look complete.
107
111
  ```bash
108
112
  npm install -g @junghanacs/entwurf
109
113
 
110
- # wire a target project for the pi adapter / ACP plugin lane
111
- cd /path/to/your-project
112
- entwurf install .
114
+ entwurf setup /path/to/your-project
113
115
  entwurf check-bridge
114
116
  ```
115
117
 
116
- This writes `.pi/settings.json` in the target project with the absolute path to
117
- the installed `entwurf-bridge` launcher. (The old `~/.pi/agent/` target-registry
118
- link is gone — #50 C3; nothing reads it.) The global install is the easiest path when
118
+ `setup` wires the target project for the pi adapter / ACP plugin lane and composes
119
+ whatever native harnesses are present. The global install is the easiest path when
119
120
  Claude Code's USER-scope MCP registration should work from every cwd.
120
121
 
121
122
  ### From npm — project-local install
@@ -124,27 +125,25 @@ Claude Code's USER-scope MCP registration should work from every cwd.
124
125
  cd /path/to/your-project
125
126
  npm install --save-dev @junghanacs/entwurf
126
127
 
127
- npx entwurf install .
128
+ npx entwurf setup .
128
129
  npx entwurf check-bridge
129
130
  ```
130
131
 
131
- For an npm upgrade, rerun the install command in the same scope (use
132
+ For an npm upgrade, rerun `setup` in the same scope (use
132
133
  `@junghanacs/entwurf@latest` when you want the registry's stable line explicitly),
133
134
  then make the first check from that same scope: `entwurf check-bridge` for a global
134
135
  install or `npx entwurf check-bridge` for a project-local install. Native-harness
135
- installers and process restarts remain a separate post-upgrade step below.
136
-
137
- For manual MCP registration from a project-local install, point the host at:
138
-
139
- ```text
140
- /path/to/your-project/node_modules/.bin/entwurf-bridge
141
- ```
136
+ repair and process restarts remain a separate post-upgrade step below.
142
137
 
143
- or at the package launcher directly:
138
+ `entwurf install <project>` is the narrower repair leaf: it writes only
139
+ `.pi/settings.json` in the target project, with the absolute path to the installed
140
+ `entwurf-bridge` launcher, and composes no harness. Reach for it when the pi wiring
141
+ alone needs redoing. (The old `~/.pi/agent/` target-registry link is gone — #50 C3;
142
+ nothing reads it.)
144
143
 
145
- ```text
146
- /path/to/your-project/node_modules/@junghanacs/entwurf/mcp/entwurf-bridge/start.sh
147
- ```
144
+ To register the bridge in an MCP host by hand from a project-local install, point it at
145
+ `node_modules/.bin/entwurf-bridge` — see
146
+ [External MCP registration](#external-mcp-registration).
148
147
 
149
148
  ### From source — development clone
150
149
 
@@ -164,29 +163,19 @@ and a detected harness that cannot be completed (including a below-floor `pi`) i
164
163
  that makes setup exit nonzero. `setup` never installs a harness binary or touches a credential
165
164
  store. It also exposes stable commands under `~/.local/bin`, including `entwurf` → this
166
165
  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
166
+ installation. A detected `copilot` composes all four of its native units (birth → MCP →
167
+ receiver → visible footer) in one go. Package consumers
170
168
  run the same `entwurf setup <project>` through their npm-provided bin: installed mode is decided
171
169
  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.
170
+ already provided by npm bin linking.
174
171
 
175
172
  The pi user-scope registration is ONE shared entry with a recorded owner (#86 C2): installing
176
173
  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.
174
+ with zero settings bytes written, `entwurf takeover-user-scope` is the operator-explicit move,
175
+ `entwurf doctor-pi-package` names the ownership verdict, and `entwurf remove-user-scope` is
176
+ same-owner-only. The full contract atomicity across the package and provider halves, the
177
+ split verdict over an operator's own override, legacy adoption, and the `managedSettingsPath`
178
+ binding is [docs/setup-clean-host.md §1.1](./docs/setup-clean-host.md#11-user-scope-ownership-one-shared-registration-one-recorded-owner).
190
179
 
191
180
  A development clone runs the bridge source through Node's strip-types path;
192
181
  an npm-installed package runs the prebuilt JS under `mcp/entwurf-bridge/dist/`
@@ -214,41 +203,30 @@ For daily operator sessions, launch pi with `--entwurf-control` — no id
214
203
  injection; the meta-record mints the garden address (see [Garden launcher](#garden-launcher)). Older pi
215
204
  versions may silently miss the provider/extension surface, so treat the pi floor
216
205
  as release-critical for the ACP/plugin lane. A host that only uses
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
206
+ `entwurf-bridge` from Claude Code / Copilot / Codex / Antigravity / OMP does not need pi at all for
207
+ delivery: no `entwurf_v2` rail launches a pi process. OMP is a pi fork, but it is its own binary and
208
+ resolves its own agent directory, so that lineage does not reintroduce a `pi` requirement either. That external-only shape works with the same
219
209
  `setup` command: pi is optional-by-presence there, so a pi-less host simply gets an explicit pi
220
210
  SKIP while the detected harnesses are composed.
221
211
 
222
- ### Native harness install and doctors
212
+ ### Native harness repair and doctors
223
213
 
224
214
  A plain MCP registration exposes the bridge tools; a **garden-native** session also
225
- needs entwurf's lifecycle hook and identity marker. Use the managed installers rather
226
- than editing native-harness state by hand:
227
-
228
- ```bash
229
- # Claude Code (Linux-certified axis)
230
- entwurf install-meta-bridge
231
- entwurf doctor-meta-bridge
232
-
233
- # Antigravity / agy
234
- entwurf install-agy-bridge
235
- entwurf install-agy-statusline
236
- entwurf install-agy-hooks
237
- entwurf doctor-agy-bridge
238
- entwurf doctor-agy-statusline
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
251
- ```
215
+ needs entwurf's lifecycle hook and identity marker. `setup` already composes all of that
216
+ for every harness it detects — you do not paste this list to install. OMP is the one
217
+ exception and it is deliberate: its units are operator-selectable installs that `setup`
218
+ does not compose, so the three `install-omp-*` commands below are how an omp host gets
219
+ wired. Otherwise this is the repair surface: each unit has its own installer, its own
220
+ doctor with a named refusal, and its own inverse, so a single broken unit can be redone
221
+ without touching the rest.
222
+
223
+ - **Claude Code** (Linux-certified axis) — `install-meta-bridge`, `doctor-meta-bridge`.
224
+ - **Antigravity / agy** — `install-agy-bridge`, `install-agy-statusline`, `install-agy-hooks`, each with a matching `doctor-agy-*`.
225
+ - **GitHub Copilot CLI** — four independent units, four independent failure modes: `install-copilot-bridge` (birth: garden id + who-sent, on the first prompt), `install-copilot-mcp` (the entwurf tool hand, where `entwurf_inbox_read` lives), `install-copilot-receive` (the receiver extension: doorbell + receiver marker), `install-copilot-statusline` (optional for a manual citizen, required for supported fresh) — each with a matching `doctor-copilot-*` and `uninstall-copilot-*`.
226
+ - **OMP (`omp`)** — three units, all in-process extensions rather than launchers: `install-omp-bridge` (birth: the `mode === "tui"` visible host, its garden id on the status line, and who-sent), `install-omp-mcp` (the omp-native `entwurf-bridge` entry), `install-omp-receive` (the receiver extension: mailbox watch + announce-only doorbell) — each with a matching `doctor-omp-*` and `uninstall-omp-*`. One OPERATOR setting sits beside them and is not ours to write: `tools: xdev: false` in the omp agent config, without which the vendor mounts MCP tools as `xd://` devices the model cannot call.
227
+
228
+ Run them as `entwurf <command>`. Which unit a doctor's refusal names, and the clean-host
229
+ walk-through for each harness, live in [docs/setup-clean-host.md](./docs/setup-clean-host.md).
252
230
 
253
231
  #### Launching Copilot as a garden citizen — `entwurf copilot`
254
232
 
@@ -294,8 +272,10 @@ the default permission prompts — which an idle, unattended session is not ther
294
272
 
295
273
  Claude Code uses the supported floor `>=2.1.217`; older versions silently discard the
296
274
  exec-hook `args`, so install and doctor fail loud rather than falling back. After any
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
275
+ upgrade, rerun `entwurf setup <project>` it re-composes every detected harness, all four
276
+ Copilot units included and restart its existing processes; reach for a single
277
+ `install-*` only when one unit needs repair on its own. A claimed Claude host is certified
278
+ only when a **new** session using the
299
279
  installed artifact makes `doctor-meta-bridge` exit 0 with the live owner join.
300
280
 
301
281
  Linux is the only currently certified Claude meta-bridge axis. New macOS wiring is
@@ -409,7 +389,7 @@ Reference shape lives in [`pi/settings.reference.json`](./pi/settings.reference.
409
389
  ### External MCP registration
410
390
 
411
391
  `entwurf-bridge` can also be registered in a separate MCP-aware harness (Claude Code,
412
- Copilot CLI, Codex CLI, Antigravity). Two shapes exist and they are not interchangeable:
392
+ Copilot CLI, Codex CLI, Antigravity, OMP). Two shapes exist and they are not interchangeable:
413
393
 
414
394
  - **plain external MCP host** — no garden meta-record or sender marker. It can read the
415
395
  surfaces, but `entwurf_v2` is **refused by default**: there is no authoritative sender.
@@ -421,7 +401,10 @@ claude mcp add --scope user entwurf-bridge entwurf-bridge
421
401
  ```
422
402
 
423
403
  Per-harness registration (Claude Code `~/.mcp.json`, Codex `~/.codex/config.toml`, the
424
- managed `install-agy-*` surfaces), the PATH/env boundary for GUI-launched MCP servers, the
404
+ managed `install-agy-*` surfaces, and OMP's managed `install-omp-mcp` into `<omp agent dir>/mcp.json`
405
+ — whose pinned server key is what shadows a borrowed Claude import, see
406
+ [docs/external-mcp-host.md](./docs/external-mcp-host.md) §OMP), the PATH/env boundary for
407
+ GUI-launched MCP servers, the
425
408
  anonymous-sender hatch, and the full external/meta-session semantics are in
426
409
  [docs/external-mcp-host.md](./docs/external-mcp-host.md).
427
410
  For the maintained multi-harness setup and skill/command packaging details, see `agent-config`. See also the MCP entry in [Concept primer](#concept-primer), the sender envelope contract in [AGENTS.md](./AGENTS.md), and [Custom skills](#custom-skills) for the in-pi ACP skill surface.
@@ -527,7 +510,7 @@ A two-pane recording covers the pre-0.12 v1 surface end-to-end — sibling resum
527
510
 
528
511
  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.)
529
512
 
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.
513
+ 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 / self-fetch meta-mailbox (Claude Code, Copilot, OMP) / 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`, `copilot`, or `omp`) 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, and an `omp` launch carries its task in the `--entwurf-bootstrap` payload instead of an argv prompt because that vendor connects its MCP tools after the session has already started. 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.
531
514
 
532
515
  ### `entwurf_v2` — canonical dispatch verb
533
516
 
@@ -544,9 +527,9 @@ In ACP-backed and external native-harness sessions, `entwurf-bridge` exposes sev
544
527
  | dead / indeterminate native-push conversation | fire-and-forget | **reject** (`native-push-target-dead` / `native-push-probe-indeterminate`) |
545
528
  | record-less control socket (no meta-record) | any | **reject** (`record-less-socket` — pre-probe; diagnostic state, #50 C4) |
546
529
 
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.
530
+ **`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, Copilot or OMP, 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.
548
531
 
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.
532
+ 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, Copilot or OMP 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; OMP is the opposite — the bare `omp` runtime with no positional prompt at all, because that vendor connects its MCP tools after the session has started, so the task rides a two-stage `--entwurf-bootstrap` payload the installed birth extension releases once the callback tool has actually answered. 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.
550
533
 
551
534
  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.
552
535