@junghanacs/entwurf 0.30.1 → 0.32.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.
Files changed (76) hide show
  1. package/AGENTS.md +4 -2
  2. package/CHANGELOG.md +194 -0
  3. package/DELIVERY.md +1 -0
  4. package/README.md +64 -14
  5. package/VERIFY.md +36 -1
  6. package/demo/README.md +1 -1
  7. package/docs/acp-backend-rail.md +1 -1
  8. package/docs/setup-clean-host.md +121 -4
  9. package/mcp/entwurf-bridge/dist/mcp/entwurf-bridge/src/index.js +7 -7
  10. package/mcp/entwurf-bridge/dist/pi-extensions/entwurf-capabilities.json +1 -0
  11. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-deliverability.js +7 -1
  12. package/mcp/entwurf-bridge/dist/pi-extensions/lib/fresh-call-composition.js +34 -3
  13. package/mcp/entwurf-bridge/dist/pi-extensions/lib/meta-sender-identity.js +7 -1
  14. package/mcp/entwurf-bridge/dist/pi-extensions/lib/meta-session.js +28 -2
  15. package/mcp/entwurf-bridge/dist/pi-extensions/lib/mux-fresh-call.js +28 -2
  16. package/mcp/entwurf-bridge/dist/pi-extensions/lib/pi-durable-fresh-preflight.js +92 -0
  17. package/mcp/entwurf-bridge/dist/pi-extensions/lib/pi-durable-runtime.js +236 -0
  18. package/mcp/entwurf-bridge/dist/pi-extensions/meta-bridge-pi-durable.js +746 -0
  19. package/mcp/entwurf-bridge/src/index.ts +7 -7
  20. package/mcp/entwurf-bridge/tsconfig.build.json +8 -1
  21. package/package.json +19 -19
  22. package/pi/entwurf-capabilities.json +1 -0
  23. package/pi/pi-durable/bootstrap.mjs +404 -0
  24. package/pi/pi-durable/carrier/LICENSE +21 -0
  25. package/pi/pi-durable/carrier/harness-setup.js +123 -0
  26. package/pi/pi-durable/carrier/prompt.js +74 -0
  27. package/pi/pi-durable/carrier/runtime.js +402 -0
  28. package/pi/pi-durable/carrier/sessions.js +56 -0
  29. package/pi/pi-durable/carrier/subagent.js +55 -0
  30. package/pi/pi-durable/carrier/tui.js +655 -0
  31. package/pi/pi-durable/carrier-relocation.mjs +160 -0
  32. package/pi/pi-durable/carrier-resolver.mjs +38 -0
  33. package/pi/pi-durable/overlay/runtime-contacts.patch +91 -0
  34. package/pi/pi-durable/overlay/upstream-pin.json +108 -0
  35. package/pi-extensions/lib/entwurf-deliverability.ts +7 -1
  36. package/pi-extensions/lib/fresh-call-composition.ts +42 -7
  37. package/pi-extensions/lib/meta-sender-identity.ts +7 -1
  38. package/pi-extensions/lib/meta-session.ts +28 -2
  39. package/pi-extensions/lib/mux-fresh-call.ts +32 -2
  40. package/pi-extensions/lib/pi-durable-fresh-preflight.ts +113 -0
  41. package/pi-extensions/lib/pi-durable-runtime.ts +318 -0
  42. package/pi-extensions/meta-bridge-pi-durable.ts +1024 -0
  43. package/run.sh +624 -184
  44. package/scripts/check-entwurf-bridge-pi-free.ts +7 -5
  45. package/scripts/check-fresh-call-dispatch.ts +18 -0
  46. package/scripts/check-gate-qualification.ts +87 -34
  47. package/scripts/check-herdr-activation.ts +5 -4
  48. package/scripts/check-pi-durable-contact.ts +767 -0
  49. package/scripts/check-pi-durable-receive.ts +1079 -0
  50. package/scripts/check-pi-durable-send.ts +1821 -0
  51. package/scripts/emit-pi-durable-carrier.ts +355 -0
  52. package/scripts/lib/mutation-qualify.ts +21 -45
  53. package/scripts/lib/pi-durable-fresh-live-receipts.ts +139 -0
  54. package/scripts/lib/pi-durable-operator-guard.ts +89 -0
  55. package/scripts/lib/pi-durable-r1.ts +100 -0
  56. package/scripts/lib/pi-durable-reap.ts +27 -0
  57. package/scripts/lib/pi-durable-receive.ts +137 -0
  58. package/scripts/lib/qualification-receipt.ts +7 -11
  59. package/scripts/mutants/control-send-receipt.json +1 -1
  60. package/scripts/mutants/copilot-birth.json +2 -2
  61. package/scripts/mutants/gate-qualification.json +13 -0
  62. package/scripts/mutants/meta-hook-session-switch.json +4 -2
  63. package/scripts/mutants/mux-fresh-call.json +69 -3
  64. package/scripts/mutants/omp-birth.json +2 -2
  65. package/scripts/mutants/omp-fresh.json +6 -4
  66. package/scripts/mutants/pack-install.json +3 -3
  67. package/scripts/mutants/pi-durable.json +723 -0
  68. package/scripts/mutants/pi-floor.json +24 -2
  69. package/scripts/pi-durable-contact-host.mjs +105 -0
  70. package/scripts/pi-durable-receive-host.mjs +127 -0
  71. package/scripts/pi-durable-send-host.mjs +284 -0
  72. package/scripts/smoke-herdr-raw-install-live.sh +1 -1
  73. package/scripts/smoke-pi-durable-fresh-live.ts +624 -0
  74. package/scripts/smoke-setup-verdict.sh +22 -3
  75. package/scripts/smoke-user-scope-citizen.sh +1 -1
  76. package/scripts/tsconfig.json +1 -0
package/run.sh CHANGED
@@ -132,6 +132,10 @@ Usage:
132
132
  ./run.sh check-mailbox-receipt-state # deterministic gate (0.11 Stage 0 step 3B): mailbox receipt state schema + store (stamp→persist→read-back) in a temp mailbox, strict keyset, no API
133
133
  ./run.sh check-copilot-birth-hook # #82 gate: drives the real Copilot assembler into a temp dir, fires the baked launcher with NO ARGV (the way Copilot's `exec`-string schema forces), and requires a backend:"copilot" v3 record + attach + peer row + a SENDER marker the resolver joins back to that record, and still zero mailbox/receiver marker (who-sent needs a shared parent; a receiver needs a doorbell this backend has not got). Hermetic; no Copilot, no model turn
134
134
  ./run.sh check-omp-birth-hook # #87 gate: drives the real OMP assembler into a temp dir, imports the ASSEMBLED index.ts into a MOCK omp host and fires session_start/session_switch. tui mints one backend:"omp" record + a sender marker keyed to the host's OWN pid (the one-process join) + the garden id on the status line; print/rpc/json mint NOTHING (hasUI is true on the rpc rows, as in the vendor); switch attaches on the same native id and mints the replacement on a new one; the CERTIFIED receiver reader still finds no marker. Also the four-root joint binding: extension and omp-labeled bridge child resolve the same sessions/mailbox/senders/receivers bundle against test-built literals (never the production resolver as its own oracle), under a poisoned PI_CODING_AGENT_DIR and under four distinct ENTWURF_META_* overrides; the override grammar is absolute-or-~ and both halves plus the doctor refuse anything else by name; and a drifted provenance label selects no root policy at all. Hermetic; no omp, no model turn
135
+ ./run.sh check-pi-durable-contact # #129 L-identity gate, CHECKOUT-ONLY (an installed package refuses it in run_ts). Opens the REAL durable app from THIS checkout's verified carrier (pi/pi-durable/carrier/ under its resolver, against the checkout's pinned pi SDK set — refused by name first) with the pi-durable contact, births the citizen and runs its tools adapter-direct through a privately emitted branch bridge (ENTWURF_PI_DURABLE_BRIDGE_ENTRY; its registry must match this checkout). Matrix roots {default, explicit} x {fresh, --continue}: one record keyed by the durable session dir, sender marker on the spawned host pid, exactly one bridge child whose /proc PPID is that host, poisoned identity carriers kept from it, its four root carriers bound to the roots written, the receiver ARMED (raw marker owned by the host pid + start key as pi-durable-host, joined to the sender marker; removed on close), self = the record and replyable, peers from an outside bridge, same garden id on reopen; the bundle's compiled owner join must admit pi-durable-host. Reaps only the host/bridges it owns on any failure. Harness-bypassed: no model turn, no ToolTask. Missing inputs → SKIP (97), never a pass
136
+ ./run.sh check-pi-durable-send # #129 L-send FIRST CELL, CHECKOUT-ONLY. Same input as check-pi-durable-contact. One native durable turn: the app's own controller input → GenerationTask → ToolTask → the contact's entwurf_v2 → the private bridge → a seeded self-fetch target (C2 recipe, owned by a separate idle child). S = a 127.0.0.1 scripted OpenAI-compatible endpoint configured through the app's documented models.json (dummy key, PI_OFFLINE); H = the native ToolTask path; V (a vendor turn) is NOT run. Oracles: the wire (tools offered, v2 schema = the bridge's tools/list, exactly two requests, the tool result + receipt in the second), the raw mailbox (.msg 1, signal, sender = the host record), the native view (one call, its settled result, root current). Not exactly-once, not recovery, not a recipient turn. Missing inputs → SKIP (97)
137
+ ./run.sh check-pi-durable-beside # #129 mutant execution coordinate (VERIFY rule 3): run_vitest over the five pi-durable beside tests (adapter, carrier/SDK-set verifier, preflight, owner join, carrier relocation/resolver/emitter) — the pi-durable qualification lane's gate argv. Same files check-tests-beside-behavior already runs.
138
+ ./run.sh check-pi-durable-receive # #129 L-receive FIRST CELL, CHECKOUT-ONLY. Same input (the bundle must carry the pi-durable-host owner join). The host driver gives the app NO input; a seeded claude-code sender (marker owned by a separate idle child, handed to a gate-owned sending bridge through ENTWURF_META_SENDER_MARKER) enqueues one message, the host's doorbell admits its notice to the ROOT conversation, and the scripted model drains it with entwurf_inbox_read. S withholds response #1 until the ENQUEUE receipt, the .delivered bytes, the signal, the host-owned receiver marker and the notice are captured. Oracles: receiver() admission, native view, raw archive (.delivered.read + lastReadAt), wire (exactly two requests, drain result = view), and after a green reap a forensic read-only SQLite copy (submission row by request id = reported id, root, input, done, placed entry = the notice). V NOT run. Missing inputs → SKIP (97)
135
139
  ./run.sh check-copilot-receive-arm # #82 RAIL 5 gate: the REAL receiver installer + the REAL extension.mjs forked with a stubbed SDK. Arms only after birth, marker owned by the WATCHER pid, self-fetch dispatch answer, doorbell carries the garden id and NOT the body, id-drift/foreign-parent refusals. Hermetic; no Copilot, no model turn
136
140
  ./run.sh check-copilot-launch # #82 RAIL 7 gate: the MANAGED launch `entwurf copilot`, driven through its public address against a FAKE VENDOR on a sandbox PATH. Receiver precondition refusals, EXTENSIONS token + operator token preservation, injected defaults before the `--` terminator, byte-identical argv, the 11 explicit permission/surface policy overrides that suppress `--yolo`, exec (not fork) pid identity, exit passthrough, recursion refusal. Hermetic; no Copilot, no model turn
137
141
  ./run.sh check-codex-app-server-launch # #95 gate: the MANAGED app-server launch `entwurf codex-app-server`, driven through its public address against a FAKE VENDOR on a sandbox PATH. The injected `app-server --listen unix://<socket>` required to equal the product's own resolveCodexDefaultSocketPath over an env matrix that includes the inputs a bash transcription was MEASURED to diverge on (BOM-only CODEX_HOME, trailing slash, `..`), non-absolute and control-character addresses refused before any write, byte-identical operator argv, exec (not fork) parent identity, exit passthrough, live-socket and indeterminate-socket and second---listen and recursion refusals, dead-socket launch-over, the tmux fact line, and both identity carriers stripped. Hermetic; no Codex, no app-server, no model turn
@@ -199,6 +203,7 @@ Usage:
199
203
  ./run.sh smoke-setup-verdict # #86 C1+C3b aggregate setup verdict fixture: all-absent SKIP/green, pi presence+floor, detected-FAIL nonzero, copilot four-unit composition (present=independent PASS/FAIL rows, absent=zero-state SKIP), installed-mode named branch, credential store untouched. Offline/deterministic
200
204
  ./run.sh check-agy-permission-matrix # AGY permission CONTRACT SPACE as a literal table (55 cells): parser-state × operation × settings × ownership × precedence with stated exclusion rules; expectations are hand-written literals, never read from the SUT. Offline/deterministic (deps: python3)
201
205
  ./run.sh smoke-omp-fresh-live # #87 bundle C acceptance, RELEASE MUST: omp opened as ONE visible fresh sibling through the PUBLIC entwurf_fresh_call surface (never raw tmux), exact nonce callback -> garden id from the SENDER ENVELOPE -> record/backend identity -> addressed receive -> lastReadAt -> the drain visible in that same native session's own transcript. Needs LIVE=1 and spends real model turns. Self-deciding: omp outside FRESH_CALL_BACKENDS or without a mailbox rail is a protocol SKIP, which --cut reads as RED
206
+ ./run.sh smoke-pi-durable-fresh-live # #129 first-admission acceptance, RELEASE MUST: real Pi opens pi-durable through entwurf_fresh_call (managed verb), exact-nonce callback into Pi, Pi→pd addressed receive drained by pd's live turn, pd→Pi dispatch relayed to the collector; pd's actual model + task-wide offering read from its native store (online sqlite backup). LIVE=1; models ENTWURF_PI_DURABLE_FRESH_MODEL / _PI_MODEL (default openai-codex/gpt-6.1-sol)
202
207
  ./run.sh smoke-omp-receive-live # #87 bundle B acceptance: another harness wakes an ALREADY-OPEN omp citizen, which reads its inbox and answers in the same session. Decides its own outcome from the capability registry — omp without a drainable mailbox is a protocol SKIP (no receiver unit exists to arm), which an unattended release-gate reports and `--cut` reads as RED; a registry that claims a receive rail with no acceptance body here is a FAIL
203
208
  ./run.sh smoke-entwurf-chain-live # LIVE cross-harness delivery chain: native Claude Code -> pi GPT -> pi ACP Sonnet -> mailbox terminus, proving sender identity/replyable at every hop and a real read receipt at the end. Prerequisites (claude on PATH, pi credentials per backend) report protocol SKIP, never a pass
204
209
  ./run.sh check-release-gate-outcomes # release-gate STEP OUTCOME protocol (P1): one skip exit code shared by the shell + TS halves, classifier never rounds a skip up to a pass, `--cut` refuses a MUST SKIP while a bare diagnostic stays exit 0, no LIVE smoke keeps the old exit-0 skip shape, and both real skip surfaces are INVOKED and observed to propagate the code
@@ -221,6 +226,7 @@ Usage:
221
226
  ./run.sh uninstall-meta-bridge # 1.0.0 meta-bridge Phase 2: stateful GLOBAL uninstall (restore only keys/items captured in install-state)
222
227
  ./run.sh doctor-meta-bridge # THE RELEASE ORACLE (#51, Linux-certified repair axis). exit 0 = every required layer was MEASURED on this Linux host: toolchain + state + plugin/MCP + resolved-artifact launch-form classification (all 3 owner hooks + doorbell static contract) + synthetic owner join + store scan + hook errors + SessionStart evidence + REQUIRED live MCP↔marker join + writer-version parity. Missing live evidence is NOT CERTIFIED (open a Claude session and re-run), never a pass; Darwin is NOT CERTIFIED and stays nonzero, on ONE narrowed reason — start-key and ppid are portable there (`ps` fallback, same string the TS core mints), so what holds the lane closed is per-process environment DISCOVERY alone, pending physical-host evidence. Detection power is held by check-meta-doctor-oracle
223
228
  ./run.sh copilot [args...] # #82 RAIL 7: the MANAGED Copilot launch. exec()s the vendor CLI in THIS terminal (cwd/pid/exit preserved) with COPILOT_CLI_ENABLED_FEATURE_FLAGS=EXTENSIONS set for that one invocation — entwurf owns no part of your shell and writes nothing to it, but it owns the process it is about to become. Without that flag Copilot skips the extension scan SILENTLY. Refuses to launch unless the receiver unit is actually installed. Injects `--model auto` when no --model was given and `--yolo` when no explicit permission/surface policy flag was given, both BEFORE any `--`; every argument you pass is forwarded byte-identical. RUNNING THIS IS YOUR CONSENT to that profile — use plain `copilot` for stock vendor behaviour. Not tmux, not fresh-call, mints no citizen (birth is still the first prompt)
229
+ ./run.sh pi-durable [args...] # #129/#130: the MANAGED durable launch. Verifies this package's pi-durable CARRIER (pi/pi-durable/carrier/, the pinned upstream app emitted by the maintainer) and the pi SDK set it resolves — every declared member/direct edge at exactly the pinned version — refusing by name (package-incomplete / carrier-drift / sdk-absent / sdk-mismatch, exit 3), then exec()s node under the carrier resolver with the packaged bootstrap (pi/pi-durable/bootstrap.mjs) in this terminal. A new session needs --provider <p> --model <exact catalog id> --width task-wide [--entwurf-bootstrap <{v:2,task}>]; --continue takes none of them. Nothing outside the package is located, installed or repaired
224
230
  ./run.sh install-copilot-bridge # #82: GLOBAL install of the Copilot BIRTH plugin (own marketplace root; node+entry baked into the no-argv exec string). MCP wiring and the RECEIVER extension are separate install surfaces (install-copilot-mcp, install-copilot-receive). Also retires the stale Claude unit (--keep-stale-claude-unit opts out)
225
231
  ./run.sh uninstall-copilot-bridge # #86 C3a: honest inverse of install-copilot-bridge from the package-owned install-state (exact qualified id + exact marketplace name/path + recorded assembly only; state deleted LAST; a failing vendor list is UNKNOWN and refuses; never --force, never the stale Claude unit)
226
232
  ./run.sh doctor-copilot-bridge # #82/#86: fail-loud surface for that unit — runtime axis (red = a hook that RAN and failed, or a broken/unbaked artifact; "installed with zero records" is NOT red and is reported as NOT-YET: a Copilot session is born on its FIRST PROMPT, not when the window opens, measured) + ownership axis (install-state shape/binding, marketplace path drift, UNKNOWN vendor lists; legacy no-state install = named non-green, repair = install-copilot-bridge adoption). Either axis red = red
@@ -276,13 +282,13 @@ Usage:
276
282
  ./run.sh peer-facts # #116 M2-a owner-normalized READ-ONLY peer projection: the same listEntwurfFacts provider through the same renderer entwurf_peers uses, emitted as deterministic JSON {schemaVersion:1, storeDir, peers, diagnostics} so a consumer never re-implements the placement join (whose pi half is a measured vendor floor). NOT the same bytes as an entwurf_peers call and deliberately so: that surface is a human listing (observationLimit, text over the wire), this is a machine projection with UNBOUNDED observation, because a rationed row says `unobserved` and a machine cannot tell that from "nobody could look". `placement` stays a STRUCTURED tagged union ({kind:"herdr-pane",paneId} / unobserved / none / ambiguous) — never the human `herdr <pane>` string. Publishes NO socket coordinate (ENTWURF_DIR selects what is PROBED, #50 C4) and NO herdr agent_status / interactive_ready / screen text. One placement read per listing, no watcher. EXIT: 0 readable (diagnostics in-band; missing store = empty), 2 usage, 3 unreadable ([dir])
277
283
  ./run.sh meta-bridge-managed-keys # 0.10.0 meta-bridge: print the SSOT of settings keys entwurf OWNS (consumers read this to stay disjoint — keyset-owner invariant)
278
284
  ./run.sh check-keyset-overlap <fragment.json...> # 0.10.0 meta-bridge: PREVENTIVE keyset guard — fail if a consumer fragment collides with any pi-owned key (cross-repo; not in pnpm check)
279
- ./run.sh check-dep-versions # local deterministic check that the pi pin agrees across package.json (devDeps + peer range), run.sh (peer-install pins), and the baseline docs (AGENTS/README/ROADMAP/setup-clean-host/demo)
285
+ ./run.sh check-dep-versions # local deterministic check that the pi pin agrees across the durable pin's SDK set, package.json (exact production dependencies, no Earendil dev/peer duplicate), run.sh (install pins), the workspace closure and the baseline docs (README/ROADMAP/setup-clean-host/demo/VERIFY/herdr/acp)
280
286
  ./run.sh check-node-floor-coherence # binds the Node floor (24+, single axis) across engines.node, run.sh setup preflight, meta-bridge install/doctor judgment logic, clean-host docs, the bridge launcher header, and the CI runner node-version — engines.node is the SSOT, everything else is derived; sweeps tracked contract text for an unregistered declaration
281
287
  ./run.sh check-pack # publish gate (dry-run): npm pack --dry-run + tarball invariants (runtime-critical present, dev residue absent)
282
288
  ./run.sh check-pack-pin-matcher # pure self-test of check-pack-install's pin-leak matcher against synthetic .pnpm lookalikes, one cell per property: version boundary (a prefix-extended @0.86.0-beta.1 and an off-pin @0.85.1 must leak, @0.86.0 bare or with any measured peer-hash must pass) and closure prefix (an off-pin @earendil-works/chord must leak — it carries no `pi-` prefix); snapshot-safe qualification oracle, also run first inside check-pack-install
283
289
  ./run.sh check-pack-consumer-roots # pure self-test of check-pack-install's consumer sandbox: under a poisoned inherited HOME/XDG/PI_CODING_AGENT_DIR every writable root a consumer subcall sees resolves inside its own consumer home; snapshot-safe qualification oracle, also run first inside check-pack-install
284
290
  ./run.sh check-fresh-cut-gate # SOURCE cell of the generation-boundary proof (IN pnpm run check:full): drives real install/setup/fresh-cut in a sandbox; certification refusal is pre-write, quiescence is fail-closed, archives preserve bytes, and the #54 exit matrix distinguishes complete / no-move / usage / incomplete transition / complete-with-cleanup-residue. No model/network/cost
285
- ./run.sh check-pack-install # heavy publish gate (prepublishOnly): actual npm pack + tar -tf + fresh-temp install smoke + the #116 M3-b1 herdr-plugin RUNTIME VERIFIER run against that actual installed tarball (the focused check-herdr-runtime-bootstrap drives a fixture; THIS is where the real package is proven) with the pinned pi peers (pins derived from the package.json devDep; check-dep-versions binds them) + the npm-installed bridge BOOTS (tools/list) and DELIVERS (tools/call entwurf_v2 → .msg lands) + the installed all-absent and copilot-present (four-unit fake-vendor) `entwurf setup` rows + the INSTALLED generation lifecycle on a seeded previous-generation host (REFUSE before activation writes / zero Claude invocations → installed fresh-cut archives + opens empty → install-meta-bridge PASSES) + the INSTALLED-PACKAGE branch of the Copilot and OMP birth installers actually RUN (compiled entry selected, no raw .ts, and a real birth edge mints a citizen — the half a required-artifact list can never stand in for)
291
+ ./run.sh check-pack-install # heavy publish gate (prepublishOnly): actual npm pack + tar -tf + fresh-temp install smoke + the #116 M3-b1 herdr-plugin RUNTIME VERIFIER run against that actual installed tarball (the focused check-herdr-runtime-bootstrap drives a fixture; THIS is where the real package is proven) with the pinned pi SDK (pins derived from the package.json exact production dependency; check-dep-versions binds them) + the npm-installed bridge BOOTS (tools/list) and DELIVERS (tools/call entwurf_v2 → .msg lands) + the installed all-absent and copilot-present (four-unit fake-vendor) `entwurf setup` rows + the INSTALLED generation lifecycle on a seeded previous-generation host (REFUSE before activation writes / zero Claude invocations → installed fresh-cut archives + opens empty → install-meta-bridge PASSES) + the INSTALLED-PACKAGE branch of the Copilot and OMP birth installers actually RUN (compiled entry selected, no raw .ts, and a real birth edge mints a citizen — the half a required-artifact list can never stand in for) + the INSTALLED #129 durable bootstrap (pi/pi-durable/bootstrap.mjs) imported from node_modules: main/openDurableCitizen exported and PACKAGED_BRIDGE_ENTRY = the installed package's own dist entry — import only, no runtime/TUI/birth/bridge started + the #130 pi-durable CARRIER on actual npm consumers (Entwurf-only / aligned host / isolated global): the installed verifier, a consumer-contained resolution oracle with a same-sandbox neighbour control, every physical SDK edge at the pin, a mixed tree refused as pi-durable-sdk-mismatch though it still imports, and the public `entwurf pi-durable` reaching the app's own explicit-model refusal before any birth, also with one explicit `--native-module` (the synthetic fixture, initialized first; a reserved extension name refused before anything opens) (offline, sandbox HOME)
286
292
  ./run.sh check-install-container # 0.12.8 (#51 C): Linux artifact-CONSUMER gate — one candidate .tgz handed read-only to a checkout-invisible node:<engines-major>-bookworm cell. Default packs once to temp; ENTWURF_CANDIDATE_TGZ=/absolute/preserved.tgz consumes those exact bytes with no re-pack and prints canonical path+sha256 for release. Non-root global PATH install, frozen package, MCP tools/list, fake-Claude install-meta-bridge, path+sha256 fence, strict doctor, and the GENERATION host-state matrix (clean / v3-only store bytes unchanged / previous-generation REFUSE→fresh-cut→retry PASS) seeded inline. Docker missing = honest SKIP; ENTWURF_REQUIRE_DOCKER=1 makes that RED (required CI)
287
293
  ./run.sh install [project-dir] # INTERNAL part of `setup` (project .pi/settings.json wiring) + npm-consumer entry — prefer `setup`, don't call directly for dev
288
294
  ./run.sh remove [project-dir] # remove entwurf entries from project .pi/settings.json (project scope only; global user-scope citizen left intact)
@@ -449,17 +455,12 @@ preflight_dep_integrity() {
449
455
  # still catching a pnpm dir-move that left the symlink store dangling
450
456
  # (accessSync follows the link).
451
457
  #
452
- # Probe set = the BUNDLED runtime `dependencies` only. The `@earendil-works/pi-*`
453
- # peer trio is intentionally EXCLUDED, and the reason depends on the lane — the old
454
- # blanket claim ("the pi loader provides that runtime itself") was true for only one
455
- # of them and is corrected here:
456
- # pi-managed install — pi omits peers (--legacy-peer-deps) and its own loader
457
- # supplies the runtime, so the trio is legitimately absent.
458
- # neutral npm/pnpm install — nothing supplies it. The optional peer is simply
459
- # unresolved (entwurf-preflight.ts:51), and the project-trust lane that needs it
460
- # is dead on that host. Excluding the trio from this probe is still right (a hard
461
- # require would reject every consumer install), but it is a KNOWN GAP, not proof
462
- # that the runtime is present by another route.
458
+ # Probe set = the non-Earendil bundled runtime `dependencies` below. The
459
+ # `@earendil-works/*` SDK set has been an exact production dependency since #130
460
+ # (before that it was an optional peer trio this probe could not require), and #130
461
+ # left it out of this probe: its installed integrity — every edge at the exact pin —
462
+ # is the pi-durable carrier verifier's subject (setup's pi-durable row and
463
+ # `entwurf pi-durable`), not a presence probe's.
463
464
  # pi runtime presence/version is covered by check-pi-runtime-version /
464
465
  # check-pi-import-surface, neither of which runs on a consumer host.
465
466
  local probe=(
@@ -719,7 +720,7 @@ install_user_scope() {
719
720
  # scripts/herdr-plugin-activate.mjs → `run.sh install-user-scope --plugin-runtime …`) and had no
720
721
  # version check at all, so a host pi setup would name as FAIL got its citizen written here in
721
722
  # silence. Both doors now tell the same truth, off the SAME derived range — `pi_supported_range`
722
- # reads the package.json devDep pin, so there is one range in the repo and no second parser.
723
+ # reads the pinned SDK generation, so there is one range in the repo and no second parser.
723
724
  #
724
725
  # ABSENT pi is deliberately NOT changed here. This verb is called by an activation that has
725
726
  # already decided pi is one of its backends; inventing a presence verdict at the writer would put
@@ -968,6 +969,19 @@ smoke_omp_fresh_live() {
968
969
  run_ts scripts/smoke-omp-fresh-live.ts
969
970
  }
970
971
 
972
+ smoke_pi_durable_fresh_live() {
973
+ # #129 first-admission acceptance, RELEASE MUST: docs/adding-a-harness.md step 9 clause 7 and
974
+ # the two cross-harness dispatch receipts. A collector fixture opens a REAL visible Pi through
975
+ # the public entwurf_fresh_call; Pi's live turn opens pi-durable through the managed verb; pd's
976
+ # first action calls back into Pi by exact nonce; Pi's live turn → pd (mailbox, drained by pd's
977
+ # live turn) and pd's live turn → Pi (control socket), relayed to the fixture. pd's actual
978
+ # offering and model are read from its native store via an online node:sqlite backup. Private
979
+ # tmux server; REAL meta roots (one Pi + one pd minted, asserted); records/transcripts/DB kept.
980
+ # pi-durable absent from FRESH_CALL_BACKENDS or without a mailbox rail, or LIVE!=1, is a
981
+ # protocol SKIP; with LIVE=1 every missing prerequisite (a verified carrier, pi, tmux, bridge) FAILs.
982
+ run_ts scripts/smoke-pi-durable-fresh-live.ts
983
+ }
984
+
971
985
  smoke_codex_fresh_live() {
972
986
  # #95 first-admission RELEASE MUST. The explicit operator-owned app-server PID is the
973
987
  # seat authority: validate its Codex app-server argv and /proc TMUX/TMUX_PANE, then use
@@ -1283,8 +1297,9 @@ check_entwurf_bridge_boot() {
1283
1297
 
1284
1298
  check_entwurf_bridge_pi_free() {
1285
1299
  # 0.12.1 A-gate (static half): the entwurf-bridge MCP server must boot WITHOUT any
1286
- # pi package. entwurf is a harness-neutral npm package; pi is one optional adapter
1287
- # lane, not a boot dependency. Walks the EAGER static value-import closure of
1300
+ # pi package. entwurf is a harness-neutral npm package; pi is one adapter lane, not a
1301
+ # boot dependency — since #130 the pi SDK is installed (the pi-durable carrier's), but
1302
+ # the bridge must still not import it. Walks the EAGER static value-import closure of
1288
1303
  # mcp/entwurf-bridge/src/index.ts and fails if any reachable module statically
1289
1304
  # value-imports @earendil-works/pi-*. Type-only imports and dynamic `await import()`
1290
1305
  # (the intended lazy preflight boundary) are excluded — the runtime boot smoke is the
@@ -1492,7 +1507,22 @@ check_mux_fresh_call() {
1492
1507
  # codex-caller-seat rides here for the same reason as the two preflights: the leaf exists
1493
1508
  # only as freshCall's Codex placement input (#95 lane B), so certifying it apart from the
1494
1509
  # composition it feeds would let the two halves of one seat decision drift.
1495
- run_vitest test/mux-fresh-call.test.ts test/fresh-call-composition.contract.test.ts test/copilot-fresh-preflight.test.ts test/codex-fresh-preflight.test.ts test/fresh-call-surfaces.contract.test.ts test/fresh-call-provider.contract.test.ts test/omp-fresh-bootstrap.contract.test.ts test/tmux-coordinate-row.test.ts test/codex-fresh-live-protocol.test.ts test/codex-caller-seat.test.ts
1510
+ # pi-durable-fresh-live-receipts rides here for tmux-coordinate-row's reason: it is the
1511
+ # deterministic half of a LIVE sibling (smoke-pi-durable-fresh-live, #129) — the oracles that judge
1512
+ # that gate's receipts, proven on inline rows, so no model turn and no durable host is needed.
1513
+ run_vitest test/mux-fresh-call.test.ts test/fresh-call-composition.contract.test.ts test/copilot-fresh-preflight.test.ts test/codex-fresh-preflight.test.ts test/fresh-call-surfaces.contract.test.ts test/fresh-call-provider.contract.test.ts test/omp-fresh-bootstrap.contract.test.ts test/tmux-coordinate-row.test.ts test/codex-fresh-live-protocol.test.ts test/codex-caller-seat.test.ts test/pi-durable-fresh-live-receipts.test.ts
1514
+ }
1515
+
1516
+ check_pi_durable_beside() {
1517
+ # #129: the pi-durable qualification lane's execution coordinate (VERIFY 「Four rules」 rule 3). It is
1518
+ # not a discovery path — check-tests-beside-behavior already finds these files. It exists because the
1519
+ # qualification runner re-invokes a gate argv once per mutant, and the narrow filter keeps one mutant
1520
+ # from re-running every beside test. The six files are one contact: the adapter and its packaged
1521
+ # bootstrap, the installed carrier/SDK-set verifier (which also reads the three checkout gates as
1522
+ # SOURCE), the fresh preflight, the owner join that admits the durable host, the carrier's
1523
+ # relocation grammar, resolver and maintainer emitter (#130), and the bootstrap's explicit
1524
+ # --native-module ingress (#130 P2).
1525
+ run_vitest pi-extensions/meta-bridge-pi-durable.test.ts pi-extensions/lib/pi-durable-runtime.test.ts pi-extensions/lib/pi-durable-fresh-preflight.test.ts pi-extensions/lib/entwurf-deliverability.owner-join.test.ts pi-extensions/lib/pi-durable-carrier.test.ts pi-extensions/lib/pi-durable-native-module.test.ts
1496
1526
  }
1497
1527
 
1498
1528
  smoke_mux_fresh_call_live() {
@@ -2168,67 +2198,57 @@ assert.equal(geminiBundled, undefined, 'package.json must not bundle @google/gem
2168
2198
  // heredoc (which is indented, so won't match `^...`).
2169
2199
  const runSh = readFileSync('run.sh', 'utf8');
2170
2200
 
2171
- // pi peer/dev alignment (#26 / 0.8.0 dep-alignment gate). The three
2172
- // @earendil-works/pi-* devDeps must pin one identical version, and that
2173
- // version must match the check-pack-install peer-install pins below
2174
- // (`pnpm add @earendil-works/pi-ai@X ...`). Without this, a pi bump could
2175
- // drift package.json devDeps away from the fresh-temp install smoke and
2176
- // the "dependency alignment gate" would not actually verify pi.
2177
- const piAi = pkg.devDependencies?.['@earendil-works/pi-ai'];
2178
- const piCoding = pkg.devDependencies?.['@earendil-works/pi-coding-agent'];
2179
- const piTui = pkg.devDependencies?.['@earendil-works/pi-tui'];
2180
- // #120 P2: pi-agent-core is now DIRECTLY imported by test/pi-queue.oracle.test.ts, which proves
2181
- // what the control-socket receipt says about a receiver's queues. An oracle pinned to a DIFFERENT
2182
- // pi than the one this repo targets would answer honestly about a pi nobody runs, so its exact
2183
- // dev pin moves with the floor or the floor is not a floor.
2184
- const piCore = pkg.devDependencies?.['@earendil-works/pi-agent-core'];
2185
- assert.ok(piAi, 'package.json devDependencies must pin @earendil-works/pi-ai');
2201
+ // pi SDK alignment (#26 / 0.8.0 dep-alignment gate; #130 production set). Since 0.32 the pi SDK is
2202
+ // a PRODUCTION dependency set — the pi-durable carrier runs against it — declared once, exactly, in
2203
+ // pi/pi-durable/overlay/upstream-pin.json (`sdk.members` + `sdk.direct`). package.json must carry
2204
+ // exactly that set in `dependencies`, and no @earendil-works package as a dev, peer or optional
2205
+ // dependency beside it: one name in two dependency classes is install behaviour nothing here has
2206
+ // measured. The members are one SDK generation, and that version is the FLOOR every other surface
2207
+ // below names (the check-pack-install constellation, the workspace closure, the docs) — the host
2208
+ // range `>=<floor> <<major>.<minor+1>` is derived from it, never retyped.
2209
+ const durablePin = JSON.parse(readFileSync('pi/pi-durable/overlay/upstream-pin.json', 'utf8'));
2210
+ const sdkMembers = durablePin.sdk?.members ?? {};
2211
+ const sdkDirect = durablePin.sdk?.direct ?? {};
2212
+ const piAi = sdkMembers['@earendil-works/pi-ai'];
2213
+ assert.ok(/^\d+\.\d+\.\d+$/.test(piAi ?? ''), 'pi/pi-durable/overlay/upstream-pin.json sdk.members must pin @earendil-works/pi-ai exactly');
2214
+ // #120 P2: pi-agent-core is DIRECTLY imported by test/pi-queue.oracle.test.ts, which proves what the
2215
+ // control-socket receipt says about a receiver's queues. An oracle pinned to a DIFFERENT pi than the
2216
+ // one this repo targets would answer honestly about a pi nobody runs, so its exact pin moves with
2217
+ // the floor or the floor is not a floor.
2218
+ const piCore = pkg.dependencies?.['@earendil-works/pi-agent-core'];
2186
2219
  assert.equal(piCore, piAi,
2187
2220
  `[QK:PI-CORE-PIN-TRACKS-FLOOR] @earendil-works/pi-agent-core (${piCore}) must match @earendil-works/pi-ai (${piAi}) — the queue oracle imports it directly to prove what the control-socket receipt claims about a receiver's queues, so a pin that drifts off the floor would answer honestly about a pi nobody here runs`);
2188
- assert.equal(piCoding, piAi,
2189
- `@earendil-works/pi-coding-agent (${piCoding}) must match @earendil-works/pi-ai (${piAi})`);
2190
- assert.equal(piTui, piAi,
2191
- `@earendil-works/pi-tui (${piTui}) must match @earendil-works/pi-ai (${piAi})`);
2221
+ for (const [name, ver] of Object.entries(sdkMembers)) {
2222
+ assert.equal(ver, piAi, `pi/pi-durable/overlay/upstream-pin.json: sdk member ${name}@${ver} is not the one SDK generation ${piAi}`);
2223
+ }
2224
+ for (const [name, ver] of Object.entries({ ...sdkMembers, ...sdkDirect })) {
2225
+ assert.equal(pkg.dependencies?.[name], ver,
2226
+ `[QK:PI-SDK-PRODUCTION-SET] package.json dependencies ${name} (${pkg.dependencies?.[name]}) must be exactly the pinned ${ver} (pi/pi-durable/overlay/upstream-pin.json sdk) — the carrier runs against the package's own production set, not whatever a host provides`);
2227
+ }
2228
+ for (const name of Object.keys(pkg.dependencies ?? {}).filter((n) => n.startsWith('@earendil-works/'))) {
2229
+ assert.ok(name in sdkMembers, `package.json dependencies ${name} is not a declared sdk member — declare it in the pin or remove it`);
2230
+ }
2231
+ for (const field of ['devDependencies', 'peerDependencies', 'optionalDependencies', 'peerDependenciesMeta']) {
2232
+ const dual = Object.keys(pkg[field] ?? {}).filter((n) => n.startsWith('@earendil-works/'));
2233
+ assert.deepEqual(dual, [], `[QK:PI-SDK-NO-DUAL-DECLARATION] package.json ${field} must carry no @earendil-works package beside the production set (found ${dual.join(', ')}) — one name in two dependency classes is install behaviour nothing here has measured`);
2234
+ }
2192
2235
 
2193
- // check-pack-install peer-install pins (quoted `@earendil-works/pi-*@<ver>`
2236
+ // check-pack-install install pins (quoted `@earendil-works/pi-*@<ver>`
2194
2237
  // args; the `\d`-anchored version avoids matching this regex literal itself).
2195
2238
  const peerAi = runSh.match(/"@earendil-works\/pi-ai@(\d[\d.]*)"/)?.[1];
2196
2239
  const peerCoding = runSh.match(/"@earendil-works\/pi-coding-agent@(\d[\d.]*)"/)?.[1];
2197
2240
  const peerTui = runSh.match(/"@earendil-works\/pi-tui@(\d[\d.]*)"/)?.[1];
2198
2241
  assert.equal(peerAi, piAi,
2199
- `run.sh check-pack-install pi-ai peer pin (${peerAi}) must match package.json devDep (${piAi})`);
2242
+ `run.sh check-pack-install pi-ai install pin (${peerAi}) must match the pinned SDK (${piAi})`);
2200
2243
  assert.equal(peerCoding, piAi,
2201
- `run.sh check-pack-install pi-coding-agent peer pin (${peerCoding}) must match (${piAi})`);
2244
+ `run.sh check-pack-install pi-coding-agent install pin (${peerCoding}) must match (${piAi})`);
2202
2245
  assert.equal(peerTui, piAi,
2203
- `run.sh check-pack-install pi-tui peer pin (${peerTui}) must match (${piAi})`);
2204
-
2205
- // peerDependencies must be a CLOSED range (0.11 Stage 0, drift-proofing): the
2206
- // floor tracks the devDep pin so a consumer can't install against a pi lacking
2207
- // the public trust exports the bridge imports at the pinned minor, AND an upper
2208
- // bound at the next minor stops a fresh install from silently pulling a future
2209
- // pi (past the declared ceiling — 0.87+ at the current 0.86.0 pin) whose
2210
- // internal export surface has drifted from the one we typecheck against.
2211
- // pi moves its public surface every minor (the 0.79→0.80 getModels→provider-
2212
- // factory churn is exactly this, and 0.86 is the same shape again: a custom
2213
- // provider's streamSimple now receives a branded TranscriptContext instead of a
2214
- // raw Context, with systemPrompt/tools folded into a leading system message), so
2215
- // an open `>=` floor is exactly how the next installer re-acquires the drift.
2216
- // The floor is also the HARD MINIMUM a consumer install resolves: at `>=0.86.0`
2217
- // an existing 0.85.1 host is upgraded, not kept.
2218
- // Expected shape: `>=<devDep> <<major>.<minor+1>` (e.g. `>=0.86.0 <0.87`, `>=1.0.0 <1.1`).
2219
- // Pi 1.0 did not change the rule: an unverified minor is still unverified, so the ceiling
2220
- // stays the next minor of the pin's own major.
2246
+ `run.sh check-pack-install pi-tui install pin (${peerTui}) must match (${piAi})`);
2247
+
2248
+ // The supported HOST range a doc may declare is derived, closed: `>=<floor> <<major>.<minor+1>`.
2249
+ // pi moves its public surface every minor (the 0.79→0.80 getModels churn, the 0.86 TranscriptContext
2250
+ // change), so an unverified minor is still unverified; the ceiling stays the next minor of the floor.
2221
2251
  const [piMaj, piMin] = piAi.split('.').map(Number);
2222
- const expectedPeer = `>=${piAi} <${piMaj}.${piMin + 1}`;
2223
- const peerDepAi = pkg.peerDependencies?.['@earendil-works/pi-ai'];
2224
- const peerDepCoding = pkg.peerDependencies?.['@earendil-works/pi-coding-agent'];
2225
- const peerDepTui = pkg.peerDependencies?.['@earendil-works/pi-tui'];
2226
- assert.equal(peerDepAi, expectedPeer,
2227
- `package.json peerDependencies @earendil-works/pi-ai (${peerDepAi}) must be "${expectedPeer}" (devDep floor + next-minor ceiling)`);
2228
- assert.equal(peerDepCoding, expectedPeer,
2229
- `package.json peerDependencies @earendil-works/pi-coding-agent (${peerDepCoding}) must be "${expectedPeer}"`);
2230
- assert.equal(peerDepTui, expectedPeer,
2231
- `package.json peerDependencies @earendil-works/pi-tui (${peerDepTui}) must be "${expectedPeer}"`);
2232
2252
 
2233
2253
  // BASELINE DOCS (0.12.8). This gate was BORN reading a doc: 362becd added it
2234
2254
  // after the 21de0f9 drift and asserted README.md's codex-acp install pin against
@@ -2292,14 +2312,14 @@ for (const file of BASELINE_DOCS) {
2292
2312
  for (const [decl, floor, ceilMajor, ceilMinor] of text.matchAll(/>=\s?(\d+\.\d+\.\d+)\s?<\s?(\d+)\.(\d+)/g)) {
2293
2313
  rangeDecls++;
2294
2314
  assert.equal(floor, piAi,
2295
- `${file}: declared pi floor in "${decl}" is ${floor}, but the devDep pin is ${piAi} — a baseline doc may not advertise a version no gate drives`);
2315
+ `${file}: declared pi floor in "${decl}" is ${floor}, but the pinned SDK is ${piAi} — a baseline doc may not advertise a version no gate drives`);
2296
2316
  assert.equal(`${ceilMajor}.${ceilMinor}`, `${piMaj}.${piMin + 1}`,
2297
2317
  `${file}: declared pi ceiling in "${decl}" must be the next minor (${piMaj}.${piMin + 1})`);
2298
2318
  }
2299
2319
  // Exact install pins: `@earendil-works/pi-<pkg>@<version>`.
2300
2320
  for (const [decl, ver] of text.matchAll(/@earendil-works\/pi-(?:ai|coding-agent|tui)@(\d+\.\d+\.\d+)/g)) {
2301
2321
  exactDecls++;
2302
- assert.equal(ver, piAi, `${file}: install example "${decl}" pins ${ver}, but the devDep pin is ${piAi}`);
2322
+ assert.equal(ver, piAi, `${file}: install example "${decl}" pins ${ver}, but the pinned SDK is ${piAi}`);
2303
2323
  }
2304
2324
  }
2305
2325
  // Prose declarations carry the pin in sentences the two patterns above cannot
@@ -2313,7 +2333,7 @@ const PROSE_DECLS = [
2313
2333
  for (const [file, re, shape] of PROSE_DECLS) {
2314
2334
  const m = readFileSync(file, 'utf8').match(re);
2315
2335
  assert.ok(m, `${file}: the baseline sentence "${shape}" is gone — restore it or update check-dep-versions; a doc reword must not silently drop the pin from the gate`);
2316
- assert.equal(m[1], piAi, `${file}: "${shape}" declares ${m[1]}, but the devDep pin is ${piAi}`);
2336
+ assert.equal(m[1], piAi, `${file}: "${shape}" declares ${m[1]}, but the pinned SDK is ${piAi}`);
2317
2337
  }
2318
2338
  // ── #120 P4: ONE FLOOR, FOUR MECHANICAL SURFACES ──────────────────────────
2319
2339
  // The bump is mostly a number substitution, and that is exactly how a bump goes
@@ -2333,6 +2353,8 @@ const PI_CONSTELLATION = [
2333
2353
  '@earendil-works/pi-telemetry', '@earendil-works/chord',
2334
2354
  // `[측정 2026-09-30]` 0.99.1: pi-coding-agent's runtime deps gained the built-in extensions' packages.
2335
2355
  '@earendil-works/pi-codemode', '@earendil-works/pi-mcp',
2356
+ // #130: the durable library the pi-durable carrier imports — a production dependency since 0.32.
2357
+ '@earendil-works/pi-durable',
2336
2358
  ];
2337
2359
  const sameSet = (got, want, where) => {
2338
2360
  const missing = want.filter((n) => !got.includes(n));
@@ -2377,7 +2399,7 @@ if (!matcherPin) {
2377
2399
  const CURRENT_CONTRACT_DECLS = [
2378
2400
  ['plugins/herdr/README.md', /npm install -g "@earendil-works\/pi-coding-agent@(>=[\d.]+ <\d+\.\d+)"/, 'the herdr install line'],
2379
2401
  ['docs/mux-launch-rail.md', /현 supported range 는 `(>=[\d.]+ <\d+\.\d+)`/, "mux-launch-rail's current supported range"],
2380
- ['docs/acp-backend-rail.md', /devDep exact `(\d+\.\d+\.\d+)`, peer `(>=[\d.]+ <\d+\.\d+)`/, "the ACP support row's exact+range pair"],
2402
+ ['docs/acp-backend-rail.md', /SDK exact `(\d+\.\d+\.\d+)`, host `(>=[\d.]+ <\d+\.\d+)`/, "the ACP support row's exact+range pair"],
2381
2403
  ];
2382
2404
  for (const [file, re, shape] of CURRENT_CONTRACT_DECLS) {
2383
2405
  const m = readFileSync(file, 'utf8').match(re);
@@ -2388,14 +2410,14 @@ for (const [file, re, shape] of CURRENT_CONTRACT_DECLS) {
2388
2410
  }
2389
2411
  }
2390
2412
  assert.deepEqual(floorGaps, [],
2391
- `[QK:PI-FLOOR-SINGLE-SOURCE] the pi floor is declared on four mechanical surfaces — the package's exact dev pins and peer ceiling, run.sh's install constellation, the pnpm workspace closure, and the current-contract sentences in the docs — and every one of them must name the SAME floor. A bump that moves three of the four is the ordinary way this breaks, and the surface left behind is the one nothing else reads`);
2413
+ `[QK:PI-FLOOR-SINGLE-SOURCE] the pi floor is declared on four mechanical surfaces — the durable pin's SDK set with package.json's exact production dependencies, run.sh's install constellation, the pnpm workspace closure, and the current-contract sentences in the docs — and every one of them must name the SAME floor. A bump that moves three of the four is the ordinary way this breaks, and the surface left behind is the one nothing else reads`);
2392
2414
 
2393
2415
  // Guard the guard: if the patterns ever stop matching, the loops above pass
2394
2416
  // vacuously and the docs fall back OUT of the gate without a word.
2395
2417
  assert.ok(rangeDecls >= 5, `expected at least 5 pi range declarations across the baseline docs, found ${rangeDecls} — the doc scan matched (almost) nothing and would pass vacuously`);
2396
2418
  assert.ok(exactDecls >= 1, `expected at least 1 exact pi install pin in the baseline docs, found ${exactDecls}`);
2397
2419
 
2398
- console.log(`[check-dep-versions] ok — pi ${piAi} is coherent across package.json (devDeps + peer range), run.sh (peer-install pins), and ${BASELINE_DOCS.length} baseline docs (${rangeDecls} range + ${exactDecls} exact + ${PROSE_DECLS.length} prose declarations)`);
2420
+ console.log(`[check-dep-versions] ok — pi ${piAi} is coherent across the durable pin's SDK set, package.json (exact production dependencies, no dev/peer duplicate), run.sh (install pins), and ${BASELINE_DOCS.length} baseline docs (${rangeDecls} range + ${exactDecls} exact + ${PROSE_DECLS.length} prose declarations)`);
2399
2421
  EOF
2400
2422
  )
2401
2423
  }
@@ -2806,6 +2828,18 @@ check_pi_import_surface() {
2806
2828
  # `/providers/*`, and any deeper path stay FORBIDDEN (we use only `/compat`).
2807
2829
  # Do NOT widen this to a `providers/*` subpath — it typechecks but CANNOT
2808
2830
  # resolve under the extension loader.
2831
+ #
2832
+ # #130 CARRIER FENCE (replaces the #129 bootstrap exception, retired with the source runtime):
2833
+ # the pi-durable carrier (pi/pi-durable/carrier/*.js, upstream app files emitted by
2834
+ # scripts/emit-pi-durable-carrier.ts) is the ONE place pi-coding-agent internals are reached,
2835
+ # and only through the reserved `entwurf-pi-dist:` scheme its resolver answers. The fence is
2836
+ # declared and asserted here, not inferred from the root-only scan staying quiet:
2837
+ # - the scheme is quoted ONLY in carrier files and at its one definition
2838
+ # (pi/pi-durable/carrier-relocation.mjs);
2839
+ # - a carrier file names no pi-coding-agent specifier at all, and every bare specifier it does
2840
+ # name is `node:*`, a declared sdk member (root or subpath) or a declared direct dependency
2841
+ # (pi/pi-durable/overlay/upstream-pin.json `sdk`).
2842
+ # Proved on an external fixture repo below, like the work-surface denominator.
2809
2843
  # ONE corpus, TWO callers. The real scan and the denominator fixture must run the
2810
2844
  # SAME two lines, or the fixture would prove a command the gate does not use.
2811
2845
  pi_import_work_surface() {
@@ -2816,8 +2850,33 @@ check_pi_import_surface() {
2816
2850
  | xargs -0r grep -HnE "[\"'\`]@earendil-works/pi-(ai|coding-agent|tui)/" 2>/dev/null \
2817
2851
  | grep -vE "[\"'\`]@earendil-works/pi-ai/compat[\"'\`]" 2>/dev/null || true
2818
2852
  }
2853
+ # The carrier fence over the same work surface: prints one line per violation. $1 = the pin.
2854
+ pi_carrier_fence_scan() {
2855
+ pi_import_work_surface | node -e '
2856
+ const fs = require("node:fs");
2857
+ const pin = JSON.parse(fs.readFileSync(process.argv[1], "utf8"));
2858
+ const declared = new Set([...Object.keys(pin.sdk?.members ?? {}), ...Object.keys(pin.sdk?.direct ?? {})]);
2859
+ const files = fs.readFileSync(0, "utf8").split("\0").filter(Boolean);
2860
+ const carrier = (f) => /^pi\/pi-durable\/carrier\/[^/]+\.js$/.test(f);
2861
+ const scheme = /["\x27`]entwurf-pi-dist:/g;
2862
+ for (const f of files) {
2863
+ const text = fs.readFileSync(f, "utf8");
2864
+ const quoted = (text.match(scheme) ?? []).length;
2865
+ if (!carrier(f) && quoted > (f === "pi/pi-durable/carrier-relocation.mjs" ? 1 : 0)) {
2866
+ console.log(`${f}: quotes the reserved entwurf-pi-dist: scheme outside the carrier`);
2867
+ }
2868
+ if (!carrier(f)) continue;
2869
+ for (const m of text.matchAll(/^\s*(?:import|export|\})[^;]*?\bfrom\s*"([^"]+)";|^\s*import\s*"([^"]+)";/gm)) {
2870
+ const spec = m[1] ?? m[2];
2871
+ if (spec.startsWith("./") || spec.startsWith("entwurf-pi-dist:") || spec.startsWith("node:")) continue;
2872
+ const name = spec.startsWith("@") ? spec.split("/").slice(0, 2).join("/") : spec.split("/")[0];
2873
+ if (name === "@earendil-works/pi-coding-agent" || !declared.has(name)) console.log(`${f}: imports ${spec}, outside the declared carrier set`);
2874
+ }
2875
+ }
2876
+ ' "$1"
2877
+ }
2819
2878
 
2820
- local probe_dir probe_hits hits
2879
+ local probe_dir probe_hits probe_fence hits fence
2821
2880
  probe_dir=$(mktemp -d "${TMPDIR:-/tmp}/entwurf-pi-import-denominator.XXXXXX")
2822
2881
  (
2823
2882
  cd "$probe_dir" || exit 1
@@ -2825,8 +2884,19 @@ check_pi_import_surface() {
2825
2884
  printf '%s\n' 'import { getModels } from "@earendil-works/pi-ai/compat";' > tracked-allowed.ts
2826
2885
  git add tracked-allowed.ts
2827
2886
  printf '%s\n' 'import "@earendil-works/pi-ai/private-probe";' > untracked-forbidden.ts
2887
+ mkdir -p pi/pi-durable/carrier pi/pi-durable/overlay
2888
+ printf '%s\n' '{"sdk":{"members":{"@earendil-works/pi-ai":"1.0.4","@earendil-works/pi-durable":"1.0.4"},"direct":{"proper-lockfile":"4.1.2"}}}' \
2889
+ > pi/pi-durable/overlay/upstream-pin.json
2890
+ printf '%s\n' 'import { a } from "@earendil-works/pi-ai";' 'import { b } from "@earendil-works/pi-durable/tools";' \
2891
+ 'import lock from "proper-lockfile";' 'import { c } from "entwurf-pi-dist:core/x.js";' 'import { d } from "./sibling.js";' \
2892
+ > pi/pi-durable/carrier/allowed.js
2893
+ printf '%s\n' 'import { e } from "@earendil-works/pi-coding-agent";' > pi/pi-durable/carrier/coding-agent.js
2894
+ printf '%s\n' 'import { f } from "left-pad";' > pi/pi-durable/carrier/undeclared.js
2895
+ printf '%s\n' 'import { g } from "entwurf-pi-dist:core/x.js";' > elsewhere.mjs
2896
+ git add pi/pi-durable elsewhere.mjs
2828
2897
  ) || { fail "[check-pi-import-surface] could not build the denominator fixture in $probe_dir"; rm -rf "$probe_dir"; return 1; }
2829
2898
  probe_hits=$(cd "$probe_dir" && pi_import_scan)
2899
+ probe_fence=$(cd "$probe_dir" && pi_carrier_fence_scan pi/pi-durable/overlay/upstream-pin.json)
2830
2900
  rm -rf "$probe_dir"
2831
2901
  # ONE assertion, ONE claim token: the manifest contract requires the signature to
2832
2902
  # occur exactly once in this file, so both fixture conditions are folded into a
@@ -2838,6 +2908,15 @@ check_pi_import_surface() {
2838
2908
  fail "[QK:PIIMPORT-WORK-SURFACE] the denominator fixture disagrees with the corpus — untracked-forbidden reached=$saw_untracked_forbidden (want yes: a brand-new forbidden import must not escape until it is staged), tracked-allowed flagged=$saw_tracked_allowed (want no: the /compat exception must survive)"
2839
2909
  return 1
2840
2910
  fi
2911
+ local fence_allowed="no" fence_coding_agent="no" fence_undeclared="no" fence_elsewhere="no"
2912
+ grep -qF 'carrier/allowed.js' <<<"$probe_fence" && fence_allowed="yes"
2913
+ grep -qF 'carrier/coding-agent.js' <<<"$probe_fence" && fence_coding_agent="yes"
2914
+ grep -qF 'carrier/undeclared.js' <<<"$probe_fence" && fence_undeclared="yes"
2915
+ grep -qF 'elsewhere.mjs' <<<"$probe_fence" && fence_elsewhere="yes"
2916
+ if [ "$fence_allowed" != "no" ] || [ "$fence_coding_agent" != "yes" ] || [ "$fence_undeclared" != "yes" ] || [ "$fence_elsewhere" != "yes" ]; then
2917
+ fail "[QK:PIIMPORT-CARRIER-FENCE] the #130 carrier fence is not exact — declared imports in a carrier file flagged=$fence_allowed (want no), a pi-coding-agent specifier in a carrier file reached=$fence_coding_agent (want yes), an undeclared package in a carrier file reached=$fence_undeclared (want yes), the reserved scheme outside the carrier reached=$fence_elsewhere (want yes)"
2918
+ return 1
2919
+ fi
2841
2920
 
2842
2921
  hits=$(cd "$REPO_DIR" && pi_import_scan)
2843
2922
  if [ -n "$hits" ]; then
@@ -2845,7 +2924,13 @@ check_pi_import_surface() {
2845
2924
  echo "$hits"
2846
2925
  exit 1
2847
2926
  fi
2848
- ok "[check-pi-import-surface] pi references are root/compat-only across tracked + untracked-non-ignored ts/js; the untracked denominator is proved on an external fixture repo"
2927
+ fence=$(cd "$REPO_DIR" && pi_carrier_fence_scan pi/pi-durable/overlay/upstream-pin.json)
2928
+ if [ -n "$fence" ]; then
2929
+ echo "[check-pi-import-surface] FAIL: the #130 pi-durable carrier fence is broken:"
2930
+ echo "$fence"
2931
+ exit 1
2932
+ fi
2933
+ ok "[check-pi-import-surface] pi references are root/compat-only across tracked + untracked-non-ignored ts/js, and the pi-durable carrier reaches pi-coding-agent only through its reserved scheme with declared bare imports; the untracked denominator and the carrier fence are proved on an external fixture repo"
2849
2934
  }
2850
2935
 
2851
2936
  check_env_namespace() {
@@ -2872,65 +2957,26 @@ check_env_namespace() {
2872
2957
  }
2873
2958
 
2874
2959
  check_pi_runtime_version() {
2875
- # 0.11 Stage 0 (동결결정 9, runtime half): tsc catches a missing 0.80 export
2876
- # at dev time, but an installed environment can still resolve a pi OUTSIDE the
2877
- # supported range at runtime — older, where the named trust exports / 0.80
2878
- # provider-factory surface do not exist; or newer, where they have moved again.
2879
- # Verify VERSION against the DECLARED CLOSED RANGE (both ends, see below) via a
2880
- # DYNAMIC import of the package root only — never statically import a
2881
- # range-only symbol here, or this guard would crash before it can fail loud.
2960
+ # 0.11 Stage 0 (동결결정 9, runtime half), re-scoped by #130. tsc catches a missing export at
2961
+ # dev time, but an environment can still resolve a pi other than the one this repo pins at
2962
+ # runtime. Since 0.32 the pi SDK is a production dependency set (the pi-durable carrier runs
2963
+ # against it), so the runtime this checkout's tests, bridge and carrier import IS that set, and
2964
+ # the question is no longer "inside a range" but "exactly the pinned generation, edge by edge".
2965
+ # It is answered by the ONE native-set verifier the launcher, setup and the fresh preflight use
2966
+ # (pi-extensions/lib/pi-durable-runtime.ts): every declared member and direct dependency resolves
2967
+ # from the carrier, and every member's own member edge, at exactly the pinned version. A stale
2968
+ # development node_modules (an older pi left behind a bump) is a named sdk-absent/sdk-mismatch.
2882
2969
  #
2883
- # The floor is DERIVED from the package.json devDep pin, never a second literal.
2884
- # A hand-kept `const FLOOR = '<version>'` is a declaration no gate enforces:
2885
- # check-dep-versions binds the devDeps, the peer range, and the check-pack-install
2886
- # peer pins to one another, but it never saw this constant — so a pi bump that
2887
- # forgot it would leave the runtime gate still blessing the OLD floor, silently.
2888
- # That is the same "declared runtime ≠ verified runtime" split the 0.12.8
2889
- # check-pack-install fix closed; there must be exactly ONE pin to move.
2890
- (cd "$REPO_DIR" && node --input-type=module <<'EOF'
2891
- import { readFileSync } from 'node:fs';
2892
-
2893
- const pkg = JSON.parse(readFileSync('package.json', 'utf8'));
2894
- const FLOOR = pkg.devDependencies?.['@earendil-works/pi-coding-agent'];
2895
- if (typeof FLOOR !== 'string' || !/^\d+\.\d+\.\d+$/.test(FLOOR)) {
2896
- console.error(`[check-pi-runtime-version] FAIL: package.json devDependencies['@earendil-works/pi-coding-agent'] must be an EXACT x.y.z pin to serve as the runtime floor (got ${FLOOR ?? 'nothing'})`);
2897
- process.exit(1);
2898
- }
2899
- // The declared contract is a CLOSED range (`>=<devDep> <<major>.<minor+1>`, enforced on
2900
- // package.json by check-dep-versions), so the runtime check must be closed too.
2901
- // A floor-only comparison would bless a resolved pi ABOVE the ceiling — and an
2902
- // out-of-range pi is exactly the drift this cut exists to stop: 0.80.6 landed on
2903
- // the dev box while the repo still declared 0.80.3, and every gate stayed green.
2904
- // Verifying only half of a declared range is the same lie in the other direction.
2905
- const [FLOOR_MAJOR, FLOOR_MINOR] = FLOOR.split('.').map(Number);
2906
- const CEILING = `${FLOOR_MAJOR}.${FLOOR_MINOR + 1}.0`;
2907
- const cmp = (a, b) => {
2908
- const pa = a.split('.').map(Number), pb = b.split('.').map(Number);
2909
- for (let i = 0; i < 3; i++) { if ((pa[i] || 0) !== (pb[i] || 0)) return (pa[i] || 0) - (pb[i] || 0); }
2910
- return 0;
2911
- };
2912
- let VERSION;
2913
- try {
2914
- ({ VERSION } = await import('@earendil-works/pi-coding-agent'));
2915
- } catch (e) {
2916
- console.error(`[check-pi-runtime-version] FAIL: cannot import @earendil-works/pi-coding-agent root — ${e?.message ?? e}`);
2917
- process.exit(1);
2918
- }
2919
- if (typeof VERSION !== 'string') {
2920
- console.error('[check-pi-runtime-version] FAIL: pi root export VERSION is not a string');
2921
- process.exit(1);
2922
- }
2923
- if (cmp(VERSION, FLOOR) < 0) {
2924
- console.error(`[check-pi-runtime-version] FAIL: pi VERSION ${VERSION} < ${FLOOR} — the bridge is built and tested against the ${FLOOR} public/runtime surface (trust exports hasTrustRequiringProjectResources + ProjectTrustStore nearest-ancestor get, the 0.80 model-catalog API getModels reached via the deprecated /compat entrypoint — 0.80 moved the standalone root getModels there, and the extension loader resolves only /compat, NOT the providers/* factory subpath — provider registration surface, compaction semantics) that older pi lacks or behaves differently on. Bump @earendil-works/pi-*.`);
2925
- process.exit(1);
2926
- }
2927
- if (cmp(VERSION, CEILING) >= 0) {
2928
- console.error(`[check-pi-runtime-version] FAIL: pi VERSION ${VERSION} >= ${CEILING} — OUTSIDE the declared range (>=${FLOOR} <${CEILING.slice(0, -2)}). pi moves its public surface every minor (the 0.79→0.80 getModels→/compat churn), so a next-minor runtime is unverified by definition: no gate here has driven it. Either pin the repo to that pi (devDeps + peer range + baseline docs move together) or install the declared one.`);
2929
- process.exit(1);
2930
- }
2931
- console.log(`[check-pi-runtime-version] ok — pi VERSION ${VERSION} within the declared range (>=${FLOOR} <${CEILING.slice(0, -2)})`);
2932
- EOF
2933
- )
2970
+ # What this deliberately does NOT answer: the ordinary Pi HOST on PATH. That is a separate
2971
+ # subject with its own range (`pi_supported_range`, enforced by setup and install-user-scope on a
2972
+ # real `pi --version`), and a green here says nothing about it.
2973
+ local out rc=0
2974
+ out=$(cd "$REPO_DIR" && run_ts pi-extensions/lib/pi-durable-runtime.ts resolve 2>&1) || rc=$?
2975
+ if [ "$rc" -ne 0 ]; then
2976
+ fail "[check-pi-runtime-version] the pinned pi SDK set is not what this checkout resolves: ${out##*$'\n'}"
2977
+ return 1
2978
+ fi
2979
+ echo "[check-pi-runtime-version] ok — this checkout resolves exactly the pinned pi SDK set (native set; the PATH host is setup's subject)"
2934
2980
  }
2935
2981
 
2936
2982
  check_install_preflight() {
@@ -2976,8 +3022,8 @@ check_install_preflight() {
2976
3022
  # (a bare `test -d` would pass) with the other runtime deps symlinked live, but a
2977
3023
  # representative dep (@anthropic-ai/sdk) dangles. Asserts the dir-move blind spot
2978
3024
  # is closed AND that the failure names the broken dep. Uses a BUNDLED runtime dep
2979
- # because the @earendil-works/pi-* peer trio is loader-provided and no longer part
2980
- # of the install preflight probe set.
3025
+ # because the @earendil-works SDK set is not part of the install preflight probe
3026
+ # set (the carrier verifier owns its integrity).
2981
3027
  fake=$(mktemp -d); proj=$(mktemp -d)
2982
3028
  cp "$REPO_DIR/run.sh" "$fake/run.sh"
2983
3029
  mkdir -p "$fake/node_modules/@modelcontextprotocol" "$fake/node_modules/@agentclientprotocol" "$fake/node_modules/@anthropic-ai"
@@ -3508,19 +3554,25 @@ check_pack() {
3508
3554
  # Intent + policy live in NEXT.md Phase 2.3.
3509
3555
  section "pack invariants (dry-run)"
3510
3556
 
3511
- local json
3512
- # --silent so the `prepack` build (pnpm --silent run build-bridge → tsc) and
3513
- # npm's own lifecycle banner stay off stdout; otherwise they pollute the --json
3514
- # payload this parses. prepack runs on dry-run too, which is how dist lands in
3515
- # this gate's file list.
3557
+ local json pack_err
3558
+ # --silent keeps npm's own lifecycle banner off stdout, and prepack sends its build
3559
+ # output to stderr (`pnpm run build-bridge 1>&2`); either on stdout would pollute the
3560
+ # --json payload this parses. --foreground-scripts streams that stderr instead of
3561
+ # npm's pipe buffer, and it is CAPTURED, not discarded: a pnpm verify-deps refusal
3562
+ # inside prepack used to fail this gate with no reason anywhere. prepack runs on
3563
+ # dry-run too, which is how dist lands in this gate's file list.
3516
3564
  # with-dist-lock wraps the WHOLE pack (prepack build-bridge emit + npm's
3517
3565
  # post-build dist read) so a concurrent pack/build can't `rm -rf dist` mid-read
3518
3566
  # (the 2026-07-03 phantom "dist missing" race). The nested prepack build-bridge
3519
3567
  # is reentrant via ENTWURF_BUILD_LOCK_HELD.
3520
- json=$(cd "$REPO_DIR" && bash scripts/with-dist-lock.sh npm pack --dry-run --json --silent 2>/dev/null) || {
3568
+ pack_err=$(mktemp)
3569
+ json=$(cd "$REPO_DIR" && bash scripts/with-dist-lock.sh npm pack --dry-run --json --silent --foreground-scripts 2>"$pack_err") || {
3521
3570
  fail "[check-pack] npm pack --dry-run failed"
3571
+ sed 's/^/ /' "$pack_err" >&2
3572
+ rm -f "$pack_err"
3522
3573
  return 1
3523
3574
  }
3575
+ rm -f "$pack_err"
3524
3576
 
3525
3577
  local file_list
3526
3578
  file_list=$(node -e '
@@ -3615,6 +3667,21 @@ check_pack() {
3615
3667
  # check before assembling anything. The unit skeleton's package.json below is the other
3616
3668
  # half — the installer copies it, and `pi/` ships per-FILE.
3617
3669
  "mcp/entwurf-bridge/dist/pi-extensions/meta-bridge-omp.js"
3670
+ "mcp/entwurf-bridge/dist/pi-extensions/meta-bridge-pi-durable.js"
3671
+ "mcp/entwurf-bridge/dist/pi-extensions/lib/pi-durable-runtime.js"
3672
+ "mcp/entwurf-bridge/dist/pi-extensions/lib/pi-durable-fresh-preflight.js"
3673
+ "pi/pi-durable/bootstrap.mjs"
3674
+ "pi/pi-durable/overlay/upstream-pin.json"
3675
+ "pi/pi-durable/overlay/runtime-contacts.patch"
3676
+ "pi/pi-durable/carrier-resolver.mjs"
3677
+ "pi/pi-durable/carrier-relocation.mjs"
3678
+ "pi/pi-durable/carrier/runtime.js"
3679
+ "pi/pi-durable/carrier/tui.js"
3680
+ "pi/pi-durable/carrier/harness-setup.js"
3681
+ "pi/pi-durable/carrier/prompt.js"
3682
+ "pi/pi-durable/carrier/sessions.js"
3683
+ "pi/pi-durable/carrier/subagent.js"
3684
+ "pi/pi-durable/carrier/LICENSE"
3618
3685
  "pi/meta-bridge-omp/entwurf-meta-omp/package.json"
3619
3686
  "mcp/entwurf-bridge/dist/pi-extensions/lib/meta-session.js"
3620
3687
  "scripts/postinstall-chmod.cjs"
@@ -3743,7 +3810,7 @@ check_pack() {
3743
3810
  # which is exactly what property (1)'s `(_|$)` absorbs; all three shapes are fixtures
3744
3811
  # below so a future suffix change cannot pass vacuously.)
3745
3812
  pack_install_leaked_pi() {
3746
- grep '^@earendil-works+' | grep -Ev '@1\.0\.0(_|$)' || true
3813
+ grep '^@earendil-works+' | grep -Ev '@1\.0\.4(_|$)' || true
3747
3814
  }
3748
3815
 
3749
3816
  # Matcher self-test on SYNTHETIC lookalikes: a healthy install tree cannot exercise either
@@ -3763,15 +3830,15 @@ check_pack_pin_matcher() {
3763
3830
  # None may leak; the two lookalikes must — a PREFIX-EXTENDED version (`0.86.0-beta.1`,
3764
3831
  # the prerelease shape that an unanchored match would bless) and an off-pin version.
3765
3832
  matcher_probe=$(printf '%s\n' \
3766
- '@earendil-works+pi-ai@1.0.0' \
3767
- '@earendil-works+pi-ai@1.0.0_@modelcontextprotocol+sdk@1.29.0_zod@4.3.6' \
3768
- '@earendil-works+pi-ai@1.0.0_ws@8.21.3' \
3769
- '@earendil-works+pi-ai@1.0.0_@modelcontextprotocol+sdk@1.29.0_zod@4.3.6__ws@8.21.3_zod@4.3.6' \
3770
- '@earendil-works+pi-ai@1.0.0-beta.1' \
3833
+ '@earendil-works+pi-ai@1.0.4' \
3834
+ '@earendil-works+pi-ai@1.0.4_@modelcontextprotocol+sdk@1.29.0_zod@4.3.6' \
3835
+ '@earendil-works+pi-ai@1.0.4_ws@8.21.3' \
3836
+ '@earendil-works+pi-ai@1.0.4_@modelcontextprotocol+sdk@1.29.0_zod@4.3.6__ws@8.21.3_zod@4.3.6' \
3837
+ '@earendil-works+pi-ai@1.0.4-beta.1' \
3771
3838
  '@earendil-works+pi-agent-core@0.87.1' | pack_install_leaked_pi)
3772
- if [ "$matcher_probe" != '@earendil-works+pi-ai@1.0.0-beta.1
3839
+ if [ "$matcher_probe" != '@earendil-works+pi-ai@1.0.4-beta.1
3773
3840
  @earendil-works+pi-agent-core@0.87.1' ]; then
3774
- fail "[QK:PACK-INSTALL-PIN-MATCHER-BOUNDED] the pin-leak matcher must flag the prefix-extended 1.0.0-beta.1 and the off-pin 0.87.1 lookalikes, and pass 1.0.0 bare or with any measured peer-hash — got: ${matcher_probe:-<nothing leaked>}"
3841
+ fail "[QK:PACK-INSTALL-PIN-MATCHER-BOUNDED] the pin-leak matcher must flag the prefix-extended 1.0.4-beta.1 and the off-pin 0.87.1 lookalikes, and pass 1.0.4 bare or with any measured peer-hash — got: ${matcher_probe:-<nothing leaked>}"
3775
3842
  return 1
3776
3843
  fi
3777
3844
 
@@ -3779,9 +3846,9 @@ check_pack_pin_matcher() {
3779
3846
  # (0.85.0 onward, still true at 0.99.1). An off-pin chord MUST leak; the pinned one must not. A matcher narrowed
3780
3847
  # back to `^@earendil-works+pi-` sees nothing here and dies at this signature.
3781
3848
  matcher_probe=$(printf '%s\n' \
3782
- '@earendil-works+chord@1.0.0' \
3849
+ '@earendil-works+chord@1.0.4' \
3783
3850
  '@earendil-works+chord@0.87.1' \
3784
- '@earendil-works+pi-ai@1.0.0' | pack_install_leaked_pi)
3851
+ '@earendil-works+pi-ai@1.0.4' | pack_install_leaked_pi)
3785
3852
  if [ "$matcher_probe" != '@earendil-works+chord@0.87.1' ]; then
3786
3853
  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 is 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) — got: ${matcher_probe:-<nothing leaked>}"
3787
3854
  return 1
@@ -3790,6 +3857,233 @@ check_pack_pin_matcher() {
3790
3857
  echo "[check-pack-pin-matcher] ok — the pin-leak matcher is version-bounded (lookalikes leak, pinned version passes bare and with either measured peer-hash) and covers the whole @earendil-works closure (chord included)"
3791
3858
  }
3792
3859
 
3860
+ # #130 — the pi-durable carrier proved on ACTUAL npm consumers of the candidate tarball.
3861
+ # $1 tarball $2 the gate's npm sandbox $3 an existing Entwurf-only npm consumer root inside it.
3862
+ # Cells: L1s (Entwurf alone, $3), L1 (project-local beside an aligned host pi-coding-agent at the
3863
+ # pinned SDK version), L2 (an isolated `npm -g --prefix`), each through (a) the INSTALLED verifier
3864
+ # (exit 0, prints its own carrier-resolver) and (b) an independent consumer oracle; then the L3
3865
+ # NEGATIVE (an override binds pi-durable's pi-ai edge one patch behind: the installed verifier must
3866
+ # refuse it by name, while a bare import still succeeds); then (c) the public launch on L1s.
3867
+ # The oracle observes RESOLUTIONS (a resolve hook), not every filesystem read; it asserts each
3868
+ # resolved product file lies inside THIS consumer's node_modules — a sandbox ancestor is not the
3869
+ # boundary (#130 P1a review, Defect B) — and proves that predicate on a control: the installed
3870
+ # package of a NEIGHBOUR consumer in the same sandbox, which must be judged outside.
3871
+ # Same-version sharing within the consumer is an ordinary layout; copies are recorded, not refused.
3872
+ pack_install_durable_cells() {
3873
+ local tgz="$1" box="$2" l1s="$3" pin_ver oracle_dir cell root control pkg out rc
3874
+ local host_specs=() host_old_specs=() spec
3875
+ pin_ver=$(cd "$REPO_DIR" && node -p "require('./pi/pi-durable/overlay/upstream-pin.json').sdk.members['@earendil-works/pi-coding-agent']")
3876
+ # The HOST fixture needs a coherent generation too: coding-agent@1.0.4 alone
3877
+ # has ^1.0.4 SDK edges, which can resolve 1.1.0 (CI37694671506: pi-ai tar 404).
3878
+ # These are fixture roots from the owning pin, not product overrides or fallback.
3879
+ out=$(cd "$REPO_DIR" && node -e 'const p = require("./pi/pi-durable/overlay/upstream-pin.json"); console.log(Object.entries(p.sdk.members).map(([n,v]) => `${n}@${v}`).join("\n"));') || return 1
3880
+ mapfile -t host_specs <<<"$out"
3881
+ oracle_dir="$box/pd-oracle"; mkdir -p "$oracle_dir"
3882
+ cat > "$oracle_dir/oracle.mjs" <<'ORACLE'
3883
+ // node --import <pkg>/pi/pi-durable/carrier-resolver.mjs oracle.mjs <pkg> <consumer node_modules> <control file> <receipt>
3884
+ import fs from "node:fs";
3885
+ import { registerHooks } from "node:module";
3886
+ import path from "node:path";
3887
+ import { fileURLToPath, pathToFileURL } from "node:url";
3888
+ const [pkg, consumerRoot, control, receiptPath] = process.argv.slice(2);
3889
+ const seen = [];
3890
+ registerHooks({ resolve(specifier, context, next) { const r = next(specifier, context); seen.push({ specifier, parent: context.parentURL ?? null, url: r.url }); return r; } });
3891
+ const root = fs.realpathSync(consumerRoot);
3892
+ const inside = (file) => fs.realpathSync(file).startsWith(root + path.sep);
3893
+ const pin = JSON.parse(fs.readFileSync(path.join(pkg, "pi/pi-durable/overlay/upstream-pin.json"), "utf8"));
3894
+ const members = pin.sdk.members, direct = pin.sdk.direct;
3895
+ const carrier = path.join(pkg, "pi/pi-durable/carrier");
3896
+ const exportsOk = (await import(pathToFileURL(path.join(carrier, "runtime.js")).href)).openDurable instanceof Function
3897
+ && (await import(pathToFileURL(path.join(carrier, "tui.js")).href)).runDurableTui instanceof Function;
3898
+ const files = [...new Set(seen.filter((s) => s.url.startsWith("file:")).map((s) => fileURLToPath(s.url)))];
3899
+ const outside = files.filter((f) => !inside(f));
3900
+ const owner = (file) => { for (let d = path.dirname(fs.realpathSync(file)); d !== path.dirname(d); d = path.dirname(d)) { const m = path.join(d, "package.json"); if (fs.existsSync(m)) { const j = JSON.parse(fs.readFileSync(m, "utf8")); if (typeof j.name === "string") return { name: j.name, version: j.version, root: d }; } } return null; };
3901
+ const copies = {};
3902
+ for (const f of files) { const o = owner(f); if (o?.name.startsWith("@earendil-works/")) (copies[o.name] ??= new Set()).add(`${o.version}@${path.relative(root, o.root)}`); }
3903
+ const relocated = seen.filter((s) => s.specifier.startsWith("entwurf-pi-dist:"));
3904
+ const codingAgentRoots = new Set(relocated.map((s) => owner(fileURLToPath(s.url))?.root));
3905
+ // Independent of the product's resolver: the package directory a bare import from `fromFile` binds is the
3906
+ // nearest ancestor `node_modules/<name>` of the file's REAL directory, walked here by hand.
3907
+ const lookup = (name, fromFile) => { for (let d = fs.realpathSync(path.dirname(fromFile)); ; d = path.dirname(d)) { const m = path.join(d, "node_modules", name, "package.json"); if (fs.existsSync(m)) return m; if (d === path.dirname(d)) return null; } };
3908
+ const edge = (from, to, want, fromFile) => { const m = lookup(to, fromFile); if (!m) return { from, to, want, got: null, name: null }; const j = JSON.parse(fs.readFileSync(m, "utf8")); return { from, to, want, got: j.version, name: j.name, at: path.relative(root, fs.realpathSync(path.dirname(m))) }; };
3909
+ const base = path.join(carrier, "runtime.js");
3910
+ const edges = Object.entries({ ...members, ...direct }).map(([n, v]) => edge("carrier", n, v, base));
3911
+ // Every reachable member copy, at any depth, once per real directory.
3912
+ const queue = edges.filter((e) => e.to in members && e.at).map((e) => e.at), seenCopies = new Set();
3913
+ while (queue.length > 0) { const at = queue.shift(); if (seenCopies.has(at)) continue; seenCopies.add(at); const m = path.join(root, at, "package.json"); const j = JSON.parse(fs.readFileSync(m, "utf8")); for (const dep of Object.keys(j.dependencies ?? {}).filter((x) => x in members)) { const e = edge(j.name, dep, members[dep], m); edges.push(e); if (e.at) queue.push(e.at); } }
3914
+ const verdict = {
3915
+ exportsOk,
3916
+ resolvedFiles: files.length,
3917
+ outsideConsumer: outside,
3918
+ controlRejected: fs.existsSync(control) && !inside(control),
3919
+ loadedVersionsOk: Object.entries(copies).every(([n, set]) => [...set].every((c) => c.startsWith(`${members[n]}@`))),
3920
+ relocations: relocated.length,
3921
+ relocationOneDistInside: codingAgentRoots.size === 1 && relocated.every((s) => inside(fileURLToPath(s.url))),
3922
+ memberEdges: edges.filter((e) => e.from !== "carrier").length,
3923
+ memberCopiesWalked: seenCopies.size,
3924
+ edgeMismatches: edges.filter((e) => e.got !== e.want || e.name !== e.to),
3925
+ };
3926
+ fs.writeFileSync(receiptPath, JSON.stringify({ consumerRoot: root, verdict, copies: Object.fromEntries(Object.entries(copies).map(([n, s]) => [n, [...s]])), edges, resolutions: seen }, null, 1));
3927
+ const ok = verdict.exportsOk && verdict.outsideConsumer.length === 0 && verdict.controlRejected && verdict.loadedVersionsOk
3928
+ && verdict.relocations > 0 && verdict.relocationOneDistInside && verdict.memberEdges > 0 && verdict.edgeMismatches.length === 0;
3929
+ console.log(JSON.stringify({ ok, ...verdict, outsideConsumer: verdict.outsideConsumer.length, edgeMismatches: verdict.edgeMismatches.length }));
3930
+ process.exit(ok ? 0 : 1);
3931
+ ORACLE
3932
+
3933
+ mkdir -p "$box/pd-aligned" "$box/pd-global" "$box/pd-mismatch"
3934
+ out=$(cd "$box/pd-aligned" && npm install "$tgz" "${host_specs[@]}" --no-audit --no-fund 2>&1) || {
3935
+ fail "[check-pack-install] pi-durable L1 (aligned host) install failed:"; echo "$out" | tail -10 | sed 's/^/ /' >&2; return 1; }
3936
+ out=$(cd "$box" && npm install -g --prefix "$box/pd-global" "$tgz" --no-audit --no-fund 2>&1) || {
3937
+ fail "[check-pack-install] pi-durable L2 (isolated global) install failed:"; echo "$out" | tail -10 | sed 's/^/ /' >&2; return 1; }
3938
+
3939
+ for cell in L1s L1 L2; do
3940
+ case "$cell" in
3941
+ L1s) root="$l1s/node_modules" control="$box/pd-aligned/node_modules/@junghanacs/entwurf/package.json" ;;
3942
+ L1) root="$box/pd-aligned/node_modules" control="$box/pd-global/lib/node_modules/@junghanacs/entwurf/package.json" ;;
3943
+ L2) root="$box/pd-global/lib/node_modules" control="$l1s/node_modules/@junghanacs/entwurf/package.json" ;;
3944
+ esac
3945
+ pkg="$root/@junghanacs/entwurf"
3946
+ rc=0; out=$(cd "$box" && node "$pkg/mcp/entwurf-bridge/dist/pi-extensions/lib/pi-durable-runtime.js" resolve 2>&1) || rc=$?
3947
+ if [ "$rc" -ne 0 ] || [ "$(realpath "$out" 2>/dev/null)" != "$(realpath "$pkg/pi/pi-durable/carrier-resolver.mjs")" ]; then
3948
+ fail "[check-pack-install] pi-durable $cell: the INSTALLED verifier did not verify its own carrier (rc=$rc): $out"
3949
+ return 1
3950
+ fi
3951
+ rc=0; out=$(cd "$box" && node --import "$out" "$oracle_dir/oracle.mjs" "$pkg" "$root" "$control" "$box/pd-oracle/$cell.json" 2>&1) || rc=$?
3952
+ if [ "$rc" -ne 0 ]; then
3953
+ fail "[QK:PACK-INSTALL-DURABLE-CONSUMER] [check-pack-install] pi-durable $cell: the carrier is not a coherent, consumer-contained load (receipt $box/pd-oracle/$cell.json): $out"
3954
+ return 1
3955
+ fi
3956
+ echo "[check-pack-install] pi-durable $cell: installed verifier PASS; consumer oracle $out"
3957
+ done
3958
+
3959
+ 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"
3960
+ out=$(cd "$box/pd-mismatch" && npm install "$tgz" --no-audit --no-fund 2>&1) || {
3961
+ fail "[check-pack-install] pi-durable L3 (mismatch control) install failed:"; echo "$out" | tail -10 | sed 's/^/ /' >&2; return 1; }
3962
+ pkg="$box/pd-mismatch/node_modules/@junghanacs/entwurf"
3963
+ rc=0; out=$(cd "$box" && node "$pkg/mcp/entwurf-bridge/dist/pi-extensions/lib/pi-durable-runtime.js" resolve 2>&1) || rc=$?
3964
+ 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
3965
+ 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"
3966
+ return 1
3967
+ fi
3968
+ 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=$?
3969
+ if [ "$rc" -ne 0 ] || [ "$out" != "function" ]; then
3970
+ fail "[check-pack-install] pi-durable L3: the control premise failed — the mixed tree was expected to still IMPORT (rc=$rc): $out"
3971
+ return 1
3972
+ fi
3973
+ echo "[check-pack-install] pi-durable L3: the mixed tree still imports, and the installed verifier refuses it as pi-durable-sdk-mismatch"
3974
+
3975
+ # L4 — a THIRD-depth mismatch control on the same coherence axis. `[측정 2026-10-07]` npm 11 does not
3976
+ # produce this layout from an override (nested strategy included, it dedupes pi-durable's pi-ai to the
3977
+ # shared copy and overrides that), so it is CONSTRUCTED from real package bytes inside the already
3978
+ # proved L2 consumer: pi-durable gets its own same-version copy of the installed pi-ai, whose
3979
+ # pi-telemetry is the real published 1.0.3. Both the installed verifier and the independent oracle
3980
+ # must name the depth-3 edge; a walk that stopped at the carrier's own members would pass it.
3981
+ local l4_root="$box/pd-global/lib/node_modules" l4_tel="$box/pd-tel-103" l4_tel_ver="1.0.3"
3982
+ pkg="$l4_root/@junghanacs/entwurf"
3983
+ mkdir -p "$l4_tel" "$pkg/node_modules/@earendil-works/pi-durable/node_modules/@earendil-works"
3984
+ out=$(cd "$l4_tel" && npm pack "@earendil-works/pi-telemetry@$l4_tel_ver" --silent 2>&1 && tar -xzf ./*.tgz 2>&1) || {
3985
+ fail "[check-pack-install] could not fetch the real pi-telemetry 1.0.3 for the L4 control:"; echo "$out" | tail -5 | sed 's/^/ /' >&2; return 1; }
3986
+ local l4_ai="$pkg/node_modules/@earendil-works/pi-durable/node_modules/@earendil-works/pi-ai"
3987
+ cp -a "$(realpath "$pkg/node_modules/@earendil-works/pi-ai")" "$l4_ai"
3988
+ mkdir -p "$l4_ai/node_modules/@earendil-works"
3989
+ cp -a "$l4_tel/package" "$l4_ai/node_modules/@earendil-works/pi-telemetry"
3990
+ rc=0; out=$(cd "$box" && node "$pkg/mcp/entwurf-bridge/dist/pi-extensions/lib/pi-durable-runtime.js" resolve 2>&1) || rc=$?
3991
+ if [ "$rc" -ne 3 ] || ! grep -q "pi-durable-sdk-mismatch: @earendil-works/pi-ai → @earendil-works/pi-telemetry wants $pin_ver, binds 1.0.3 at $(realpath "$l4_ai")/node_modules/@earendil-works/pi-telemetry" <<<"$out"; then
3992
+ fail "[QK:PACK-INSTALL-DURABLE-DEPTH3-REFUSED] [check-pack-install] pi-durable L4: a depth-3 pi-telemetry 1.0.3 under pi-durable's own pi-ai copy must be refused by name (rc=$rc): $out"
3993
+ return 1
3994
+ fi
3995
+ rc=0; out=$(cd "$box" && node --import "$pkg/pi/pi-durable/carrier-resolver.mjs" "$oracle_dir/oracle.mjs" "$pkg" "$l4_root" "$l1s/node_modules/@junghanacs/entwurf/package.json" "$box/pd-oracle/L4.json" 2>&1) || rc=$?
3996
+ if [ "$rc" -eq 0 ] || ! grep -q '"edgeMismatches":1' <<<"$out"; then
3997
+ fail "[check-pack-install] pi-durable L4: the independent oracle did not see exactly the one depth-3 mismatch (rc=$rc): $out"
3998
+ return 1
3999
+ fi
4000
+ echo "[check-pack-install] pi-durable L4: a constructed depth-3 pi-telemetry 1.0.3 is refused by the installed verifier and seen by the independent oracle"
4001
+
4002
+ # (c) The public launch on L1s, through the installed bin: verifier → resolver → bootstrap → TUI
4003
+ # module → the carrier's real openDurable on the pinned SDK, whose own model resolution
4004
+ # (`findInitialAgentModel`) refuses a provider no catalog registers — `[측정 2026-10-07]` the
4005
+ # upstream message, before the overlay's exact-model check is reached. Offline (PI_OFFLINE),
4006
+ # sandbox HOME/agent dir, no credential; the contact's bridge
4007
+ # is spawned only at the birth, which this refusal precedes, so no citizen may exist after it.
4008
+ local launch_home="$box/pd-launch-home" launch_cwd="$box/pd-launch-cwd" launch_env=()
4009
+ mkdir -p "$launch_home/.pi/agent" "$launch_cwd"
4010
+ mapfile -t launch_env < <(pack_install_consumer_env "$launch_home")
4011
+ rc=0; out=$(cd "$launch_cwd" && env "${launch_env[@]}" PI_OFFLINE=1 "$l1s/node_modules/.bin/entwurf" pi-durable --provider entwurf-pack-probe --model no-such-model --width task-wide 2>&1 </dev/null) || rc=$?
4012
+ if [ "$rc" -eq 0 ] || ! grep -q 'Could not resolve model: Unknown provider "entwurf-pack-probe"' <<<"$out" || ! grep -q "at openDurable (file://.*/pi/pi-durable/carrier/runtime.js" <<<"$out" || grep -qE "pi-durable-(package-incomplete|carrier-drift|sdk-absent|sdk-mismatch)|ERR_MODULE_NOT_FOUND|ERR_UNSUPPORTED_ESM_URL_SCHEME" <<<"$out"; then
4013
+ fail "[QK:PACK-INSTALL-DURABLE-PUBLIC-LAUNCH] [check-pack-install] the installed 'entwurf pi-durable' did not reach the carrier openDurable's own model refusal on the pinned SDK (rc=$rc):"
4014
+ echo "$out" | tail -15 | sed 's/^/ /' >&2
4015
+ return 1
4016
+ fi
4017
+ if ls "$launch_home/.pi/agent/meta-sessions" 2>/dev/null | grep -q .; then
4018
+ fail "[check-pack-install] the refused durable launch minted a meta record — a refusal before birth must leave no citizen"
4019
+ return 1
4020
+ fi
4021
+ echo "[check-pack-install] pi-durable public launch: the installed verb ran the carrier's openDurable on the pinned SDK, whose own model resolution refused the unregistered provider, before any birth"
4022
+
4023
+ # (c2) #130 P2: ONE explicit operator module on the INSTALLED package. The synthetic fixture is
4024
+ # copied from this checkout into the sandbox — it never ships (test/ is outside the package files).
4025
+ # Its initialization appends `init` to the receipt this cell names; the same public launch must then
4026
+ # reach the same carrier model refusal, so the installed bootstrap loaded the module before the
4027
+ # runtime and the native registry installed its extension (carrier/runtime.js 146) before that
4028
+ # refusal (runtime.js 162). A module claiming an extension name the app installs itself is refused by
4029
+ # name before anything opens: no durable session directory, no citizen, in a fresh sandbox home.
4030
+ # Native SDK file opens here are ordinary (the module runs in the native process); none is judged.
4031
+ local mod_dir="$box/pd-module" mod_home="$box/pd-module-home" mod_env=()
4032
+ mkdir -p "$mod_dir" "$mod_home/.pi/agent"
4033
+ cp "$REPO_DIR/test/fixtures/pi-durable-native-module/mock.mjs" "$mod_dir/mock.mjs"
4034
+ printf 'export default { name: "subagent" };\n' > "$mod_dir/reserved.mjs"
4035
+ rc=0; out=$(cd "$launch_cwd" && env "${launch_env[@]}" PI_OFFLINE=1 MOCK_NATIVE_RECEIPT="$mod_dir/receipt.log" "$l1s/node_modules/.bin/entwurf" pi-durable --provider entwurf-pack-probe --model no-such-model --width task-wide --native-module "$mod_dir/mock.mjs" 2>&1 </dev/null) || rc=$?
4036
+ if [ "$rc" -eq 0 ] || [ "$(cat "$mod_dir/receipt.log" 2>/dev/null)" != "init" ] || ! grep -q 'Could not resolve model: Unknown provider "entwurf-pack-probe"' <<<"$out" || ! grep -q "at openDurable (file://.*/pi/pi-durable/carrier/runtime.js" <<<"$out" || grep -q "native-module-" <<<"$out"; then
4037
+ fail "[QK:PACK-INSTALL-DURABLE-NATIVE-MODULE] [check-pack-install] the installed 'entwurf pi-durable --native-module' did not initialize the module once and then reach the carrier openDurable's own model refusal (rc=$rc, receipt $(cat "$mod_dir/receipt.log" 2>/dev/null | tr '\n' ' ')):"
4038
+ echo "$out" | tail -15 | sed 's/^/ /' >&2
4039
+ return 1
4040
+ fi
4041
+ mapfile -t mod_env < <(pack_install_consumer_env "$mod_home")
4042
+ rc=0; out=$(cd "$launch_cwd" && env "${mod_env[@]}" PI_OFFLINE=1 "$l1s/node_modules/.bin/entwurf" pi-durable --provider entwurf-pack-probe --model no-such-model --width task-wide --native-module "$mod_dir/reserved.mjs" 2>&1 </dev/null) || rc=$?
4043
+ if [ "$rc" -eq 0 ] || ! grep -q "native-module-name-reserved: .*subagent" <<<"$out" || grep -q "Unknown provider" <<<"$out" || [ -e "$mod_home/.pi/agent/experimental" ] || [ -e "$mod_home/.pi/agent/meta-sessions" ]; then
4044
+ fail "[QK:PACK-INSTALL-DURABLE-NATIVE-MODULE] [check-pack-install] the installed launch did not refuse a reserved-name module by name before anything opened (rc=$rc):"
4045
+ echo "$out" | tail -15 | sed 's/^/ /' >&2
4046
+ return 1
4047
+ fi
4048
+ echo "[check-pack-install] pi-durable native module: the installed bootstrap initialized one explicit module before the carrier's runtime (then the same model refusal), and refused a reserved extension name before anything opened"
4049
+
4050
+ # (d) Host vs native are two subjects. The installed package's native SDK set is the pin (above);
4051
+ # the ordinary Pi HOST on PATH is judged by its OWN real `pi --version` against the derived range.
4052
+ # Real host binaries, no forged version: a sandbox install of the previous pi (1.0.2) must make the
4053
+ # installed setup's pi row a named FAIL with ZERO Pi wiring while pi-durable still PASSes, and the
4054
+ # aligned host binary (the pinned version, installed above for L1) must be inside the range.
4055
+ # The previous host version is a variable, not a quoted `@pkg@x.y.z` spec: check-dep-versions reads
4056
+ # those specs as this repo's install pins, and this one is deliberately off the floor.
4057
+ local host_home host_proj host_old="$box/pd-host-old" host_env=() host_old_ver="1.0.2"
4058
+ mkdir -p "$host_old"
4059
+ for spec in "${host_specs[@]}"; do host_old_specs+=("${spec%@*}@$host_old_ver"); done
4060
+ out=$(cd "$host_old" && npm install "${host_old_specs[@]}" --no-audit --no-fund 2>&1) || {
4061
+ fail "[check-pack-install] could not install the previous-host pi fixture (1.0.2):"; echo "$out" | tail -10 | sed 's/^/ /' >&2; return 1; }
4062
+ for cell in old aligned; do
4063
+ host_home="$box/pd-host-$cell-home"; host_proj="$box/pd-host-$cell-proj"
4064
+ mkdir -p "$host_home/.pi/agent" "$host_proj"
4065
+ mapfile -t host_env < <(pack_install_consumer_env "$host_home")
4066
+ local host_bin="$host_old/node_modules/.bin/pi"
4067
+ [ "$cell" = aligned ] && host_bin="$box/pd-aligned/node_modules/.bin/pi"
4068
+ rc=0; out=$(cd "$box" && env "${host_env[@]}" PI_BIN="$host_bin" CLAUDE_BIN="$box/definitely-absent" AGY_BIN="$box/definitely-absent" \
4069
+ COPILOT_BIN="$box/definitely-absent" OMP_BIN="$box/definitely-absent" CODEX_BIN="$box/definitely-absent" \
4070
+ "$l1s/node_modules/.bin/entwurf" setup "$host_proj" 2>&1) || rc=$?
4071
+ if ! grep -q "pi-durable: PASS" <<<"$out"; then
4072
+ fail "[check-pack-install] host $cell: the native pi-durable row must stay PASS whatever the PATH host is:"; echo "$out" | tail -20 | sed 's/^/ /' >&2; return 1
4073
+ fi
4074
+ if [ "$cell" = old ]; then
4075
+ if [ "$rc" -eq 0 ] || ! grep -q "pi: FAIL — detected pi 1.0.2 is outside the supported range >=$pin_ver <1.1" <<<"$out" || [ -e "$host_proj/.pi" ] || [ -e "$host_home/.pi/agent/settings.json" ]; then
4076
+ fail "[QK:PACK-INSTALL-HOST-RANGE-SEPARATE] [check-pack-install] a real pi 1.0.2 host beside the $pin_ver native set must be a named pi FAIL with zero Pi wiring and a non-green setup (rc=$rc):"
4077
+ echo "$out" | tail -20 | sed 's/^/ /' >&2
4078
+ return 1
4079
+ fi
4080
+ elif grep -q "outside the supported range" <<<"$out"; then
4081
+ fail "[check-pack-install] host aligned: the pinned-version host binary was judged outside the range:"; echo "$out" | tail -20 | sed 's/^/ /' >&2; return 1
4082
+ fi
4083
+ done
4084
+ echo "[check-pack-install] host vs native: a real pi 1.0.2 host is a named pi FAIL with no wiring while pi-durable PASSes; the $pin_ver host binary is inside the range"
4085
+ }
4086
+
3793
4087
  # The writable roots of ONE consumer sandbox, shared by every consumer subcall of
3794
4088
  # check-pack-install that drives an installed `run.sh` writer. The writers resolve their agent
3795
4089
  # dir as `${PI_CODING_AGENT_DIR:-$HOME/.pi/agent}` (`resolve`/`install` below), so swapping HOME
@@ -3981,6 +4275,21 @@ _check_pack_install_impl() {
3981
4275
  # check before assembling anything. The unit skeleton's package.json below is the other
3982
4276
  # half — the installer copies it, and `pi/` ships per-FILE.
3983
4277
  "mcp/entwurf-bridge/dist/pi-extensions/meta-bridge-omp.js"
4278
+ "mcp/entwurf-bridge/dist/pi-extensions/meta-bridge-pi-durable.js"
4279
+ "mcp/entwurf-bridge/dist/pi-extensions/lib/pi-durable-runtime.js"
4280
+ "mcp/entwurf-bridge/dist/pi-extensions/lib/pi-durable-fresh-preflight.js"
4281
+ "pi/pi-durable/bootstrap.mjs"
4282
+ "pi/pi-durable/overlay/upstream-pin.json"
4283
+ "pi/pi-durable/overlay/runtime-contacts.patch"
4284
+ "pi/pi-durable/carrier-resolver.mjs"
4285
+ "pi/pi-durable/carrier-relocation.mjs"
4286
+ "pi/pi-durable/carrier/runtime.js"
4287
+ "pi/pi-durable/carrier/tui.js"
4288
+ "pi/pi-durable/carrier/harness-setup.js"
4289
+ "pi/pi-durable/carrier/prompt.js"
4290
+ "pi/pi-durable/carrier/sessions.js"
4291
+ "pi/pi-durable/carrier/subagent.js"
4292
+ "pi/pi-durable/carrier/LICENSE"
3984
4293
  "pi/meta-bridge-omp/entwurf-meta-omp/package.json"
3985
4294
  "mcp/entwurf-bridge/dist/pi-extensions/lib/meta-session.js"
3986
4295
  "scripts/postinstall-chmod.cjs"
@@ -4059,9 +4368,9 @@ _check_pack_install_impl() {
4059
4368
  # repo packages with; --ignore-workspace stops it from re-attaching
4060
4369
  # to our pnpm-workspace.yaml; --ignore-scripts blocks the husky
4061
4370
  # prepare hook (and any future install scripts) from running inside
4062
- # the consumer project. Peer deps are pinned to the CURRENT pi release
4063
- # baseline (the package.json devDep pin — the pins below are what
4064
- # check-dep-versions binds to that devDep) so the smoke matches the same
4371
+ # the consumer project. The pi SDK is pinned to the CURRENT pi release
4372
+ # baseline (the package.json exact production dependency — the pins below
4373
+ # are what check-dep-versions binds to it) so the smoke matches the same
4065
4374
  # shape an external pi user would have after `pi install`.
4066
4375
  local tmp npm_tmp
4067
4376
  tmp=$(mktemp -d -t entwurf-install-smoke.XXXXXX)
@@ -4122,20 +4431,28 @@ _check_pack_install_impl() {
4122
4431
  # @earendil-works/pi-codemode and @earendil-works/pi-mcp as runtime dependencies (the built-in
4123
4432
  # codemode and MCP extensions), so both are pinned beside the eight. pi-client and pi-protocol are
4124
4433
  # still published at 0.99.1 and keep their pins under the rule above.
4125
- echo "[check-pack-install] pnpm add into $tmp (with 1.0.x peers + chord + codemode + mcp + typebox)"
4434
+ # `[측정 2026-10-05]` at 1.0.2 the constellation is unchanged: `npm view <pkg>@1.0.2 dependencies`
4435
+ # gives pi-coding-agent {chord, pi-agent-core, pi-ai, pi-codemode, pi-mcp, pi-tui}, pi-ai
4436
+ # {pi-telemetry}, pi-client {chord, pi-protocol}, pi-protocol {chord}, all ten published at 1.0.2.
4437
+ # `[측정 2026-10-07]` at 1.0.4 the same edges hold, and #130 adds @earendil-works/pi-durable: the
4438
+ # library the pi-durable carrier imports, a production dependency of this package since 0.32 (its
4439
+ # own deps: chord, pi-ai). All eleven are published at 1.0.4. The tarball now DECLARES the nine-member
4440
+ # SDK set itself, so these pins are an aligned host install beside it, not peers it lacks.
4441
+ echo "[check-pack-install] pnpm add into $tmp (with the aligned 1.0.x SDK + chord + codemode + mcp + durable + typebox)"
4126
4442
  local install_log
4127
4443
  install_log=$(cd "$tmp" && pnpm add \
4128
4444
  "$tgz_path" \
4129
- "@earendil-works/pi-ai@1.0.0" \
4130
- "@earendil-works/pi-coding-agent@1.0.0" \
4131
- "@earendil-works/pi-tui@1.0.0" \
4132
- "@earendil-works/pi-agent-core@1.0.0" \
4133
- "@earendil-works/pi-client@1.0.0" \
4134
- "@earendil-works/pi-protocol@1.0.0" \
4135
- "@earendil-works/pi-telemetry@1.0.0" \
4136
- "@earendil-works/chord@1.0.0" \
4137
- "@earendil-works/pi-codemode@1.0.0" \
4138
- "@earendil-works/pi-mcp@1.0.0" \
4445
+ "@earendil-works/pi-ai@1.0.4" \
4446
+ "@earendil-works/pi-coding-agent@1.0.4" \
4447
+ "@earendil-works/pi-tui@1.0.4" \
4448
+ "@earendil-works/pi-agent-core@1.0.4" \
4449
+ "@earendil-works/pi-client@1.0.4" \
4450
+ "@earendil-works/pi-protocol@1.0.4" \
4451
+ "@earendil-works/pi-telemetry@1.0.4" \
4452
+ "@earendil-works/chord@1.0.4" \
4453
+ "@earendil-works/pi-codemode@1.0.4" \
4454
+ "@earendil-works/pi-mcp@1.0.4" \
4455
+ "@earendil-works/pi-durable@1.0.4" \
4139
4456
  "typebox@latest" \
4140
4457
  --ignore-workspace --ignore-scripts 2>&1) || {
4141
4458
  fail "[check-pack-install] pnpm add failed:"
@@ -4151,11 +4468,11 @@ _check_pack_install_impl() {
4151
4468
  local leaked_pi
4152
4469
  leaked_pi=$(ls "$tmp/node_modules/.pnpm" 2>/dev/null | pack_install_leaked_pi)
4153
4470
  if [ -n "$leaked_pi" ]; then
4154
- fail "[check-pack-install] UNVERIFIED pi runtime resolved into the install tree (expected only 1.0.0):"
4471
+ fail "[check-pack-install] UNVERIFIED pi runtime resolved into the install tree (expected only 1.0.4):"
4155
4472
  printf '%s\n' "$leaked_pi" | sed 's/^/ /' >&2
4156
4473
  return 1
4157
4474
  fi
4158
- echo "[check-pack-install] pi runtime tree pin verified: every @earendil-works package is 1.0.0 (chord included)"
4475
+ echo "[check-pack-install] pi runtime tree pin verified: every @earendil-works package is 1.0.4 (chord and pi-durable included)"
4159
4476
 
4160
4477
  # Resolve the installed package.json and confirm pi.extensions
4161
4478
  # arrived intact. If pi.extensions is empty or missing, the
@@ -4174,6 +4491,34 @@ _check_pack_install_impl() {
4174
4491
  }
4175
4492
  echo "[check-pack-install] installed: $probe"
4176
4493
 
4494
+ # #129 — the packaged durable bootstrap, imported FROM the installed tree. The tarball listing
4495
+ # above proves the file ships, not that its static imports reach the shipped compiled closure
4496
+ # under node_modules (where a checkout .ts import would be refused by the strip-types fence), nor
4497
+ # that the bridge entry it hands the adapter is this package's own dist. Import only: main() runs
4498
+ # solely when the file is the process entry, and the runtime/TUI are dynamic imports inside it, so
4499
+ # no runtime, TUI, birth or bridge starts here.
4500
+ local durable_proof
4501
+ durable_proof=$(cd "$tmp" && node --input-type=module -e "
4502
+ const { realpathSync } = await import('node:fs');
4503
+ const pkg = realpathSync(process.cwd() + '/node_modules/@junghanacs/entwurf');
4504
+ const m = await import(pkg + '/pi/pi-durable/bootstrap.mjs');
4505
+ for (const name of ['main', 'openDurableCitizen']) {
4506
+ if (typeof m[name] !== 'function') { console.error(name + ' is not an exported function'); process.exit(1); }
4507
+ }
4508
+ const want = pkg + '/mcp/entwurf-bridge/dist/mcp/entwurf-bridge/src/index.js';
4509
+ const entry = m.PACKAGED_BRIDGE_ENTRY;
4510
+ if (typeof entry !== 'string' || realpathSync(entry) !== want) {
4511
+ console.error('PACKAGED_BRIDGE_ENTRY ' + entry + ' is not the dist entry of the installed package (' + want + ')');
4512
+ process.exit(1);
4513
+ }
4514
+ console.log('exports main/openDurableCitizen; PACKAGED_BRIDGE_ENTRY is the installed dist entry');
4515
+ " 2>&1) || {
4516
+ fail "[check-pack-install] installed durable bootstrap import failed:"
4517
+ echo "$durable_proof" | sed 's/^/ /' >&2
4518
+ return 1
4519
+ }
4520
+ echo "[check-pack-install] installed durable bootstrap: $durable_proof"
4521
+
4177
4522
  # #116 M3-b1/b2 — THE ACTUAL-PACKAGE RUNTIME PROOF, THROUGH THE SHIPPED OWNER.
4178
4523
  # The Herdr plugin's runtime verifier decides whether a tree npm placed is a runtime the scoped
4179
4524
  # wiring may name: exact name@version, the compiled entry, all three required bins present AND
@@ -4236,8 +4581,8 @@ _check_pack_install_impl() {
4236
4581
  # and safe to run in CI. Output goes to stderr; capture both
4237
4582
  # streams with 2>&1.
4238
4583
  #
4239
- # The pi that loads the tarball is the PINNED peer this smoke just installed next to
4240
- # it ($tmp/node_modules/.bin/pi = the floor of the supported peer range), NEVER
4584
+ # The pi that loads the tarball is the PINNED SDK pi this smoke just installed next to
4585
+ # it ($tmp/node_modules/.bin/pi = the floor of the supported host range), NEVER
4241
4586
  # whatever `pi` the host happens to have on PATH. A gate may not READ the operator's
4242
4587
  # global install any more than it may WRITE it: PATH resolution made this gate green
4243
4588
  # on a dev box carrying a newer global pi and RED in CI, which carries no global pi
@@ -4260,14 +4605,14 @@ _check_pack_install_impl() {
4260
4605
  )
4261
4606
 
4262
4607
  # Assert the version: a gate that cannot name which pi it proved has proved nothing.
4263
- # package.json devDeps is the pin SSOT (check-dep-versions keeps the peer-install
4264
- # literals above in step with it).
4608
+ # The pinned SDK generation is the pin SSOT (package.json's exact production dependency, which
4609
+ # check-dep-versions binds to the durable pin and to the install literals above).
4265
4610
  local pi_bin="$tmp/node_modules/.bin/pi" pi_pin pi_ver
4266
4611
  if [ ! -x "$pi_bin" ]; then
4267
4612
  fail "[check-pack-install] pinned pi missing from the install-smoke tree ($pi_bin) — cannot run loader smoke"
4268
4613
  return 1
4269
4614
  fi
4270
- pi_pin=$(cd "$REPO_DIR" && node -p "require('./package.json').devDependencies['@earendil-works/pi-coding-agent']")
4615
+ pi_pin=$(cd "$REPO_DIR" && node -p "require('./package.json').dependencies['@earendil-works/pi-coding-agent']")
4271
4616
  pi_ver=$(cd "$tmp" && env "${pi_env[@]}" "$pi_bin" --version 2>&1 | head -1 | tr -d '[:space:]')
4272
4617
  if [ "$pi_ver" != "$pi_pin" ]; then
4273
4618
  fail "[check-pack-install] install-smoke pi is '$pi_ver', expected the pinned '$pi_pin' — the loader smoke would prove the wrong runtime"
@@ -4334,8 +4679,9 @@ _check_pack_install_impl() {
4334
4679
  # npm-managed neutral install regression — the README's PRIMARY install path is
4335
4680
  # now `npm install @junghanacs/entwurf` (NOT `pi install npm:...`). This layout
4336
4681
  # lands the package under node_modules, hoists runtime deps to the sibling
4337
- # node_modules, has no package-local node_modules, and the pi peer trio must be
4338
- # absent because pi is an optional adapter lane. The old cwd-relative
4682
+ # node_modules, has no package-local node_modules, and (since #130) carries the
4683
+ # pi SDK as a production dependency the bridge must still not import — the
4684
+ # pi-free boot below proves that under a deny hook. The old cwd-relative
4339
4685
  # preflight_dep_integrity rejected every hoisted-dep npm install; 0.12.0 then
4340
4686
  # additionally died because start.sh tried strip-types under node_modules.
4341
4687
  # Prove `entwurf`/`entwurf-bridge` bins exist, `run.sh install` writes settings
@@ -4425,6 +4771,10 @@ sys.exit(0 if any(isinstance(s,str) and s.endswith('/node_modules/@junghanacs/en
4425
4771
  fi
4426
4772
  echo "[check-pack-install] user-scope citizen regression pass (npm consumer: --entwurf-control loads from a foreign cwd)"
4427
4773
 
4774
+ # #130: the pi-durable carrier on actual npm consumers (L1s = this Entwurf-only root, L1 aligned
4775
+ # host, L2 isolated global, L3 mismatch control) and the public launch.
4776
+ pack_install_durable_cells "$tgz_path" "$npm_tmp" "$npmroot" || return 1
4777
+
4428
4778
  # Two-root ownership row (#86 C2, L1b extension): a SECOND npm root under the
4429
4779
  # SAME consumer HOME must not silently steal the user-scope registration the
4430
4780
  # first root owns — normal install refuses (naming takeover-user-scope) and the
@@ -4566,6 +4916,13 @@ sys.exit(0 if any(isinstance(s,str) and s.rstrip('/')== '$npm2_pkg' for s in src
4566
4916
  return 1
4567
4917
  fi
4568
4918
  done
4919
+ # #130: pi-durable is not a detected harness — its carrier and pi SDK set ship IN this package, so
4920
+ # the installed row must verify them from node_modules (PASS), never fall back to a zero-state SKIP.
4921
+ if ! grep -q "pi-durable: PASS" <<<"$setup_out"; then
4922
+ fail "[check-pack-install] installed all-absent setup did not verify the packaged pi-durable carrier and its SDK set (want 'pi-durable: PASS'):"
4923
+ echo "$setup_out" | tail -25 | sed 's/^/ /' >&2
4924
+ return 1
4925
+ fi
4569
4926
  if ! grep -q "bins: PASS — provided by npm bin linking" <<<"$setup_out"; then
4570
4927
  fail "[check-pack-install] installed setup must report stable bins PASS as npm-provided (capability present, not absent):"
4571
4928
  echo "$setup_out" | tail -25 | sed 's/^/ /' >&2
@@ -4920,29 +5277,55 @@ PY
4920
5277
  # surface. Two proofs in one: (1) the prepack dist JS boots under node_modules
4921
5278
  # with plain node — a strip-types fallback would crash with the exact error
4922
5279
  # above, so a parseable tools/list IS proof the dist path was taken; (2) the
4923
- # boot is pi-free — the @earendil-works peer trio is optional and must NOT be
4924
- # installed by the neutral npm path, so the eager closure stands up with pi absent.
5280
+ # boot is pi-free. Until #130 that was proved by ABSENCE (the pi peers were optional and the
5281
+ # neutral npm path did not install them). Since 0.32 the pi SDK is a production dependency, so
5282
+ # it is always installed and absence can no longer be the premise; the proof is now adversarial
5283
+ # in the other direction: the bridge boots under a preloaded resolve hook that REFUSES every
5284
+ # `@earendil-works/*` specifier, so an eager graph that imported pi would die at boot.
4925
5285
  local installed_start="$npmroot/node_modules/.bin/entwurf-bridge"
4926
5286
  local installed_dist="$npm_pkg/mcp/entwurf-bridge/dist/mcp/entwurf-bridge/src/index.js"
4927
5287
  if [ ! -f "$installed_dist" ]; then
4928
5288
  fail "[check-pack-install] installed bridge missing prebuilt dist (prepack did not emit into the tarball?): $installed_dist"
4929
5289
  return 1
4930
5290
  fi
4931
- if [ -d "$npmroot/node_modules/@earendil-works" ]; then
4932
- fail "[check-pack-install] @earendil-works present in npm-managed node_modules — pi-free boot proof is void (neutral npm install should not install optional pi peers)"
5291
+ if [ ! -d "$npmroot/node_modules/@earendil-works/pi-coding-agent" ]; then
5292
+ fail "[check-pack-install] the npm-managed install lacks @earendil-works/pi-coding-agent — the pi-free boot proof below needs pi PRESENT to deny it (#130 production SDK)"
4933
5293
  return 1
4934
5294
  fi
5295
+ local deny_pi="$npm_tmp/deny-pi/deny-pi.mjs"
5296
+ mkdir -p "$(dirname "$deny_pi")"
5297
+ cat > "$deny_pi" <<'DENY'
5298
+ import { registerHooks } from "node:module";
5299
+ registerHooks({
5300
+ resolve(specifier, context, next) {
5301
+ if (specifier.startsWith("@earendil-works/")) throw new Error(`pi-free boot violated: the bridge imported ${specifier}`);
5302
+ return next(specifier, context);
5303
+ },
5304
+ });
5305
+ DENY
5306
+ # Control: the hook really refuses an @earendil-works import from this tree (else the proof is vacuous).
5307
+ local deny_ctl
5308
+ deny_ctl=$(cd "$npmroot" && node --import "$deny_pi" --input-type=module -e "await import('@earendil-works/pi-ai')" 2>&1) && {
5309
+ fail "[check-pack-install] the pi-deny hook let @earendil-works/pi-ai load — the pi-free boot proof would be vacuous"
5310
+ return 1
5311
+ }
5312
+ if ! grep -q "pi-free boot violated: the bridge imported @earendil-works/pi-ai" <<<"$deny_ctl"; then
5313
+ fail "[check-pack-install] the pi-deny control failed for another reason:"; echo "$deny_ctl" | tail -5 | sed 's/^/ /' >&2; return 1
5314
+ fi
4935
5315
  local boot_out
4936
- if ! boot_out=$(START_SH="$installed_start" node --input-type=module <<'JS'
5316
+ if ! boot_out=$(START_SH="$installed_start" DENY_PI="$deny_pi" node --input-type=module <<'JS'
4937
5317
  import { spawn } from 'node:child_process';
4938
5318
  const start = process.env.START_SH;
4939
5319
  // Sanitize the child env so the pi-free proof cannot be masked by a leaked
4940
5320
  // module-resolution path: if NODE_PATH (or a stray pi env) pointed at a tree
4941
5321
  // holding @earendil-works, a statically pi-importing eager graph could resolve
4942
5322
  // and boot anyway, turning this gate falsely green. Strip it so "boots with
4943
- // @earendil absent" stays an honest adversarial proof.
5323
+ // every @earendil-works resolution refused" stays an honest adversarial proof.
4944
5324
  const env = { ...process.env };
4945
5325
  delete env.NODE_PATH;
5326
+ // #130: pi is installed beside the bridge now; every @earendil-works resolution is refused instead.
5327
+ env.NODE_OPTIONS = `${env.NODE_OPTIONS ?? ''} --import ${process.env.DENY_PI}`.trim();
5328
+ delete env.DENY_PI;
4946
5329
  const child = spawn(start, { stdio: ['pipe', 'pipe', 'pipe'], env });
4947
5330
  let stdout = '', stderr = '', done = false;
4948
5331
  const timer = setTimeout(() => {
@@ -5666,13 +6049,15 @@ setup_mode() {
5666
6049
  esac
5667
6050
  }
5668
6051
 
5669
- # Supported pi range, derived at runtime from the package.json devDependencies
5670
- # pin (the SSOT check-dep-versions binds: peer range == `>=<pin> <<major>.<minor+1>`).
5671
- # Never retyped here as a second literal.
6052
+ # Supported HOST pi range, derived at runtime from the pinned SDK generation — package.json's exact
6053
+ # production dependency on pi-coding-agent, which check-dep-versions binds to the durable pin's SDK
6054
+ # set: `>=<pin> <<major>.<minor+1>`. Never retyped here as a second literal. This is the ordinary Pi
6055
+ # on PATH that loads Entwurf's extensions; the carrier's own SDK set is a different subject
6056
+ # (pi-durable-runtime.ts), and a host inside this range is not required to be the same copy.
5672
6057
  pi_supported_range() {
5673
6058
  node -e '
5674
6059
  const pkg = require(process.argv[1]);
5675
- const pin = pkg.devDependencies?.["@earendil-works/pi-coding-agent"];
6060
+ const pin = pkg.dependencies?.["@earendil-works/pi-coding-agent"];
5676
6061
  if (!/^\d+\.\d+\.\d+$/.test(pin ?? "")) { console.error(`unparseable pi pin: ${pin}`); process.exit(1); }
5677
6062
  const [maj, min] = pin.split(".").map(Number);
5678
6063
  console.log(`>=${pin} <${maj}.${min + 1}`);
@@ -6024,6 +6409,19 @@ setup_all() {
6024
6409
  setup_result codex-terminal-title FAIL "detected codex, but the terminal-title setting did not complete (see above)"
6025
6410
  fi
6026
6411
  fi
6412
+ # ── pi-durable ── #129, #130: the durable app is this package's own carrier and its pi SDK set
6413
+ # is a production dependency, so there is nothing to detect and setup writes NOTHING for it —
6414
+ # the row reports whether the installed carrier can run. There is no SKIP: a missing piece is an
6415
+ # incomplete installation, a named FAIL repaired by reinstalling Entwurf. The verdict is the
6416
+ # same leaf the managed launch and the fresh preflight ask — 0 verified, anything else refused.
6417
+ local pd_rc pd_out
6418
+ pd_rc=0; pd_out=$(run_ts pi-extensions/lib/pi-durable-runtime.ts resolve 2>&1) || pd_rc=$?
6419
+ if [ "$pd_rc" -eq 0 ]; then
6420
+ setup_result pi-durable PASS "carrier and its pi SDK set verify — launch with 'entwurf pi-durable --provider <p> --model <id> --width task-wide'"
6421
+ else
6422
+ setup_result pi-durable FAIL "the installed pi-durable carrier is refused — ${pd_out##*$'\n'}"
6423
+ fi
6424
+
6027
6425
  # ── core bridge boundary ── deterministic preflight lives in `pnpm run
6028
6426
  # check:full`; live substrate acceptance lives in `LIVE=1 ./run.sh
6029
6427
  # release-gate <scratch> --cut`. Setup is the install path, so it verifies the
@@ -6536,6 +6934,8 @@ release_gate() {
6536
6934
  # Codex then opens the outbound Pi and correlates that callback. Actor coordinates travel,
6537
6935
  # and fresh Codex == app-server/MCP inherited session == outbound Pi is a hard check.
6538
6936
  run_live_step "smoke-codex-fresh-live (#95: visible Pi -> visible Codex/callback/addressed v2 -> visible Pi/callback)" gate bash "$self" smoke-codex-fresh-live
6937
+ # #129 first-admission: step 9 clause 7 + the two cross-harness receipts for pi-durable.
6938
+ run_live_step "smoke-pi-durable-fresh-live (#129: visible Pi -> visible pi-durable/callback/addressed v2 -> Pi relay)" gate bash "$self" smoke-pi-durable-fresh-live
6539
6939
 
6540
6940
  # 4. BEHAVIOR lane (advisory, non-blocking). Model-in-loop gates that probe
6541
6941
  # whether the model AUTONOMOUSLY drives the MCP entwurf surface. These never
@@ -6660,6 +7060,9 @@ case "$cmd" in
6660
7060
  smoke-omp-fresh-live)
6661
7061
  smoke_omp_fresh_live
6662
7062
  ;;
7063
+ smoke-pi-durable-fresh-live)
7064
+ smoke_pi_durable_fresh_live
7065
+ ;;
6663
7066
  check-capability-bundle-reach)
6664
7067
  check_capability_bundle_reach
6665
7068
  ;;
@@ -6699,6 +7102,26 @@ case "$cmd" in
6699
7102
  # on the status line; and still zero mailbox/receiver marker. Hermetic; no omp, no model turn
6700
7103
  run_ts scripts/check-omp-birth-hook.ts
6701
7104
  ;;
7105
+ check-pi-durable-contact)
7106
+ # #129 L-identity G-contact, checkout-only. Spawning gate (VERIFY: a vitest lane cannot hold
7107
+ # it): the real overlay durable app as a child host, the branch-emitted bridge as ITS child,
7108
+ # /proc as the PPID oracle. Both runtime inputs are operator-provisioned; absent → SKIP 97,
7109
+ # never pass. Installed: run_ts refuses it (no compiled twin) before any input is read.
7110
+ run_ts scripts/check-pi-durable-contact.ts
7111
+ ;;
7112
+ check-pi-durable-send)
7113
+ # #129 L-send first cell, checkout-only spawning gate: one native durable turn against a
7114
+ # gate-owned scripted endpoint (S) through the native ToolTask (H) to a seeded target. No
7115
+ # vendor turn (V). Inputs as check-pi-durable-contact; absent → SKIP 97.
7116
+ run_ts scripts/check-pi-durable-send.ts
7117
+ ;;
7118
+ check-pi-durable-receive)
7119
+ # #129 L-receive first cell, checkout-only spawning gate: no controller input; a gate-owned
7120
+ # sending bridge enqueues, the host's doorbell admits to the root, the scripted endpoint (S)
7121
+ # holds request #1 until the enqueue is captured, the native turn (H) drains it. No vendor (V).
7122
+ # Inputs as check-pi-durable-contact; absent → SKIP 97.
7123
+ run_ts scripts/check-pi-durable-receive.ts
7124
+ ;;
6702
7125
  check-copilot-statusline)
6703
7126
  check_copilot_statusline
6704
7127
  ;;
@@ -6777,6 +7200,9 @@ case "$cmd" in
6777
7200
  check-mux-fresh-call)
6778
7201
  check_mux_fresh_call
6779
7202
  ;;
7203
+ check-pi-durable-beside)
7204
+ check_pi_durable_beside
7205
+ ;;
6780
7206
  check-mux-resume-call)
6781
7207
  check_mux_resume_call
6782
7208
  ;;
@@ -7415,6 +7841,20 @@ case "$cmd" in
7415
7841
  shift || true
7416
7842
  exec bash "$REPO_DIR/scripts/copilot-launch.sh" "$@"
7417
7843
  ;;
7844
+ pi-durable)
7845
+ # #129 W (L3), #130: the MANAGED durable launch. Same shape and reasons as `copilot` above:
7846
+ # `exec`, no subshell and no cd — the durable TUI must own this terminal, the caller's cwd, this
7847
+ # pid and its exit status — and `shift`, because everything after the verb is the bootstrap's argv.
7848
+ #
7849
+ # The durable app is this package's CARRIER (pi/pi-durable/carrier/), and it is verified with its
7850
+ # declared pi SDK set BEFORE any of it runs: the resolver it prints is the first code this launch
7851
+ # loads. An incomplete package, a drifted carrier or an absent/mismatched SDK edge is a named
7852
+ # refusal (exit 3) from the same leaf the fresh preflight and setup ask. Nothing outside the
7853
+ # package is located, installed or repaired.
7854
+ shift || true
7855
+ pd_resolver=$(run_ts pi-extensions/lib/pi-durable-runtime.ts resolve) || exit $?
7856
+ exec node --import "$pd_resolver" "$REPO_DIR/pi/pi-durable/bootstrap.mjs" "$@"
7857
+ ;;
7418
7858
  pi)
7419
7859
  # #118 홉 2: the managed pi launch. Same shape and same reasons as `copilot` above —
7420
7860
  # `exec`, no subshell and no cd, because this operates on the operator's session; and