@junghanacs/entwurf 0.36.0 → 0.37.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +290 -0
- package/DELIVERY.md +2 -0
- package/README.md +5 -0
- package/VERIFY.md +11 -4
- package/docs/acp-backend-rail.md +69 -5
- package/docs/setup-clean-host.md +1 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/meta-bridge-pi-durable.js +16 -1
- package/package.json +5 -4
- package/pi/pi-durable/bootstrap.mjs +113 -11
- package/pi/pi-durable/carrier/harness-setup.js +4 -1
- package/pi/pi-durable/carrier/prompt.js +31 -2
- package/pi/pi-durable/carrier/runtime.js +11 -2
- package/pi/pi-durable/carrier/sessions.js +75 -5
- package/pi/pi-durable/carrier/tui.js +23 -0
- package/pi/pi-durable/overlay/upstream-pin.json +10 -10
- package/pi-extensions/meta-bridge-pi-durable.ts +20 -0
- package/run.sh +5 -2
- package/scripts/check-acp-prompt-lifecycle.ts +4 -2
- package/scripts/check-acp-session-reuse.ts +4 -2
- package/scripts/check-acp-stop-reason.ts +4 -2
- package/scripts/check-acp-stream-hooks.ts +4 -2
- package/scripts/check-acp-usage-accounting.ts +4 -2
- package/scripts/check-gate-qualification.ts +3 -3
- package/scripts/check-herdr-supply.ts +32 -7
- package/scripts/fixtures/herdr-supply.json +12 -10
- package/scripts/lib/acp-gate-home.ts +75 -0
- package/scripts/lib/codex-socket-listener.ts +429 -0
- package/scripts/mutants/acp-stop-reason.json +11 -0
- package/scripts/mutants/codex-native.json +52 -0
- package/scripts/mutants/herdr-supply.json +8 -8
- package/scripts/mutants/pi-durable.json +80 -8
- package/scripts/smoke-codex-fresh-live.ts +31 -7
- package/scripts/smoke-pi-durable-fresh-live.ts +22 -3
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,296 @@ All notable changes to this project will be documented here. Format follows [Kee
|
|
|
4
4
|
|
|
5
5
|
## Unreleased
|
|
6
6
|
|
|
7
|
+
## 0.37.0 - 2026-10-15
|
|
8
|
+
|
|
9
|
+
### pi-durable
|
|
10
|
+
|
|
11
|
+
- **The durable app forwards pi's transport and stops compacting beside a running turn (#143).**
|
|
12
|
+
The owned Harness settings now carry `SettingsManager.getTransport()` (pi's default `auto`)
|
|
13
|
+
into every request; the reference app had left the field out, so the SDK chose per connection.
|
|
14
|
+
`auto` permits a cached WebSocket continuation only when the provider's own body and prefix
|
|
15
|
+
conditions hold; it does not make every request a delta. Background compaction is off
|
|
16
|
+
(`backgroundTokens: 0`): a turn past the blocking threshold waits for the SDK's summary instead
|
|
17
|
+
of one being written beside it. Threshold, manual and overflow compaction stay the SDK's, on
|
|
18
|
+
pi's own compaction settings. The SDK engine, models, auth and storage are unchanged, and this
|
|
19
|
+
claims neither lower vendor overload nor better memory.
|
|
20
|
+
- **`entwurf pi-durable --resume` reopens a chosen session (#143).** It lists the current
|
|
21
|
+
directory's durable sessions from their files and the record store only (creation time, garden
|
|
22
|
+
id, last write, open elsewhere), most recently written first, and opens the chosen one as it
|
|
23
|
+
was saved, under its own garden id. Cancelling opens nothing. No session, a non-terminal, a
|
|
24
|
+
session open elsewhere, or a new session's argument beside it refuse by name, with no fallback.
|
|
25
|
+
`--continue` still opens the newest-created session.
|
|
26
|
+
- **The durable app lists a project's skills again.** The owned prompt handed pi's `skills`
|
|
27
|
+
setting to the skill loader as written, so a relative entry was read against the session
|
|
28
|
+
directory: a project's `.pi/settings.json` entry `../.claude/skills` named a `.claude/skills`
|
|
29
|
+
beside the project, in its parent, and the project's own skills were silently not listed.
|
|
30
|
+
Relative path entries are now read as pi reads them, the project's against its `.pi` and the
|
|
31
|
+
global ones against the agent directory. Both scopes are kept, the project's first, so a project
|
|
32
|
+
skill wins over a global one of the same name. Absolute and `~` entries are unchanged.
|
|
33
|
+
Patterns, package skills, `.agents/skills` and skill diagnostics are not supported by this
|
|
34
|
+
change. No project trust check is added: the durable app reads a project's settings without a
|
|
35
|
+
trust decision, as it did before.
|
|
36
|
+
|
|
37
|
+
### Herdr plugin
|
|
38
|
+
|
|
39
|
+
- **The measured Herdr moves to 0.9.5, the hotfix of 0.9.4.** `scripts/fixtures/herdr-supply.json`
|
|
40
|
+
pins v0.9.5 (`2ecf19a9`), protocol 22, with both Linux asset digests taken from the immutable
|
|
41
|
+
release and named by its attestation. On aarch64 the pin rests on a private CLI baseline:
|
|
42
|
+
version, protocol 22, 18 `integration status` rows unchanged from 0.9.4, and a residue-free
|
|
43
|
+
`server stop`. The full `check-herdr-sandbox` then passed at this pin on x86_64 in the CI run of
|
|
44
|
+
the SHA that introduced it (run 38667564413 at `d8cd8f3`, 12 assertions on private Herdr 0.9.5),
|
|
45
|
+
and the x86_64 asset is recorded as executed with that receipt; it has not run on aarch64. The
|
|
46
|
+
supply receipt mutant now removes the x86_64 receipt instead of flipping an unexecuted flag,
|
|
47
|
+
because no asset at this pin is unexecuted. Plugin admission (Herdr >= 0.9.0), the plugin
|
|
48
|
+
version and the runtime lock are unchanged.
|
|
49
|
+
- **A host that uses the Claude integration needs `herdr integration install claude` again
|
|
50
|
+
after upgrading to Herdr 0.9.5.** 0.9.5 raises its claude integration from v10 to v11 and leaves pi at v9. Over integrations seeded by
|
|
51
|
+
0.9.4 it reads claude as `outdated (v10 < v11)`, which the plugin's activation profile files as
|
|
52
|
+
`herdr-integration-outdated`, so the build and pane block [1] refuse by name until the
|
|
53
|
+
reinstall. In the measured 0.9.4-seeded private sandbox, the reinstall changed only the hook's
|
|
54
|
+
version line; the pi extension and the Claude settings stayed byte-identical. The plugin README
|
|
55
|
+
and the launch-rail document say so.
|
|
56
|
+
|
|
57
|
+
### Release boundary and verification
|
|
58
|
+
|
|
59
|
+
- **Scope.** A core minor over `v0.36.1`: #143 at `3a65c8d`, the Herdr 0.9.5 pin at `d8cd8f3`
|
|
60
|
+
and its CI record at `dbb8a32`, and the skill fix at `4968ffd`. Not in it: #142 (the ACP
|
|
61
|
+
thinking option), and the default thinking level of a new durable session named by a model
|
|
62
|
+
without `:<level>`, which starts at `off` where pi starts at its configured default; that is
|
|
63
|
+
recorded on #142 and not changed here. The Herdr manifest stays 0.36.0; plugin 0.37.0 is a
|
|
64
|
+
separate cut after the core is published.
|
|
65
|
+
- **Local evidence before this preparation is focused, not a whole pass.** Each affected
|
|
66
|
+
qualification group (PARTIAL) measured its own change with a green control, and none is
|
|
67
|
+
carried to another. #143 killed 81/81 in the `check-pi-durable-beside` group. The supply
|
|
68
|
+
receipt mutant re-aimed at `dbb8a32` killed with its whole `check-herdr-supply` group, 11/11;
|
|
69
|
+
that commit skipped CI, so the re-aimed mutant has no CI run yet. The skill fix killed 82/82 in
|
|
70
|
+
the `check-pi-durable-beside` group (receipt `d934e43c4dad5dc28b49a3a85c5a133d126df9558f2bc260087c1b34cf7422f9`),
|
|
71
|
+
with 900 of the 982 committed claims not run. The qualification body has not run on any of them.
|
|
72
|
+
- **Unpublished candidate carrier.** As in 0.36.1, `plugins/herdr/runtime-lock.json` names the
|
|
73
|
+
closed `herdr-checkout` source, because 0.37.0 has no published integrity yet. The npm 0.36.1
|
|
74
|
+
bytes and every existing tag are unchanged. Re-pinning registry bytes is the separate Herdr
|
|
75
|
+
0.37.0 cut, after the operator publishes core 0.37.0.
|
|
76
|
+
- **No local static floor; it is the exact CI's.** `check:hermetic` carries
|
|
77
|
+
`check-herdr-plugin-build` and `check-herdr-sandbox`, which must not run on the operator host,
|
|
78
|
+
and `release-gate`'s first MUST step is `pnpm run check:full`. By the operator's decision for
|
|
79
|
+
this cut only, `check:full` was not run locally and is owed whole to the prepared SHA's CI.
|
|
80
|
+
The decision is not inherited by a later cut.
|
|
81
|
+
- **LIVE, taken step by step, not as `release-gate --cut`.** The gate's 23 non-static MUST steps
|
|
82
|
+
and its BEHAVIOR step ran in the gate's own order, command, environment and working directory,
|
|
83
|
+
stopping at the first red, on the frozen candidate (`4968ffd` with this preparation's edits),
|
|
84
|
+
with the candidate fingerprint the same before and after each run. The first run passed steps
|
|
85
|
+
1-18 and failed `smoke-mux-lifecycle-live`: its pi-native sibling (`openai-codex/gpt-6-luna`)
|
|
86
|
+
was alive and answered with a shell call and "ACK" but never called the callback tool within
|
|
87
|
+
300 s. No input of that step changed in this cut, and the cause was not measured. By the
|
|
88
|
+
operator's choice it was run again once, unchanged, with steps 1-18 reused: steps 19-23 and the
|
|
89
|
+
BEHAVIOR step then passed. **MUST 23/23 PASS (18 + 5 on the second run), SKIP 0; BEHAVIOR 1/1
|
|
90
|
+
PASS.** `smoke-mux-lifecycle-live` passed 81 checks, `smoke-codex-fresh-live` 66 assertions
|
|
91
|
+
against an app-server started for each run in its own tmux session (named pane pid, a child
|
|
92
|
+
holding the kernel LISTEN inode, `gpt-5.6-luna` and `openai-codex/gpt-5.6-luna`, stopped by one
|
|
93
|
+
TERM after the run), and `smoke-pi-durable-fresh-live` 36 assertions. The gate sessions held no
|
|
94
|
+
ambient identity carrier. This is local LIVE evidence, not a release-gate verdict and not the
|
|
95
|
+
exact CI.
|
|
96
|
+
- **Exact CI owes the whole floor.** The prepared SHA's exact CI (the four required jobs, the
|
|
97
|
+
whole qualification body over the 982 committed claims, the re-aimed supply mutant included,
|
|
98
|
+
and `check-herdr-sandbox` on pinned Herdr 0.9.5) is the next checkpoint and is not presumed
|
|
99
|
+
PASS.
|
|
100
|
+
- **Publication boundary.** No make artifact, core tag, GitHub Release or npm publication is
|
|
101
|
+
claimed; they wait for the exact CI, the make decision and the operator.
|
|
102
|
+
|
|
103
|
+
## 0.36.1 - 2026-10-14
|
|
104
|
+
|
|
105
|
+
### ACP
|
|
106
|
+
|
|
107
|
+
- **Claude ACP closure (#128):** adapter `0.85.1` → `0.90.0`, shared direct/child ACP wire SDK
|
|
108
|
+
`1.6.0` → `1.8.0`, Agent SDK `0.3.286` → `0.3.296`; the shared-wire contract is retained and
|
|
109
|
+
Entwurf adds no new policy. Two no-prompt readbacks against the installed 0.90.0 found the
|
|
110
|
+
three curated ids answering exactly as on 0.85.1, so the set-model alias rows are unchanged.
|
|
111
|
+
The first followed only the production launch and overlay seam; the second also built every
|
|
112
|
+
session with the production session meta under the shipped defaults. Neither sent a prompt or
|
|
113
|
+
saw a model-turn update, but they do not prove zero API calls, measure the billing rail or an
|
|
114
|
+
operator's own settings, or certify a 0.90.0 prompt turn. The first run's two operating
|
|
115
|
+
observations, both with the cause unmeasured, are in the ROADMAP ledger.
|
|
116
|
+
- **ACP gates no longer touch the operator's Claude overlay (#128).** Five offline gates that
|
|
117
|
+
drive the compiled turn loop now sandbox HOME, XDG and the pi agent dir before importing the
|
|
118
|
+
backend; a tripwire and a paired mutant hold that.
|
|
119
|
+
|
|
120
|
+
### Codex
|
|
121
|
+
|
|
122
|
+
- **The Codex fresh-call smoke reads app-server socket ownership from the kernel (#128).** It
|
|
123
|
+
used to accept the named app-server when its argv spelled `--listen` for the default control
|
|
124
|
+
socket. It now takes the physical endpoint the production socket classifier resolves, and
|
|
125
|
+
requires exactly one process in the named PID's own subtree (the named PID, bound to the start
|
|
126
|
+
time read when its identity was checked, or one of its descendants) to hold that socket's
|
|
127
|
+
kernel LISTEN inode, and a bounded second read must agree. Anything else fails closed as
|
|
128
|
+
not-owned or indeterminate. The 0.36.1 LIVE run exercised it once on an owned app-server
|
|
129
|
+
started with the explicit `--listen` wrapper (below); a vendor-managed daemon is still not
|
|
130
|
+
admitted by it.
|
|
131
|
+
|
|
132
|
+
### pi-durable
|
|
133
|
+
|
|
134
|
+
- **The pi-durable fresh-call smoke waits for its doorbell turn to end.** It took pd's native
|
|
135
|
+
snapshot as soon as Pi's relay reached the fixture, then required the doorbell submission to
|
|
136
|
+
be `done`. A pi-durable input stays `placed` while the run that consumed it is active and turns
|
|
137
|
+
`done` only when that run ends, so a slow final generation read as a failed admission. The
|
|
138
|
+
0.36.1 LIVE run caught exactly that (submission `placed`, the last generation task still
|
|
139
|
+
`running`, every delivery receipt green). The smoke now waits, with its existing bounded
|
|
140
|
+
`until`, for this message's submission to settle (`done` or `unanswered`) before the snapshot,
|
|
141
|
+
and still requires `done`. Smoke-only; no pi-durable runtime byte changes.
|
|
142
|
+
|
|
143
|
+
### Release boundary and verification
|
|
144
|
+
|
|
145
|
+
- **Scope.** A core patch over `v0.36.0`: the four #128 checkpoints `fdf8c3d`, `3da44be`,
|
|
146
|
+
`3e4f52f` and `3242922` above, the handoff `b043833`, and the smoke fix `91cc080`. The
|
|
147
|
+
pi-durable runtime, the Herdr manifest (0.36.0), its host pin (Herdr 0.9.4) and the plugin
|
|
148
|
+
implementation are unchanged; the Herdr runtime's acquisition source does change (the carrier
|
|
149
|
+
below). #142 is not in it. Still open in #128 and not claimed here: admission of a
|
|
150
|
+
vendor-managed Codex daemon, the Copilot 1.0.97 cell, agy 1.3.1 (not certified), the OMP
|
|
151
|
+
18.10.4 doctors, and the remaining native cells.
|
|
152
|
+
- **Local evidence before this preparation is focused, not a whole pass.** Each affected
|
|
153
|
+
qualification wave (PARTIAL) measured its own checkpoint, each with a green control, and none
|
|
154
|
+
is carried to another. The Codex socket-ownership bytes killed 150/150 in the
|
|
155
|
+
`check-mux-fresh-call` group; 825 of the 975 committed claims were not run. The dependency move
|
|
156
|
+
killed 4/4 in `check-dep-versions`; 971 of 975 were not run. The gate isolation killed 45/45 in
|
|
157
|
+
five groups (prompt lifecycle 15, usage accounting 12, stream hooks 10, stop reason 7, session
|
|
158
|
+
reuse 1); 931 of the 976 committed claims were not run. The qualification body has not run on
|
|
159
|
+
any of them.
|
|
160
|
+
- **Unpublished candidate carrier.** As in 0.36.0, `plugins/herdr/runtime-lock.json`
|
|
161
|
+
temporarily names the closed `herdr-checkout` source: an npm-source lock must agree with the
|
|
162
|
+
checkout's core version, and 0.36.1 has no published integrity yet. The Herdr manifest stays
|
|
163
|
+
0.36.0. Operating effect: a Herdr plugin installed from `--ref main` while this carrier stands
|
|
164
|
+
acquires this cut's checkout runtime, not npm 0.36.0. The `v2026.10.14-herdr.0.36.0` tag and
|
|
165
|
+
the npm 0.36.0 bytes are unchanged, and moving a host between the two sources needs an
|
|
166
|
+
explicit deactivate/activate. Re-pinning the registry bytes is a separate, verified commit
|
|
167
|
+
after the operator publishes core 0.36.1.
|
|
168
|
+
- **The local FULL is RED, and its Herdr cell is CI's.** `pnpm run check:full` ran once on the
|
|
169
|
+
frozen candidate (642 s, exit 1). `check:core` passed, `check:hermetic` passed every step but
|
|
170
|
+
its last, and `check-herdr-sandbox` failed its version boundary: the pin is Herdr 0.9.4 and
|
|
171
|
+
the host binary was 0.9.3. That cell is not to run locally (it reads the operator's own
|
|
172
|
+
panes), and the aggregate reached it unnoticed; before failing it executed the host binary's
|
|
173
|
+
`--version` once and read no pane. `check:package`, which never ran, then passed alone
|
|
174
|
+
(163 s; 601 files in the dry-run pack). By the operator's decision for this cut only, the
|
|
175
|
+
sandbox cell is owed to the prepared SHA's CI, which runs it on the digest-pinned Herdr 0.9.4
|
|
176
|
+
and must pass there; the exception is not inherited by a later cut. Logs: FULL
|
|
177
|
+
`29b08c71e3d3c92fecb936f5611838088d35b8c5566f4dde99e03cc4b76b0078`, package
|
|
178
|
+
`937e69eeb551bcd0aea049550185e1f6d9f24d917f28099f768928554a16ae84`.
|
|
179
|
+
- **LIVE, taken step by step, not as `release-gate --cut`.** The gate's first MUST step re-runs
|
|
180
|
+
`check:full`, so it would have reached the same local Herdr cell. By the operator's decision for
|
|
181
|
+
this cut, the gate's other 23 MUST steps ran in its own order, command, environment and
|
|
182
|
+
working directory (`run.sh` 7027-7127), stopping at the first red, with no static step. The
|
|
183
|
+
first run passed steps 1-22 and failed `smoke-pi-durable-fresh-live` on the race fixed above.
|
|
184
|
+
The fixed smoke then passed alone on the same candidate (36 assertions, 66 s); steps 1-22 are
|
|
185
|
+
reused because the fix is not an input to them. **MUST 23/23 PASS (22 + the fixed step 23),
|
|
186
|
+
SKIP 0**. The advisory BEHAVIOR step was not run. Among them: the 12 ACP smokes ran the first
|
|
187
|
+
prompt turns on the 0.90.0 driver, and `smoke-codex-fresh-live` passed 66 assertions against
|
|
188
|
+
an app-server started for this cut in its own tmux session (pid 2345801, the named PID's child
|
|
189
|
+
2345861 holding the kernel LISTEN inode) with `gpt-5.6-luna` and `openai-codex/gpt-5.6-luna`.
|
|
190
|
+
The gate session held no ambient identity carrier, and the candidate fingerprint was the same
|
|
191
|
+
before and after each run. Scratch `/tmp/entwurf-release-gate-0.36.1.1aZK83`. This is local
|
|
192
|
+
LIVE evidence, not a release-gate verdict and not the exact CI.
|
|
193
|
+
- **Exact CI owes the whole floor.** The prepared SHA's exact CI (the four required jobs, the
|
|
194
|
+
qualification body, and `check-herdr-sandbox` on pinned Herdr 0.9.4) is the next checkpoint and
|
|
195
|
+
is not presumed PASS.
|
|
196
|
+
- **Publication boundary.** No make artifact, core tag, GitHub Release or npm publication
|
|
197
|
+
is claimed; they wait for the exact CI, the make decision and the operator.
|
|
198
|
+
|
|
199
|
+
## v2026.10.14-herdr.0.36.0 — Herdr Entwurf plugin 0.36.0
|
|
200
|
+
|
|
201
|
+
A separate Herdr plugin snapshot, not another core release: core package 0.36.0 and
|
|
202
|
+
its `v0.36.0` tag remain unchanged. The operator has published the core to npm.
|
|
203
|
+
|
|
204
|
+
### Herdr plugin
|
|
205
|
+
|
|
206
|
+
- **Re-pin the published core 0.36.0.** `plugins/herdr/runtime-lock.json` names npm
|
|
207
|
+
`@junghanacs/entwurf@0.36.0` with the registry's measured sha512
|
|
208
|
+
(`sha512-3aPk69RmAF35x…244UwQ==`). The plugin manifest moves 0.35.1 → 0.36.0;
|
|
209
|
+
admission remains Herdr >= 0.9.0 and the one-build/one-pane/Linux shape is unchanged.
|
|
210
|
+
- **The measured Herdr moves to 0.9.4.** `scripts/fixtures/herdr-supply.json` pins
|
|
211
|
+
v0.9.4 (`7e26bad9`), protocol 22, with both Linux asset digests taken from the
|
|
212
|
+
immutable release and named by its attestation. On aarch64 the pin rests on a
|
|
213
|
+
private CLI baseline: version, protocol 22, 18 `integration status` rows unchanged
|
|
214
|
+
from 0.9.3, pi (v9) and claude (v10) integrations seeded by 0.9.3 reading `current`
|
|
215
|
+
and reinstalling byte-identically, and a residue-free `server stop`. The full
|
|
216
|
+
`check-herdr-sandbox` has not run at this pin on either architecture; its first run
|
|
217
|
+
is this snapshot's CI. The x86_64 asset is recorded as not yet executed.
|
|
218
|
+
- **Herdr 0.9.4 changes how plugins are installed.** It builds each install or update
|
|
219
|
+
in a new managed installation directory, keeps the previous one until no server uses
|
|
220
|
+
it, and adds `herdr plugin update`. The plugin README now says so. The Entwurf runtime
|
|
221
|
+
and harness wiring live outside the checkout, so an update should leave an activated
|
|
222
|
+
host working; that is not yet measured.
|
|
223
|
+
|
|
224
|
+
### Source-specific verification
|
|
225
|
+
|
|
226
|
+
- Registry metadata reports version/latest 0.36.0, repair 0.12.8-repair.1. The downloaded
|
|
227
|
+
tarball is 11121634 bytes, SHA256
|
|
228
|
+
`7478acd01828280ccb989c4eeb230ae5238bddeeb791f6caa209a4fca2e24a0a`, byte-identical to
|
|
229
|
+
the preserved accepted core candidate; independently computed sha512 equals
|
|
230
|
+
`dist.integrity`. A sandboxed `pi install npm:@junghanacs/entwurf@0.36.0` resolved the
|
|
231
|
+
ACP bridge inside the sandbox and listed the curated `entwurf` models.
|
|
232
|
+
- **Focused gates on the branch.** On aarch64, with the digest-checked Herdr 0.9.4 first
|
|
233
|
+
on `PATH`, nine focused gates passed: `check-herdr-supply` (11 assertions),
|
|
234
|
+
`check-herdr-plugin` (49), `check-herdr-plugin-profile` (14), `check-herdr-plugin-build`
|
|
235
|
+
(21), `check-herdr-runtime-bootstrap` (31), `check-herdr-activation` (33),
|
|
236
|
+
`check-herdr-placement` (25), `check-herdr-fresh-call` (45) and `check-gate-manifests`.
|
|
237
|
+
That last one validated 971 committed mutants across 72 lanes and executed none of them.
|
|
238
|
+
The scripts typecheck fence was clean.
|
|
239
|
+
- **Receipt binding tightened, then re-checked.** Review found that the supply gate's
|
|
240
|
+
receipt check accepted a `herdr 0.9.40` receipt for the 0.9.4 pin, because it matched a
|
|
241
|
+
substring. It now requires the receipt to begin with the exact version token. Nine
|
|
242
|
+
in-memory stimuli against the gate's own check then returned the expected verdicts. They
|
|
243
|
+
include that prefix-version counterexample, which is refused, and a synthetic future x86_64
|
|
244
|
+
positive, which is not execution evidence. The supply gate, the fence and
|
|
245
|
+
`check-gate-manifests` were re-run green. The supply group's qualification PARTIAL killed
|
|
246
|
+
11/11 mutants with a green control. The other 960 committed mutants did not run, so this is
|
|
247
|
+
not qualification of the snapshot.
|
|
248
|
+
- **The npm runtime swap against the real registry.** These cells used the production
|
|
249
|
+
runtime helper against the real registry, in sandboxed user roots.
|
|
250
|
+
- Seeding: the 0.35.1 runtime came from its own committed lock and package metadata.
|
|
251
|
+
- Swap: 0.35.1 → 0.36.0 took one acquisition. The cached 0.36.0 tarball's sha512 equals
|
|
252
|
+
the lock. Its sha256 `7478acd0…` equals the ready journal's observed digest and the
|
|
253
|
+
accepted core candidate.
|
|
254
|
+
- Journals: the `installing` journal carried the 0.35.1 identity as `previousRuntime`;
|
|
255
|
+
the `runtime-ready` journal carries `previousRuntime: null`.
|
|
256
|
+
- Last-good tree: when acquisition began, the 0.35.1 tree was still at the active address.
|
|
257
|
+
That it stays there until the candidate verifies comes from the helper's source order
|
|
258
|
+
(write `installing`, acquire and verify in staging, then swap). The run observed only
|
|
259
|
+
the start of that window.
|
|
260
|
+
- Torn swap: a manually seeded one (the active tree moved to the backup address; not a
|
|
261
|
+
killed process or power loss) was restored before acquisition and reported
|
|
262
|
+
`recovered: torn-swap`.
|
|
263
|
+
- Re-request: an exact re-request acquired nothing and left journal and tree
|
|
264
|
+
byte-identical.
|
|
265
|
+
- Removal: the owned removal reclaimed cache, runtime and journal.
|
|
266
|
+
- Not certified by these cells: Herdr's install/update relocation, harness wiring, the
|
|
267
|
+
installed CLI's deactivate path, a model turn, or recovery from a real kill.
|
|
268
|
+
- Pending, and not claimed here:
|
|
269
|
+
- the pushed SHA's four-job CI, with the full `check-herdr-sandbox` and the qualification
|
|
270
|
+
body (971 mutants);
|
|
271
|
+
- the public-ref install/use/update/deactivate journey on Herdr 0.9.4, including whether
|
|
272
|
+
an activated runtime survives a plugin update.
|
|
273
|
+
|
|
274
|
+
### Publication checkpoint
|
|
275
|
+
|
|
276
|
+
Recorded after the snapshot, as docs only; no Herdr plugin byte, manifest or pin changes with it.
|
|
277
|
+
|
|
278
|
+
- **Publication checkpoint for `v2026.10.14-herdr.0.36.0`, measured at its commit `9f33dd1`
|
|
279
|
+
before the snapshot was published.** The snapshot's push CI 38479767989 passed all four jobs
|
|
280
|
+
and the official exact-SHA oracle. Its check job verified the pinned x86_64 Herdr 0.9.4 digest
|
|
281
|
+
before execution; `check-herdr-sandbox` passed 12 assertions on private Herdr 0.9.4; the
|
|
282
|
+
qualification body killed 971/971. On Herdr 0.9.4 in a clean aarch64 container, the unchanged
|
|
283
|
+
`smoke-herdr-raw-install-live --ref main` passed at that commit. A driver-level journey then
|
|
284
|
+
went from the public `v2026.10.13-herdr.0.35.1` snapshot on Pi 1.1.0, through a pinned Pi
|
|
285
|
+
upgrade to 1.2.0, to a version-changing cross-ref reinstall of the 0.36.0 plugin (`herdr
|
|
286
|
+
plugin install … --ref main`) under a private Herdr server, and passed 10/10: wiring entries
|
|
287
|
+
unchanged; a session born before the reinstall kept its process, garden id and control socket;
|
|
288
|
+
a session was born after it; `herdr plugin update` at that same ref was only an
|
|
289
|
+
already-up-to-date no-op; and deactivate removed the owned runtime, cache, ledger and wiring
|
|
290
|
+
entries. On a fixed Pi 1.2.0 the 0.35.1 snapshot refuses by its own range: 0.35.1 and 0.36.0
|
|
291
|
+
accept disjoint Pi ranges. Not certified: a session's MCP or model continuity, `pi update
|
|
292
|
+
self`, a host upgrade, a `plugin update` that finds a new commit, a herdr-pane row, an x86_64
|
|
293
|
+
container, the tag as `--ref`, or when Herdr reclaims a retained previous installation. The
|
|
294
|
+
supply manifest's x86_64 `executed: false` remains the preparation-time declaration; flipping
|
|
295
|
+
it is a separate change.
|
|
296
|
+
|
|
7
297
|
## 0.36.0 - 2026-10-13
|
|
8
298
|
|
|
9
299
|
### pi-durable
|
package/DELIVERY.md
CHANGED
|
@@ -504,6 +504,8 @@ ENTWURF_CODEX_FRESH_PI_MODEL=<pi-model> \
|
|
|
504
504
|
./run.sh smoke-codex-fresh-live
|
|
505
505
|
```
|
|
506
506
|
|
|
507
|
+
The gate reads that ownership from the kernel, not from argv (#128). It takes the endpoint `checkCodexSocketFile` verified, finds its one LISTEN row in the named process's own socket table, and requires exactly one process in the named PID's own subtree to hold that inode — and still hold it on a bounded second read of that holder's fds, with every walked pid's parent and start time unchanged and the named PID still the process whose uid, argv and environment were checked (`scripts/lib/codex-socket-listener.ts`). A wrapper whose child listens (the pnpm `codex.js` shape measured at the 0.36.0 cut) and a listener started with the vendor-default `--listen unix://` are both read as what they are; a subtree that holds no fd on the inode is not-owned, and zero or several rows, several holders, an unreadable entry, or a pid, parent, fd or row that moved between reads is indeterminate. Either is a FAIL. A positive is only this ownership fact: it does not admit a vendor-managed daemon as an operator shape, and it does not satisfy the separate A ≠ S seat precondition.
|
|
508
|
+
|
|
507
509
|
This is the intended gate entrypoint, not acceptance by command name. The accepted receipt above
|
|
508
510
|
has a fixture only collecting evidence: a real visible record-backed Pi opens Codex from outside the
|
|
509
511
|
home, and the later exact callback and bidirectional delivery receipts close the topology claim.
|
package/README.md
CHANGED
|
@@ -91,8 +91,13 @@ incomplete, drifted or mixed supply refuses by name.
|
|
|
91
91
|
```bash
|
|
92
92
|
entwurf pi-durable --provider <provider> --model <model-id> --width task-wide
|
|
93
93
|
entwurf pi-durable --continue
|
|
94
|
+
entwurf pi-durable --resume
|
|
94
95
|
```
|
|
95
96
|
|
|
97
|
+
`--continue` opens this directory's newest durable session. `--resume` lists its
|
|
98
|
+
sessions, most recently written first, by creation time, garden id and last write, and
|
|
99
|
+
reopens the one you choose as it was saved; a cancelled choice opens nothing.
|
|
100
|
+
|
|
96
101
|
The contact adds garden identity and two-way mailbox communication. Entwurf owns the
|
|
97
102
|
thin app/TUI composition (shipped as an emitted JS carrier); execution, models/auth, queues,
|
|
98
103
|
SQLite, transcripts and recovery remain the upstream SDK's:
|
package/VERIFY.md
CHANGED
|
@@ -106,7 +106,7 @@ that moved out of `scripts/` and into a file beside its subject would otherwise
|
|
|
106
106
|
|
|
107
107
|
> **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.
|
|
108
108
|
|
|
109
|
-
> **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. `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.
|
|
109
|
+
> **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.
|
|
110
110
|
|
|
111
111
|
> **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.
|
|
112
112
|
|
|
@@ -227,7 +227,14 @@ holds that node and the badge counts it, `pending` while recovered), and the car
|
|
|
227
227
|
`durableLayout` drawn by the published pi-tui renderer on a fake terminal across rows 1–8 × cols
|
|
228
228
|
1–12, count changes and resizes, against the pinned upstream screen for the zero-task case. Its
|
|
229
229
|
editor is a fixed stand-in: the real `CustomEditor`, overlays, emoji width and a consumer's own task
|
|
230
|
-
are the visible-terminal and consumer subjects.
|
|
230
|
+
are the visible-terminal and consumer subjects. The 1.2 bootstrap child also observes `--resume`
|
|
231
|
+
reopening the chosen session and cancelling to nothing (#143). Two more sandboxed children reach the
|
|
232
|
+
reference app's settings and prompt mapping: one drives the real bootstrap against a loopback
|
|
233
|
+
WebSocket Responses endpoint, whose received frames decide the forwarded transport and its
|
|
234
|
+
continuation and the reference compaction policy (no background summary; blocking and overflow
|
|
235
|
+
kept, #143); the other renders the carrier prompt's skills section with no model, against a fixture
|
|
236
|
+
of both settings scopes, an absolute entry, a same-name pair and a wrong-base decoy.
|
|
237
|
+
`check-pi-runtime-version` asks the
|
|
231
238
|
same verifier whether this checkout resolves exactly the pinned SDK set (the native set; the PATH
|
|
232
239
|
host is setup's subject), and `check-pi-import-surface` asserts the carrier fence. The separate
|
|
233
240
|
`check-pi-durable-contact`, `check-pi-durable-send` and `check-pi-durable-receive` are
|
|
@@ -454,14 +461,14 @@ The single-turn / multi-turn / cross-process / persistence-boundary / shutdown i
|
|
|
454
461
|
|
|
455
462
|
| Invariant | Current gate (pointer) |
|
|
456
463
|
|---|---|
|
|
457
|
-
| Single-turn prompt extraction, SessionStart hook not mistaken for prompt | `smoke-acp-raw-turn-live
|
|
464
|
+
| Single-turn prompt extraction, SessionStart hook not mistaken for prompt | `smoke-acp-raw-turn-live` (a bare-vendor shape by design: no overlay and no session meta, so it is not the production session shape), `check-acp-prompt-builder` |
|
|
458
465
|
| Multi-turn continuity + recall (process-scoped reuse) | `smoke-acp-session-reuse-live`, `check-acp-session-reuse` |
|
|
459
466
|
| Cross-process continuity / cache before-after | `check-acp-session-store` (signature, decideBootstrap, persist/parse) |
|
|
460
467
|
| Lifecycle policy — turn-scoped is always new; process-scoped may reuse only the live in-memory session; persisted records are not a resume/load path today | `check-acp-session-store`, `check-acp-session-reuse` |
|
|
461
468
|
| Tool-call / event mapping | `check-acp-event-mapper`, `smoke-acp-provider-live` |
|
|
462
469
|
| Prompt lifecycle — no wall-clock cutoff on a running turn; abort ends it by ACP `session/cancel` with bounded cleanup; a child death is reported with exit status + stderr on new AND reuse turns; our prompt-phase error text is not classified transient by pi | `check-acp-prompt-lifecycle`, `check-probe-ordering` |
|
|
463
470
|
| Claude typed `incomplete_tool_call` prompt failure — real pi retry/overflow classifiers reject the fixed verdict, raw evidence stays in existing diagnostics, exactly one seal and drop on new/reuse; other failures retain the legacy stderr path, cancel stays separate. A deterministic receipt does not certify the target vendor runtime. No LIVE gate induces this rejection; the raw-turn and reuse smokes certify only their own rows above. | `check-acp-vendor-rejection` (four mutants) |
|
|
464
|
-
| Claude set-model answer read back before the prompt — an exact row, or the one alias row (value and name) measured for the requested id; a missing model option, another row or any other value/name pair refuses with non-transient text. Deterministic only: the measured 0.85.1
|
|
471
|
+
| Claude set-model answer read back before the prompt — an exact row, or the one alias row (value and name) measured for the requested id; a missing model option, another row or any other value/name pair refuses with non-transient text. Deterministic only: the answer shapes measured at 0.85.1, and unchanged at 0.90.0 with the production session meta (no prompt), are fixture literals, not a runtime certificate. | `check-acp-set-model-readback` (two mutants), `check-probe-ordering` |
|
|
465
472
|
| Operator mcpServers / skills reach the live session | `smoke-acp-mcp-live`, `smoke-acp-skill-live`, `check-acp-config` |
|
|
466
473
|
| Overlay isolation + memory containment | `check-acp-overlay`, `smoke-acp-memory-containment-live`, `check-acp-tool-surface` |
|
|
467
474
|
|
package/docs/acp-backend-rail.md
CHANGED
|
@@ -134,10 +134,10 @@ ledger, 2026-10-03).
|
|
|
134
134
|
| Surface | Declaration | Class | What a green actually says |
|
|
135
135
|
|---|---|---|---|
|
|
136
136
|
| 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 |
|
|
137
|
-
| pi runtime | SDK exact `1.2.0`, host `>=1.2.0 <1.3` | **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.
|
|
138
|
-
| ACP wire SDK | `@agentclientprotocol/sdk 1.
|
|
139
|
-
| Claude ACP adapter | `@agentclientprotocol/claude-agent-acp 0.
|
|
140
|
-
| Claude Agent SDK | `0.3.
|
|
137
|
+
| pi runtime | SDK exact `1.2.0`, host `>=1.2.0 <1.3` | **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 ships this 1.2.0 set (#141). 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
|
+
| ACP wire SDK | `@agentclientprotocol/sdk 1.8.0` | **exact** | the shared wire oracle both adapters speak |
|
|
139
|
+
| Claude ACP adapter | `@agentclientprotocol/claude-agent-acp 0.90.0` | **exact**, bundled | checkout candidate; resolved before any PATH fallback; declaration is not compatibility evidence |
|
|
140
|
+
| Claude Agent SDK | `0.3.296` (transitive) | **exact** oracle | the runtime risk surface behind the adapter |
|
|
141
141
|
| 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) |
|
|
142
142
|
| Claude Code runtime | `>=2.1.217` (`entwurf.claudeCodeFloor`) | **floor** | below it, hook args are silently dropped; entwurf enforces this itself |
|
|
143
143
|
| Node | `>=24` (`engines.node`) | **floor** | single axis, derived everywhere else |
|
|
@@ -333,6 +333,68 @@ different reasons, and collapsing them would hide a real risk**:
|
|
|
333
333
|
`turnQuotaMeta` and `quotaTokenCount` are byte-identical as extracted entry bodies. Turn
|
|
334
334
|
settlement moved inside the changed `runConsumer`.
|
|
335
335
|
|
|
336
|
+
- **0.85.1 → 0.90.0 (#128) is a compatibility bump; Entwurf writes no new contract for it.**
|
|
337
|
+
`[측정 2026-10-14; upstream compare 72 commits / 185 files read through the GitHub API; nothing
|
|
338
|
+
executed but the readback below]`. The closure and evidence classes are in the ROADMAP Dep bump
|
|
339
|
+
entry for 2026-10-14.
|
|
340
|
+
- **Set-model readback re-measured, not inherited.** One no-prompt run against the installed
|
|
341
|
+
0.90.0 through the production launch and overlay seam. On the wire: `initialize` ×1,
|
|
342
|
+
`session/new` ×4, `session/set_config_option` ×3, no `session/prompt`, and no message,
|
|
343
|
+
thought, tool, plan or usage update. Production `enforceModel` accepted all three curated
|
|
344
|
+
ids with the 0.85.1 answers, so `CLAUDE_SET_MODEL_ALIAS_ROWS` is unchanged. The offered list
|
|
345
|
+
grew from 12 to 13 rows: Fable 5.1 is now offered as the alias `fable` (the set answer still
|
|
346
|
+
names its own `claude-fable-5-1` row), `haiku` is named `Haiku 5.5`, and
|
|
347
|
+
`claude-haiku-4-5-20251001` is new. Offered rows are not Entwurf support: only the three
|
|
348
|
+
curated ids (`model-ids.js`) were set and read back, and no other row is measured or
|
|
349
|
+
admitted. The vendor's auth-status notifications and context probes are not model turns,
|
|
350
|
+
and nothing here proves zero API calls; the billing rail is unmeasured.
|
|
351
|
+
- **Evidence limits, stated before anything builds on this entry.** The readback followed the
|
|
352
|
+
production launch and overlay seam only. Its session meta carried the model alone and
|
|
353
|
+
omitted the production `settingSources: []` (`tool-surface.ts:152`), so it is not a full
|
|
354
|
+
production-shape measurement. The focused green of `check-acp-{stop-reason,usage-accounting,
|
|
355
|
+
prompt-lifecycle,stream-hooks,session-reuse}` at that point proved fake semantics only, not
|
|
356
|
+
Rule 12 isolation: their `ensureOverlay` stub sat on deps, which backend never reads, so the
|
|
357
|
+
real adapter refreshed the operator overlay. The earlier "no gate touches the operator
|
|
358
|
+
overlay" claim is retired.
|
|
359
|
+
- **Both limits repaired afterwards (2026-10-14).** The five gates now sandbox HOME, XDG and the
|
|
360
|
+
pi agent dir before importing the compiled backend, assert that the overlay constant resolves
|
|
361
|
+
inside that sandbox (`scripts/lib/acp-gate-home.ts`, paired mutant
|
|
362
|
+
`ACP-GATE-HOME-SANDBOXED`), and no longer carry the dead stubs. A second single no-prompt run
|
|
363
|
+
then built every session through the production path — `resolveProviderConfig` over explicit
|
|
364
|
+
empty operator input, `loadCarrier`, `buildSessionMeta`, `enrichMcpServersWithEnvelope` — so
|
|
365
|
+
each `session/new` carried `settingSources: []`, the default tools, the inline settings and
|
|
366
|
+
the shipped carrier. On the wire: `initialize` ×1, `session/new` ×3, `session/set_config_option`
|
|
367
|
+
×3, seven responses, no `session/prompt` and no turn update. The three curated ids answered
|
|
368
|
+
exactly as before. These are the shipped defaults; an operator's own settings were not
|
|
369
|
+
measured.
|
|
370
|
+
- **Two operating observations from that run, both with the cause unmeasured.** The overlay
|
|
371
|
+
`projects/` held 524 entries after `ensureOverlay` and before the spawn, and 469 after the
|
|
372
|
+
child group was reaped; the run itself deletes nothing there and left no entry for its
|
|
373
|
+
scratch cwd. Which process removed the 55 entries, and whether they were transcripts, is not
|
|
374
|
+
measured, and it is not claimed for earlier driver versions. Separately, the inbound wire tap
|
|
375
|
+
saw one stream error event in a phase that was not recorded, while every request was
|
|
376
|
+
answered and no line failed to parse. In the production-meta run the overlay `projects/`
|
|
377
|
+
count stayed 469 → 469, which is consistent with the vendor skipping its retention sweep when
|
|
378
|
+
`settingSources` is empty but does not establish that cause, and the one inbound-tap stream
|
|
379
|
+
error fell in its teardown phase; that does not date the first run's.
|
|
380
|
+
- **Permission options are unchanged.** The nine option builders under `permissions/` are
|
|
381
|
+
byte-identical between the tags. `presentation.ts` adds titles only for v2 clients, and
|
|
382
|
+
`exit-plan.ts` publishes the approved plan only behind the `plan` client capability; we
|
|
383
|
+
advertise neither.
|
|
384
|
+
- **Cancel and turn settlement are fixed upstream and compatible here.** The stop-reason set is
|
|
385
|
+
unchanged; only `cancelled` gains emission sites (5 → 7), across #1228, #1229, #1242 and
|
|
386
|
+
#1243 (0.86) and #1221, #1309, #1313 and #1327 (0.90). The typed `incomplete_tool_call`
|
|
387
|
+
rejection keeps its kind and text. Our abort is still `session/cancel` → grace → teardown;
|
|
388
|
+
the runtime effect of these fixes is UNRUN.
|
|
389
|
+
- **Ambient only:** 0.90 merges model settings as `{...CLAUDE_MODEL_CONFIG, ...provided}`, so
|
|
390
|
+
an ambient `CLAUDE_MODEL_CONFIG` can now shape the model rows. Entwurf neither sets nor
|
|
391
|
+
scrubs it (the spawn env spreads `process.env`), the same posture as
|
|
392
|
+
`CLAUDE_AGENT_ACP_EXPERIMENTAL_V2`.
|
|
393
|
+
- **Behind capabilities we do not advertise:** v2 router fixes, RFD subagents, the AIR session
|
|
394
|
+
index and background terminals. Observation: a prompt that is exactly `/mcp` is answered by
|
|
395
|
+
the agent (#1218); a new session's first prompt carries our augment ahead of the user text,
|
|
396
|
+
a reuse turn does not.
|
|
397
|
+
|
|
336
398
|
- **The one 0.73.0 → 0.75.1 change that DOES reach us:** context compaction is now surfaced as a
|
|
337
399
|
synthetic ACP tool lifecycle (0.75.0, #991) — a `tool_call` with `kind: "think"`, title
|
|
338
400
|
`Compact conversation`, and `_meta.contextCompaction` schema v1 — where it used to arrive as
|
|
@@ -405,7 +467,9 @@ typing `pi --model …` keeps the upstream fuzzy behaviour, so use the full 5.5
|
|
|
405
467
|
The adapter reads its set-model answer back before the prompt. The driver resolves the
|
|
406
468
|
requested id onto one of the rows it offers and answers with that row's `value` as the
|
|
407
469
|
model option's `currentValue` (0.85.1 `dist/acp-agent.js:5459-5499`). Measured on
|
|
408
|
-
2026-10-12 against 0.85.1
|
|
470
|
+
2026-10-12 against 0.85.1, and again unchanged on 2026-10-14 against 0.90.0 (the second
|
|
471
|
+
time also with the production session meta), with no prompt sent: `claude-fable-5-1`
|
|
472
|
+
answers its own row,
|
|
409
473
|
while `claude-sonnet-5-5` and `claude-opus-5-5` answer the alias rows `sonnet` and
|
|
410
474
|
`opus`, named `Sonnet 5.5` and `Opus 5.5`. The id an alias row resolves to is not on the
|
|
411
475
|
ACP wire, so `CLAUDE_SET_MODEL_ALIAS_ROWS` (`backend-adapter.ts`) declares, for each
|
package/docs/setup-clean-host.md
CHANGED
|
@@ -358,6 +358,7 @@ tree (for example an override that binds pi-durable's `pi-ai` to an older publis
|
|
|
358
358
|
```bash
|
|
359
359
|
entwurf pi-durable --provider <provider> --model <exact catalog id> --width task-wide
|
|
360
360
|
entwurf pi-durable --continue # this directory's newest durable session
|
|
361
|
+
entwurf pi-durable --resume # choose one of this directory's durable sessions
|
|
361
362
|
```
|
|
362
363
|
|
|
363
364
|
The bootstrap imports only the compiled closure shipped beside it
|
|
@@ -107,9 +107,24 @@ import * as fs from "node:fs";
|
|
|
107
107
|
import * as path from "node:path";
|
|
108
108
|
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
109
109
|
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
110
|
-
import { defaultMetaMailboxDir, defaultMetaReceiversDir, defaultMetaSendersDir, defaultMetaSessionsDir, readAddressableMetaIdentity, readMetaReceiverMarker, readMetaSenderMarker, requireGardenId, upsertMetaSession, writeMetaReceiverMarker, writeMetaSenderMarker, } from "./lib/meta-session.js";
|
|
110
|
+
import { defaultMetaMailboxDir, defaultMetaReceiversDir, defaultMetaSendersDir, defaultMetaSessionsDir, listAllMetaIdentitiesDir, readAddressableMetaIdentity, readMetaReceiverMarker, readMetaSenderMarker, requireGardenId, upsertMetaSession, writeMetaReceiverMarker, writeMetaSenderMarker, } from "./lib/meta-session.js";
|
|
111
111
|
/** The backend id this contact mints under. */
|
|
112
112
|
export const PI_DURABLE_BACKEND = "pi-durable";
|
|
113
|
+
/**
|
|
114
|
+
* The garden ids the record store holds for these durable sessions (#143 `--resume`), read only: a session no record
|
|
115
|
+
* names is absent, and an unreadable record is counted, never guessed.
|
|
116
|
+
*/
|
|
117
|
+
export function piDurableGardenIds(nativeSessionIds, sessionsDir = defaultMetaSessionsDir()) {
|
|
118
|
+
const wanted = new Set(nativeSessionIds);
|
|
119
|
+
const { identities, errors } = listAllMetaIdentitiesDir(sessionsDir);
|
|
120
|
+
const gardens = new Map();
|
|
121
|
+
for (const identity of identities) {
|
|
122
|
+
if (identity.backend === PI_DURABLE_BACKEND && wanted.has(identity.nativeSessionId)) {
|
|
123
|
+
gardens.set(identity.nativeSessionId, identity.gardenId);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return { gardens, unreadable: errors.length };
|
|
127
|
+
}
|
|
113
128
|
/** The durable Extension's name, owned exactly. */
|
|
114
129
|
export const PI_DURABLE_EXTENSION_NAME = "entwurf";
|
|
115
130
|
/** Every bridge tool this contact exposes. A tool outside this list is not callable here. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@junghanacs/entwurf",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.37.0",
|
|
4
4
|
"description": "Visible sibling sessions across independent agent harnesses: launch in Herdr or tmux, reopen pi in tmux, and deliver messages by garden id; native bridges and Claude/Cortex ACP adapters.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|
|
@@ -96,8 +96,8 @@
|
|
|
96
96
|
"claudeCodeFloor": ">=2.1.217"
|
|
97
97
|
},
|
|
98
98
|
"dependencies": {
|
|
99
|
-
"@agentclientprotocol/claude-agent-acp": "0.
|
|
100
|
-
"@agentclientprotocol/sdk": "1.
|
|
99
|
+
"@agentclientprotocol/claude-agent-acp": "0.90.0",
|
|
100
|
+
"@agentclientprotocol/sdk": "1.8.0",
|
|
101
101
|
"@anthropic-ai/sdk": "0.100.1",
|
|
102
102
|
"@earendil-works/chord": "1.2.0",
|
|
103
103
|
"@earendil-works/pi-agent-core": "1.2.0",
|
|
@@ -123,7 +123,8 @@
|
|
|
123
123
|
"husky": "^9.1.7",
|
|
124
124
|
"rregex": "1.13.1",
|
|
125
125
|
"typescript": "^6.0.2",
|
|
126
|
-
"vitest": "4.1.9"
|
|
126
|
+
"vitest": "4.1.9",
|
|
127
|
+
"ws": "8.21.3"
|
|
127
128
|
},
|
|
128
129
|
"scripts": {
|
|
129
130
|
"prepare": "npm run --silent build-bridge 1>&2",
|