@junghanacs/entwurf 0.38.1 → 0.40.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/BASELINE.md +11 -4
- package/CHANGELOG.md +323 -0
- package/DELIVERY.md +34 -0
- package/LICENSES/node-gyp.txt +24 -0
- package/README.md +20 -6
- package/VERIFY.md +23 -4
- package/docs/setup-clean-host.md +43 -2
- package/mcp/entwurf-bridge/dist/mcp/entwurf-bridge/src/index.js +32 -9
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/control-socket-path.js +56 -1
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-control-rpc.js +25 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/meta-mailbox-body.js +10 -4
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/meta-session.js +14 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/remote-pair-receive.js +123 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/remote-pair-send.js +259 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/remote-pair-transport.js +80 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/socket-discovery.js +14 -2
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/socket-probe.js +21 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/win32-native.js +182 -0
- package/mcp/entwurf-bridge/dist/scripts/remote-pair-receive.js +65 -0
- package/mcp/entwurf-bridge/src/index.ts +38 -9
- package/mcp/entwurf-bridge/tsconfig.build.json +7 -0
- package/native/win32-x64/entwurf_win.node +0 -0
- package/native/win32-x64/manifest.json +45 -0
- package/package.json +5 -3
- package/pi-extensions/entwurf-control.ts +6 -1
- package/pi-extensions/lib/control-socket-path.js +56 -1
- package/pi-extensions/lib/entwurf-control-rpc.ts +32 -0
- package/pi-extensions/lib/meta-mailbox-body.ts +13 -4
- package/pi-extensions/lib/meta-session.ts +14 -0
- package/pi-extensions/lib/remote-pair-receive.ts +184 -0
- package/pi-extensions/lib/remote-pair-send.ts +325 -0
- package/pi-extensions/lib/remote-pair-transport.ts +81 -0
- package/pi-extensions/lib/socket-discovery.ts +12 -1
- package/pi-extensions/lib/socket-probe.ts +20 -0
- package/pi-extensions/lib/win32-native.js +198 -0
- package/run.sh +149 -1
- package/scripts/check-entwurf-self-address.ts +15 -4
- package/scripts/check-gate-qualification.ts +8 -1
- package/scripts/check-remote-pair-installed.ts +242 -0
- package/scripts/lib/remote-pair-installed-audit.ts +25 -0
- package/scripts/mutants/control-send-receipt.json +2 -2
- package/scripts/mutants/pack-install.json +28 -2
- package/scripts/mutants/remote-pair-display.json +53 -0
- package/scripts/mutants/remote-pair-installed-audit.json +28 -0
- package/scripts/mutants/remote-pair-receive.json +126 -0
- package/scripts/mutants/remote-pair-roundtrip.json +83 -0
- package/scripts/mutants/remote-pair-send.json +194 -0
- package/scripts/mutants/remote-pair-transport.json +66 -0
- package/scripts/mutants/win32-preview.json +215 -0
- package/scripts/remote-pair-receive.ts +73 -0
package/run.sh
CHANGED
|
@@ -328,6 +328,9 @@ Usage:
|
|
|
328
328
|
./run.sh meta-bridge-prune # 1.0.0 meta-bridge Phase 4: LISTING-ONLY store hygiene — classify orphan/stale/ambiguous/keep, print manual rm commands, delete NOTHING ([dir] [--ttl-days N])
|
|
329
329
|
./run.sh meta-bridge-fresh-cut # the ONE generation verb (the verb every v3-only rejection names): quiesce-check live sockets/markers/native-push conversations (refusing any surface it cannot inspect), archive meta-sessions/ + meta-mailbox/ to `<dir>.archive-<ts>`, clear dead transport residue, open an empty v3 generation. No migration, no restore — the archive is forensic only. EXIT CONTRACT (#54, `--help` prints it): 0 complete / 1 NOTHING MOVED (re-run, do not setup) / 2 usage / 3 cut transition incomplete (inspect) / 4 cut complete but residue cleanup failed (`setup` may run; re-run only before new citizen birth, otherwise remove residue manually)
|
|
330
330
|
./run.sh meta-facts # #65 owner-normalized READ-ONLY store projection: deterministic JSON {schemaVersion:1, storeDir, citizens: full v3 records sorted by gardenId, defects: {filename,message}} — THE listing contract emitted by the owner so consumers stop copying the certification. No liveness/sockets/transcript contents. EXIT: 0 readable (defects in-band; missing store = empty), 2 usage, 3 unreadable ([dir])
|
|
331
|
+
./run.sh check-remote-pair-installed # #107 rail 3, CHECKOUT-ONLY: install a caller-supplied candidate tarball (--tgz <abs>) into a sandboxed consumer prefix, check its compiled remote-pair surface, run the installed `entwurf remote-pair-receive` (usage exit 2 = twin reached), run the T2 roundtrip cells against the installed bridge+entry, and audit that the subject processes resolve every module inside the consumer prefix (with a checkout control). Never repacks; no real ssh, no model
|
|
332
|
+
./run.sh check-remote-pair-installed-audit # #107 rail 3: the deterministic counterpart of check-remote-pair-installed — its checkout-invisibility acceptance primitive proven on synthetic logs (real path, root boundary). In check:vitest
|
|
333
|
+
./run.sh remote-pair-receive # #107 rail 1: the RECEIVING half of one operator-declared remote pair. Reads ONE JSON claim {from,fromAgentId,fromCwd,target,message,wantsReply} on stdin, refuses a from/target outside the declared pair before any mutation (a scope limit under the same-operator SSH account trust model, not authentication), then hands a match to the existing v2 dispatch. Ambient sender carriers are never read. Installed, run_ts runs its compiled twin (dist/scripts/remote-pair-receive.js). EXIT: 0 dispatched / 4 dispatched, the dispatch reported an error (delivery not inferred) / 3 pair refused / 2 invalid claim or declaration (JSON receipt) or wrong argv (usage on stderr, no receipt) / 1 uncaught dispatch or store throw (no receipt; the sender reads it as indeterminate) (--pair <absolute declaration file>)
|
|
331
334
|
./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])
|
|
332
335
|
./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)
|
|
333
336
|
./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)
|
|
@@ -1340,6 +1343,15 @@ check_control_send_receipt() {
|
|
|
1340
1343
|
run_vitest pi-extensions/lib/control-send-receipt.test.ts
|
|
1341
1344
|
}
|
|
1342
1345
|
|
|
1346
|
+
check_win32_preview() {
|
|
1347
|
+
# MUTANT EXECUTION COORDINATE, not a discovery path — the Windows preview's (#78) loader and
|
|
1348
|
+
# platform wiring tests are found by check-tests-beside-behavior like every other beside-behaviour
|
|
1349
|
+
# lane. This case exists because the WIN32-* mutants name it as gate argv. The addon's own Win32
|
|
1350
|
+
# behaviour is measured only on Windows, by windows-native-preview.yml.
|
|
1351
|
+
section "Windows preview loader + platform wiring (mutant execution coordinate)"
|
|
1352
|
+
run_vitest pi-extensions/lib/win32-native.test.ts pi-extensions/lib/win32-wiring.test.ts
|
|
1353
|
+
}
|
|
1354
|
+
|
|
1343
1355
|
check_entwurf_v2_native_push() {
|
|
1344
1356
|
# MUTANT EXECUTION COORDINATE, not a discovery path — the test is found by
|
|
1345
1357
|
# check-tests-beside-behavior like every other beside-behaviour lane. This case exists
|
|
@@ -2667,6 +2679,8 @@ for (const [job] of ciJobs) {
|
|
|
2667
2679
|
// to what is already bound is not a safety net; it is a restatement.
|
|
2668
2680
|
// pnpm-lock.yaml is excluded on purpose: those engine ranges belong to third-
|
|
2669
2681
|
// party dependencies and are not this repo's supported axis.
|
|
2682
|
+
// native/win32/package-lock.json (#78) is excluded for the same reason: a generated
|
|
2683
|
+
// lock of the Windows addon's build-only dependency tree (node-gyp), never shipped.
|
|
2670
2684
|
// `--cached` ONLY — deliberately not `--others`. The corpus must be the
|
|
2671
2685
|
// candidate contract (what is tracked/staged), never the operator's working
|
|
2672
2686
|
// directory: an un-ignored scratch file would otherwise turn this gate RED on
|
|
@@ -2676,7 +2690,7 @@ for (const [job] of ciJobs) {
|
|
|
2676
2690
|
// before the commit that would ship it. (Review lane, 2026-07-21.)
|
|
2677
2691
|
const tracked = execFileSync('git', ['ls-files', '--cached', '--', '*.sh', '*.json', '*.ts', '*.md', '*.yml', '*.yaml'], { encoding: 'utf8' })
|
|
2678
2692
|
.split('\n')
|
|
2679
|
-
.filter((f) => f && f !== 'pnpm-lock.yaml' && f !== 'CHANGELOG.md' && f !== 'NEXT.md');
|
|
2693
|
+
.filter((f) => f && f !== 'pnpm-lock.yaml' && f !== 'native/win32/package-lock.json' && f !== 'CHANGELOG.md' && f !== 'NEXT.md');
|
|
2680
2694
|
assert.ok(tracked.length >= 20,
|
|
2681
2695
|
`git ls-files returned ${tracked.length} first-party files — the sweep lost its corpus and would pass vacuously`);
|
|
2682
2696
|
|
|
@@ -3376,6 +3390,17 @@ check_acp_sdk_surface() {
|
|
|
3376
3390
|
run_vitest test/acp-sdk-surface.contract.test.ts
|
|
3377
3391
|
}
|
|
3378
3392
|
|
|
3393
|
+
check_remote_pair_installed_audit() {
|
|
3394
|
+
# #107 rail 3. The deterministic counterpart of `check-remote-pair-installed`: that gate needs a
|
|
3395
|
+
# caller-supplied tarball, so it cannot run in the floor or carry mutants, and its
|
|
3396
|
+
# checkout-invisibility verdict rests on one acceptance primitive
|
|
3397
|
+
# (scripts/lib/remote-pair-installed-audit.ts). This proves that primitive on tiny synthetic
|
|
3398
|
+
# logs and files. `test/**` is not discovered by check-tests-beside-behavior, so it needs this
|
|
3399
|
+
# public coordinate. Zero network, zero child process.
|
|
3400
|
+
section "remote-pair installed-closure audit (synthetic logs)"
|
|
3401
|
+
run_vitest test/remote-pair-installed-audit.test.ts
|
|
3402
|
+
}
|
|
3403
|
+
|
|
3379
3404
|
check_pi_queue_oracle() {
|
|
3380
3405
|
# #120 P2. The PUBLIC execution coordinate for `test/pi-queue.oracle.test.ts`, and the reason it
|
|
3381
3406
|
# needs one: the discovery door below selects `pi-extensions/**/*.test.ts` and
|
|
@@ -3818,6 +3843,12 @@ check_pack() {
|
|
|
3818
3843
|
# out: the placement join it emits is the copy a herdr-side consumer would
|
|
3819
3844
|
# otherwise fork, and such a consumer runs against an installed host.
|
|
3820
3845
|
"mcp/entwurf-bridge/dist/scripts/peer-facts.js"
|
|
3846
|
+
# #107 — the receiving host's entry the sender's ssh runs (run_ts twin), plus the three
|
|
3847
|
+
# remote-pair libs the bridge's entwurf_v2 handler and that entry import.
|
|
3848
|
+
"mcp/entwurf-bridge/dist/scripts/remote-pair-receive.js"
|
|
3849
|
+
"mcp/entwurf-bridge/dist/pi-extensions/lib/remote-pair-receive.js"
|
|
3850
|
+
"mcp/entwurf-bridge/dist/pi-extensions/lib/remote-pair-send.js"
|
|
3851
|
+
"mcp/entwurf-bridge/dist/pi-extensions/lib/remote-pair-transport.js"
|
|
3821
3852
|
# #87 Bundle B — omp-receive-doctor drives this operator projection through
|
|
3822
3853
|
# run.sh, so installed node_modules must have the compiled twin too.
|
|
3823
3854
|
"mcp/entwurf-bridge/dist/scripts/omp-receive-facts.js"
|
|
@@ -3851,6 +3882,14 @@ check_pack() {
|
|
|
3851
3882
|
"pi/pi-durable/carrier/LICENSE"
|
|
3852
3883
|
"pi/meta-bridge-omp/entwurf-meta-omp/package.json"
|
|
3853
3884
|
"mcp/entwurf-bridge/dist/pi-extensions/lib/meta-session.js"
|
|
3885
|
+
# #78 — the Windows preview loader. The compiled meta-session reaches it only through a
|
|
3886
|
+
# win32-only createRequire, not the import graph, so its compiled copy is pinned by name.
|
|
3887
|
+
"pi-extensions/lib/win32-native.js"
|
|
3888
|
+
"mcp/entwurf-bridge/dist/pi-extensions/lib/win32-native.js"
|
|
3889
|
+
# #78 — the MIT notice for node-gyp's win_delay_load_hook.cc, which build.mjs compiles into the
|
|
3890
|
+
# Windows addon. It rides every pack (one per-FILE entry), so the preview carrier never ships
|
|
3891
|
+
# without it; build.mjs refuses unless it equals the pinned node-gyp LICENSE.
|
|
3892
|
+
"LICENSES/node-gyp.txt"
|
|
3854
3893
|
"scripts/postinstall-chmod.cjs"
|
|
3855
3894
|
"pi/entwurf-capabilities.json"
|
|
3856
3895
|
"pi/meta-bridge/.claude-plugin/marketplace.json"
|
|
@@ -3925,6 +3964,9 @@ check_pack() {
|
|
|
3925
3964
|
'\.test\.[cm]?[jt]s$'
|
|
3926
3965
|
# Maintainer take/renderer/helper TS is source-only, including non-test implementations.
|
|
3927
3966
|
'^demo/.*\.ts$'
|
|
3967
|
+
# #78 — the Windows addon's C source, gyp file and build pin are CI build inputs, never a
|
|
3968
|
+
# consumer repair surface: only the built native/win32-x64/ carrier may ship.
|
|
3969
|
+
'^native/win32/'
|
|
3928
3970
|
)
|
|
3929
3971
|
# #141: the owned pi-durable source, its typecheck fence and the retired overlay patch are maintainer
|
|
3930
3972
|
# input. An installed consumer runs the emitted carrier JS (and its LICENSE) only — Rule 11: compiled
|
|
@@ -3957,6 +3999,20 @@ check_pack() {
|
|
|
3957
3999
|
pass=0
|
|
3958
4000
|
fi
|
|
3959
4001
|
|
|
4002
|
+
# #78 — a pack carries the Windows addon not at all, or as exactly a verified addon + manifest
|
|
4003
|
+
# pair (pack_win32_carrier_verdict, proven on owned fixtures by check-pack-win32-carrier).
|
|
4004
|
+
# "present" means the carrier verified, never that the build was approved for publication.
|
|
4005
|
+
local carrier_verdict
|
|
4006
|
+
carrier_verdict=$(pack_win32_carrier_verdict "$file_list" "$REPO_DIR")
|
|
4007
|
+
case "$carrier_verdict" in
|
|
4008
|
+
absent) echo "[check-pack] win32 addon: absent (not a Windows preview artifact)" ;;
|
|
4009
|
+
present\ *) echo "[check-pack] win32 addon: present sha256=${carrier_verdict#present } (carrier verified; not a publication approval)" ;;
|
|
4010
|
+
*)
|
|
4011
|
+
fail "[check-pack] native/win32-x64/ is not one verified addon + manifest pair: $carrier_verdict"
|
|
4012
|
+
pass=0
|
|
4013
|
+
;;
|
|
4014
|
+
esac
|
|
4015
|
+
|
|
3960
4016
|
local total
|
|
3961
4017
|
total=$(printf '%s\n' "$file_list" | wc -l | tr -d ' ')
|
|
3962
4018
|
echo "[check-pack] $total files in tarball"
|
|
@@ -3969,6 +4025,68 @@ check_pack() {
|
|
|
3969
4025
|
return 1
|
|
3970
4026
|
}
|
|
3971
4027
|
|
|
4028
|
+
# #78 — the Windows addon carrier decision, shared by check_pack (the real tarball list) and
|
|
4029
|
+
# check_pack_win32_carrier (owned fake-byte fixtures). $1 = tarball file list, $2 = the package root
|
|
4030
|
+
# holding native/win32-x64/. Prints ONE verdict: `absent`, `pair-broken`, `invalid <code>` or
|
|
4031
|
+
# `present <sha256>`. The manifest + bytes check IS the loader's own verifyWin32NativeAt (manifest
|
|
4032
|
+
# shape, N-API 8, digest), so the pack and the consumer cannot disagree about what a carrier is.
|
|
4033
|
+
pack_win32_carrier_verdict() {
|
|
4034
|
+
local list="$1" root="$2" carrier
|
|
4035
|
+
carrier=$(grep -E '^native/win32-x64/' <<<"$list" | sort || true)
|
|
4036
|
+
if [ -z "$carrier" ]; then echo "absent"; return 0; fi
|
|
4037
|
+
if [ "$carrier" != $'native/win32-x64/entwurf_win.node\nnative/win32-x64/manifest.json' ]; then echo "pair-broken"; return 0; fi
|
|
4038
|
+
node -e '
|
|
4039
|
+
import(require("node:url").pathToFileURL(process.argv[1]).href).then((m) => {
|
|
4040
|
+
try { process.stdout.write(`present ${m.verifyWin32NativeAt(process.argv[2], { napi: "8" }).sha256}`); }
|
|
4041
|
+
catch (err) { process.stdout.write(`invalid ${err.code ?? "uncoded"}`); }
|
|
4042
|
+
});' "$REPO_DIR/pi-extensions/lib/win32-native.js" "$root"
|
|
4043
|
+
}
|
|
4044
|
+
|
|
4045
|
+
check_pack_win32_carrier() {
|
|
4046
|
+
section "pack: Windows addon carrier verdict (owned fake-byte fixtures)"
|
|
4047
|
+
# Fake bytes and node-written manifests, never a PE: this proves the pair/digest/ABI decision,
|
|
4048
|
+
# not a loadable addon (windows-native-preview.yml owns that, on installed bytes).
|
|
4049
|
+
local fx pair=$'native/win32-x64/entwurf_win.node\nnative/win32-x64/manifest.json' got
|
|
4050
|
+
fx=$(mktemp -d "${TMPDIR:-/tmp}/entwurf-pack-carrier.XXXXXX")
|
|
4051
|
+
trap 'rm -rf -- "$fx"' RETURN
|
|
4052
|
+
win32_carrier_cell() { # name napiVersion -> a root with a matching addon + manifest
|
|
4053
|
+
mkdir -p "$fx/$1/native/win32-x64"
|
|
4054
|
+
printf 'fake addon %s\n' "$1" >"$fx/$1/native/win32-x64/entwurf_win.node"
|
|
4055
|
+
node -e '
|
|
4056
|
+
const fs = require("node:fs"), dir = process.argv[1];
|
|
4057
|
+
const sha = require("node:crypto").createHash("sha256").update(fs.readFileSync(dir + "/entwurf_win.node")).digest("hex");
|
|
4058
|
+
fs.writeFileSync(dir + "/manifest.json", JSON.stringify({ schema: 1, platform: "win32", arch: "x64", file: "entwurf_win.node", sha256: sha, napiVersion: Number(process.argv[2]) }));
|
|
4059
|
+
' "$fx/$1/native/win32-x64" "$2"
|
|
4060
|
+
}
|
|
4061
|
+
win32_carrier_cell match 8
|
|
4062
|
+
win32_carrier_cell drift 8 && printf 'changed\n' >"$fx/drift/native/win32-x64/entwurf_win.node"
|
|
4063
|
+
win32_carrier_cell abi 7
|
|
4064
|
+
win32_carrier_cell garbled 8 && printf 'not json' >"$fx/garbled/native/win32-x64/manifest.json"
|
|
4065
|
+
local want_sha
|
|
4066
|
+
want_sha=$(sha256sum "$fx/match/native/win32-x64/entwurf_win.node" | cut -d' ' -f1)
|
|
4067
|
+
got=$(pack_win32_carrier_verdict "package.json" "$fx/match")
|
|
4068
|
+
[ "$got" = "absent" ] || { fail "[check-pack-win32-carrier] a list without native/win32-x64/ must be absent — got: $got"; return 1; }
|
|
4069
|
+
for cell in drift abi garbled; do
|
|
4070
|
+
got=$(pack_win32_carrier_verdict "$pair" "$fx/$cell")
|
|
4071
|
+
[ "$got" = "invalid ENTWURF_WIN32_NATIVE_DRIFT" ] || {
|
|
4072
|
+
fail "[QK:WIN32-PACK-DIGEST] the $cell carrier (changed byte / N-API 7 / unparsable manifest) must be invalid as drift, never present — got: $got"
|
|
4073
|
+
return 1
|
|
4074
|
+
}
|
|
4075
|
+
done
|
|
4076
|
+
# The positive cell comes after the refusals, so a weakened check dies on its own QK first.
|
|
4077
|
+
got=$(pack_win32_carrier_verdict "$pair" "$fx/match")
|
|
4078
|
+
[ "$got" = "present $want_sha" ] || { fail "[check-pack-win32-carrier] a matching pair must be present $want_sha — got: $got"; return 1; }
|
|
4079
|
+
local list
|
|
4080
|
+
for list in 'native/win32-x64/entwurf_win.node' 'native/win32-x64/manifest.json' "$pair"$'\nnative/win32-x64/x.dll'; do
|
|
4081
|
+
got=$(pack_win32_carrier_verdict "$list" "$fx/match")
|
|
4082
|
+
[ "$got" = "pair-broken" ] || {
|
|
4083
|
+
fail "[QK:WIN32-PACK-PAIR] native/win32-x64/ must carry exactly the addon + manifest pair — $(tr '\n' ' ' <<<"$list") gave: $got"
|
|
4084
|
+
return 1
|
|
4085
|
+
}
|
|
4086
|
+
done
|
|
4087
|
+
echo "[check-pack-win32-carrier] ok — absent, present, three drift shapes and three broken pairs decided by the shared verdict"
|
|
4088
|
+
}
|
|
4089
|
+
|
|
3972
4090
|
# The pin-leak filter for the install tree's .pnpm listing, shared by the real scan and its
|
|
3973
4091
|
# self-test below. TWO properties are load-bearing and each has its own cell below.
|
|
3974
4092
|
#
|
|
@@ -4472,6 +4590,12 @@ _check_pack_install_impl() {
|
|
|
4472
4590
|
"mcp/entwurf-bridge/dist/scripts/meta-facts.js"
|
|
4473
4591
|
# #116 M2-a — the owner-normalized peer projection (see check-pack).
|
|
4474
4592
|
"mcp/entwurf-bridge/dist/scripts/peer-facts.js"
|
|
4593
|
+
# #107 — the receiving host's entry the sender's ssh runs (run_ts twin), plus the three
|
|
4594
|
+
# remote-pair libs the bridge's entwurf_v2 handler and that entry import.
|
|
4595
|
+
"mcp/entwurf-bridge/dist/scripts/remote-pair-receive.js"
|
|
4596
|
+
"mcp/entwurf-bridge/dist/pi-extensions/lib/remote-pair-receive.js"
|
|
4597
|
+
"mcp/entwurf-bridge/dist/pi-extensions/lib/remote-pair-send.js"
|
|
4598
|
+
"mcp/entwurf-bridge/dist/pi-extensions/lib/remote-pair-transport.js"
|
|
4475
4599
|
# #87 Bundle B — omp-receive-doctor reaches this through the installed dispatcher.
|
|
4476
4600
|
"mcp/entwurf-bridge/dist/scripts/omp-receive-facts.js"
|
|
4477
4601
|
# 0.12.5 — node_modules-safe plugin hook + lib (see check-pack). The installed
|
|
@@ -7378,6 +7502,9 @@ case "$cmd" in
|
|
|
7378
7502
|
check-control-send-receipt)
|
|
7379
7503
|
check_control_send_receipt
|
|
7380
7504
|
;;
|
|
7505
|
+
check-win32-preview)
|
|
7506
|
+
check_win32_preview
|
|
7507
|
+
;;
|
|
7381
7508
|
check-entwurf-v2-native-push)
|
|
7382
7509
|
check_entwurf_v2_native_push
|
|
7383
7510
|
;;
|
|
@@ -8311,6 +8438,21 @@ case "$cmd" in
|
|
|
8311
8438
|
shift || true
|
|
8312
8439
|
run_ts scripts/meta-facts.ts "$@"
|
|
8313
8440
|
;;
|
|
8441
|
+
check-remote-pair-installed)
|
|
8442
|
+
# #107 rail 3, CHECKOUT-ONLY install gate: installs a caller-supplied candidate tarball into a
|
|
8443
|
+
# sandboxed consumer prefix and drives that install's bridge and entry through the T2 scene,
|
|
8444
|
+
# auditing that the subject processes resolve nothing outside the prefix. Never repacks.
|
|
8445
|
+
shift || true
|
|
8446
|
+
run_ts scripts/check-remote-pair-installed.ts "$@"
|
|
8447
|
+
;;
|
|
8448
|
+
remote-pair-receive)
|
|
8449
|
+
# #107 rail 1: the receiving host's entry for one declared remote pair. The pair
|
|
8450
|
+
# check runs before the v2 dispatch, so a claim outside the pair mutates nothing;
|
|
8451
|
+
# a match reaches the same record/owner/deliverability checks every local send does.
|
|
8452
|
+
# Installed, run_ts runs its compiled twin; the sending host's ssh reaches it through the bin.
|
|
8453
|
+
shift || true
|
|
8454
|
+
run_ts scripts/remote-pair-receive.ts "$@"
|
|
8455
|
+
;;
|
|
8314
8456
|
peer-facts)
|
|
8315
8457
|
# #116 M2-a READ-ONLY projection of the OBSERVED peer listing: one provider
|
|
8316
8458
|
# (`listEntwurfFacts`) through one renderer (`renderEntwurfPeers().payload`), so
|
|
@@ -8380,6 +8522,9 @@ case "$cmd" in
|
|
|
8380
8522
|
check-pi-queue-oracle)
|
|
8381
8523
|
check_pi_queue_oracle
|
|
8382
8524
|
;;
|
|
8525
|
+
check-remote-pair-installed-audit)
|
|
8526
|
+
check_remote_pair_installed_audit
|
|
8527
|
+
;;
|
|
8383
8528
|
check-demo-reproducibility)
|
|
8384
8529
|
check_demo_reproducibility
|
|
8385
8530
|
;;
|
|
@@ -8489,6 +8634,9 @@ case "$cmd" in
|
|
|
8489
8634
|
check-pack-pin-matcher)
|
|
8490
8635
|
check_pack_pin_matcher
|
|
8491
8636
|
;;
|
|
8637
|
+
check-pack-win32-carrier)
|
|
8638
|
+
check_pack_win32_carrier
|
|
8639
|
+
;;
|
|
8492
8640
|
check-pack-consumer-roots)
|
|
8493
8641
|
check_pack_consumer_roots
|
|
8494
8642
|
;;
|
|
@@ -263,8 +263,19 @@ ok(
|
|
|
263
263
|
// call: the dir is still this adapter's own policy (ENTWURF_DIR honours the env
|
|
264
264
|
// override the pi side does not), but the join is not re-authored here.
|
|
265
265
|
ok(
|
|
266
|
-
"buildStrictPiSenderEnvelope
|
|
267
|
-
/
|
|
266
|
+
"buildStrictPiSenderEnvelope probes the canonical address via the shared grammar (controlSocketPathIn(ENTWURF_DIR, …))",
|
|
267
|
+
/controlAddressPresent\s*\(\s*socketPath\s*\)/.test(piBody) &&
|
|
268
|
+
/controlSocketPathIn\s*\(\s*ENTWURF_DIR\s*,/.test(piBody),
|
|
269
|
+
);
|
|
270
|
+
// The presence probe keeps the socket-file existsSync off Windows; on Windows (#78) the address
|
|
271
|
+
// is a named pipe, which only the owner-validated probe may touch (a stat would connect).
|
|
272
|
+
const presentBody = functionBody("controlAddressPresent");
|
|
273
|
+
ok(
|
|
274
|
+
"controlAddressPresent: existsSync(socketPath) off Windows, the owner-validated pipe probe on it",
|
|
275
|
+
/existsSync\s*\(\s*socketPath\s*\)/.test(presentBody) &&
|
|
276
|
+
/process\.platform === "win32"\)\s*return \(await probeSocketLiveness\(socketPath\)\) === "alive"/.test(
|
|
277
|
+
presentBody,
|
|
278
|
+
),
|
|
268
279
|
);
|
|
269
280
|
|
|
270
281
|
const metaBuilder = functionBody("buildMetaSenderEnvelope");
|
|
@@ -280,8 +291,8 @@ ok(
|
|
|
280
291
|
|
|
281
292
|
const selfRegion = toolRegion("entwurf_self");
|
|
282
293
|
ok(
|
|
283
|
-
"entwurf_self
|
|
284
|
-
/
|
|
294
|
+
"entwurf_self probes the pi control address (alive vs expected, no synthesized path lie)",
|
|
295
|
+
/controlAddressPresent\s*\(\s*socketPath\s*\)/.test(selfRegion),
|
|
285
296
|
);
|
|
286
297
|
|
|
287
298
|
// ── SLICE INTEGRITY (before any claim is read off the slice) ─────────────────
|
|
@@ -3488,7 +3488,7 @@ let manifestCount: number;
|
|
|
3488
3488
|
"mux-fresh-call": 65,
|
|
3489
3489
|
"mux-launcher-fence": 8,
|
|
3490
3490
|
"mux-parent-artifact": 3,
|
|
3491
|
-
"pack-install":
|
|
3491
|
+
"pack-install": 5,
|
|
3492
3492
|
"pi-package-ownership": 8,
|
|
3493
3493
|
"qualification-ledger": 42,
|
|
3494
3494
|
"mux-resume-call": 10,
|
|
@@ -3497,6 +3497,12 @@ let manifestCount: number;
|
|
|
3497
3497
|
"omp-receive": 11,
|
|
3498
3498
|
"probe-ordering": 1,
|
|
3499
3499
|
"release-gate": 21,
|
|
3500
|
+
"remote-pair-display": 3,
|
|
3501
|
+
"remote-pair-installed-audit": 2,
|
|
3502
|
+
"remote-pair-receive": 8,
|
|
3503
|
+
"remote-pair-roundtrip": 5,
|
|
3504
|
+
"remote-pair-send": 12,
|
|
3505
|
+
"remote-pair-transport": 4,
|
|
3500
3506
|
"retired-sent-surface": 2,
|
|
3501
3507
|
"resume-args": 6,
|
|
3502
3508
|
"resume-launch-identity": 7,
|
|
@@ -3506,6 +3512,7 @@ let manifestCount: number;
|
|
|
3506
3512
|
"typing-call-fence": 1,
|
|
3507
3513
|
"v2-surface": 13,
|
|
3508
3514
|
"v2-visible-resume": 18,
|
|
3515
|
+
"win32-preview": 19,
|
|
3509
3516
|
};
|
|
3510
3517
|
const laneTally: Record<string, number> = {};
|
|
3511
3518
|
for (const man of manifests) laneTally[man.lane] = (laneTally[man.lane] ?? 0) + man.mutants.length;
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* check-remote-pair-installed — #107 rail 3: the remote pair on an INSTALLED package.
|
|
4
|
+
*
|
|
5
|
+
* `./run.sh check-remote-pair-installed --tgz <absolute candidate tarball>`
|
|
6
|
+
*
|
|
7
|
+
* Why this is a scripts/ gate and not a source-adjacent vitest test: its subject is an ARTIFACT.
|
|
8
|
+
* It installs a caller-supplied tarball into a fresh consumer prefix with npm and then drives
|
|
9
|
+
* that install's own processes (its bridge `start.sh` → compiled dist, its `run.sh` → run_ts →
|
|
10
|
+
* compiled entry). A test beside the source can only reach the source.
|
|
11
|
+
*
|
|
12
|
+
* What it does, once, with every writable root (HOME, npm cache/userconfig, XDG, PI agent dir)
|
|
13
|
+
* inside one temp sandbox:
|
|
14
|
+
* 1. copies the given tarball into the sandbox and installs THAT copy (never repacks; the copy's
|
|
15
|
+
* sha256 is printed). npm reads the package's declared dependencies from the registry into the
|
|
16
|
+
* sandbox cache — a network read, but no model, no operator config and no ssh;
|
|
17
|
+
* 2. checks the installed package carries the receive entry's compiled twin and the remote libs;
|
|
18
|
+
* 3. runs the installed `entwurf remote-pair-receive` with wrong argv: usage on stderr, exit 2 —
|
|
19
|
+
* the compiled twin was reached through run_ts, not a TS crossing;
|
|
20
|
+
* 4. runs the existing T2 scene (pi-extensions/lib/remote-pair-roundtrip.test.ts) with its subject
|
|
21
|
+
* switched to this install: real installed bridge, stand-in ssh → installed run.sh → installed
|
|
22
|
+
* entry → production dispatch, with the same mailbox/provenance/receipt/reverse-dispatch oracles;
|
|
23
|
+
* 5. audits checkout invisibility: a preload resolve hook records every module each SUBJECT
|
|
24
|
+
* process resolves; every file must sit (by real path) inside the consumer prefix. The
|
|
25
|
+
* orchestrator (vitest + its fixture writer) is this checkout and is not audited;
|
|
26
|
+
* 6. control: the same hook on this checkout's own `run.sh remote-pair-receive` must report a
|
|
27
|
+
* checkout path, so the audit can see the thing it refuses.
|
|
28
|
+
*
|
|
29
|
+
* The subject switch (ENTWURF_REMOTE_PAIR_INSTALLED_PKG and the resolve-hook variables) is set
|
|
30
|
+
* here and only here: it changes what the T2 cells measure, so it must not be set ambiently.
|
|
31
|
+
*
|
|
32
|
+
* Mutation proof covers only the audit's acceptance primitive (scripts/lib/remote-pair-installed-audit.ts);
|
|
33
|
+
* the install, compiled-surface, twin and roundtrip cells are actual-runtime evidence with the
|
|
34
|
+
* checkout control, not mutation-qualified.
|
|
35
|
+
*
|
|
36
|
+
* Not claimed: the published npm artifact (this is whatever tarball the caller passes), real sshd,
|
|
37
|
+
* a real remote host, a durable host reading or replying, or the check-pack/check-pack-install floor.
|
|
38
|
+
*/
|
|
39
|
+
|
|
40
|
+
import { spawnSync } from "node:child_process";
|
|
41
|
+
import { createHash } from "node:crypto";
|
|
42
|
+
import * as fs from "node:fs";
|
|
43
|
+
import * as os from "node:os";
|
|
44
|
+
import * as path from "node:path";
|
|
45
|
+
import { fileURLToPath } from "node:url";
|
|
46
|
+
import { filesOutsideRoot as outside } from "./lib/remote-pair-installed-audit.ts";
|
|
47
|
+
|
|
48
|
+
const LABEL = "check-remote-pair-installed";
|
|
49
|
+
const REPO = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
50
|
+
|
|
51
|
+
const args = process.argv.slice(2);
|
|
52
|
+
if (args.length !== 2 || args[0] !== "--tgz" || !path.isAbsolute(args[1])) {
|
|
53
|
+
console.error(`usage: ./run.sh ${LABEL} --tgz <absolute candidate tarball>`);
|
|
54
|
+
process.exit(2);
|
|
55
|
+
}
|
|
56
|
+
const tgz = args[1];
|
|
57
|
+
if (!fs.lstatSync(tgz).isFile()) {
|
|
58
|
+
console.error(`${LABEL}: --tgz must be a regular file (${tgz})`);
|
|
59
|
+
process.exit(2);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
let passed = 0;
|
|
63
|
+
let failed = 0;
|
|
64
|
+
function ok(label: string, cond: boolean, detail = ""): void {
|
|
65
|
+
if (cond) {
|
|
66
|
+
passed++;
|
|
67
|
+
console.log(` ok ${label}`);
|
|
68
|
+
} else {
|
|
69
|
+
failed++;
|
|
70
|
+
console.log(` FAIL ${label}${detail ? `\n ${detail}` : ""}`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const sandbox = fs.mkdtempSync(path.join(os.tmpdir(), "entwurf-remote-pair-installed-"));
|
|
75
|
+
const consumer = path.join(sandbox, "consumer");
|
|
76
|
+
const npmHome = path.join(sandbox, "npm-home");
|
|
77
|
+
const logs = path.join(sandbox, "resolve-logs");
|
|
78
|
+
const hook = path.join(sandbox, "resolve-hook.mjs");
|
|
79
|
+
for (const d of [consumer, npmHome, logs]) fs.mkdirSync(d, { recursive: true });
|
|
80
|
+
|
|
81
|
+
function sandboxEnv(home: string): NodeJS.ProcessEnv {
|
|
82
|
+
return {
|
|
83
|
+
PATH: process.env.PATH ?? "",
|
|
84
|
+
HOME: home,
|
|
85
|
+
XDG_CONFIG_HOME: path.join(home, ".config"),
|
|
86
|
+
XDG_DATA_HOME: path.join(home, ".local", "share"),
|
|
87
|
+
XDG_STATE_HOME: path.join(home, ".local", "state"),
|
|
88
|
+
XDG_CACHE_HOME: path.join(home, ".cache"),
|
|
89
|
+
PI_CODING_AGENT_DIR: path.join(home, ".pi", "agent"),
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
fs.writeFileSync(
|
|
94
|
+
hook,
|
|
95
|
+
[
|
|
96
|
+
'import { appendFileSync } from "node:fs";',
|
|
97
|
+
'import { registerHooks } from "node:module";',
|
|
98
|
+
"const log = process.env.ENTWURF_REMOTE_PAIR_RESOLVE_LOG;",
|
|
99
|
+
"if (log) {",
|
|
100
|
+
"\tregisterHooks({",
|
|
101
|
+
"\t\tresolve(specifier, context, next) {",
|
|
102
|
+
"\t\t\tconst r = next(specifier, context);",
|
|
103
|
+
'\t\t\tappendFileSync(log, r.url + "\\n");',
|
|
104
|
+
"\t\t\treturn r;",
|
|
105
|
+
"\t\t},",
|
|
106
|
+
"\t});",
|
|
107
|
+
"}",
|
|
108
|
+
"",
|
|
109
|
+
].join("\n"),
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
function hookEnv(label: string): NodeJS.ProcessEnv {
|
|
113
|
+
return { NODE_OPTIONS: `--import=${hook}`, ENTWURF_REMOTE_PAIR_RESOLVE_LOG: path.join(logs, `${label}.log`) };
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// The artifact is a snapshot: copy the caller's file into the sandbox once, then hash and install
|
|
117
|
+
// THAT copy, so what is measured is exactly what is installed. Never repacked.
|
|
118
|
+
const snapshot = path.join(sandbox, "artifact", path.basename(tgz));
|
|
119
|
+
fs.mkdirSync(path.dirname(snapshot), { recursive: true });
|
|
120
|
+
fs.copyFileSync(tgz, snapshot);
|
|
121
|
+
const sha = createHash("sha256").update(fs.readFileSync(snapshot)).digest("hex");
|
|
122
|
+
console.log(
|
|
123
|
+
`[${LABEL}] caller tarball ${tgz}\n[${LABEL}] snapshot ${snapshot}\n[${LABEL}] snapshot sha256 ${sha}\n[${LABEL}] sandbox ${sandbox}`,
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
try {
|
|
127
|
+
// 1. Install as-is.
|
|
128
|
+
fs.writeFileSync(
|
|
129
|
+
path.join(consumer, "package.json"),
|
|
130
|
+
JSON.stringify({ name: "remote-pair-consumer", private: true }),
|
|
131
|
+
);
|
|
132
|
+
const npm = spawnSync("npm", ["install", snapshot, "--no-audit", "--no-fund"], {
|
|
133
|
+
cwd: consumer,
|
|
134
|
+
encoding: "utf8",
|
|
135
|
+
env: {
|
|
136
|
+
...sandboxEnv(npmHome),
|
|
137
|
+
npm_config_cache: path.join(sandbox, "npm-cache"),
|
|
138
|
+
npm_config_userconfig: path.join(npmHome, ".npmrc"),
|
|
139
|
+
},
|
|
140
|
+
});
|
|
141
|
+
ok(
|
|
142
|
+
"[QK:RPI-INSTALLED] npm installs the given tarball into a fresh consumer prefix",
|
|
143
|
+
npm.status === 0,
|
|
144
|
+
npm.stderr.slice(-800),
|
|
145
|
+
);
|
|
146
|
+
const pkg = path.join(consumer, "node_modules", "@junghanacs", "entwurf");
|
|
147
|
+
const pkgStat = fs.existsSync(pkg) ? fs.lstatSync(pkg) : null;
|
|
148
|
+
ok(
|
|
149
|
+
"[QK:RPI-INSTALLED] the package is a real directory inside the consumer (not a link to a checkout)",
|
|
150
|
+
pkgStat !== null && pkgStat.isDirectory() && !pkgStat.isSymbolicLink(),
|
|
151
|
+
);
|
|
152
|
+
if (failed) throw new Error("install failed; later cells would not measure the artifact");
|
|
153
|
+
|
|
154
|
+
// 2. The compiled surface is in the package.
|
|
155
|
+
for (const rel of [
|
|
156
|
+
"mcp/entwurf-bridge/dist/scripts/remote-pair-receive.js",
|
|
157
|
+
"mcp/entwurf-bridge/dist/pi-extensions/lib/remote-pair-receive.js",
|
|
158
|
+
"mcp/entwurf-bridge/dist/pi-extensions/lib/remote-pair-send.js",
|
|
159
|
+
"mcp/entwurf-bridge/dist/pi-extensions/lib/remote-pair-transport.js",
|
|
160
|
+
]) {
|
|
161
|
+
ok(`[QK:RPI-COMPILED-SURFACE] the installed package carries ${rel}`, fs.existsSync(path.join(pkg, rel)));
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// 3. The installed bin reaches the compiled twin.
|
|
165
|
+
const home = path.join(sandbox, "usage-home");
|
|
166
|
+
const usage = spawnSync(path.join(pkg, "run.sh"), ["remote-pair-receive"], {
|
|
167
|
+
encoding: "utf8",
|
|
168
|
+
env: { ...sandboxEnv(home), ...hookEnv("installed-usage") },
|
|
169
|
+
});
|
|
170
|
+
ok(
|
|
171
|
+
"[QK:RPI-INSTALLED-ENTRY-TWIN] the installed `entwurf remote-pair-receive` answers wrong argv with usage on stderr, exit 2, no receipt",
|
|
172
|
+
usage.status === 2 && /usage: entwurf remote-pair-receive/.test(usage.stderr) && usage.stdout === "",
|
|
173
|
+
`status=${usage.status} stdout=${usage.stdout.slice(0, 200)} stderr=${usage.stderr.slice(0, 300)}`,
|
|
174
|
+
);
|
|
175
|
+
|
|
176
|
+
// 4. The T2 scene against the install.
|
|
177
|
+
const vitest = spawnSync(
|
|
178
|
+
path.join(REPO, "node_modules", ".bin", "vitest"),
|
|
179
|
+
["run", "pi-extensions/lib/remote-pair-roundtrip.test.ts"],
|
|
180
|
+
{
|
|
181
|
+
cwd: REPO,
|
|
182
|
+
encoding: "utf8",
|
|
183
|
+
env: {
|
|
184
|
+
...process.env,
|
|
185
|
+
ENTWURF_REMOTE_PAIR_INSTALLED_PKG: pkg,
|
|
186
|
+
ENTWURF_REMOTE_PAIR_RESOLVE_HOOK: hook,
|
|
187
|
+
ENTWURF_REMOTE_PAIR_RESOLVE_LOG_DIR: logs,
|
|
188
|
+
},
|
|
189
|
+
},
|
|
190
|
+
);
|
|
191
|
+
const summary = (vitest.stdout.match(/Tests\s+.*$/m) ?? [""])[0].trim();
|
|
192
|
+
ok(
|
|
193
|
+
"[QK:RPI-ROUNDTRIP-ON-INSTALL] the T2 cells (A→B, reverse dispatch, opt-in absent off, other host refuses, both socket dirs) pass against the installed bridge and entry",
|
|
194
|
+
vitest.status === 0 && /^Tests\s+5 passed\b/.test(summary),
|
|
195
|
+
`${summary}\n${vitest.stdout
|
|
196
|
+
.split("\n")
|
|
197
|
+
.filter((l) => /×|FAIL|Error/.test(l))
|
|
198
|
+
.slice(0, 10)
|
|
199
|
+
.join("\n")}`,
|
|
200
|
+
);
|
|
201
|
+
|
|
202
|
+
// 5. Checkout invisibility of the subject processes.
|
|
203
|
+
const all = fs.readdirSync(logs).map((n) => path.join(logs, n));
|
|
204
|
+
const bridgeLogs = all.filter((f) => path.basename(f).startsWith("bridge-"));
|
|
205
|
+
const entryLogs = all.filter((f) => path.basename(f).startsWith("entry-"));
|
|
206
|
+
const subjectLogs = [...bridgeLogs, ...entryLogs, ...all.filter((f) => path.basename(f) === "installed-usage.log")];
|
|
207
|
+
ok(
|
|
208
|
+
"[QK:RPI-AUDIT-SAW-SUBJECTS] the resolve hook recorded the installed bridge processes and the installed entry processes",
|
|
209
|
+
bridgeLogs.some((f) => fs.statSync(f).size > 0) && entryLogs.some((f) => fs.statSync(f).size > 0),
|
|
210
|
+
`bridge logs=${bridgeLogs.length} entry logs=${entryLogs.length}`,
|
|
211
|
+
);
|
|
212
|
+
const audit = outside(subjectLogs, consumer);
|
|
213
|
+
ok(
|
|
214
|
+
`[QK:RPI-CHECKOUT-INVISIBLE] every module the installed subject processes resolved (${audit.total}) sits inside the consumer prefix`,
|
|
215
|
+
audit.total > 0 && audit.outside.length === 0,
|
|
216
|
+
audit.outside.slice(0, 10).join("\n "),
|
|
217
|
+
);
|
|
218
|
+
|
|
219
|
+
// 6. Control: the audit sees a checkout when there is one.
|
|
220
|
+
const control = spawnSync(path.join(REPO, "run.sh"), ["remote-pair-receive"], {
|
|
221
|
+
encoding: "utf8",
|
|
222
|
+
env: { ...sandboxEnv(path.join(sandbox, "control-home")), ...hookEnv("control-checkout") },
|
|
223
|
+
});
|
|
224
|
+
const controlAudit = outside([path.join(logs, "control-checkout.log")], consumer);
|
|
225
|
+
const realRepo = fs.realpathSync(REPO);
|
|
226
|
+
ok(
|
|
227
|
+
"[QK:RPI-AUDIT-CONTROL] the same audit on this checkout's own entry reports checkout paths (the detector is live)",
|
|
228
|
+
control.status === 2 && controlAudit.outside.some((p) => p.startsWith(`${realRepo}${path.sep}`)),
|
|
229
|
+
`status=${control.status} outside=${controlAudit.outside.length}`,
|
|
230
|
+
);
|
|
231
|
+
} catch (err) {
|
|
232
|
+
ok(`[${LABEL}] gate ran to completion`, false, err instanceof Error ? err.message : String(err));
|
|
233
|
+
} finally {
|
|
234
|
+
// Synthetic fixture data only. Kept on failure so the resolve logs and npm output can be read.
|
|
235
|
+
if (failed === 0) fs.rmSync(sandbox, { recursive: true, force: true });
|
|
236
|
+
else console.log(`[${LABEL}] kept the sandbox for diagnosis: ${sandbox}`);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
console.log(
|
|
240
|
+
`\n${LABEL}: ${passed} passed, ${failed} failed — installed artifact ${sha.slice(0, 12)}; no real ssh, no model`,
|
|
241
|
+
);
|
|
242
|
+
process.exit(failed === 0 ? 0 : 1);
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The acceptance primitive of `check-remote-pair-installed`'s checkout-invisibility cell (#107 rail 3),
|
|
3
|
+
* as a pure function over resolve-hook logs so it can be proven on tiny synthetic files
|
|
4
|
+
* (test/remote-pair-installed-audit.test.ts). Each log line is one URL a subject process resolved.
|
|
5
|
+
* A `file:` URL counts and is judged by its REAL path; anything else (node: builtins) is ignored.
|
|
6
|
+
*/
|
|
7
|
+
import * as fs from "node:fs";
|
|
8
|
+
import * as path from "node:path";
|
|
9
|
+
import { fileURLToPath } from "node:url";
|
|
10
|
+
|
|
11
|
+
/** Every file the logs name that is NOT inside `root` (both compared by real path), and how many file URLs were read. */
|
|
12
|
+
export function filesOutsideRoot(logFiles: readonly string[], root: string): { total: number; outside: string[] } {
|
|
13
|
+
const realRoot = fs.realpathSync(root);
|
|
14
|
+
const out = new Set<string>();
|
|
15
|
+
let total = 0;
|
|
16
|
+
for (const f of logFiles) {
|
|
17
|
+
for (const url of fs.readFileSync(f, "utf8").split("\n").filter(Boolean)) {
|
|
18
|
+
if (!url.startsWith("file:")) continue;
|
|
19
|
+
total++;
|
|
20
|
+
const real = fs.realpathSync(fileURLToPath(url));
|
|
21
|
+
if (real !== realRoot && !real.startsWith(`${realRoot}${path.sep}`)) out.add(real);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return { total, outside: [...out].sort() };
|
|
25
|
+
}
|
|
@@ -166,10 +166,10 @@
|
|
|
166
166
|
"title": "the pi queue oracle's public coordinate is dropped from `check:vitest`, so the file that proves every claim the receipt makes about a receiver's queues is green only when somebody runs `pnpm test` by hand — the discovery door deliberately does not select `test/**`, so nothing else would notice",
|
|
167
167
|
"subject": "package.json",
|
|
168
168
|
"find": [
|
|
169
|
-
"\"check:vitest\": \"./run.sh check-mux-fresh-call && ./run.sh check-acp-sdk-surface && ./run.sh check-pi-queue-oracle && ./run.sh check-tests-beside-behavior\""
|
|
169
|
+
"\"check:vitest\": \"./run.sh check-mux-fresh-call && ./run.sh check-acp-sdk-surface && ./run.sh check-pi-queue-oracle && ./run.sh check-tests-beside-behavior && ./run.sh check-remote-pair-installed-audit\""
|
|
170
170
|
],
|
|
171
171
|
"replace": [
|
|
172
|
-
"\"check:vitest\": \"./run.sh check-mux-fresh-call && ./run.sh check-acp-sdk-surface && ./run.sh check-tests-beside-behavior\""
|
|
172
|
+
"\"check:vitest\": \"./run.sh check-mux-fresh-call && ./run.sh check-acp-sdk-surface && ./run.sh check-tests-beside-behavior && ./run.sh check-remote-pair-installed-audit\""
|
|
173
173
|
],
|
|
174
174
|
"gate": ["bash", "run.sh", "check-tests-beside-behavior"],
|
|
175
175
|
"timeoutSeconds": 600,
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"mutants": [
|
|
5
5
|
{
|
|
6
6
|
"claim": "PACK-INSTALL-PIN-MATCHER-BOUNDED",
|
|
7
|
-
"title": "the pin-leak matcher loses its version boundary and blesses substring lookalikes
|
|
7
|
+
"title": "the pin-leak matcher loses its version boundary and blesses substring lookalikes \u2014 @0.85.10 reads as the pinned 0.85.1 and the gate announces a verified tree it never verified (independent review, 2026-08-25)",
|
|
8
8
|
"subject": "run.sh",
|
|
9
9
|
"find": [" grep '^@earendil-works+' | grep -Ev '@1\\.3\\.0(_|$)' || true"],
|
|
10
10
|
"replace": [" grep '^@earendil-works+' | grep -v '@0\\.87\\.1' || true"],
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
},
|
|
16
16
|
{
|
|
17
17
|
"claim": "PACK-INSTALL-PIN-MATCHER-COVERS-CLOSURE",
|
|
18
|
-
"title": "the pin-leak matcher narrows back to the pi-* families and goes blind to a non-pi member of the same runtime closure
|
|
18
|
+
"title": "the pin-leak matcher narrows back to the pi-* families and goes blind to a non-pi member of the same runtime closure \u2014 @earendil-works/chord (new in pi 0.85.0, a runtime dependency of pi-coding-agent/pi-agent-core/pi-client/pi-protocol) floats unpinned while the gate prints a verified pin (measured 2026-09-06 on a real 0.85.1 install tree)",
|
|
19
19
|
"subject": "run.sh",
|
|
20
20
|
"find": [" grep '^@earendil-works+' | grep -Ev '@1\\.3\\.0(_|$)' || true"],
|
|
21
21
|
"replace": [" grep '^@earendil-works+pi-' | grep -Ev '@1\\.3\\.0(_|$)' || true"],
|
|
@@ -34,6 +34,32 @@
|
|
|
34
34
|
"timeoutSeconds": 120,
|
|
35
35
|
"signature": "[QK:PACK-INSTALL-CONSUMER-ROOTS-CONTAINED]",
|
|
36
36
|
"signatureSource": "run.sh"
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"claim": "WIN32-PACK-PAIR",
|
|
40
|
+
"title": "the Windows carrier pair check becomes a presence test, so an addon without its manifest, or with an extra DLL beside it, is packed as a carrier",
|
|
41
|
+
"subject": "run.sh",
|
|
42
|
+
"find": [
|
|
43
|
+
" if [ \"$carrier\" != $'native/win32-x64/entwurf_win.node\\nnative/win32-x64/manifest.json' ]; then echo \"pair-broken\"; return 0; fi"
|
|
44
|
+
],
|
|
45
|
+
"replace": [" if ! grep -q 'entwurf_win.node' <<<\"$carrier\"; then echo \"pair-broken\"; return 0; fi"],
|
|
46
|
+
"gate": ["bash", "run.sh", "check-pack-win32-carrier"],
|
|
47
|
+
"timeoutSeconds": 120,
|
|
48
|
+
"signature": "[QK:WIN32-PACK-PAIR]",
|
|
49
|
+
"signatureSource": "run.sh"
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"claim": "WIN32-PACK-DIGEST",
|
|
53
|
+
"title": "the pack stops verifying the carrier with the loader's own check, so changed bytes or a foreign N-API manifest are packed as `present`",
|
|
54
|
+
"subject": "run.sh",
|
|
55
|
+
"find": [
|
|
56
|
+
" try { process.stdout.write(`present ${m.verifyWin32NativeAt(process.argv[2], { napi: \"8\" }).sha256}`); }"
|
|
57
|
+
],
|
|
58
|
+
"replace": [" try { process.stdout.write(`present unverified`); }"],
|
|
59
|
+
"gate": ["bash", "run.sh", "check-pack-win32-carrier"],
|
|
60
|
+
"timeoutSeconds": 120,
|
|
61
|
+
"signature": "[QK:WIN32-PACK-DIGEST]",
|
|
62
|
+
"signatureSource": "run.sh"
|
|
37
63
|
}
|
|
38
64
|
]
|
|
39
65
|
}
|