@junghanacs/entwurf 0.15.0 → 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 (90) hide show
  1. package/AGENTS.md +1 -0
  2. package/CHANGELOG.md +299 -0
  3. package/DELIVERY.md +4 -3
  4. package/README.md +99 -67
  5. package/VERIFY.md +13 -7
  6. package/docs/acp-backend-rail.md +1 -1
  7. package/docs/external-mcp-host.md +147 -9
  8. package/docs/setup-clean-host.md +221 -16
  9. package/mcp/entwurf-bridge/dist/mcp/entwurf-bridge/src/index.js +65 -20
  10. package/mcp/entwurf-bridge/dist/pi-extensions/entwurf-capabilities.json +1 -0
  11. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-self-address.js +17 -6
  12. package/mcp/entwurf-bridge/dist/pi-extensions/lib/meta-sender-identity.js +1 -1
  13. package/mcp/entwurf-bridge/dist/pi-extensions/lib/meta-session.js +172 -5
  14. package/mcp/entwurf-bridge/dist/pi-extensions/lib/mux-fresh-call.js +152 -17
  15. package/mcp/entwurf-bridge/dist/pi-extensions/lib/omp-fresh-preflight.js +271 -0
  16. package/mcp/entwurf-bridge/dist/pi-extensions/meta-bridge-omp.js +988 -0
  17. package/mcp/entwurf-bridge/dist/pi-extensions/meta-bridge-receive-omp.js +574 -0
  18. package/mcp/entwurf-bridge/dist/scripts/omp-receive-facts.js +84 -0
  19. package/mcp/entwurf-bridge/src/index.ts +65 -19
  20. package/mcp/entwurf-bridge/tsconfig.build.json +19 -3
  21. package/mcp/tsconfig.json +10 -0
  22. package/package.json +14 -4
  23. package/pi/entwurf-capabilities.json +1 -0
  24. package/pi/meta-bridge-omp/entwurf-meta-omp/package.json +7 -0
  25. package/pi/omp-receive/entwurf-receive-omp/package.json +7 -0
  26. package/pi-extensions/entwurf-control.ts +12 -9
  27. package/pi-extensions/lib/entwurf-self-address.ts +18 -7
  28. package/pi-extensions/lib/meta-sender-identity.ts +1 -1
  29. package/pi-extensions/lib/meta-session.ts +219 -5
  30. package/pi-extensions/lib/mux-fresh-call.ts +171 -17
  31. package/pi-extensions/lib/omp-fresh-preflight.ts +275 -0
  32. package/pi-extensions/meta-bridge-omp.ts +1244 -0
  33. package/pi-extensions/meta-bridge-receive-omp.ts +666 -0
  34. package/run.sh +1041 -174
  35. package/scripts/agy-bridge.sh +4 -2
  36. package/scripts/check-copilot-birth-hook.ts +700 -23
  37. package/scripts/check-entwurf-self-address.ts +31 -0
  38. package/scripts/check-fresh-cut-gate.sh +12 -4
  39. package/scripts/check-gate-qualification.ts +8 -2
  40. package/scripts/check-harness-admission-parity.ts +143 -0
  41. package/scripts/check-meta-doctor-oracle.sh +20 -0
  42. package/scripts/check-omp-birth-hook.ts +1049 -0
  43. package/scripts/check-omp-fresh-preflight.ts +181 -0
  44. package/scripts/check-omp-receive-arm.ts +516 -0
  45. package/scripts/check-setup-qualification.sh +126 -0
  46. package/scripts/copilot-bridge-doctor.sh +104 -71
  47. package/scripts/copilot-bridge-install.sh +180 -20
  48. package/scripts/copilot-bridge-oracle.sh +223 -0
  49. package/scripts/copilot-bridge-uninstall.sh +137 -0
  50. package/scripts/dev-bin.sh +50 -9
  51. package/scripts/fake-copilot-vendor.sh +67 -0
  52. package/scripts/inventory-verification-surface.ts +1 -1
  53. package/scripts/meta-bridge-hook-log.sh +9 -1
  54. package/scripts/mutants/copilot-birth.json +96 -5
  55. package/scripts/mutants/mux-fresh-call.json +22 -22
  56. package/scripts/mutants/omp-birth.json +173 -0
  57. package/scripts/mutants/omp-fresh.json +300 -0
  58. package/scripts/mutants/omp-receive.json +135 -0
  59. package/scripts/mutants/pi-package-ownership.json +92 -0
  60. package/scripts/mutants/self-address.json +34 -0
  61. package/scripts/mutants/setup-verdict.json +148 -0
  62. package/scripts/mutants/source-install.json +32 -0
  63. package/scripts/omp-bridge-doctor.sh +315 -0
  64. package/scripts/omp-bridge-install.sh +221 -0
  65. package/scripts/omp-bridge-oracle.sh +154 -0
  66. package/scripts/omp-bridge-uninstall.sh +57 -0
  67. package/scripts/omp-mcp-bridge.sh +320 -0
  68. package/scripts/omp-mcp-config.py +392 -0
  69. package/scripts/omp-receive-doctor.sh +246 -0
  70. package/scripts/omp-receive-facts.ts +106 -0
  71. package/scripts/omp-receive-install.sh +228 -0
  72. package/scripts/omp-receive-uninstall.sh +60 -0
  73. package/scripts/omp-tool-surface.py +349 -0
  74. package/scripts/pi_settings_io.py +22 -0
  75. package/scripts/raw-omp-measure/README.md +420 -0
  76. package/scripts/raw-omp-measure/probe-extension.ts +76 -0
  77. package/scripts/raw-omp-measure/probe-receive-surface.ts +250 -0
  78. package/scripts/raw-omp-measure/source-audit.md +414 -0
  79. package/scripts/register-pi-package.py +525 -7
  80. package/scripts/register-pi-provider.py +162 -12
  81. package/scripts/smoke-agy-install-state.sh +86 -30
  82. package/scripts/smoke-agy-statusline-state.sh +9 -6
  83. package/scripts/smoke-omp-bridge-state.sh +221 -0
  84. package/scripts/smoke-omp-fresh-live.ts +497 -0
  85. package/scripts/smoke-omp-mcp-state.sh +327 -0
  86. package/scripts/smoke-omp-receive-live.ts +340 -0
  87. package/scripts/smoke-omp-receive-state.sh +196 -0
  88. package/scripts/smoke-setup-verdict.sh +291 -0
  89. package/scripts/smoke-user-scope-citizen.sh +388 -1
  90. package/scripts/tsconfig.json +2 -0
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,305 @@ 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
+
236
+ ## 0.15.1 — 2026-08-27
237
+
238
+ This patch is the Linux install-honesty floor for #86: Entwurf installs itself only,
239
+ `setup` composes harnesses the operator already chose, and a detected integration that
240
+ cannot be completed makes the command non-green. It is not a new garden capability.
241
+ macOS and native-Windows implementation move to #78; this cut makes no cross-platform
242
+ support claim.
243
+
244
+ ### Changed
245
+
246
+ - **`setup` is presence-driven composition, not recruitment.** Mode is a named
247
+ source-versus-installed branch before any prerequisite check or write. Each detected
248
+ harness (pi, Claude Code, Copilot, agy) is completed independently; absence is
249
+ zero-state SKIP; below-floor or incomplete is named FAIL and a nonzero exit. The old
250
+ unconditional `DONE ... green` is gone — the summary is computed from component
251
+ outcomes. Pi is optional-by-presence with the package-derived floor `>=0.84.3 <0.85`.
252
+ A detected Copilot composes all four native units (birth → MCP → receiver → visible
253
+ footer). Installed-package setup never runs npm/pnpm inside `node_modules`. (#86)
254
+ - **The shared Pi user-scope registration has a recorded owner.** Silent
255
+ last-writer-wins normalization is retired. A second root's install/setup/remove refuses
256
+ with zero settings writes and names `takeover-user-scope`; only that explicit operator
257
+ verb moves the entry (old→new reported). The inverse removes only the recorded owner's
258
+ exact entry. (#86 C2)
259
+ - **Setup no longer mutates credential stores.** The unconditional `sync_auth` path that
260
+ copied the Anthropic OAuth object to an `entwurf` alias is removed. Hosts that already
261
+ ran the legacy path keep the alias and `auth.json.bak` as a documented manual cleanup,
262
+ not an automated one. (#86 A5)
263
+
264
+ ### Added
265
+
266
+ - **Copilot birth has a package-owned inverse.** `uninstall-copilot-bridge` uses the
267
+ qualified `plugin@marketplace` identity, runs a read-only ownership/safety preflight
268
+ before any vendor write, and refuses to remove an unproven foreign unit. Installer,
269
+ inverse, and doctor share one structural oracle. (#86 C3a)
270
+ - **Packed-consumer and all-absent setup rows are first-class fixtures.**
271
+ `smoke-setup-verdict` and the installed `entwurf setup` path inside `check-pack-install`
272
+ prove absence, independence, false-success refusal, and the named installed-versus-source
273
+ branch without a model turn. That npm-consumer harness is the Linux install evidence
274
+ #78 waits on. (#86)
275
+
276
+ ### Fixed
277
+
278
+ - **Source setup now exposes the `entwurf` operator command Copilot fresh requires.**
279
+ `./run.sh setup` manages `~/.local/bin/entwurf` as an ownership-checked symlink to the
280
+ current checkout's `run.sh`, with per-bin state and an honest inverse. npm consumers
281
+ already received this command through package bin linking; the missing source half caused
282
+ `entwurf_fresh_call {backend:"copilot"}` to reject `runtime-unresolved` unless an unrelated
283
+ global package happened to mask the defect. Source setup now fails loud when that
284
+ operator path is foreign, outside PATH, or shadowed; helper units are attempted
285
+ independently and a foreign helper is a named FAIL, never a warning followed by cosmetic
286
+ success. The managed `entwurf copilot` boundary and every fresh/preflight/delivery
287
+ contract are unchanged. Discovered while rechecking #82. (#86)
288
+
289
+ ### Verification
290
+
291
+ - **`pnpm run check:full`** — PASS, exit 0, 252s standalone / 247s inside the gate
292
+ on the 0.15.1 versioned tree (HEAD `4a23af1` + uncommitted changelog/version).
293
+ - **`LIVE=1 ./run.sh release-gate /tmp/entwurf-release-gate-0.15.1.hAAn7U --cut`** —
294
+ **MUST PASS=21 FAIL=0 SKIP=0, BEHAVIOR PASS=1 FAIL=0 SKIP=0, `cut: OK`.**
295
+ `check-gate-qualification` inside the gate killed **277/277** mutants across 33
296
+ lanes. Log: `/tmp/entwurf-release-gate-0.15.1.hAAn7U/release-gate.log`
297
+ (SHA-256 `aa1364922301e685c650670dd6556a3c27feacdf1481d850d8e2ef56ef5cb23e`).
298
+
299
+ ### Notes
300
+
301
+ - #78 owns macOS and native-Windows implementation. WSL remains Linux evidence.
302
+ - Native-harness doctors stay the per-leaf live-certification surface. Setup writes
303
+ artifacts; a new session is still required before `doctor-meta-bridge` / Copilot receive
304
+ can go green on a host that already had processes open.
305
+
7
306
  ## 0.15.0 — 2026-08-25
8
307
 
9
308
  This release admits Copilot CLI as a full self-fetch garden citizen — birth, an owned
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
- | **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
+ | **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,