@junghanacs/entwurf 0.40.0 → 0.41.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 (128) hide show
  1. package/AGENTS.md +4 -2
  2. package/BASELINE.md +55 -2
  3. package/CHANGELOG.md +562 -0
  4. package/CONTRIBUTING.md +1 -1
  5. package/DELIVERY.md +18 -2
  6. package/FAQ.md +8 -4
  7. package/README.md +60 -45
  8. package/VERIFY.md +14 -11
  9. package/demo/README.md +1 -1
  10. package/docs/acp-backend-rail.md +6 -2
  11. package/docs/external-mcp-host.md +8 -6
  12. package/docs/fresh-cut-policy.md +9 -1
  13. package/docs/setup-clean-host.md +76 -20
  14. package/mcp/entwurf-bridge/dist/mcp/entwurf-bridge/src/index.js +81 -19
  15. package/mcp/entwurf-bridge/dist/pi-extensions/lib/cc-retire-pidfd.py +236 -0
  16. package/mcp/entwurf-bridge/dist/pi-extensions/lib/cc-retire-request.js +473 -0
  17. package/mcp/entwurf-bridge/dist/pi-extensions/lib/control-retire.js +125 -0
  18. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-retire-production.js +149 -0
  19. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-retire.js +158 -0
  20. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-surface.js +2 -1
  21. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-visible-resume.js +283 -20
  22. package/mcp/entwurf-bridge/dist/pi-extensions/lib/fresh-call-composition.js +7 -0
  23. package/mcp/entwurf-bridge/dist/pi-extensions/lib/fresh-call-dispatch.js +47 -3
  24. package/mcp/entwurf-bridge/dist/pi-extensions/lib/herdr-fresh-call.js +3 -1
  25. package/mcp/entwurf-bridge/dist/pi-extensions/lib/meta-session.js +96 -16
  26. package/mcp/entwurf-bridge/dist/pi-extensions/lib/mux-fresh-call.js +5 -29
  27. package/mcp/entwurf-bridge/dist/pi-extensions/lib/mux-launch.js +28 -0
  28. package/mcp/entwurf-bridge/dist/pi-extensions/lib/mux-resume-call.js +26 -11
  29. package/mcp/entwurf-bridge/dist/pi-extensions/lib/pi-retire-request.js +76 -0
  30. package/mcp/entwurf-bridge/dist/pi-extensions/lib/resume-launch-identity.js +67 -17
  31. package/mcp/entwurf-bridge/dist/pi-extensions/lib/retirement-event.js +211 -0
  32. package/mcp/entwurf-bridge/dist/pi-extensions/meta-bridge-hook.js +124 -10
  33. package/mcp/entwurf-bridge/dist/pi-extensions/meta-bridge-pi-durable.js +53 -2
  34. package/mcp/entwurf-bridge/dist/scripts/meta-facts.js +1 -1
  35. package/mcp/entwurf-bridge/dist/scripts/probe-bridge-command.js +1 -0
  36. package/mcp/entwurf-bridge/src/index.ts +93 -19
  37. package/mcp/entwurf-bridge/test.sh +2 -2
  38. package/mcp/entwurf-bridge/tsconfig.build.json +10 -1
  39. package/package.json +15 -14
  40. package/pi/meta-bridge/entwurf-meta-receive/scripts/hook-launch.sh +3 -3
  41. package/pi/pi-durable/carrier/tui.js +18 -3
  42. package/pi/pi-durable/overlay/upstream-pin.json +12 -12
  43. package/pi-extensions/entwurf-control.ts +55 -20
  44. package/pi-extensions/lib/acp/backend-adapter.ts +5 -3
  45. package/pi-extensions/lib/acp/backend.ts +12 -9
  46. package/pi-extensions/lib/cc-retire-pidfd.py +236 -0
  47. package/pi-extensions/lib/cc-retire-request.ts +553 -0
  48. package/pi-extensions/lib/control-retire.ts +195 -0
  49. package/pi-extensions/lib/entwurf-control-rpc.ts +17 -1
  50. package/pi-extensions/lib/entwurf-v2-retire-production.ts +227 -0
  51. package/pi-extensions/lib/entwurf-v2-retire.ts +269 -0
  52. package/pi-extensions/lib/entwurf-v2-surface.ts +2 -1
  53. package/pi-extensions/lib/entwurf-v2-visible-resume.ts +390 -36
  54. package/pi-extensions/lib/fresh-call-composition.ts +15 -0
  55. package/pi-extensions/lib/fresh-call-dispatch.ts +45 -4
  56. package/pi-extensions/lib/herdr-fresh-call.ts +5 -0
  57. package/pi-extensions/lib/meta-session.ts +140 -16
  58. package/pi-extensions/lib/mux-fresh-call.ts +7 -27
  59. package/pi-extensions/lib/mux-launch.ts +29 -0
  60. package/pi-extensions/lib/mux-resume-call.ts +33 -12
  61. package/pi-extensions/lib/pi-retire-request.ts +111 -0
  62. package/pi-extensions/lib/resume-launch-identity.ts +104 -18
  63. package/pi-extensions/lib/retirement-event.ts +253 -0
  64. package/pi-extensions/meta-bridge-hook.ts +142 -13
  65. package/pi-extensions/meta-bridge-pi-durable.ts +57 -3
  66. package/run.sh +123 -34
  67. package/scripts/agy-bridge-config.py +4 -1
  68. package/scripts/build-bridge.sh +14 -0
  69. package/scripts/check-capability-bundle-reach.ts +135 -2
  70. package/scripts/check-entwurf-bridge-boot.ts +2 -1
  71. package/scripts/check-entwurf-fact-provider.ts +1 -0
  72. package/scripts/check-entwurf-v2-production.ts +2 -0
  73. package/scripts/check-entwurf-v2-surface.ts +7 -0
  74. package/scripts/check-gate-qualification.ts +14 -8
  75. package/scripts/check-herdr-activation.ts +2 -2
  76. package/scripts/check-herdr-placement.ts +1 -0
  77. package/scripts/check-hook-launch-topology.ts +6 -15
  78. package/scripts/check-install-container.sh +2 -2
  79. package/scripts/check-meta-doctor-oracle.sh +27 -1
  80. package/scripts/check-meta-facts.ts +8 -3
  81. package/scripts/check-meta-hook-session-switch.ts +230 -13
  82. package/scripts/check-meta-identity-consumers.ts +1 -0
  83. package/scripts/check-meta-listing.ts +1 -0
  84. package/scripts/check-meta-manifest-schema.py +1 -1
  85. package/scripts/check-meta-session.ts +31 -0
  86. package/scripts/check-meta-v3-record.ts +55 -1
  87. package/scripts/check-mux-resume-call.ts +35 -9
  88. package/scripts/check-peer-facts.ts +1 -0
  89. package/scripts/check-pi-durable-contact.ts +3 -1
  90. package/scripts/check-pi-durable-send.ts +4 -2
  91. package/scripts/check-pi-mcp-bridge.ts +1 -0
  92. package/scripts/check-resume-launch-identity.ts +62 -23
  93. package/scripts/lib/codex-fresh-live-protocol.ts +3 -2
  94. package/scripts/lib/meta-hook-bundle.ts +72 -0
  95. package/scripts/meta-bridge-claude-floor.sh +13 -9
  96. package/scripts/meta-bridge-doctor.sh +17 -0
  97. package/scripts/meta-bridge-install.sh +6 -0
  98. package/scripts/meta-facts.ts +1 -1
  99. package/scripts/mutants/bridge-boot-resume.json +5 -2
  100. package/scripts/mutants/cc-retire-pidfd.json +129 -0
  101. package/scripts/mutants/cc-retire-request.json +171 -0
  102. package/scripts/mutants/control-retire.json +105 -0
  103. package/scripts/mutants/control-send-receipt.json +1 -1
  104. package/scripts/mutants/copilot-receive.json +2 -6
  105. package/scripts/mutants/entwurf-v2-retire.json +476 -0
  106. package/scripts/mutants/fresh-call-dispatch.json +37 -0
  107. package/scripts/mutants/meta-hook-session-switch.json +88 -0
  108. package/scripts/mutants/meta-identity.json +57 -0
  109. package/scripts/mutants/mux-fresh-call.json +1 -1
  110. package/scripts/mutants/mux-resume-call.json +40 -4
  111. package/scripts/mutants/pack-install.json +4 -4
  112. package/scripts/mutants/pi-durable.json +139 -0
  113. package/scripts/mutants/pi-floor.json +5 -5
  114. package/scripts/mutants/pi-retire-request.json +72 -0
  115. package/scripts/mutants/resume-launch-identity.json +35 -8
  116. package/scripts/mutants/retirement-event.json +195 -0
  117. package/scripts/mutants/v2-surface.json +16 -0
  118. package/scripts/mutants/v2-visible-resume.json +283 -13
  119. package/scripts/probe-bridge-command.ts +1 -0
  120. package/scripts/smoke-agy-install-state.sh +2 -2
  121. package/scripts/smoke-codex-fresh-live.ts +2 -1
  122. package/scripts/smoke-copilot-mcp-state.sh +1 -1
  123. package/scripts/smoke-herdr-raw-install-live.sh +15 -7
  124. package/scripts/smoke-meta-install-state.sh +4 -4
  125. package/scripts/smoke-mux-lifecycle-live.ts +10 -10
  126. package/scripts/smoke-omp-mcp-state.sh +1 -1
  127. package/scripts/smoke-pi-durable-fresh-live.ts +5 -3
  128. package/scripts/smoke-pi-provider-state.sh +3 -3
package/FAQ.md CHANGED
@@ -8,7 +8,7 @@ No. Entwurf is closer to a workshop than a factory: it connects independently ow
8
8
 
9
9
  ## What does Entwurf do?
10
10
 
11
- It opens, reopens and messages visible sibling sessions across independent harnesses, each under a stable **garden id**. `entwurf_fresh_call` opens a new sibling in the caller's Herdr workspace or tmux session by default, `entwurf_resume_call` reopens a dormant pi sibling under the same id, and `entwurf_v2` delivers a message, reply or hand-off to an existing one through the rail it actually supports. It does not reconstruct prompts, hydrate transcripts, manage credentials, or emulate another harness.
11
+ It opens, reopens and messages visible sibling sessions across independent harnesses, each under a stable **garden id**. `entwurf_fresh_call` opens a new sibling in the caller's Herdr workspace or tmux session by default, `entwurf_resume_call` reopens a dormant pi or Claude Code sibling under the same id, and `entwurf_v2` delivers a message, reply or hand-off to an existing one through the rail it actually supports. It does not reconstruct prompts, hydrate transcripts, manage credentials, or emulate another harness.
12
12
 
13
13
  ## What is a garden id?
14
14
 
@@ -24,15 +24,19 @@ The visible top-level session is the garden principal. Internal children remain
24
24
 
25
25
  ## Does sending a message start a process?
26
26
 
27
- No. [`entwurf_v2`](./README.md#entwurf_v2--canonical-dispatch-verb) only addresses an existing garden citizen and selects its supported delivery rail. `entwurf_fresh_call` opens a new visible sibling; `entwurf_resume_call` reopens a dormant pi citizen under the same id.
27
+ No. [`entwurf_v2`](./README.md#entwurf_v2--canonical-dispatch-verb) only addresses an existing garden citizen and selects its supported delivery rail. `entwurf_fresh_call` opens a new visible sibling; `entwurf_resume_call` reopens a dormant pi or Claude Code citizen under the same id.
28
+
29
+ ## Can an agent retire a sibling on its own?
30
+
31
+ No. `entwurf_retire_call` (new in 0.41.0) attempts retirement of one Pi or Claude Code sibling, and only one the human operator explicitly requested. Seeing the tool, a coordinator role or the caller's identity is not permission; where a host provides an approval step it applies too, and Pi and pi-durable 1.3.0 have none by default. The configured Claude ACP child is also [set to run without per-call permission prompts](./docs/acp-backend-rail.md#configured-claude-acp-permissions); that is source configuration, not measured ACP retirement. The record stays, so `entwurf_resume_call` can reopen the same id, and an unknown result is never a reason to retry.
28
32
 
29
33
  ## Where do visible siblings open?
30
34
 
31
- Inside Herdr, Entwurf opens a new unfocused tab in the caller's workspace and currently admits pi and Claude Code. Outside Herdr, it opens one of the fixed pi, pi-durable, Claude Code, Copilot, OMP, or Codex runtimes in a new window of the caller's tmux session, or of an existing session the caller names; without an explicit tmux placement, a Codex caller opens beside its own TUI pane. A dormant pi sibling reopens under the same id in a visible tmux window. The runtime keeps its own model, tools, authentication, and transcript.
35
+ Inside Herdr, Entwurf opens a new unfocused tab in the caller's workspace and currently admits pi and Claude Code. Outside Herdr, it opens one of the fixed pi, pi-durable, Claude Code, Copilot, OMP, or Codex runtimes in a new window of the caller's tmux session, or of an existing session the caller names; without an explicit tmux placement, a Codex caller opens beside its own TUI pane. A dormant pi or Claude Code sibling reopens under the same id in a visible tmux window. The runtime keeps its own model, tools, authentication, and transcript.
32
36
 
33
37
  ## Does Entwurf replace Herdr or tmux?
34
38
 
35
- No. Herdr and tmux provide visible seats. Entwurf opens siblings into them, reopens dormant pi siblings, and carries identity, delivery, and receipts across independent harness sessions. A pane or tmux session is a location for a sibling, never its address or liveness proof.
39
+ No. Herdr and tmux provide visible seats. Entwurf opens siblings into them, reopens dormant pi or Claude Code siblings, and carries identity, delivery, and receipts across independent harness sessions. A pane or tmux session is a location for a sibling, never its address or liveness proof.
36
40
 
37
41
  ## How do I install it?
38
42
 
package/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # entwurf
2
2
 
3
3
  `entwurf` connects independent agent harnesses as visible siblings: open a new session,
4
- reopen a dormant pi session in tmux, or reach an existing sibling by **garden id**. Fresh
4
+ reopen a supported dormant sibling in tmux, or reach an existing sibling by **garden id**. Fresh
5
5
  sessions open in the caller's Herdr workspace or tmux session by default; explicit tmux
6
6
  placement can name another existing session. Each harness keeps its own runtime,
7
7
  authentication, tools and transcript.
@@ -26,7 +26,8 @@ Git branches, worktrees, file isolation and internal delegation remain their con
26
26
  | Verb | Tool | Where it lands | Harnesses |
27
27
  |---|---|---|---|
28
28
  | **Open** a new sibling | `entwurf_fresh_call` | Herdr: a new unfocused tab in the caller's workspace. tmux: a new window in the caller's session, or an existing session named by `placement.tmuxSession`; without an explicit tmux placement, a Codex caller opens beside its own TUI pane | Herdr: pi, Claude Code. tmux: pi, pi-durable, Claude Code, Copilot, OMP, Codex |
29
- | **Reopen** a dormant sibling | `entwurf_resume_call` | A visible tmux window under the same garden id, with the transcript, model, provider and cwd its record names; no turn is run | pi |
29
+ | **Reopen** a dormant sibling | `entwurf_resume_call` | A visible tmux window under the same garden id, with its record selecting the native session (Pi: identity-checked recorded transcript supplies launch metadata; Claude Code: `claude --resume <session id>` at the record cwd); the verb submits no prompt | pi, Claude Code |
30
+ | **Retire** one sibling the human operator explicitly requested | `entwurf_retire_call` | One guarded attempt on that target: a Pi shutdown request or Claude Code fd-pinned process termination. Refused/unknown outcomes remain possible; the record stays for guarded same-id return. Not a grant, a roster cleanup or an automatic retry | pi, Claude Code |
30
31
  | **Message** an existing sibling | `entwurf_v2` | Its own rail: control socket, self-fetch mailbox or native push; delivery starts no process | pi, pi-durable, Claude Code, Copilot, OMP, Antigravity, Codex |
31
32
 
32
33
  Antigravity is message-only: an existing conversation registers itself, and Entwurf never
@@ -53,15 +54,26 @@ ownership of the harness.
53
54
 
54
55
  Prerequisites: **Node >=24.0.0**, Python 3 for `setup`, and any harnesses you choose
55
56
  to use, already installed and authenticated. Pi is optional-by-presence, supported
56
- range `>=1.3.0 <1.4`; Claude Code supported floor `>=2.1.217` is required for its
57
+ range `>=1.4.0 <1.5`; Claude Code supported floor `>=2.1.306` is required for its
57
58
  managed exec-hook lifecycle. pnpm is needed only for source-checkout setup.
58
59
 
60
+ **Release state.** The Pi range above belongs to this source candidate, which moves the Pi SDK
61
+ set to 1.4.0 and is **not published to npm yet**. The newest published package, `0.41.0`,
62
+ ships the Pi 1.3.0 SDK set and requires Pi 1.3.x; the README inside that tarball and the Herdr
63
+ snapshot `v2026.10.22-herdr.0.41.0` are its contract. Pick the path that matches the Pi you run.
64
+
65
+ The published package (Pi 1.3.x):
66
+
59
67
  ```bash
60
- npm install -g @junghanacs/entwurf
68
+ npm install -g @junghanacs/entwurf@0.41.0
61
69
  entwurf setup /path/to/your-project
62
70
  entwurf check-bridge
63
71
  ```
64
72
 
73
+ This candidate (Pi 1.4): a source checkout of this commit, set up with `./run.sh setup` as
74
+ [Clean-host setup](./docs/setup-clean-host.md) describes. It has no npm install command until it
75
+ is released.
76
+
65
77
  `setup` composes each harness it finds and reports PASS / SKIP / FAIL. An absent
66
78
  harness is an explicit SKIP; a detected incomplete/below-floor integration is a
67
79
  named FAIL with nonzero exit, not a false green. `check-bridge` proves the installed
@@ -75,27 +87,18 @@ consent: [the operator steps](./docs/setup-clean-host.md#three-operator-decision
75
87
  Plain MCP registration alone does not make a garden citizen.
76
88
 
77
89
  **Platform bounds:** Linux is the certified harness-rail axis. macOS Entwurf-only
78
- installation is **CERTIFIED (CI)**, weaker than a physical-host doctor; its native
79
- harness rails, ACP and mux remain **NOT CERTIFIED — pending physical host**.
80
- Detected harness wiring on Darwin can therefore be written while setup stays
81
- non-green. WSL2: a CI runner (experimental) measured the Entwurf-only install and a
82
- zero-token Pi citizen; general, physical-machine or harness-parity WSL2 support is **not
83
- declared**, and delivery, other harnesses and physical machines are unmeasured. Native
84
- Windows has an **experimental first preview**, a CI-built x64 addon that ships in core
85
- 0.39.0 (published to npm); 0.38.1 and earlier carry no Windows addon. On a GitHub Actions
86
- `windows-2025` x64 runner with Node 24.18.1 and Pi 1.3.0, a CI-built artifact was installed,
87
- wired with `pi install`, birthed two Pi citizens and
88
- delivered one garden-id `entwurf_v2` message between them, with an offline scripted model
89
- and no vendor turn. Every other native-Windows surface (other harnesses, `setup` and the
90
- `.sh` bins, fresh/resume, mux/Herdr/ACP, physical machines) is **not declared**; the
91
- measured scope and its named gaps are in [BASELINE](./BASELINE.md#release-host-baseline).
92
- The package has no npm `os` restriction; that is not certification of every platform's
93
- harness rails.
90
+ installation is **CERTIFIED (CI)**; native harness rails, ACP and mux remain
91
+ **NOT CERTIFIED — pending physical host**, so detected wiring can still leave setup non-green.
92
+ WSL2 and native Windows have **experimental CI previews**, not general or physical-host support.
93
+ The native-Windows preview covers Pi citizens and offline scripted delivery, not vendor turns,
94
+ other harnesses, shell entrypoints, fresh/resume, mux, Herdr or ACP.
95
+ [BASELINE](./BASELINE.md#release-host-baseline) owns the version-specific receipts and gaps;
96
+ an npm package without an `os` restriction is not an all-platform certification.
94
97
 
95
98
  ### Native durable (pi-durable)
96
99
 
97
- **The package supplies Entwurf's owned app/TUI carrier and nine exact Pi 1.3.0 SDK
98
- members.** No separate runtime checkout, patch, build or ordinary Pi CLI is needed.
100
+ **This candidate's package supplies Entwurf's owned app/TUI carrier and nine exact Pi 1.4.0 SDK
101
+ members** (unpublished; the published 0.41.0 supplies the 1.3.0 set). No separate runtime checkout, patch, build or ordinary Pi CLI is needed.
99
102
  The launch verifies carrier integrity and every reachable SDK edge's name/version;
100
103
  incomplete, drifted or mixed supply refuses by name.
101
104
 
@@ -105,7 +108,7 @@ entwurf pi-durable --continue
105
108
  entwurf pi-durable --resume
106
109
  ```
107
110
 
108
- `--continue` opens this directory's newest durable session. `--resume` lists its
111
+ `--continue` opens this directory's latest-created durable session. `--resume` lists its
109
112
  sessions, most recently written first, by creation time, garden id and last write, and
110
113
  reopens the one you choose as it was saved; a cancelled choice opens nothing.
111
114
 
@@ -116,20 +119,23 @@ SQLite, transcripts and recovery remain the upstream SDK's:
116
119
 
117
120
  Optional `--native-module <absolute file>` initializes trusted ESM before runtime/TUI
118
121
  imports, then installs its default native Extension after the contact.
119
- `--native-module-dir <absolute directory>` (#134) loads the top-level `*.extension.mjs` files of that
120
- one named directory in byte-wise order instead, and the durable TUI keeps a live
121
- background-task badge on screen at any terminal size. No automatic module discovery or
122
+ `--native-module-dir <absolute directory>` loads that named directory's top-level
123
+ `*.extension.mjs` files in byte-wise order instead. No automatic module discovery or
122
124
  classic Pi API shim; guards are not a sandbox or rollback.
123
- Ordinary Pi's known `peerDependencies` warning reflects the separate native SDK supply.
124
125
  [Native setup](./docs/setup-clean-host.md#2b-optional-native-durable-pi-durable)
125
126
  owns flags, refusals and the extension contract.
126
127
 
127
128
  ### Herdr workbench
128
129
 
129
130
  ```bash
130
- herdr plugin install junghan0611/entwurf/plugins/herdr --yes
131
+ herdr plugin install junghan0611/entwurf/plugins/herdr --ref "$CANDIDATE_REF" --yes
131
132
  ```
132
133
 
134
+ Set `CANDIDATE_REF` explicitly to a published branch or tag that carries the runtime you mean;
135
+ these examples do not select a candidate from Herdr's default ref. A validation branch is not a
136
+ release acceptance. The [Herdr guide](./plugins/herdr/README.md#from-a-raw-linux-box-in-nine-steps)
137
+ says which runtime each ref installs.
138
+
133
139
  This separate path activates Herdr-integrated **pi and Claude Code** with an
134
140
  independently locked runtime and an observation pane; it puts no Entwurf bins on PATH.
135
141
  An Entwurf release does not automatically re-pin that runtime.
@@ -168,9 +174,11 @@ In that **harness session**, ask the agent to:
168
174
  ```
169
175
 
170
176
  These are tools, not shell subcommands. Pi calls
171
- `mcp__entwurf_bridge__<verb>` through Pi's built-in MCP. The server exposes **eight**
172
- verbs; a pi citizen has **six active + two hidden** (`entwurf_inbox_read` and
177
+ `mcp__entwurf_bridge__<verb>` through Pi's built-in MCP. The bridge exposes **nine**
178
+ verbs; a pi citizen has **seven active + two hidden** (`entwurf_inbox_read` and
173
179
  `entwurf_register_native`), since it has no mailbox and its own extension births it.
180
+ Those counts describe the source; a running host offers what its already-loaded bridge
181
+ listed, so see [applying new source to a running host](./docs/setup-clean-host.md#applying-new-source-to-a-running-host).
174
182
  The server key stays `entwurf-bridge`. An operator/trusted-project MCP entry with
175
183
  that normalized namespace can replace the registration by Pi's own precedence;
176
184
  `/mcp` shows it, and Entwurf does not scan or fight that choice.
@@ -195,7 +203,16 @@ pi / pi-durable / Claude Code / Copilot / OMP / Antigravity / Codex
195
203
  |---|---|---|
196
204
  | Message, reply or hand off to an existing garden id | `entwurf_v2 {target, intent:"fire-and-forget", message}` | Receiver acceptance boundary, mailbox enqueue or native injection; not completion |
197
205
  | Open a new visible sibling | `entwurf_fresh_call {backend, model, task, cwd?}` | Launch receipt only; the sibling's nonce callback envelope supplies its new garden id |
198
- | Reopen a dormant pi citizen under its own id | `entwurf_resume_call {target}` | Separate LAUNCH and socket OBSERVATION receipts; no turn is run |
206
+ | Reopen a dormant pi or Claude Code citizen under its own id | `entwurf_resume_call {target}` | Separate LAUNCH and OBSERVATION receipts (pi: same-id socket; Claude Code: a new live receiver marker in the opened pane, bound to the record); the verb submits no prompt |
207
+ | Attempt retirement of one explicitly requested Pi or Claude Code target | `entwurf_retire_call {target}` | Separate action, append-only record and lock-release results. Pi ACK is request-only; CC exit is that fd-pinned process only |
208
+
209
+ **Retirement is never autonomous.** It requires the human operator's explicit target/action;
210
+ a role, caller identity or tool annotation grants no permission. Host approval applies where
211
+ provided or configured. No roster expansion, timer or retry of an unknown/cancelled/lost result;
212
+ an absent event is unknown, not proof that no action occurred.
213
+ [DELIVERY](./DELIVERY.md#candidate-lifecycle-commands-are-not-delivery-boundaries-147)
214
+ owns the lifecycle boundaries; [BASELINE](./BASELINE.md#147-native-round-trip-2026-10-22-n1)
215
+ keeps dated native evidence separate from release and rollout acceptance.
199
216
 
200
217
  **Delivery starts no process.** A live pi receives over its record-addressed control
201
218
  address (a Unix socket; on the experimental native-Windows Pi preview, a named pipe);
@@ -214,20 +231,18 @@ is a live rail fact, not merely a consequence of trusted identity.
214
231
 
215
232
  **One declared remote pair (opt-in, #107).** The operator places an `entwurf-remote-pair.json` on each of two hosts. Then `entwurf_v2` to the declared remote garden id runs the other host's `entwurf remote-pair-receive` once over the operator's own SSH, and that host checks the pair and dispatches locally. The trust is the same operator's SSH account. It makes one attempt with no retry, and it uses no relay or listener. [DELIVERY](./DELIVERY.md#remote-pair-107-one-declared-pair-across-two-hosts) owns the contract; [setup](./docs/setup-clean-host.md#2c-optional-remote-pair-between-two-hosts-107) covers the operator steps.
216
233
 
217
- **Mux launches; it never delivers.** Inside Herdr, fresh opens a new unfocused tab
218
- in the caller's workspace, pi/Claude Code only, with no tmux fallback. Outside,
219
- it opens pi/pi-durable/Claude Code/Copilot/OMP/Codex visibly on the caller's own tmux
220
- server. pi-durable runs the installed carrier on its exact SDK set described above and
221
- refuses by name before any window when that verdict is not green; Herdr does not fresh-open it. The model is required; optional `cwd` is a literal
222
- absolute directory, omitted or empty means the caller's cwd. Optional `placement.tmuxSession` names an existing
223
- tmux session (not permitted inside Herdr), never a new session or a cwd lookup.
224
- Omitted tmux placement follows the caller: Codex beside its own TUI pane matched
225
- by thread-id, everyone else in their own session. Missing/ambiguous context refuses.
226
- A launch receipt is neither the callback address nor proof of task processing.
227
- Resume is pi-only, tmux-only, same id, record-supplied transcript/model/provider/cwd;
228
- a live or non-pi target refuses, and an unobserved window stays visible without retry.
229
- Exact boundaries: [tmux launch](https://github.com/junghan0611/entwurf/blob/main/docs/mux-launch-rail.md)
230
- and [Herdr launch](https://github.com/junghan0611/entwurf/blob/main/docs/herdr-launch-rail.md).
234
+ **Mux launches; it never delivers.** Fresh uses the caller's Herdr workspace or tmux server,
235
+ within the harness limits in the table above. The model is required; optional `cwd` is a literal
236
+ absolute directory (omitted or empty means caller cwd). `placement.tmuxSession` names an
237
+ existing tmux session, never a new session or a cwd lookup, and is refused inside Herdr.
238
+ Without that override, a Codex caller opens beside its thread-id-matched TUI pane;
239
+ other callers use their own session. Missing or ambiguous context refuses without fallback.
240
+ Resume is tmux-only for pi and Claude Code, selected by the record, with no submitted prompt.
241
+ Claude Code dormancy covers only attaches its hook committed, not every possible session holder;
242
+ [V3 arm binding](./docs/adding-a-harness.md#arm-binding-within-v3) is not occupancy or permission.
243
+ An unobserved window stays visible without retry. Address liveness alone is not conversation recall.
244
+ Exact argv, identity checks and limits belong to [tmux launch](./docs/mux-launch-rail.md)
245
+ and [Herdr launch](./docs/herdr-launch-rail.md).
231
246
 
232
247
  ## Concepts
233
248
 
package/VERIFY.md CHANGED
@@ -5,7 +5,7 @@ invariants live in gates; this file defines evidence strength, release acceptanc
5
5
  and the manual judgements a gate cannot make.
6
6
 
7
7
  > **Current surface.** `entwurf-bridge` exposes `entwurf_v2`, `entwurf_peers`,
8
- > `entwurf_fresh_call`, `entwurf_resume_call`, `entwurf_callback`, `entwurf_self`, `entwurf_inbox_read`, and `entwurf_register_native`. The ACP
8
+ > `entwurf_fresh_call`, `entwurf_resume_call`, `entwurf_retire_call` (0.41.0; operator-requested target only), `entwurf_callback`, `entwurf_self`, `entwurf_inbox_read`, and `entwurf_register_native` — nine; a pi citizen offers seven with two hidden, the pi-durable contact eight. The ACP
9
9
  > backends are Claude and Snowflake Cortex Code. Antigravity is a separate shipped
10
10
  > native-push citizen lane. Codex is a native-push citizen supported in 0.21.0, whose birth hook is
11
11
  > operator-owned and gated on one vendor trust receipt, with strict request-scoped identity and
@@ -105,7 +105,7 @@ that moved out of `scripts/` and into a file beside its subject would otherwise
105
105
 
106
106
  > **The herdr rail is an on-demand axis until it has run.** `smoke-herdr-fresh-call-live` opens a real caller inside a real herdr pane, has it invoke the PUBLIC `entwurf_fresh_call`, and reads the child's nonce callback — the one axis no deterministic gate reaches, since the rail is selected from the caller's own process environment. **The release aggregate does not yet require the herdr rail**: herdr is optional (a cut on a host without it must not block), and first evidence precedes promotion — a rail whose first acceptance run is days old has not earned a place in the gate that blocks every cut. Run it directly with `LIVE=1 ./run.sh smoke-herdr-fresh-call-live` whenever a cut changes herdr rail code, and promote it to a MUST only after the axis has a run history to point at. Without `LIVE=1`, or without herdr/pi/claude on PATH, it reports protocol SKIP — never a pass.
107
107
 
108
- > **The plugin's FIRST USER PATH is its own on-demand axis, and an npm acquisition receipt holds only for the ref it ran on (#118 홉 1).** Every other herdr gate proves the plugin against a source this repository controls: `check-herdr-plugin-build` drives a stub, and `smoke-herdr-plugin-build-live` redirects the product remote to a local bare clone with git `insteadOf` and asserts `identity.kind === "herdr-checkout"`. npm has been the production authority class since `dd84ac0`; the 0.30.0 follow-up re-pinned `@junghanacs/entwurf@0.30.0` with its published sha512 after the candidate windows rode `herdr-checkout` (`8b92e09`), and the 0.30.1 preparation rode that closed verification carrier again without inventing a registry integrity. The plugin snapshot `v2026.10.9-herdr.0.34.0` pinned the published `@junghanacs/entwurf@0.34.0` with its measured sha512 (#132); 0.31–0.33 were never pinned. The 0.34.1 preparation rode the closed `herdr-checkout` verification carrier again without inventing a registry integrity, and the snapshot `v2026.10.9-herdr.0.34.1` re-pins the published `@junghanacs/entwurf@0.34.1` with its measured sha512 (#135). The 0.34.2 preparation rode the same carrier, and the snapshot `v2026.10.11-herdr.0.34.2` re-pins the published `@junghanacs/entwurf@0.34.2` with its measured sha512 (#136). The source re-proof has separate dated npm rows: 2026-09-23 public `main=113ca53`, plugin 0.5.0, runtime 0.25.0 (CHANGELOG 0.25.1), and the 0.30.0 re-pin accepted at public `506c46f` in the [#126 thread](https://github.com/junghan0611/entwurf/issues/126). Neither certifies the 0.34.0 registry acquisition. That pin's real-npm swap / torn-swap recovery was measured locally on 2026-10-09 by driving the registry-shipped `scripts/herdr-runtime.mjs` (byte-equal to this checkout's) with its own npm acquisition: a 0.33.0 → 0.34.0 swap, and the same upgrade torn between its two renames then resumed, where the last-good 0.33.0 tree was restored before 0.34.0 was acquired again. Both ended `runtime-ready` at observed digest `sha256-e9d995ff…`, the accepted 0.34.0 artifact. That local receipt is recorded in the [#132 thread](https://github.com/junghan0611/entwurf/issues/132); it covers runtime recovery, not this smoke's cells. This smoke's public-ref acceptance of the 0.34.0 pin was measured locally at `--ref main`: PASS at resolved `b5d15d8` and again at the docs follow-up `8ed4d1a`, each acquiring `kind=npm @junghanacs/entwurf@0.34.0` — a main-ref receipt, not a tag-ref, native or full-Herdr claim. The 0.34.1 re-pin's npm-source rows — swap / torn-swap recovery, this smoke at a public ref, and the package consumer — are its own, recorded with that pin and never inherited from 0.34.0; checkout candidate evidence does not transfer. The 0.36.0 re-pin's public-ref row was measured on Herdr 0.9.4 at `--ref main`: PASS at resolved `9f33dd1`, acquiring `kind=npm @junghanacs/entwurf@0.36.0` at the accepted digest — a main-ref receipt, not a tag-ref claim. Its version-changing cross-ref reinstall (`herdr plugin install … --ref main` over the `v2026.10.13-herdr.0.35.1` installation) across a pinned Pi upgrade was a driver-level journey recorded in that snapshot's release notes, not a row of this smoke; `herdr plugin update` there was measured only as a same-ref already-up-to-date no-op. `LIVE=1 ./run.sh smoke-herdr-raw-install-live [--ref <REF>]` fills that row in a clean `node:24` container with no socket, config, cache or repo mounted, no `insteadOf`, the public remote spelled as a user would type it, and pi / herdr / Claude Code installed in the IMAGE BUILD as scaffolding (Hard Rule 17 — the product face begins at `herdr integration install pi`). herdr itself comes from `scripts/install-herdr-ci.sh` against `scripts/fixtures/herdr-supply.json`, so no version or digest lives here. `--ref` defaults to `main` rather than a pinned SHA because receipt (a) is defined as what a user gets today; the resolved commit is printed, so the receipt stays exact. It takes a REMOTE REF and not an arbitrary commit — measured on herdr 0.9.1, `--ref <sha>` fails as `couldn't find remote ref`, because the checkout fetches by name — so a candidate is addressed by its branch and pinned by the resolved commit the gate prints. **Its first question is a measurement, not an assumption:** herdr registers a plugin through a running server and falls back to `persist_plugin_offline` only on a connection error (`src/cli/plugin.rs:914-981,1016-1021` @ `c77af189`, 0.9.0 source), and a container has no server. The 2026-09-23 PASS installed with no server on the 0.9.1 binary `113ca53`'s `herdr-supply.json` pins, so that install path worked on 0.9.1 — inferred from the PASS, not a direct observation of the fallback branch; a later "no" on another herdr is #118 investigation output rather than a completion. `--yes` is always passed so a non-interactive exit 2 (`:189`) can never be misread as a missing server. **At a ref whose lock names `herdr-checkout` the acquisition axis reports a NAMED SKIP rather than a red, and the rest of the journey still runs.** That lock is the candidate carrier a cut legitimately sits on — 0.23.0 was tagged that way and `dd84ac0` pinned npm afterwards — so a red there would report a broken install when what is true is that the ref does not lock npm. What is asserted at BOTH lock kinds is that the runtime came from the source the ref locks, and the package name is read from the checkout manifest rather than the lock, because a `herdr-checkout` lock carries no `name` (`herdr-runtime.mjs:317-328`) and joining `undefined` throws instead of failing an assertion. **Cells [6]–[9] carry it past the install into the USE path (#118 H1-4/H1-5).** An install is not a use, so the same container then runs what those bytes became: the compiled bridge entry, the three `REQUIRED_BINS` as executables, and a real `entwurf check-bridge` printing its exact eight-verb set — the installed-runtime row below, measured rather than inferred from the bootstrap's refusal to install without them. Then the pi that this plugin WIRED is started with no `-e` and no `--no-extensions`, because the subject is the wiring and not this checkout: one `get_state` in `--mode rpc` costs zero tokens and needs no provider argument (a bogus `--provider` exits 1 before session_start; omitting the pair lets pi resolve the default the extension itself registers), and while that session is alive the cell reads its V3 record, its gardenId-keyed control socket, and the installed `entwurf peer-facts` reporting it `alive`. The plugin's own status fan is then driven with NEITHER side stubbed, in both states a user can be in: with no herdr server it must name `herdr-agent-list-failed` and go red rather than draw an empty table, and under a headless `herdr server` — scaffolding for that second read only, stopped again before the teardown — it exits 0 and COUNTS the citizen as `unobserved` instead of dropping it. Last, the shipped `entwurf herdr-plugin-deactivate` runs from the installed bin, deletes the runtime it is executing from, and the plugin reinstalls onto the host it fully left — the deactivation row, on the npm source. **What these cells still do not close:** swap / torn-swap recovery on the npm source, the package-consumer proof, and the fan drawing a citizen as a ROW — that needs `placement.kind === "herdr-pane"`, so the pi session must live in a herdr pane a session reference joins, and a headless container has no panes. **This smoke carries no test beside behavior and no mutant**, and that is a property of its class rather than an omission: it is a real-live container journey whose oracle is the container's own end state, so there is no pure seam to assert against and nothing a source mutation could kill deterministically. Like the other two herdr LIVE lanes it is **on-demand and not an aggregate MUST** — herdr is optional, a cut on a host without it must not block, and first evidence precedes promotion. Without `LIVE=1`, or without a usable Docker daemon, it reports a named SKIP; `ENTWURF_REQUIRE_DOCKER=1` turns the Docker absence red. Neither skip is a pass.
108
+ > **The plugin's FIRST USER PATH is its own on-demand axis, and an npm acquisition receipt holds only for the ref it ran on (#118 홉 1).** Every other herdr gate proves the plugin against a source this repository controls: `check-herdr-plugin-build` drives a stub, and `smoke-herdr-plugin-build-live` redirects the product remote to a local bare clone with git `insteadOf` and asserts `identity.kind === "herdr-checkout"`. npm has been the production authority class since `dd84ac0`; the 0.30.0 follow-up re-pinned `@junghanacs/entwurf@0.30.0` with its published sha512 after the candidate windows rode `herdr-checkout` (`8b92e09`), and the 0.30.1 preparation rode that closed verification carrier again without inventing a registry integrity. The plugin snapshot `v2026.10.9-herdr.0.34.0` pinned the published `@junghanacs/entwurf@0.34.0` with its measured sha512 (#132); 0.31–0.33 were never pinned. The 0.34.1 preparation rode the closed `herdr-checkout` verification carrier again without inventing a registry integrity, and the snapshot `v2026.10.9-herdr.0.34.1` re-pins the published `@junghanacs/entwurf@0.34.1` with its measured sha512 (#135). The 0.34.2 preparation rode the same carrier, and the snapshot `v2026.10.11-herdr.0.34.2` re-pins the published `@junghanacs/entwurf@0.34.2` with its measured sha512 (#136). The source re-proof has separate dated npm rows: 2026-09-23 public `main=113ca53`, plugin 0.5.0, runtime 0.25.0 (CHANGELOG 0.25.1), and the 0.30.0 re-pin accepted at public `506c46f` in the [#126 thread](https://github.com/junghan0611/entwurf/issues/126). Neither certifies the 0.34.0 registry acquisition. That pin's real-npm swap / torn-swap recovery was measured locally on 2026-10-09 by driving the registry-shipped `scripts/herdr-runtime.mjs` (byte-equal to this checkout's) with its own npm acquisition: a 0.33.0 → 0.34.0 swap, and the same upgrade torn between its two renames then resumed, where the last-good 0.33.0 tree was restored before 0.34.0 was acquired again. Both ended `runtime-ready` at observed digest `sha256-e9d995ff…`, the accepted 0.34.0 artifact. That local receipt is recorded in the [#132 thread](https://github.com/junghan0611/entwurf/issues/132); it covers runtime recovery, not this smoke's cells. This smoke's public-ref acceptance of the 0.34.0 pin was measured locally at `--ref main`: PASS at resolved `b5d15d8` and again at the docs follow-up `8ed4d1a`, each acquiring `kind=npm @junghanacs/entwurf@0.34.0` — a main-ref receipt, not a tag-ref, native or full-Herdr claim. The 0.34.1 re-pin's npm-source rows — swap / torn-swap recovery, this smoke at a public ref, and the package consumer — are its own, recorded with that pin and never inherited from 0.34.0; checkout candidate evidence does not transfer. The 0.36.0 re-pin's public-ref row was measured on Herdr 0.9.4 at `--ref main`: PASS at resolved `9f33dd1`, acquiring `kind=npm @junghanacs/entwurf@0.36.0` at the accepted digest — a main-ref receipt, not a tag-ref claim. Its version-changing cross-ref reinstall (`herdr plugin install … --ref main` over the `v2026.10.13-herdr.0.35.1` installation) across a pinned Pi upgrade was a driver-level journey recorded in that snapshot's release notes, not a row of this smoke; `herdr plugin update` there was measured only as a same-ref already-up-to-date no-op. `LIVE=1 ./run.sh smoke-herdr-raw-install-live [--ref <REF>]` fills that row in a clean `node:24` container with no socket, config, cache or repo mounted, no `insteadOf`, the public remote spelled as a user would type it, and pi / herdr / Claude Code installed in the IMAGE BUILD as scaffolding (Hard Rule 17 — the product face begins at `herdr integration install pi`). herdr itself comes from `scripts/install-herdr-ci.sh` against `scripts/fixtures/herdr-supply.json`, so no version or digest lives here. `--ref` defaults to `main` rather than a pinned SHA because receipt (a) is defined as what a user gets today; the resolved commit is printed, so the receipt stays exact. It takes a REMOTE REF and not an arbitrary commit — measured on herdr 0.9.1, `--ref <sha>` fails as `couldn't find remote ref`, because the checkout fetches by name — so a candidate is addressed by its branch and pinned by the resolved commit the gate prints. **Its first question is a measurement, not an assumption:** herdr registers a plugin through a running server and falls back to `persist_plugin_offline` only on a connection error (`src/cli/plugin.rs:914-981,1016-1021` @ `c77af189`, 0.9.0 source), and a container has no server. The 2026-09-23 PASS installed with no server on the 0.9.1 binary `113ca53`'s `herdr-supply.json` pins, so that install path worked on 0.9.1 — inferred from the PASS, not a direct observation of the fallback branch; a later "no" on another herdr is #118 investigation output rather than a completion. `--yes` is always passed so a non-interactive exit 2 (`:189`) can never be misread as a missing server. **At a ref whose lock names `herdr-checkout` the acquisition axis reports a NAMED SKIP rather than a red, and the rest of the journey still runs.** That lock is the candidate carrier a cut legitimately sits on — 0.23.0 was tagged that way and `dd84ac0` pinned npm afterwards — so a red there would report a broken install when what is true is that the ref does not lock npm. What is asserted at BOTH lock kinds is that the runtime came from the source the ref locks, and the package name is read from the checkout manifest rather than the lock, because a `herdr-checkout` lock carries no `name` (`herdr-runtime.mjs:317-328`) and joining `undefined` throws instead of failing an assertion. **Cells [6]–[9] carry it past the install into the USE path (#118 H1-4/H1-5).** An install is not a use, so the same container then runs what those bytes became: the compiled bridge entry, the three `REQUIRED_BINS` as executables, and a real `entwurf check-bridge` printing the exact verb set the installed bridge declares (nine since 0.41.0; each earlier recorded PASS printed its own date's set, and none of them is a nine-verb LIVE receipt) — the installed-runtime row below, measured rather than inferred from the bootstrap's refusal to install without them. Then the pi that this plugin WIRED is started with no `-e` and no `--no-extensions`, because the subject is the wiring and not this checkout: one `get_state` in `--mode rpc` costs zero tokens and needs no provider argument (a bogus `--provider` exits 1 before session_start; omitting the pair lets pi resolve the default the extension itself registers), and while that session is alive the cell reads its V3 record, its gardenId-keyed control socket, and the installed `entwurf peer-facts` reporting it `alive`. The plugin's own status fan is then driven with NEITHER side stubbed, in both states a user can be in: with no herdr server it must name `herdr-agent-list-failed` and go red rather than draw an empty table, and under a headless `herdr server` — scaffolding for that second read only, stopped again before the teardown — it exits 0 and COUNTS the citizen as `unobserved` instead of dropping it. Last, the shipped `entwurf herdr-plugin-deactivate` runs from the installed bin, deletes the runtime it is executing from, and the plugin reinstalls onto the host it fully left — the deactivation row, on the npm source. **What these cells still do not close:** swap / torn-swap recovery on the npm source, the package-consumer proof, and the fan drawing a citizen as a ROW — that needs `placement.kind === "herdr-pane"`, so the pi session must live in a herdr pane a session reference joins, and a headless container has no panes. **This smoke carries no test beside behavior and no mutant**, and that is a property of its class rather than an omission: it is a real-live container journey whose oracle is the container's own end state, so there is no pure seam to assert against and nothing a source mutation could kill deterministically. Like the other two herdr LIVE lanes it is **on-demand and not an aggregate MUST** — herdr is optional, a cut on a host without it must not block, and first evidence precedes promotion. Without `LIVE=1`, or without a usable Docker daemon, it reports a named SKIP; `ENTWURF_REQUIRE_DOCKER=1` turns the Docker absence red. Neither skip is a pass.
109
109
 
110
110
  > **The Herdr plugin's install-time build has two axes, and one of them needs the network (#116 M3-b3).** `check-herdr-plugin-build` is deterministic and rides `check:hermetic`: the listing reaches the runner through its own spawn seam (there is no fake `herdr` binary in this repo), and the one cell that drives the REAL binary reports a named SKIP on a host without it — `ENTWURF_REQUIRE_HERDR=1` makes that absence red. What it cannot have is the real acquisition: `npm pack git+https://github.com/junghan0611/entwurf.git#<full sha>` builds the bridge through `prepare`, which installs devDependencies from the registry. So `LIVE=1 ./run.sh smoke-herdr-plugin-build-live` owns that axis, and its cells are **two kinds of evidence, labelled as such**: three are the real Herdr journey — a real `herdr plugin install` driving the real `[[build]]`, with the UNCHANGED product remote redirected to a local bare clone by git `insteadOf` (the one substitution, which is what makes an unpushed candidate testable) — covering remote-commit available, the `{pi}` → `{pi, claude-code}` reinstall with OpenCode as a zero-write negative control and exactly ONE counted vendor MCP owner entry, and the post-build Herdr-commit gap (where the ledger has already rebound to the gap commit, so the honest assertion is that transition and not unchanged bytes). The fourth cell — remote-commit **unavailable** — calls the shipped runtime leaf directly with the fixed product argv and is therefore **acquisition-leaf evidence, not a journey**; routing it through Herdr would add a second explanation for one red. Its oracle is the honest one too: active runtime, ledger and harness bytes preserved, while the journal is left as the certified `installing` retry authority the transaction deliberately writes before acquiring, and a normal install afterwards is green. Its Claude cell is where the vendor question the deterministic gates refuse to claim gets answered — whether Claude Code accepts an ABSOLUTE executable as an MCP command — and it reports a named skip on a host without `claude`, never a pass. Like the herdr fresh-call rail, this smoke is **on-demand and not an aggregate MUST**. The release aggregate does not require the plugin build smoke: herdr is optional, a cut on a host without it must not block, and first evidence precedes promotion.
111
111
 
@@ -115,7 +115,7 @@ that moved out of `scripts/` and into a file beside its subject would otherwise
115
115
  >
116
116
  > A cut that touches the prompt-lifecycle contract (no wall clock on a running turn) owes one long-turn acceptance the aggregate floor is too short to carry: `LIVE=1 ./run.sh smoke-acp-long-turn-live` drives a real turn whose tool work outlasts the retired 600s cutoff and requires exactly one cold ACP bootstrap in the transcript. It takes >12 minutes by construction and is on-demand, not part of `release-gate`.
117
117
  >
118
- > **The integrated mux lifecycle IS a release-gate MUST.** `LIVE=1 ./run.sh smoke-mux-lifecycle-live` enters through a real MCP `tools/call` on the shipped bridge launcher and follows one citizen the whole way: fresh-call launch receipt, the sibling's own nonce callback whose SENDER ENVELOPE is the address, an `entwurf_v2` control-socket send that lands in the sibling's own transcript, `entwurf_resume_call` REFUSED while that citizen is live, a stable-handle close proving pane and socket gone with the record preserved, delivery to the dormant citizen refused honestly, then the public `entwurf_resume_call` — whose LAUNCH and OBSERVATION receipts stay apart — and finally an `entwurf_v2` send the resumed session answers with a fact it was told BEFORE the window closed. That last step is the point: a socket answering under the same garden id proves a process stood up at that address, and only the recall proves the same conversation came back. Both pi provider shapes are driven to it (native `openai-codex/gpt-6.1-sol:low` and recorded-ACP `entwurf/claude-sonnet-5-5`, the only shape that re-injects a bridge extension), and the Claude Code cell ends where its capability ends: its resume is refused as `target-not-pi` with no window opened and no lock residue (the per-gid coordination lock is released).
118
+ > **The integrated mux lifecycle IS a release-gate MUST.** `LIVE=1 ./run.sh smoke-mux-lifecycle-live` enters through a real MCP `tools/call` on the shipped bridge launcher and follows one citizen the whole way: fresh-call launch receipt, the sibling's own nonce callback whose SENDER ENVELOPE is the address, an `entwurf_v2` control-socket send that lands in the sibling's own transcript, `entwurf_resume_call` REFUSED while that citizen is live, a stable-handle close proving pane and socket gone with the record preserved, delivery to the dormant citizen refused honestly, then the public `entwurf_resume_call` — whose LAUNCH and OBSERVATION receipts stay apart — and finally an `entwurf_v2` send the resumed session answers with a fact it was told BEFORE the window closed. That last step is the point: a socket answering under the same garden id proves a process stood up at that address, and only the recall proves the same conversation came back. Both pi provider shapes are driven to it (native `openai-codex/gpt-6.1-sol:low` and recorded-ACP `entwurf/claude-sonnet-5-5`, the only shape that re-injects a bridge extension), and the Claude Code cell, whose sibling is still live at that point, has its resume refused as `target-live` with no window opened and no lock residue (the per-gid coordination lock is released). Claude Code's dormant same-id resume (#147 L2) is certified deterministically; its LIVE dormant→resume leg is not in this MUST yet. One test-owned retire→resume round trip outside this gate is recorded as dated n=1 evidence in [BASELINE](./BASELINE.md#147-native-round-trip-2026-10-22-n1); it is not this MUST.
119
119
  >
120
120
  > One cell cannot be fully fenced, and that is recorded rather than glossed: the recorded-ACP pi cell runs under the operator's REAL home, because its provider spawns `claude` as a child that authenticates from there — measured, a fenced HOME kills that turn with `Authentication required` before the model is ever reached, and `CLAUDE_CONFIG_DIR` alone does not fix it. That is the same trade `smoke-acp-bundled-mcp-live` already makes. While that cell runs, its citizen's control socket and per-gid lock exist under the operator's own `~/.pi/entwurf-control` and `~/.pi/entwurf-v2-locks`; its meta store stays fixture, so no garden record is minted outside the fixture, and the run ends by proving all six real roots hold the entry sets they started with, with no fixture garden id and no leftover lock.
121
121
  >
@@ -285,7 +285,7 @@ caller/target/cross-harness LIVE are owed by `smoke-pi-durable-fresh-live`, a re
285
285
  | Packed install | `check-pack-install` | Real tarball, but checkout-visible. |
286
286
  | Linux artifact consumer | required `check-install-container` CI job against one preserved candidate | Fixtures prove package/oracle shape, not a real Claude lifecycle. |
287
287
  | Exact release commit | the exact SHA's push run: all four required CI jobs green (the qualification body is not read, #144) | A different green SHA is not transferable evidence. |
288
- | macOS Entwurf-only install | required `macos-install-surface` CI job at the exact release SHA | Shape-only, CERTIFIED (CI): certifies the Entwurf-only install path on macOS only — never Claude/Copilot/OMP/agy wiring, the pi rail, ACP or mux. Promoted from non-voting (`continue-on-error`) to required in 0.20.0 after its first CERTIFIED (CI) green on a GitHub Actions `macos-latest` runner (not a physical host): run 34303884286 @ `70eda03` (2026-09-09), 65s, all 10 steps success — the packed candidate installed clean, `entwurf --help` and `entwurf check-bridge` ran (the latter listing the seven `entwurf-bridge` MCP tools of that date; eight today), an all-harness-absent `entwurf setup` reached `result: green` across the five documented SKIP probes (`pi`/`claude`/`agy`/`copilot`/`omp`), and five checked paths (`.pi`, `settings.json`, `.gemini`, `.copilot`, `auth.json.bak`) plus a byte-identical `auth.json` proved zero harness/credential writes. A macOS runner failure now blocks every release cut, the same as any other required job. |
288
+ | macOS Entwurf-only install | required `macos-install-surface` CI job at the exact release SHA | Shape-only, CERTIFIED (CI): certifies the Entwurf-only install path on macOS only — never Claude/Copilot/OMP/agy wiring, the pi rail, ACP or mux. Promoted from non-voting (`continue-on-error`) to required in 0.20.0 after its first CERTIFIED (CI) green on a GitHub Actions `macos-latest` runner (not a physical host): run 34303884286 @ `70eda03` (2026-09-09), 65s, all 10 steps success — the packed candidate installed clean, `entwurf --help` and `entwurf check-bridge` ran (the latter listing the seven `entwurf-bridge` MCP tools of that date; 0.41.0 declares nine, unmeasured on that runner), an all-harness-absent `entwurf setup` reached `result: green` across the five documented SKIP probes (`pi`/`claude`/`agy`/`copilot`/`omp`), and five checked paths (`.pi`, `settings.json`, `.gemini`, `.copilot`, `auth.json.bak`) plus a byte-identical `auth.json` proved zero harness/credential writes. A macOS runner failure now blocks every release cut, the same as any other required job. |
289
289
  | Native-Windows carrier (carrier-bearing release, from 0.39.0) | one `windows-native-preview.yml` dispatch at the exact prepared SHA after its four `ci.yml` jobs are green: accepted build receipt (two byte-identical MSVC builds, exact import set), `pack` printing `present`, eight Windows addon/citizen cells on the installed tarball; that run's `win32-preview-tgz` is the release candidate, retrieved (never packed locally) with its `SHA256SUMS`, the Windows-printed install hash and the downloaded hash equal, then accepted by the Linux `check-install-container` unchanged (release skill M3w) | Experimental, shape-only, non-voting for `ci.yml`: no vendor turn, x64 runner only, named gaps in BASELINE. The four `ci.yml` jobs (incl. macOS) saw an addon-less pack, so the carrier-bearing tarball's macOS install is unmeasured. Artifacts expire after 14 days; the preserved copy is the durable one. |
290
290
  | LIVE runtime | `LIVE=1 ./run.sh release-gate <scratch> --cut` plus any shipped on-demand backend axis | `--cut` enforces `SKIP=0`; a red wired gate blocks the cut. |
291
291
  | Native Claude host | installed strict doctor against a new real session | Missing live join is `NOT CERTIFIED`, not a fixture PASS. |
@@ -293,7 +293,10 @@ caller/target/cross-harness LIVE are owed by `smoke-pi-durable-fresh-live`, a re
293
293
  | Native Codex host | caller-seat topology accepted 2026-09-16 (56 assertions, exit 0); caller-DIRECTORY axis accepted the same day (65 assertions, exit 0) | Operator-owned birth/MCP/statusline/terminal-title atoms and the on-demand four-coordinate chain are measured on Linux at Codex 0.153.4: the app-server alone in session A (`$30/@41/%45`, pid 737636), and initial Pi / omitted-placement fresh Codex / Codex-opened outbound Pi all in a different session S (`$2`, `@46`/`@47`/`@48`; `@58`/`@59`/`@61` in the lane C run); exact callbacks and addressed delivery both ways. A ≠ S is the load-bearing half — the app-server's inherited `TMUX` names A, so the outbound Pi landing in S can only have come from the caller's own pane title, and its receipt records `seat-source=codex-title-anchor`. **#95 lane C added the second axis to the same card: WHERE the sibling starts.** Hop 1 joins four authorities that cannot borrow from each other — the pane's `#{pane_current_path}`, the vendor rollout's `session_meta.cwd`, the Entwurf record and the requested scratch — and hop 1's leg requests a cwd while hop 2 requests NONE, so hop 2's directory can only come from the Codex caller's own record; both are asserted different from the app-server's live `/proc/<pid>/cwd`. 65 and 56 are different contracts, not a regression. The 2026-09-12 fixed-home chain is PAST CONTRACT (#95 D1); `DELIVERY.md` owns every receipt and digest. Placement beside a TUI whose thread nobody named is unsupported and unclaimed. |
294
294
 
295
295
  The repo-local `entwurf-release` skill owns the `land → prepare → make → publish`
296
- state machine. Each mode is a separate GLG authorization. Preserve one candidate
296
+ state machine. Each mode is a separate GLG authorization. A narrower host restriction may
297
+ block a mode; it never waives a release-acceptance axis below. Feature-branch CI is not a
298
+ substitute for a forbidden local/LIVE floor: keep make blocked until the named cut has the
299
+ required execution authority and evidence. Preserve one candidate
297
300
  (for a carrier-bearing release, the one retrieved from the preview run, never a local
298
301
  pack), accept and publish those exact bytes without repacking, and record its digest and
299
302
  consumer image outside this standing protocol.
@@ -344,7 +347,7 @@ every focused gate green at the time:
344
347
 
345
348
  ### Wording — avoid safety-interpretation contamination
346
349
 
347
- When injecting a fact for a continuity check, use **plaintext that does not trigger model safety interpretation**. Avoid `secret token`, `password`, `API key`, `credential`, and meta-directives like "do not leak" — such wording makes the model treat the prompt as an exfiltration attempt and refuse, which makes **continuity look broken even when it is alive** (this happened once with `test-token-123`, misdiagnosed as a delegation failure). Instead: `The password is owl → reply in one word → owl`; code names / colors / animal names. Do not mix continuity and safety-behavior verification in one prompt.
350
+ When injecting a fact for a continuity check, use **plaintext that does not trigger model safety interpretation**. Avoid `secret token`, `password`, `API key`, `credential`, and meta-directives like "do not leak" — such wording makes the model treat the prompt as an exfiltration attempt and refuse, which makes **continuity look broken even when it is alive** (this happened once with `test-token-123`, misdiagnosed as a delegation failure). Instead: `The code word is owl → reply in one word → owl`; code names / colors / animal names. Do not mix continuity and safety-behavior verification in one prompt.
348
351
 
349
352
  ### bridge continuity vs semantic continuity
350
353
 
@@ -371,7 +374,7 @@ The goal is not merely "invoke Claude Code." We want:
371
374
  **One install command to remember: `./run.sh setup <project>`.** It is idempotent — re-run the exact same command whenever anything looks wrong. There is no second install surface to juggle: from a clone `setup` runs the whole floor in order.
372
375
 
373
376
  1. `pnpm install` — installs the pinned development dependencies and builds the bridge (source-checkout bootstrap only; an installed package never runs npm/pnpm inside `node_modules`)
374
- 2. pi wiring → `<project>/.pi/settings.json` + user-scope registration — only when a `pi` inside the supported range (`>=1.3.0 <1.4`) is on PATH; absent pi is an explicit zero-state SKIP, a below-floor pi is a detected FAIL. The user-scope entry is owner-recorded (#86 C2): another root's live-or-missing ownership makes this step a zero-write refusal (setup: pi FAIL) that names `takeover-user-scope`; the install-states bind the exact managed settings path (a drifted/symlinked/corrupt target is a zero-write refusal) and the inverse removes only the recorded owner's exact entry; `doctor-pi-package` reports the verdict
377
+ 2. pi wiring → `<project>/.pi/settings.json` + user-scope registration — only when a `pi` inside the supported range (`>=1.4.0 <1.5`) is on PATH; absent pi is an explicit zero-state SKIP, a below-floor pi is a detected FAIL. The user-scope entry is owner-recorded (#86 C2): another root's live-or-missing ownership makes this step a zero-write refusal (setup: pi FAIL) that names `takeover-user-scope`; the install-states bind the exact managed settings path (a drifted/symlinked/corrupt target is a zero-write refusal) and the inverse removes only the recorded owner's exact entry; `doctor-pi-package` reports the verdict
375
378
  3. Claude meta-bridge global plugin — only when `claude` is on PATH; otherwise skipped cleanly
376
379
  4. source stable-bin exposure — including certified `entwurf` → this checkout's `run.sh`, the managed runtime Copilot fresh resolves; helper units are attempted independently and a foreign helper is a named FAIL
377
380
  5. agy bridge + exact permission + statusline + `PreInvocation` hook — only when `agy` is on PATH; each adapter is idempotent and independently doctorable
@@ -438,7 +441,7 @@ mailbox with `origin=pi-session`, `replyable=true`).
438
441
 
439
442
  ### 1.4 Cross-install / cross-backend parity (optional, high-value)
440
443
 
441
- Compare a fresh self-awareness report across axes: (1) same backend, different install path — answer must be path-invariant; (2) same backend, different machine — identical native tool list + MCP server/tool set; (3) different backend, same bridge — same garden capability but **different** native tool surface (a Claude session reporting another backend's native tools is a fail); (4) native pi routing vs ACP-bridged, same model — the native pi citizen reaches the compiled `entwurf-bridge` through Pi's built-in MCP (`mcp__entwurf_bridge__<verb>`, six active tools and two hidden), while the ACP backend reaches that bridge through the explicit `entwurfProvider.mcpServers` projection in its own callable dialect (see §2.1). Compare the actual ingress provenance and backend-native tools, not an assumed absence of MCP on pi; receive to the pi citizen remains its record-addressed control address (a Unix socket; a named pipe on the experimental native-Windows Pi preview). Honest "I cannot tell" hedging is preferable to inventing provenance. This is the current #125 comparison criterion, not a newly measured parity receipt. agy is graded by its native-citizen checklist, not by pretending it has an ACP overlay.
444
+ Compare a fresh self-awareness report across axes: (1) same backend, different install path — answer must be path-invariant; (2) same backend, different machine — identical native tool list + MCP server/tool set; (3) different backend, same bridge — same garden capability but **different** native tool surface (a Claude session reporting another backend's native tools is a fail); (4) native pi routing vs ACP-bridged, same model — the native pi citizen reaches the compiled `entwurf-bridge` through Pi's built-in MCP (`mcp__entwurf_bridge__<verb>`, seven active tools and two hidden in the 0.41.0 source; installed/live offering requires its own receipt), while the ACP backend reaches that bridge through the explicit `entwurfProvider.mcpServers` projection in its own callable dialect (see §2.1). Compare the actual ingress provenance and backend-native tools, not an assumed absence of MCP on pi; receive to the pi citizen remains its record-addressed control address (a Unix socket; a named pipe on the experimental native-Windows Pi preview). Honest "I cannot tell" hedging is preferable to inventing provenance. This is the current #125 comparison criterion, not a newly measured parity receipt. agy is graded by its native-citizen checklist, not by pretending it has an ACP overlay.
442
445
 
443
446
  **Cortex on axis 3.** A cortex session must expose the same garden capability
444
447
  while keeping cortex's own native tool surface. Its callable MCP identifier shape is
@@ -468,7 +471,7 @@ Pass (carrier honesty): the subject distinguishes engraving from pi-context-augm
468
471
 
469
472
  - **Layer 0 — self-awareness:** ask environment self-awareness / MCP visibility / upstream-instruction awareness, guessing prohibited. Pass: recognizes native tool family, says "I don't know" honestly, answers MCP visibility only as configured, describes upstream instruction type without reproducing internal prompts. Fail: claims a nonexistent tool, conflates pi-custom and native tools, hallucinates MCP visibility, or conflates the two carriers (§1A.0).
470
473
  - **Layer 1 — native tool use:** throw file-reading / structure-analysis / regression-hunting tasks. Pass: Read/Edit/Bash/Grep/Glob selection is natural; no detour through MCP or recursive `pi`. Fail: strange detours for simple reads; speaks from memory without reading.
471
- - **Layer 2 — MCP boundary:** by default the eight entwurf MCP tools are not visible (they appear only when `entwurf-bridge` is registered). Pass: says invisible tools are not visible; explains the native-vs-MCP boundary; treats `entwurf_register_native` as binding an already-running conversation, never as fresh spawn. Fail: pretends to use an unseen tool; mimics entwurf via recursive `pi`.
474
+ - **Layer 2 — MCP boundary:** by default the entwurf MCP tools are not visible (they appear only when `entwurf-bridge` is registered). Pass: says invisible tools are not visible; explains the native-vs-MCP boundary; treats `entwurf_register_native` as binding an already-running conversation, never as fresh spawn. Fail: pretends to use an unseen tool; mimics entwurf via recursive `pi`.
472
475
  - **Layer 3 — focus across turns:** inject a fact, then accumulate turns mixing retrieval/exploration. Pass (post-0.4.1): after **8 turns** holds **3+ early facts** incl. **one verbatim string injected before turn 5**; no repeated exploration, no self-contradiction, no tool-strategy drift. Fail: forgets early reads; paraphrases instead of returning the verbatim string. Note: entwurf exposes no user-facing compaction; use the backend's `usage_update` footer as an overflow-risk signal (it follows the ACP backend's `used/size`, not pi's visible-transcript estimate).
473
476
  - **Layer 4 — vs direct Claude Code:** requires a verifier holding **both** the `entwurf` and a direct path (human-in-loop, or both transport handles). Compare latency / native tool accuracy / detours / boundary confusion / quality around turns 10–15. Repeated tool confusion, long-turn forgetting, or boundary workarounds are a fail.
474
477
 
@@ -540,7 +543,7 @@ The minimum passing bar:
540
543
  2. **All four required CI jobs green on the exact release commit:** `check`, `install-surface`, the required Linux `artifact-consumer`, and `macos-install-surface` — promoted from non-voting to required in 0.20.0 (see the release acceptance axes table above for what its green run does and does not certify); preserve the Linux artifact-consumer's tarball digest and image identity. For a carrier-bearing release those CI tarballs are addon-less and are NOT the release candidate: the exact release bytes are the M3w-retrieved preview tarball, accepted separately by the eight Windows cells and the Linux container (axes row "Native-Windows carrier"). The four-job floor, macOS included, is unchanged. A macOS runner failure now blocks the cut like any other required job.
541
544
  3. **Live floor MUST green:** `LIVE=1 ./run.sh release-gate <dir> --cut` exits 0 reporting `MUST PASS=N FAIL=0 SKIP=0`; with `--cut` a single SKIP is red, so the exit code itself now carries this condition. A BEHAVIOR FAIL is advisory, not blocking.
542
545
  4. **Native-host doctor green where the Claude meta-bridge is claimed:** a new post-install Claude session exists, live evidence is present, and the installed `doctor-meta-bridge` exits 0. `NOT CERTIFIED` is a release failure for that host, not a skip.
543
- 5. **Honest self-recognition:** the bridged model identifies its actual harness/backend, lists `entwurf-bridge` as the single MCP server with its eight current tools, and presents a backend-native (not normalized) tool surface.
546
+ 5. **Honest self-recognition:** the bridged model identifies its actual harness/backend, lists `entwurf-bridge` as the single MCP server with the tools its loaded bridge actually lists (the current source lists nine; a pi citizen offers seven with two hidden, the pi-durable contact eight), and presents a backend-native (not normalized) tool surface.
544
547
  6. **Carrier separation honored:** engraving vs pi-context-augment kept distinct (§1A.0); no bridge-identity narrative attributed to the engraving carrier.
545
548
  7. **agy shipped lane accepted:** all three agy doctors are green; automatic birth/statusline/sender identity and same-gid native-push reply are confirmed in a fresh conversation. `agentId=meta-session/antigravity` is correct; model display is not part of that contract. Same-pid concurrent conversation invocation is not claimed.
546
549
  8. **Codex caller-seat topology accepted:** its birth/MCP/status-line/terminal-title doctors are green and the on-demand real visible `Pi → Codex → Pi` cell records four coordinates: the operator-owned app-server in its own session A; initial Pi, omitted-placement fresh Codex, and Codex-opened outbound Pi all in a DIFFERENT session S; exact callbacks and addressed delivery succeed both ways. A ≠ S is a precondition, not a preference — the app-server's inherited environment names A, so an outbound Pi in S can only have come from the caller's own pane title, and the receipt must name `codex-title-anchor` as the rule that chose it. 0 or 2+ matching panes, and a caller whose `[tui].terminal_title` lacks `thread-id`, reject without mutation. Entwurf neither creates nor supervises the app-server, infers attached-TUI panes, nor claims placement beside a TUI whose thread nobody named. (The 2026-09-12 fixed-`codex`-home acceptance this condition replaced stays recorded in `DELIVERY.md` as history; #95 D1 retired that room.) Codex remains outside ACP and has no resume claim.
@@ -558,7 +561,7 @@ Passing establishes a **release verification floor**, not an 8-hour/day operatio
558
561
 
559
562
  - **ACP turn evidence.** The ACP path writes no `[entwurf:*]` stderr markers, and `ENTWURF_CHILD_STDERR_LOG` is not an ACP observation surface (its mirror in `pi-extensions/lib/entwurf-core.ts` belongs to the retired child-pi spawn and has no caller). Read three things instead: the lifecycle notices the backend writes into the assistant text (`[acp: preparing …]` / `[acp: reusing live session]`), the turn's typed `stopReason` (an ACP `cancelled` ending maps to `aborted`, `pi-extensions/lib/acp/backend.ts:865-866`), and the identity (pid + starttime) of the launcher and of any process the turn started.
560
563
  - **Retired dedicated smokes, live code invariants** (manual/troubleshooting only — *not* part of the release floor):
561
- - *Model-switch lock* — entwurf sessions are locked to their starting model. Gate: `check-model-lock` (in `pnpm check`, core tier). The dedicated live `smoke-model-switch` was retired in v2; the invariant lives in `pi-extensions/model-lock.ts` (extension guard) + `session-store.ts` `SessionModelLockedError` (the `decideBootstrap` fail-loud model lock).
564
+ - *ACP-boundary model guard* — a session using the `entwurf` provider keeps its starting model: `pi-extensions/model-lock.ts` reverts set/cycle transitions touching that provider (native-to-native switching is free). This is an after-event revert, not a transcript-clean refusal. Separately, `session-store.ts` `SessionModelLockedError` refuses a persisted-record model mismatch at `decideBootstrap`. Gate: `check-model-lock` (core tier); the dedicated live `smoke-model-switch` was retired in v2. This is not a lifetime model pin on every garden citizen.
562
565
  - *Cancel / abort cleanup* — an abort sends ACP `session/cancel` and lets the agent end its own turn (`awaitAcpPromptTurn`, `pi-extensions/lib/acp/backend.ts:762-801`); process-group teardown follows only after a bounded grace. A cancelled reuse turn keeps its live session; a cancelled first turn is not retained (`backend.ts:1861`), so the next turn bootstraps a new one. Dedicated `smoke-cancel` retired; invariant in code.
563
566
 
564
567
  ### Evidence preservation when a problem occurs
package/demo/README.md CHANGED
@@ -24,7 +24,7 @@ takes/helpers/tests are maintainer tooling and are deliberately excluded from th
24
24
 
25
25
  - Node at the `package.json#engines.node` floor; the checkout's pinned dependencies and
26
26
  current compiled bridge. After a source/bootstrap change, build with `pnpm run build-bridge`.
27
- - Pi at the checkout's supported floor (**current floor 1.3.0**); `entwurf` on PATH must be
27
+ - Pi at the checkout's supported floor (**current floor 1.4.0**); `entwurf` on PATH must be
28
28
  the intended source stable-bin. Source evidence is not registry-package acceptance.
29
29
  - `herdr` on PATH at exactly `scripts/fixtures/herdr-supply.json#version`; `asciinema` v3.
30
30
  - Herdr's existing Pi integration at
@@ -135,12 +135,12 @@ Dep bump ledger. 0.38.0 (#145) changes only the pi SDK set; the ACP rows are unc
135
135
  | Surface | Declaration | Class | What a green actually says |
136
136
  |---|---|---|---|
137
137
  | Entwurf package | `package.json` `version` | checkout declaration, not a release receipt | names the package label these rows sit beside; whether that label shipped, and with which rows, is CHANGELOG's record |
138
- | pi runtime | SDK exact `1.3.0`, host `>=1.3.0 <1.4` | **exact** oracle + **closed range** | the SDK set this checkout builds, gates and (since #130) ships as exact production dependencies; an ordinary Pi host inside the range is accepted, and the ceiling moves only on measurement. The published 0.36.0 through 0.37.1 shipped the 1.2.0 set (#141); 0.38.0 (#145) is the first to carry 1.3.0. Historical: the published 0.33.0 through 0.35.1 ship SDK exact 1.1.0, 0.32.0 ships SDK exact 1.0.4 with the next-minor ceiling (ROADMAP's 2026-10-07 bump entry), and 0.31.0 declared the earlier peer floor 1.0.2 |
138
+ | pi runtime | SDK exact `1.4.0`, host `>=1.4.0 <1.5` | **exact** oracle + **closed range** | the SDK set this checkout builds, gates and (since #130) ships as exact production dependencies; an ordinary Pi host inside the range is accepted, and the ceiling moves only on measurement. The published 0.36.0 through 0.37.1 shipped the 1.2.0 set (#141); 0.38.0 (#145) is the first to carry 1.3.0. Historical: the published 0.33.0 through 0.35.1 ship SDK exact 1.1.0, 0.32.0 ships SDK exact 1.0.4 with the next-minor ceiling (ROADMAP's 2026-10-07 bump entry), and 0.31.0 declared the earlier peer floor 1.0.2 |
139
139
  | ACP wire SDK | `@agentclientprotocol/sdk 1.8.0` | **exact** | the shared wire oracle both adapters speak |
140
140
  | Claude ACP adapter | `@agentclientprotocol/claude-agent-acp 0.90.0` | **exact**, bundled | shipped since 0.36.1; resolved before any PATH fallback; declaration is not compatibility evidence |
141
141
  | Claude Agent SDK | `0.3.296` (transitive) | **exact** oracle | the runtime risk surface behind the adapter |
142
142
  | Anthropic SDK | `0.100.1` | **exact**, peer-resolution only | satisfies the Agent SDK peer floor (0.93.0+); never an API client here (gate L4) |
143
- | Claude Code runtime | `>=2.1.217` (`entwurf.claudeCodeFloor`) | **floor** | below it, hook args are silently dropped; entwurf enforces this itself |
143
+ | Claude Code runtime | `>=2.1.306` (`entwurf.claudeCodeFloor`) | **floor** | below it, hook args are silently dropped; entwurf enforces this itself |
144
144
  | Node | `>=24` (`engines.node`) | **floor** | single axis, derived everywhere else |
145
145
  | Cortex Code | operator-installed CLI | **on-demand**, external | NOT a pinned dependency. Each LIVE record must name the exact CLI version it measured |
146
146
 
@@ -419,6 +419,10 @@ Otherwise record and defer. Every certification separates declared pins, determi
419
419
  evidence, Claude LIVE aggregate evidence, and Cortex direct on-demand evidence (with its CLI
420
420
  version). Per-bump measurements live in the ROADMAP **Dep bump(별도 트랙)** ledger.
421
421
 
422
+ ### Configured Claude ACP permissions
423
+
424
+ Source configuration, not a retirement LIVE receipt: `pi-extensions/lib/acp/overlay.ts` sets `permissions.defaultMode: "bypassPermissions"`, and `config.ts` includes `mcp__*` in the default `permissionAllow`. The configured Claude ACP child therefore does not supply a per-call permission prompt; these settings do not widen the explicit callable tool surface. `enrichMcpServersWithEnvelope` in `config.ts` injects the host Pi session's `PI_SESSION_ID` and the ACP agent label into the explicit entwurf-bridge stdio entry. If that bridge exposes `entwurf_retire_call`, a promptless call attributed to the host Pi citizen is consequently possible by source inference — ACP retirement through this path is unmeasured, not part of the ordinary Native Pi/Claude Code n=1 round trips. Neither those settings, annotations nor caller attribution is an operator grant: an explicit human request for that target/action and agent operating policy are still required. This describes existing configuration, not a new ACP retirement ban or permission mechanism.
425
+
422
426
  ### Bridge reachability is part of the contract
423
427
 
424
428
  Explicit entwurf-bridge connectivity inside an ACP turn is core value, so it is measured rather than
@@ -11,7 +11,7 @@ the one-line registration; everything an operator needs to actually wire a host
11
11
 
12
12
  **Being garden-native is not the same as being replyable, and Copilot is the case that separates them.** Its hook writes a sender marker, so an `entwurf_v2` send carries its own garden id and the receiver learns who wrote — measured 2026-08-21 on Copilot CLI 1.0.80, where a live send arrived under its own garden id with `origin: "meta-session"` and `replyable: false`. Replyability arrived later and through a different process: a first-party extension (`run.sh install-copilot-receive`) that the CLI forks, which binds to the same V3 record and writes a receiver marker owned by its own pid (#82 RAIL 5). So a Copilot citizen is `replyable: true` exactly while that extension is armed for it, and `replyable: false` — honestly, with a real garden identity — when it is not installed, not launched with `COPILOT_CLI_ENABLED_FEATURE_FLAGS=EXTENSIONS` (which is what `entwurf copilot` sets for one invocation), not yet born, or gone. Read the two facts off different rails: the sender marker answers *who sent this*; the receiver marker answers *can a reply land*. The onboarding obligations are in [`adding-a-harness.md`](./adding-a-harness.md) step 7, and the evidence boundary — the managed LIVE acceptance that moved receive to D6, and what D7-partial / D3-pending still exclude — is in [`DELIVERY.md`](../DELIVERY.md).
13
13
 
14
- **Which verb an external agent should reach for:** to deliver to / reply to a garden id, use **`entwurf_v2`** — it is the canonical delivery surface and the only one that reads whether the target is live pi, dormant pi, a mailbox-backed self-fetch citizen (Claude Code, Copilot, OMP), or native-push Antigravity/Codex and routes correctly. Discover targets with `entwurf_peers`, confirm your own identity with `entwurf_self`, drain a mailbox with `entwurf_inbox_read`, and use `entwurf_register_native` only as the explicit/manual fallback for binding an already-running agy conversation. Open a NEW sibling with **`entwurf_fresh_call {backend, model, task, cwd?, placement?}`**, and reopen a DORMANT pi citizen under the same id with **`entwurf_resume_call {target}`**.
14
+ **Which verb an external agent should reach for:** to deliver to / reply to a garden id, use **`entwurf_v2`** — it is the canonical delivery surface and the only one that reads whether the target is live pi, dormant pi, a mailbox-backed self-fetch citizen (Claude Code, Copilot, OMP), or native-push Antigravity/Codex and routes correctly. Discover targets with `entwurf_peers`, confirm your own identity with `entwurf_self`, drain a mailbox with `entwurf_inbox_read`, and use `entwurf_register_native` only as the explicit/manual fallback for binding an already-running agy conversation. Open a NEW sibling with **`entwurf_fresh_call {backend, model, task, cwd?, placement?}`**, and reopen a DORMANT pi or Claude Code citizen under the same id with **`entwurf_resume_call {target}`**. **`entwurf_retire_call {target}`** (new in 0.41.0) attempts retirement of exactly one target, and only one the human operator explicitly requested; mounting or seeing the tool, a coordinator role or caller identity is not permission, and an unknown result is not a reason to replay.
15
15
 
16
16
  Observed: Claude Code, Codex CLI, Antigravity CLI, GitHub Copilot CLI and OMP all reach this MCP bridge. Claude, Copilot and OMP become symmetric/replyable through mailbox-backed meta-sessions; agy and Codex use native-push. Codex's identity is request-scoped `_meta.threadId`, and its replyability requires that exact record plus a loaded app-server thread. Plain external hosts still need an identity lane to send.
17
17
 
@@ -90,7 +90,7 @@ External/meta-session semantics:
90
90
  #### Claude Code
91
91
 
92
92
  Plain MCP registration works independently of the native lifecycle. A garden-native
93
- mailbox citizen requires Claude Code >=2.1.217 plus the managed meta-bridge installer;
93
+ mailbox citizen requires Claude Code >=2.1.306 plus the managed meta-bridge installer;
94
94
  older versions silently drop exec-hook arguments and are refused.
95
95
 
96
96
  Claude Code supports both CLI registration and a separated global MCP config. The separated file is recommended for dotfile / `agent-config` workflows because `~/.claude.json` also carries OAuth-bearing state.
@@ -185,7 +185,7 @@ entwurf doctor-agy-hooks
185
185
 
186
186
  The three adapters deliberately own different atoms:
187
187
 
188
- - bridge: one `entwurf-bridge` server in `~/.gemini/config/mcp_config.json`, plus one narrow permission string per tool the normal agy workflow calls — `mcp(entwurf-bridge/entwurf_v2)`, `mcp(entwurf-bridge/entwurf_peers)`, `mcp(entwurf-bridge/entwurf_self)` — in `~/.gemini/antigravity-cli/settings.json`. agy defaults every `mcp` action to Ask, so a tool that ships without its own rule stops for a y/n on every call; `entwurf_inbox_read` is deliberately not granted (native-push has no inbox), neither is the manual `entwurf_register_native` fallback, and neither are `entwurf_fresh_call` / `entwurf_resume_call` (both launch into the caller's own tmux server, which an agy conversation has none of);
188
+ - bridge: one `entwurf-bridge` server in `~/.gemini/config/mcp_config.json`, plus one narrow permission string per tool the normal agy workflow calls — `mcp(entwurf-bridge/entwurf_v2)`, `mcp(entwurf-bridge/entwurf_peers)`, `mcp(entwurf-bridge/entwurf_self)` — in `~/.gemini/antigravity-cli/settings.json`. agy defaults every `mcp` action to Ask, so a tool that ships without its own rule stops for a y/n on every call; `entwurf_inbox_read` is deliberately not granted (native-push has no inbox), neither is the manual `entwurf_register_native` fallback, neither are `entwurf_fresh_call` / `entwurf_resume_call` (both launch into the caller's own tmux server, which an agy conversation has none of), and `entwurf_retire_call` stays at agy's default Ask (a retirement is one operator-requested action, never a standing grant);
189
189
  - statusline: the complete `statusLine` subtree pointing at the bare stable bin `entwurf-agy-statusline`;
190
190
  - hooks: one named `PreInvocation` hook pointing at the bare stable bin `entwurf-agy-imprint`.
191
191
 
@@ -253,8 +253,10 @@ spawned. The installer refuses to write into a config that denylists its own key
253
253
  **The tool names are omp's dialect, not Claude's.** omp mints `mcp__<server>_<tool>` after
254
254
  lowercasing and replacing every `[^a-z_]+` run with `_`, collapsing runs and trimming edges,
255
255
  so `entwurf_v2` surfaces as `mcp__entwurf_bridge_entwurf_v` — the trailing digit is eaten by
256
- the charset, not by the length cap. The live tool list is the acceptance oracle; a live
257
- session mounts all eight.
256
+ the charset, not by the length cap. The live tool list is the acceptance oracle. The recorded
257
+ observation mounted eight tools; the 0.41.0 bridge has nine, and that expanded OMP
258
+ live offering needs its own receipt. Retirement remains explicitly operator-requested, not
259
+ auto-granted by mounting the tool.
258
260
 
259
261
  **And the NAME is not the invocation. Under omp's default settings an MCP tool is not a
260
262
  function the model calls — it is a virtual file it writes to.** `tools.xdev` (boolean,
@@ -273,7 +275,7 @@ top-level (`Tool web_search not found`, upstream #5973), fixed by pinning it via
273
275
 
274
276
  `read xd://` reports exactly what the default hides. `[측정]` on a host with only this
275
277
  bridge registered, **11 devices**: omp's own `ast_edit`, `debug`, `lsp`, `browser`, plus all
276
- seven `entwurf_*` (as reported in that earlier measurement; the current bridge declares eight). So the default does not merely wrap entwurf — it wraps omp's own LSP and
278
+ seven `entwurf_*` (as reported in that earlier measurement; the current source bridge declares nine). So the default does not merely wrap entwurf — it wraps omp's own LSP and
277
279
  debugger too.
278
280
 
279
281
  **`entwurf setup` writes this for a detected omp host, and `entwurf install-omp-config` is
@@ -84,7 +84,15 @@ entwurf meta-bridge-fresh-cut && entwurf setup /path/to/project
84
84
  ## Upgrade order
85
85
 
86
86
  A checkout-backed host can begin running new code immediately after `git pull`, so
87
- make the boundary explicit:
87
+ make the boundary explicit. First distinguish an unreadable generation from a stale reader:
88
+ a known V3 schema extension (such as `attachIncarnation`) or newly admitted backend rejected
89
+ by an older strict reader is **reader-version skew, not a generation change**. Preserve the
90
+ records and native identity, roll every participating source/compiled/installed reader and
91
+ external mirror forward to the owning contract, then verify each active rail. Do not delete
92
+ fields, invent bindings, rewrite ids or execute an old reader's fresh-cut prescription merely
93
+ to silence that skew. Unmeasured readers remain an open rollout axis, not a pass.
94
+
95
+ For an actual generation transition, the order remains:
88
96
 
89
97
  ```text
90
98
  quiesce sessions → update package/checkout → fresh-cut if prescribed → reinstall/setup → reopen