@junghanacs/entwurf 0.31.0 → 0.33.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +3 -2
- package/CHANGELOG.md +128 -0
- package/README.md +62 -117
- package/VERIFY.md +40 -21
- package/demo/README.md +1 -1
- package/docs/acp-backend-rail.md +1 -1
- package/docs/setup-clean-host.md +112 -95
- package/mcp/entwurf-bridge/dist/mcp/entwurf-bridge/src/index.js +1 -1
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/fresh-call-composition.js +1 -1
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/mux-fresh-call.js +8 -10
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/pi-durable-fresh-preflight.js +3 -3
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/pi-durable-runtime.js +153 -205
- package/mcp/entwurf-bridge/dist/pi-extensions/meta-bridge-pi-durable.js +6 -4
- package/mcp/entwurf-bridge/src/index.ts +1 -1
- package/package.json +14 -17
- package/pi/pi-durable/bootstrap.mjs +142 -19
- package/pi/pi-durable/carrier/LICENSE +21 -0
- package/pi/pi-durable/carrier/harness-setup.js +123 -0
- package/pi/pi-durable/carrier/prompt.js +74 -0
- package/pi/pi-durable/carrier/runtime.js +402 -0
- package/pi/pi-durable/carrier/sessions.js +56 -0
- package/pi/pi-durable/carrier/subagent.js +55 -0
- package/pi/pi-durable/carrier/tui.js +655 -0
- package/pi/pi-durable/carrier-relocation.mjs +160 -0
- package/pi/pi-durable/carrier-resolver.mjs +38 -0
- package/pi/pi-durable/overlay/upstream-pin.json +103 -11
- package/pi-extensions/lib/fresh-call-composition.ts +1 -1
- package/pi-extensions/lib/mux-fresh-call.ts +8 -10
- package/pi-extensions/lib/pi-durable-fresh-preflight.ts +3 -3
- package/pi-extensions/lib/pi-durable-runtime.ts +197 -263
- package/pi-extensions/meta-bridge-pi-durable.ts +13 -7
- package/run.sh +519 -248
- package/scripts/check-entwurf-bridge-pi-free.ts +7 -5
- package/scripts/check-fresh-call-dispatch.ts +1 -1
- package/scripts/check-gate-qualification.ts +86 -34
- package/scripts/check-herdr-activation.ts +5 -4
- package/scripts/check-pi-durable-contact.ts +26 -93
- package/scripts/check-pi-durable-receive.ts +22 -59
- package/scripts/check-pi-durable-send.ts +235 -75
- package/scripts/check-release-gate-outcomes.ts +21 -17
- package/scripts/emit-pi-durable-carrier.ts +355 -0
- package/scripts/lib/mutation-qualify.ts +21 -45
- package/scripts/lib/pi-durable-operator-guard.ts +89 -0
- package/scripts/lib/qualification-receipt.ts +7 -11
- package/scripts/mutants/control-send-receipt.json +1 -1
- package/scripts/mutants/gate-qualification.json +13 -0
- package/scripts/mutants/mux-fresh-call.json +13 -13
- package/scripts/mutants/pack-install.json +3 -3
- package/scripts/mutants/pi-durable.json +442 -109
- package/scripts/mutants/pi-floor.json +24 -2
- package/scripts/mutants/release-gate.json +5 -3
- package/scripts/pi-durable-contact-host.mjs +3 -3
- package/scripts/pi-durable-receive-host.mjs +3 -3
- package/scripts/pi-durable-send-host.mjs +76 -5
- package/scripts/smoke-herdr-raw-install-live.sh +1 -1
- package/scripts/smoke-pi-durable-fresh-live.ts +3 -3
- package/scripts/smoke-setup-verdict.sh +15 -15
- package/scripts/smoke-user-scope-citizen.sh +1 -1
package/run.sh
CHANGED
|
@@ -132,10 +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
|
|
136
|
-
./run.sh check-pi-durable-send # #129 L-send FIRST CELL, CHECKOUT-ONLY. Same
|
|
137
|
-
./run.sh check-pi-durable-beside # #129 mutant execution coordinate (VERIFY rule 3): run_vitest over the
|
|
138
|
-
./run.sh check-pi-durable-receive # #129 L-receive FIRST CELL, CHECKOUT-ONLY. Same
|
|
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)
|
|
139
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
|
|
140
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
|
|
141
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
|
|
@@ -226,7 +226,7 @@ Usage:
|
|
|
226
226
|
./run.sh uninstall-meta-bridge # 1.0.0 meta-bridge Phase 2: stateful GLOBAL uninstall (restore only keys/items captured in install-state)
|
|
227
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
|
|
228
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: the MANAGED durable launch.
|
|
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
|
|
230
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)
|
|
231
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)
|
|
232
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
|
|
@@ -282,13 +282,13 @@ Usage:
|
|
|
282
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])
|
|
283
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)
|
|
284
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)
|
|
285
|
-
./run.sh check-dep-versions # local deterministic check that the pi pin agrees across package.json (
|
|
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)
|
|
286
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
|
|
287
287
|
./run.sh check-pack # publish gate (dry-run): npm pack --dry-run + tarball invariants (runtime-critical present, dev residue absent)
|
|
288
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
|
|
289
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
|
|
290
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
|
|
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
|
|
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)
|
|
292
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)
|
|
293
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
|
|
294
294
|
./run.sh remove [project-dir] # remove entwurf entries from project .pi/settings.json (project scope only; global user-scope citizen left intact)
|
|
@@ -455,17 +455,12 @@ preflight_dep_integrity() {
|
|
|
455
455
|
# still catching a pnpm dir-move that left the symlink store dangling
|
|
456
456
|
# (accessSync follows the link).
|
|
457
457
|
#
|
|
458
|
-
# Probe set = the
|
|
459
|
-
#
|
|
460
|
-
#
|
|
461
|
-
# of
|
|
462
|
-
#
|
|
463
|
-
#
|
|
464
|
-
# neutral npm/pnpm install — nothing supplies it. The optional peer is simply
|
|
465
|
-
# unresolved (entwurf-preflight.ts:51), and the project-trust lane that needs it
|
|
466
|
-
# is dead on that host. Excluding the trio from this probe is still right (a hard
|
|
467
|
-
# require would reject every consumer install), but it is a KNOWN GAP, not proof
|
|
468
|
-
# 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.
|
|
469
464
|
# pi runtime presence/version is covered by check-pi-runtime-version /
|
|
470
465
|
# check-pi-import-surface, neither of which runs on a consumer host.
|
|
471
466
|
local probe=(
|
|
@@ -725,7 +720,7 @@ install_user_scope() {
|
|
|
725
720
|
# scripts/herdr-plugin-activate.mjs → `run.sh install-user-scope --plugin-runtime …`) and had no
|
|
726
721
|
# version check at all, so a host pi setup would name as FAIL got its citizen written here in
|
|
727
722
|
# silence. Both doors now tell the same truth, off the SAME derived range — `pi_supported_range`
|
|
728
|
-
# reads the
|
|
723
|
+
# reads the pinned SDK generation, so there is one range in the repo and no second parser.
|
|
729
724
|
#
|
|
730
725
|
# ABSENT pi is deliberately NOT changed here. This verb is called by an activation that has
|
|
731
726
|
# already decided pi is one of its backends; inventing a presence verdict at the writer would put
|
|
@@ -983,7 +978,7 @@ smoke_pi_durable_fresh_live() {
|
|
|
983
978
|
# offering and model are read from its native store via an online node:sqlite backup. Private
|
|
984
979
|
# tmux server; REAL meta roots (one Pi + one pd minted, asserted); records/transcripts/DB kept.
|
|
985
980
|
# pi-durable absent from FRESH_CALL_BACKENDS or without a mailbox rail, or LIVE!=1, is a
|
|
986
|
-
# protocol SKIP; with LIVE=1 every missing prerequisite (
|
|
981
|
+
# protocol SKIP; with LIVE=1 every missing prerequisite (a verified carrier, pi, tmux, bridge) FAILs.
|
|
987
982
|
run_ts scripts/smoke-pi-durable-fresh-live.ts
|
|
988
983
|
}
|
|
989
984
|
|
|
@@ -1302,8 +1297,9 @@ check_entwurf_bridge_boot() {
|
|
|
1302
1297
|
|
|
1303
1298
|
check_entwurf_bridge_pi_free() {
|
|
1304
1299
|
# 0.12.1 A-gate (static half): the entwurf-bridge MCP server must boot WITHOUT any
|
|
1305
|
-
# pi package. entwurf is a harness-neutral npm package; pi is one
|
|
1306
|
-
#
|
|
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
|
|
1307
1303
|
# mcp/entwurf-bridge/src/index.ts and fails if any reachable module statically
|
|
1308
1304
|
# value-imports @earendil-works/pi-*. Type-only imports and dynamic `await import()`
|
|
1309
1305
|
# (the intended lazy preflight boundary) are excluded — the runtime boot smoke is the
|
|
@@ -1521,10 +1517,12 @@ check_pi_durable_beside() {
|
|
|
1521
1517
|
# #129: the pi-durable qualification lane's execution coordinate (VERIFY 「Four rules」 rule 3). It is
|
|
1522
1518
|
# not a discovery path — check-tests-beside-behavior already finds these files. It exists because the
|
|
1523
1519
|
# qualification runner re-invokes a gate argv once per mutant, and the narrow filter keeps one mutant
|
|
1524
|
-
# from re-running every beside test. The
|
|
1525
|
-
# bootstrap, the
|
|
1526
|
-
# fresh preflight,
|
|
1527
|
-
|
|
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
|
|
1528
1526
|
}
|
|
1529
1527
|
|
|
1530
1528
|
smoke_mux_fresh_call_live() {
|
|
@@ -2200,67 +2198,57 @@ assert.equal(geminiBundled, undefined, 'package.json must not bundle @google/gem
|
|
|
2200
2198
|
// heredoc (which is indented, so won't match `^...`).
|
|
2201
2199
|
const runSh = readFileSync('run.sh', 'utf8');
|
|
2202
2200
|
|
|
2203
|
-
// pi
|
|
2204
|
-
//
|
|
2205
|
-
//
|
|
2206
|
-
//
|
|
2207
|
-
//
|
|
2208
|
-
//
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
const
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
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'];
|
|
2218
2219
|
assert.equal(piCore, piAi,
|
|
2219
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`);
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
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
|
+
}
|
|
2224
2235
|
|
|
2225
|
-
// check-pack-install
|
|
2236
|
+
// check-pack-install install pins (quoted `@earendil-works/pi-*@<ver>`
|
|
2226
2237
|
// args; the `\d`-anchored version avoids matching this regex literal itself).
|
|
2227
2238
|
const peerAi = runSh.match(/"@earendil-works\/pi-ai@(\d[\d.]*)"/)?.[1];
|
|
2228
2239
|
const peerCoding = runSh.match(/"@earendil-works\/pi-coding-agent@(\d[\d.]*)"/)?.[1];
|
|
2229
2240
|
const peerTui = runSh.match(/"@earendil-works\/pi-tui@(\d[\d.]*)"/)?.[1];
|
|
2230
2241
|
assert.equal(peerAi, piAi,
|
|
2231
|
-
`run.sh check-pack-install pi-ai
|
|
2242
|
+
`run.sh check-pack-install pi-ai install pin (${peerAi}) must match the pinned SDK (${piAi})`);
|
|
2232
2243
|
assert.equal(peerCoding, piAi,
|
|
2233
|
-
`run.sh check-pack-install pi-coding-agent
|
|
2244
|
+
`run.sh check-pack-install pi-coding-agent install pin (${peerCoding}) must match (${piAi})`);
|
|
2234
2245
|
assert.equal(peerTui, piAi,
|
|
2235
|
-
`run.sh check-pack-install pi-tui
|
|
2236
|
-
|
|
2237
|
-
//
|
|
2238
|
-
//
|
|
2239
|
-
//
|
|
2240
|
-
// bound at the next minor stops a fresh install from silently pulling a future
|
|
2241
|
-
// pi (past the declared ceiling — 0.87+ at the current 0.86.0 pin) whose
|
|
2242
|
-
// internal export surface has drifted from the one we typecheck against.
|
|
2243
|
-
// pi moves its public surface every minor (the 0.79→0.80 getModels→provider-
|
|
2244
|
-
// factory churn is exactly this, and 0.86 is the same shape again: a custom
|
|
2245
|
-
// provider's streamSimple now receives a branded TranscriptContext instead of a
|
|
2246
|
-
// raw Context, with systemPrompt/tools folded into a leading system message), so
|
|
2247
|
-
// an open `>=` floor is exactly how the next installer re-acquires the drift.
|
|
2248
|
-
// The floor is also the HARD MINIMUM a consumer install resolves: at `>=0.86.0`
|
|
2249
|
-
// an existing 0.85.1 host is upgraded, not kept.
|
|
2250
|
-
// Expected shape: `>=<devDep> <<major>.<minor+1>` (e.g. `>=0.86.0 <0.87`, `>=1.0.0 <1.1`).
|
|
2251
|
-
// Pi 1.0 did not change the rule: an unverified minor is still unverified, so the ceiling
|
|
2252
|
-
// 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.
|
|
2253
2251
|
const [piMaj, piMin] = piAi.split('.').map(Number);
|
|
2254
|
-
const expectedPeer = `>=${piAi} <${piMaj}.${piMin + 1}`;
|
|
2255
|
-
const peerDepAi = pkg.peerDependencies?.['@earendil-works/pi-ai'];
|
|
2256
|
-
const peerDepCoding = pkg.peerDependencies?.['@earendil-works/pi-coding-agent'];
|
|
2257
|
-
const peerDepTui = pkg.peerDependencies?.['@earendil-works/pi-tui'];
|
|
2258
|
-
assert.equal(peerDepAi, expectedPeer,
|
|
2259
|
-
`package.json peerDependencies @earendil-works/pi-ai (${peerDepAi}) must be "${expectedPeer}" (devDep floor + next-minor ceiling)`);
|
|
2260
|
-
assert.equal(peerDepCoding, expectedPeer,
|
|
2261
|
-
`package.json peerDependencies @earendil-works/pi-coding-agent (${peerDepCoding}) must be "${expectedPeer}"`);
|
|
2262
|
-
assert.equal(peerDepTui, expectedPeer,
|
|
2263
|
-
`package.json peerDependencies @earendil-works/pi-tui (${peerDepTui}) must be "${expectedPeer}"`);
|
|
2264
2252
|
|
|
2265
2253
|
// BASELINE DOCS (0.12.8). This gate was BORN reading a doc: 362becd added it
|
|
2266
2254
|
// after the 21de0f9 drift and asserted README.md's codex-acp install pin against
|
|
@@ -2324,14 +2312,14 @@ for (const file of BASELINE_DOCS) {
|
|
|
2324
2312
|
for (const [decl, floor, ceilMajor, ceilMinor] of text.matchAll(/>=\s?(\d+\.\d+\.\d+)\s?<\s?(\d+)\.(\d+)/g)) {
|
|
2325
2313
|
rangeDecls++;
|
|
2326
2314
|
assert.equal(floor, piAi,
|
|
2327
|
-
`${file}: declared pi floor in "${decl}" is ${floor}, but the
|
|
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`);
|
|
2328
2316
|
assert.equal(`${ceilMajor}.${ceilMinor}`, `${piMaj}.${piMin + 1}`,
|
|
2329
2317
|
`${file}: declared pi ceiling in "${decl}" must be the next minor (${piMaj}.${piMin + 1})`);
|
|
2330
2318
|
}
|
|
2331
2319
|
// Exact install pins: `@earendil-works/pi-<pkg>@<version>`.
|
|
2332
2320
|
for (const [decl, ver] of text.matchAll(/@earendil-works\/pi-(?:ai|coding-agent|tui)@(\d+\.\d+\.\d+)/g)) {
|
|
2333
2321
|
exactDecls++;
|
|
2334
|
-
assert.equal(ver, piAi, `${file}: install example "${decl}" pins ${ver}, but the
|
|
2322
|
+
assert.equal(ver, piAi, `${file}: install example "${decl}" pins ${ver}, but the pinned SDK is ${piAi}`);
|
|
2335
2323
|
}
|
|
2336
2324
|
}
|
|
2337
2325
|
// Prose declarations carry the pin in sentences the two patterns above cannot
|
|
@@ -2345,7 +2333,7 @@ const PROSE_DECLS = [
|
|
|
2345
2333
|
for (const [file, re, shape] of PROSE_DECLS) {
|
|
2346
2334
|
const m = readFileSync(file, 'utf8').match(re);
|
|
2347
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`);
|
|
2348
|
-
assert.equal(m[1], piAi, `${file}: "${shape}" declares ${m[1]}, but the
|
|
2336
|
+
assert.equal(m[1], piAi, `${file}: "${shape}" declares ${m[1]}, but the pinned SDK is ${piAi}`);
|
|
2349
2337
|
}
|
|
2350
2338
|
// ── #120 P4: ONE FLOOR, FOUR MECHANICAL SURFACES ──────────────────────────
|
|
2351
2339
|
// The bump is mostly a number substitution, and that is exactly how a bump goes
|
|
@@ -2365,6 +2353,8 @@ const PI_CONSTELLATION = [
|
|
|
2365
2353
|
'@earendil-works/pi-telemetry', '@earendil-works/chord',
|
|
2366
2354
|
// `[측정 2026-09-30]` 0.99.1: pi-coding-agent's runtime deps gained the built-in extensions' packages.
|
|
2367
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',
|
|
2368
2358
|
];
|
|
2369
2359
|
const sameSet = (got, want, where) => {
|
|
2370
2360
|
const missing = want.filter((n) => !got.includes(n));
|
|
@@ -2409,7 +2399,7 @@ if (!matcherPin) {
|
|
|
2409
2399
|
const CURRENT_CONTRACT_DECLS = [
|
|
2410
2400
|
['plugins/herdr/README.md', /npm install -g "@earendil-works\/pi-coding-agent@(>=[\d.]+ <\d+\.\d+)"/, 'the herdr install line'],
|
|
2411
2401
|
['docs/mux-launch-rail.md', /현 supported range 는 `(>=[\d.]+ <\d+\.\d+)`/, "mux-launch-rail's current supported range"],
|
|
2412
|
-
['docs/acp-backend-rail.md', /
|
|
2402
|
+
['docs/acp-backend-rail.md', /SDK exact `(\d+\.\d+\.\d+)`, host `(>=[\d.]+ <\d+\.\d+)`/, "the ACP support row's exact+range pair"],
|
|
2413
2403
|
];
|
|
2414
2404
|
for (const [file, re, shape] of CURRENT_CONTRACT_DECLS) {
|
|
2415
2405
|
const m = readFileSync(file, 'utf8').match(re);
|
|
@@ -2420,14 +2410,14 @@ for (const [file, re, shape] of CURRENT_CONTRACT_DECLS) {
|
|
|
2420
2410
|
}
|
|
2421
2411
|
}
|
|
2422
2412
|
assert.deepEqual(floorGaps, [],
|
|
2423
|
-
`[QK:PI-FLOOR-SINGLE-SOURCE] the pi floor is declared on four mechanical surfaces — the
|
|
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`);
|
|
2424
2414
|
|
|
2425
2415
|
// Guard the guard: if the patterns ever stop matching, the loops above pass
|
|
2426
2416
|
// vacuously and the docs fall back OUT of the gate without a word.
|
|
2427
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`);
|
|
2428
2418
|
assert.ok(exactDecls >= 1, `expected at least 1 exact pi install pin in the baseline docs, found ${exactDecls}`);
|
|
2429
2419
|
|
|
2430
|
-
console.log(`[check-dep-versions] ok — pi ${piAi} is coherent across package.json (
|
|
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)`);
|
|
2431
2421
|
EOF
|
|
2432
2422
|
)
|
|
2433
2423
|
}
|
|
@@ -2839,17 +2829,17 @@ check_pi_import_surface() {
|
|
|
2839
2829
|
# Do NOT widen this to a `providers/*` subpath — it typechecks but CANNOT
|
|
2840
2830
|
# resolve under the extension loader.
|
|
2841
2831
|
#
|
|
2842
|
-
# #
|
|
2843
|
-
#
|
|
2844
|
-
#
|
|
2845
|
-
#
|
|
2846
|
-
#
|
|
2847
|
-
#
|
|
2848
|
-
#
|
|
2849
|
-
#
|
|
2850
|
-
#
|
|
2851
|
-
#
|
|
2852
|
-
#
|
|
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.
|
|
2853
2843
|
# ONE corpus, TWO callers. The real scan and the denominator fixture must run the
|
|
2854
2844
|
# SAME two lines, or the fixture would prove a command the gate does not use.
|
|
2855
2845
|
pi_import_work_surface() {
|
|
@@ -2858,12 +2848,35 @@ check_pi_import_surface() {
|
|
|
2858
2848
|
pi_import_scan() {
|
|
2859
2849
|
pi_import_work_surface \
|
|
2860
2850
|
| xargs -0r grep -HnE "[\"'\`]@earendil-works/pi-(ai|coding-agent|tui)/" 2>/dev/null \
|
|
2861
|
-
| grep -vE "[\"'\`]@earendil-works/pi-ai/compat[\"'\`]" 2>/dev/null
|
|
2862
|
-
|
|
2863
|
-
|
|
2851
|
+
| grep -vE "[\"'\`]@earendil-works/pi-ai/compat[\"'\`]" 2>/dev/null || true
|
|
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"
|
|
2864
2877
|
}
|
|
2865
2878
|
|
|
2866
|
-
local probe_dir probe_hits hits
|
|
2879
|
+
local probe_dir probe_hits probe_fence hits fence
|
|
2867
2880
|
probe_dir=$(mktemp -d "${TMPDIR:-/tmp}/entwurf-pi-import-denominator.XXXXXX")
|
|
2868
2881
|
(
|
|
2869
2882
|
cd "$probe_dir" || exit 1
|
|
@@ -2871,15 +2884,19 @@ check_pi_import_surface() {
|
|
|
2871
2884
|
printf '%s\n' 'import { getModels } from "@earendil-works/pi-ai/compat";' > tracked-allowed.ts
|
|
2872
2885
|
git add tracked-allowed.ts
|
|
2873
2886
|
printf '%s\n' 'import "@earendil-works/pi-ai/private-probe";' > untracked-forbidden.ts
|
|
2874
|
-
mkdir -p pi/pi-durable
|
|
2875
|
-
printf '%s\n' '
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
|
|
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
|
|
2881
2897
|
) || { fail "[check-pi-import-surface] could not build the denominator fixture in $probe_dir"; rm -rf "$probe_dir"; return 1; }
|
|
2882
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)
|
|
2883
2900
|
rm -rf "$probe_dir"
|
|
2884
2901
|
# ONE assertion, ONE claim token: the manifest contract requires the signature to
|
|
2885
2902
|
# occur exactly once in this file, so both fixture conditions are folded into a
|
|
@@ -2891,13 +2908,13 @@ check_pi_import_surface() {
|
|
|
2891
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)"
|
|
2892
2909
|
return 1
|
|
2893
2910
|
fi
|
|
2894
|
-
local
|
|
2895
|
-
grep -
|
|
2896
|
-
grep -
|
|
2897
|
-
grep -
|
|
2898
|
-
grep -
|
|
2899
|
-
if [ "$
|
|
2900
|
-
fail "[QK:PIIMPORT-
|
|
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)"
|
|
2901
2918
|
return 1
|
|
2902
2919
|
fi
|
|
2903
2920
|
|
|
@@ -2907,7 +2924,13 @@ check_pi_import_surface() {
|
|
|
2907
2924
|
echo "$hits"
|
|
2908
2925
|
exit 1
|
|
2909
2926
|
fi
|
|
2910
|
-
|
|
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"
|
|
2911
2934
|
}
|
|
2912
2935
|
|
|
2913
2936
|
check_env_namespace() {
|
|
@@ -2934,65 +2957,26 @@ check_env_namespace() {
|
|
|
2934
2957
|
}
|
|
2935
2958
|
|
|
2936
2959
|
check_pi_runtime_version() {
|
|
2937
|
-
# 0.11 Stage 0 (동결결정 9, runtime half)
|
|
2938
|
-
#
|
|
2939
|
-
#
|
|
2940
|
-
#
|
|
2941
|
-
#
|
|
2942
|
-
#
|
|
2943
|
-
#
|
|
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.
|
|
2944
2969
|
#
|
|
2945
|
-
#
|
|
2946
|
-
#
|
|
2947
|
-
#
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
const pkg = JSON.parse(readFileSync('package.json', 'utf8'));
|
|
2956
|
-
const FLOOR = pkg.devDependencies?.['@earendil-works/pi-coding-agent'];
|
|
2957
|
-
if (typeof FLOOR !== 'string' || !/^\d+\.\d+\.\d+$/.test(FLOOR)) {
|
|
2958
|
-
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'})`);
|
|
2959
|
-
process.exit(1);
|
|
2960
|
-
}
|
|
2961
|
-
// The declared contract is a CLOSED range (`>=<devDep> <<major>.<minor+1>`, enforced on
|
|
2962
|
-
// package.json by check-dep-versions), so the runtime check must be closed too.
|
|
2963
|
-
// A floor-only comparison would bless a resolved pi ABOVE the ceiling — and an
|
|
2964
|
-
// out-of-range pi is exactly the drift this cut exists to stop: 0.80.6 landed on
|
|
2965
|
-
// the dev box while the repo still declared 0.80.3, and every gate stayed green.
|
|
2966
|
-
// Verifying only half of a declared range is the same lie in the other direction.
|
|
2967
|
-
const [FLOOR_MAJOR, FLOOR_MINOR] = FLOOR.split('.').map(Number);
|
|
2968
|
-
const CEILING = `${FLOOR_MAJOR}.${FLOOR_MINOR + 1}.0`;
|
|
2969
|
-
const cmp = (a, b) => {
|
|
2970
|
-
const pa = a.split('.').map(Number), pb = b.split('.').map(Number);
|
|
2971
|
-
for (let i = 0; i < 3; i++) { if ((pa[i] || 0) !== (pb[i] || 0)) return (pa[i] || 0) - (pb[i] || 0); }
|
|
2972
|
-
return 0;
|
|
2973
|
-
};
|
|
2974
|
-
let VERSION;
|
|
2975
|
-
try {
|
|
2976
|
-
({ VERSION } = await import('@earendil-works/pi-coding-agent'));
|
|
2977
|
-
} catch (e) {
|
|
2978
|
-
console.error(`[check-pi-runtime-version] FAIL: cannot import @earendil-works/pi-coding-agent root — ${e?.message ?? e}`);
|
|
2979
|
-
process.exit(1);
|
|
2980
|
-
}
|
|
2981
|
-
if (typeof VERSION !== 'string') {
|
|
2982
|
-
console.error('[check-pi-runtime-version] FAIL: pi root export VERSION is not a string');
|
|
2983
|
-
process.exit(1);
|
|
2984
|
-
}
|
|
2985
|
-
if (cmp(VERSION, FLOOR) < 0) {
|
|
2986
|
-
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-*.`);
|
|
2987
|
-
process.exit(1);
|
|
2988
|
-
}
|
|
2989
|
-
if (cmp(VERSION, CEILING) >= 0) {
|
|
2990
|
-
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.`);
|
|
2991
|
-
process.exit(1);
|
|
2992
|
-
}
|
|
2993
|
-
console.log(`[check-pi-runtime-version] ok — pi VERSION ${VERSION} within the declared range (>=${FLOOR} <${CEILING.slice(0, -2)})`);
|
|
2994
|
-
EOF
|
|
2995
|
-
)
|
|
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)"
|
|
2996
2980
|
}
|
|
2997
2981
|
|
|
2998
2982
|
check_install_preflight() {
|
|
@@ -3038,8 +3022,8 @@ check_install_preflight() {
|
|
|
3038
3022
|
# (a bare `test -d` would pass) with the other runtime deps symlinked live, but a
|
|
3039
3023
|
# representative dep (@anthropic-ai/sdk) dangles. Asserts the dir-move blind spot
|
|
3040
3024
|
# is closed AND that the failure names the broken dep. Uses a BUNDLED runtime dep
|
|
3041
|
-
# because the @earendil-works
|
|
3042
|
-
#
|
|
3025
|
+
# because the @earendil-works SDK set is not part of the install preflight probe
|
|
3026
|
+
# set (the carrier verifier owns its integrity).
|
|
3043
3027
|
fake=$(mktemp -d); proj=$(mktemp -d)
|
|
3044
3028
|
cp "$REPO_DIR/run.sh" "$fake/run.sh"
|
|
3045
3029
|
mkdir -p "$fake/node_modules/@modelcontextprotocol" "$fake/node_modules/@agentclientprotocol" "$fake/node_modules/@anthropic-ai"
|
|
@@ -3689,6 +3673,15 @@ check_pack() {
|
|
|
3689
3673
|
"pi/pi-durable/bootstrap.mjs"
|
|
3690
3674
|
"pi/pi-durable/overlay/upstream-pin.json"
|
|
3691
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"
|
|
3692
3685
|
"pi/meta-bridge-omp/entwurf-meta-omp/package.json"
|
|
3693
3686
|
"mcp/entwurf-bridge/dist/pi-extensions/lib/meta-session.js"
|
|
3694
3687
|
"scripts/postinstall-chmod.cjs"
|
|
@@ -3817,7 +3810,7 @@ check_pack() {
|
|
|
3817
3810
|
# which is exactly what property (1)'s `(_|$)` absorbs; all three shapes are fixtures
|
|
3818
3811
|
# below so a future suffix change cannot pass vacuously.)
|
|
3819
3812
|
pack_install_leaked_pi() {
|
|
3820
|
-
grep '^@earendil-works+' | grep -Ev '@1\.0
|
|
3813
|
+
grep '^@earendil-works+' | grep -Ev '@1\.1\.0(_|$)' || true
|
|
3821
3814
|
}
|
|
3822
3815
|
|
|
3823
3816
|
# Matcher self-test on SYNTHETIC lookalikes: a healthy install tree cannot exercise either
|
|
@@ -3837,15 +3830,15 @@ check_pack_pin_matcher() {
|
|
|
3837
3830
|
# None may leak; the two lookalikes must — a PREFIX-EXTENDED version (`0.86.0-beta.1`,
|
|
3838
3831
|
# the prerelease shape that an unanchored match would bless) and an off-pin version.
|
|
3839
3832
|
matcher_probe=$(printf '%s\n' \
|
|
3840
|
-
'@earendil-works+pi-ai@1.0
|
|
3841
|
-
'@earendil-works+pi-ai@1.
|
|
3842
|
-
'@earendil-works+pi-ai@1.
|
|
3843
|
-
'@earendil-works+pi-ai@1.
|
|
3844
|
-
'@earendil-works+pi-ai@1.0
|
|
3833
|
+
'@earendil-works+pi-ai@1.1.0' \
|
|
3834
|
+
'@earendil-works+pi-ai@1.1.0_@modelcontextprotocol+sdk@1.29.0_zod@4.3.6' \
|
|
3835
|
+
'@earendil-works+pi-ai@1.1.0_ws@8.21.3' \
|
|
3836
|
+
'@earendil-works+pi-ai@1.1.0_@modelcontextprotocol+sdk@1.29.0_zod@4.3.6__ws@8.21.3_zod@4.3.6' \
|
|
3837
|
+
'@earendil-works+pi-ai@1.1.0-beta.1' \
|
|
3845
3838
|
'@earendil-works+pi-agent-core@0.87.1' | pack_install_leaked_pi)
|
|
3846
|
-
if [ "$matcher_probe" != '@earendil-works+pi-ai@1.0
|
|
3839
|
+
if [ "$matcher_probe" != '@earendil-works+pi-ai@1.1.0-beta.1
|
|
3847
3840
|
@earendil-works+pi-agent-core@0.87.1' ]; then
|
|
3848
|
-
fail "[QK:PACK-INSTALL-PIN-MATCHER-BOUNDED] the pin-leak matcher must flag the prefix-extended 1.0
|
|
3841
|
+
fail "[QK:PACK-INSTALL-PIN-MATCHER-BOUNDED] the pin-leak matcher must flag the prefix-extended 1.1.0-beta.1 and the off-pin 0.87.1 lookalikes, and pass 1.1.0 bare or with any measured peer-hash — got: ${matcher_probe:-<nothing leaked>}"
|
|
3849
3842
|
return 1
|
|
3850
3843
|
fi
|
|
3851
3844
|
|
|
@@ -3853,9 +3846,9 @@ check_pack_pin_matcher() {
|
|
|
3853
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
|
|
3854
3847
|
# back to `^@earendil-works+pi-` sees nothing here and dies at this signature.
|
|
3855
3848
|
matcher_probe=$(printf '%s\n' \
|
|
3856
|
-
'@earendil-works+chord@1.0
|
|
3849
|
+
'@earendil-works+chord@1.1.0' \
|
|
3857
3850
|
'@earendil-works+chord@0.87.1' \
|
|
3858
|
-
'@earendil-works+pi-ai@1.0
|
|
3851
|
+
'@earendil-works+pi-ai@1.1.0' | pack_install_leaked_pi)
|
|
3859
3852
|
if [ "$matcher_probe" != '@earendil-works+chord@0.87.1' ]; then
|
|
3860
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>}"
|
|
3861
3854
|
return 1
|
|
@@ -3864,6 +3857,234 @@ check_pack_pin_matcher() {
|
|
|
3864
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)"
|
|
3865
3858
|
}
|
|
3866
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 to an older published version: 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: a coding-agent root alone has
|
|
3877
|
+
# caret SDK edges, which can float to a newer generation (CI37694671506, measured at
|
|
3878
|
+
# coding-agent@1.0.4 with ^1.0.4 edges resolving 1.1.0: pi-ai tar 404).
|
|
3879
|
+
# These are fixture roots from the owning pin, not product overrides or fallback.
|
|
3880
|
+
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
|
|
3881
|
+
mapfile -t host_specs <<<"$out"
|
|
3882
|
+
oracle_dir="$box/pd-oracle"; mkdir -p "$oracle_dir"
|
|
3883
|
+
cat > "$oracle_dir/oracle.mjs" <<'ORACLE'
|
|
3884
|
+
// node --import <pkg>/pi/pi-durable/carrier-resolver.mjs oracle.mjs <pkg> <consumer node_modules> <control file> <receipt>
|
|
3885
|
+
import fs from "node:fs";
|
|
3886
|
+
import { registerHooks } from "node:module";
|
|
3887
|
+
import path from "node:path";
|
|
3888
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
3889
|
+
const [pkg, consumerRoot, control, receiptPath] = process.argv.slice(2);
|
|
3890
|
+
const seen = [];
|
|
3891
|
+
registerHooks({ resolve(specifier, context, next) { const r = next(specifier, context); seen.push({ specifier, parent: context.parentURL ?? null, url: r.url }); return r; } });
|
|
3892
|
+
const root = fs.realpathSync(consumerRoot);
|
|
3893
|
+
const inside = (file) => fs.realpathSync(file).startsWith(root + path.sep);
|
|
3894
|
+
const pin = JSON.parse(fs.readFileSync(path.join(pkg, "pi/pi-durable/overlay/upstream-pin.json"), "utf8"));
|
|
3895
|
+
const members = pin.sdk.members, direct = pin.sdk.direct;
|
|
3896
|
+
const carrier = path.join(pkg, "pi/pi-durable/carrier");
|
|
3897
|
+
const exportsOk = (await import(pathToFileURL(path.join(carrier, "runtime.js")).href)).openDurable instanceof Function
|
|
3898
|
+
&& (await import(pathToFileURL(path.join(carrier, "tui.js")).href)).runDurableTui instanceof Function;
|
|
3899
|
+
const files = [...new Set(seen.filter((s) => s.url.startsWith("file:")).map((s) => fileURLToPath(s.url)))];
|
|
3900
|
+
const outside = files.filter((f) => !inside(f));
|
|
3901
|
+
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; };
|
|
3902
|
+
const copies = {};
|
|
3903
|
+
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)}`); }
|
|
3904
|
+
const relocated = seen.filter((s) => s.specifier.startsWith("entwurf-pi-dist:"));
|
|
3905
|
+
const codingAgentRoots = new Set(relocated.map((s) => owner(fileURLToPath(s.url))?.root));
|
|
3906
|
+
// Independent of the product's resolver: the package directory a bare import from `fromFile` binds is the
|
|
3907
|
+
// nearest ancestor `node_modules/<name>` of the file's REAL directory, walked here by hand.
|
|
3908
|
+
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; } };
|
|
3909
|
+
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))) }; };
|
|
3910
|
+
const base = path.join(carrier, "runtime.js");
|
|
3911
|
+
const edges = Object.entries({ ...members, ...direct }).map(([n, v]) => edge("carrier", n, v, base));
|
|
3912
|
+
// Every reachable member copy, at any depth, once per real directory.
|
|
3913
|
+
const queue = edges.filter((e) => e.to in members && e.at).map((e) => e.at), seenCopies = new Set();
|
|
3914
|
+
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); } }
|
|
3915
|
+
const verdict = {
|
|
3916
|
+
exportsOk,
|
|
3917
|
+
resolvedFiles: files.length,
|
|
3918
|
+
outsideConsumer: outside,
|
|
3919
|
+
controlRejected: fs.existsSync(control) && !inside(control),
|
|
3920
|
+
loadedVersionsOk: Object.entries(copies).every(([n, set]) => [...set].every((c) => c.startsWith(`${members[n]}@`))),
|
|
3921
|
+
relocations: relocated.length,
|
|
3922
|
+
relocationOneDistInside: codingAgentRoots.size === 1 && relocated.every((s) => inside(fileURLToPath(s.url))),
|
|
3923
|
+
memberEdges: edges.filter((e) => e.from !== "carrier").length,
|
|
3924
|
+
memberCopiesWalked: seenCopies.size,
|
|
3925
|
+
edgeMismatches: edges.filter((e) => e.got !== e.want || e.name !== e.to),
|
|
3926
|
+
};
|
|
3927
|
+
fs.writeFileSync(receiptPath, JSON.stringify({ consumerRoot: root, verdict, copies: Object.fromEntries(Object.entries(copies).map(([n, s]) => [n, [...s]])), edges, resolutions: seen }, null, 1));
|
|
3928
|
+
const ok = verdict.exportsOk && verdict.outsideConsumer.length === 0 && verdict.controlRejected && verdict.loadedVersionsOk
|
|
3929
|
+
&& verdict.relocations > 0 && verdict.relocationOneDistInside && verdict.memberEdges > 0 && verdict.edgeMismatches.length === 0;
|
|
3930
|
+
console.log(JSON.stringify({ ok, ...verdict, outsideConsumer: verdict.outsideConsumer.length, edgeMismatches: verdict.edgeMismatches.length }));
|
|
3931
|
+
process.exit(ok ? 0 : 1);
|
|
3932
|
+
ORACLE
|
|
3933
|
+
|
|
3934
|
+
mkdir -p "$box/pd-aligned" "$box/pd-global" "$box/pd-mismatch"
|
|
3935
|
+
out=$(cd "$box/pd-aligned" && npm install "$tgz" "${host_specs[@]}" --no-audit --no-fund 2>&1) || {
|
|
3936
|
+
fail "[check-pack-install] pi-durable L1 (aligned host) install failed:"; echo "$out" | tail -10 | sed 's/^/ /' >&2; return 1; }
|
|
3937
|
+
out=$(cd "$box" && npm install -g --prefix "$box/pd-global" "$tgz" --no-audit --no-fund 2>&1) || {
|
|
3938
|
+
fail "[check-pack-install] pi-durable L2 (isolated global) install failed:"; echo "$out" | tail -10 | sed 's/^/ /' >&2; return 1; }
|
|
3939
|
+
|
|
3940
|
+
for cell in L1s L1 L2; do
|
|
3941
|
+
case "$cell" in
|
|
3942
|
+
L1s) root="$l1s/node_modules" control="$box/pd-aligned/node_modules/@junghanacs/entwurf/package.json" ;;
|
|
3943
|
+
L1) root="$box/pd-aligned/node_modules" control="$box/pd-global/lib/node_modules/@junghanacs/entwurf/package.json" ;;
|
|
3944
|
+
L2) root="$box/pd-global/lib/node_modules" control="$l1s/node_modules/@junghanacs/entwurf/package.json" ;;
|
|
3945
|
+
esac
|
|
3946
|
+
pkg="$root/@junghanacs/entwurf"
|
|
3947
|
+
rc=0; out=$(cd "$box" && node "$pkg/mcp/entwurf-bridge/dist/pi-extensions/lib/pi-durable-runtime.js" resolve 2>&1) || rc=$?
|
|
3948
|
+
if [ "$rc" -ne 0 ] || [ "$(realpath "$out" 2>/dev/null)" != "$(realpath "$pkg/pi/pi-durable/carrier-resolver.mjs")" ]; then
|
|
3949
|
+
fail "[check-pack-install] pi-durable $cell: the INSTALLED verifier did not verify its own carrier (rc=$rc): $out"
|
|
3950
|
+
return 1
|
|
3951
|
+
fi
|
|
3952
|
+
rc=0; out=$(cd "$box" && node --import "$out" "$oracle_dir/oracle.mjs" "$pkg" "$root" "$control" "$box/pd-oracle/$cell.json" 2>&1) || rc=$?
|
|
3953
|
+
if [ "$rc" -ne 0 ]; then
|
|
3954
|
+
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"
|
|
3955
|
+
return 1
|
|
3956
|
+
fi
|
|
3957
|
+
echo "[check-pack-install] pi-durable $cell: installed verifier PASS; consumer oracle $out"
|
|
3958
|
+
done
|
|
3959
|
+
|
|
3960
|
+
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"
|
|
3961
|
+
out=$(cd "$box/pd-mismatch" && npm install "$tgz" --no-audit --no-fund 2>&1) || {
|
|
3962
|
+
fail "[check-pack-install] pi-durable L3 (mismatch control) install failed:"; echo "$out" | tail -10 | sed 's/^/ /' >&2; return 1; }
|
|
3963
|
+
pkg="$box/pd-mismatch/node_modules/@junghanacs/entwurf"
|
|
3964
|
+
rc=0; out=$(cd "$box" && node "$pkg/mcp/entwurf-bridge/dist/pi-extensions/lib/pi-durable-runtime.js" resolve 2>&1) || rc=$?
|
|
3965
|
+
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
|
|
3966
|
+
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"
|
|
3967
|
+
return 1
|
|
3968
|
+
fi
|
|
3969
|
+
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=$?
|
|
3970
|
+
if [ "$rc" -ne 0 ] || [ "$out" != "function" ]; then
|
|
3971
|
+
fail "[check-pack-install] pi-durable L3: the control premise failed — the mixed tree was expected to still IMPORT (rc=$rc): $out"
|
|
3972
|
+
return 1
|
|
3973
|
+
fi
|
|
3974
|
+
echo "[check-pack-install] pi-durable L3: the mixed tree still imports, and the installed verifier refuses it as pi-durable-sdk-mismatch"
|
|
3975
|
+
|
|
3976
|
+
# L4 — a THIRD-depth mismatch control on the same coherence axis. `[측정 2026-10-07]` npm 11 does not
|
|
3977
|
+
# produce this layout from an override (nested strategy included, it dedupes pi-durable's pi-ai to the
|
|
3978
|
+
# shared copy and overrides that), so it is CONSTRUCTED from real package bytes inside the already
|
|
3979
|
+
# proved L2 consumer: pi-durable gets its own same-version copy of the installed pi-ai, whose
|
|
3980
|
+
# pi-telemetry is the real published 1.0.3. Both the installed verifier and the independent oracle
|
|
3981
|
+
# must name the depth-3 edge; a walk that stopped at the carrier's own members would pass it.
|
|
3982
|
+
local l4_root="$box/pd-global/lib/node_modules" l4_tel="$box/pd-tel-103" l4_tel_ver="1.0.3"
|
|
3983
|
+
pkg="$l4_root/@junghanacs/entwurf"
|
|
3984
|
+
mkdir -p "$l4_tel" "$pkg/node_modules/@earendil-works/pi-durable/node_modules/@earendil-works"
|
|
3985
|
+
out=$(cd "$l4_tel" && npm pack "@earendil-works/pi-telemetry@$l4_tel_ver" --silent 2>&1 && tar -xzf ./*.tgz 2>&1) || {
|
|
3986
|
+
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; }
|
|
3987
|
+
local l4_ai="$pkg/node_modules/@earendil-works/pi-durable/node_modules/@earendil-works/pi-ai"
|
|
3988
|
+
cp -a "$(realpath "$pkg/node_modules/@earendil-works/pi-ai")" "$l4_ai"
|
|
3989
|
+
mkdir -p "$l4_ai/node_modules/@earendil-works"
|
|
3990
|
+
cp -a "$l4_tel/package" "$l4_ai/node_modules/@earendil-works/pi-telemetry"
|
|
3991
|
+
rc=0; out=$(cd "$box" && node "$pkg/mcp/entwurf-bridge/dist/pi-extensions/lib/pi-durable-runtime.js" resolve 2>&1) || rc=$?
|
|
3992
|
+
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
|
|
3993
|
+
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"
|
|
3994
|
+
return 1
|
|
3995
|
+
fi
|
|
3996
|
+
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=$?
|
|
3997
|
+
if [ "$rc" -eq 0 ] || ! grep -q '"edgeMismatches":1' <<<"$out"; then
|
|
3998
|
+
fail "[check-pack-install] pi-durable L4: the independent oracle did not see exactly the one depth-3 mismatch (rc=$rc): $out"
|
|
3999
|
+
return 1
|
|
4000
|
+
fi
|
|
4001
|
+
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"
|
|
4002
|
+
|
|
4003
|
+
# (c) The public launch on L1s, through the installed bin: verifier → resolver → bootstrap → TUI
|
|
4004
|
+
# module → the carrier's real openDurable on the pinned SDK, whose own model resolution
|
|
4005
|
+
# (`findInitialAgentModel`) refuses a provider no catalog registers — `[측정 2026-10-07]` the
|
|
4006
|
+
# upstream message, before the overlay's exact-model check is reached. Offline (PI_OFFLINE),
|
|
4007
|
+
# sandbox HOME/agent dir, no credential; the contact's bridge
|
|
4008
|
+
# is spawned only at the birth, which this refusal precedes, so no citizen may exist after it.
|
|
4009
|
+
local launch_home="$box/pd-launch-home" launch_cwd="$box/pd-launch-cwd" launch_env=()
|
|
4010
|
+
mkdir -p "$launch_home/.pi/agent" "$launch_cwd"
|
|
4011
|
+
mapfile -t launch_env < <(pack_install_consumer_env "$launch_home")
|
|
4012
|
+
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=$?
|
|
4013
|
+
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
|
|
4014
|
+
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):"
|
|
4015
|
+
echo "$out" | tail -15 | sed 's/^/ /' >&2
|
|
4016
|
+
return 1
|
|
4017
|
+
fi
|
|
4018
|
+
if ls "$launch_home/.pi/agent/meta-sessions" 2>/dev/null | grep -q .; then
|
|
4019
|
+
fail "[check-pack-install] the refused durable launch minted a meta record — a refusal before birth must leave no citizen"
|
|
4020
|
+
return 1
|
|
4021
|
+
fi
|
|
4022
|
+
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"
|
|
4023
|
+
|
|
4024
|
+
# (c2) #130 P2: ONE explicit operator module on the INSTALLED package. The synthetic fixture is
|
|
4025
|
+
# copied from this checkout into the sandbox — it never ships (test/ is outside the package files).
|
|
4026
|
+
# Its initialization appends `init` to the receipt this cell names; the same public launch must then
|
|
4027
|
+
# reach the same carrier model refusal, so the installed bootstrap loaded the module before the
|
|
4028
|
+
# runtime and the native registry installed its extension (carrier/runtime.js 146) before that
|
|
4029
|
+
# refusal (runtime.js 162). A module claiming an extension name the app installs itself is refused by
|
|
4030
|
+
# name before anything opens: no durable session directory, no citizen, in a fresh sandbox home.
|
|
4031
|
+
# Native SDK file opens here are ordinary (the module runs in the native process); none is judged.
|
|
4032
|
+
local mod_dir="$box/pd-module" mod_home="$box/pd-module-home" mod_env=()
|
|
4033
|
+
mkdir -p "$mod_dir" "$mod_home/.pi/agent"
|
|
4034
|
+
cp "$REPO_DIR/test/fixtures/pi-durable-native-module/mock.mjs" "$mod_dir/mock.mjs"
|
|
4035
|
+
printf 'export default { name: "subagent" };\n' > "$mod_dir/reserved.mjs"
|
|
4036
|
+
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=$?
|
|
4037
|
+
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
|
|
4038
|
+
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' ' ')):"
|
|
4039
|
+
echo "$out" | tail -15 | sed 's/^/ /' >&2
|
|
4040
|
+
return 1
|
|
4041
|
+
fi
|
|
4042
|
+
mapfile -t mod_env < <(pack_install_consumer_env "$mod_home")
|
|
4043
|
+
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=$?
|
|
4044
|
+
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
|
|
4045
|
+
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):"
|
|
4046
|
+
echo "$out" | tail -15 | sed 's/^/ /' >&2
|
|
4047
|
+
return 1
|
|
4048
|
+
fi
|
|
4049
|
+
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"
|
|
4050
|
+
|
|
4051
|
+
# (d) Host vs native are two subjects. The installed package's native SDK set is the pin (above);
|
|
4052
|
+
# the ordinary Pi HOST on PATH is judged by its OWN real `pi --version` against the derived range.
|
|
4053
|
+
# Real host binaries, no forged version: a sandbox install of the previous pi generation must make the
|
|
4054
|
+
# installed setup's pi row a named FAIL with ZERO Pi wiring while pi-durable still PASSes, and the
|
|
4055
|
+
# aligned host binary (the pinned version, installed above for L1) must be inside the range.
|
|
4056
|
+
# The previous host version is a variable, not a quoted `@pkg@x.y.z` spec: check-dep-versions reads
|
|
4057
|
+
# those specs as this repo's install pins, and this one is deliberately off the floor.
|
|
4058
|
+
local host_home host_proj host_old="$box/pd-host-old" host_env=() host_old_ver="1.0.4"
|
|
4059
|
+
mkdir -p "$host_old"
|
|
4060
|
+
for spec in "${host_specs[@]}"; do host_old_specs+=("${spec%@*}@$host_old_ver"); done
|
|
4061
|
+
out=$(cd "$host_old" && npm install "${host_old_specs[@]}" --no-audit --no-fund 2>&1) || {
|
|
4062
|
+
fail "[check-pack-install] could not install the previous-host pi fixture ($host_old_ver):"; echo "$out" | tail -10 | sed 's/^/ /' >&2; return 1; }
|
|
4063
|
+
for cell in old aligned; do
|
|
4064
|
+
host_home="$box/pd-host-$cell-home"; host_proj="$box/pd-host-$cell-proj"
|
|
4065
|
+
mkdir -p "$host_home/.pi/agent" "$host_proj"
|
|
4066
|
+
mapfile -t host_env < <(pack_install_consumer_env "$host_home")
|
|
4067
|
+
local host_bin="$host_old/node_modules/.bin/pi"
|
|
4068
|
+
[ "$cell" = aligned ] && host_bin="$box/pd-aligned/node_modules/.bin/pi"
|
|
4069
|
+
rc=0; out=$(cd "$box" && env "${host_env[@]}" PI_BIN="$host_bin" CLAUDE_BIN="$box/definitely-absent" AGY_BIN="$box/definitely-absent" \
|
|
4070
|
+
COPILOT_BIN="$box/definitely-absent" OMP_BIN="$box/definitely-absent" CODEX_BIN="$box/definitely-absent" \
|
|
4071
|
+
"$l1s/node_modules/.bin/entwurf" setup "$host_proj" 2>&1) || rc=$?
|
|
4072
|
+
if ! grep -q "pi-durable: PASS" <<<"$out"; then
|
|
4073
|
+
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
|
|
4074
|
+
fi
|
|
4075
|
+
if [ "$cell" = old ]; then
|
|
4076
|
+
if [ "$rc" -eq 0 ] || ! grep -q "pi: FAIL — detected pi $host_old_ver is outside the supported range >=$pin_ver <1.2" <<<"$out" || [ -e "$host_proj/.pi" ] || [ -e "$host_home/.pi/agent/settings.json" ]; then
|
|
4077
|
+
fail "[QK:PACK-INSTALL-HOST-RANGE-SEPARATE] [check-pack-install] a real pi $host_old_ver host beside the $pin_ver native set must be a named pi FAIL with zero Pi wiring and a non-green setup (rc=$rc):"
|
|
4078
|
+
echo "$out" | tail -20 | sed 's/^/ /' >&2
|
|
4079
|
+
return 1
|
|
4080
|
+
fi
|
|
4081
|
+
elif grep -q "outside the supported range" <<<"$out"; then
|
|
4082
|
+
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
|
|
4083
|
+
fi
|
|
4084
|
+
done
|
|
4085
|
+
echo "[check-pack-install] host vs native: a real pi $host_old_ver host is a named pi FAIL with no wiring while pi-durable PASSes; the $pin_ver host binary is inside the range"
|
|
4086
|
+
}
|
|
4087
|
+
|
|
3867
4088
|
# The writable roots of ONE consumer sandbox, shared by every consumer subcall of
|
|
3868
4089
|
# check-pack-install that drives an installed `run.sh` writer. The writers resolve their agent
|
|
3869
4090
|
# dir as `${PI_CODING_AGENT_DIR:-$HOME/.pi/agent}` (`resolve`/`install` below), so swapping HOME
|
|
@@ -4061,6 +4282,15 @@ _check_pack_install_impl() {
|
|
|
4061
4282
|
"pi/pi-durable/bootstrap.mjs"
|
|
4062
4283
|
"pi/pi-durable/overlay/upstream-pin.json"
|
|
4063
4284
|
"pi/pi-durable/overlay/runtime-contacts.patch"
|
|
4285
|
+
"pi/pi-durable/carrier-resolver.mjs"
|
|
4286
|
+
"pi/pi-durable/carrier-relocation.mjs"
|
|
4287
|
+
"pi/pi-durable/carrier/runtime.js"
|
|
4288
|
+
"pi/pi-durable/carrier/tui.js"
|
|
4289
|
+
"pi/pi-durable/carrier/harness-setup.js"
|
|
4290
|
+
"pi/pi-durable/carrier/prompt.js"
|
|
4291
|
+
"pi/pi-durable/carrier/sessions.js"
|
|
4292
|
+
"pi/pi-durable/carrier/subagent.js"
|
|
4293
|
+
"pi/pi-durable/carrier/LICENSE"
|
|
4064
4294
|
"pi/meta-bridge-omp/entwurf-meta-omp/package.json"
|
|
4065
4295
|
"mcp/entwurf-bridge/dist/pi-extensions/lib/meta-session.js"
|
|
4066
4296
|
"scripts/postinstall-chmod.cjs"
|
|
@@ -4139,9 +4369,9 @@ _check_pack_install_impl() {
|
|
|
4139
4369
|
# repo packages with; --ignore-workspace stops it from re-attaching
|
|
4140
4370
|
# to our pnpm-workspace.yaml; --ignore-scripts blocks the husky
|
|
4141
4371
|
# prepare hook (and any future install scripts) from running inside
|
|
4142
|
-
# the consumer project.
|
|
4143
|
-
# baseline (the package.json
|
|
4144
|
-
# check-dep-versions binds to
|
|
4372
|
+
# the consumer project. The pi SDK is pinned to the CURRENT pi release
|
|
4373
|
+
# baseline (the package.json exact production dependency — the pins below
|
|
4374
|
+
# are what check-dep-versions binds to it) so the smoke matches the same
|
|
4145
4375
|
# shape an external pi user would have after `pi install`.
|
|
4146
4376
|
local tmp npm_tmp
|
|
4147
4377
|
tmp=$(mktemp -d -t entwurf-install-smoke.XXXXXX)
|
|
@@ -4205,20 +4435,30 @@ _check_pack_install_impl() {
|
|
|
4205
4435
|
# `[측정 2026-10-05]` at 1.0.2 the constellation is unchanged: `npm view <pkg>@1.0.2 dependencies`
|
|
4206
4436
|
# gives pi-coding-agent {chord, pi-agent-core, pi-ai, pi-codemode, pi-mcp, pi-tui}, pi-ai
|
|
4207
4437
|
# {pi-telemetry}, pi-client {chord, pi-protocol}, pi-protocol {chord}, all ten published at 1.0.2.
|
|
4208
|
-
|
|
4438
|
+
# `[측정 2026-10-07]` at 1.0.4 the same edges hold, and #130 adds @earendil-works/pi-durable: the
|
|
4439
|
+
# library the pi-durable carrier imports, a production dependency of this package since 0.32 (its
|
|
4440
|
+
# own deps: chord, pi-ai). All eleven are published at 1.0.4. The tarball now DECLARES the nine-member
|
|
4441
|
+
# SDK set itself, so these pins are an aligned host install beside it, not peers it lacks.
|
|
4442
|
+
# `[측정 2026-10-08]` at 1.1.0 the constellation is unchanged: the nine registry manifests
|
|
4443
|
+
# (.tmp-verify/033-prep/registry-sdk-manifests.json) give pi-coding-agent {chord, pi-agent-core,
|
|
4444
|
+
# pi-ai, pi-codemode, pi-mcp, pi-tui}, pi-ai {pi-telemetry}, pi-agent-core {pi-ai}, pi-durable
|
|
4445
|
+
# {chord, pi-ai}, and `npm view` gives pi-client {chord, pi-protocol}, pi-protocol {chord}. All
|
|
4446
|
+
# eleven are published at 1.1.0.
|
|
4447
|
+
echo "[check-pack-install] pnpm add into $tmp (with the aligned 1.1.x SDK + chord + codemode + mcp + durable + typebox)"
|
|
4209
4448
|
local install_log
|
|
4210
4449
|
install_log=$(cd "$tmp" && pnpm add \
|
|
4211
4450
|
"$tgz_path" \
|
|
4212
|
-
"@earendil-works/pi-ai@1.0
|
|
4213
|
-
"@earendil-works/pi-coding-agent@1.0
|
|
4214
|
-
"@earendil-works/pi-tui@1.0
|
|
4215
|
-
"@earendil-works/pi-agent-core@1.0
|
|
4216
|
-
"@earendil-works/pi-client@1.0
|
|
4217
|
-
"@earendil-works/pi-protocol@1.0
|
|
4218
|
-
"@earendil-works/pi-telemetry@1.0
|
|
4219
|
-
"@earendil-works/chord@1.0
|
|
4220
|
-
"@earendil-works/pi-codemode@1.0
|
|
4221
|
-
"@earendil-works/pi-mcp@1.0
|
|
4451
|
+
"@earendil-works/pi-ai@1.1.0" \
|
|
4452
|
+
"@earendil-works/pi-coding-agent@1.1.0" \
|
|
4453
|
+
"@earendil-works/pi-tui@1.1.0" \
|
|
4454
|
+
"@earendil-works/pi-agent-core@1.1.0" \
|
|
4455
|
+
"@earendil-works/pi-client@1.1.0" \
|
|
4456
|
+
"@earendil-works/pi-protocol@1.1.0" \
|
|
4457
|
+
"@earendil-works/pi-telemetry@1.1.0" \
|
|
4458
|
+
"@earendil-works/chord@1.1.0" \
|
|
4459
|
+
"@earendil-works/pi-codemode@1.1.0" \
|
|
4460
|
+
"@earendil-works/pi-mcp@1.1.0" \
|
|
4461
|
+
"@earendil-works/pi-durable@1.1.0" \
|
|
4222
4462
|
"typebox@latest" \
|
|
4223
4463
|
--ignore-workspace --ignore-scripts 2>&1) || {
|
|
4224
4464
|
fail "[check-pack-install] pnpm add failed:"
|
|
@@ -4234,11 +4474,11 @@ _check_pack_install_impl() {
|
|
|
4234
4474
|
local leaked_pi
|
|
4235
4475
|
leaked_pi=$(ls "$tmp/node_modules/.pnpm" 2>/dev/null | pack_install_leaked_pi)
|
|
4236
4476
|
if [ -n "$leaked_pi" ]; then
|
|
4237
|
-
fail "[check-pack-install] UNVERIFIED pi runtime resolved into the install tree (expected only 1.0
|
|
4477
|
+
fail "[check-pack-install] UNVERIFIED pi runtime resolved into the install tree (expected only 1.1.0):"
|
|
4238
4478
|
printf '%s\n' "$leaked_pi" | sed 's/^/ /' >&2
|
|
4239
4479
|
return 1
|
|
4240
4480
|
fi
|
|
4241
|
-
echo "[check-pack-install] pi runtime tree pin verified: every @earendil-works package is 1.0
|
|
4481
|
+
echo "[check-pack-install] pi runtime tree pin verified: every @earendil-works package is 1.1.0 (chord and pi-durable included)"
|
|
4242
4482
|
|
|
4243
4483
|
# Resolve the installed package.json and confirm pi.extensions
|
|
4244
4484
|
# arrived intact. If pi.extensions is empty or missing, the
|
|
@@ -4347,8 +4587,8 @@ _check_pack_install_impl() {
|
|
|
4347
4587
|
# and safe to run in CI. Output goes to stderr; capture both
|
|
4348
4588
|
# streams with 2>&1.
|
|
4349
4589
|
#
|
|
4350
|
-
# The pi that loads the tarball is the PINNED
|
|
4351
|
-
# it ($tmp/node_modules/.bin/pi = the floor of the supported
|
|
4590
|
+
# The pi that loads the tarball is the PINNED SDK pi this smoke just installed next to
|
|
4591
|
+
# it ($tmp/node_modules/.bin/pi = the floor of the supported host range), NEVER
|
|
4352
4592
|
# whatever `pi` the host happens to have on PATH. A gate may not READ the operator's
|
|
4353
4593
|
# global install any more than it may WRITE it: PATH resolution made this gate green
|
|
4354
4594
|
# on a dev box carrying a newer global pi and RED in CI, which carries no global pi
|
|
@@ -4371,14 +4611,14 @@ _check_pack_install_impl() {
|
|
|
4371
4611
|
)
|
|
4372
4612
|
|
|
4373
4613
|
# Assert the version: a gate that cannot name which pi it proved has proved nothing.
|
|
4374
|
-
#
|
|
4375
|
-
#
|
|
4614
|
+
# The pinned SDK generation is the pin SSOT (package.json's exact production dependency, which
|
|
4615
|
+
# check-dep-versions binds to the durable pin and to the install literals above).
|
|
4376
4616
|
local pi_bin="$tmp/node_modules/.bin/pi" pi_pin pi_ver
|
|
4377
4617
|
if [ ! -x "$pi_bin" ]; then
|
|
4378
4618
|
fail "[check-pack-install] pinned pi missing from the install-smoke tree ($pi_bin) — cannot run loader smoke"
|
|
4379
4619
|
return 1
|
|
4380
4620
|
fi
|
|
4381
|
-
pi_pin=$(cd "$REPO_DIR" && node -p "require('./package.json').
|
|
4621
|
+
pi_pin=$(cd "$REPO_DIR" && node -p "require('./package.json').dependencies['@earendil-works/pi-coding-agent']")
|
|
4382
4622
|
pi_ver=$(cd "$tmp" && env "${pi_env[@]}" "$pi_bin" --version 2>&1 | head -1 | tr -d '[:space:]')
|
|
4383
4623
|
if [ "$pi_ver" != "$pi_pin" ]; then
|
|
4384
4624
|
fail "[check-pack-install] install-smoke pi is '$pi_ver', expected the pinned '$pi_pin' — the loader smoke would prove the wrong runtime"
|
|
@@ -4445,8 +4685,9 @@ _check_pack_install_impl() {
|
|
|
4445
4685
|
# npm-managed neutral install regression — the README's PRIMARY install path is
|
|
4446
4686
|
# now `npm install @junghanacs/entwurf` (NOT `pi install npm:...`). This layout
|
|
4447
4687
|
# lands the package under node_modules, hoists runtime deps to the sibling
|
|
4448
|
-
# node_modules, has no package-local node_modules, and
|
|
4449
|
-
#
|
|
4688
|
+
# node_modules, has no package-local node_modules, and (since #130) carries the
|
|
4689
|
+
# pi SDK as a production dependency the bridge must still not import — the
|
|
4690
|
+
# pi-free boot below proves that under a deny hook. The old cwd-relative
|
|
4450
4691
|
# preflight_dep_integrity rejected every hoisted-dep npm install; 0.12.0 then
|
|
4451
4692
|
# additionally died because start.sh tried strip-types under node_modules.
|
|
4452
4693
|
# Prove `entwurf`/`entwurf-bridge` bins exist, `run.sh install` writes settings
|
|
@@ -4536,6 +4777,10 @@ sys.exit(0 if any(isinstance(s,str) and s.endswith('/node_modules/@junghanacs/en
|
|
|
4536
4777
|
fi
|
|
4537
4778
|
echo "[check-pack-install] user-scope citizen regression pass (npm consumer: --entwurf-control loads from a foreign cwd)"
|
|
4538
4779
|
|
|
4780
|
+
# #130: the pi-durable carrier on actual npm consumers (L1s = this Entwurf-only root, L1 aligned
|
|
4781
|
+
# host, L2 isolated global, L3 mismatch control) and the public launch.
|
|
4782
|
+
pack_install_durable_cells "$tgz_path" "$npm_tmp" "$npmroot" || return 1
|
|
4783
|
+
|
|
4539
4784
|
# Two-root ownership row (#86 C2, L1b extension): a SECOND npm root under the
|
|
4540
4785
|
# SAME consumer HOME must not silently steal the user-scope registration the
|
|
4541
4786
|
# first root owns — normal install refuses (naming takeover-user-scope) and the
|
|
@@ -4670,13 +4915,20 @@ sys.exit(0 if any(isinstance(s,str) and s.rstrip('/')== '$npm2_pkg' for s in src
|
|
|
4670
4915
|
return 1
|
|
4671
4916
|
fi
|
|
4672
4917
|
local skip_probe
|
|
4673
|
-
for skip_probe in "pi: SKIP" "claude: SKIP" "agy: SKIP" "copilot: SKIP" "omp: SKIP" "codex: SKIP"
|
|
4918
|
+
for skip_probe in "pi: SKIP" "claude: SKIP" "agy: SKIP" "copilot: SKIP" "omp: SKIP" "codex: SKIP"; do
|
|
4674
4919
|
if ! grep -q "$skip_probe" <<<"$setup_out"; then
|
|
4675
4920
|
fail "[QK:CODEX-PACK-SETUP-ZERO-STATE] [check-pack-install] installed all-absent setup missing explicit zero-state '$skip_probe':"
|
|
4676
4921
|
echo "$setup_out" | tail -25 | sed 's/^/ /' >&2
|
|
4677
4922
|
return 1
|
|
4678
4923
|
fi
|
|
4679
4924
|
done
|
|
4925
|
+
# #130: pi-durable is not a detected harness — its carrier and pi SDK set ship IN this package, so
|
|
4926
|
+
# the installed row must verify them from node_modules (PASS), never fall back to a zero-state SKIP.
|
|
4927
|
+
if ! grep -q "pi-durable: PASS" <<<"$setup_out"; then
|
|
4928
|
+
fail "[check-pack-install] installed all-absent setup did not verify the packaged pi-durable carrier and its SDK set (want 'pi-durable: PASS'):"
|
|
4929
|
+
echo "$setup_out" | tail -25 | sed 's/^/ /' >&2
|
|
4930
|
+
return 1
|
|
4931
|
+
fi
|
|
4680
4932
|
if ! grep -q "bins: PASS — provided by npm bin linking" <<<"$setup_out"; then
|
|
4681
4933
|
fail "[check-pack-install] installed setup must report stable bins PASS as npm-provided (capability present, not absent):"
|
|
4682
4934
|
echo "$setup_out" | tail -25 | sed 's/^/ /' >&2
|
|
@@ -5031,29 +5283,55 @@ PY
|
|
|
5031
5283
|
# surface. Two proofs in one: (1) the prepack dist JS boots under node_modules
|
|
5032
5284
|
# with plain node — a strip-types fallback would crash with the exact error
|
|
5033
5285
|
# above, so a parseable tools/list IS proof the dist path was taken; (2) the
|
|
5034
|
-
# boot is pi-free
|
|
5035
|
-
#
|
|
5286
|
+
# boot is pi-free. Until #130 that was proved by ABSENCE (the pi peers were optional and the
|
|
5287
|
+
# neutral npm path did not install them). Since 0.32 the pi SDK is a production dependency, so
|
|
5288
|
+
# it is always installed and absence can no longer be the premise; the proof is now adversarial
|
|
5289
|
+
# in the other direction: the bridge boots under a preloaded resolve hook that REFUSES every
|
|
5290
|
+
# `@earendil-works/*` specifier, so an eager graph that imported pi would die at boot.
|
|
5036
5291
|
local installed_start="$npmroot/node_modules/.bin/entwurf-bridge"
|
|
5037
5292
|
local installed_dist="$npm_pkg/mcp/entwurf-bridge/dist/mcp/entwurf-bridge/src/index.js"
|
|
5038
5293
|
if [ ! -f "$installed_dist" ]; then
|
|
5039
5294
|
fail "[check-pack-install] installed bridge missing prebuilt dist (prepack did not emit into the tarball?): $installed_dist"
|
|
5040
5295
|
return 1
|
|
5041
5296
|
fi
|
|
5042
|
-
if [ -d "$npmroot/node_modules/@earendil-works" ]; then
|
|
5043
|
-
fail "[check-pack-install]
|
|
5297
|
+
if [ ! -d "$npmroot/node_modules/@earendil-works/pi-coding-agent" ]; then
|
|
5298
|
+
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)"
|
|
5299
|
+
return 1
|
|
5300
|
+
fi
|
|
5301
|
+
local deny_pi="$npm_tmp/deny-pi/deny-pi.mjs"
|
|
5302
|
+
mkdir -p "$(dirname "$deny_pi")"
|
|
5303
|
+
cat > "$deny_pi" <<'DENY'
|
|
5304
|
+
import { registerHooks } from "node:module";
|
|
5305
|
+
registerHooks({
|
|
5306
|
+
resolve(specifier, context, next) {
|
|
5307
|
+
if (specifier.startsWith("@earendil-works/")) throw new Error(`pi-free boot violated: the bridge imported ${specifier}`);
|
|
5308
|
+
return next(specifier, context);
|
|
5309
|
+
},
|
|
5310
|
+
});
|
|
5311
|
+
DENY
|
|
5312
|
+
# Control: the hook really refuses an @earendil-works import from this tree (else the proof is vacuous).
|
|
5313
|
+
local deny_ctl
|
|
5314
|
+
deny_ctl=$(cd "$npmroot" && node --import "$deny_pi" --input-type=module -e "await import('@earendil-works/pi-ai')" 2>&1) && {
|
|
5315
|
+
fail "[check-pack-install] the pi-deny hook let @earendil-works/pi-ai load — the pi-free boot proof would be vacuous"
|
|
5044
5316
|
return 1
|
|
5317
|
+
}
|
|
5318
|
+
if ! grep -q "pi-free boot violated: the bridge imported @earendil-works/pi-ai" <<<"$deny_ctl"; then
|
|
5319
|
+
fail "[check-pack-install] the pi-deny control failed for another reason:"; echo "$deny_ctl" | tail -5 | sed 's/^/ /' >&2; return 1
|
|
5045
5320
|
fi
|
|
5046
5321
|
local boot_out
|
|
5047
|
-
if ! boot_out=$(START_SH="$installed_start" node --input-type=module <<'JS'
|
|
5322
|
+
if ! boot_out=$(START_SH="$installed_start" DENY_PI="$deny_pi" node --input-type=module <<'JS'
|
|
5048
5323
|
import { spawn } from 'node:child_process';
|
|
5049
5324
|
const start = process.env.START_SH;
|
|
5050
5325
|
// Sanitize the child env so the pi-free proof cannot be masked by a leaked
|
|
5051
5326
|
// module-resolution path: if NODE_PATH (or a stray pi env) pointed at a tree
|
|
5052
5327
|
// holding @earendil-works, a statically pi-importing eager graph could resolve
|
|
5053
5328
|
// and boot anyway, turning this gate falsely green. Strip it so "boots with
|
|
5054
|
-
// @earendil
|
|
5329
|
+
// every @earendil-works resolution refused" stays an honest adversarial proof.
|
|
5055
5330
|
const env = { ...process.env };
|
|
5056
5331
|
delete env.NODE_PATH;
|
|
5332
|
+
// #130: pi is installed beside the bridge now; every @earendil-works resolution is refused instead.
|
|
5333
|
+
env.NODE_OPTIONS = `${env.NODE_OPTIONS ?? ''} --import ${process.env.DENY_PI}`.trim();
|
|
5334
|
+
delete env.DENY_PI;
|
|
5057
5335
|
const child = spawn(start, { stdio: ['pipe', 'pipe', 'pipe'], env });
|
|
5058
5336
|
let stdout = '', stderr = '', done = false;
|
|
5059
5337
|
const timer = setTimeout(() => {
|
|
@@ -5777,13 +6055,15 @@ setup_mode() {
|
|
|
5777
6055
|
esac
|
|
5778
6056
|
}
|
|
5779
6057
|
|
|
5780
|
-
# Supported pi range, derived at runtime from the package.json
|
|
5781
|
-
#
|
|
5782
|
-
# Never retyped here as a second literal.
|
|
6058
|
+
# Supported HOST pi range, derived at runtime from the pinned SDK generation — package.json's exact
|
|
6059
|
+
# production dependency on pi-coding-agent, which check-dep-versions binds to the durable pin's SDK
|
|
6060
|
+
# set: `>=<pin> <<major>.<minor+1>`. Never retyped here as a second literal. This is the ordinary Pi
|
|
6061
|
+
# on PATH that loads Entwurf's extensions; the carrier's own SDK set is a different subject
|
|
6062
|
+
# (pi-durable-runtime.ts), and a host inside this range is not required to be the same copy.
|
|
5783
6063
|
pi_supported_range() {
|
|
5784
6064
|
node -e '
|
|
5785
6065
|
const pkg = require(process.argv[1]);
|
|
5786
|
-
const pin = pkg.
|
|
6066
|
+
const pin = pkg.dependencies?.["@earendil-works/pi-coding-agent"];
|
|
5787
6067
|
if (!/^\d+\.\d+\.\d+$/.test(pin ?? "")) { console.error(`unparseable pi pin: ${pin}`); process.exit(1); }
|
|
5788
6068
|
const [maj, min] = pin.split(".").map(Number);
|
|
5789
6069
|
console.log(`>=${pin} <${maj}.${min + 1}`);
|
|
@@ -6135,19 +6415,18 @@ setup_all() {
|
|
|
6135
6415
|
setup_result codex-terminal-title FAIL "detected codex, but the terminal-title setting did not complete (see above)"
|
|
6136
6416
|
fi
|
|
6137
6417
|
fi
|
|
6138
|
-
# ── pi-durable ── #129:
|
|
6139
|
-
#
|
|
6140
|
-
#
|
|
6141
|
-
# named FAIL
|
|
6142
|
-
# fresh preflight ask — 0 verified,
|
|
6143
|
-
# the runtime (docs/setup-clean-host.md §2b).
|
|
6418
|
+
# ── pi-durable ── #129, #130: the durable app is this package's own carrier and its pi SDK set
|
|
6419
|
+
# is a production dependency, so there is nothing to detect and setup writes NOTHING for it —
|
|
6420
|
+
# the row reports whether the installed carrier can run. There is no SKIP: a missing piece is an
|
|
6421
|
+
# incomplete installation, a named FAIL repaired by reinstalling Entwurf. The verdict is the
|
|
6422
|
+
# same leaf the managed launch and the fresh preflight ask — 0 verified, anything else refused.
|
|
6144
6423
|
local pd_rc pd_out
|
|
6145
6424
|
pd_rc=0; pd_out=$(run_ts pi-extensions/lib/pi-durable-runtime.ts resolve 2>&1) || pd_rc=$?
|
|
6146
|
-
|
|
6147
|
-
|
|
6148
|
-
|
|
6149
|
-
|
|
6150
|
-
|
|
6425
|
+
if [ "$pd_rc" -eq 0 ]; then
|
|
6426
|
+
setup_result pi-durable PASS "carrier and its pi SDK set verify — launch with 'entwurf pi-durable --provider <p> --model <id> --width task-wide'"
|
|
6427
|
+
else
|
|
6428
|
+
setup_result pi-durable FAIL "the installed pi-durable carrier is refused — ${pd_out##*$'\n'}"
|
|
6429
|
+
fi
|
|
6151
6430
|
|
|
6152
6431
|
# ── core bridge boundary ── deterministic preflight lives in `pnpm run
|
|
6153
6432
|
# check:full`; live substrate acceptance lives in `LIVE=1 ./run.sh
|
|
@@ -6529,19 +6808,11 @@ release_gate() {
|
|
|
6529
6808
|
results+=("FAIL static (pnpm run check:full)"); failc=$((failc + 1))
|
|
6530
6809
|
fi
|
|
6531
6810
|
|
|
6532
|
-
# 1b. Discriminating power
|
|
6533
|
-
#
|
|
6534
|
-
#
|
|
6535
|
-
#
|
|
6536
|
-
#
|
|
6537
|
-
section "release-gate step: check-gate-qualification"
|
|
6538
|
-
if (cd "$REPO_DIR" && bash "$self" check-gate-qualification); then
|
|
6539
|
-
ok "check-gate-qualification: PASS"
|
|
6540
|
-
results+=("PASS check-gate-qualification"); pass=$((pass + 1))
|
|
6541
|
-
else
|
|
6542
|
-
fail "check-gate-qualification: FAIL"
|
|
6543
|
-
results+=("FAIL check-gate-qualification"); failc=$((failc + 1))
|
|
6544
|
-
fi
|
|
6811
|
+
# 1b. Discriminating power is NOT re-proven here. A cut re-proves that the gates still kill
|
|
6812
|
+
# what they claim exactly once: the qualification BODY step of the exact-SHA CI run at the
|
|
6813
|
+
# prepared SHA, which the release oracle (verify-exact-ci.sh) requires before any candidate
|
|
6814
|
+
# exists (#132 / #133 — a local BODY here repeated that ~100-minute proof on a SHA nobody ships).
|
|
6815
|
+
# Its head still rides check:full as check-gate-manifests.
|
|
6545
6816
|
|
|
6546
6817
|
# 2. (gemini-availability step removed — claude-only floor; gemini CLI is
|
|
6547
6818
|
# deprecated, so the gate no longer asserts a three-backend claim.)
|
|
@@ -7569,15 +7840,15 @@ case "$cmd" in
|
|
|
7569
7840
|
exec bash "$REPO_DIR/scripts/copilot-launch.sh" "$@"
|
|
7570
7841
|
;;
|
|
7571
7842
|
pi-durable)
|
|
7572
|
-
# #129 W (L3): the MANAGED durable launch. Same shape and reasons as `copilot` above:
|
|
7573
|
-
# no subshell and no cd — the durable TUI must own this terminal, the caller's cwd, this
|
|
7574
|
-
# and its exit status — and `shift`, because everything after the verb is the bootstrap's argv.
|
|
7843
|
+
# #129 W (L3), #130: the MANAGED durable launch. Same shape and reasons as `copilot` above:
|
|
7844
|
+
# `exec`, no subshell and no cd — the durable TUI must own this terminal, the caller's cwd, this
|
|
7845
|
+
# pid and its exit status — and `shift`, because everything after the verb is the bootstrap's argv.
|
|
7575
7846
|
#
|
|
7576
|
-
# The durable app is
|
|
7577
|
-
#
|
|
7578
|
-
#
|
|
7579
|
-
# refusal from the same leaf the fresh preflight and setup ask
|
|
7580
|
-
#
|
|
7847
|
+
# The durable app is this package's CARRIER (pi/pi-durable/carrier/), and it is verified with its
|
|
7848
|
+
# declared pi SDK set BEFORE any of it runs: the resolver it prints is the first code this launch
|
|
7849
|
+
# loads. An incomplete package, a drifted carrier or an absent/mismatched SDK edge is a named
|
|
7850
|
+
# refusal (exit 3) from the same leaf the fresh preflight and setup ask. Nothing outside the
|
|
7851
|
+
# package is located, installed or repaired.
|
|
7581
7852
|
shift || true
|
|
7582
7853
|
pd_resolver=$(run_ts pi-extensions/lib/pi-durable-runtime.ts resolve) || exit $?
|
|
7583
7854
|
exec node --import "$pd_resolver" "$REPO_DIR/pi/pi-durable/bootstrap.mjs" "$@"
|