@junghanacs/entwurf 0.41.1 → 0.45.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 +2 -1
- package/BASELINE.md +7 -5
- package/CHANGELOG.md +344 -0
- package/DELIVERY.md +37 -20
- package/ENTWURF.json +236 -0
- package/README.md +75 -65
- package/VERIFY.md +9 -9
- package/docs/external-mcp-host.md +1 -1
- package/docs/setup-clean-host.md +59 -33
- package/mcp/entwurf-bridge/dist/ENTWURF.json +236 -0
- package/mcp/entwurf-bridge/dist/mcp/entwurf-bridge/src/index.js +32 -190
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/agent-guidance.js +299 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-self-address.js +10 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-contract.js +23 -2
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-decider.js +7 -1
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-surface.js +23 -24
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-visible-resume.js +15 -11
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/fresh-call-composition.js +21 -10
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/herdr-fresh-call.js +4 -2
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/meta-sender-identity.js +5 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/mux-fresh-call.js +29 -24
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/mux-resume-call.js +18 -16
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/native-push/register.js +13 -2
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/omp-fresh-preflight.js +2 -2
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/pi-durable-fresh-preflight.js +1 -1
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/remote-pair-receive.js +25 -17
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/remote-pair-send.js +101 -32
- package/mcp/entwurf-bridge/dist/pi-extensions/meta-bridge-omp.js +57 -7
- package/mcp/entwurf-bridge/dist/pi-extensions/meta-bridge-pi-durable.js +23 -45
- package/mcp/entwurf-bridge/dist/scripts/remote-pair-receive.js +12 -12
- package/mcp/entwurf-bridge/src/index.ts +126 -321
- package/native/win32-x64/entwurf_win.node +0 -0
- package/native/win32-x64/manifest.json +2 -2
- package/package.json +5 -4
- package/pi-extensions/lib/agent-guidance.ts +341 -0
- package/pi-extensions/lib/entwurf-self-address.ts +13 -0
- package/pi-extensions/lib/entwurf-v2-contract.ts +25 -2
- package/pi-extensions/lib/entwurf-v2-decider.ts +7 -0
- package/pi-extensions/lib/entwurf-v2-surface.ts +27 -30
- package/pi-extensions/lib/entwurf-v2-visible-resume.ts +15 -16
- package/pi-extensions/lib/fresh-call-composition.ts +21 -10
- package/pi-extensions/lib/herdr-fresh-call.ts +4 -2
- package/pi-extensions/lib/meta-sender-identity.ts +4 -0
- package/pi-extensions/lib/mux-fresh-call.ts +29 -30
- package/pi-extensions/lib/mux-resume-call.ts +18 -19
- package/pi-extensions/lib/native-push/register.ts +17 -2
- package/pi-extensions/lib/omp-fresh-preflight.ts +2 -2
- package/pi-extensions/lib/pi-durable-fresh-preflight.ts +1 -1
- package/pi-extensions/lib/remote-pair-receive.ts +27 -19
- package/pi-extensions/lib/remote-pair-send.ts +124 -34
- package/pi-extensions/meta-bridge-omp.ts +69 -7
- package/pi-extensions/meta-bridge-pi-durable.ts +23 -64
- package/run.sh +182 -22
- package/scripts/build-bridge.sh +10 -0
- package/scripts/check-agy-sender-identity.ts +6 -4
- package/scripts/check-capability-bundle-reach.ts +86 -0
- package/scripts/check-entwurf-self-address.ts +37 -6
- package/scripts/check-entwurf-v2-contract.ts +24 -2
- package/scripts/check-entwurf-v2-production.ts +45 -31
- package/scripts/check-entwurf-v2-surface.ts +94 -98
- package/scripts/check-gate-qualification.ts +17 -13
- package/scripts/check-mux-resume-call.ts +20 -3
- package/scripts/check-native-push-adapter.ts +4 -0
- package/scripts/check-native-push-register.ts +32 -82
- package/scripts/check-remote-pair-installed.ts +2 -2
- package/scripts/dev-bin.sh +112 -1
- package/scripts/lib/chain-terminus-publication.ts +58 -0
- package/scripts/lib/codex-fresh-live-protocol.ts +30 -0
- package/scripts/lib/codex-fresh-source-receipts.ts +112 -3
- package/scripts/lib/pi-durable-fresh-live-receipts.ts +29 -1
- package/scripts/mutants/agent-guidance.json +160 -0
- package/scripts/mutants/bridge-boot-resume.json +6 -8
- package/scripts/mutants/capability-bundle-reach.json +124 -0
- package/scripts/mutants/chain-terminus-publication.json +17 -0
- package/scripts/mutants/codex-native.json +98 -0
- package/scripts/mutants/control-send-receipt.json +23 -6
- package/scripts/mutants/mux-fresh-call.json +30 -4
- package/scripts/mutants/mux-resume-call.json +28 -3
- package/scripts/mutants/native-push-paused.json +17 -0
- package/scripts/mutants/omp-fresh.json +13 -2
- package/scripts/mutants/pack-install.json +46 -0
- package/scripts/mutants/pi-durable.json +17 -5
- package/scripts/mutants/remote-pair-display.json +3 -3
- package/scripts/mutants/remote-pair-receive.json +41 -11
- package/scripts/mutants/remote-pair-roundtrip.json +67 -7
- package/scripts/mutants/remote-pair-send.json +177 -19
- package/scripts/mutants/remote-pair-transport.json +4 -4
- package/scripts/mutants/self-address.json +10 -10
- package/scripts/mutants/source-install.json +66 -0
- package/scripts/mutants/v2-surface.json +50 -64
- package/scripts/mutants/v2-visible-resume.json +11 -0
- package/scripts/remote-pair-receive.ts +12 -12
- package/scripts/smoke-agy-install-state.sh +121 -0
- package/scripts/smoke-agy-native-push-live.ts +13 -0
- package/scripts/smoke-codex-fresh-live.ts +50 -27
- package/scripts/smoke-entwurf-chain-live.ts +7 -9
- package/scripts/smoke-herdr-fresh-call-live.ts +4 -4
- package/scripts/smoke-herdr-plugin-build-live.ts +179 -50
- package/scripts/smoke-mux-lifecycle-live.ts +140 -8
- package/scripts/smoke-omp-fresh-live.ts +5 -4
- package/scripts/smoke-pi-durable-fresh-live.ts +29 -7
- package/scripts/smoke-setup-verdict.sh +27 -2
package/run.sh
CHANGED
|
@@ -184,6 +184,7 @@ Usage:
|
|
|
184
184
|
./run.sh check-pi-durable-beside # #129 mutant execution coordinate (VERIFY rule 3): run_vitest over four pi-durable beside tests (adapter, carrier/SDK-set verifier, preflight, owner join) — a pi-durable qualification lane gate argv. Same files check-tests-beside-behavior already runs.
|
|
185
185
|
./run.sh check-pi-durable-carrier # #144 mutant execution coordinate (VERIFY rule 3): run_vitest over the carrier test alone (relocation/resolver, owned-source emission and typecheck fence, package file list, task footer, the real bootstrap on the 1.2 SDK, stream transport, skills prompt) — the gate argv of the carrier claims, whose QKs live only in that file. Same file check-tests-beside-behavior already runs.
|
|
186
186
|
./run.sh check-pi-durable-native-module # #144 mutant execution coordinate (VERIFY rule 3): run_vitest over the bootstrap's native-module file and directory ingress test alone — the gate argv of the ingress claims, whose QKs live only in that file. Same file check-tests-beside-behavior already runs.
|
|
187
|
+
./run.sh check-remote-pair-beside # #149 mutant execution coordinate (VERIFY rule 3): run_vitest over the five remote-pair beside tests (send, receive, roundtrip, transport, mailbox body) — the gate argv of the remote-pair claims. Same files check-tests-beside-behavior already runs.
|
|
187
188
|
./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)
|
|
188
189
|
./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
|
|
189
190
|
./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
|
|
@@ -221,10 +222,10 @@ Usage:
|
|
|
221
222
|
./run.sh check-typing-call-fence # deterministic gate for the boundary nobody has broken: this product composes ARGUMENTS and LETTERS, never keystrokes. Scans every tracked production source under pi-extensions/ and mcp/ — comments blanked, string literals kept — for tmux send-keys/paste-buffer/load-buffer and herdr's PTY verbs (agent.prompt, agent.send_keys, pane.send_text, pane.input.set). Prose is exempt on purpose: the sentence stating the rule names the very calls it forbids, and a scanner that read prose could be made green by deleting the law. Carries its own oracle — the detector is proven on planted code, proven blind to comments, and the scanned inventory is measured
|
|
222
223
|
./run.sh check-resume-launch-identity # deterministic gate for resume-launch-identity.ts, the record-authoritative launch-identity leaf preserved through the visible-first cut (spawn-bg and all its callers are gone; this leaf answers "which being is this, and which conversation is theirs"). Temp meta-store fixture: gardenId→record.transcriptPath happy path with header cwd/provider/model; C3 integrity (header id ≠ record.nativeSessionId → refused, never resumed); #52 ADDRESSABLE read (a gid that no longer holds its nativeSessionId alone is refused from EITHER side — the plain targeted read would resume one transcript twice under two locks); cause fidelity per impossible resume incl. the F7 pin (recorded-but-deleted transcript → MISSING, not "no recorded model"); header↔gate SSOT. No spawn/socket/timer
|
|
223
224
|
./run.sh smoke-entwurf-v2-matrix-live # LIVE sentinel (0.11 Stage 0 step 5d-5, D4-b) — OUT of pnpm check, needs LIVE=1. Drives REAL production runEntwurfV2 deps over REAL OS objects, 4 cells: C1 control-socket (real pi --entwurf-control resident → RPC send → lock acquire→release ×1), C1b record-less socket (#50 C4: live record-less pi → EVERY intent rejected pre-probe record-less-socket, no lock, rendered hint names record authority + fresh-cut), C2 meta-mailbox deliverable (armed self-fetch citizen → real .msg enqueue, lock-free), C3 meta-mailbox guard (no armed receiver → reject, no garbage). Model-in-loop OUT (transport/lock/enqueue gate, GPT Q2); negative/timeout stay deterministic. Model: ENTWURF_LIVE_TARGET=<provider>/<model> (default openai-codex/gpt-5.6-luna). LIVE=1 ./run.sh smoke-entwurf-v2-matrix-live
|
|
224
|
-
./run.sh smoke-agy-native-push-live #
|
|
225
|
+
./run.sh smoke-agy-native-push-live # PAUSED (#152): exits 2 with [entwurf:paused] before it reads LIVE or AGY_CONVERSATION_ID — not a PASS and not a SKIP; OUT of pnpm check. Open no agy conversation for it while the pause holds. Its 봉인 8 native-push round trip (real antigravity adapter + register core + runEntwurfV2) runs only after a separate change lifts the pause.
|
|
225
226
|
./run.sh smoke-codex-native-push-live # on-demand LIVE acceptance for Codex native-push — needs LIVE=1 + CODEX_LIVE_THREAD_ID loaded by a visible TUI attached to the default app-server; probes the real UDS, routes through production runEntwurfV2, queues one exact-token turn without retry, re-probes, and leaves the visible TUI to show the model result
|
|
226
227
|
./run.sh smoke-codex-fresh-live # #95 RELEASE MUST: record-backed receipt fixture -> PUBLIC initial Pi fresh/callback in a session S that is NOT the app-server's -> Pi PUBLIC omitted-placement Codex fresh/exact callback, also in S -> Pi-to-Codex addressed native-push v2 -> Codex PUBLIC omitted-placement AND omitted-cwd fresh Pi/exact callback IN S beside its own TUI (lane B claim: the app-server env names another session, so S can only come from the caller's pane title; lane C claim: no cwd was asked for, so the directory can only come from the Codex caller's own record — and the Codex thread's own rollout session_meta.cwd, its pane and its record must all be the requested scratch, not the app-server's repo) -> Codex final v2 evidence. Needs LIVE=1 + ENTWURF_CODEX_APP_SERVER_PID + ENTWURF_CODEX_FRESH_MODEL + ENTWURF_CODEX_FRESH_PI_MODEL, and its ONE stable launch directory answered 'Trust' once in a plain codex -C there: codex records its direct consent per exact directory, so a directory with no answer at all opens a consent screen instead of a first turn. Only THIS gate treats that as a precondition and fails up front as codex-launch-cwd-undecided rather than timing out on a callback — an ordinary entwurf_fresh_call only prints the note and opens the window, because a human can answer the screen. The smoke PRINTS that directory as 'launch-cwd <dir>' and repeats it in the failure as a ready-to-run repair command — do not retype it from memory, and do not write it as \$TMPDIR/... (os.tmpdir() falls back to /tmp when TMPDIR is unset, so that spelling names a different directory in the filesystem root). The PID names the operator-owned app-server, which must sit in a DIFFERENT session from S; the smoke checks/prints all four coordinates, never starts/stops/guesses the app-server/session, never reads screen text, interrupts only its exact still-running Codex turn, and cleans only receipt-named window ids
|
|
227
|
-
./run.sh smoke-mux-lifecycle-live # RELEASE MUST integrated LIVE lifecycle acceptance for mux, through the REAL MCP surface — OUT of pnpm check, needs LIVE=1 and spends model turns (two pi siblings: native + recorded-ACP provider, each resumed once; one Claude Code sibling). tools/call fresh_call -> nonce callback sender envelope -> v2 control send landing in the sibling's own transcript -> resume_call REFUSED while live (window count unchanged) -> stable-handle close (pane gone, socket dead, record kept) -> dormant delivery refused honestly -> public entwurf_resume_call with LAUNCH and OBSERVATION receipts kept apart, same-gid socket alive, zero new citizens, zero lock residue, resumed pane_start_path == RECORD cwd (separate tmux query), transcript byte-identical across the resume -> v2 recall of the pre-close fact. claude-code resume of the still-live sibling refused target-live, no window opened and no lock residue (its dormant same-id resume is deterministic-only, #147 L2). LIVE=1 ./run.sh smoke-mux-lifecycle-live
|
|
228
|
+
./run.sh smoke-mux-lifecycle-live # RELEASE MUST integrated LIVE lifecycle acceptance for mux, through the REAL MCP surface — OUT of pnpm check, needs LIVE=1 and spends model turns (two pi siblings: native + recorded-ACP provider, each resumed once; one Claude Code sibling). tools/call fresh_call (the native pi cell names a caller-distinct seat and tmux is asked where the pane landed, #108) -> nonce callback sender envelope -> v2 control send landing in the sibling's own transcript -> resume_call REFUSED while live (window count unchanged) -> stable-handle close (pane gone, socket dead, record kept) -> dormant delivery refused honestly -> public entwurf_resume_call with LAUNCH and OBSERVATION receipts kept apart, same-gid socket alive, zero new citizens, zero lock residue, resumed pane_start_path == RECORD cwd (separate tmux query), transcript byte-identical across the resume -> v2 recall of the pre-close fact. claude-code resume of the still-live sibling refused target-live, no window opened and no lock residue (its dormant same-id resume is deterministic-only, #147 L2). LIVE=1 ./run.sh smoke-mux-lifecycle-live
|
|
228
229
|
./run.sh check-meta-facts # deterministic gate for the meta-facts projection (#65): drives the REAL CLI — full-record join, parse-before-uniqueness, no-winner duplicates, drift/symlink/invalid-UTF-8 defects in-band, deterministic bytes, exit contract 0/2/3, dispatch+emit reachability
|
|
229
230
|
./run.sh check-herdr-plugin # deterministic gate for the #116 M2-b herdr plugin (`plugins/herdr/`): STATIC manifest shape for herdr 0.9.0 + the forbidden sections ([[startup]]/[[events]]/[[actions]]/[[link_handlers]]) asserted absent ([[build]] is check-herdr-plugin-build's subject since #116 M3-b3); BEHAVIOURAL drive of the REAL pane entry against a stub `entwurf` and stub HERDR_BIN_PATH that LOG every call — exactly one `herdr integration status` + one `entwurf peer-facts` + one `herdr agent list` per open, in that order (counted, not claimed), through ONE spawn call site; the four blocks each naming their own owner/observation time with no aggregate verdict; the D3=C fallback reading the activation ledger from the path its WRITER owns (XDG state, never the runtime's data root) and only when the phase is `active`, with ENTWURF_BIN and PATH still winning outright; the citizen axis alone skipping by name when no binary is reachable; every broken-evidence state a named red rather than a crash or an empty table, and a selected herdr atom that is `outdated`/`needs repair` as block [1]'s OWN red (`not installed` stays a reported skip); ambiguous never first-wins, diagnostics shown, activity in its own column, and zero writes to the plugin state/config dirs. No herdr binary, no Entwurf install
|
|
230
231
|
./run.sh check-herdr-runtime-bootstrap # deterministic gate for the #116 M3-b1 runtime leaf (`plugins/herdr/lib/runtime-bootstrap.mjs`): the Entwurf-owned stable active root as a REAL directory (nothing is put on PATH — the scoped wiring above names ABSOLUTE commands under it), ownership decided BEFORE the first mkdir, disk facts from lstat (a dangling symlink is not 'absent' and a link into another tree is not a directory), exact name@version + compiled entry + all three required bins present AND executable + a real `check-bridge`, the plugin-owned artifact lock coherent with the checkout, the owned npm cache and --ignore-scripts, a CERTIFIED journal (non-object/array/scalar/blank-identity/phase-contradicting-digest all refused) as the only ownership proof, all EIGHT active/staging/previous combinations named, the last good runtime never lost (torn swap AND corrupt-active beside a good backup), prior provenance carried across an install that may not finish, a failed candidate leaving the running runtime byte-identical, idempotent same-spec reinstall, a journal believed only while the disk backs it, a preflight-then-mutate inverse taking runtime last, and the refusal to record Herdr's commit as ours. NETWORK ZERO, npm ZERO — it drives a FIXTURE package, so the ACTUAL package proof lives in check-pack-install
|
|
@@ -240,6 +241,8 @@ Usage:
|
|
|
240
241
|
./run.sh check-entwurf-deliverability # deterministic gate (SE-1/SE-2 slice 2c): conversational-mailbox deliverability predicate — computeMetaReceiverActive (recordBacked ∧ ownerAlive ∧ watchArmed) + mailboxConversationalDeliverable (self-fetch AND active); direct-inject pi refused (SE-1), self-fetch dead/unarmed refused (SE-2); self-address shares the same atom
|
|
241
242
|
./run.sh check-native-push-adapter # deterministic gate (봉인 3/8): native-push adapter leaf (antigravity) via a FAKE runner — FULL pid scan (not head -1), dead vs indeterminate, VOLATILE route re-discovery (no cache), send argv+ANTIGRAVITY_LS_ADDRESS env, non-zero exit throws, NO adapter-level retry (executor-owned), resolveNativePushAdapter fail-fast
|
|
242
243
|
./run.sh check-native-push-register # deterministic gate (봉인 5): registerNativeConversation (entwurf_register_native core) via fake adapter + isolated mkdtemp store — live probe→CREATE, re-register→ATTACH (same gid, cwd refreshed, no dup), not-live probe→REFUSE (throws, no record), receiver-marker abstinence (보정① source guard)
|
|
244
|
+
./run.sh check-native-push-paused # #152 mutant execution coordinate: run_vitest over paused.test.ts alone — dispatch, sender replyable and entwurf_register_native refuse a paused backend (antigravity) before any probe. Same file check-tests-beside-behavior already runs.
|
|
245
|
+
./run.sh check-chain-terminus-publication # mutant execution coordinate: run_vitest over test/chain-terminus-publication.test.ts alone — smoke-entwurf-chain-live's terminus wait ends only on a body AND inbox.signal, inside the same deadline.
|
|
243
246
|
./run.sh check-agy-sender-identity # deterministic gate (#46 sender lane): WHO is calling the bridge — real agy hook as a child process writes an antigravity sender marker keyed by its PARENT pid (never on upsert failure), and resolveTrustedMetaSenderIdentity over isolated stores yields 0→null / 1→identity on EITHER backend / two distinct live identities on one owner pid→THROW (never guess, never downgrade to anonymous). This is what turns an agy send from external-mcp/unknown-host into a replyable garden citizen
|
|
244
247
|
./run.sh check-package-source-routing # deterministic gate (#29): package-source -> install-root mapping + fail-fast routing (local/git/npm/missing/project/no-source × local+remote, self-root, resume), no backend
|
|
245
248
|
./run.sh new-session-id # print one fresh garden id from the generateSessionId SSOT (#50 C2: no launcher injection — the record layer is the consumer; pi mints its own session id)
|
|
@@ -338,6 +341,7 @@ Usage:
|
|
|
338
341
|
./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
|
|
339
342
|
./run.sh check-pack # publish gate (dry-run): npm pack --dry-run + tarball invariants (runtime-critical present, dev residue absent)
|
|
340
343
|
./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
|
|
344
|
+
./run.sh check-pack-argv-matcher # pure self-test of check-pack-install's dispatcher-verb leak refusal (the copilot and codex-app-server cells): a front-loaded or trailing leak in ~260 KB is refused where the retired printf | grep -qF shape misses it (teeth control measured each run), clean output passes silently, a failing grep is refused by name, and both cells still return through it; snapshot-safe qualification oracle, also run first inside check-pack-install
|
|
341
345
|
./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
|
|
342
346
|
./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
|
|
343
347
|
./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)
|
|
@@ -1402,6 +1406,26 @@ check_entwurf_v2_native_push() {
|
|
|
1402
1406
|
run_vitest pi-extensions/lib/entwurf-v2-native-push.test.ts
|
|
1403
1407
|
}
|
|
1404
1408
|
|
|
1409
|
+
check_native_push_paused() {
|
|
1410
|
+
# MUTANT EXECUTION COORDINATE, not a discovery path — paused.test.ts is found by
|
|
1411
|
+
# check-tests-beside-behavior like every other beside-behaviour lane. This case exists
|
|
1412
|
+
# because the AGY-NATIVE-PUSH-PAUSED mutant (#152) names it as gate argv; the narrow file
|
|
1413
|
+
# filter keeps that mutant from re-running every beside-behaviour test. It stays inside
|
|
1414
|
+
# check:contracts for the same reason as check-entwurf-v2-native-push above.
|
|
1415
|
+
section "native-push pause policy (#152, mutant execution coordinate)"
|
|
1416
|
+
run_vitest pi-extensions/lib/native-push/paused.test.ts
|
|
1417
|
+
}
|
|
1418
|
+
|
|
1419
|
+
check_chain_terminus_publication() {
|
|
1420
|
+
# MUTANT EXECUTION COORDINATE for the CHAIN-TERMINUS-WAITS-FOR-SIGNAL mutant: run_vitest over
|
|
1421
|
+
# test/chain-terminus-publication.test.ts alone — the terminus wait of smoke-entwurf-chain-live
|
|
1422
|
+
# (scripts/lib/chain-terminus-publication.ts) driven offline through the two-write publication
|
|
1423
|
+
# enqueueMetaMessage produces (.msg first, inbox.signal last). It stays inside check:contracts
|
|
1424
|
+
# because a gate a mutant names must be reachable outside qualification.
|
|
1425
|
+
section "chain terminus publication wait (mutant execution coordinate)"
|
|
1426
|
+
run_vitest test/chain-terminus-publication.test.ts
|
|
1427
|
+
}
|
|
1428
|
+
|
|
1405
1429
|
check_entwurf_v2_surface() {
|
|
1406
1430
|
# Deterministic gate for 0.11 Stage 0 step 5d-3a: the ctx-free surface adapter
|
|
1407
1431
|
# (entwurf-v2-surface.ts) + the entwurf-control.ts wiring contract. Proves the pure parts:
|
|
@@ -1663,6 +1687,12 @@ check_mux_fresh_call() {
|
|
|
1663
1687
|
run_vitest test/mux-fresh-call.test.ts test/fresh-call-composition.contract.test.ts test/copilot-fresh-preflight.test.ts test/codex-fresh-preflight.test.ts test/fresh-call-surfaces.contract.test.ts test/fresh-call-provider.contract.test.ts test/omp-fresh-bootstrap.contract.test.ts test/tmux-coordinate-row.test.ts test/codex-fresh-live-protocol.test.ts test/codex-caller-seat.test.ts test/pi-durable-fresh-live-receipts.test.ts test/codex-socket-listener.test.ts
|
|
1664
1688
|
}
|
|
1665
1689
|
|
|
1690
|
+
check_agent_guidance_beside() {
|
|
1691
|
+
# #154: mutation execution coordinate only; the beside-behavior glob already discovers it.
|
|
1692
|
+
# Keep the existing run_vitest reporter/attribution, without a build or package lifecycle.
|
|
1693
|
+
run_vitest pi-extensions/lib/agent-guidance.test.ts
|
|
1694
|
+
}
|
|
1695
|
+
|
|
1666
1696
|
check_pi_durable_beside() {
|
|
1667
1697
|
# #129: the pi-durable qualification lane's execution coordinate (VERIFY 「Four rules」 rule 3). It is
|
|
1668
1698
|
# not a discovery path — check-tests-beside-behavior already finds these files. It exists because the
|
|
@@ -1693,6 +1723,14 @@ check_pi_durable_native_module() {
|
|
|
1693
1723
|
run_vitest pi-extensions/lib/pi-durable-native-module.test.ts
|
|
1694
1724
|
}
|
|
1695
1725
|
|
|
1726
|
+
check_remote_pair_beside() {
|
|
1727
|
+
# #149: the execution coordinate for the declared remote pairs (VERIFY 「Four rules」 rule 3). Not a
|
|
1728
|
+
# discovery path — check-tests-beside-behavior already finds these files. Every remote-pair claim
|
|
1729
|
+
# carries its QK in one of them, and the qualification runner re-invokes a gate argv once per
|
|
1730
|
+
# mutant, so the narrow filter keeps one mutant from re-running every beside test.
|
|
1731
|
+
run_vitest pi-extensions/lib/remote-pair-send.test.ts pi-extensions/lib/remote-pair-receive.test.ts pi-extensions/lib/remote-pair-roundtrip.test.ts pi-extensions/lib/remote-pair-transport.test.ts pi-extensions/lib/meta-mailbox-body.test.ts
|
|
1732
|
+
}
|
|
1733
|
+
|
|
1696
1734
|
smoke_mux_fresh_call_live() {
|
|
1697
1735
|
# LIVE acceptance for entwurf_fresh_call — OUT of pnpm check, needs LIVE=1, spends two model
|
|
1698
1736
|
# turns. The one axis no deterministic gate reaches: a real window, a real runtime, a real first
|
|
@@ -4215,6 +4253,106 @@ check_pack_pin_matcher() {
|
|
|
4215
4253
|
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)"
|
|
4216
4254
|
}
|
|
4217
4255
|
|
|
4256
|
+
# The dispatcher-verb leak refusal of check-pack-install's copilot and codex-app-server cells: the
|
|
4257
|
+
# vendor stand-in prints one `ARG<…>` line per argument, and the dispatcher verb among them means it
|
|
4258
|
+
# leaked into the vendor's argv. The search it replaces, `printf '%s' "$op_out" | grep -qF …`,
|
|
4259
|
+
# refused only when that pipeline exited 0: bash's printf writes a multi-line value a line at a
|
|
4260
|
+
# time, `-q` closes the pipe at the first match, and pipefail turned the producer's 141 into a pass
|
|
4261
|
+
# (pipefail audit, 2026-10-23: latent, no false green observed in CI). A here-string has no
|
|
4262
|
+
# producer to kill, and the verdict reads the search's own rc: 1 (clean) passes, 0 (leaked) and any
|
|
4263
|
+
# other rc (the search itself failed) refuse with the cell's diagnostic.
|
|
4264
|
+
pack_install_refuse_argv_leak() {
|
|
4265
|
+
local label="$1" out="$2" token="$3" argv_search_rc=0
|
|
4266
|
+
grep -qF -- "$token" <<<"$out" || argv_search_rc=$?
|
|
4267
|
+
case "$argv_search_rc" in
|
|
4268
|
+
1) return 0 ;;
|
|
4269
|
+
0)
|
|
4270
|
+
fail "[check-pack-install] installed 'entwurf $label' leaked the dispatcher verb into the vendor's argv:"
|
|
4271
|
+
echo "$out" | tail -8 | sed 's/^/ /' >&2
|
|
4272
|
+
;;
|
|
4273
|
+
*) fail "[check-pack-install] installed 'entwurf $label': the dispatcher-verb search itself failed (rc=$argv_search_rc) — refused, never read as no leak" ;;
|
|
4274
|
+
esac
|
|
4275
|
+
return 1
|
|
4276
|
+
}
|
|
4277
|
+
|
|
4278
|
+
# Self-test of pack_install_refuse_argv_leak — the SAME verdict both cells return through, never a
|
|
4279
|
+
# re-typed predicate — on synthetic stand-in output: its pass/refuse result and the diagnostic that
|
|
4280
|
+
# names why, one QK per verdict, plus a text check that both cells still return through it.
|
|
4281
|
+
# Snapshot-safe like check-pack-pin-matcher, and run first inside check-pack-install. The large
|
|
4282
|
+
# fixtures put the leak ahead of ~260 KB, past the default 64 KiB pipe buffer. The teeth control
|
|
4283
|
+
# measures, on THIS host, that the retired pipe shape misses it there (grep matched, the pipeline
|
|
4284
|
+
# still failed); a fixture that stops biting is a red of its own, not a vacuous green.
|
|
4285
|
+
check_pack_argv_matcher() {
|
|
4286
|
+
local cop_leak cop_clean cx_leak cx_clean big_tail big_leak_front big_leak_end big_clean
|
|
4287
|
+
local rc out label fx token missed shim bound_copilot bound_codex red=0 teeth=()
|
|
4288
|
+
cop_leak=$'FLAG=[EXTENSIONS]\nARG<copilot>\nARG<-p>\nARG<hi>\nARG<--model>\nARG<auto>\nARG<--yolo>'
|
|
4289
|
+
cop_clean=$'FLAG=[EXTENSIONS]\nARG<-p>\nARG<hi>\nARG<--model>\nARG<auto>\nARG<--yolo>'
|
|
4290
|
+
cx_leak=$'ARG<codex-app-server>\nARG<app-server>\nARG<--listen>\nARG<unix:///h/.codex/app-server.sock>\nARG<--config>\nARG<probe=1>'
|
|
4291
|
+
cx_clean=$'ARG<app-server>\nARG<--listen>\nARG<unix:///h/.codex/app-server.sock>\nARG<--config>\nARG<probe=1>'
|
|
4292
|
+
big_tail=$(seq 1 24000 | sed 's/^/ARG<x/; s/$/>/')
|
|
4293
|
+
big_leak_front=$'FLAG=[EXTENSIONS]\nARG<copilot>\n'"$big_tail"
|
|
4294
|
+
big_leak_end="$big_tail"$'\nARG<codex-app-server>'
|
|
4295
|
+
big_clean=$'FLAG=[EXTENSIONS]\n'"$big_tail"
|
|
4296
|
+
|
|
4297
|
+
# Teeth control: the defect's own signature — grep matched (its PIPESTATUS is 0) yet the pipeline
|
|
4298
|
+
# failed. A grep error is not teeth, and an exact 141 is not required (an inherited ignored
|
|
4299
|
+
# SIGPIPE still makes the producer fail, and the miss is just as real).
|
|
4300
|
+
{ printf '%s' "$big_leak_front" | grep -qF -- 'ARG<copilot>'; teeth=("$?" "${PIPESTATUS[@]}"); } || true
|
|
4301
|
+
if [ "${teeth[0]}" -eq 0 ] || [ "${teeth[2]:-unset}" != 0 ]; then
|
|
4302
|
+
fail "[check-pack-argv-matcher] the planted fixture has no teeth here: the retired printf | grep -qF shape did not miss a leak it matched (pipeline=${teeth[0]} PIPESTATUS=${teeth[*]:1}), so the cells below would prove nothing — enlarge it"
|
|
4303
|
+
return 1
|
|
4304
|
+
fi
|
|
4305
|
+
|
|
4306
|
+
missed=""
|
|
4307
|
+
for fx in "copilot cop_leak ARG<copilot>" "codex-app-server cx_leak ARG<codex-app-server>" "copilot big_leak_front ARG<copilot>" "codex-app-server big_leak_end ARG<codex-app-server>"; do
|
|
4308
|
+
read -r label fx token <<<"$fx"
|
|
4309
|
+
rc=0; out=$(pack_install_refuse_argv_leak "$label" "${!fx}" "$token" 2>&1) || rc=$?
|
|
4310
|
+
{ [ "$rc" -eq 1 ] && [[ "$out" == *"leaked the dispatcher verb"* ]]; } || missed+=" $fx(rc=$rc)"
|
|
4311
|
+
done
|
|
4312
|
+
if [ -n "$missed" ]; then
|
|
4313
|
+
fail "[QK:PACK-INSTALL-ARGV-LEAK-CAUGHT] a leaked dispatcher verb must be refused as a leak wherever it sits and however long the output — passed or misnamed:$missed"
|
|
4314
|
+
red=1
|
|
4315
|
+
fi
|
|
4316
|
+
|
|
4317
|
+
missed=""
|
|
4318
|
+
for fx in "copilot cop_clean ARG<copilot>" "codex-app-server cx_clean ARG<codex-app-server>" "copilot big_clean ARG<copilot>"; do
|
|
4319
|
+
read -r label fx token <<<"$fx"
|
|
4320
|
+
rc=0; out=$(pack_install_refuse_argv_leak "$label" "${!fx}" "$token" 2>&1) || rc=$?
|
|
4321
|
+
{ [ "$rc" -eq 0 ] && [ -z "$out" ]; } || missed+=" $fx(rc=$rc)"
|
|
4322
|
+
done
|
|
4323
|
+
if [ -n "$missed" ]; then
|
|
4324
|
+
fail "[QK:PACK-INSTALL-ARGV-CLEAN-PASSES] stand-in output without the verb must pass silently, small or large — refused:$missed"
|
|
4325
|
+
red=1
|
|
4326
|
+
fi
|
|
4327
|
+
|
|
4328
|
+
# A search that fails is refused, never read as clean: a `grep` that exits 2 on PATH.
|
|
4329
|
+
shim=$(mktemp -d "${TMPDIR:-/tmp}/entwurf-argv-shim.XXXXXX")
|
|
4330
|
+
printf '#!/bin/sh\n: > "%s/reached"\nexit 2\n' "$shim" > "$shim/grep"
|
|
4331
|
+
chmod +x "$shim/grep"
|
|
4332
|
+
rc=0; out=$(PATH="$shim:$PATH"; pack_install_refuse_argv_leak copilot "$cop_clean" 'ARG<copilot>' 2>&1) || rc=$?
|
|
4333
|
+
if [ ! -e "$shim/reached" ]; then
|
|
4334
|
+
rm -rf "$shim"
|
|
4335
|
+
fail "[check-pack-argv-matcher] the failing grep stand-in was never reached, so the search-error cell would prove nothing"
|
|
4336
|
+
return 1
|
|
4337
|
+
fi
|
|
4338
|
+
rm -rf "$shim"
|
|
4339
|
+
if [ "$rc" -ne 1 ] || [[ "$out" != *"search itself failed (rc=2)"* ]]; then
|
|
4340
|
+
fail "[QK:PACK-INSTALL-ARGV-SEARCH-ERROR-REFUSED] a search that fails (grep rc=2) must refuse the cell and say so, never pass it as clean — got rc=$rc: ${out:-<silent>}"
|
|
4341
|
+
red=1
|
|
4342
|
+
fi
|
|
4343
|
+
|
|
4344
|
+
# Both cells must return through this verdict; a reverted cell would leave the cells above green.
|
|
4345
|
+
bound_copilot=$(grep -cxF " pack_install_refuse_argv_leak copilot \"\$op_out\" 'ARG<copilot>' || return 1" "$REPO_DIR/run.sh" || true)
|
|
4346
|
+
bound_codex=$(grep -cxF " pack_install_refuse_argv_leak codex-app-server \"\$op_out\" 'ARG<codex-app-server>' || return 1" "$REPO_DIR/run.sh" || true)
|
|
4347
|
+
if [ "$bound_copilot" != 1 ] || [ "$bound_codex" != 1 ]; then
|
|
4348
|
+
fail "[QK:PACK-INSTALL-ARGV-CELLS-BOUND] the copilot and codex-app-server leak refusals of check-pack-install must each return through pack_install_refuse_argv_leak once (copilot=$bound_copilot codex=$bound_codex)"
|
|
4349
|
+
red=1
|
|
4350
|
+
fi
|
|
4351
|
+
|
|
4352
|
+
[ "$red" -eq 0 ] || return 1
|
|
4353
|
+
echo "[check-pack-argv-matcher] ok — the dispatcher-verb refusal refuses a leak at the front or end of ~260 KB (where the retired pipe shape misses it), passes clean output silently, refuses a failed search by name, and both check-pack-install cells return through it"
|
|
4354
|
+
}
|
|
4355
|
+
|
|
4218
4356
|
# #130 — the pi-durable carrier proved on ACTUAL npm consumers of the candidate tarball.
|
|
4219
4357
|
# $1 tarball $2 the gate's npm sandbox $3 an existing Entwurf-only npm consumer root inside it.
|
|
4220
4358
|
# Cells: L1s (Entwurf alone, $3), L1 (project-local beside an aligned host pi-coding-agent at the
|
|
@@ -4531,6 +4669,7 @@ pack_install_sentinel_untouched() {
|
|
|
4531
4669
|
|
|
4532
4670
|
_check_pack_install_impl() {
|
|
4533
4671
|
check_pack_pin_matcher || return 1
|
|
4672
|
+
check_pack_argv_matcher || return 1
|
|
4534
4673
|
check_pack_consumer_roots || return 1
|
|
4535
4674
|
|
|
4536
4675
|
# Heavy publish gate. Runs the remaining three checks in #13's
|
|
@@ -5939,11 +6078,9 @@ JS
|
|
|
5939
6078
|
return 1
|
|
5940
6079
|
fi
|
|
5941
6080
|
done
|
|
5942
|
-
|
|
5943
|
-
|
|
5944
|
-
|
|
5945
|
-
return 1
|
|
5946
|
-
fi
|
|
6081
|
+
# A refusal on a match whose search cannot read a SIGPIPE as "no leak"; the verdict is the
|
|
6082
|
+
# leaf check-pack-argv-matcher drives.
|
|
6083
|
+
pack_install_refuse_argv_leak copilot "$op_out" 'ARG<copilot>' || return 1
|
|
5947
6084
|
echo "[check-pack-install] installed 'entwurf copilot' reached the vendor with the scan flag and the managed argv"
|
|
5948
6085
|
|
|
5949
6086
|
# #95 — the managed app-server SPELLING, from the INSTALLED package. Same packaging risk as
|
|
@@ -5990,11 +6127,7 @@ process.stdout.write(resolveCodexDefaultSocketPath(process.env));
|
|
|
5990
6127
|
return 1
|
|
5991
6128
|
fi
|
|
5992
6129
|
done
|
|
5993
|
-
|
|
5994
|
-
fail "[check-pack-install] installed 'entwurf codex-app-server' leaked the dispatcher verb into the vendor's argv:"
|
|
5995
|
-
echo "$op_out" | tail -8 | sed 's/^/ /' >&2
|
|
5996
|
-
return 1
|
|
5997
|
-
fi
|
|
6130
|
+
pack_install_refuse_argv_leak codex-app-server "$op_out" 'ARG<codex-app-server>' || return 1
|
|
5998
6131
|
echo "[check-pack-install] installed 'entwurf codex-app-server' reached the vendor at the INSTALLED resolver's address ($cx_want)"
|
|
5999
6132
|
|
|
6000
6133
|
# The generation verb must reach its verdict from under node_modules. On the
|
|
@@ -6641,7 +6774,11 @@ setup_all() {
|
|
|
6641
6774
|
else
|
|
6642
6775
|
bins_rc=0; expose_dev_bin || bins_rc=$?
|
|
6643
6776
|
if [ "$bins_rc" -eq 0 ]; then
|
|
6644
|
-
setup_result bins PASS "source operator + helper bins exposed and certified
|
|
6777
|
+
setup_result bins PASS "source operator + helper bins exposed and certified; no other root in this PATH or the npm/pnpm global bin dir (other shells not covered)"
|
|
6778
|
+
elif [ "$bins_rc" -eq 5 ]; then
|
|
6779
|
+
setup_result bins FAIL "another entwurf root is still selectable (named above; nothing removed) — remove it, then re-run setup"
|
|
6780
|
+
elif [ "$bins_rc" -eq 6 ]; then
|
|
6781
|
+
setup_result bins FAIL "the package manifest or a package manager's global bin dir could not be inspected (named above) — refused, not read as clean"
|
|
6645
6782
|
elif [ "$bins_rc" -eq 3 ]; then
|
|
6646
6783
|
setup_result bins FAIL "foreign helper bin(s) refused (named above; every unit was attempted) — repair them, then re-run setup"
|
|
6647
6784
|
else
|
|
@@ -7030,7 +7167,13 @@ expose_dev_bin() {
|
|
|
7030
7167
|
return 1
|
|
7031
7168
|
fi
|
|
7032
7169
|
|
|
7033
|
-
|
|
7170
|
+
# A certified winner is not yet a developer install: a later PATH entry or a package manager's
|
|
7171
|
+
# global bin dir may still hold another root under the same names, which a shell with another
|
|
7172
|
+
# PATH order (non-interactive SSH) selects. exclusive names it (5) or refuses an uninspectable
|
|
7173
|
+
# package manifest or package manager (6); nothing is removed.
|
|
7174
|
+
local ex_rc=0
|
|
7175
|
+
bash "$REPO_DIR/scripts/dev-bin.sh" exclusive || ex_rc=$?
|
|
7176
|
+
[ "$rc" -eq 0 ] && return "$ex_rc"
|
|
7034
7177
|
if [ "$rc" -eq 3 ]; then
|
|
7035
7178
|
echo "[setup] dev-bin: foreign helper bin path(s) refused, never clobbered — the refused names are in the summary above." >&2
|
|
7036
7179
|
echo "[setup] Every unit was attempted independently; the source operator itself is certified." >&2
|
|
@@ -7594,6 +7737,12 @@ case "$cmd" in
|
|
|
7594
7737
|
check-entwurf-v2-native-push)
|
|
7595
7738
|
check_entwurf_v2_native_push
|
|
7596
7739
|
;;
|
|
7740
|
+
check-native-push-paused)
|
|
7741
|
+
check_native_push_paused
|
|
7742
|
+
;;
|
|
7743
|
+
check-chain-terminus-publication)
|
|
7744
|
+
check_chain_terminus_publication
|
|
7745
|
+
;;
|
|
7597
7746
|
check-entwurf-control-rpc)
|
|
7598
7747
|
check_entwurf_control_rpc
|
|
7599
7748
|
;;
|
|
@@ -7639,6 +7788,9 @@ case "$cmd" in
|
|
|
7639
7788
|
check-mux-fresh-call)
|
|
7640
7789
|
check_mux_fresh_call
|
|
7641
7790
|
;;
|
|
7791
|
+
check-agent-guidance-beside)
|
|
7792
|
+
check_agent_guidance_beside
|
|
7793
|
+
;;
|
|
7642
7794
|
check-pi-durable-beside)
|
|
7643
7795
|
check_pi_durable_beside
|
|
7644
7796
|
;;
|
|
@@ -7648,6 +7800,9 @@ case "$cmd" in
|
|
|
7648
7800
|
check-pi-durable-native-module)
|
|
7649
7801
|
check_pi_durable_native_module
|
|
7650
7802
|
;;
|
|
7803
|
+
check-remote-pair-beside)
|
|
7804
|
+
check_remote_pair_beside
|
|
7805
|
+
;;
|
|
7651
7806
|
check-mux-resume-call)
|
|
7652
7807
|
check_mux_resume_call
|
|
7653
7808
|
;;
|
|
@@ -7994,12 +8149,14 @@ case "$cmd" in
|
|
|
7994
8149
|
(cd "$REPO_DIR" && bash scripts/smoke-pi-provider-state.sh)
|
|
7995
8150
|
;;
|
|
7996
8151
|
smoke-agy-native-push-live)
|
|
7997
|
-
#
|
|
7998
|
-
#
|
|
7999
|
-
#
|
|
8000
|
-
#
|
|
8001
|
-
#
|
|
8002
|
-
#
|
|
8152
|
+
# PAUSED (#152): the script exits 2 [entwurf:paused] before it reads LIVE or
|
|
8153
|
+
# AGY_CONVERSATION_ID — not a PASS and not a SKIP. The body after that exit is the 봉인 8
|
|
8154
|
+
# LIVE acceptance for the native-push (agy) rail and runs only once a separate change lifts
|
|
8155
|
+
# the pause: REAL antigravity adapter + register core + runEntwurfV2 (production deps)
|
|
8156
|
+
# against a live agy conversation (probe route, register create/attach idempotency,
|
|
8157
|
+
# fire→native-push delivered, bogus-conv probe-indeterminate reject), meta-store isolated
|
|
8158
|
+
# to a temp dir, honest SKIP when LIVE!=1, doctor-static preflight FAIL before the agy
|
|
8159
|
+
# bridge is wired (③).
|
|
8003
8160
|
run_ts scripts/smoke-agy-native-push-live.ts
|
|
8004
8161
|
;;
|
|
8005
8162
|
smoke-codex-native-push-live)
|
|
@@ -8535,8 +8692,8 @@ case "$cmd" in
|
|
|
8535
8692
|
run_ts scripts/check-remote-pair-installed.ts "$@"
|
|
8536
8693
|
;;
|
|
8537
8694
|
remote-pair-receive)
|
|
8538
|
-
# #107 rail 1: the receiving host's entry for
|
|
8539
|
-
# check runs before the v2 dispatch, so a claim outside
|
|
8695
|
+
# #107 rail 1 / #149: the receiving host's entry for its declared remote pairs. The pair
|
|
8696
|
+
# check runs before the v2 dispatch, so a claim outside every declared pair mutates nothing;
|
|
8540
8697
|
# a match reaches the same record/owner/deliverability checks every local send does.
|
|
8541
8698
|
# Installed, run_ts runs its compiled twin; the sending host's ssh reaches it through the bin.
|
|
8542
8699
|
shift || true
|
|
@@ -8723,6 +8880,9 @@ case "$cmd" in
|
|
|
8723
8880
|
check-pack-pin-matcher)
|
|
8724
8881
|
check_pack_pin_matcher
|
|
8725
8882
|
;;
|
|
8883
|
+
check-pack-argv-matcher)
|
|
8884
|
+
check_pack_argv_matcher
|
|
8885
|
+
;;
|
|
8726
8886
|
check-pack-win32-carrier)
|
|
8727
8887
|
check_pack_win32_carrier
|
|
8728
8888
|
;;
|
package/scripts/build-bridge.sh
CHANGED
|
@@ -19,6 +19,16 @@ TSC="$REPO_DIR/node_modules/.bin/tsc"
|
|
|
19
19
|
[ -x "$TSC" ] || TSC=tsc
|
|
20
20
|
"$TSC" -p "$REPO_DIR/mcp/entwurf-bridge/tsconfig.build.json"
|
|
21
21
|
|
|
22
|
+
# One authored product-guidance source (#154). The fixed module-relative reader resolves
|
|
23
|
+
# ../../ENTWURF.json from dist/pi-extensions/lib/, so its deterministic exact copy belongs at
|
|
24
|
+
# the DIST ROOT, not beside the capability registry. This is transport, never another source.
|
|
25
|
+
GUIDANCE_SOURCE="$REPO_DIR/ENTWURF.json"
|
|
26
|
+
if [ ! -f "$GUIDANCE_SOURCE" ] || [ -L "$GUIDANCE_SOURCE" ]; then
|
|
27
|
+
echo "build-bridge: $GUIDANCE_SOURCE is missing or not a regular file" >&2
|
|
28
|
+
exit 1
|
|
29
|
+
fi
|
|
30
|
+
cp "$GUIDANCE_SOURCE" "$REPO_DIR/mcp/entwurf-bridge/dist/ENTWURF.json"
|
|
31
|
+
|
|
22
32
|
# The capability registry must TRAVEL WITH the bundle.
|
|
23
33
|
#
|
|
24
34
|
# `metaCapabilitiesFilePath()` computes its path from the module's own location.
|
|
@@ -442,7 +442,7 @@ try {
|
|
|
442
442
|
}
|
|
443
443
|
|
|
444
444
|
ok(
|
|
445
|
-
"antigravity
|
|
445
|
+
"antigravity stays a native-push backend (its reply rail is native-push; while #152 pauses it, that rail answers replyable:false by policy)",
|
|
446
446
|
nativePushSupported("antigravity"),
|
|
447
447
|
);
|
|
448
448
|
ok(
|
|
@@ -455,10 +455,12 @@ try {
|
|
|
455
455
|
// probe can only be a broken registry or a probe that could not run at all. Folding that into
|
|
456
456
|
// replyable:false would tell the receiver a lie about this sender AND bury the defect — the
|
|
457
457
|
// exact Crash-Don't-Warn shape this lane exists to remove. It must propagate.
|
|
458
|
+
// The mapping is exercised on codex, the native-push backend that is not paused: antigravity is
|
|
459
|
+
// paused (#152) and answers false by policy without resolving an adapter (paused.test.ts).
|
|
458
460
|
{
|
|
459
461
|
const identity: Pick<MetaIdentity, "backend" | "nativeSessionId"> = {
|
|
460
|
-
backend: "
|
|
461
|
-
nativeSessionId: "
|
|
462
|
+
backend: "codex",
|
|
463
|
+
nativeSessionId: "thread-x",
|
|
462
464
|
};
|
|
463
465
|
const adapterWith = (probe: NativePushAdapter["probe"]) => ({ resolveAdapter: () => ({ probe }) });
|
|
464
466
|
|
|
@@ -466,7 +468,7 @@ try {
|
|
|
466
468
|
"probe alive → replyable true",
|
|
467
469
|
(await probeNativeSenderAlive(
|
|
468
470
|
identity,
|
|
469
|
-
adapterWith(async () => ({ status: "alive", route: { backend: "
|
|
471
|
+
adapterWith(async () => ({ status: "alive", route: { backend: "codex", socketPath: "/tmp/gate-codex.sock" } })),
|
|
470
472
|
)) === true,
|
|
471
473
|
);
|
|
472
474
|
|
|
@@ -32,6 +32,8 @@
|
|
|
32
32
|
* the installer copies, and the hook staged with only that closure runs.
|
|
33
33
|
* - D (#147 G3): the compiled Claude Code retire adapter resolves its helper beside itself,
|
|
34
34
|
* byte-identical to pi-extensions/lib/cc-retire-pidfd.py.
|
|
35
|
+
* - F (#154): the fixed guidance carrier has the canonical Root bytes, and its emitted
|
|
36
|
+
* reader exports that Root's version/tools (plain JSON expected data, not a source reader).
|
|
35
37
|
*
|
|
36
38
|
* Deterministic: filesystem, import, and ONE shipped-hook child process (C2) that writes a
|
|
37
39
|
* record into its own private store under a private HOME and XDG roots. No backend, vendor
|
|
@@ -48,6 +50,8 @@ import fs from "node:fs";
|
|
|
48
50
|
import os from "node:os";
|
|
49
51
|
import path from "node:path";
|
|
50
52
|
import { pathToFileURL } from "node:url";
|
|
53
|
+
import { isDeepStrictEqual } from "node:util";
|
|
54
|
+
import { FRESH_CUT_PRESCRIPTION } from "../pi-extensions/lib/meta-session.ts";
|
|
51
55
|
import { hookImportClosure, installerCopiedLibFiles, stageMetaHookBundle } from "./lib/meta-hook-bundle.ts";
|
|
52
56
|
import { reclaimOnExit } from "./lib/reclaim-on-exit.ts";
|
|
53
57
|
|
|
@@ -260,4 +264,86 @@ for (const copy of copies) {
|
|
|
260
264
|
);
|
|
261
265
|
}
|
|
262
266
|
|
|
267
|
+
// F — one guidance carrier/current-reader contract, rooted in canonical authored bytes (#154).
|
|
268
|
+
// Presence/type are preconditions. The reader is NOT first loaded here: since #154 C1-C the compiled
|
|
269
|
+
// retire adapter that D imports reaches it through entwurf-v2-visible-resume, so the module is
|
|
270
|
+
// already evaluated and cached. A carrier that still parses and validates (a byte divergence) loaded
|
|
271
|
+
// fine there and fails the intended F1 byte assertion; a carrier the reader refuses already threw at
|
|
272
|
+
// D's import, unlabelled. The import below is skipped on divergence so F1 stays the reported cause.
|
|
273
|
+
{
|
|
274
|
+
const carrier = path.join(BRIDGE_DIST, "ENTWURF.json");
|
|
275
|
+
const reader = path.join(BRIDGE_DIST, "pi-extensions", "lib", "agent-guidance.js");
|
|
276
|
+
for (const file of [carrier, reader]) {
|
|
277
|
+
const stat = fs.lstatSync(file, { throwIfNoEntry: false });
|
|
278
|
+
ok(
|
|
279
|
+
`F0: the compiled guidance file is regular and non-symlink: ${rel(file)}`,
|
|
280
|
+
stat !== undefined && stat.isFile() && !stat.isSymbolicLink(),
|
|
281
|
+
);
|
|
282
|
+
}
|
|
283
|
+
const rootBytes = fs.readFileSync(path.join(REPO, "ENTWURF.json"));
|
|
284
|
+
const root = JSON.parse(rootBytes.toString("utf8"));
|
|
285
|
+
const sameBytes = fs.readFileSync(carrier).equals(rootBytes);
|
|
286
|
+
const loaded = sameBytes ? await import(pathToFileURL(reader).href) : undefined;
|
|
287
|
+
ok(
|
|
288
|
+
"F1: the fixed guidance carrier has canonical Root bytes and its reader exports the current version/tools [QK:BUNDLE-REACH-GUIDANCE-CARRIER]",
|
|
289
|
+
sameBytes &&
|
|
290
|
+
isDeepStrictEqual(loaded?.ENTWURF_GUIDANCE_VERSION, {
|
|
291
|
+
schemaVersion: root.schemaVersion,
|
|
292
|
+
revision: root.revision,
|
|
293
|
+
}) &&
|
|
294
|
+
isDeepStrictEqual(loaded?.ENTWURF_TOOL_GUIDANCE, root.tools),
|
|
295
|
+
);
|
|
296
|
+
// F2 — the second authored family reaches the compiled reader too. Separate from F1 so a reader
|
|
297
|
+
// that mis-exports only the hints fails here while the carrier and tool payload stay green.
|
|
298
|
+
ok(
|
|
299
|
+
"F2: the compiled reader exports the canonical Root static reject hints [QK:BUNDLE-REACH-REJECT-HINTS]",
|
|
300
|
+
sameBytes && isDeepStrictEqual(loaded?.ENTWURF_REJECT_HINTS, root.rejectHints),
|
|
301
|
+
);
|
|
302
|
+
// F3 — the third family (#154 C1-C), separate for the same reason as F2.
|
|
303
|
+
ok(
|
|
304
|
+
"F3: the compiled reader exports the canonical Root visible-resume reject hints [QK:BUNDLE-REACH-RESUME-REJECT-HINTS]",
|
|
305
|
+
sameBytes && isDeepStrictEqual(loaded?.ENTWURF_RESUME_REJECT_HINTS, root.resumeRejectHints),
|
|
306
|
+
);
|
|
307
|
+
// F4 — the two tmux launch-stage families (#154 C1-D), one assertion because they enter together;
|
|
308
|
+
// this proves the compiled reader's exports, not the compiled mux renderers that read them.
|
|
309
|
+
ok(
|
|
310
|
+
"F4: the compiled reader exports the canonical Root fresh and resume launch-stage reject hints [QK:BUNDLE-REACH-LAUNCH-REJECT-HINTS]",
|
|
311
|
+
sameBytes &&
|
|
312
|
+
isDeepStrictEqual(loaded?.ENTWURF_FRESH_LAUNCH_REJECT_HINTS, root.freshLaunchRejectHints) &&
|
|
313
|
+
isDeepStrictEqual(loaded?.ENTWURF_RESUME_LAUNCH_REJECT_HINTS, root.resumeLaunchRejectHints),
|
|
314
|
+
);
|
|
315
|
+
// F5 — the delivery-result clauses (#154 E1a), separate for the same reason as F2; this proves the
|
|
316
|
+
// compiled reader's export, not the compiled receipt renderers that read it.
|
|
317
|
+
ok(
|
|
318
|
+
"F5: the compiled reader exports the canonical Root delivery-result clauses [QK:BUNDLE-REACH-DELIVERY-RESULT-CLAUSES]",
|
|
319
|
+
sameBytes && isDeepStrictEqual(loaded?.ENTWURF_DELIVERY_RESULT_CLAUSES, root.deliveryResultClauses),
|
|
320
|
+
);
|
|
321
|
+
// F6 — the slotted reject-hint segments (#154 E1b), separate for the same reason as F2; this proves the
|
|
322
|
+
// compiled reader's export, not the compiled surface that composes them with the code-owned slot.
|
|
323
|
+
ok(
|
|
324
|
+
"F6: the compiled reader exports the canonical Root slotted reject-hint segments [QK:BUNDLE-REACH-SLOTTED-REJECT-HINTS]",
|
|
325
|
+
sameBytes && isDeepStrictEqual(loaded?.ENTWURF_SLOTTED_REJECT_HINTS, root.slottedRejectHints),
|
|
326
|
+
);
|
|
327
|
+
// F7 — the first COMPILED CONSUMER proof (#154 E1b): the compiled surface, not only the reader, renders
|
|
328
|
+
// the record-less-socket hint from the Root segments around the compiled owner's fix command, and that
|
|
329
|
+
// command is the source owner's value (an emptied or drifted slot cannot pass by composing with itself).
|
|
330
|
+
// Imported only when the carrier matches, for the same reason as the reader import above.
|
|
331
|
+
const surface = sameBytes
|
|
332
|
+
? await import(pathToFileURL(path.join(BRIDGE_DIST, "pi-extensions", "lib", "entwurf-v2-surface.js")).href)
|
|
333
|
+
: undefined;
|
|
334
|
+
const metaSession = sameBytes
|
|
335
|
+
? await import(pathToFileURL(path.join(BRIDGE_DIST, "pi-extensions", "lib", "meta-session.js")).href)
|
|
336
|
+
: undefined;
|
|
337
|
+
const rls = root.slottedRejectHints["record-less-socket"];
|
|
338
|
+
ok(
|
|
339
|
+
"F7: the compiled surface renders the record-less-socket hint as the Root text around the compiled owner's fix command, which equals the source owner's [QK:BUNDLE-REACH-RECORD-LESS-SOCKET-CONSUMER]",
|
|
340
|
+
sameBytes &&
|
|
341
|
+
typeof metaSession?.FRESH_CUT_PRESCRIPTION === "string" &&
|
|
342
|
+
metaSession.FRESH_CUT_PRESCRIPTION.length > 0 &&
|
|
343
|
+
metaSession.FRESH_CUT_PRESCRIPTION === FRESH_CUT_PRESCRIPTION &&
|
|
344
|
+
surface?.actionableRejectHint("record-less-socket") ===
|
|
345
|
+
`${rls.beforeFreshCutPrescription}${FRESH_CUT_PRESCRIPTION}${rls.afterFreshCutPrescription}`,
|
|
346
|
+
);
|
|
347
|
+
}
|
|
348
|
+
|
|
263
349
|
console.log(`\ncheck-capability-bundle-reach: PASS (${passed} assertions)`);
|
|
@@ -112,7 +112,7 @@ function row(facts: SelfAddressabilityFacts): { replyable: boolean; socketState:
|
|
|
112
112
|
ok("meta/self-fetch + record but undefined owner/watch → fail-closed NOT replyable", partial.replyable === false);
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
-
// meta-session / NATIVE-PUSH rail (antigravity): a SEPARATE axis — recordBacked ∧ probeAlive.
|
|
115
|
+
// meta-session / NATIVE-PUSH rail (Codex; antigravity is paused, #152): a SEPARATE axis — recordBacked ∧ probeAlive.
|
|
116
116
|
// The two rails must not be able to borrow each other's facts (보정①): a mailbox signal
|
|
117
117
|
// deciding an agy reply, or a probe deciding a Claude reply, is a category error.
|
|
118
118
|
{
|
|
@@ -124,8 +124,8 @@ function row(facts: SelfAddressabilityFacts): { replyable: boolean; socketState:
|
|
|
124
124
|
const reachable = row(push({ recordBacked: true, probeAlive: true }));
|
|
125
125
|
ok("meta/native-push + record + probe-alive → replyable", reachable.replyable === true);
|
|
126
126
|
|
|
127
|
-
// THE ROW THIS RAIL EXISTS FOR:
|
|
128
|
-
// self-fetch atom it would be un-replyable forever; on its own rail it is reachable.
|
|
127
|
+
// THE ROW THIS RAIL EXISTS FOR: a native-push citizen never arms a mailbox watch. Under
|
|
128
|
+
// the self-fetch atom it would be un-replyable forever; on its own rail it is reachable.
|
|
129
129
|
ok(
|
|
130
130
|
"meta/native-push + record + probe-alive + watch NEVER armed → STILL replyable (no mailbox axis leak)",
|
|
131
131
|
row(push({ recordBacked: true, probeAlive: true, watchArmed: false })).replyable === true,
|
|
@@ -156,6 +156,37 @@ function row(facts: SelfAddressabilityFacts): { replyable: boolean; socketState:
|
|
|
156
156
|
);
|
|
157
157
|
}
|
|
158
158
|
|
|
159
|
+
// #152 policy row: a PAUSED native-push backend is decided BEFORE the pure record+probe
|
|
160
|
+
// predicate. Record-backed + paused → not replyable with a reason naming the pause, even if
|
|
161
|
+
// a probe fact says alive (none ran for this answer). The record axis keeps precedence: a
|
|
162
|
+
// record-less citizen keeps its own reason and is never relabelled as paused.
|
|
163
|
+
{
|
|
164
|
+
const paused = computeSelfAddressability({
|
|
165
|
+
origin: "meta-session",
|
|
166
|
+
metaDeliveryDomain: "native-push",
|
|
167
|
+
recordBacked: true,
|
|
168
|
+
probeAlive: true,
|
|
169
|
+
nativePushPaused: true,
|
|
170
|
+
});
|
|
171
|
+
ok(
|
|
172
|
+
"meta/native-push + record + PAUSED (#152) → NOT replyable, socket none",
|
|
173
|
+
paused.replyable === false && paused.socketState === "none",
|
|
174
|
+
);
|
|
175
|
+
ok("meta/native-push + record + PAUSED (#152) → the reason names the pause", /paused \(#152\)/.test(paused.reason));
|
|
176
|
+
const recordless = computeSelfAddressability({
|
|
177
|
+
origin: "meta-session",
|
|
178
|
+
metaDeliveryDomain: "native-push",
|
|
179
|
+
recordBacked: false,
|
|
180
|
+
nativePushPaused: true,
|
|
181
|
+
});
|
|
182
|
+
ok(
|
|
183
|
+
"meta/native-push + NO record + paused → the record-less reason wins (pause never masks it)",
|
|
184
|
+
recordless.replyable === false &&
|
|
185
|
+
/no backing meta-record/.test(recordless.reason) &&
|
|
186
|
+
!/paused/.test(recordless.reason),
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
|
|
159
190
|
// meta-session / NONE rail: no inbound transport at all. Mailbox facts and a live probe
|
|
160
191
|
// cannot buy replyability — there is nothing to land on. Distinct from the unsupplied
|
|
161
192
|
// (undefined) domain row below, which stays fail-closed for a caller that forgot the axis.
|
|
@@ -222,9 +253,9 @@ function functionBody(name: string): string {
|
|
|
222
253
|
|
|
223
254
|
/** Extract a `server.tool("NAME", ...)` call region by brace-counting from its open paren. */
|
|
224
255
|
function toolRegion(name: string): string {
|
|
225
|
-
const
|
|
226
|
-
|
|
227
|
-
|
|
256
|
+
const matches = [...src.matchAll(/server\.tool\(\s*"entwurf_self"\s*,/g)];
|
|
257
|
+
assert.equal(matches.length, 1, `server.tool("${name}") present exactly once`);
|
|
258
|
+
const at = matches[0].index;
|
|
228
259
|
const open = src.indexOf("(", at);
|
|
229
260
|
let depth = 0;
|
|
230
261
|
for (let i = open; i < src.length; i++) {
|