@junghanacs/entwurf 0.15.1 → 0.16.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. package/AGENTS.md +1 -1
  2. package/CHANGELOG.md +354 -0
  3. package/DELIVERY.md +3 -2
  4. package/README.md +68 -88
  5. package/VERIFY.md +4 -1
  6. package/demo/README.md +1 -1
  7. package/docs/acp-backend-rail.md +1 -1
  8. package/docs/external-mcp-host.md +147 -9
  9. package/docs/setup-clean-host.md +125 -6
  10. package/mcp/entwurf-bridge/dist/mcp/entwurf-bridge/src/index.js +65 -20
  11. package/mcp/entwurf-bridge/dist/pi-extensions/entwurf-capabilities.json +1 -0
  12. package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/backend-adapter.js +19 -10
  13. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-self-address.js +17 -6
  14. package/mcp/entwurf-bridge/dist/pi-extensions/lib/meta-sender-identity.js +1 -1
  15. package/mcp/entwurf-bridge/dist/pi-extensions/lib/meta-session.js +172 -5
  16. package/mcp/entwurf-bridge/dist/pi-extensions/lib/mux-fresh-call.js +152 -17
  17. package/mcp/entwurf-bridge/dist/pi-extensions/lib/omp-fresh-preflight.js +271 -0
  18. package/mcp/entwurf-bridge/dist/pi-extensions/meta-bridge-omp.js +988 -0
  19. package/mcp/entwurf-bridge/dist/pi-extensions/meta-bridge-receive-omp.js +574 -0
  20. package/mcp/entwurf-bridge/dist/scripts/omp-receive-facts.js +84 -0
  21. package/mcp/entwurf-bridge/src/index.ts +65 -19
  22. package/mcp/entwurf-bridge/tsconfig.build.json +19 -3
  23. package/mcp/tsconfig.json +10 -0
  24. package/package.json +19 -9
  25. package/pi/entwurf-capabilities.json +1 -0
  26. package/pi/meta-bridge-omp/entwurf-meta-omp/package.json +7 -0
  27. package/pi/omp-receive/entwurf-receive-omp/package.json +7 -0
  28. package/pi-extensions/entwurf-control.ts +12 -9
  29. package/pi-extensions/lib/acp/backend-adapter.ts +19 -9
  30. package/pi-extensions/lib/acp/backend.ts +125 -7
  31. package/pi-extensions/lib/acp/claude-acp-launch.js +100 -0
  32. package/pi-extensions/lib/entwurf-self-address.ts +18 -7
  33. package/pi-extensions/lib/meta-sender-identity.ts +1 -1
  34. package/pi-extensions/lib/meta-session.ts +219 -5
  35. package/pi-extensions/lib/mux-fresh-call.ts +171 -17
  36. package/pi-extensions/lib/omp-fresh-preflight.ts +275 -0
  37. package/pi-extensions/meta-bridge-omp.ts +1244 -0
  38. package/pi-extensions/meta-bridge-receive-omp.ts +666 -0
  39. package/run.sh +399 -31
  40. package/scripts/check-acp-launch-namespace.ts +127 -0
  41. package/scripts/check-acp-prompt-lifecycle.ts +145 -2
  42. package/scripts/check-copilot-birth-hook.ts +28 -1
  43. package/scripts/check-entwurf-self-address.ts +31 -0
  44. package/scripts/check-gate-qualification.ts +7 -3
  45. package/scripts/check-harness-admission-parity.ts +143 -0
  46. package/scripts/check-meta-doctor-oracle.sh +20 -0
  47. package/scripts/check-omp-birth-hook.ts +1049 -0
  48. package/scripts/check-omp-fresh-preflight.ts +208 -0
  49. package/scripts/check-omp-receive-arm.ts +516 -0
  50. package/scripts/check-setup-qualification.sh +40 -2
  51. package/scripts/copilot-bridge-oracle.sh +14 -6
  52. package/scripts/fake-copilot-vendor.sh +4 -2
  53. package/scripts/inventory-verification-surface.ts +1 -1
  54. package/scripts/meta-bridge-hook-log.sh +9 -1
  55. package/scripts/mutants/acp-launch-namespace.json +34 -0
  56. package/scripts/mutants/acp-prompt-lifecycle.json +67 -2
  57. package/scripts/mutants/copilot-birth.json +10 -10
  58. package/scripts/mutants/mux-fresh-call.json +22 -22
  59. package/scripts/mutants/omp-birth.json +173 -0
  60. package/scripts/mutants/omp-fresh.json +300 -0
  61. package/scripts/mutants/omp-receive.json +135 -0
  62. package/scripts/mutants/pack-install.json +2 -2
  63. package/scripts/mutants/self-address.json +34 -0
  64. package/scripts/mutants/setup-verdict.json +35 -0
  65. package/scripts/omp-bridge-doctor.sh +315 -0
  66. package/scripts/omp-bridge-install.sh +221 -0
  67. package/scripts/omp-bridge-oracle.sh +154 -0
  68. package/scripts/omp-bridge-uninstall.sh +57 -0
  69. package/scripts/omp-config-xdev.py +310 -0
  70. package/scripts/omp-config-xdev.sh +76 -0
  71. package/scripts/omp-mcp-bridge.sh +320 -0
  72. package/scripts/omp-mcp-config.py +392 -0
  73. package/scripts/omp-receive-doctor.sh +246 -0
  74. package/scripts/omp-receive-facts.ts +106 -0
  75. package/scripts/omp-receive-install.sh +228 -0
  76. package/scripts/omp-receive-uninstall.sh +60 -0
  77. package/scripts/omp-tool-surface.py +400 -0
  78. package/scripts/raw-acp-child-exit-measure/README.md +285 -0
  79. package/scripts/raw-acp-child-exit-measure/acp-turn-population.py +89 -0
  80. package/scripts/raw-acp-child-exit-measure/reaper-correlation.py +47 -0
  81. package/scripts/raw-omp-measure/README.md +420 -0
  82. package/scripts/raw-omp-measure/probe-extension.ts +76 -0
  83. package/scripts/raw-omp-measure/probe-receive-surface.ts +250 -0
  84. package/scripts/raw-omp-measure/source-audit.md +414 -0
  85. package/scripts/smoke-omp-bridge-state.sh +221 -0
  86. package/scripts/smoke-omp-fresh-live.ts +497 -0
  87. package/scripts/smoke-omp-mcp-state.sh +327 -0
  88. package/scripts/smoke-omp-receive-live.ts +340 -0
  89. package/scripts/smoke-omp-receive-state.sh +196 -0
  90. package/scripts/smoke-setup-verdict.sh +48 -3
  91. package/scripts/tsconfig.json +2 -0
package/AGENTS.md CHANGED
@@ -182,7 +182,7 @@ implement → affected focused gates → independent review → one amendment bu
182
182
  - Every `.ts` file belongs to one typecheck fence: root emit-capable config, MCP strip-types config, or scripts strip-types config. Do not hide files with `exclude`.
183
183
  - Root pi extensions import TypeBox through `@earendil-works/pi-ai`; do not mix direct `@sinclair/typebox` types.
184
184
  - MCP/scripts use explicit `.ts` imports where Node strip-types requires them. Installed operator surfaces route to compiled JS.
185
- - pi runtime range is `>=0.84.3 <0.85` with devDep exact `0.84.3`; re-evaluate loader aliases and `/compat` at every minor ceiling. The ceiling moves on measurement, never on assumption, and a previous bump's argument is never reused: at 0.83.0→0.84.0 `compat.ts` was still byte-identical but `loader.ts` was NOT, so the diff itself had to be read and judged reachable-or-not. Per-bump hashes, diff judgments, and reachability findings live in the ROADMAP **Dep bump(별도 트랙)** ledger — keep them there, not here.
185
+ - pi runtime range is `>=0.84.4 <0.85` with devDep exact `0.84.4`; re-evaluate loader aliases and `/compat` at every minor ceiling. The ceiling moves on measurement, never on assumption, and a previous bump's argument is never reused: at 0.83.0→0.84.0 `compat.ts` was still byte-identical but `loader.ts` was NOT, so the diff itself had to be read and judged reachable-or-not. Per-bump hashes, diff judgments, and reachability findings live in the ROADMAP **Dep bump(별도 트랙)** ledger — keep them there, not here.
186
186
  - ACP pins are recorded in `package.json` and checked by `check-dep-versions`/`check-acp-sdk-surface`; do not describe a dependency bump as a behavioral fix without evidence.
187
187
 
188
188
  ## Working Style
package/CHANGELOG.md CHANGED
@@ -4,6 +4,360 @@ All notable changes to this project will be documented here. Format follows [Kee
4
4
 
5
5
  ## Unreleased
6
6
 
7
+ ## 0.16.1 - 2026-09-01
8
+
9
+ ### Verification
10
+
11
+ Each receipt carries its own scope; none of them is transferable to another commit or host.
12
+
13
+ - **`pnpm run check:full`** — PASS, exit 0, **454s** inside the gate, on the 0.16.1
14
+ versioned tree (HEAD `2e7ceb4` + the uncommitted changelog/version edits).
15
+ - **`./run.sh check-gate-qualification`** — **335/335 KILLED, NOT KILLED 0**, as the
16
+ release-gate MUST step. The first run of this cut reported **334/335**: the single
17
+ miss was `PACK-INSTALL-PIN-MATCHER-BOUNDED` reading `MUTANT-STALE` because `5c1bda5`
18
+ moved `run.sh`'s pin matcher to the 0.84.4 floor and left that mutant's anchor on
19
+ 0.84.3, so it matched 0× and the gate had silently stopped asking its question.
20
+ Repaired in `2e7ceb4` and re-run green. A rotted anchor is recorded here rather than
21
+ quietly fixed, because "N killed" only means something when N is the whole set.
22
+ - **`LIVE=1 ./run.sh release-gate /tmp/entwurf-release-gate-0.16.1.m0LUjr --cut`** —
23
+ **MUST: PASS=23 FAIL=0 SKIP=0**, **BEHAVIOR: PASS=1 FAIL=0 SKIP=0**. Log at
24
+ `/tmp/entwurf-release-gate-0.16.1.m0LUjr/release-gate.log`. `smoke-mux-lifecycle-live`
25
+ failed the first run on a pi-native nonce callback that never arrived inside its 300s
26
+ window (the codex rail was measured healthy at the time: 5h 23%, weekly 77%), and
27
+ passed on re-run — recorded because a LIVE step that needed a retry is not the same
28
+ evidence as one that passed first time.
29
+ - **#72 launch/observation receipts** — the launcher's two field claims are not
30
+ unit-provable, so they were measured on the host that produced the failure:
31
+ `/proc/<pid>/cmdline` carries no vendor name, the janitor's own Phase 1 selector run
32
+ against the real `ps` row with its age threshold forced to 0 selects nothing, and a
33
+ real SIGTERM still ends at exit 0 with the frame on stderr. Commands and output in
34
+ `scripts/raw-acp-child-exit-measure/README.md`.
35
+
36
+ ### Added
37
+
38
+ - **`setup` composes OMP — the fifth backend is now in the one command.** v0.16.0 admitted OMP
39
+ as a D6 citizen with installers, doctors and inverses for every unit, but `setup_all` had no
40
+ `omp` branch: on an operator host the released package printed a **green** setup summary while
41
+ OMP had nothing installed — no extension, no `mcp.json` entry, no garden id on the status line.
42
+ Presence-driven now, same shape as Copilot: `omp` on PATH composes four independent named
43
+ components (birth → MCP hand → the `tools.xdev` operator setting → receiver), absent is one
44
+ zero-state SKIP, and a detected-but-incomplete unit is a named FAIL owning a nonzero exit.
45
+ Gate: `smoke-setup-verdict` S-8 drives the real composition against a stub vendor and asserts
46
+ every install-state, both extension units in the sandbox agent dir, the effective `xdev-off`
47
+ reading, and idempotence on a second run; S-1 pins `OMP_BIN` absent and requires the SKIP row.
48
+ - **`install-omp-config` / `uninstall-omp-config` — the operator setting is a unit, not a
49
+ documentation step.** `tools: xdev: false` was a hand-edit in `docs/setup-clean-host.md`, so a
50
+ host that skipped it registered every entwurf tool behind the vendor's `xd://` wrapper and the
51
+ doorbell announced a tool the model could not call. The writer owns exactly the lines it adds
52
+ (recorded in its install-state), refuses a symlinked config, refuses a config it cannot parse,
53
+ and refuses an EXPLICIT operator `tools: xdev: true` **by name** rather than overwriting a
54
+ decision — setup surfaces that as a component FAIL for the operator to resolve. The inverse
55
+ takes back only the recorded lines and refuses when the file changed since install.
56
+ - **`docs/adding-a-harness.md` step 10 — an onboarding is not finished until `setup` composes
57
+ it.** The rule the OMP gap forced, with the five conditions a new harness owes (presence-only
58
+ trigger, one independent component row per unit, compose only what an inverse can undo,
59
+ operator settings are units with a refusing writer, pin the probe seam and add an aggregate
60
+ cell) and the shape to watch for: two closed parity loops with no gate owning the edge between
61
+ them.
62
+
63
+ ### Changed
64
+
65
+ - **pi floor moved to 0.84.4.** devDependency exact pin, the three peer ranges (`>=0.84.4 <0.85`),
66
+ the `check-pack-install` runtime pins, the pin-leak matcher's version boundary with its synthetic
67
+ fixture, and the pnpm lockfile all move together — the floor is one fact with several spellings,
68
+ and a partial bump is how they drift apart.
69
+
70
+ ### Fixed
71
+
72
+ - **#72 — the ACP Claude child is no longer selected by the host janitor that was killing it, and
73
+ a caught signal survives the vendor erasing it.** The child was not crashing: it was being **SIGTERMed from outside**. A
74
+ janitor installed on the operator's host for a *different* harness (openclaw's acpx, upstream
75
+ PR #245) selects the vendor process name `claude-agent-acp` by **argv substring** and kills
76
+ anything older than 900s. entwurf **retains** its child across turns, so that child's age is the
77
+ age of the SESSION, not of a turn — every session past 15 minutes was shot at every 5 minutes.
78
+ The vendor's own handler then turned the signal into `dispose(); process.exit(0)`, so the death
79
+ reached entwurf as exit code 0 with no signal, indistinguishable from a clean shutdown. That is
80
+ why three diagnosis passes retired four candidate causes and still missed it. Measured: **12 of
81
+ 12** anomalous terminations across two boots correlate with a reap, pid- and timestamp-locked —
82
+ including the sample that opened the issue (2026-07-30) and the original field report
83
+ (2026-08-16); receipts in `scripts/raw-acp-child-exit-measure/`. Two changes, both inside the
84
+ issue's repair fence (no timeout, no replay, no watcher/supervisor/hidden retry): (a) the default
85
+ launch is now an **entwurf-owned launcher** (`pi-extensions/lib/acp/claude-acp-launch.js`) that
86
+ imports the vendor **in-process** — same process, so there is no child to restart and it cannot
87
+ become a supervisor — carrying a name no vendor-name scanner matches, consuming no argv (the
88
+ vendor's own `argv.slice(1)` self-reinvocation still works), standing down if it would otherwise
89
+ be the only signal listener (an observer that suppressed default termination would make the
90
+ process signal-immune), and dying nonzero without retry if the import fails; (b) a caught
91
+ SIGTERM/SIGINT is recorded as a **typed lifecycle fact** on its own line
92
+ (`launch observed SIGTERM before child exit (sender not attributed)`) via an exact full-line
93
+ control frame that is consumed out of the vendor stderr tail — the tail stays vendor evidence,
94
+ the observation is entwurf's, and sender attribution is explicitly NOT claimed (that needs the
95
+ host journal). `CLAUDE_AGENT_ACP_COMMAND` stays verbatim: an operator who names their own command
96
+ owns the result. **Known trade:** after the name split a host janitor can no longer collect
97
+ entwurf's children even when they genuinely leak, so entwurf now owns that cleanup story.
98
+ **Read the order right: the cause was the host's janitor, and its owner has retired it**
99
+ (`nixos-config e283d92`, 2026-09-01 — `acp-zombie-reaper.timer` disabled). Measured there:
100
+ PR #245 is CLOSED, not merged; that deployment runs `acp.enabled=false` with no ACP agent;
101
+ acpx is not even installed in the running image (`/app/node_modules/acpx` absent, its `.bin`
102
+ entries dangling symlinks); and every reap since the 2026-06-10 ACP removal was a single-process
103
+ misfire matching our pid table. So the change here is **not what stops the symptom** — the
104
+ retirement is. It is defense-in-depth against the class, and that class is wider than this
105
+ issue: the selector reads the WHOLE `ps` line, so on a harness that puts the prompt in argv, a
106
+ session merely *discussing* `claude-agent-acp` matched. Measured 2026-09-01 with zero ACP
107
+ processes alive, the script's own counter read `alive_acp=3` — three ordinary agent sessions,
108
+ one of them the session investigating it. A launcher whose name we own survives the next such
109
+ janitor too. Gates:
110
+ new `check-acp-launch-namespace` (2 mutants) plus `check-acp-prompt-lifecycle` CELLs 12–13,
111
+ where 13 is the negative sibling holding that vendor prose mentioning `SIGTERM` can never forge
112
+ the observation (3 mutants). The primary repair still belongs to the janitor's owner — a cleaner
113
+ should scope by a positive marker it owns, not by a name anyone may share.
114
+
115
+ - **A vendor-written `config.yml` no longer reads as `unreadable`.** omp's own settings writer
116
+ emits `modelRoles:` followed by an indented `{}`, and `scripts/omp-tool-surface.py`'s
117
+ block-only reader returned None for the WHOLE file on that shape — so an untouched operator
118
+ config classified as unreadable and `doctor-omp-mcp` went RED for a reason unrelated to
119
+ `tools.xdev` (measured on a real host, omp 18.0.0). The reader now parses flow collections in
120
+ value position and as a whole child block. `check-omp-fresh-preflight` gains the vendor shapes
121
+ plus `[QK:OMP-XDEV-VENDOR-SHAPE-READABLE]` direct assertions — agreement between the two
122
+ readers could never catch this, since both collapse `unreadable` and `true` into "not false".
123
+ - **The Copilot plugin-row grammar admits the vendor's state token.** Copilot CLI 1.0.81 prints
124
+ `<qualified> (v0.1.0) (enabled)` plus an indented `from <path>` continuation line;
125
+ `copilot_exact_row_version` read the version as `0.1.0) (enabled`, called the row malformed,
126
+ and refused the birth install on a host whose plugin was installed and enabled — `entwurf
127
+ setup` reported `copilot-birth: FAIL`. The grammar now admits exactly one optional
128
+ `(enabled)`/`(disabled)` token; any other trailing token is still malformed. The fake vendors
129
+ in `scripts/fake-copilot-vendor.sh` and `check-copilot-birth-hook.ts` emit the 1.0.81 shape,
130
+ with `[QK:COPILOT-ROW-STATE-TOKEN-ADMITTED]` holding both directions.
131
+
132
+ ## 0.16.0 - 2026-08-31
133
+
134
+ This release admits **OMP (`omp`) as the fifth garden backend** and closes the admission
135
+ contract that let a citizen ship without the legs that make it reachable. Bundles A, B and C
136
+ are one arc — birth and identity, addressed receive, visible fresh — and the durable half is
137
+ the general rule they forced: from OMP onward, the FIRST release of a harness admitted under
138
+ the #82 contract owes its clause 7 visible-fresh receipt as a release-gate MUST step, its
139
+ citizen↔fresh parity edge inside `pnpm run check:full`, and two cross-harness dispatch
140
+ receipts. Every OMP LIVE claim below is one Linux host, one model, one accepted run; that
141
+ limit is stated once in Notes and is not softened anywhere above it. (#87)
142
+
143
+ ### Added
144
+
145
+ - **OMP is an addressable garden citizen.** `omp` enters the backend registry (`wakeMode`
146
+ `self-fetch`, `deliveryLevel` `D6`, `nativeIdLabel` `sessionId`) and
147
+ `pi-extensions/meta-bridge-omp.ts` mints it: an omp hook IS an in-process extension, so
148
+ birth, the scope fence, the status line and the sender marker are one module. Both birth
149
+ edges are bound — `session_switch` is where `/new`, fork and in-TUI resume re-fire — and
150
+ every mode but `mode === "tui"` is refused before any write. The fence is the vendor's own
151
+ mode and nothing else: `hasUI` is true under rpc/rpc-ui/ACP, so a fence built on it would
152
+ admit exactly the sessions this refuses. A task subagent mints nothing. The step-6 join is
153
+ one process, so the marker is keyed to `process.pid`, never `process.ppid`. Gates:
154
+ `check-omp-birth-hook`, `smoke-omp-bridge-state`, `scripts/mutants/omp-birth.json`. (#87)
155
+ - **An omp-native MCP hand that shadows the borrowed Claude import.** The writer targets omp's
156
+ own `<omp agent dir>/mcp.json` in the vendor's own writer shape, and the server key is a
157
+ PINNED LITERAL `entwurf-bridge` — that pin is the entire mechanism. On a host that ever used
158
+ Claude Code, omp already imports an `entwurf-bridge` server labeled
159
+ `external-mcp/claude-code`; native provider priority beats the import and dedupe is
160
+ first-wins on the key with `equivalent()` never consulted, so a byte-identical key suppresses
161
+ the import while a different key would load BOTH and one of them would keep introducing an
162
+ omp session as Claude Code. `disabledServers` is never the hide-import tool — suppression is
163
+ by name and a suppressed item still claims the key — so the writer refuses to install under a
164
+ denylist of its own key and the doctor is red while one exists. `doctor-omp-mcp` adds an
165
+ EFFECTIVE-source read (native-wins / import-wins / both-suppressed / no-entry) and labels it
166
+ what it is: a configuration read of vendor precedence, never a runtime receipt. Gate:
167
+ `smoke-omp-mcp-state`. (#87)
168
+ - **OMP receives addressed messages — the garden is no longer one-way for it.**
169
+ `pi-extensions/meta-bridge-receive-omp.ts` joins the citizen in the SAME process, holds an
170
+ `fs.watch` on its mailbox signal, and rings an announce-only doorbell through the vendor's
171
+ `pi.sendUserMessage` — measured to live on the factory object rather than the event ctx, and
172
+ measured to start a turn on an idle host with zero typing. The model drains with
173
+ `entwurf_inbox_read`, and that read is the receipt. The `/new` unarm is rail-specific: the
174
+ watch lives inside the operator's TUI, where pid plus start-key cannot see a citizen change
175
+ underneath a living process, so the old garden id is explicitly unarmed and the replacement
176
+ armed. Bounded arm defer, watch-error, vanished-signal and overlapping-edge paths are all
177
+ fail-closed. Install, uninstall and doctor surfaces ship with it, plus
178
+ `check-omp-receive-arm` with `scripts/mutants/omp-receive.json`, `smoke-omp-receive-state`
179
+ and `smoke-omp-receive-live`. (#87)
180
+ - **`entwurf_fresh_call` opens omp on all three public surfaces.** A five-axis pre-mutation
181
+ preflight decides the launch before the tmux window exists, so a missing prerequisite is a
182
+ named refusal instead of a dead window; the fifth axis is omp-specific (`tools.xdev !==
183
+ true`). There is NO positional prompt. The fixed registered flag `--entwurf-bootstrap`
184
+ carries a closed `{v,target,nonce,task}` grammar — an unknown key is a refusal — and the
185
+ installed birth extension runs a two-stage bootstrap: bounded readiness polling over both
186
+ public tool snapshots, a callback-ONLY prompt, then the task released only by the exact
187
+ successful `tool_result` (stored `toolCallId`, tool name, target, nonce and
188
+ `isError === false` all matching) and delivered at that same session's next `turn_end`. That
189
+ shape is a measurement, not a preference: the positional candidate opened its window, minted
190
+ its citizen, received the framing byte-identical, and answered the literal text `ACK` with
191
+ zero tool calls — the vendor's interactive UI defers MCP discovery while the positional
192
+ `initialMessage` prompts straight after `mode.init()`, so the turn began roughly 830ms before
193
+ the tool it was told to call existed. (#87)
194
+ - **`check-harness-admission-parity` — the missing edge between two closed loops.** Every
195
+ backend in `META_CITIZEN_BACKENDS` must appear in `FRESH_CALL_BACKENDS` or be a declared
196
+ pre-#82 legacy admission whose exception a reader can find in `DELIVERY.md`. Registry↔citizens
197
+ and surfaces↔fresh-set each already had a guard, but no file imported both constants, which is
198
+ how omp could pass the entire floor as a D6 citizen that `entwurf_fresh_call` cannot open. The
199
+ gate went red on `Unaccounted: omp` the moment it was added and green when Bundle C closed it.
200
+ It runs inside `check:contracts`, and therefore inside `pnpm run check:full`. (#87)
201
+ - **Two OMP LIVE steps in the release-gate MUST tier.** `smoke-omp-fresh-live` is clause 7's
202
+ worked instance. `smoke-omp-receive-live` reads the capability registry and decides its own
203
+ outcome — no drainable mailbox is a protocol SKIP, which `--cut` reads as red; a registry
204
+ claiming a receive rail with no acceptance body here is a FAIL. Neither is a hardcoded pass,
205
+ and the receive step was wired while omp was still outbound-only, so the one-way boundary
206
+ stopped being prose a cut could pass over in silence. (#87)
207
+ - **`docs/adding-a-harness.md` gains the shipped map and the release stop.** A comparative table
208
+ of the five admitted backends — lineage, receive rail, callback dialect, fresh launch form —
209
+ sits at the top so the variety is expected rather than re-derived at each admission. It pins
210
+ two lessons: lineage does not choose the rail (omp is pi underneath and rides Claude's mailbox
211
+ shape, because the rail follows the measured wake surface; what ancestry does instead is
212
+ concentrate the danger, since a shared env vocabulary is exactly where a fork splits a store),
213
+ and the doorbell doctrine — a hard bridge of measured dialects, explicit doorbells and honest
214
+ rejects is what makes the thing on the other side a peer rather than a disposable worker. The
215
+ release stop then states both executable halves of the admission rule plus the cross-harness
216
+ leg. (#87)
217
+
218
+ ### Changed
219
+
220
+ - **`setup` is the install story on all three entry shapes.** npm global, npm project-local and
221
+ source clone now lead with `entwurf setup <project>`; `entwurf install <project>` is stated as
222
+ the narrower pi-wiring repair leaf that composes no harness. The 14-line native-harness paste
223
+ that read as the install recipe is now "repair and doctors" — one paragraph plus the command
224
+ names, with every per-unit installer, doctor and inverse retained, because a single broken unit
225
+ must still be redoable alone. The user-scope ownership matrix and the manual MCP registration
226
+ paths moved to the documents that already own them. Stale live claim corrected: after an
227
+ upgrade the operator was told to rerun every owned installer; `setup` re-composes them.
228
+ `package.json` gains the `copilot` keyword, shipped since 0.15.0 but unlisted. (#86)
229
+ - **OMP meta roots resolve as one indivisible bundle, never through `piAgentDir()`.** omp is a pi
230
+ fork and inherits pi's env vocabulary, so `PI_CODING_AGENT_DIR` means "pi's persistence root"
231
+ to entwurf and "my agent dir" to the vendor — a plain `omp --profile work` sets it. One pure
232
+ resolver leaf now serves both consumers, the in-process birth extension and the omp-labeled
233
+ bridge child, so agreement is by construction rather than two places computing the same thing.
234
+ A relative override fails closed instead of letting cwd become an authority: measured, the same
235
+ relative value resolved to two different stores because the extension's cwd is wherever the
236
+ operator launched omp while the doctor's is the repository. Installers refuse rather than guess
237
+ under an inherited `PI_CODING_AGENT_DIR` / `PI_CONFIG_DIR` / `PI_PROFILE`. (#87)
238
+ - **`PI_SESSION_ID` and `PI_AGENT_ID` are scrubbed at the launch seam for every backend.**
239
+ Measured on the operator host: a tmux server env carrying `PI_SESSION_ID` was inherited
240
+ verbatim by a new pane, so a sibling's bridge child could phone home under another citizen's
241
+ identity. The scrub is at the seam, not per backend. (#87)
242
+ - **The deterministic floor composes the omp gates.** `check:hermetic` gains
243
+ `smoke-omp-bridge-state`, `smoke-omp-mcp-state`, `smoke-omp-receive-state`,
244
+ `check-omp-receive-arm` and `check-omp-fresh-preflight`; `check:contracts` gains
245
+ `check-harness-admission-parity`. Both tiers still exclude the separately scheduled
246
+ `check-gate-qualification`, and `setup` still does not compose omp. (#87)
247
+ - **`docs/adding-a-harness.md` step 5 now says to measure the invocation FORM, not only the tool
248
+ name.** omp mounts MCP tools as `xd://` virtual devices by default (`tools.xdev`), so a correct
249
+ tool NAME is still not a callable function: the model must read the device for a schema that
250
+ `tools.xdevDocs` keeps off-prompt, then write JSON to the same path to execute. Measured on omp
251
+ 18.0.0, a plain send under that default listed peers and then reported delivery with nothing
252
+ enqueued, and the mount also hid omp's own lsp, debug, browser and ast_edit — 11 devices in
253
+ all. `tools.xdev: false` restores every enabled tool top-level and leaves plan mode's
254
+ `xd://propose` finalization intact. (#87)
255
+
256
+ ### Fixed
257
+
258
+ - **The bridge boot entry was in no typecheck program.** `mcp/tsconfig.json` extended the root
259
+ config without re-declaring `exclude`, so the inherited `"mcp"` resolved to `<repo>/mcp` and
260
+ filtered out everything its own `include` added; `mcp/entwurf-bridge/src/index.ts` — the
261
+ 785-line bridge boot entry — was the only tracked `.ts` in the repo in no `tsc --noEmit`
262
+ program at all, and nothing imports it, so nothing rescued it through the import graph. Orphan
263
+ census 1 line before, 0 after; the mcp program's repo-file count moved 27 → 54, all product
264
+ sources. The emit path is untouched, so there is no product runtime change.
265
+ - **`entwurf_self` stopped rendering a mailbox nothing drains.** `metaDeliveryDomain` was derived
266
+ as native-push or self-fetch, so an omp citizen was handed a `mailboxPath` with no drainer. It
267
+ now has three values: `nativePushSupported` → native-push, else the decider's mailbox seam →
268
+ self-fetch, else none. Dispatch and `wakeMode` are untouched. (#87)
269
+ - **`doctor-omp-mcp` reads `tools.xdev` on the runtime axis.** An absent file or key applies the
270
+ vendor default (xdev on, empty inline allowlist) and is RED while the native hand is the
271
+ effective source; `xdev: false` is ok and a covering `xdevInlineDevices` glob is
272
+ ok-with-note. Ownership stays a separate question, and the smoke never touches the host
273
+ `config.yml`. (#87)
274
+ - **The bootstrap epoch now ends before the birth edge can bail.** Epoch termination depended on
275
+ reaching `startOmpBootstrap`, so a later birth edge that bailed early — a refused envelope or a
276
+ throwing upsert — skipped the invalidation, and on a same-id resume both handler fences still
277
+ passed, leaving the earlier defect shape alive in a narrow window. One rule, one owner:
278
+ `endBootstrapEpochOnLaterEdge` runs right after the mode fence, before the envelope is read,
279
+ and the consumed branch delegates to the same helper. Zero new state, zero new exports. (#87)
280
+ - **The omp-fresh qualification manifests were settled by measurement.** The first standalone
281
+ qualification run on the Bundle C bytes caught what static exactness could not: four mutants
282
+ whose claim and signature named different cells, one QK token minted twice in its gate source,
283
+ twelve find hunks left stale by the bundle's own refactor and the amendment's rewiring, and one
284
+ kill-site guess that died at an anonymous cell. Every repair was adjudicated by
285
+ apply/gate/revert measurement rather than by guess, and kill sites the floor disputed were
286
+ moved to where the kill actually lands. Separately, two mutants had been added to the
287
+ self-address lane without extending `EXPECTED_LANE_MUTANTS` — exactly the drift that assertion
288
+ exists to catch; the declared contract now says 5. (#87)
289
+ - **Two packaging omissions in the omp units.** `pi/meta-bridge-omp/entwurf-meta-omp/package.json`
290
+ and `pi/omp-receive/entwurf-receive-omp/package.json` were absent from `files[]`, so
291
+ `install-omp-receive` died in the installed package while passing from a checkout. (#87)
292
+ - **`check-pack-install` pins the transitive `@earendil-works/pi-telemetry` to 0.84.3.** Upstream
293
+ published the 0.84.4 family on 2026-08-28; `pi-agent-core@0.84.3` and `pi-ai@0.84.3` both carry
294
+ a `^0.84.3` caret on it, so the lockfile-less fresh-temp install floated to 0.84.4 and the leak
295
+ assertion failed closed in the `install-surface` CI job. The pin keeps the verified 0.84.3
296
+ constellation ours to hold; the leak assertion still guards every other pi package and any
297
+ future closure growth. The 0.84.4 bump itself remains a separate hard-cut lane.
298
+ - **The omp doctor treated Bundle C's empty tmux scrub as inherited identity.** `tmux -e NAME=`
299
+ writes `PI_SESSION_ID=` / `PI_AGENT_ID=` present-but-empty; authoritative readers trim and
300
+ require truthy values, so empty and absent are the same answer. The doctor was presence-testing
301
+ those names, which turned `check:full` red whenever a visible fresh omp citizen was alive.
302
+ It now flags only a nonblank value, and the hermetic smoke hands it fixture pids rather than
303
+ the host `pgrep`. (#87)
304
+
305
+ ### Verification
306
+
307
+ Each receipt carries its own scope; none of them is transferable to another commit or host.
308
+
309
+ - **`./run.sh check-gate-qualification`** — **324/324 KILLED, NOT KILLED 0**, standalone on the
310
+ Bundle C candidate (`fd5e462`), independently re-verified by a second model on the gate
311
+ predicate plus a full-lane rescan and a 3/3 sample re-kill.
312
+ - **`pnpm run check:full`** — exit 0 in 430s on that same candidate, after the `build-bridge`
313
+ refresh the stale-dist tripwire demanded.
314
+ - **Clause 7 LIVE** — `smoke-omp-fresh-live` green 2026-08-30, 21 assertions, oracle / omp 18.0.0
315
+ / `openai-codex/gpt-5.6-sol`. The sibling's garden `20260830T192913-df52b9` came from the
316
+ CALLBACK's sender envelope and never from the launch receipt; exactly one new citizen;
317
+ `lastEnqueuedAt 10:29:19.179Z` / `lastReadAt 10:29:24.673Z`, with the drain visible in that
318
+ session's own transcript for its own garden id. It took two runs: the first went red on
319
+ stage-two delivery alone, which is the argument for making it a MUST.
320
+ - **Receive acceptance (D6)** — 2026-08-30, oracle, omp 18.0.0: garden `20260830T140819-116f6a`,
321
+ `lastEnqueuedAt 05:08:20.555Z` / `lastReadAt 05:08:23.958Z`. D3 isolation is PROVEN, not
322
+ pending: with two live armed omp citizens, one addressed enqueue rang exactly one doorbell
323
+ while the sibling persisted no transcript and kept an empty mailbox.
324
+ - **Cross-harness leg** — 2026-08-31, the first bidirectional live-turn roundtrip on the committed
325
+ bytes: a claude-code citizen opened a fresh omp sibling through the public surface (callback
326
+ sender garden `20260831T124226-eac41a`), dispatched an addressed `entwurf_v2` into it, and the
327
+ omp citizen's own live turn drained the doorbell and replied into the claude mailbox.
328
+ - **`LIVE=1 ./run.sh release-gate <scratch> --cut`** — **MUST PASS=23 FAIL=0 SKIP=0,
329
+ BEHAVIOR PASS=1 FAIL=0 SKIP=0, `cut: OK`.** `check-gate-qualification` inside the gate
330
+ killed **325/325** mutants across 36 lanes. Log:
331
+ `/tmp/entwurf-release-gate-0.16.0.run2.DfMtRj/release-gate.log` (SHA-256
332
+ `2df4f6d4bddd563431ca41d2a43074f2cb6ccf9926ec6e8c7323de1a6ddf8192`).
333
+ A first attempt on the same versioned tree (scratch `.../entwurf-release-gate-0.16.0.AyHcIJ`)
334
+ hit one MUST FAIL in `smoke-entwurf-v2-matrix-live` C1b: the hidden-store resident did not
335
+ birth a record inside the 30s boot window. The same smoke rerun standalone passed 17 checks
336
+ in 4.9s; the rerun cut above is the acceptance. `pnpm run check:full` on the versioned tree
337
+ was exit 0 in 432s before P5.
338
+
339
+ ### Notes
340
+
341
+ - **Evidence limit.** Every OMP LIVE claim above is one Linux host (oracle, ARM), one model, one
342
+ accepted run. Multi-host, multi-model and repeated fresh calls inside one process are not
343
+ claimed and are not evidenced here.
344
+ - **Operator prerequisite.** `tools: xdev: false` in `~/.omp/agent/config.yml`. Under the vendor
345
+ default the doorbell would name a tool the model cannot call, so both the fresh preflight and
346
+ the LIVE smokes check it as a precondition. `setup` does not compose omp.
347
+ - Measured against **omp 18.0.0** while the vendor announces 18.0.11. The `mode === "tui"`
348
+ discriminator, the `xd://` behaviour and the five measurement cells recorded in
349
+ `scripts/raw-omp-measure/README.md` §M7 are re-measurement targets on upgrade.
350
+ - Copilot's operator-metered clause 7 exception is preserved, not reopened. The release stop
351
+ applies from OMP onward, forward-only.
352
+ - The cross-harness leg's deterministic half — every post-contract citizen backend having a wired
353
+ cross-harness LIVE step or a declared metered exception — is an owed follow-up. Until that gate
354
+ lands the rule is prose, and `docs/adding-a-harness.md` says so in place.
355
+ - Pi `0.84.4` remains a separate hard-cut lane.
356
+ - #90 is measured but unfixed: installed Claude Code sends `model` as a STRING on the interactive
357
+ `SessionStart` envelope and omits it entirely in print mode, while the reader accepts only the
358
+ object shape, so claude-code records carry no model. Widening the reader and pinning both shapes
359
+ in a fixture is a separate lane.
360
+
7
361
  ## 0.15.1 — 2026-08-27
8
362
 
9
363
  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,