@junghanacs/entwurf 0.13.0 → 0.13.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 (72) hide show
  1. package/AGENTS.md +5 -3
  2. package/BASELINE.md +57 -203
  3. package/CHANGELOG.md +19 -0
  4. package/CONTRIBUTING.md +1 -1
  5. package/DELIVERY.md +117 -261
  6. package/README.md +68 -408
  7. package/VERIFY.md +58 -87
  8. package/demo/README.md +1 -1
  9. package/docs/acp-backend-rail.md +231 -0
  10. package/docs/external-mcp-host.md +132 -0
  11. package/docs/fresh-cut-policy.md +99 -0
  12. package/docs/setup-clean-host.md +123 -328
  13. package/mcp/entwurf-bridge/dist/mcp/entwurf-bridge/src/index.js +21 -48
  14. package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/acp-client.js +12 -0
  15. package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/backend-adapter.js +11 -10
  16. package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/config.js +1 -1
  17. package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/engraving.js +42 -1
  18. package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/models.js +4 -4
  19. package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/overlay.js +3 -3
  20. package/mcp/entwurf-bridge/src/index.ts +21 -48
  21. package/package.json +12 -9
  22. package/pi-extensions/entwurf-control.ts +20 -49
  23. package/pi-extensions/lib/acp/acp-client.ts +22 -0
  24. package/pi-extensions/lib/acp/augment.ts +42 -3
  25. package/pi-extensions/lib/acp/backend-adapter.ts +12 -11
  26. package/pi-extensions/lib/acp/backend.ts +305 -36
  27. package/pi-extensions/lib/acp/config.ts +1 -1
  28. package/pi-extensions/lib/acp/engraving.ts +43 -1
  29. package/pi-extensions/lib/acp/event-mapper.ts +16 -13
  30. package/pi-extensions/lib/acp/models.ts +4 -4
  31. package/pi-extensions/lib/acp/overlay.ts +3 -3
  32. package/prompts/engraving.md +13 -5
  33. package/run.sh +244 -108
  34. package/scripts/check-acp-carrier-augment.ts +246 -10
  35. package/scripts/check-acp-cortex.ts +5 -5
  36. package/scripts/check-acp-prompt-lifecycle.ts +565 -0
  37. package/scripts/check-acp-sdk-surface.ts +60 -21
  38. package/scripts/check-acp-stop-reason.ts +342 -0
  39. package/scripts/check-entwurf-v2-surface.ts +41 -0
  40. package/scripts/check-gate-qualification.ts +6 -3
  41. package/scripts/check-probe-ordering.ts +39 -7
  42. package/scripts/check-release-gate-outcomes.ts +332 -0
  43. package/scripts/lib/live-skip.ts +33 -0
  44. package/scripts/lib/probe-acp-turn.ts +33 -12
  45. package/scripts/lib/step-outcome.sh +88 -0
  46. package/scripts/mutants/acp-augment.json +106 -0
  47. package/scripts/mutants/acp-cortex.json +2 -2
  48. package/scripts/mutants/acp-prompt-lifecycle.json +100 -0
  49. package/scripts/mutants/acp-stop-reason.json +80 -0
  50. package/scripts/mutants/probe-ordering.json +27 -5
  51. package/scripts/mutants/release-gate.json +105 -0
  52. package/scripts/mutants/v2-surface.json +26 -15
  53. package/scripts/smoke-acp-bundled-mcp-live.ts +4 -3
  54. package/scripts/smoke-acp-carrier-augment-live.ts +2 -2
  55. package/scripts/smoke-acp-cortex-live.ts +8 -14
  56. package/scripts/smoke-acp-long-turn-live.ts +185 -0
  57. package/scripts/smoke-acp-mcp-live.ts +2 -2
  58. package/scripts/smoke-acp-memory-containment-live.ts +2 -2
  59. package/scripts/smoke-acp-ordering-probe-live.ts +2 -2
  60. package/scripts/smoke-acp-overlay-live.ts +2 -2
  61. package/scripts/smoke-acp-provider-live.ts +2 -2
  62. package/scripts/smoke-acp-raw-turn-live.ts +3 -3
  63. package/scripts/smoke-acp-session-reuse-live.ts +2 -2
  64. package/scripts/smoke-acp-skill-live.ts +2 -2
  65. package/scripts/smoke-acp-socket-citizen-live.ts +4 -3
  66. package/scripts/smoke-acp-v2-send-live.ts +4 -3
  67. package/scripts/smoke-agy-native-push-live.ts +4 -3
  68. package/scripts/smoke-claude-native-resume-live.sh +13 -3
  69. package/scripts/smoke-entwurf-chain-live.ts +352 -0
  70. package/scripts/smoke-entwurf-v2-matrix-live.ts +2 -2
  71. package/scripts/smoke-entwurf-v2-spawn-resume-live.ts +2 -4
  72. package/scripts/smoke-meta-install-state.sh +4 -0
@@ -1,284 +1,119 @@
1
- # Clean-host setup walk-through
2
-
3
- End-to-end install of **entwurf** on a host with only `git` available — no
4
- node, no npm package, no pi binary, no dotfiles. The point is to validate the
5
- public install surface as an outside user would experience it.
6
-
7
- > **Scope.** This is the current entwurf 0.12.x install recipe, including the
8
- > 0.12.7 Antigravity (`agy`) citizen surface. The base package install is
9
- > **neutral npm**, not `pi install npm:...`. Pi is an optional adapter lane for
10
- > the ACP provider / control-socket runtime. Installed packages under
11
- > `node_modules` must not run raw `.ts` bridge, doctor, or native-hook helpers
12
- > through Node strip-types.
13
-
14
- `entwurf` is a garden-citizen dispatch substrate and meta-bridge. It does not
15
- provide, copy, or mediate backend credentials — it lets the official backend CLI
16
- or the pi adapter read whatever auth the user already trusts on the host
17
- (AGENTS.md Hard Rule #9).
18
-
19
- ## Reference target
20
-
21
- Written against a clean **Linux** host (Ubuntu / Debian / NixOS) reachable via
22
- SSH, here called `cleanhost`. `nvm` keeps the Node path independent of the distro.
23
- The neutral npm package may install elsewhere, but Linux is the only currently
24
- certified Claude meta-bridge axis. macOS has no `/proc` bridge discovery
25
- and is not yet verified/certified for this cut, so its installer refuses new wiring
26
- and its strict doctor stays `NOT CERTIFIED`/nonzero.
27
- This is not permanent; future native validation may reopen the lane, while Darwin
28
- uninstall remains available for older managed state.
1
+ # Clean-host setup
29
2
 
30
- ```bash
31
- ssh cleanhost 'uname -a; whoami; which git node npm pi claude agy 2>/dev/null'
32
- # expect on a fully clean host: git present, node/npm/pi/claude absent
33
- ```
3
+ Current operator recipe for a fresh Linux desktop/workstation. The neutral npm
4
+ package can install elsewhere, but Claude's garden-native meta-bridge is certified
5
+ only on Linux because its strict live-owner join uses `/proc`.
6
+
7
+ ## Requirements
34
8
 
35
- ### What the automated Linux consumer already proves
36
-
37
- The required CI job `artifact-consumer` runs `check-install-container` against one
38
- candidate tarball in a Node 24 Linux image that has never seen the checkout. It
39
- records the artifact sha256 plus image id/repository digest, mounts only that tarball
40
- read-only, installs globally as non-root through an isolated npm prefix, resolves all
41
- five bins through PATH, freezes the package root, checks the regular-file path+sha256
42
- manifest across `install-meta-bridge`, boots MCP `tools/list`, and drives the strict
43
- doctor. This closes the installed package shape; it does **not** replace this real-host
44
- walk-through. Its fake Claude CLI, planted plugin cache, stand-in owner, and `/proc`
45
- bridge are fixtures, so they cannot prove native plugin installation, real hook spawn,
46
- or idle wake.
47
-
48
- Default CI lets the gate pack once into a temporary directory. Release acceptance
49
- instead preserves the `npm pack` output and passes its absolute path as
50
- `ENTWURF_CANDIDATE_TGZ`; the gate verifies package name/version, prints canonical
51
- path+sha256, and consumes that exact file without chmod/copy/re-pack. The accepted
52
- file is the one later published with `--tag repair` (full commands in VERIFY.md).
53
-
54
- The direct runtime complement is #51 B/B2: actual Claude sessions on one NixOS host
55
- showed 2.1.138 dropping `args` while reporting success and 2.1.217 honoring exec argv
56
- and waking on FileChanged exit 2. A target host is still accepted only after installing
57
- the released artifact, opening a new Claude session, and obtaining installed-doctor
58
- exit 0.
59
-
60
- ## Pin matrix
61
-
62
- | Component | Pin / floor | Source of truth |
9
+ | Component | Requirement | Needed for |
63
10
  |---|---|---|
64
- | Node | **`>=24.0.0`** — single supported axis, no Node 22 lane | `engines.node` (bound by `check-node-floor-coherence`) |
65
- | Claude Code | **`>=2.1.217`** the exec-form hook floor; an older Claude drops the hook's `args` silently and still reports success, so there is no fallback lane | `entwurf.claudeCodeFloor` (bound by `check-claude-floor-coherence`) |
66
- | npm | bundled with Node 24 | public package install path |
67
- | entwurf | `@junghanacs/entwurf` | neutral npm package; exposes `entwurf`, `entwurf-bridge`, `entwurf-statusline`, `entwurf-agy-statusline`, and `entwurf-agy-imprint` bins |
68
- | pi binary | **optional**, `@earendil-works/pi-coding-agent >=0.82.1 <0.83` | needed only for the pi adapter / ACP provider / spawn-bg resume lane |
69
- | Antigravity `agy` | **optional**, operator-installed/authenticated native CLI | needed only for the shipped native-push citizen lane; entwurf never moves its auth |
11
+ | Node | **`>=24.0.0`** | package and bridge runtime |
12
+ | npm | bundled with Node | package installation |
13
+ | entwurf | `@junghanacs/entwurf` | all lanes |
14
+ | pi | optional, `@earendil-works/pi-coding-agent >=0.83.0 <0.84` | ACP provider, control sockets, spawn-bg resume |
15
+ | Claude Code | optional, **`>=2.1.217`** the exec-form hook floor | Claude ACP auth/runtime and mailbox-backed native citizen |
16
+ | Antigravity `agy` | optional, operator-installed and authenticated | native-push citizen |
17
+ | Cortex Code | optional, operator-installed and authenticated | Cortex ACP backend |
70
18
 
71
- ## Stage 0 Node 24 via nvm
19
+ Claude Code >=2.1.217 is required for the managed exec-hook lifecycle. The package
20
+ never supplies or proxies backend credentials.
72
21
 
73
- ```bash
74
- ssh cleanhost
22
+ ## 1. Install Node and entwurf
75
23
 
76
- curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
77
- source ~/.nvm/nvm.sh
24
+ Use the host's normal Node 24 installation. With nvm:
78
25
 
26
+ ```bash
79
27
  nvm install 24
80
- nvm alias default 24
81
- node -v
82
- npm -v
28
+ nvm use 24
29
+ node --version
30
+ npm --version
83
31
  ```
84
32
 
85
- Drift points:
86
- - Corporate proxy / sudo policy may block `curl | bash`. Fallback: clone `nvm`
87
- via git and source `~/.nvm/nvm.sh` directly.
88
- - **Subshell trap**: `nvm install 24 | tail` runs in a pipe-subshell, so PATH
89
- changes do not reach the parent shell. Drop the pipe, or run `nvm use 24` in
90
- the same shell afterward.
91
-
92
- ## Stage 1 — neutral entwurf npm install
93
-
94
- Install the public package with npm. This does **not** require pi.
33
+ Global install is simplest when native harnesses should find stable bins from every
34
+ working directory:
95
35
 
96
36
  ```bash
97
37
  npm install -g @junghanacs/entwurf
98
-
99
- which entwurf
100
- which entwurf-bridge
101
- which entwurf-statusline
102
- which entwurf-agy-statusline
103
- which entwurf-agy-imprint
104
- entwurf --help | head -5
38
+ entwurf --help
39
+ entwurf check-bridge
105
40
  ```
106
41
 
107
- `@earendil-works/*` pi packages are optional peers. A neutral npm install should
108
- not pull them in as package dependencies. That separation is intentional: the
109
- MCP bridge can boot in Claude Code / Codex / Antigravity without pi present.
110
-
111
- ## Stage 2 — auth-free bridge boot
112
-
113
- Prove the installed MCP server answers `tools/list` from inside `node_modules`.
114
- This is the first `node_modules` strip-types regression fixed in 0.12.0: Node
115
- refuses `--experimental-strip-types` for `.ts` under `node_modules`, so the
116
- installed package must boot the prebuilt JS under `mcp/entwurf-bridge/dist/`.
117
- The same installed-vs-dev split closes every shipped `.ts`-at-runtime surface on
118
- that fence: the `doctor-meta-bridge` store-scan helper (0.12.4), the Claude plugin
119
- hook (0.12.5, compiled `dist/pi-extensions/meta-bridge-hook.js`), and the agy
120
- `PreInvocation` imprint (0.12.7, compiled `dist/scripts/agy-imprint.js`). Installed
121
- packages run tsc-emitted JS on these paths; dev clones keep transparent `.ts`
122
- source execution.
42
+ A project-local installation is also supported:
123
43
 
124
44
  ```bash
125
- node --input-type=module <<'JS'
126
- import { spawn } from 'node:child_process';
127
- const child = spawn('entwurf-bridge', { stdio: ['pipe', 'pipe', 'pipe'], env: { ...process.env, NODE_PATH: '' } });
128
- let out = '', err = '';
129
- const timer = setTimeout(() => { child.kill('SIGKILL'); console.error(err || 'timeout'); process.exit(1); }, 5000);
130
- child.stderr.on('data', d => err += d);
131
- child.stdout.on('data', d => {
132
- out += d;
133
- try {
134
- const msg = JSON.parse(out.trim());
135
- const names = (msg.result?.tools ?? []).map(t => t.name).sort();
136
- for (const n of ['entwurf_v2','entwurf_peers','entwurf_self','entwurf_inbox_read','entwurf_register_native']) {
137
- if (!names.includes(n)) throw new Error(`missing ${n}: ${names.join(',')}`);
138
- }
139
- clearTimeout(timer);
140
- child.kill('SIGTERM');
141
- console.log(names.join(','));
142
- } catch {}
143
- });
144
- child.stdin.write(JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'tools/list' }) + '\n');
145
- JS
45
+ mkdir -p ~/entwurf-smoke && cd ~/entwurf-smoke
46
+ npm init -y
47
+ npm install --save-dev @junghanacs/entwurf
48
+ npx entwurf check-bridge
146
49
  ```
147
50
 
148
- Expected: a comma-separated tool list containing all five current `entwurf_*`
149
- tools, exit code 0, no backend auth required. `entwurf_register_native` binds an
150
- already-running native conversation; it is not a fresh-spawn verb.
51
+ `check-bridge` is auth-free. It proves the installed prebuilt MCP server boots and
52
+ lists the five garden tools; it does not prove a backend model turn or native hook.
151
53
 
152
- ## Stage 3 — wire a project for the pi adapter / ACP plugin (optional)
54
+ ## 2. Optional pi adapter / ACP plugin
153
55
 
154
- If the host will run pi sessions or the Claude ACP provider through pi, install
155
- a compatible pi binary separately and wire the target project.
56
+ Install the exact release floor, then wire the project:
156
57
 
157
58
  ```bash
158
- npm install -g @earendil-works/pi-coding-agent@0.82.1
59
+ npm install -g @earendil-works/pi-coding-agent@0.83.0
159
60
  pi --version
160
61
 
161
- mkdir -p ~/entwurf-smoke
162
62
  cd ~/entwurf-smoke
163
63
  entwurf install .
164
- entwurf check-bridge
165
-
166
- # pi adapter/provider registration smoke
167
64
  pi -e "$(npm root -g)/@junghanacs/entwurf" --list-models entwurf
168
65
  ```
169
66
 
170
- Drift points:
171
- - `entwurf install .` writes `.pi/settings.json` and registers the bundled
172
- `entwurf-bridge`. (The `entwurf-targets.json` link is gone #50 C3 removed the
173
- target registry; a leftover operator link is inert.)
174
- - Older pi versions may silently miss the provider/extension surface. Use the
175
- pinned floor (`>=0.82.1 <0.83`) for release verification.
176
- - A host that only uses the external MCP bridge can skip this stage until it
177
- needs `owned-outcome` spawn-bg resume or pi-native control sockets.
67
+ The supported range is `>=0.83.0 <0.84`. It is a hard minimum: installing this
68
+ release onto a 0.82.x pi host upgrades the runtime rather than keeping the older
69
+ minor. A host using only the external MCP bridge can skip pi until it needs a
70
+ control socket or dormant `owned-outcome` resume.
71
+
72
+ For daily garden-native pi sessions:
73
+
74
+ ```bash
75
+ cd ~/entwurf-smoke
76
+ pi -e "$(npm root -g)/@junghanacs/entwurf" --entwurf-control
77
+ ```
78
+
79
+ The V3 record births the garden id; do not inject a pi session id manually.
178
80
 
179
- ## Stage 4 Claude Code meta-bridge (optional, garden-native native sessions)
81
+ ## 3. Optional Claude Code native citizen
180
82
 
181
- For an external Claude Code session to be replyable by garden id, install the
182
- meta-bridge plugin globally. This is still a neutral npm-package command; it
183
- registers Claude Code USER-scope MCP + the SessionStart hook. For this cut,
184
- perform and certify this stage on Linux only. The installer rejects Darwin with a
185
- “not yet verified/certified for this repair cut” diagnosis and the macOS doctor stays
186
- nonzero until future native validation supplies a real live-owner measurement.
83
+ First register the MCP bridge if the stable bin is not already present:
84
+
85
+ ```bash
86
+ claude mcp add --scope user entwurf-bridge entwurf-bridge
87
+ ```
88
+
89
+ Then install and certify the mailbox/self-fetch lifecycle:
187
90
 
188
91
  ```bash
189
92
  entwurf install-meta-bridge
93
+ # restart every already-open Claude Code process
94
+ # open a new Claude Code session
190
95
  entwurf doctor-meta-bridge
191
96
  ```
192
97
 
193
- > **Upgrades are not live-reload safe across this hook-launch cut.** Re-run `install-meta-bridge`, then restart **all already-open Claude Code sessions** before trusting send/receive. A new hook reached through the old cached command does not get the owner join it depends on, even though a meta-record may still land; reinstall pairs the artifact and the manifest, and restart makes the native process load that pair. This release also refuses Claude Code below `>=2.1.217` at install and doctor time — an older Claude silently drops the hook's `args`, runs the command alone, and still reports the hook as successful, so there is no fallback lane to fall into.
98
+ The supported floor `>=2.1.217` is enforced by package metadata through installer
99
+ and doctor gates. Older Claude versions validate an exec-form hook but silently drop
100
+ its `args` at runtime, so there is no shell-form fallback.
194
101
 
195
- On an installed package (`.../node_modules/@junghanacs/entwurf`), the doctor must
196
- not try to strip-types-run raw `.ts` helpers or hooks. In the output, check for
197
- these floor-regression signals:
102
+ A doctor PASS requires both ownership and runtime evidence, including a live
103
+ MCP↔sender↔receiver owner join. `NOT CERTIFIED` exits nonzero: a fixture, plugin
104
+ validation, or hand-inspected marker cannot replace a new real session. If the launch
105
+ form is unsupported, reinstall; if ownership is correct but the live join is absent,
106
+ restart the affected session.
198
107
 
199
- ```text
200
- ok claude 2.1.217 (>= 2.1.217, exec-form launch contract supported)
201
- ok launch form: exec form through the shipped hook-launch.sh (no shell on the path)
202
- ok installed owner argv execs directly (no shell) through hook-launch.sh and keys its sender marker to the live host pid
203
- ok <N> live Claude MCP process(es): sender + receiver owner join is live and record-backed
204
- ok full store scan: no corrupt records, duplicate nativeSessionId, body/filename drift, or backend↔wakeMode contradiction
205
- ok check-entwurf-v2-surface: shipped surface source present; exhaustive source-shape gate is a repo/release invariant (not run under node_modules)
206
- ```
108
+ New macOS wiring is refused because the live join is not instrumented there. Darwin
109
+ uninstall remains available for cleaning an older managed install; this is an evidence
110
+ boundary, not a permanent impossibility claim.
207
111
 
208
- The live-process line is **not** a warning any more. If no matching Claude MCP child
209
- is open — or the host has no `/proc` — the doctor reports `NOT CERTIFIED` and exits
210
- nonzero, because only the static + synthetic checks were possible and neither of them
211
- can measure the live join. A host whose live tier was never measured is an
212
- unmeasured host, not a passing one. An `UNSUPPORTED` launch form needs reinstall; a
213
- failed live owner join after reinstall means the already-open Claude process still
214
- holds the old hook definition in memory and must be restarted — the hook itself
215
- refuses to write markers in that state rather than keying them to whatever the old
216
- command's shell left behind.
217
-
218
- If any of those sections reports `ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING`,
219
- the host is still running a pre-0.12.5 package or a broken tarball. Reinstall the
220
- current package and re-run `entwurf install-meta-bridge && entwurf doctor-meta-bridge`.
221
-
222
- Store invariant (only for a host that is NOT clean): this walkthrough assumes no
223
- prior `~/.pi/agent/meta-sessions`. Production reads **schemaVersion 3 only** and
224
- the repo carries no legacy reader or migrator — the active store provides no
225
- cross-generation continuity (sessions flow; memory lives in the native
226
- transcripts and the embedding axes). On a store the live schema cannot read,
227
- `entwurf_self`, `entwurf_v2`, and inbox reads fail loud; `entwurf_peers` keeps
228
- its fact listing alive and reports the records as diagnostics. The owned
229
- `setup` / `install` / `install-meta-bridge` entrypoints refuse before activation
230
- writes rather than crossing this boundary silently. The one prescription is the
231
- generation cut: `entwurf meta-bridge-fresh-cut` (quiesce-checked; archives the
232
- whole previous generation to a timestamped sibling and opens an empty one — the
233
- archive is forensic only, no restore verb). Once per boundary. A truly clean
234
- host has nothing to cut and can ignore this paragraph. The refusal is a
235
- preflight, not a lock: for checkout-backed installs, quiesce sessions before
236
- pull, then fresh-cut, setup, and reopen.
237
-
238
- Read the cut's exit status rather than only chaining it (#54; `--help` prints the
239
- contract): `0` complete → run `setup`; `1` NOTHING MOVED — a live/unprovable
240
- surface, an occupied archive destination, an unreadable surface — fix the named
241
- cause and re-run, and do not run `setup`, because the store it refused is still
242
- there; `2` usage; `3` CUT TRANSITION INCOMPLETE — at least one archive move
243
- happened but the fresh generation is not confirmed open — inspect, or re-run to
244
- finish under a new stamp; `4` the cut is COMPLETE but marker/socket residue could
245
- not be unlinked — `setup` may run. Prefer fixing the named residue and re-running
246
- before `setup`; if new citizens have already been born, remove it manually rather
247
- than fresh-cutting their new generation. Only `0` is success.
248
-
249
- Upgrade invariant: every global npm/pnpm package upgrade must be followed by
250
- `entwurf install-meta-bridge` from that same installed binary and then
251
- `entwurf doctor-meta-bridge`. Installed statusline/MCP entries use stable bin
252
- shims and the marketplace source lives in a version-stable operator data dir, but
253
- package managers still do not re-materialize Claude's plugin bundle/cache. If a
254
- dev checkout's `./run.sh doctor-meta-bridge` expects repo-owned paths while the
255
- global install intentionally owns the meta-bridge (or the reverse), that is an
256
- ownership mismatch — run the doctor from the surface that intentionally owns the
257
- install, or reinstall from the other surface. Restart already-open Claude Code
258
- sessions after changing the meta-bridge install.
259
-
260
- A plain external MCP host can call the read surfaces (`entwurf_peers`,
261
- `entwurf_inbox_read`), but an `entwurf_v2` send is **refused by default** (#50 C4:
262
- "if we don't know who sent it, we don't send it") — it has no authoritative sender.
263
- A deliberately-anonymous host may wire the explicit
264
- `ENTWURF_BRIDGE_ALLOW_ANONYMOUS_SENDER=1` hatch and then delivers external and
265
- non-replyable; see README §"Wiring `entwurf-bridge` into an external MCP host".
266
- A garden-native meta-session has a garden id and a trusted sender marker, so it can
267
- call `entwurf_self` and be addressed by garden id. **Whether it has a mailbox depends
268
- on its rail**: a self-fetch backend (Claude Code) has a drainable inbox and receives
269
- mailbox wakeups, while a native-push backend (Antigravity) has **no mailbox and no
270
- idle-wake watch at all** — a reply is injected straight into its live conversation, and
271
- it is reachable only while the adapter probe finds that conversation. Do not assume a
272
- mailbox from "garden-native meta-session".
273
-
274
- ## Stage 5 — Antigravity native citizen (optional)
275
-
276
- If `agy` is already installed and authenticated by the operator, wire the three
277
- separate ownership atoms. Entwurf does not install agy or copy its auth.
112
+ ## 4. Optional Antigravity native citizen
278
113
 
279
- ```bash
280
- which agy
114
+ Install the three independently owned surfaces:
281
115
 
116
+ ```bash
282
117
  entwurf install-agy-bridge
283
118
  entwurf install-agy-statusline
284
119
  entwurf install-agy-hooks
@@ -288,118 +123,78 @@ entwurf doctor-agy-statusline
288
123
  entwurf doctor-agy-hooks
289
124
  ```
290
125
 
291
- What these commands own:
126
+ The bridge owns one MCP server and narrow rules for the normal tools; the statusline
127
+ owns its subtree; the hook owns one `PreInvocation` entry. Unrelated settings are
128
+ preserved. A fresh conversation initially may show `🪛 ? agy`; the first invocation
129
+ births the record by native `conversationId`, after which the garden id appears.
292
130
 
293
- - `install-agy-bridge`: one MCP server in `~/.gemini/config/mcp_config.json`
294
- and one narrow rule per tool the normal agy workflow calls —
295
- `mcp(entwurf-bridge/entwurf_v2)`, `mcp(entwurf-bridge/entwurf_peers)`,
296
- `mcp(entwurf-bridge/entwurf_self)` — in
297
- `~/.gemini/antigravity-cli/settings.json`'s permission allow-list;
298
- - `install-agy-statusline`: the `statusLine` subtree only, pointing at
299
- `entwurf-agy-statusline`;
300
- - `install-agy-hooks`: one named plugin `PreInvocation` hook pointing at
301
- `entwurf-agy-imprint`.
131
+ Real native-push acceptance needs an already-running conversation:
302
132
 
303
- They preserve unrelated user state, record independent install-state under
304
- `$XDG_DATA_HOME/entwurf/`, and refuse symlink-owned config instead of writing
305
- through someone else's SSOT. Broad YOLO rules such as `command(*)` and
306
- `unsandboxed(*)` are operator policy and are never granted by this package.
307
-
308
- Restart agy, open a **fresh conversation**, and make one model invocation. The
309
- hook's first `PreInvocation` births/attaches the conversation by native
310
- `conversationId`; after that, the statusline should show `🪛 <garden-id> agy`.
311
- Verify:
133
+ ```bash
134
+ LIVE=1 AGY_CONVERSATION_ID=<id> entwurf smoke-agy-native-push-live
135
+ ```
312
136
 
313
- 1. `entwurf_self` reports that gid with `agentId=meta-session/antigravity` and
314
- `replyable:true` while the native route probes alive;
315
- 2. an `entwurf_v2` send from agy reaches a sibling with that sender gid;
316
- 3. a sibling's `entwurf_v2(..., intent=fire-and-forget)` reply to the same gid
317
- direct-injects into the same agy conversation.
137
+ ## 5. Optional ACP backend turns
318
138
 
319
- This rail has no mailbox/receiver marker and no `owned-outcome` authority.
320
- Same-pid concurrent conversation invocation is not supported; separate agy
321
- processes have separate pid/start-key sender markers.
139
+ Claude uses the operator's existing local Claude authentication:
322
140
 
323
- ## Stage 6 — backend auth and live ACP runtime smoke
141
+ ```bash
142
+ LIVE=1 entwurf smoke-acp-provider-live
143
+ ```
324
144
 
325
- Backend authentication is the operator's responsibility and lives entirely
326
- outside entwurf. For the Claude ACP lane:
145
+ Cortex requires an authenticated `cortex` CLI and an explicit connection. Keep
146
+ `CORTEX_HOME` unset; the adapter refuses its presence because it bypasses containment.
327
147
 
328
148
  ```bash
329
- curl -fsSL https://claude.ai/install.sh | bash
330
- echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc
331
- source ~/.bashrc
332
- claude login
333
-
334
- cd ~/entwurf-smoke
335
- pi --provider entwurf --model claude-sonnet-5 -p "reply with ok only"
149
+ LIVE=1 ENTWURF_ACP_CORTEX_CONNECTION=<conn> \
150
+ entwurf smoke-acp-cortex-live
336
151
  ```
337
152
 
338
- If the backend CLI fails directly (`claude -p "ping"`), fix that upstream first.
339
- `entwurf` surfaces missing auth; it does not repair it.
153
+ The aggregate release gate is Claude-backed and does not run Cortex automatically.
154
+ Its silence is not a Cortex PASS.
340
155
 
341
- **This smoke proves the PROVIDER surface, not the garden one.** It shows auth,
342
- model routing, and one real turn — nothing more. A plain `pi -p` run is not a
343
- garden citizen: with no `--entwurf-control` there is no routable control socket,
344
- so entwurf deliberately leaves `PI_SESSION_ID` unset and a bundled
345
- `entwurf_self` / `entwurf_v2` call **fails loud** naming the missing wiring.
346
- That is the contract, not a defect — an address a peer cannot route to is worse
347
- than none. For the garden surface (citizen birth, addressable sends) use Stage 7.
156
+ ## 6. Upgrade and repair
348
157
 
349
- ## Stage 7 garden/control-socket surface (optional)
158
+ After upgrading the package, rerun the managed installers for every native harness
159
+ in use and restart their existing processes. Native plugin caches are not live-reload
160
+ safe across launch-contract changes.
350
161
 
351
- To address a long-lived pi session from another session or an external MCP host,
352
- open it with `--entwurf-control`. No id injection and no special launcher
353
- (#50 C2): pi mints its own session id (a `uuidv7` is normal), `session_start`
354
- attaches that session to its meta-record, and the **record** mints the garden id
355
- everything addressable hangs off.
162
+ If install or doctor reports an unreadable/old active citizen generation, do not edit
163
+ records by hand:
356
164
 
357
165
  ```bash
358
- pi --entwurf-control --provider entwurf --model claude-sonnet-5
166
+ # close pi, Claude, and agy sessions first
167
+ entwurf meta-bridge-fresh-cut
168
+ entwurf setup ~/entwurf-smoke
359
169
  ```
360
170
 
361
- The control socket is `~/.pi/entwurf-control/<record gardenId>.sock` keyed on
362
- the id the record minted, which is *not* pi's session id. Read the address off
363
- `entwurf_peers` (or `entwurf_self` from inside the session) instead of guessing
364
- the filename. If the record cannot be written the control server is refused,
365
- `PI_SESSION_ID` stays unset, and the reason is on stderr: an unaddressable
366
- resident must never survive quietly.
171
+ Read the cut's exit status before chaining setup. The complete quiescence, archive,
172
+ and exit-code contract is [fresh-cut-policy.md](./fresh-cut-policy.md).
367
173
 
368
- Use `entwurf_peers` to discover citizens and `entwurf_v2` to deliver by garden
369
- id. Do not choose the transport by hand: the same-looking id may name a live pi
370
- socket, a dormant pi record, a mailbox-backed Claude session, or a native-push
371
- Antigravity conversation.
174
+ ## 7. Release acceptance versus host acceptance
372
175
 
373
- ## Teardown
176
+ - `entwurf check-bridge`: installed MCP bytes boot; no backend auth.
177
+ - `pnpm check`: source deterministic floor; maintainer checkout only.
178
+ - `check-install-container`: checkout-invisible Linux package-consumer shape using
179
+ fixtures; not a native lifecycle proof.
180
+ - `doctor-meta-bridge`: one installed real Claude host, only with a new live session.
181
+ - `LIVE=1 entwurf release-gate /path/to/scratch --cut`: aggregate runtime acceptance (`--cut` makes any MUST SKIP red; without it the run is a diagnostic pass).
374
182
 
375
- ```bash
376
- # project wiring
377
- rm -rf ~/entwurf-smoke/.pi
183
+ Keep these verdicts separate. Current protocol is [VERIFY.md](../VERIFY.md); recorded
184
+ host verdicts are [BASELINE.md](../BASELINE.md).
378
185
 
379
- # native-harness surfaces (if installed)
380
- entwurf uninstall-meta-bridge 2>/dev/null || true
381
- entwurf uninstall-agy-hooks 2>/dev/null || true
382
- entwurf uninstall-agy-statusline 2>/dev/null || true
383
- entwurf uninstall-agy-bridge 2>/dev/null || true
186
+ ## Uninstall
384
187
 
385
- # package and optional pi binary
386
- npm uninstall -g @junghanacs/entwurf
387
- npm uninstall -g @earendil-works/pi-coding-agent 2>/dev/null || true
188
+ Run only the surfaces this host owns:
388
189
 
389
- # node via nvm
390
- nvm uninstall 24
391
- rm -rf ~/.nvm
190
+ ```bash
191
+ entwurf uninstall-meta-bridge
192
+ entwurf uninstall-agy-hooks
193
+ entwurf uninstall-agy-statusline
194
+ entwurf uninstall-agy-bridge
195
+ entwurf uninstall ~/entwurf-smoke
196
+ npm uninstall -g @junghanacs/entwurf
392
197
  ```
393
198
 
394
- This walk-through is a verification floor underneath release cuts: neutral npm
395
- install, installed bridge boot, optional pi adapter registration, Claude
396
- meta-bridge verification where used, all three agy doctors plus a fresh native
397
- round trip where used, and at least one authenticated ACP runtime smoke. Preserve
398
- the exact package version, candidate tarball sha256, container image identity,
399
- host OS, Claude version, and installed doctor output together. The repair history
400
- is fixed: `0.12.8-repair.0` cannot deliver, while published
401
- `0.12.8-repair.1` passed installed-native doctors on maintainer + secondary Linux
402
- hosts and remains under dist-tag `repair`. Stable promotion publishes accepted
403
- `0.12.8` bytes under `latest` and must preserve `repair=0.12.8-repair.1`.
404
- GLG owns every version, tag, publish, push, and host-reinstall decision; the
405
- complete ordered checklist is in VERIFY.md §Stable 0.12.8 order.
199
+ Each managed surface has an honest inverse and preserves unrelated native-harness
200
+ configuration.
@@ -298,54 +298,27 @@ function abbreviateHomeMcp(cwd) {
298
298
  // NOT gate on replyability (a `wants_reply` from an external/non-replyable caller
299
299
  // is surfaced honestly, not rejected; the decider routes on target + intent, not
300
300
  // sender replyability).
301
- server.tool("entwurf_v2", "CANONICAL DELIVERY SURFACE for garden ids. When you have a garden id and want to reach " +
302
- "whoever it names message / reply / hand-off use THIS verb. A garden id alone does " +
303
- "not tell you which rail that citizen answers on a live socket citizen, a dormant one, a " +
304
- "mailbox-backed self-fetch session, or a native-push session and entwurf_v2 is the one " +
305
- 'surface that reads that for you and routes correctly (so "when unsure which transport, use ' +
306
- 'entwurf_v2"). You give the target ' +
307
- "garden id + your intent; the decider picks the transport from the target's liveness " +
308
- "(live socket citizen → control-socket send; dormant socket citizen → spawn-bg resume; active " +
309
- "deliverable self-fetch citizen meta-bridge mailbox; probe-alive native-push citizen direct " +
310
- "injection into its live conversation) under the v2 lock policy, and reports ONE outcome " +
311
- "(delivered / rejected / lock-retained / delivered-but-lock-dirty). LOCK POLICY (do not " +
312
- "over-generalize it): the per-target lock is taken for a control-socket-DOMAIN dispatch, which is " +
313
- "both the live send AND the dormant cell's spawn-bg resume spawn-bg is a separate relaunch " +
314
- "transport yet it still runs under that domain's lock. The mailbox and native-push rails are " +
315
- "lock-free: the mailbox is guarded instead by active-receiver deliverability, and native-push by " +
316
- "its adapter probe. The decider not the " +
317
- "caller chooses the transport. Note: entwurf_v2 dispatches to EXISTING targets; " +
318
- "brand-new sibling creation is deferred to a later v2 lane. " +
319
- "CHOOSING INTENT (read thispicking wrong is rejected, never auto-fixed): to message / " +
320
- "reply / hand off a peer that entwurf_peers shows as liveness=alive (a live socket citizen, " +
321
- "currently backend pi) use intent: fire-and-forget it routes to the live control-socket; set " +
322
- "wants_reply:true if you need an answer (wants_reply is NOT owned-outcome). Replies to a citizen " +
323
- "with NO socket liveness (liveness=unsupported) are ALSO fire-and-forget, and the decider picks " +
324
- "that citizen's own rail: a self-fetch backend (e.g. Claude Code) gets the meta-bridge mailbox, " +
325
- "while a native-push backend (e.g. Antigravity) gets direct injection into its live conversation " +
326
- "and has NO mailbox at all — do not assume mailbox semantics for every unsupported citizen. A " +
327
- "native-push target IS measured by its own adapter probe, and that probe is THREE-valued, so " +
328
- "the send is never silently queued: alive → injected; dead → rejected as " +
329
- "native-push-target-dead; indeterminate → rejected as native-push-probe-indeterminate. " +
330
- "Those two rejects stay distinct because 'we could not establish it' is not 'it is gone' — " +
331
- "collapsing them reports a guess as a fact. THERE IS A " +
332
- "THIRD RESULT, so do not read liveness=unsupported as 'reachable by some rail': the mailbox path " +
333
- "delivers only to a DELIVERABLE citizen (a self-fetch backend whose receiver is live and armed), " +
334
- "so a terminated Claude Code session — and any record whose backend has no adapter on this lane, " +
335
- "e.g. codex — is rejected as mailbox-undeliverable rather than queued into an inbox nobody drains. " +
336
- "unsupported means only 'this backend has no control-socket probe'. " +
337
- "owned-outcome is ONLY for waking a DORMANT socket-domain citizen (spawn-bg resume, currently " +
338
- "backend pi); on a live target it is rejected as owned-live-no-autosend. Neither self-fetch nor " +
339
- "native-push has resume authority, but they reject under DIFFERENT reasons — self-fetch as " +
340
- "backend-liveness-unsupported, native-push as native-push-no-resume-authority. " +
341
- "It is NEVER auto-converted — so pick the right intent up front. " +
342
- "mode applies to a CONTROL-SOCKET send only — it is the injection style for a live pi turn, and " +
343
- "the mailbox, native-push, and spawn-bg plans carry no mode at all, so setting it for those " +
344
- "targets changes nothing (a native-push send IS live and still ignores it). wants_reply rides " +
345
- "every rail. Use entwurf_peers to discover targets. " +
346
- "Payload guidance: message hard cap 16000 chars. For larger reviews/logs, write an " +
347
- "artifact and dispatch its path plus a short digest; avoid multi-part sends because " +
348
- "mailbox doorbells may coalesce.", {
301
+ server.tool("entwurf_v2", "CANONICAL DELIVERY SURFACE for garden ids: message, reply, or hand off to whoever an id names. The id " +
302
+ "alone does not say which rail that citizen answers on. Give target + intent; the decider picks transport " +
303
+ "from liveness (live socket citizen control-socket send; dormant socket citizen spawn-bg resume; " +
304
+ "deliverable self-fetch citizen meta-bridge mailbox; probe-alive native-push citizen direct injection " +
305
+ "into its conversation) and reports ONE outcome (delivered / rejected / lock-retained / " +
306
+ "delivered-but-lock-dirty). EXISTING targets only; discover with entwurf_peers. INTENT — picking wrong is " +
307
+ "rejected, never auto-converted. A peer entwurf_peers shows as liveness=alive fire-and-forget. A " +
308
+ "citizen with NO socket liveness (liveness=unsupported) is ALSO fire-and-forget unsupported means only " +
309
+ '"no control-socket probe" and the decider picks its own rail: a self-fetch backend (e.g. Claude Code) ' +
310
+ "gets the mailbox, a native-push backend (e.g. Antigravity) gets direct injection and has NO mailbox at " +
311
+ "all. THERE IS A THIRD RESULT: the mailbox delivers only to a DELIVERABLE citizen, so a terminated " +
312
+ "session, or a backend with no adapter here (e.g. codex), is mailbox-undeliverable, not queued for an " +
313
+ "inbox nobody drains. The native-push probe is 3-valued: alive injected; dead " +
314
+ "native-push-target-dead; indeterminate native-push-probe-indeterminate (unestablished gone). " +
315
+ "owned-outcome wakes a DORMANT socket-domain citizen by spawn-bg resume ONLY live target → " +
316
+ "owned-live-no-autosend, self-fetch backend-liveness-unsupported, native-push " +
317
+ "native-push-no-resume-authority. LOCK: taken for a control-socket-DOMAIN dispatch the live send AND " +
318
+ "the dormant cell's spawn-bg resume, a separate transport that still runs under that domain's lock. The " +
319
+ "mailbox and native-push rails are lock-free deliverability and the adapter probe guard them. mode " +
320
+ "applies to a CONTROL-SOCKET send only; other plans carry no mode. wants_reply rides every rail. message " +
321
+ "caps at 16000 chars; send an artifact path + digest for more.", {
349
322
  target: z.string().min(1).describe("Target garden id (use entwurf_peers to discover)"),
350
323
  intent: z
351
324
  .enum(["fire-and-forget", "owned-outcome"])