@junghanacs/entwurf 0.41.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.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,247 @@ All notable changes to this project will be documented here. Format follows [Kee
4
4
 
5
5
  ## Unreleased
6
6
 
7
+ ## 0.41.1 — 2026-10-23
8
+
9
+ **Unpublished candidate; release acceptance is not complete.** The actual operator
10
+ transition and Native aggregate below have been measured. Final prepared-SHA CI and
11
+ the requested manual BODY, the exact carrier-bearing artifact, main promotion, tags
12
+ and publication remain separate gates. Main still carries the published 0.41.0 Herdr
13
+ lock: promoting the checkout lock can change what main-tracking installations acquire.
14
+ A validation artifact or an earlier checkpoint's green run is not final acceptance.
15
+
16
+ ### Pi 1.4.0 reference shell
17
+
18
+ - Move the coherent nine-member production SDK set from exact 1.3.0 to exact 1.4.0,
19
+ keeping Entwurf's owned app/TUI composition and deterministic carrier emission.
20
+ The SDK owns execution, storage, queues, compaction and recovery; this adds no second
21
+ engine, supervisor, automatic retry policy or credential surface. The ordinary Pi
22
+ adapter's supported range becomes `>=1.4.0 <1.5`.
23
+ - Show sleeping tasks in the background-task tally, their local wake time in the task
24
+ panel, and the SDK's blocked reason verbatim. Keep cancellation distinct from retry;
25
+ no new retry command or UI is added.
26
+ - Adapt the ACP vendor-rejection test to pi-ai 1.4's bounded HTTP-status matching:
27
+ digits inside opaque request/tool ids no longer classify as transient. The raw
28
+ `Internal error` control remains transient. The production non-replay verdict and
29
+ raw diagnostic preservation are unchanged.
30
+ - Add SDK 1.3 public-API-generated SQL fixtures and independent SQLite conformance
31
+ tests for the SDK 1.4 schema transition, row/index preservation, constraints,
32
+ reopening and future-version refusal. These are SDK conformance, not a local
33
+ mutation claim over the upstream engine. Keep fixture data and its generator out
34
+ of installed packages, with npm packlist and tarball-floor checks.
35
+ - Separate this unpublished candidate's checkout instructions from public npm
36
+ 0.41.0 / SDK 1.3 instructions. The Herdr lock uses the closed `herdr-checkout`
37
+ verification carrier; it invents no unpublished npm integrity. No new Herdr
38
+ snapshot or registry publication is implied.
39
+ - Keep the Codex LIVE fixture callback-only on its outbound Pi leg. Asking for an
40
+ extra ACK created a fourth real tool call while the audit required three. The
41
+ exact-three, nonce and terminal-turn assertions remain unchanged; only the two
42
+ matching task strings changed, not production routing or retry behavior.
43
+ - Trim historical platform details, dated labels and duplicated lifecycle prose
44
+ from the README while retaining operator consent, unknown-outcome handling,
45
+ platform limits and the published-versus-candidate install distinction.
46
+
47
+ ### Upgrade boundary
48
+
49
+ Opening each existing durable session with SDK 1.4 migrates its SQLite storage to
50
+ schema 2. SDK 1.3 / Entwurf 0.41.0 refuses that session. Reinstalling the older package
51
+ is not data rollback: restoring a pre-upgrade backup reopens the old data, but omits
52
+ work done after that backup. Database restoration does not undo external messages,
53
+ tool/file/network effects, meta records or mailbox read receipts. Preserve the newer
54
+ set and inspect unknown outcomes before considering a restore or resend.
55
+ Even a refused SDK 1.4 open can change the file header
56
+ and sidecars while preserving logical rows (measured on a future-schema copy). Back up the complete target session
57
+ file set before its first new-SDK open.
58
+
59
+ Close the old owner normally and prove the process has exited before cutover. A
60
+ stale lock is not proof: a bounded, SQLite-free fixture reproduced a stalled but live
61
+ owner losing its lock. This lock behavior already existed in 0.41.0. Use the resume
62
+ picker to select the intended session rather than guessing with `--continue`.
63
+
64
+ SDK 1.4 counts a task SDK 1.3 left running as an interrupted run at its first open;
65
+ SDK 1.4's own normal close does not add a restart. Repeated interruptions can park a
66
+ task at the restart limit. Inspect outcomes before any resend; cancellation is not
67
+ retry, and recovery of an actual parked model generation remains unmeasured.
68
+
69
+ ### Preparation and operating evidence — not final release acceptance
70
+
71
+ Implementation checkpoint `3ed079b` follows independent source/proof reviews. Its
72
+ earlier working candidate passed the deterministic FULL floor in 885 seconds with
73
+ source/index unchanged, carrier 45 tests, storage conformance 11 tests, and ACP
74
+ vendor rejection 14 tests. The final pre-commit vocabulary sweep then changed one
75
+ harness-table SDK literal into a reference to the pin authority, independently read
76
+ and checked. These are implementation-era observations, not this prepared version's
77
+ FULL, native LIVE, installed artifact, operating-session migration or exact-SHA CI.
78
+
79
+ Private sandbox observations separately cover old-package refusal, backup restoration
80
+ and loss of later marker work, SDK task replay/parking, candidate shell open/close,
81
+ SDK 1.3-born fixture identity carried into the SDK 1.4 shell, and in-process open
82
+ failure releasing the session lock. Parked non-generation Hold tasks did not block
83
+ root input; conversation abort cancelled only the non-background Hold. None proves
84
+ real model-generation continuity or an operating citizen's actual upgrade.
85
+
86
+ Kill-power re-measured at this shipped SHA: none (all 1178 committed claims not
87
+ re-proved, #144).
88
+
89
+ That required line records the source's preparation-time measurement state, not a
90
+ prediction of later CI. GLG separately requested manual BODY evidence. The earlier
91
+ validation SHA `ff86c88bd58ba776e15ef48e1e4be566971a3e9a` has a four-job green push run
92
+ [39790950791](https://github.com/junghan0611/entwurf/actions/runs/39790950791) and a
93
+ separate `workflow_dispatch qualify=true` run
94
+ [39792701159](https://github.com/junghan0611/entwurf/actions/runs/39792701159): 85 lanes,
95
+ 1178 unique claims KILLED, survived/error/skipped 0. Those are that SHA's receipts,
96
+ not this later prepared SHA's. Its requested exact-SHA CI/BODY results must be recorded
97
+ separately in [#148's live evidence thread](https://github.com/junghan0611/entwurf/issues/148#issuecomment-6348113761);
98
+ no future result is invented here and the standing release policy is unchanged.
99
+
100
+ Earlier-candidate PARTIAL (operator-attested, not this SHA): head addcb8cd0bac surface
101
+ 6a9481bc357c -- `["bash","run.sh","check-dep-versions"]` 4/4 receipt 48d085e06816.
102
+
103
+ Earlier-candidate PARTIAL (operator-attested, not this SHA): head addcb8cd0bac surface
104
+ 6a9481bc357c -- `["bash","run.sh","check-pack-pin-matcher"]` 2/2 receipt 593dd0401502.
105
+
106
+ Earlier-candidate PARTIAL (operator-attested, not this SHA): head addcb8cd0bac surface
107
+ 6a9481bc357c -- `["bash","run.sh","check-pi-durable-carrier"]` 35/35 receipt 258f783406a7.
108
+
109
+ Earlier-candidate PARTIAL (operator-attested, not this SHA): head ff86c88bd58b surface
110
+ 7c75301f53a9 -- `["bash","run.sh","check-release-gate-outcomes"]` 21/21 receipt 251b71584442.
111
+
112
+ Earlier-candidate PARTIAL (operator-attested, not this SHA): head ff86c88bd58b surface
113
+ 7c75301f53a9 -- `["bash","run.sh","check-mux-fresh-call"]` 150/150 receipt 56c3558f9a15.
114
+
115
+ Both ff86c88 PARTIAL receipts were taken with the Codex fixture correction still
116
+ uncommitted on that head, on the recorded working surface.
117
+
118
+ The versioned working candidate's earlier FULL completed in 890 seconds, exit 0,
119
+ with source/index unchanged (log SHA256 `757115f291c05f397631f6e8de48a2b77fdfde973d5219edf70e63658c3b8e88`).
120
+ It predates later checkpoint metadata and is not a final committed-SHA CI result.
121
+
122
+ **Actual operator transition:** after the human closed the original owners and their
123
+ exit was proven with the scheme-aware owner classifier, the complete session file
124
+ set was consistently backed up without opening SQLite. The exact Linux validation
125
+ tarball (`92e5b2722ab8063d2bc50936db358a36a02a5e92e36babc3e1fbf98b399c2f44`)
126
+ was accepted unchanged in a container, separately exercised through an isolated pnpm
127
+ global old-to-new takeover, then installed as the operator's 0.41.1/SDK 1.4 runtime.
128
+ User-scope extension loading and doctors passed. The coordinator resumed the actual
129
+ old conversation: garden/native/createdAt/cwd stayed unchanged, a new owner attached,
130
+ a real model turn recalled the pre-close agreement and used tools, and four
131
+ send/read/reply/read receipts closed a round trip with an existing Claude Code sibling.
132
+ The human-reopened Pi reviewer also confirmed its identity, model, tools and recall.
133
+ No separate live DB query or universal transcript/parked-generation recovery claim is
134
+ made. This addon-less Linux validation tarball is not the carrier-bearing release file.
135
+
136
+ **Native aggregate:** the first run at `ff86c88` was MUST 23/1/0 and BEHAVIOR 1/0/0:
137
+ the extra requested ACK was a fourth distinct call, not parser duplication. Its raw
138
+ failure is preserved. After the scoped fixture correction, an affected fresh Codex
139
+ run passed 66 assertions with both terminal audits exactly 3/3. The new full run at
140
+ `a9d05847295230ed58f53b0bfd689d9ebd6ac1da` then passed **MUST 24/0/0,
141
+ BEHAVIOR 1/0/0**, including the deterministic FULL; source/index stayed unchanged.
142
+ The operator still used the **ff86c88 validation install**, not an installed a9d0584
143
+ artifact. Its runtime closure (bridge dist, pi-durable carrier, bootstrap and resolver)
144
+ was byte-identical to the checkout before and after the run; README and LIVE fixture
145
+ bytes differ. This is checkout-gate evidence with that installed runtime, not a claim
146
+ that the complete a9d0584 package was installed or accepted.
147
+ Scratch `/tmp/entwurf-release-gate-0.41.1-r2.cl0DDR`, log `release-gate.log`, SHA256
148
+ `020f489bf6fe024447bfc26a12f3499e5b80e844b0dcadf1e474e6adc8b4ae05`.
149
+ P9 was a read-only census: root 4.9 GiB and home 6.9 GiB free, with the existing
150
+ Emacs infrastructure identified rather than killed; current receipts and scratch
151
+ were preserved. Only the explicitly owned test app-server was normally stopped.
152
+
153
+ The final prepared-SHA push CI/manual BODY and Win32-bearing artifact acceptance
154
+ are still pending. The validation tarball has no Win32 addon; it cannot stand for
155
+ M3w or final macOS/Windows acceptance. No tag, GitHub release, npm publication,
156
+ Herdr re-pin or laptop rollout has occurred. Earlier 0.41.0 receipts below remain
157
+ historical.
158
+
159
+ ## v2026.10.22-herdr.0.41.0 — Herdr Entwurf plugin 0.41.0
160
+
161
+ A separate Herdr plugin snapshot, not another core release: core package 0.41.0 and its
162
+ `v0.41.0` tag remain unchanged. Core 0.41.0 is now published to npm; the publication is
163
+ verified from the registry, not from observing who ran it.
164
+
165
+ **Reading guide (2026-10-22).** The `## 0.41.0` section below and the body of the
166
+ [v0.41.0 GitHub release](https://github.com/junghan0611/entwurf/releases/tag/v0.41.0) are
167
+ prepare-time and make-time text: their npm wording (the Herdr lock's "unpublished candidate",
168
+ "npm publication each need their own approval") predates the publication. The publish is the later registry fact recorded in this section (`latest` 0.41.0,
169
+ published 2026-10-22T04:37:02.853Z, registry bytes equal to the accepted candidate). Neither the
170
+ published tarball's CHANGELOG nor the core release page is amended by this snapshot.
171
+
172
+ ### Herdr plugin
173
+
174
+ - **Re-pin the published core 0.41.0 and close the checkout window.**
175
+ `plugins/herdr/runtime-lock.json` returns from the `herdr-checkout` carrier that core 0.41.0
176
+ rode to production npm source `@junghanacs/entwurf@0.41.0` with the registry's measured
177
+ sha512 (`sha512-hSf6St0u…fBlvA==`). The plugin manifest moves 0.40.0 → 0.41.0; admission
178
+ remains Herdr >= 0.9.0 and the one-build/one-pane/Linux shape is unchanged. From the
179
+ `v2026.10.20-herdr.0.40.0` snapshot to `v0.41.0`, the changes under `plugins/herdr/`,
180
+ `scripts/herdr-runtime.mjs` and `scripts/fixtures/herdr-supply.json` were that lock's return
181
+ to `herdr-checkout` and the plugin README's resume/retire wording; the runtime helper and the
182
+ Herdr supply pin did not change.
183
+ - **What the acquired runtime now carries.** Core 0.41.0's lifecycle verbs ride in the runtime's
184
+ bytes: its installed bridge lists nine tools, `entwurf_retire_call` among them. Inside Herdr,
185
+ `entwurf_resume_call` still refuses as `herdr-resume-unsupported`. This snapshot measures no
186
+ retirement or resume through Herdr.
187
+ - **Pi range unchanged.** The acquired runtime still accepts Pi `>=1.3.0 <1.4`; core 0.41.0
188
+ keeps the Pi 1.3.0 SDK set.
189
+ - **Upgrade by acquisition source, not just version.** A settled npm-source 0.40.0 installation
190
+ can rebind under the plugin README's ownership conditions. A host that installed an
191
+ intervening `herdr-checkout` candidate must explicitly deactivate by its own absolute runtime
192
+ path before reinstalling the npm-source snapshot. No operator installation or active session
193
+ is rewired by this release work.
194
+ - **README.** The first-user-path receipt description no longer says the installed
195
+ `check-bridge` lists an eight-verb set; it names the verb set the installed bridge declares,
196
+ nine since 0.41.0.
197
+ - **Raw-install smoke scaffolding follows the Claude Code floor.** The first public-ref run of
198
+ `smoke-herdr-raw-install-live` at `cfdae0e` built its container with Claude Code 2.1.278, a
199
+ hand-kept pin from 2026-09-19, and the acquired 0.41.0 runtime refused to wire Claude below its
200
+ supported floor `>= 2.1.306` — the product's intended refusal, not a runtime defect. The smoke now
201
+ derives its exact Claude Code version from `package.json` `entwurf.claudeCodeFloor` instead of
202
+ retyping it. The product floor, the Pi, Herdr and Node pins and the smoke's end-state oracle are
203
+ unchanged.
204
+
205
+ ### Source-specific verification
206
+
207
+ - Registry metadata reports `latest` 0.41.0 and `repair` 0.12.8-repair.1, 642 files, published
208
+ 2026-10-22T04:37:02.853Z. The downloaded registry tarball is byte-equal to the preserved
209
+ accepted core candidate (SHA256 `2bd03ba900324a7c0882c2738c29593df0a1e4829fb3ae18d22be62186cf67ad`,
210
+ 11399545 bytes); its independently computed sha512 and sha1 equal `dist.integrity` and
211
+ `dist.shasum`, so core make's Docker consumer of those bytes (`node:24-bookworm`, repoDigest
212
+ `node@sha256:6dac556d…`) is reused here, not repeated. A sandboxed `pi install` of the
213
+ registry package resolved its bridge inside the sandbox, recorded the registry URL with that
214
+ integrity, and listed the curated Claude anchors with `--list-models entwurf`; a sandboxed
215
+ global npm install of the same package passed its installed `check-bridge` with nine tools.
216
+ No model turn. The accepted run stripped every `PI_`, `ENTWURF_`, `CLAUDE`, `CODEX_HOME`,
217
+ `TMUX` and `HERDR_` carrier and ran on the branch worktree, whose only checkout input (the
218
+ bridge resolver script) is unchanged; an earlier run from the clean tag HEAD left `TMUX` and
219
+ non-identity `PI_*` variables set and is kept only as a preliminary record.
220
+ - **The npm runtime swap against the real registry**, by the registry-shipped
221
+ `scripts/herdr-runtime.mjs` (byte-equal to this checkout's) in sandboxed user roots, 22/22
222
+ checks. The 0.40.0 runtime was seeded from its own committed lock (`036e268`); the swap to
223
+ 0.41.0 took one acquisition, the cached tarball's sha512 equals the lock and its sha256 equals
224
+ both the ready journal's observed digest and the accepted candidate. The `installing` journal
225
+ carried the 0.40.0 identity as `previousRuntime` and the `runtime-ready` journal carries
226
+ `null`; when acquisition began the 0.40.0 tree was still at the active address. A manually
227
+ seeded torn swap (the active tree moved to the backup address with a half candidate in
228
+ staging; not a killed process) was restored before acquisition and reported `recovered:
229
+ torn-swap-with-candidate`. An exact re-request acquired nothing and left journal and tree
230
+ byte-identical, and the owned removal reclaimed cache, runtime and journal. The helper is a
231
+ leaf that probes no Pi, so these cells certify neither a Pi upgrade nor Herdr's
232
+ install/update relocation, harness wiring, the installed CLI's deactivate path, a model turn,
233
+ or recovery from a real kill.
234
+ - **Focused gates on the branch**, with HEAD, index and work surface unchanged across them:
235
+ `check-herdr-supply` (11 assertions), `check-herdr-plugin` (49),
236
+ `check-herdr-plugin-profile` (14), `check-herdr-runtime-bootstrap` (31),
237
+ `check-herdr-activation` (33), `check-herdr-placement` (25), `check-herdr-fresh-call` (45),
238
+ `check-herdr-plugin-build` (21, its real-binary cell on herdr 0.9.5), `check-herdr-sandbox`
239
+ (12, a private herdr 0.9.5 server), `check-dep-versions`, `check-install-surface` and
240
+ `check-gate-manifests` (1173 committed mutants across 85 lanes validated, none executed).
241
+ - Pending, and not claimed here: the pushed SHA's four-job CI and the public-ref
242
+ install/use/deactivate journey of this npm pin, on Herdr 0.9.5 (the binary
243
+ `scripts/fixtures/herdr-supply.json` names) with Pi 1.3.0, from the public `main` ref rather
244
+ than the tag. Earlier npm or checkout-carrier receipts cannot certify these source-specific
245
+ axes. Those outcomes are recorded in the snapshot's post-push acceptance ledger, not in this
246
+ frozen text.
247
+
7
248
  ## 0.41.0 - 2026-10-22
8
249
 
9
250
  A sibling's garden id is its birthday, and this cut lets it rest and come back under it. A
package/README.md CHANGED
@@ -27,7 +27,7 @@ Git branches, worktrees, file isolation and internal delegation remain their con
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
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 (new in 0.41.0) | `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
+ | **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 |
31
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 |
32
32
 
33
33
  Antigravity is message-only: an existing conversation registers itself, and Entwurf never
@@ -54,15 +54,26 @@ ownership of the harness.
54
54
 
55
55
  Prerequisites: **Node >=24.0.0**, Python 3 for `setup`, and any harnesses you choose
56
56
  to use, already installed and authenticated. Pi is optional-by-presence, supported
57
- range `>=1.3.0 <1.4`; Claude Code supported floor `>=2.1.306` is required for its
57
+ range `>=1.4.0 <1.5`; Claude Code supported floor `>=2.1.306` is required for its
58
58
  managed exec-hook lifecycle. pnpm is needed only for source-checkout setup.
59
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
+
60
67
  ```bash
61
- npm install -g @junghanacs/entwurf
68
+ npm install -g @junghanacs/entwurf@0.41.0
62
69
  entwurf setup /path/to/your-project
63
70
  entwurf check-bridge
64
71
  ```
65
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
+
66
77
  `setup` composes each harness it finds and reports PASS / SKIP / FAIL. An absent
67
78
  harness is an explicit SKIP; a detected incomplete/below-floor integration is a
68
79
  named FAIL with nonzero exit, not a false green. `check-bridge` proves the installed
@@ -76,27 +87,18 @@ consent: [the operator steps](./docs/setup-clean-host.md#three-operator-decision
76
87
  Plain MCP registration alone does not make a garden citizen.
77
88
 
78
89
  **Platform bounds:** Linux is the certified harness-rail axis. macOS Entwurf-only
79
- installation is **CERTIFIED (CI)**, weaker than a physical-host doctor; its native
80
- harness rails, ACP and mux remain **NOT CERTIFIED — pending physical host**.
81
- Detected harness wiring on Darwin can therefore be written while setup stays
82
- non-green. WSL2: a CI runner (experimental) measured the Entwurf-only install and a
83
- zero-token Pi citizen; general, physical-machine or harness-parity WSL2 support is **not
84
- declared**, and delivery, other harnesses and physical machines are unmeasured. Native
85
- Windows has an **experimental first preview**, a CI-built x64 addon that ships in core
86
- 0.39.0 (published to npm); 0.38.1 and earlier carry no Windows addon. On a GitHub Actions
87
- `windows-2025` x64 runner with Node 24.18.1 and Pi 1.3.0, a CI-built artifact was installed,
88
- wired with `pi install`, birthed two Pi citizens and
89
- delivered one garden-id `entwurf_v2` message between them, with an offline scripted model
90
- and no vendor turn. Every other native-Windows surface (other harnesses, `setup` and the
91
- `.sh` bins, fresh/resume, mux/Herdr/ACP, physical machines) is **not declared**; the
92
- measured scope and its named gaps are in [BASELINE](./BASELINE.md#release-host-baseline).
93
- The package has no npm `os` restriction; that is not certification of every platform's
94
- 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.
95
97
 
96
98
  ### Native durable (pi-durable)
97
99
 
98
- **The package supplies Entwurf's owned app/TUI carrier and nine exact Pi 1.3.0 SDK
99
- 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.
100
102
  The launch verifies carrier integrity and every reachable SDK edge's name/version;
101
103
  incomplete, drifted or mixed supply refuses by name.
102
104
 
@@ -106,7 +108,7 @@ entwurf pi-durable --continue
106
108
  entwurf pi-durable --resume
107
109
  ```
108
110
 
109
- `--continue` opens this directory's newest durable session. `--resume` lists its
111
+ `--continue` opens this directory's latest-created durable session. `--resume` lists its
110
112
  sessions, most recently written first, by creation time, garden id and last write, and
111
113
  reopens the one you choose as it was saved; a cancelled choice opens nothing.
112
114
 
@@ -117,20 +119,23 @@ SQLite, transcripts and recovery remain the upstream SDK's:
117
119
 
118
120
  Optional `--native-module <absolute file>` initializes trusted ESM before runtime/TUI
119
121
  imports, then installs its default native Extension after the contact.
120
- `--native-module-dir <absolute directory>` (#134) loads the top-level `*.extension.mjs` files of that
121
- one named directory in byte-wise order instead, and the durable TUI keeps a live
122
- 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
123
124
  classic Pi API shim; guards are not a sandbox or rollback.
124
- Ordinary Pi's known `peerDependencies` warning reflects the separate native SDK supply.
125
125
  [Native setup](./docs/setup-clean-host.md#2b-optional-native-durable-pi-durable)
126
126
  owns flags, refusals and the extension contract.
127
127
 
128
128
  ### Herdr workbench
129
129
 
130
130
  ```bash
131
- herdr plugin install junghan0611/entwurf/plugins/herdr --yes
131
+ herdr plugin install junghan0611/entwurf/plugins/herdr --ref "$CANDIDATE_REF" --yes
132
132
  ```
133
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
+
134
139
  This separate path activates Herdr-integrated **pi and Claude Code** with an
135
140
  independently locked runtime and an observation pane; it puts no Entwurf bins on PATH.
136
141
  An Entwurf release does not automatically re-pin that runtime.
@@ -169,7 +174,7 @@ In that **harness session**, ask the agent to:
169
174
  ```
170
175
 
171
176
  These are tools, not shell subcommands. Pi calls
172
- `mcp__entwurf_bridge__<verb>` through Pi's built-in MCP. The 0.41.0 bridge exposes **nine**
177
+ `mcp__entwurf_bridge__<verb>` through Pi's built-in MCP. The bridge exposes **nine**
173
178
  verbs; a pi citizen has **seven active + two hidden** (`entwurf_inbox_read` and
174
179
  `entwurf_register_native`), since it has no mailbox and its own extension births it.
175
180
  Those counts describe the source; a running host offers what its already-loaded bridge
@@ -199,9 +204,15 @@ pi / pi-durable / Claude Code / Copilot / OMP / Antigravity / Codex
199
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 |
200
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 |
201
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 |
202
- | Attempt retirement of one explicitly requested Pi or Claude Code target (new in 0.41.0) | `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 |
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 |
203
208
 
204
- **Retirement is never autonomous.** Invoke it only for a target/action the human operator explicitly requested; development, coordinator status and caller identity are not standing permission. Where a host provides or is configured with an approval step, that step applies as well; Pi and pi-durable 1.3.0 have no built-in approval prompt by default. The configured Claude ACP child is likewise [set to run without per-call permission prompts](./docs/acp-backend-rail.md#configured-claude-acp-permissions); ACP retirement remains unmeasured. Tool annotations and the caller's identity are hints and attribution, not permission. No roster expansion, timer or automatic retry of an unknown/cancelled/lost result. The retirement path records requested/outcome events under a target-gid sidecar; absence is unknown, not proof of no retirement. Source, compiled fixture, Native/LIVE, rollout and release acceptance remain separate: one dated test-owned [Native round trip per backend](./BASELINE.md#147-native-round-trip-2026-10-22-n1) is recorded, which is neither a release gate nor a rollout claim.
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.
205
216
 
206
217
  **Delivery starts no process.** A live pi receives over its record-addressed control
207
218
  address (a Unix socket; on the experimental native-Windows Pi preview, a named pipe);
@@ -220,32 +231,18 @@ is a live rail fact, not merely a consequence of trusted identity.
220
231
 
221
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.
222
233
 
223
- **Mux launches; it never delivers.** Inside Herdr, fresh opens a new unfocused tab
224
- in the caller's workspace, pi/Claude Code only, with no tmux fallback. Outside,
225
- it opens pi/pi-durable/Claude Code/Copilot/OMP/Codex visibly on the caller's own tmux
226
- server. pi-durable runs the installed carrier on its exact SDK set described above and
227
- 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
228
- absolute directory, omitted or empty means the caller's cwd. Optional `placement.tmuxSession` names an existing
229
- tmux session (not permitted inside Herdr), never a new session or a cwd lookup.
230
- Omitted tmux placement follows the caller: Codex beside its own TUI pane matched
231
- by thread-id, everyone else in their own session. Missing/ambiguous context refuses.
232
- A launch receipt is neither the callback address nor proof of task processing.
233
- Resume is pi or Claude Code, tmux-only, same id, with the record as the sole address authority.
234
- For Pi, the record selects the absolute transcript and its native id must match; the transcript's
235
- session-header cwd and first `model_change` provider/model feed the launch resolver, without a
236
- record/host cwd fallback. Claude Code uses `claude --resume <record session id>` at the record cwd,
237
- without restoring model or permission flags;
238
- a live, unprovably dormant, unbound or other-backend target refuses, and an unobserved window
239
- stays visible without retry. Claude Code's [V3 arm binding](./docs/adding-a-harness.md#arm-binding-within-v3)
240
- is a record–receiver witness, not a new identity or an occupancy/permission token.
241
- Claude Code dormancy is read only from attaches the Entwurf hook committed: a process that
242
- holds the session without one (hooks disabled, a lock timeout or store failure at its start)
243
- is invisible to that check.
244
- Claude Code same-id resume is new in 0.41.0. One dated test-owned
245
- [Native round trip](./BASELINE.md#147-native-round-trip-2026-10-22-n1) was measured; its
246
- release acceptance remains separate from the shipped Pi resume history.
247
- Exact boundaries: [tmux launch](https://github.com/junghan0611/entwurf/blob/main/docs/mux-launch-rail.md)
248
- 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).
249
246
 
250
247
  ## Concepts
251
248
 
package/VERIFY.md CHANGED
@@ -374,7 +374,7 @@ The goal is not merely "invoke Claude Code." We want:
374
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.
375
375
 
376
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`)
377
- 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
378
378
  3. Claude meta-bridge global plugin — only when `claude` is on PATH; otherwise skipped cleanly
379
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
380
380
  5. agy bridge + exact permission + statusline + `PreInvocation` hook — only when `agy` is on PATH; each adapter is idempotent and independently doctorable
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,7 +135,7 @@ 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 |
@@ -23,14 +23,18 @@ evidence split, and its three states must not collapse into "macOS is supported"
23
23
 
24
24
  ## Requirements
25
25
 
26
+ This table is this source candidate's, which moves the Pi SDK set to 1.4.0 and is **not published
27
+ to npm yet**. The newest published package, `@junghanacs/entwurf@0.41.0`, ships the Pi 1.3.0 SDK
28
+ set and requires Pi 1.3.x; the setup guide inside that tarball is its contract.
29
+
26
30
  | Component | Requirement | Needed for |
27
31
  |---|---|---|
28
32
  | Node | **`>=24.0.0`** | package and bridge runtime |
29
33
  | npm/pnpm | npm is bundled with Node; pnpm is required for source setup | package or source installation |
30
34
  | Python 3 | required by `setup`/`install` (project-path normalization + settings writers); `--help`/`check-bridge` stay Python-free | pi/Claude/agy/Copilot/Codex wiring writers |
31
35
  | entwurf | global/project-local `@junghanacs/entwurf`, or a source checkout | operator command and garden capability |
32
- | pi | optional-by-presence, `>=1.3.0 <1.4` — absent is an explicit setup SKIP, below-floor is a named FAIL | ACP provider, control sockets |
33
- | Native durable (`pi-durable`) | supplied by the Entwurf package (#130): Entwurf's owned app carrier plus the exact 1.3.0 pi SDK set as production dependencies — setup PASS when they verify, a named FAIL otherwise (never SKIP) | the `entwurf pi-durable` contact; native birth/LIVE on this supply surface is a separate release proof (§2b) |
36
+ | pi | optional-by-presence, `>=1.4.0 <1.5` — absent is an explicit setup SKIP, below-floor is a named FAIL | ACP provider, control sockets |
37
+ | Native durable (`pi-durable`) | supplied by the Entwurf package (#130): Entwurf's owned app carrier plus the exact 1.4.0 pi SDK set as production dependencies — setup PASS when they verify, a named FAIL otherwise (never SKIP) | the `entwurf pi-durable` contact; native birth/LIVE on this supply surface is a separate release proof (§2b) |
34
38
  | Claude Code | optional, **`>=2.1.306`** — the exec-form hook floor | Claude ACP auth/runtime and mailbox-backed native citizen |
35
39
  | GitHub Copilot CLI | optional-by-presence, operator-installed and authenticated — absent is an explicit setup SKIP; detected composes all four units (birth/MCP/receiver/footer) | self-fetch citizen and visible fresh |
36
40
  | OMP (`omp`) | optional-by-presence, operator-installed — absent is an explicit setup SKIP; detected composes all four units (birth/MCP/`tools.xdev` setting/receiver) | self-fetch citizen and visible fresh (accepted on one host — see §4b) |
@@ -79,11 +83,15 @@ node --version
79
83
  npm --version
80
84
  ```
81
85
 
86
+ The two npm forms below install the **published** package, `0.41.0` (Pi 1.3.x, the Pi 1.3.0 SDK
87
+ set), not this candidate. This candidate (Pi 1.4) installs only from a source checkout of its
88
+ commit, further down this section; it has no npm install command until it is released.
89
+
82
90
  Global install is simplest when native harnesses should find stable bins from every
83
91
  working directory:
84
92
 
85
93
  ```bash
86
- npm install -g @junghanacs/entwurf
94
+ npm install -g @junghanacs/entwurf@0.41.0
87
95
  entwurf --help
88
96
  entwurf setup /path/to/your-project
89
97
  entwurf check-bridge
@@ -94,7 +102,7 @@ A project-local installation is also supported:
94
102
  ```bash
95
103
  mkdir -p ~/entwurf-smoke && cd ~/entwurf-smoke
96
104
  npm init -y
97
- npm install --save-dev @junghanacs/entwurf
105
+ npm install --save-dev @junghanacs/entwurf@0.41.0
98
106
  npx entwurf setup .
99
107
  npx entwurf check-bridge
100
108
  ```
@@ -119,9 +127,15 @@ with a nonzero setup result):
119
127
  ```bash
120
128
  git clone https://github.com/junghan0611/entwurf ~/repos/gh/entwurf
121
129
  cd ~/repos/gh/entwurf
130
+ git checkout "$CANDIDATE_REF" # set it first to the commit you mean to run; a bare clone gives the default branch
122
131
  ./run.sh setup /path/to/consumer-project
123
132
  ```
124
133
 
134
+ Set `CANDIDATE_REF` explicitly to a published branch or tag that carries the commit you mean;
135
+ a bare clone selects the default branch, not a promised candidate. A validation branch is not a
136
+ release acceptance. A checkout that already holds the intended commit skips the clone and the
137
+ `git checkout`.
138
+
125
139
  This owns `~/.local/bin/entwurf` as a symlink to that checkout's `run.sh` and fails if the
126
140
  link is foreign, outside PATH, or shadowed by another command. It detects and wires
127
141
  pi/Claude/agy/Copilot/OMP/Codex by presence and prints a computed per-component
@@ -207,18 +221,21 @@ physical-Darwin receipt. `entwurf install` may write `.pi/settings.json`
207
221
  on Darwin; that is wiring, not a rail receipt. If `pi` is on PATH,
208
222
  `setup` names the pi row FAIL with the §1 Darwin wording.
209
223
 
210
- Install the exact release floor, then wire the project:
224
+ Install this candidate's exact Pi floor, then wire the project from the candidate's source
225
+ checkout (§1; `entwurf` is the link `./run.sh setup` made to it). The published 0.41.0 instead
226
+ takes the Pi 1.3.x its own setup guide names and the global package root,
227
+ `$(npm root -g)/@junghanacs/entwurf`, in place of the checkout:
211
228
 
212
229
  ```bash
213
- npm install -g @earendil-works/pi-coding-agent@1.3.0
230
+ npm install -g @earendil-works/pi-coding-agent@1.4.0
214
231
  pi --version
215
232
 
216
233
  cd ~/entwurf-smoke
217
234
  entwurf install .
218
- pi -e "$(npm root -g)/@junghanacs/entwurf" --list-models entwurf
235
+ pi -e ~/repos/gh/entwurf --list-models entwurf
219
236
  ```
220
237
 
221
- The supported range is `>=1.3.0 <1.4`. It is a hard minimum: installing this
238
+ The supported range is `>=1.4.0 <1.5`. It is a hard minimum: installing this
222
239
  release onto a 0.83.x pi host upgrades the runtime rather than keeping the older
223
240
  minor. A host using only the external MCP bridge can skip pi until it needs a
224
241
  control socket; no delivery rail launches a pi process.
@@ -322,7 +339,7 @@ by `entwurf pi` or by registering ordinary Pi's adapter.
322
339
  What the Entwurf package supplies:
323
340
 
324
341
  1. **The pi SDK set** — `@earendil-works/{pi-coding-agent, pi-ai, pi-tui, pi-agent-core,
325
- pi-durable, chord, pi-mcp, pi-codemode, pi-telemetry}` at exactly `1.3.0`, plus
342
+ pi-durable, chord, pi-mcp, pi-codemode, pi-telemetry}` at exactly `1.4.0`, plus
326
343
  `proper-lockfile`, as Entwurf's own production dependencies (declared once in
327
344
  `pi/pi-durable/overlay/upstream-pin.json` `sdk`). Model data rides the published
328
345
  `pi-ai`; there is no catalog copy.
@@ -335,7 +352,7 @@ What the Entwurf package supplies:
335
352
  (`pi/pi-durable/carrier-resolver.mjs`), which maps them to the published package's
336
353
  own `dist/` as installed — never through a source checkout.
337
354
 
338
- The upstream [library guide](https://github.com/earendil-works/pi/blob/v1.3.0/packages/durable/README.md)
355
+ The upstream [library guide](https://github.com/earendil-works/pi/blob/v1.4.0/packages/durable/README.md)
339
356
  and the [experimental app guide](https://github.com/earendil-works/pi/blob/v1.1.0/packages/coding-agent/src/experimental/durable/README.md)
340
357
  (last published at v1.1.0) describe what the carrier is. Running the bare upstream app does not attach Entwurf's
341
358
  garden contact. Entwurf does not rebuild the controller/model/auth/storage system or
@@ -420,7 +437,7 @@ tree (for example an override that binds pi-durable's `pi-ai` to an older publis
420
437
  manifest field; it is a known constraint of supplying both from one package, accepted for 0.32,
421
438
  not a claim that every load path is harmless.
422
439
 
423
- The ordinary Pi CLI on PATH is a separate subject (§2, range `>=1.3.0 <1.4`): a host Pi is
440
+ The ordinary Pi CLI on PATH is a separate subject (§2, range `>=1.4.0 <1.5`): a host Pi is
424
441
  not needed to run pi-durable, and a host inside the range need not be the same copy as the
425
442
  carrier's. Native Pi owns credentials, model settings and durable SQLite sessions; keep them
426
443
  in place rather than copying credentials or migrating ordinary Pi transcripts. Native
@@ -431,6 +448,29 @@ to the app's own model resolution; the checkout contact/send/receive gates run t
431
448
  with scripted S plus native H; Pi ↔ pi-durable LIVE on this supply surface is a separate release
432
449
  proof ([#130](https://github.com/junghan0611/entwurf/issues/130)).
433
450
 
451
+ ### Moving durable sessions from the Pi 1.3 SDK to 1.4
452
+
453
+ This candidate is unpublished, and moving a live host onto it is a separate operator decision. When
454
+ you do:
455
+
456
+ 1. **Confirm the old owner is gone.** Close the 0.41 (SDK 1.3) durable session normally and check
457
+ that its process has exited. A stale session lock alone is not proof.
458
+ 2. **Back up first.** Before the first 1.4 open, copy the target session directory whole
459
+ (`session.sqlite` with any `-wal`/`-shm`) as one consistent set.
460
+ 3. **Choose the session by name.** Reopen with the `--resume` picker and pick the entry you mean;
461
+ do not use `--continue` on a host with more than one session.
462
+ 4. **Package downgrade is not data rollback.** The first 1.4 open migrates SQLite storage to
463
+ schema 2, which SDK 1.3 (Entwurf 0.41) refuses to open. Reinstalling the old package does not
464
+ undo that. Restoring the backup does, and work done after the 1.4 open is not in the backup.
465
+ 5. **Inspect parked work before retrying.** On its first open, 1.4 counts a task that SDK 1.3 left
466
+ running as one restart, and after repeated cut-offs it parks the task (`restart_limit`). Read
467
+ what the task already did before you resend anything: recovery of a parked model turn is not
468
+ measured, and its side effects or outcome may be unknown. The existing conversation abort is
469
+ cancellation, not retry; it need not affect background tasks.
470
+
471
+ The measurements behind these points, and what they do not cover, are in the
472
+ [#148 checkpoint](https://github.com/junghan0611/entwurf/issues/148#issuecomment-6339048361).
473
+
434
474
  ### Maintainer: re-emitting the carrier
435
475
 
436
476
  Only a maintainer changing the pi-durable app runs `scripts/emit-pi-durable-carrier.ts`. Its
@@ -647,7 +687,7 @@ as a note rather than a fault.
647
687
 
648
688
  **entwurf sets no OMP version floor in code, and will not grow one on schedule.** Detection is
649
689
  presence-only (`command -v "${OMP_BIN:-omp}"`); there is no `entwurf.ompFloor`, no coherence
650
- gate, and no exact pin — unlike Node (`engines.node`), pi (`>=1.3.0 <1.4`) and Claude Code
690
+ gate, and no exact pin — unlike Node (`engines.node`), pi (`>=1.4.0 <1.5`) and Claude Code
651
691
  (`entwurf.claudeCodeFloor`), each of which has an enforcement point. That asymmetry is a
652
692
  decision, not an omission. A floor is the answer to a vendor that fails SILENTLY — Claude Code
653
693
  earned one because an older binary validates the exec manifest, drops `args` at runtime, and
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@junghanacs/entwurf",
3
- "version": "0.41.0",
3
+ "version": "0.41.1",
4
4
  "description": "Visible sibling sessions across independent agent harnesses: launch in Herdr or tmux, retire on request and reopen pi or Claude Code in tmux, and deliver messages by garden id; native bridges and Claude/Cortex ACP adapters.",
5
5
  "license": "MIT",
6
6
  "author": {
@@ -89,7 +89,8 @@
89
89
  "CHANGELOG.md",
90
90
  "!**/__pycache__",
91
91
  "!**/*.pyc",
92
- "!pi-extensions/**/*.test.ts"
92
+ "!pi-extensions/**/*.test.ts",
93
+ "!pi-extensions/**/fixtures/**"
93
94
  ],
94
95
  "engines": {
95
96
  "node": ">=24.0.0"
@@ -101,15 +102,15 @@
101
102
  "@agentclientprotocol/claude-agent-acp": "0.90.0",
102
103
  "@agentclientprotocol/sdk": "1.8.0",
103
104
  "@anthropic-ai/sdk": "0.100.1",
104
- "@earendil-works/chord": "1.3.0",
105
- "@earendil-works/pi-agent-core": "1.3.0",
106
- "@earendil-works/pi-ai": "1.3.0",
107
- "@earendil-works/pi-codemode": "1.3.0",
108
- "@earendil-works/pi-coding-agent": "1.3.0",
109
- "@earendil-works/pi-durable": "1.3.0",
110
- "@earendil-works/pi-mcp": "1.3.0",
111
- "@earendil-works/pi-telemetry": "1.3.0",
112
- "@earendil-works/pi-tui": "1.3.0",
105
+ "@earendil-works/chord": "1.4.0",
106
+ "@earendil-works/pi-agent-core": "1.4.0",
107
+ "@earendil-works/pi-ai": "1.4.0",
108
+ "@earendil-works/pi-codemode": "1.4.0",
109
+ "@earendil-works/pi-coding-agent": "1.4.0",
110
+ "@earendil-works/pi-durable": "1.4.0",
111
+ "@earendil-works/pi-mcp": "1.4.0",
112
+ "@earendil-works/pi-telemetry": "1.4.0",
113
+ "@earendil-works/pi-tui": "1.4.0",
113
114
  "@modelcontextprotocol/sdk": "^1.12.1",
114
115
  "proper-lockfile": "4.1.2",
115
116
  "smol-toml": "^1.8.0",
@@ -219,6 +219,7 @@ export class TaskBadge {
219
219
  ...tally("aborting", (node) => node.abortRequested),
220
220
  ...tally("completing", (node) => !node.abortRequested && node.state.status === "completing"),
221
221
  ...tally("waiting", (node) => !node.abortRequested && node.state.status === "waiting"),
222
+ ...tally("sleeping", (node) => !node.abortRequested && node.state.status === "sleeping"),
222
223
  ...tally("pending", (node) => !node.abortRequested && node.state.status === "pending"),
223
224
  ].join(", ");
224
225
  }
@@ -621,19 +622,33 @@ class DurableTui {
621
622
  }
622
623
  }
623
624
 
624
- function describeTask(node ) {
625
+ /** A task's line in the tasks panel: a sleeping task says when it wakes, a blocked one why it cannot run. */
626
+ export function describeTask(node ) {
625
627
  const state = node.state;
626
628
  const status =
627
629
  state.status === "waiting"
628
630
  ? `waiting on ${state.on.join(", ")}`
629
631
  : state.status === "completing"
630
632
  ? `completing (${state.outcome})`
631
- : `${state.status} ${state.phase}`;
632
- const flags = [node.background ? "background" : "", node.abortRequested ? "aborting" : ""].filter(Boolean);
633
+ : state.status === "sleeping"
634
+ ? `sleeping ${state.phase} until ${localTime(state.until)}`
635
+ : `${state.status} ${state.phase}`;
636
+ const flags = [
637
+ node.background ? "background" : "",
638
+ node.abortRequested ? "aborting" : "",
639
+ node.blocked !== undefined ? `blocked:${node.blocked}` : "",
640
+ ].filter(Boolean);
633
641
  const owned = node.conversations.length > 0 ? ` owns conversation ${node.conversations.join(", ")}` : "";
634
642
  return `${node.kind} #${node.id}: ${status}${flags.length > 0 ? ` [${flags.join(", ")}]` : ""}${owned}`;
635
643
  }
636
644
 
645
+ /** Epoch milliseconds as local wall-clock time, `YYYY-MM-DD HH:MM:SS`. */
646
+ function localTime(epoch ) {
647
+ const at = new Date(epoch);
648
+ const pad = (n ) => String(n).padStart(2, "0");
649
+ return `${at.getFullYear()}-${pad(at.getMonth() + 1)}-${pad(at.getDate())} ${pad(at.getHours())}:${pad(at.getMinutes())}:${pad(at.getSeconds())}`;
650
+ }
651
+
637
652
  function userText(content ) {
638
653
  if (typeof content === "string") return content;
639
654
  return content
@@ -76,8 +76,8 @@
76
76
  {
77
77
  "name": "tui.js",
78
78
  "source": "pi/pi-durable/src/experimental/durable/tui.ts",
79
- "blob": "eb5e2ec4e98919cd3ff4f81f166eb52afe6555af",
80
- "sha256": "a1b0d9d9358c5b54ac0974de84068b7792669171917ebd7b7351374ed625c9c5"
79
+ "blob": "405600f6ba1558bf6bf22d144afdcf3f1295c282",
80
+ "sha256": "d4bf4e2861bde917e1dcdea6f063bdc3e101ab500107a9c5fab0e3ce97fb2c4a"
81
81
  },
82
82
  {
83
83
  "name": "harness-setup.js",
@@ -132,17 +132,17 @@
132
132
  ]
133
133
  },
134
134
  "sdk": {
135
- "comment": "The published library set the carrier runs against, derived from the 1.3.0 declarations. members: the @earendil-works packages the carrier imports and every @earendil-works package those declare. direct: a third-party package a carrier file imports itself (sessions.ts: proper-lockfile, at the version pi-coding-agent 1.3.0 pins). Each is an exact production dependency in package.json. Since 1.3.0 the members' own edges are exact too (upstream #10865), and the installed physical edges are still what must equal these versions.",
135
+ "comment": "The published library set the carrier runs against, derived from the 1.4.0 declarations. members: the @earendil-works packages the carrier imports and every @earendil-works package those declare. direct: a third-party package a carrier file imports itself (sessions.ts: proper-lockfile, at the version pi-coding-agent 1.4.0 pins). Each is an exact production dependency in package.json. Since 1.3.0 the members' own edges are exact too (upstream #10865), and the installed physical edges are still what must equal these versions.",
136
136
  "members": {
137
- "@earendil-works/chord": "1.3.0",
138
- "@earendil-works/pi-agent-core": "1.3.0",
139
- "@earendil-works/pi-ai": "1.3.0",
140
- "@earendil-works/pi-codemode": "1.3.0",
141
- "@earendil-works/pi-coding-agent": "1.3.0",
142
- "@earendil-works/pi-durable": "1.3.0",
143
- "@earendil-works/pi-mcp": "1.3.0",
144
- "@earendil-works/pi-telemetry": "1.3.0",
145
- "@earendil-works/pi-tui": "1.3.0"
137
+ "@earendil-works/chord": "1.4.0",
138
+ "@earendil-works/pi-agent-core": "1.4.0",
139
+ "@earendil-works/pi-ai": "1.4.0",
140
+ "@earendil-works/pi-codemode": "1.4.0",
141
+ "@earendil-works/pi-coding-agent": "1.4.0",
142
+ "@earendil-works/pi-durable": "1.4.0",
143
+ "@earendil-works/pi-mcp": "1.4.0",
144
+ "@earendil-works/pi-telemetry": "1.4.0",
145
+ "@earendil-works/pi-tui": "1.4.0"
146
146
  },
147
147
  "direct": {
148
148
  "proper-lockfile": "4.1.2"
@@ -239,9 +239,11 @@ export interface AcpBackendAdapter {
239
239
 
240
240
  /** OPTIONAL capability (#127): recognise a typed vendor rejection of `session/prompt`
241
241
  * whose raw text must not become pi's `errorMessage`. pi decides a whole-prompt replay
242
- * from `errorMessage` alone, and a vendor's "Internal error" — or any digit run in the
243
- * ids it carries — reads as transient, so a turn whose tools already ran would be resent
244
- * from a cold session. A recognised rejection returns a fixed, authored verdict for
242
+ * from `errorMessage` alone, and a vendor's "Internal error" reads as transient
243
+ * (pi-ai's `internal.?error` term), so a turn whose tools already ran would be resent
244
+ * from a cold session. Through pi-ai 1.3 a digit run inside the ids it carries (`…8503…`)
245
+ * read as an HTTP status too; pi-ai 1.4 bounds the status terms (upstream #11057), so
246
+ * the ids no longer trigger it — the "Internal error" text alone still does. A recognised rejection returns a fixed, authored verdict for
245
247
  * `errorMessage`; backend.ts keeps the raw rejection as a pi diagnostic instead.
246
248
  *
247
249
  * ABSENT means every failure of this backend keeps the verbatim-first `errorMessage`.
@@ -668,13 +668,15 @@ async function settleChildEnd(latch: ChildEndLatch, ms: number): Promise<void> {
668
668
  * may appear here, or a dead child would put a full cold prompt replay back on
669
669
  * the table — with the tool side effects this turn already produced.
670
670
  *
671
- * That pattern is a bare substring alternation (`new RegExp(terms.join("|"), "i")`
672
- * — NO word boundaries) and its terms enumerate the HTTP statuses "429", "500",
673
- * "502", "503", "504" and "524". So the settle bound is deliberately NOT interpolated: rendering
674
- * CHILD_END_SETTLE_MS made this line say "within 500ms", which pi read as an
675
- * HTTP 500 and classified as transient. Naming the window in prose keeps a later
676
- * change of that constant from silently re-arming the replay — do not "improve"
677
- * this by putting the number back; the bound belongs in the code and the gate.
671
+ * That pattern is a substring alternation (`new RegExp(terms.join("|"), "i")` — NO word
672
+ * boundaries on its word terms). Through pi-ai 1.3 its HTTP status terms ("429", "500",
673
+ * "502", "503", "504", "520", "524") were bare substrings too, and rendering
674
+ * CHILD_END_SETTLE_MS made this line say "within 500ms", which pi read as an HTTP 500 and
675
+ * classified as transient. pi-ai 1.4 bounds only those status terms (upstream #11057), so
676
+ * "500ms" no longer matches; the word terms above are still unbounded. The settle bound
677
+ * stays deliberately NOT interpolated: naming the window in prose keeps a later change of
678
+ * that constant, or of the classifier, from silently re-arming the replay — do not
679
+ * "improve" this by putting the number back; the bound belongs in the code and the gate.
678
680
  *
679
681
  * The three endings are kept DISTINCT on purpose (issue #72 Done-when): an
680
682
  * exit code, a signal, and "we waited and it never said" are three different
@@ -1468,8 +1470,9 @@ export function streamAcpTurn(
1468
1470
  // the turn-scoped full-transcript-replay cause.
1469
1471
  const hint = aborted ? undefined : actionableAcpBackendHint(full);
1470
1472
  // #127: pi's retry and overflow classifiers read `errorMessage` ALONE, and a
1471
- // vendor's "Internal error" or any digit run in its ids/stderr reads as transient
1472
- // — a replay of a turn whose tools already ran. A rejection the adapter names gets
1473
+ // vendor's "Internal error" reads as transient — a replay of a turn whose tools
1474
+ // already ran. (Through pi-ai 1.3 a digit run in its ids/stderr did too; 1.4 bounds
1475
+ // the status terms, upstream #11057.) A rejection the adapter names gets
1473
1476
  // its fixed verdict there; every raw part rides one diagnostic, appended after any
1474
1477
  // existing ones. An abort keeps the abort path.
1475
1478
  const rejection = aborted ? undefined : adapter?.readPromptRejection?.(err);
@@ -354,7 +354,7 @@ export interface PiDurableRoots {
354
354
  // ---------------------------------------------------------------------------
355
355
  // Durable surface, typed NARROWLY and locally — read at pi-durable 1.0.2 @cd32f77 (#129,
356
356
  // `harness/types.ts`; historical coordinates, not re-anchored here). The current supply is the
357
- // carrier emitted from Entwurf's owned source (pi/pi-durable/src, #141) against the 1.3.0 SDK set
357
+ // carrier emitted from Entwurf's owned source (pi/pi-durable/src, #141) against the 1.4.0 SDK set
358
358
  // `pi/pi-durable/overlay/upstream-pin.json` names (origin `abe508e`). `[read 2026-10-13]` at
359
359
  // v1.2.0 one shape typed below moved: `ToolExecutionResult.content` is now `output`, beside the new
360
360
  // `structuredOutput` (`harness/types.ts`, its CHANGELOG's breaking changes). An omitted `output`
package/run.sh CHANGED
@@ -4021,6 +4021,10 @@ check_pack() {
4021
4021
  # JS for installed entrypoints. The files array lists pi/pi-durable per FILE, so nothing admits these
4022
4022
  # today; this tripwire is what fails loud the day an allowlist entry would.
4023
4023
  local durable_source_pattern='^pi/pi-durable/(src/|tsconfig\.json$|overlay/runtime-contacts\.patch$)'
4024
+ # #148 S1b: the goldens and generator a beside test reads (pi-extensions/lib/fixtures/) are test input,
4025
+ # not product. `pi-extensions/` ships whole, so the `!pi-extensions/**/fixtures/**` negation is the only
4026
+ # thing keeping them out, and this is its tripwire (same 결합 규칙 as the `.test.ts` pattern above).
4027
+ local test_fixture_pattern='^pi-extensions/(.*/)?fixtures/'
4024
4028
 
4025
4029
  local pass=1 f pat hit
4026
4030
 
@@ -4047,6 +4051,13 @@ check_pack() {
4047
4051
  pass=0
4048
4052
  fi
4049
4053
 
4054
+ hit=$(grep -E "$test_fixture_pattern" <<<"$file_list" || true)
4055
+ if [ -n "$hit" ]; then
4056
+ fail "[check-pack] a beside test's fixtures (pi-extensions/**/fixtures/) ride the tarball — test input is not product; restore the files-array negation:"
4057
+ echo "$hit" | sed 's/^/ /' >&2
4058
+ pass=0
4059
+ fi
4060
+
4050
4061
  # #78 — a pack carries the Windows addon not at all, or as exactly a verified addon + manifest
4051
4062
  # pair (pack_win32_carrier_verdict, proven on owned fixtures by check-pack-win32-carrier).
4052
4063
  # "present" means the carrier verified, never that the build was approved for publication.
@@ -4157,7 +4168,7 @@ check_pack_win32_carrier() {
4157
4168
  # which is exactly what property (1)'s `(_|$)` absorbs; all three shapes are fixtures
4158
4169
  # below so a future suffix change cannot pass vacuously.)
4159
4170
  pack_install_leaked_pi() {
4160
- grep '^@earendil-works+' | grep -Ev '@1\.3\.0(_|$)' || true
4171
+ grep '^@earendil-works+' | grep -Ev '@1\.4\.0(_|$)' || true
4161
4172
  }
4162
4173
 
4163
4174
  # Matcher self-test on SYNTHETIC lookalikes: a healthy install tree cannot exercise either
@@ -4174,18 +4185,18 @@ check_pack_pin_matcher() {
4174
4185
  # Cell 1 — the version boundary. Every measured peer-hash shape is a fixture: the
4175
4186
  # 0.84.4-era `_@modelcontextprotocol+sdk@…`, the `_ws@8.21.3` observed on a real
4176
4187
  # 0.85.1 tree, and `[측정 2026-09-20]` the combined suffix a 0.86.0 install carries.
4177
- # None may leak; the two lookalikes must — a PREFIX-EXTENDED version (`0.86.0-beta.1`,
4188
+ # None may leak; the two lookalikes must — a PREFIX-EXTENDED version (`1.4.0-beta.1`,
4178
4189
  # the prerelease shape that an unanchored match would bless) and an off-pin version.
4179
4190
  matcher_probe=$(printf '%s\n' \
4180
- '@earendil-works+pi-ai@1.3.0' \
4181
- '@earendil-works+pi-ai@1.3.0_@modelcontextprotocol+sdk@1.29.0_zod@4.3.6' \
4182
- '@earendil-works+pi-ai@1.3.0_ws@8.21.3' \
4183
- '@earendil-works+pi-ai@1.3.0_@modelcontextprotocol+sdk@1.29.0_zod@4.3.6__ws@8.21.3_zod@4.3.6' \
4184
- '@earendil-works+pi-ai@1.3.0-beta.1' \
4191
+ '@earendil-works+pi-ai@1.4.0' \
4192
+ '@earendil-works+pi-ai@1.4.0_@modelcontextprotocol+sdk@1.29.0_zod@4.3.6' \
4193
+ '@earendil-works+pi-ai@1.4.0_ws@8.21.3' \
4194
+ '@earendil-works+pi-ai@1.4.0_@modelcontextprotocol+sdk@1.29.0_zod@4.3.6__ws@8.21.3_zod@4.3.6' \
4195
+ '@earendil-works+pi-ai@1.4.0-beta.1' \
4185
4196
  '@earendil-works+pi-agent-core@0.87.1' | pack_install_leaked_pi)
4186
- if [ "$matcher_probe" != '@earendil-works+pi-ai@1.3.0-beta.1
4197
+ if [ "$matcher_probe" != '@earendil-works+pi-ai@1.4.0-beta.1
4187
4198
  @earendil-works+pi-agent-core@0.87.1' ]; then
4188
- fail "[QK:PACK-INSTALL-PIN-MATCHER-BOUNDED] the pin-leak matcher must flag the prefix-extended 1.3.0-beta.1 and the off-pin 0.87.1 lookalikes, and pass 1.3.0 bare or with any measured peer-hash — got: ${matcher_probe:-<nothing leaked>}"
4199
+ fail "[QK:PACK-INSTALL-PIN-MATCHER-BOUNDED] the pin-leak matcher must flag the prefix-extended 1.4.0-beta.1 and the off-pin 0.87.1 lookalikes, and pass 1.4.0 bare or with any measured peer-hash — got: ${matcher_probe:-<nothing leaked>}"
4189
4200
  return 1
4190
4201
  fi
4191
4202
 
@@ -4193,9 +4204,9 @@ check_pack_pin_matcher() {
4193
4204
  # (0.85.0 onward, still true at 0.99.1). An off-pin chord MUST leak; the pinned one must not. A matcher narrowed
4194
4205
  # back to `^@earendil-works+pi-` sees nothing here and dies at this signature.
4195
4206
  matcher_probe=$(printf '%s\n' \
4196
- '@earendil-works+chord@1.3.0' \
4207
+ '@earendil-works+chord@1.4.0' \
4197
4208
  '@earendil-works+chord@0.87.1' \
4198
- '@earendil-works+pi-ai@1.3.0' | pack_install_leaked_pi)
4209
+ '@earendil-works+pi-ai@1.4.0' | pack_install_leaked_pi)
4199
4210
  if [ "$matcher_probe" != '@earendil-works+chord@0.87.1' ]; then
4200
4211
  fail "[QK:PACK-INSTALL-PIN-MATCHER-COVERS-CLOSURE] the pin-leak matcher must cover every @earendil-works closure member, not just the pi-* families — an off-pin @earendil-works/chord has to leak (it was a runtime dependency of pi-coding-agent, pi-agent-core, pi-client and pi-protocol — `[측정 2026-09-20]` at 0.86.0, re-measured `[측정 2026-09-22]` at 0.87.0, `[측정 2026-09-23]` at 0.87.1 and `[측정 2026-09-30]` at 0.99.1, where pi-coding-agent adds pi-codemode and pi-mcp beside them; `[측정 2026-10-13]` at 1.2.0 pi-durable alone declares it) — got: ${matcher_probe:-<nothing leaked>}"
4201
4212
  return 1
@@ -4209,7 +4220,7 @@ check_pack_pin_matcher() {
4209
4220
  # Cells: L1s (Entwurf alone, $3), L1 (project-local beside an aligned host pi-coding-agent at the
4210
4221
  # pinned SDK version), L2 (an isolated `npm -g --prefix`), each through (a) the INSTALLED verifier
4211
4222
  # (exit 0, prints its own carrier-resolver) and (b) an independent consumer oracle; then the L3
4212
- # NEGATIVE (an override binds pi-durable's pi-ai edge to an older published version: the installed verifier must
4223
+ # NEGATIVE (an override binds pi-durable's pi-codemode edge to an older published version: the installed verifier must
4213
4224
  # refuse it by name, while a bare import still succeeds); then (c) the public launch on L1s.
4214
4225
  # The oracle observes RESOLUTIONS (a resolve hook), not every filesystem read; it asserts each
4215
4226
  # resolved product file lies inside THIS consumer's node_modules — a sandbox ancestor is not the
@@ -4304,13 +4315,18 @@ ORACLE
4304
4315
  echo "[check-pack-install] pi-durable $cell: installed verifier PASS; consumer oracle $out"
4305
4316
  done
4306
4317
 
4307
- printf '%s\n' "{\"name\":\"pd-mismatch\",\"version\":\"1.0.0\",\"private\":true,\"overrides\":{\"@earendil-works/pi-durable\":{\"@earendil-works/pi-ai\":\"1.0.3\"}}}" > "$box/pd-mismatch/package.json"
4318
+ # The wrong edge must still IMPORT, or the control proves a crash rather than a refusal. `[측정 2026-10-22]`
4319
+ # SDK 1.4 pi-durable imports pi-ai's new `utils/identity`, absent from the older pi-ai control versions
4320
+ # checked here (1.0.3 and 1.3.0 both fail to load); pi-telemetry 1.0.3/1.3.0 and chord 1.3.0 lack exports
4321
+ # it imports. pi-codemode 1.3.0 loads, and depends on no SDK member, so this tree carries exactly one
4322
+ # depth-2 mismatch (L4 keeps depth 3).
4323
+ printf '%s\n' "{\"name\":\"pd-mismatch\",\"version\":\"1.0.0\",\"private\":true,\"overrides\":{\"@earendil-works/pi-durable\":{\"@earendil-works/pi-codemode\":\"1.3.0\"}}}" > "$box/pd-mismatch/package.json"
4308
4324
  out=$(cd "$box/pd-mismatch" && npm install "$tgz" --no-audit --no-fund 2>&1) || {
4309
4325
  fail "[check-pack-install] pi-durable L3 (mismatch control) install failed:"; echo "$out" | tail -10 | sed 's/^/ /' >&2; return 1; }
4310
4326
  pkg="$box/pd-mismatch/node_modules/@junghanacs/entwurf"
4311
4327
  rc=0; out=$(cd "$box" && node "$pkg/mcp/entwurf-bridge/dist/pi-extensions/lib/pi-durable-runtime.js" resolve 2>&1) || rc=$?
4312
- if [ "$rc" -ne 3 ] || ! grep -q "pi-durable-sdk-mismatch: @earendil-works/pi-durable → @earendil-works/pi-ai wants $pin_ver, binds 1.0.3" <<<"$out"; then
4313
- fail "[QK:PACK-INSTALL-DURABLE-MISMATCH-REFUSED] [check-pack-install] pi-durable L3: an installed tree whose pi-durable binds pi-ai 1.0.3 must be refused by name as pi-durable-sdk-mismatch (rc=$rc): $out"
4328
+ if [ "$rc" -ne 3 ] || ! grep -q "pi-durable-sdk-mismatch: @earendil-works/pi-durable → @earendil-works/pi-codemode wants $pin_ver, binds 1.3.0" <<<"$out"; then
4329
+ fail "[QK:PACK-INSTALL-DURABLE-MISMATCH-REFUSED] [check-pack-install] pi-durable L3: an installed tree whose pi-durable binds pi-codemode 1.3.0 must be refused by name as pi-durable-sdk-mismatch (rc=$rc): $out"
4314
4330
  return 1
4315
4331
  fi
4316
4332
  rc=0; out=$(cd "$box" && node --input-type=module -e "import { pathToFileURL } from 'node:url'; await import('$pkg/pi/pi-durable/carrier-resolver.mjs'); const m = await import(pathToFileURL('$pkg/pi/pi-durable/carrier/runtime.js').href); console.log(typeof m.openDurable);" 2>&1) || rc=$?
@@ -4835,19 +4851,21 @@ _check_pack_install_impl() {
4835
4851
  # `[측정 2026-10-16]` at 1.3.0 the constellation is unchanged: the installed manifests give the
4836
4852
  # same member edges as at 1.2.0, now every one exact 1.3.0 (upstream #10865), and the consumer
4837
4853
  # oracle walked 10 member edges with 0 mismatches (.tmp-verify/145-stage1/08-pack-install.log).
4838
- echo "[check-pack-install] pnpm add into $tmp (with the aligned 1.3.x SDK + chord + codemode + mcp + durable + typebox)"
4854
+ # `[측정 2026-10-22]` at 1.4.0 the nine members are again exact 1.4.0 edges (registry manifests; #148 S0),
4855
+ # and pi-tui 1.4.0 adds one third-party dependency (marked-cjk-friendly 0.1.2) outside the closure.
4856
+ echo "[check-pack-install] pnpm add into $tmp (with the aligned 1.4.x SDK + chord + codemode + mcp + durable + typebox)"
4839
4857
  local install_log
4840
4858
  install_log=$(cd "$tmp" && pnpm add \
4841
4859
  "$tgz_path" \
4842
- "@earendil-works/pi-ai@1.3.0" \
4843
- "@earendil-works/pi-coding-agent@1.3.0" \
4844
- "@earendil-works/pi-tui@1.3.0" \
4845
- "@earendil-works/pi-agent-core@1.3.0" \
4846
- "@earendil-works/pi-telemetry@1.3.0" \
4847
- "@earendil-works/chord@1.3.0" \
4848
- "@earendil-works/pi-codemode@1.3.0" \
4849
- "@earendil-works/pi-mcp@1.3.0" \
4850
- "@earendil-works/pi-durable@1.3.0" \
4860
+ "@earendil-works/pi-ai@1.4.0" \
4861
+ "@earendil-works/pi-coding-agent@1.4.0" \
4862
+ "@earendil-works/pi-tui@1.4.0" \
4863
+ "@earendil-works/pi-agent-core@1.4.0" \
4864
+ "@earendil-works/pi-telemetry@1.4.0" \
4865
+ "@earendil-works/chord@1.4.0" \
4866
+ "@earendil-works/pi-codemode@1.4.0" \
4867
+ "@earendil-works/pi-mcp@1.4.0" \
4868
+ "@earendil-works/pi-durable@1.4.0" \
4851
4869
  "typebox@latest" \
4852
4870
  --ignore-workspace --ignore-scripts 2>&1) || {
4853
4871
  fail "[check-pack-install] pnpm add failed:"
@@ -4863,11 +4881,11 @@ _check_pack_install_impl() {
4863
4881
  local leaked_pi
4864
4882
  leaked_pi=$(ls "$tmp/node_modules/.pnpm" 2>/dev/null | pack_install_leaked_pi)
4865
4883
  if [ -n "$leaked_pi" ]; then
4866
- fail "[check-pack-install] UNVERIFIED pi runtime resolved into the install tree (expected only 1.3.0):"
4884
+ fail "[check-pack-install] UNVERIFIED pi runtime resolved into the install tree (expected only 1.4.0):"
4867
4885
  printf '%s\n' "$leaked_pi" | sed 's/^/ /' >&2
4868
4886
  return 1
4869
4887
  fi
4870
- echo "[check-pack-install] pi runtime tree pin verified: every @earendil-works package is 1.3.0 (chord and pi-durable included)"
4888
+ echo "[check-pack-install] pi runtime tree pin verified: every @earendil-works package is 1.4.0 (chord and pi-durable included)"
4871
4889
 
4872
4890
  # Resolve the installed package.json and confirm pi.extensions
4873
4891
  # arrived intact. If pi.extensions is empty or missing, the
@@ -3470,7 +3470,7 @@ let manifestCount: number;
3470
3470
  "pi-bridge-sender": 4,
3471
3471
  "pi-mcp-bridge": 2,
3472
3472
  "pi-mcp-register": 3,
3473
- "pi-durable": 89,
3473
+ "pi-durable": 94,
3474
3474
  "copilot-receive": 20,
3475
3475
  "entwurf-peers": 1,
3476
3476
  "fresh-call-dispatch": 15,
@@ -52,8 +52,9 @@ export function buildCodexInstruction(input: CodexInstructionInput): string {
52
52
  // DIRECTORY must come from this Codex's own record (#95 lane C), which is a fact only
53
53
  // because its thread now opens where its pane is rather than where the app-server sits.
54
54
  // Omitting the parameter is the whole point — passing one would prove neither.
55
- 'NO cwd parameter and NO placement parameter, and task "After your automatic callback succeeds, answer ACK and ' +
56
- 'stop. Do not open another sibling."\n' +
55
+ 'NO cwd parameter and NO placement parameter, and task "Your automatic callback is the whole task and there is ' +
56
+ "no result to send. After it succeeds, end the turn: no message, no report, no further tool call. Do not open " +
57
+ 'another sibling."\n' +
57
58
  "2. Stop and wait for the Pi callback delivered into this Codex thread. Accept only a callback whose body " +
58
59
  "repeats the exact nonce from your LAUNCH receipt and whose sender envelope carries a garden id on its `session:` line.\n" +
59
60
  `3. Only after that match, call public mcp__entwurf_bridge__entwurf_v2 once with target ${input.callerGid}, ` +
@@ -181,7 +181,7 @@
181
181
  "claim": "PI-CORE-PIN-TRACKS-FLOOR",
182
182
  "title": "the queue oracle's pi-agent-core pin drifts off the floor the rest of the pi runtime is pinned to, so the oracle keeps answering honestly — about a pi nobody in this repo runs",
183
183
  "subject": "package.json",
184
- "find": ["\"@earendil-works/pi-agent-core\": \"1.3.0\","],
184
+ "find": ["\"@earendil-works/pi-agent-core\": \"1.4.0\","],
185
185
  "replace": ["\"@earendil-works/pi-agent-core\": \"0.87.1\","],
186
186
  "gate": ["bash", "run.sh", "check-dep-versions"],
187
187
  "timeoutSeconds": 300,
@@ -6,8 +6,8 @@
6
6
  "claim": "PACK-INSTALL-PIN-MATCHER-BOUNDED",
7
7
  "title": "the pin-leak matcher loses its version boundary and blesses substring lookalikes \u2014 @0.85.10 reads as the pinned 0.85.1 and the gate announces a verified tree it never verified (independent review, 2026-08-25)",
8
8
  "subject": "run.sh",
9
- "find": [" grep '^@earendil-works+' | grep -Ev '@1\\.3\\.0(_|$)' || true"],
10
- "replace": [" grep '^@earendil-works+' | grep -v '@0\\.87\\.1' || true"],
9
+ "find": [" grep '^@earendil-works+' | grep -Ev '@1\\.4\\.0(_|$)' || true"],
10
+ "replace": [" grep '^@earendil-works+' | grep -v '@1\\.4\\.0' || true"],
11
11
  "gate": ["bash", "run.sh", "check-pack-pin-matcher"],
12
12
  "timeoutSeconds": 120,
13
13
  "signature": "[QK:PACK-INSTALL-PIN-MATCHER-BOUNDED]",
@@ -17,8 +17,8 @@
17
17
  "claim": "PACK-INSTALL-PIN-MATCHER-COVERS-CLOSURE",
18
18
  "title": "the pin-leak matcher narrows back to the pi-* families and goes blind to a non-pi member of the same runtime closure \u2014 @earendil-works/chord (new in pi 0.85.0, a runtime dependency of pi-coding-agent/pi-agent-core/pi-client/pi-protocol) floats unpinned while the gate prints a verified pin (measured 2026-09-06 on a real 0.85.1 install tree)",
19
19
  "subject": "run.sh",
20
- "find": [" grep '^@earendil-works+' | grep -Ev '@1\\.3\\.0(_|$)' || true"],
21
- "replace": [" grep '^@earendil-works+pi-' | grep -Ev '@1\\.3\\.0(_|$)' || true"],
20
+ "find": [" grep '^@earendil-works+' | grep -Ev '@1\\.4\\.0(_|$)' || true"],
21
+ "replace": [" grep '^@earendil-works+pi-' | grep -Ev '@1\\.4\\.0(_|$)' || true"],
22
22
  "gate": ["bash", "run.sh", "check-pack-pin-matcher"],
23
23
  "timeoutSeconds": 120,
24
24
  "signature": "[QK:PACK-INSTALL-PIN-MATCHER-COVERS-CLOSURE]",
@@ -1207,6 +1207,66 @@
1207
1207
  "signatureSource": "pi-extensions/lib/pi-durable-carrier.test.ts",
1208
1208
  "build": { "argv": ["bash", "scripts/build-bridge.sh"], "output": "mcp/entwurf-bridge/dist" }
1209
1209
  },
1210
+ {
1211
+ "claim": "PI-DURABLE-TASK-SLEEPING-COUNTED",
1212
+ "title": "the badge stops naming sleeping tasks: a background task in its durable backoff is counted but said to be nothing",
1213
+ "subject": "pi/pi-durable/carrier/tui.js",
1214
+ "find": ["\t\t\t...tally(\"sleeping\", (node) => !node.abortRequested && node.state.status === \"sleeping\"),"],
1215
+ "replace": ["\t\t\t// sleeping is not named"],
1216
+ "gate": ["bash", "run.sh", "check-pi-durable-carrier"],
1217
+ "timeoutSeconds": 300,
1218
+ "signature": "[QK:PI-DURABLE-TASK-SLEEPING-COUNTED]",
1219
+ "signatureSource": "pi-extensions/lib/pi-durable-carrier.test.ts",
1220
+ "build": { "argv": ["bash", "scripts/build-bridge.sh"], "output": "mcp/entwurf-bridge/dist" }
1221
+ },
1222
+ {
1223
+ "claim": "PI-DURABLE-TASK-SLEEPING-SHOWN",
1224
+ "title": "a sleeping task's panel line drops its wake time, so nothing on screen says when the sleeping work comes back",
1225
+ "subject": "pi/pi-durable/carrier/tui.js",
1226
+ "find": ["\t\t\t\t\t? `sleeping ${state.phase} until ${localTime(state.until)}`"],
1227
+ "replace": ["\t\t\t\t\t? `sleeping ${state.phase}`"],
1228
+ "gate": ["bash", "run.sh", "check-pi-durable-carrier"],
1229
+ "timeoutSeconds": 300,
1230
+ "signature": "[QK:PI-DURABLE-TASK-SLEEPING-SHOWN]",
1231
+ "signatureSource": "pi-extensions/lib/pi-durable-carrier.test.ts",
1232
+ "build": { "argv": ["bash", "scripts/build-bridge.sh"], "output": "mcp/entwurf-bridge/dist" }
1233
+ },
1234
+ {
1235
+ "claim": "PI-DURABLE-TASK-BLOCKED-SHOWN",
1236
+ "title": "the panel stops marking blocked tasks: a task the SDK will not run reads as ordinary pending work",
1237
+ "subject": "pi/pi-durable/carrier/tui.js",
1238
+ "find": ["\t\tnode.blocked !== undefined ? `blocked:${node.blocked}` : \"\","],
1239
+ "replace": ["\t\t\"\","],
1240
+ "gate": ["bash", "run.sh", "check-pi-durable-carrier"],
1241
+ "timeoutSeconds": 300,
1242
+ "signature": "[QK:PI-DURABLE-TASK-BLOCKED-SHOWN]",
1243
+ "signatureSource": "pi-extensions/lib/pi-durable-carrier.test.ts",
1244
+ "build": { "argv": ["bash", "scripts/build-bridge.sh"], "output": "mcp/entwurf-bridge/dist" }
1245
+ },
1246
+ {
1247
+ "claim": "PI-DURABLE-TASK-BLOCKED-REASON",
1248
+ "title": "every blocked task is said to be parked by the restart limit, whatever reason the SDK gave",
1249
+ "subject": "pi/pi-durable/carrier/tui.js",
1250
+ "find": ["\t\tnode.blocked !== undefined ? `blocked:${node.blocked}` : \"\","],
1251
+ "replace": ["\t\tnode.blocked !== undefined ? `blocked:restart_limit` : \"\","],
1252
+ "gate": ["bash", "run.sh", "check-pi-durable-carrier"],
1253
+ "timeoutSeconds": 300,
1254
+ "signature": "[QK:PI-DURABLE-TASK-BLOCKED-REASON]",
1255
+ "signatureSource": "pi-extensions/lib/pi-durable-carrier.test.ts",
1256
+ "build": { "argv": ["bash", "scripts/build-bridge.sh"], "output": "mcp/entwurf-bridge/dist" }
1257
+ },
1258
+ {
1259
+ "claim": "PI-DURABLE-TEST-FIXTURES-NOT-SHIPPED",
1260
+ "title": "package.json drops its fixture negation, so every published tarball carries the test goldens under pi-extensions/**/fixtures/",
1261
+ "subject": "package.json",
1262
+ "find": ["\t\t\"!pi-extensions/**/*.test.ts\",", "\t\t\"!pi-extensions/**/fixtures/**\""],
1263
+ "replace": ["\t\t\"!pi-extensions/**/*.test.ts\""],
1264
+ "gate": ["bash", "run.sh", "check-pi-durable-carrier"],
1265
+ "timeoutSeconds": 300,
1266
+ "signature": "[QK:PI-DURABLE-TEST-FIXTURES-NOT-SHIPPED]",
1267
+ "signatureSource": "pi-extensions/lib/pi-durable-carrier.test.ts",
1268
+ "build": { "argv": ["bash", "scripts/build-bridge.sh"], "output": "mcp/entwurf-bridge/dist" }
1269
+ },
1210
1270
  {
1211
1271
  "claim": "PI-DURABLE-STREAM-TRANSPORT-WIRE",
1212
1272
  "title": "the Harness settings stop carrying the transport: the provider never continues a response, so every request of a long session resends the whole conversation",
@@ -7,10 +7,10 @@
7
7
  "title": "one row of the pnpm workspace closure is left on the previous floor while every other surface moved — the shape a version bump actually fails in. Nothing else reads that file, so the stranded row surfaces only at install time, in a different command, far from the change that stranded it",
8
8
  "subject": "pnpm-workspace.yaml",
9
9
  "find": [
10
- " - '@earendil-works/pi-telemetry@0.85.0 || 0.85.1 || 0.86.0 || 0.87.0 || 0.87.1 || 0.99.1 || 0.99.2 || 1.0.0 || 1.0.2 || 1.0.4 || 1.1.0 || 1.2.0 || 1.3.0'"
10
+ " - '@earendil-works/pi-telemetry@0.85.0 || 0.85.1 || 0.86.0 || 0.87.0 || 0.87.1 || 0.99.1 || 0.99.2 || 1.0.0 || 1.0.2 || 1.0.4 || 1.1.0 || 1.2.0 || 1.3.0 || 1.4.0'"
11
11
  ],
12
12
  "replace": [
13
- " - '@earendil-works/pi-telemetry@0.85.0 || 0.85.1 || 0.86.0 || 0.87.0 || 0.87.1 || 0.99.1 || 0.99.2 || 1.0.0 || 1.0.2 || 1.0.4 || 1.1.0 || 1.2.0'"
13
+ " - '@earendil-works/pi-telemetry@0.85.0 || 0.85.1 || 0.86.0 || 0.87.0 || 0.87.1 || 0.99.1 || 0.99.2 || 1.0.0 || 1.0.2 || 1.0.4 || 1.1.0 || 1.2.0 || 1.3.0'"
14
14
  ],
15
15
  "gate": ["bash", "run.sh", "check-dep-versions"],
16
16
  "timeoutSeconds": 300,
@@ -21,8 +21,8 @@
21
21
  "claim": "PI-SDK-PRODUCTION-SET",
22
22
  "title": "the durable library's production pin falls one release behind the SDK set the pin declares, so the carrier would bind a pi-durable nobody verified it against",
23
23
  "subject": "package.json",
24
- "find": ["\"@earendil-works/pi-durable\": \"1.3.0\","],
25
- "replace": ["\"@earendil-works/pi-durable\": \"1.2.0\","],
24
+ "find": ["\"@earendil-works/pi-durable\": \"1.4.0\","],
25
+ "replace": ["\"@earendil-works/pi-durable\": \"1.3.0\","],
26
26
  "gate": ["bash", "run.sh", "check-dep-versions"],
27
27
  "timeoutSeconds": 300,
28
28
  "signature": "[QK:PI-SDK-PRODUCTION-SET]",
@@ -33,7 +33,7 @@
33
33
  "title": "an @earendil-works package returns as a dev dependency beside its production pin, one name in two dependency classes",
34
34
  "subject": "package.json",
35
35
  "find": ["\"@biomejs/biome\": \"^2.4.13\","],
36
- "replace": ["\"@biomejs/biome\": \"^2.4.13\",\n\t\t\"@earendil-works/pi-ai\": \"1.3.0\","],
36
+ "replace": ["\"@biomejs/biome\": \"^2.4.13\",\n\t\t\"@earendil-works/pi-ai\": \"1.4.0\","],
37
37
  "gate": ["bash", "run.sh", "check-dep-versions"],
38
38
  "timeoutSeconds": 300,
39
39
  "signature": "[QK:PI-SDK-NO-DUAL-DECLARATION]",
@@ -1082,7 +1082,8 @@ async function run(): Promise<void> {
1082
1082
  const codexWaitToken = `CODEX-WAIT-${Math.random().toString(36).slice(2, 10).toUpperCase()}`;
1083
1083
  const finalToken = `CODEX-PI-FINAL-${Math.random().toString(36).slice(2, 10).toUpperCase()}`;
1084
1084
  expectedReportTokens.push(finalToken);
1085
- const outboundPiTask = "After your automatic callback succeeds, answer ACK and stop. Do not open another sibling.";
1085
+ const outboundPiTask =
1086
+ "Your automatic callback is the whole task and there is no result to send. After it succeeds, end the turn: no message, no report, no further tool call. Do not open another sibling.";
1086
1087
  const codexTask =
1087
1088
  `After your required callback receipt, end the turn and wait passively for the addressed instruction containing ${codexWaitToken}. ` +
1088
1089
  "Do not call shell, sleep, terminal, or any tool to wait.";
@@ -110,9 +110,16 @@ fi
110
110
 
111
111
  PI_SPEC="$(node -p "require('$REPO/package.json').dependencies['@earendil-works/pi-coding-agent']")"
112
112
  # Pinned, not floating: a receipt that cannot say WHICH Claude Code it ran is not
113
- # reproducible. This is the version measured in the container on 2026-09-19. Claude Code
114
- # is scaffolding here (Rule 17) — the product face starts at `herdr integration install`.
115
- CLAUDE_SPEC="2.1.278"
113
+ # reproducible. The exact version is the support floor itself, derived from
114
+ # `package.json` `entwurf.claudeCodeFloor` rather than retyped: a hand-kept pin stayed at
115
+ # 2.1.278 after the floor moved to 2.1.306 and the activation then refused it, as it should.
116
+ # Claude Code is scaffolding here (Rule 17) — the product face starts at `herdr integration install`.
117
+ CLAUDE_FLOOR="$(node -p "require('$REPO/package.json').entwurf.claudeCodeFloor")"
118
+ CLAUDE_SPEC="${CLAUDE_FLOOR#>=}"
119
+ if [[ "$CLAUDE_FLOOR" != ">=$CLAUDE_SPEC" || ! "$CLAUDE_SPEC" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
120
+ echo "[smoke-herdr-raw-install-live] FAIL — package.json entwurf.claudeCodeFloor is '$CLAUDE_FLOOR', not '>=X.Y.Z'." >&2
121
+ exit 1
122
+ fi
116
123
  FLOOR_SPEC="$(node -p "require('$REPO/package.json').engines.node")"
117
124
  FLOOR_MAJOR="${FLOOR_SPEC#>=}"; FLOOR_MAJOR="${FLOOR_MAJOR%%.*}"
118
125
  HERDR_VERSION="$(node -p "require('$REPO/scripts/fixtures/herdr-supply.json').version")"