@junghanacs/entwurf 0.25.1 → 0.30.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +1 -1
- package/BASELINE.md +1 -1
- package/CHANGELOG.md +190 -0
- package/DELIVERY.md +1 -1
- package/README.md +183 -674
- package/VERIFY.md +21 -17
- package/demo/README.md +1 -1
- package/docs/acp-backend-rail.md +115 -18
- package/docs/external-mcp-host.md +1 -1
- package/docs/setup-clean-host.md +346 -10
- package/mcp/entwurf-bridge/dist/mcp/entwurf-bridge/src/index.js +27 -4
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/backend-adapter.js +55 -1
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-peers-render.js +1 -1
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-decider.js +2 -2
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-mailbox.js +2 -2
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-production.js +2 -2
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-surface.js +3 -5
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-visible-resume.js +3 -3
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/fresh-call-composition.js +13 -6
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/meta-mailbox-body.js +1 -1
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/mux-fresh-call.js +3 -3
- package/mcp/entwurf-bridge/src/index.ts +30 -3
- package/package.json +13 -13
- package/pi-extensions/entwurf-control.ts +50 -637
- package/pi-extensions/lib/acp/backend-adapter.ts +81 -1
- package/pi-extensions/lib/acp/backend.ts +41 -5
- package/pi-extensions/lib/entwurf-fact-provider.ts +2 -2
- package/pi-extensions/lib/entwurf-mcp-server.ts +74 -0
- package/pi-extensions/lib/entwurf-peers-render.ts +1 -1
- package/pi-extensions/lib/entwurf-v2-decider.ts +2 -2
- package/pi-extensions/lib/entwurf-v2-mailbox.ts +2 -2
- package/pi-extensions/lib/entwurf-v2-production.ts +3 -3
- package/pi-extensions/lib/entwurf-v2-surface.ts +3 -5
- package/pi-extensions/lib/entwurf-v2-visible-resume.ts +3 -3
- package/pi-extensions/lib/fresh-call-composition.ts +13 -6
- package/pi-extensions/lib/meta-mailbox-body.ts +1 -1
- package/pi-extensions/lib/mux-fresh-call.ts +3 -3
- package/run.sh +177 -53
- package/scripts/check-entwurf-peers-surface.ts +8 -15
- package/scripts/check-entwurf-self-address.ts +7 -11
- package/scripts/check-entwurf-v2-surface.ts +20 -112
- package/scripts/check-fresh-call-dispatch.ts +12 -13
- package/scripts/check-gate-qualification.ts +250 -5
- package/scripts/check-herdr-plugin-build.ts +159 -57
- package/scripts/check-install-surface.ts +3 -2
- package/scripts/check-mux-resume-call.ts +24 -35
- package/scripts/check-pi-bridge-sender.ts +130 -0
- package/scripts/check-pi-mcp-bridge.ts +251 -0
- package/scripts/fixtures/herdr-supply.json +8 -8
- package/scripts/lib/codex-fresh-live-protocol.ts +17 -5
- package/scripts/lib/codex-fresh-source-receipts.ts +21 -2
- package/scripts/lib/mutation-qualify.ts +269 -44
- package/scripts/lib/pi-mcp-bridge-probe.mjs +170 -0
- package/scripts/meta-bridge-doctor.sh +16 -5
- package/scripts/mutants/acp-foreground-only.json +17 -0
- package/scripts/mutants/acp-prompt-lifecycle.json +2 -2
- package/scripts/mutants/acp-vendor-rejection.json +62 -0
- package/scripts/mutants/codex-native.json +11 -11
- package/scripts/mutants/control-send-receipt.json +6 -4
- package/scripts/mutants/copilot-birth.json +2 -1
- package/scripts/mutants/fresh-call-dispatch.json +3 -14
- package/scripts/mutants/gate-qualification.json +59 -0
- package/scripts/mutants/herdr-plugin-build.json +11 -0
- package/scripts/mutants/herdr-supply.json +3 -3
- package/scripts/mutants/mux-fresh-call.json +43 -45
- package/scripts/mutants/mux-resume-call.json +11 -35
- package/scripts/mutants/pack-install.json +15 -4
- package/scripts/mutants/pi-bridge-sender.json +54 -0
- package/scripts/mutants/pi-floor.json +6 -2
- package/scripts/mutants/pi-mcp-bridge.json +36 -0
- package/scripts/mutants/pi-mcp-register.json +51 -0
- package/scripts/mutants/v2-surface.json +6 -6
- package/scripts/resident-rpc-drive.ts +5 -1
- package/scripts/smoke-acp-bundled-mcp-live.ts +3 -1
- package/scripts/smoke-acp-cortex-live.ts +3 -1
- package/scripts/smoke-acp-raw-turn-live.ts +1 -1
- package/scripts/smoke-acp-session-reuse-live.ts +29 -12
- package/scripts/smoke-acp-socket-citizen-live.ts +3 -1
- package/scripts/smoke-acp-v2-send-live.ts +3 -1
- package/scripts/smoke-codex-fresh-live.ts +20 -1
- package/scripts/smoke-entwurf-chain-live.ts +6 -2
- package/scripts/smoke-entwurf-v2-matrix-live.ts +3 -1
- package/scripts/smoke-herdr-fresh-call-live.ts +21 -7
- package/scripts/smoke-herdr-raw-install-live.sh +1 -1
- package/scripts/smoke-mux-lifecycle-live.ts +1 -1
- package/scripts/smoke-resident-garden-guard.sh +3 -1
package/run.sh
CHANGED
|
@@ -140,6 +140,9 @@ Usage:
|
|
|
140
140
|
./run.sh check-omp-fresh-preflight # #87 C: the OMP fresh preflight reproduces omp_agent_dir and the tools.xdev read in TS (it runs from two emit depths and cannot call a sibling script). This drives the SHIPPED shell/python leaves over the same inputs — refusals included — and requires the TS half to agree, so the reproduction cannot silently drift from the installer's own oracle
|
|
141
141
|
./run.sh check-codex-sender-identity # Codex tools/call _meta triple agreement + addressable record join + cross-carrier conflict refusal
|
|
142
142
|
./run.sh check-codex-bridge-identity # real MCP bridge request identity for entwurf_self/v2/fresh; malformed or conflicting _meta fails loud
|
|
143
|
+
./run.sh check-pi-mcp-register # #125: mutant execution coordinate for pi-extensions/entwurf-control.test.ts — the real extension births through Pi's real loader and registers the compiled bridge after birth, before UI, direct with two hidden. Needs build-bridge
|
|
144
|
+
./run.sh check-pi-mcp-bridge # #125: a real Pi runtime (builtin MCP, default stdio, faux model only) spawns the COMPILED bridge as the born citizen — six prefixed verbs declared, hidden two absent, child env = record = entwurf_self, cwd = session cwd; newSession vendor conformance observed (old child gone, new record+child, old record kept). Needs build-bridge
|
|
145
|
+
./run.sh check-pi-bridge-sender # #125: real bridge pi-session carrier is served only when it names a readable pi RECORD; missing / foreign-backend / unreadable each refuse with their own cause; no socket = replyable:false, not refusal
|
|
143
146
|
./run.sh check-codex-native-push # app-server WebSocket-over-UDS probe + measured one-shot codex queue delivery; no replay
|
|
144
147
|
./run.sh check-codex-birth-hook # sandboxed payload + declaration: first-turn V3 birth, one record per thread, top-level event predicate, no markers
|
|
145
148
|
./run.sh check-codex-fresh-preflight # pre-mutation birth/MCP/thread-title/default-app-server readiness
|
|
@@ -277,6 +280,7 @@ Usage:
|
|
|
277
280
|
./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
|
|
278
281
|
./run.sh check-pack # publish gate (dry-run): npm pack --dry-run + tarball invariants (runtime-critical present, dev residue absent)
|
|
279
282
|
./run.sh check-pack-pin-matcher # pure self-test of check-pack-install's pin-leak matcher against synthetic .pnpm lookalikes, one cell per property: version boundary (a prefix-extended @0.86.0-beta.1 and an off-pin @0.85.1 must leak, @0.86.0 bare or with any measured peer-hash must pass) and closure prefix (an off-pin @earendil-works/chord must leak — it carries no `pi-` prefix); snapshot-safe qualification oracle, also run first inside check-pack-install
|
|
283
|
+
./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
|
|
280
284
|
./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
|
|
281
285
|
./run.sh check-pack-install # heavy publish gate (prepublishOnly): actual npm pack + tar -tf + fresh-temp install smoke + the #116 M3-b1 herdr-plugin RUNTIME VERIFIER run against that actual installed tarball (the focused check-herdr-runtime-bootstrap drives a fixture; THIS is where the real package is proven) with the pinned pi peers (pins derived from the package.json devDep; check-dep-versions binds them) + the npm-installed bridge BOOTS (tools/list) and DELIVERS (tools/call entwurf_v2 → .msg lands) + the installed all-absent and copilot-present (four-unit fake-vendor) `entwurf setup` rows + the INSTALLED generation lifecycle on a seeded previous-generation host (REFUSE before activation writes / zero Claude invocations → installed fresh-cut archives + opens empty → install-meta-bridge PASSES) + the INSTALLED-PACKAGE branch of the Copilot and OMP birth installers actually RUN (compiled entry selected, no raw .ts, and a real birth edge mints a citizen — the half a required-artifact list can never stand in for)
|
|
282
286
|
./run.sh check-install-container # 0.12.8 (#51 C): Linux artifact-CONSUMER gate — one candidate .tgz handed read-only to a checkout-invisible node:<engines-major>-bookworm cell. Default packs once to temp; ENTWURF_CANDIDATE_TGZ=/absolute/preserved.tgz consumes those exact bytes with no re-pack and prints canonical path+sha256 for release. Non-root global PATH install, frozen package, MCP tools/list, fake-Claude install-meta-bridge, path+sha256 fence, strict doctor, and the GENERATION host-state matrix (clean / v3-only store bytes unchanged / previous-generation REFUSE→fresh-cut→retry PASS) seeded inline. Docker missing = honest SKIP; ENTWURF_REQUIRE_DOCKER=1 makes that RED (required CI)
|
|
@@ -1336,6 +1340,32 @@ check_entwurf_resume_args() {
|
|
|
1336
1340
|
run_vitest pi-extensions/lib/entwurf-resume-args.test.ts
|
|
1337
1341
|
}
|
|
1338
1342
|
|
|
1343
|
+
check_acp_foreground_only() {
|
|
1344
|
+
# NOT a discovery path — check-tests-beside-behavior finds the test by glob. This case is the
|
|
1345
|
+
# MUTANT EXECUTION COORDINATE for scripts/mutants/acp-foreground-only.json (same reasoning as
|
|
1346
|
+
# check_entwurf_resume_args above), with a file filter narrow on purpose.
|
|
1347
|
+
section "claude ACP foreground-only launch env (mutant execution coordinate)"
|
|
1348
|
+
run_vitest pi-extensions/lib/acp/backend-adapter.test.ts
|
|
1349
|
+
}
|
|
1350
|
+
|
|
1351
|
+
check_acp_vendor_rejection() {
|
|
1352
|
+
# Behaviour-adjacent tests are discovered by the glob lane. This narrow case is the
|
|
1353
|
+
# mutant execution coordinate: include the adapter admission and real turn pipeline,
|
|
1354
|
+
# and keep run_vitest's failed-title attribution for qualification.
|
|
1355
|
+
section "ACP vendor rejection admission (mutant execution coordinate)"
|
|
1356
|
+
run_vitest pi-extensions/lib/acp/backend-adapter.test.ts pi-extensions/lib/acp/backend.vendor-rejection.test.ts
|
|
1357
|
+
}
|
|
1358
|
+
|
|
1359
|
+
check_pi_mcp_register() {
|
|
1360
|
+
# NOT a discovery path — check-tests-beside-behavior finds the test by glob. This case is the
|
|
1361
|
+
# MUTANT EXECUTION COORDINATE for scripts/mutants/pi-mcp-register.json (same reasoning as
|
|
1362
|
+
# check_entwurf_resume_args above): run_vitest is what emits the structured failed-test titles a
|
|
1363
|
+
# kill is attributed from. It sits in check:hermetic, not check:contracts, because the subject
|
|
1364
|
+
# registers the COMPILED bridge and refuses by name when `build-bridge` has not run.
|
|
1365
|
+
section "pi MCP registration after birth (mutant execution coordinate)"
|
|
1366
|
+
run_vitest pi-extensions/entwurf-control.test.ts
|
|
1367
|
+
}
|
|
1368
|
+
|
|
1339
1369
|
check_mux_launcher_fence() {
|
|
1340
1370
|
# Deterministic gate for the shared operator-launcher fence (issue #67):
|
|
1341
1371
|
# scripts/lib/claude-launcher-fence.ts plus its wiring into BOTH mux LIVE smokes. Replants the
|
|
@@ -1600,9 +1630,9 @@ smoke_acp_session_reuse_live() {
|
|
|
1600
1630
|
# needs LIVE=1. Forces process-scoped (pushes --entwurf-control into argv) and
|
|
1601
1631
|
# drives TWO real ACP turns over ONE reused claude-agent-acp child via the real
|
|
1602
1632
|
# streamShellAcp: turn 1 introduces a codeword (full transcript), turn 2 sends
|
|
1603
|
-
# ONLY the latest user delta
|
|
1604
|
-
#
|
|
1605
|
-
#
|
|
1633
|
+
# ONLY the latest user delta. The REUSE lifecycle notice, with no new bootstrap,
|
|
1634
|
+
# is the verdict; codeword recall is supporting only (a rebuilt full transcript
|
|
1635
|
+
# could recall it too). The one-shot exit0 half is owned by
|
|
1606
1636
|
# smoke-acp-provider-live.
|
|
1607
1637
|
# Model override: ENTWURF_ACP_PROVIDER_MODEL (default claude-sonnet-5).
|
|
1608
1638
|
# LIVE=1 ./run.sh smoke-acp-session-reuse-live
|
|
@@ -2185,11 +2215,11 @@ assert.equal(peerTui, piAi,
|
|
|
2185
2215
|
// an open `>=` floor is exactly how the next installer re-acquires the drift.
|
|
2186
2216
|
// The floor is also the HARD MINIMUM a consumer install resolves: at `>=0.86.0`
|
|
2187
2217
|
// an existing 0.85.1 host is upgraded, not kept.
|
|
2188
|
-
// Expected shape: `>=<devDep>
|
|
2218
|
+
// Expected shape: `>=<devDep> <<major>.<minor+1>` (e.g. `>=0.86.0 <0.87`, `>=1.0.0 <1.1`).
|
|
2219
|
+
// Pi 1.0 did not change the rule: an unverified minor is still unverified, so the ceiling
|
|
2220
|
+
// stays the next minor of the pin's own major.
|
|
2189
2221
|
const [piMaj, piMin] = piAi.split('.').map(Number);
|
|
2190
|
-
|
|
2191
|
-
`pi pin major must stay 0 for the next-minor ceiling rule (got ${piAi}); revisit check-dep-versions when pi reaches 1.x`);
|
|
2192
|
-
const expectedPeer = `>=${piAi} <0.${piMin + 1}`;
|
|
2222
|
+
const expectedPeer = `>=${piAi} <${piMaj}.${piMin + 1}`;
|
|
2193
2223
|
const peerDepAi = pkg.peerDependencies?.['@earendil-works/pi-ai'];
|
|
2194
2224
|
const peerDepCoding = pkg.peerDependencies?.['@earendil-works/pi-coding-agent'];
|
|
2195
2225
|
const peerDepTui = pkg.peerDependencies?.['@earendil-works/pi-tui'];
|
|
@@ -2258,13 +2288,13 @@ function stripBumpLedger(file, text) {
|
|
|
2258
2288
|
let rangeDecls = 0, exactDecls = 0;
|
|
2259
2289
|
for (const file of BASELINE_DOCS) {
|
|
2260
2290
|
const text = stripBumpLedger(file, readFileSync(file, 'utf8'));
|
|
2261
|
-
// Closed-range declarations: `>=<floor>
|
|
2262
|
-
for (const [decl, floor, ceilMinor] of text.matchAll(/>=\s?(\d+\.\d+\.\d+)\s?<\s?
|
|
2291
|
+
// Closed-range declarations: `>=<floor> <<major>.<ceiling>` (spaces optional).
|
|
2292
|
+
for (const [decl, floor, ceilMajor, ceilMinor] of text.matchAll(/>=\s?(\d+\.\d+\.\d+)\s?<\s?(\d+)\.(\d+)/g)) {
|
|
2263
2293
|
rangeDecls++;
|
|
2264
2294
|
assert.equal(floor, piAi,
|
|
2265
2295
|
`${file}: declared pi floor in "${decl}" is ${floor}, but the devDep pin is ${piAi} — a baseline doc may not advertise a version no gate drives`);
|
|
2266
|
-
assert.equal(
|
|
2267
|
-
`${file}: declared pi ceiling in "${decl}" must be the next minor (
|
|
2296
|
+
assert.equal(`${ceilMajor}.${ceilMinor}`, `${piMaj}.${piMin + 1}`,
|
|
2297
|
+
`${file}: declared pi ceiling in "${decl}" must be the next minor (${piMaj}.${piMin + 1})`);
|
|
2268
2298
|
}
|
|
2269
2299
|
// Exact install pins: `@earendil-works/pi-<pkg>@<version>`.
|
|
2270
2300
|
for (const [decl, ver] of text.matchAll(/@earendil-works\/pi-(?:ai|coding-agent|tui)@(\d+\.\d+\.\d+)/g)) {
|
|
@@ -2295,12 +2325,14 @@ for (const [file, re, shape] of PROSE_DECLS) {
|
|
|
2295
2325
|
// ONE assertion, because the claim token may appear exactly once in a gate source.
|
|
2296
2326
|
// Every way the floor can fragment is collected first and named individually in the
|
|
2297
2327
|
// failure, so "which surface" is never lost to the single-token rule.
|
|
2298
|
-
const nextFloorRange = `>=${piAi}
|
|
2328
|
+
const nextFloorRange = `>=${piAi} <${piMaj}.${piMin + 1}`;
|
|
2299
2329
|
const floorGaps = [];
|
|
2300
2330
|
const PI_CONSTELLATION = [
|
|
2301
2331
|
'@earendil-works/pi-ai', '@earendil-works/pi-coding-agent', '@earendil-works/pi-tui',
|
|
2302
2332
|
'@earendil-works/pi-agent-core', '@earendil-works/pi-client', '@earendil-works/pi-protocol',
|
|
2303
2333
|
'@earendil-works/pi-telemetry', '@earendil-works/chord',
|
|
2334
|
+
// `[측정 2026-09-30]` 0.99.1: pi-coding-agent's runtime deps gained the built-in extensions' packages.
|
|
2335
|
+
'@earendil-works/pi-codemode', '@earendil-works/pi-mcp',
|
|
2304
2336
|
];
|
|
2305
2337
|
const sameSet = (got, want, where) => {
|
|
2306
2338
|
const missing = want.filter((n) => !got.includes(n));
|
|
@@ -2343,9 +2375,9 @@ if (!matcherPin) {
|
|
|
2343
2375
|
// measurement in docs/mux-launch-rail.md, for one) are deliberately NOT scanned:
|
|
2344
2376
|
// they record what was true then, and rewriting them would falsify evidence.
|
|
2345
2377
|
const CURRENT_CONTRACT_DECLS = [
|
|
2346
|
-
['plugins/herdr/README.md', /npm install -g "@earendil-works\/pi-coding-agent@(>=[\d.]+
|
|
2347
|
-
['docs/mux-launch-rail.md', /현 supported range 는 `(>=[\d.]+
|
|
2348
|
-
['docs/acp-backend-rail.md', /devDep exact `(\d+\.\d+\.\d+)`, peer `(>=[\d.]+
|
|
2378
|
+
['plugins/herdr/README.md', /npm install -g "@earendil-works\/pi-coding-agent@(>=[\d.]+ <\d+\.\d+)"/, 'the herdr install line'],
|
|
2379
|
+
['docs/mux-launch-rail.md', /현 supported range 는 `(>=[\d.]+ <\d+\.\d+)`/, "mux-launch-rail's current supported range"],
|
|
2380
|
+
['docs/acp-backend-rail.md', /devDep exact `(\d+\.\d+\.\d+)`, peer `(>=[\d.]+ <\d+\.\d+)`/, "the ACP support row's exact+range pair"],
|
|
2349
2381
|
];
|
|
2350
2382
|
for (const [file, re, shape] of CURRENT_CONTRACT_DECLS) {
|
|
2351
2383
|
const m = readFileSync(file, 'utf8').match(re);
|
|
@@ -2864,13 +2896,14 @@ if (typeof FLOOR !== 'string' || !/^\d+\.\d+\.\d+$/.test(FLOOR)) {
|
|
|
2864
2896
|
console.error(`[check-pi-runtime-version] FAIL: package.json devDependencies['@earendil-works/pi-coding-agent'] must be an EXACT x.y.z pin to serve as the runtime floor (got ${FLOOR ?? 'nothing'})`);
|
|
2865
2897
|
process.exit(1);
|
|
2866
2898
|
}
|
|
2867
|
-
// The declared contract is a CLOSED range (`>=<devDep>
|
|
2899
|
+
// The declared contract is a CLOSED range (`>=<devDep> <<major>.<minor+1>`, enforced on
|
|
2868
2900
|
// package.json by check-dep-versions), so the runtime check must be closed too.
|
|
2869
2901
|
// A floor-only comparison would bless a resolved pi ABOVE the ceiling — and an
|
|
2870
2902
|
// out-of-range pi is exactly the drift this cut exists to stop: 0.80.6 landed on
|
|
2871
2903
|
// the dev box while the repo still declared 0.80.3, and every gate stayed green.
|
|
2872
2904
|
// Verifying only half of a declared range is the same lie in the other direction.
|
|
2873
|
-
const
|
|
2905
|
+
const [FLOOR_MAJOR, FLOOR_MINOR] = FLOOR.split('.').map(Number);
|
|
2906
|
+
const CEILING = `${FLOOR_MAJOR}.${FLOOR_MINOR + 1}.0`;
|
|
2874
2907
|
const cmp = (a, b) => {
|
|
2875
2908
|
const pa = a.split('.').map(Number), pb = b.split('.').map(Number);
|
|
2876
2909
|
for (let i = 0; i < 3; i++) { if ((pa[i] || 0) !== (pb[i] || 0)) return (pa[i] || 0) - (pb[i] || 0); }
|
|
@@ -3710,7 +3743,7 @@ check_pack() {
|
|
|
3710
3743
|
# which is exactly what property (1)'s `(_|$)` absorbs; all three shapes are fixtures
|
|
3711
3744
|
# below so a future suffix change cannot pass vacuously.)
|
|
3712
3745
|
pack_install_leaked_pi() {
|
|
3713
|
-
grep '^@earendil-works+' | grep -Ev '@0\.
|
|
3746
|
+
grep '^@earendil-works+' | grep -Ev '@1\.0\.0(_|$)' || true
|
|
3714
3747
|
}
|
|
3715
3748
|
|
|
3716
3749
|
# Matcher self-test on SYNTHETIC lookalikes: a healthy install tree cannot exercise either
|
|
@@ -3730,35 +3763,88 @@ check_pack_pin_matcher() {
|
|
|
3730
3763
|
# None may leak; the two lookalikes must — a PREFIX-EXTENDED version (`0.86.0-beta.1`,
|
|
3731
3764
|
# the prerelease shape that an unanchored match would bless) and an off-pin version.
|
|
3732
3765
|
matcher_probe=$(printf '%s\n' \
|
|
3733
|
-
'@earendil-works+pi-ai@0.
|
|
3734
|
-
'@earendil-works+pi-ai@0.
|
|
3735
|
-
'@earendil-works+pi-ai@0.
|
|
3736
|
-
'@earendil-works+pi-ai@0.
|
|
3737
|
-
'@earendil-works+pi-ai@0.
|
|
3738
|
-
'@earendil-works+pi-agent-core@0.87.
|
|
3739
|
-
if [ "$matcher_probe" != '@earendil-works+pi-ai@0.
|
|
3740
|
-
@earendil-works+pi-agent-core@0.87.
|
|
3741
|
-
fail "[QK:PACK-INSTALL-PIN-MATCHER-BOUNDED] the pin-leak matcher must flag the prefix-extended 0.
|
|
3766
|
+
'@earendil-works+pi-ai@1.0.0' \
|
|
3767
|
+
'@earendil-works+pi-ai@1.0.0_@modelcontextprotocol+sdk@1.29.0_zod@4.3.6' \
|
|
3768
|
+
'@earendil-works+pi-ai@1.0.0_ws@8.21.3' \
|
|
3769
|
+
'@earendil-works+pi-ai@1.0.0_@modelcontextprotocol+sdk@1.29.0_zod@4.3.6__ws@8.21.3_zod@4.3.6' \
|
|
3770
|
+
'@earendil-works+pi-ai@1.0.0-beta.1' \
|
|
3771
|
+
'@earendil-works+pi-agent-core@0.87.1' | pack_install_leaked_pi)
|
|
3772
|
+
if [ "$matcher_probe" != '@earendil-works+pi-ai@1.0.0-beta.1
|
|
3773
|
+
@earendil-works+pi-agent-core@0.87.1' ]; then
|
|
3774
|
+
fail "[QK:PACK-INSTALL-PIN-MATCHER-BOUNDED] the pin-leak matcher must flag the prefix-extended 1.0.0-beta.1 and the off-pin 0.87.1 lookalikes, and pass 1.0.0 bare or with any measured peer-hash — got: ${matcher_probe:-<nothing leaked>}"
|
|
3742
3775
|
return 1
|
|
3743
3776
|
fi
|
|
3744
3777
|
|
|
3745
3778
|
# Cell 2 — the closure prefix. chord is a non-`pi-` member of the same runtime closure
|
|
3746
|
-
# (0.85.0 onward, still true at 0.
|
|
3779
|
+
# (0.85.0 onward, still true at 0.99.1). An off-pin chord MUST leak; the pinned one must not. A matcher narrowed
|
|
3747
3780
|
# back to `^@earendil-works+pi-` sees nothing here and dies at this signature.
|
|
3748
3781
|
matcher_probe=$(printf '%s\n' \
|
|
3782
|
+
'@earendil-works+chord@1.0.0' \
|
|
3749
3783
|
'@earendil-works+chord@0.87.1' \
|
|
3750
|
-
'@earendil-works+
|
|
3751
|
-
|
|
3752
|
-
|
|
3753
|
-
fail "[QK:PACK-INSTALL-PIN-MATCHER-COVERS-CLOSURE] the pin-leak matcher must cover every @earendil-works closure member, not just the pi-* families — an off-pin @earendil-works/chord has to leak (it is a runtime dependency of pi-coding-agent, pi-agent-core, pi-client and pi-protocol — `[측정 2026-09-20]` at 0.86.0, re-measured `[측정 2026-09-22]` at 0.87.0 and `[측정 2026-09-23]` at 0.87.1: the same four @earendil-works direct deps) — got: ${matcher_probe:-<nothing leaked>}"
|
|
3784
|
+
'@earendil-works+pi-ai@1.0.0' | pack_install_leaked_pi)
|
|
3785
|
+
if [ "$matcher_probe" != '@earendil-works+chord@0.87.1' ]; then
|
|
3786
|
+
fail "[QK:PACK-INSTALL-PIN-MATCHER-COVERS-CLOSURE] the pin-leak matcher must cover every @earendil-works closure member, not just the pi-* families — an off-pin @earendil-works/chord has to leak (it is a runtime dependency of pi-coding-agent, pi-agent-core, pi-client and pi-protocol — `[측정 2026-09-20]` at 0.86.0, re-measured `[측정 2026-09-22]` at 0.87.0, `[측정 2026-09-23]` at 0.87.1 and `[측정 2026-09-30]` at 0.99.1, where pi-coding-agent adds pi-codemode and pi-mcp beside them) — got: ${matcher_probe:-<nothing leaked>}"
|
|
3754
3787
|
return 1
|
|
3755
3788
|
fi
|
|
3756
3789
|
|
|
3757
3790
|
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)"
|
|
3758
3791
|
}
|
|
3759
3792
|
|
|
3793
|
+
# The writable roots of ONE consumer sandbox, shared by every consumer subcall of
|
|
3794
|
+
# check-pack-install that drives an installed `run.sh` writer. The writers resolve their agent
|
|
3795
|
+
# dir as `${PI_CODING_AGENT_DIR:-$HOME/.pi/agent}` (`resolve`/`install` below), so swapping HOME
|
|
3796
|
+
# alone let an INHERITED PI_CODING_AGENT_DIR win and the consumer's user-scope registration land
|
|
3797
|
+
# outside the sandbox (#125, measured 2026-10-01: an inherited agent dir received the npm
|
|
3798
|
+
# consumer's settings.json while the gate looked under the temp HOME). Every root is named here,
|
|
3799
|
+
# none is left to the caller's environment. One NAME=value per line; read with mapfile.
|
|
3800
|
+
pack_install_consumer_env() {
|
|
3801
|
+
local h="$1"
|
|
3802
|
+
printf '%s\n' \
|
|
3803
|
+
"HOME=$h" \
|
|
3804
|
+
"XDG_CONFIG_HOME=$h/.config" \
|
|
3805
|
+
"XDG_DATA_HOME=$h/.local/share" \
|
|
3806
|
+
"XDG_STATE_HOME=$h/.local/state" \
|
|
3807
|
+
"XDG_CACHE_HOME=$h/.cache" \
|
|
3808
|
+
"PI_CODING_AGENT_DIR=$h/.pi/agent"
|
|
3809
|
+
}
|
|
3810
|
+
|
|
3811
|
+
# Self-test of the consumer roots, snapshot-safe like the pin matcher: under a POISONED inherited
|
|
3812
|
+
# environment every root a consumer subcall sees must sit inside the consumer home. Runs first
|
|
3813
|
+
# inside check-pack-install too, so the heavy gate cannot proceed on a leaking root set.
|
|
3814
|
+
check_pack_consumer_roots() {
|
|
3815
|
+
local h="/pack-consumer-home" roots=() seen line
|
|
3816
|
+
mapfile -t roots < <(pack_install_consumer_env "$h")
|
|
3817
|
+
seen=$(env HOME=/poisoned/home XDG_CONFIG_HOME=/poisoned/config XDG_DATA_HOME=/poisoned/data \
|
|
3818
|
+
XDG_STATE_HOME=/poisoned/state XDG_CACHE_HOME=/poisoned/cache PI_CODING_AGENT_DIR=/poisoned/agent \
|
|
3819
|
+
"${roots[@]}" sh -c 'printf "%s\n" "$HOME" "$XDG_CONFIG_HOME" "$XDG_DATA_HOME" "$XDG_STATE_HOME" "$XDG_CACHE_HOME" "$PI_CODING_AGENT_DIR"')
|
|
3820
|
+
while IFS= read -r line; do
|
|
3821
|
+
case "$line" in
|
|
3822
|
+
"$h" | "$h"/*) ;;
|
|
3823
|
+
*)
|
|
3824
|
+
fail "[QK:PACK-INSTALL-CONSUMER-ROOTS-CONTAINED] every writable root of a consumer subcall must be inside its own consumer home ($h) whatever the caller inherited — got: $(printf '%s' "$seen" | tr '\n' ' ')"
|
|
3825
|
+
return 1
|
|
3826
|
+
;;
|
|
3827
|
+
esac
|
|
3828
|
+
done <<<"$seen"
|
|
3829
|
+
echo "[check-pack-consumer-roots] ok — HOME, the four XDG roots and PI_CODING_AGENT_DIR of a consumer subcall all resolve inside the consumer home under a poisoned inherited environment"
|
|
3830
|
+
}
|
|
3831
|
+
|
|
3832
|
+
# Hand-built oracle for the REAL consumer run: check-pack-install exports a sentinel agent dir for
|
|
3833
|
+
# its whole body, standing in for whatever a caller inherits; a subcall that falls back to it
|
|
3834
|
+
# writes there, and this names the stage that did.
|
|
3835
|
+
pack_install_sentinel_untouched() {
|
|
3836
|
+
local sentinel="$1" stage="$2" found
|
|
3837
|
+
found=$(find "$sentinel" -mindepth 1 2>/dev/null | head -5)
|
|
3838
|
+
if [ -n "$found" ]; then
|
|
3839
|
+
fail "[QK:PACK-INSTALL-INHERITED-AGENTDIR-UNTOUCHED] $stage wrote into the inherited PI_CODING_AGENT_DIR instead of its own consumer sandbox:"
|
|
3840
|
+
printf '%s\n' "$found" | sed 's/^/ /' >&2
|
|
3841
|
+
return 1
|
|
3842
|
+
fi
|
|
3843
|
+
}
|
|
3844
|
+
|
|
3760
3845
|
_check_pack_install_impl() {
|
|
3761
3846
|
check_pack_pin_matcher || return 1
|
|
3847
|
+
check_pack_consumer_roots || return 1
|
|
3762
3848
|
|
|
3763
3849
|
# Heavy publish gate. Runs the remaining three checks in #13's
|
|
3764
3850
|
# publish checklist that check_pack (dry-run only) does not cover:
|
|
@@ -3797,6 +3883,11 @@ _check_pack_install_impl() {
|
|
|
3797
3883
|
local pack_tmp
|
|
3798
3884
|
pack_tmp=$(mktemp -d -t entwurf-pack.XXXXXX)
|
|
3799
3885
|
tgz_path="${pack_tmp}/${tgz_name}"
|
|
3886
|
+
# Stand-in for an inherited agent dir, scoped to this function: no consumer subcall may fall
|
|
3887
|
+
# back to it (pack_install_sentinel_untouched). It also keeps the caller's own one out of reach.
|
|
3888
|
+
local agentdir_sentinel="${pack_tmp}/inherited-agent-dir"
|
|
3889
|
+
mkdir -p "$agentdir_sentinel"
|
|
3890
|
+
local -x PI_CODING_AGENT_DIR="$agentdir_sentinel"
|
|
3800
3891
|
|
|
3801
3892
|
# 0.12.1 C — stale-dist guard. `tsc` emit does NOT prune orphaned files from
|
|
3802
3893
|
# outDir, and `files: ["mcp/"]` would carry any leftover dist file into the
|
|
@@ -4027,18 +4118,24 @@ _check_pack_install_impl() {
|
|
|
4027
4118
|
# pi-agent-core incident. The list moves WITH the floor and is re-measured at
|
|
4028
4119
|
# each bump, never pruned on a single quiet minor.
|
|
4029
4120
|
# The verified floor is 0.86.0 as of 2026-09-20.
|
|
4030
|
-
|
|
4121
|
+
# `[측정 2026-09-30]` at 0.99.1 the constellation GREW: pi-coding-agent now declares
|
|
4122
|
+
# @earendil-works/pi-codemode and @earendil-works/pi-mcp as runtime dependencies (the built-in
|
|
4123
|
+
# codemode and MCP extensions), so both are pinned beside the eight. pi-client and pi-protocol are
|
|
4124
|
+
# still published at 0.99.1 and keep their pins under the rule above.
|
|
4125
|
+
echo "[check-pack-install] pnpm add into $tmp (with 1.0.x peers + chord + codemode + mcp + typebox)"
|
|
4031
4126
|
local install_log
|
|
4032
4127
|
install_log=$(cd "$tmp" && pnpm add \
|
|
4033
4128
|
"$tgz_path" \
|
|
4034
|
-
"@earendil-works/pi-ai@0.
|
|
4035
|
-
"@earendil-works/pi-coding-agent@0.
|
|
4036
|
-
"@earendil-works/pi-tui@0.
|
|
4037
|
-
"@earendil-works/pi-agent-core@0.
|
|
4038
|
-
"@earendil-works/pi-client@0.
|
|
4039
|
-
"@earendil-works/pi-protocol@0.
|
|
4040
|
-
"@earendil-works/pi-telemetry@0.
|
|
4041
|
-
"@earendil-works/chord@0.
|
|
4129
|
+
"@earendil-works/pi-ai@1.0.0" \
|
|
4130
|
+
"@earendil-works/pi-coding-agent@1.0.0" \
|
|
4131
|
+
"@earendil-works/pi-tui@1.0.0" \
|
|
4132
|
+
"@earendil-works/pi-agent-core@1.0.0" \
|
|
4133
|
+
"@earendil-works/pi-client@1.0.0" \
|
|
4134
|
+
"@earendil-works/pi-protocol@1.0.0" \
|
|
4135
|
+
"@earendil-works/pi-telemetry@1.0.0" \
|
|
4136
|
+
"@earendil-works/chord@1.0.0" \
|
|
4137
|
+
"@earendil-works/pi-codemode@1.0.0" \
|
|
4138
|
+
"@earendil-works/pi-mcp@1.0.0" \
|
|
4042
4139
|
"typebox@latest" \
|
|
4043
4140
|
--ignore-workspace --ignore-scripts 2>&1) || {
|
|
4044
4141
|
fail "[check-pack-install] pnpm add failed:"
|
|
@@ -4054,11 +4151,11 @@ _check_pack_install_impl() {
|
|
|
4054
4151
|
local leaked_pi
|
|
4055
4152
|
leaked_pi=$(ls "$tmp/node_modules/.pnpm" 2>/dev/null | pack_install_leaked_pi)
|
|
4056
4153
|
if [ -n "$leaked_pi" ]; then
|
|
4057
|
-
fail "[check-pack-install] UNVERIFIED pi runtime resolved into the install tree (expected only 0.
|
|
4154
|
+
fail "[check-pack-install] UNVERIFIED pi runtime resolved into the install tree (expected only 1.0.0):"
|
|
4058
4155
|
printf '%s\n' "$leaked_pi" | sed 's/^/ /' >&2
|
|
4059
4156
|
return 1
|
|
4060
4157
|
fi
|
|
4061
|
-
echo "[check-pack-install] pi runtime tree pin verified: every @earendil-works package is 0.
|
|
4158
|
+
echo "[check-pack-install] pi runtime tree pin verified: every @earendil-works package is 1.0.0 (chord included)"
|
|
4062
4159
|
|
|
4063
4160
|
# Resolve the installed package.json and confirm pi.extensions
|
|
4064
4161
|
# arrived intact. If pi.extensions is empty or missing, the
|
|
@@ -4271,11 +4368,14 @@ _check_pack_install_impl() {
|
|
|
4271
4368
|
ls -l "$npmroot/node_modules/.bin" 2>/dev/null | sed 's/^/ /' >&2 || true
|
|
4272
4369
|
return 1
|
|
4273
4370
|
fi
|
|
4274
|
-
|
|
4371
|
+
local consumer_env=()
|
|
4372
|
+
mapfile -t consumer_env < <(pack_install_consumer_env "$npmhome")
|
|
4373
|
+
wire_log=$(env "${consumer_env[@]}" "$npm_pkg/run.sh" install "$npmproj" 2>&1) || {
|
|
4275
4374
|
fail "[check-pack-install] npm-managed run.sh install failed (preflight rejected hoisted deps?):"
|
|
4276
4375
|
echo "$wire_log" | tail -15 | sed 's/^/ /' >&2
|
|
4277
4376
|
return 1
|
|
4278
4377
|
}
|
|
4378
|
+
pack_install_sentinel_untouched "$agentdir_sentinel" "npm-managed run.sh install" || return 1
|
|
4279
4379
|
if [ ! -f "$npmproj/.pi/settings.json" ]; then
|
|
4280
4380
|
fail "[check-pack-install] npm-managed run.sh install did not write settings.json:"
|
|
4281
4381
|
echo "$wire_log" | tail -15 | sed 's/^/ /' >&2
|
|
@@ -4313,7 +4413,7 @@ sys.exit(0 if any(isinstance(s,str) and s.endswith('/node_modules/@junghanacs/en
|
|
|
4313
4413
|
# a KNOWN flag and the entwurf provider must load, sourced only from user scope.
|
|
4314
4414
|
# Before the fix this printed "Unknown options: --entwurf-control".
|
|
4315
4415
|
local foreign_out
|
|
4316
|
-
foreign_out=$(cd "$tmp" &&
|
|
4416
|
+
foreign_out=$(cd "$tmp" && env "${consumer_env[@]}" "$pi_bin" --entwurf-control --list-models entwurf 2>&1) || {
|
|
4317
4417
|
fail "[check-pack-install] foreign-cwd --entwurf-control smoke failed (user-scope citizen not loading?):"
|
|
4318
4418
|
echo "$foreign_out" | tail -10 | sed 's/^/ /' >&2
|
|
4319
4419
|
return 1
|
|
@@ -4338,7 +4438,7 @@ sys.exit(0 if any(isinstance(s,str) and s.endswith('/node_modules/@junghanacs/en
|
|
|
4338
4438
|
}
|
|
4339
4439
|
npm2_pkg="$npmroot2/node_modules/@junghanacs/entwurf"
|
|
4340
4440
|
set +e
|
|
4341
|
-
two_root_out=$(
|
|
4441
|
+
two_root_out=$(env "${consumer_env[@]}" "$npm2_pkg/run.sh" install "$npmproj2" 2>&1)
|
|
4342
4442
|
two_root_rc=$?
|
|
4343
4443
|
set -e
|
|
4344
4444
|
if [ "$two_root_rc" -eq 0 ] || ! grep -q "takeover-user-scope" <<<"$two_root_out"; then
|
|
@@ -4359,7 +4459,7 @@ sys.exit(0 if ok_a and not bad_b else 1)
|
|
|
4359
4459
|
return 1
|
|
4360
4460
|
fi
|
|
4361
4461
|
set +e
|
|
4362
|
-
two_root_out=$(
|
|
4462
|
+
two_root_out=$(env "${consumer_env[@]}" "$npm2_pkg/run.sh" takeover-user-scope 2>&1)
|
|
4363
4463
|
two_root_rc=$?
|
|
4364
4464
|
set -e
|
|
4365
4465
|
if [ "$two_root_rc" -ne 0 ] || ! grep -q "takeover: user-scope entwurf registration moved" <<<"$two_root_out"; then
|
|
@@ -4391,7 +4491,7 @@ sys.exit(0 if st.get('installerRoot','').rstrip('/')== '$npm2_pkg' else 1)
|
|
|
4391
4491
|
return 1
|
|
4392
4492
|
fi
|
|
4393
4493
|
set +e
|
|
4394
|
-
two_root_out=$(
|
|
4494
|
+
two_root_out=$(env "${consumer_env[@]}" "$npm_pkg/run.sh" remove-user-scope 2>&1)
|
|
4395
4495
|
two_root_rc=$?
|
|
4396
4496
|
set -e
|
|
4397
4497
|
if [ "$two_root_rc" -eq 0 ] || ! python3 -c "
|
|
@@ -4404,6 +4504,7 @@ sys.exit(0 if any(isinstance(s,str) and s.rstrip('/')== '$npm2_pkg' for s in src
|
|
|
4404
4504
|
echo "$two_root_out" | tail -8 | sed 's/^/ /' >&2
|
|
4405
4505
|
return 1
|
|
4406
4506
|
fi
|
|
4507
|
+
pack_install_sentinel_untouched "$agentdir_sentinel" "two-root ownership row (install / takeover-user-scope / remove-user-scope)" || return 1
|
|
4407
4508
|
echo "[check-pack-install] two-root ownership row pass (second root: normal install refused naming takeover-user-scope; explicit takeover moved entry + provider installerRoot old->new; old root's inverse refused)"
|
|
4408
4509
|
|
|
4409
4510
|
# Installed-package aggregate `setup` row (#86 C1, review blocker 2026-08-26):
|
|
@@ -5153,7 +5254,9 @@ process.stdout.write(resolveCodexDefaultSocketPath(process.env));
|
|
|
5153
5254
|
JSON
|
|
5154
5255
|
local pc_seed_sha
|
|
5155
5256
|
pc_seed_sha=$(sha256sum "$pc_store/$pc_record" | cut -d' ' -f1)
|
|
5156
|
-
|
|
5257
|
+
# `-u`: the default resolution is only proven if no inherited agent dir can pre-empt it
|
|
5258
|
+
# (check-pack-install exports a sentinel one for its whole body).
|
|
5259
|
+
local pc_env=(-u PI_CODING_AGENT_DIR HOME="$pc_home" XDG_DATA_HOME="$pc_home/.local/share" XDG_STATE_HOME="$pc_home/.local/state" XDG_CACHE_HOME="$pc_home/.cache" CLAUDE_CONFIG_DIR="$pc_cfg" FAKE_CLAUDE_LOG="$pc_claude_log" PATH="$fake_claude_dir:$npmroot/node_modules/.bin:$PATH")
|
|
5157
5260
|
local pc_before pc_after pc_out pc_rc
|
|
5158
5261
|
pc_before=$(cd "$pc_home" && find . -type f -exec sha256sum {} + 2>/dev/null | sort)
|
|
5159
5262
|
|
|
@@ -5338,6 +5441,7 @@ JSON
|
|
|
5338
5441
|
fi
|
|
5339
5442
|
echo "[check-pack-install] installed doctor dispatch lock pass (store-scan → dist JS, v2-surface deferred)"
|
|
5340
5443
|
|
|
5444
|
+
pack_install_sentinel_untouched "$agentdir_sentinel" "a later installed-consumer cell" || return 1
|
|
5341
5445
|
ok "[check-pack-install] publish install smoke pass"
|
|
5342
5446
|
return 0
|
|
5343
5447
|
}
|
|
@@ -5563,15 +5667,15 @@ setup_mode() {
|
|
|
5563
5667
|
}
|
|
5564
5668
|
|
|
5565
5669
|
# Supported pi range, derived at runtime from the package.json devDependencies
|
|
5566
|
-
# pin (the SSOT check-dep-versions binds: peer range == `>=<pin>
|
|
5670
|
+
# pin (the SSOT check-dep-versions binds: peer range == `>=<pin> <<major>.<minor+1>`).
|
|
5567
5671
|
# Never retyped here as a second literal.
|
|
5568
5672
|
pi_supported_range() {
|
|
5569
5673
|
node -e '
|
|
5570
5674
|
const pkg = require(process.argv[1]);
|
|
5571
5675
|
const pin = pkg.devDependencies?.["@earendil-works/pi-coding-agent"];
|
|
5572
|
-
if (
|
|
5573
|
-
const [, min] = pin.split(".").map(Number);
|
|
5574
|
-
console.log(`>=${pin}
|
|
5676
|
+
if (!/^\d+\.\d+\.\d+$/.test(pin ?? "")) { console.error(`unparseable pi pin: ${pin}`); process.exit(1); }
|
|
5677
|
+
const [maj, min] = pin.split(".").map(Number);
|
|
5678
|
+
console.log(`>=${pin} <${maj}.${min + 1}`);
|
|
5575
5679
|
' "$REPO_DIR/package.json"
|
|
5576
5680
|
}
|
|
5577
5681
|
|
|
@@ -6655,6 +6759,9 @@ case "$cmd" in
|
|
|
6655
6759
|
check-entwurf-resume-args)
|
|
6656
6760
|
check_entwurf_resume_args
|
|
6657
6761
|
;;
|
|
6762
|
+
check-acp-foreground-only)
|
|
6763
|
+
check_acp_foreground_only
|
|
6764
|
+
;;
|
|
6658
6765
|
check-resume-launch-identity)
|
|
6659
6766
|
check_resume_launch_identity
|
|
6660
6767
|
;;
|
|
@@ -7617,6 +7724,9 @@ case "$cmd" in
|
|
|
7617
7724
|
check-acp-prompt-lifecycle)
|
|
7618
7725
|
check_acp_prompt_lifecycle
|
|
7619
7726
|
;;
|
|
7727
|
+
check-acp-vendor-rejection)
|
|
7728
|
+
check_acp_vendor_rejection
|
|
7729
|
+
;;
|
|
7620
7730
|
check-acp-launch-namespace)
|
|
7621
7731
|
check_acp_launch_namespace
|
|
7622
7732
|
;;
|
|
@@ -7662,6 +7772,17 @@ case "$cmd" in
|
|
|
7662
7772
|
shift || true
|
|
7663
7773
|
run_ts scripts/check-codex-bridge-identity.ts "$@"
|
|
7664
7774
|
;;
|
|
7775
|
+
check-pi-bridge-sender)
|
|
7776
|
+
shift || true
|
|
7777
|
+
run_ts scripts/check-pi-bridge-sender.ts "$@"
|
|
7778
|
+
;;
|
|
7779
|
+
check-pi-mcp-register)
|
|
7780
|
+
check_pi_mcp_register
|
|
7781
|
+
;;
|
|
7782
|
+
check-pi-mcp-bridge)
|
|
7783
|
+
shift || true
|
|
7784
|
+
run_ts scripts/check-pi-mcp-bridge.ts "$@"
|
|
7785
|
+
;;
|
|
7665
7786
|
check-codex-native-push)
|
|
7666
7787
|
shift || true
|
|
7667
7788
|
run_ts scripts/check-codex-native-push.ts "$@"
|
|
@@ -7688,6 +7809,9 @@ case "$cmd" in
|
|
|
7688
7809
|
check-pack-pin-matcher)
|
|
7689
7810
|
check_pack_pin_matcher
|
|
7690
7811
|
;;
|
|
7812
|
+
check-pack-consumer-roots)
|
|
7813
|
+
check_pack_consumer_roots
|
|
7814
|
+
;;
|
|
7691
7815
|
check-pack-install)
|
|
7692
7816
|
check_pack_install
|
|
7693
7817
|
;;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* check-entwurf-peers-surface — deterministic gate for the MCP
|
|
2
|
+
* check-entwurf-peers-surface — deterministic gate for the `entwurf_peers` (one MCP surface since #125)
|
|
3
3
|
* RENDER/PAYLOAD layer (0.11 Stage 0 step 4, slice 4c; #50 C4 re-author). Drives the PURE
|
|
4
4
|
* `renderEntwurfPeers` with a fabricated `EntwurfFactsResult` (no IO) and proves
|
|
5
5
|
* the surface contract (GPi + Fable 수렴 + the C4 demotion):
|
|
@@ -17,9 +17,9 @@
|
|
|
17
17
|
* dropped),
|
|
18
18
|
* - diagnostics sharing one (kind + message) AGGREGATE in text (F8) — including
|
|
19
19
|
* record-less-socket groups, which share a message per liveness,
|
|
20
|
-
* - WIRING guard (Fable e②/6): the MCP handler
|
|
21
|
-
*
|
|
22
|
-
*
|
|
20
|
+
* - WIRING guard (Fable e②/6): the MCP handler calls listEntwurfFacts +
|
|
21
|
+
* renderEntwurfPeers; getLiveSessions and the `/entwurf-sessions` socket-scan
|
|
22
|
+
* command stay gone from the bridge AND the pi extension.
|
|
23
23
|
*
|
|
24
24
|
* No IO — the facts are fabricated; only the wiring guard reads the bridge source
|
|
25
25
|
* as text (a static assertion, not an execution).
|
|
@@ -252,31 +252,24 @@ function main(): void {
|
|
|
252
252
|
ok("the aggregated record-less line samples gids", agg.text.includes(gids[0] as string));
|
|
253
253
|
}
|
|
254
254
|
|
|
255
|
-
// ── WIRING guard:
|
|
255
|
+
// ── WIRING guard: the bridge calls the provider+render; the socket-scan lane is gone ──
|
|
256
|
+
// #125: the pi-native entwurf_peers copy is retired — a pi session reads the bridge's own
|
|
257
|
+
// surface through Pi's built-in MCP — so its wiring halves left with it. The pi file is still
|
|
258
|
+
// held to the lanes it must NOT grow back.
|
|
256
259
|
{
|
|
257
260
|
const here = path.dirname(fileURLToPath(import.meta.url));
|
|
258
261
|
const bridgeSrc = readFileSync(path.join(here, "..", "mcp", "entwurf-bridge", "src", "index.ts"), "utf8");
|
|
259
262
|
const nativeSrc = readFileSync(path.join(here, "..", "pi-extensions", "entwurf-control.ts"), "utf8");
|
|
260
263
|
ok("wiring: bridge calls listEntwurfFacts(", bridgeSrc.includes("listEntwurfFacts("));
|
|
261
264
|
ok("wiring: bridge calls renderEntwurfPeers(", bridgeSrc.includes("renderEntwurfPeers("));
|
|
262
|
-
ok("wiring: native pi tool calls listEntwurfFacts(", nativeSrc.includes("listEntwurfFacts("));
|
|
263
|
-
ok("wiring: native pi tool calls renderEntwurfPeers(", nativeSrc.includes("renderEntwurfPeers("));
|
|
264
265
|
ok(
|
|
265
266
|
"#112 wiring: MCP observation uses the render limit",
|
|
266
267
|
bridgeSrc.includes("observationLimit: ENTWURF_PEERS_RENDER_LIMIT"),
|
|
267
268
|
);
|
|
268
|
-
ok(
|
|
269
|
-
"#112 wiring: native pi observation uses the render limit",
|
|
270
|
-
nativeSrc.includes("observationLimit: render.ENTWURF_PEERS_RENDER_LIMIT"),
|
|
271
|
-
);
|
|
272
269
|
ok(
|
|
273
270
|
"wiring: bridge does not paste full JSON payload into human text",
|
|
274
271
|
!bridgeSrc.includes("JSON.stringify(payload)"),
|
|
275
272
|
);
|
|
276
|
-
ok(
|
|
277
|
-
"wiring: native pi tool does not paste full JSON payload into human text",
|
|
278
|
-
!nativeSrc.includes("JSON.stringify(payload)"),
|
|
279
|
-
);
|
|
280
273
|
// `\bname\s*\(` catches a definition OR a call (tolerating a space before the
|
|
281
274
|
// paren, GPi Q4); a bare prose mention in a removal-note comment (no paren) is
|
|
282
275
|
// allowed — the guard targets the second scan, not the word.
|
|
@@ -495,17 +495,13 @@ ok(
|
|
|
495
495
|
/origin:\s*"meta-session"/.test(metaBody) && /replyable:\s*self\.replyable/.test(metaBody),
|
|
496
496
|
);
|
|
497
497
|
|
|
498
|
-
// ──
|
|
499
|
-
//
|
|
500
|
-
//
|
|
501
|
-
//
|
|
498
|
+
// ── pi-session replyability has ONE computation now (#125) ──
|
|
499
|
+
// The pi-native sender decoration (`decoratePiSenderAddressability`) left with the native verbs: a
|
|
500
|
+
// pi session reaches entwurf_v2/entwurf_self through the bridge, whose `buildStrictPiSenderEnvelope`
|
|
501
|
+
// derives replyability from the same computeSelfAddressability + canonical-socket existsSync and is
|
|
502
|
+
// pinned above. What the pi file still takes from this fence is the not-a-citizen notice below,
|
|
503
|
+
// reached through the non-literal dynamic import that keeps the `.ts` fence out of root tsc.
|
|
502
504
|
const nativeSrc = readFileSync(path.join(REPO_DIR, "pi-extensions", "entwurf-control.ts"), "utf8");
|
|
503
|
-
ok(
|
|
504
|
-
"pi-native: decoratePiSenderAddressability derives replyable from computeSelfAddressability + canonical existsSync",
|
|
505
|
-
/function\s+decoratePiSenderAddressability/.test(nativeSrc) &&
|
|
506
|
-
/computeSelfAddressability/.test(nativeSrc) &&
|
|
507
|
-
/existsSync\s*\(\s*getSocketPath/.test(nativeSrc),
|
|
508
|
-
);
|
|
509
505
|
ok(
|
|
510
506
|
"pi-native: reaches the self-address fence via non-literal dynamic import (no static import; TS5097)",
|
|
511
507
|
/const ENTWURF_SELF_ADDRESS_MODULE\s*=/.test(nativeSrc) &&
|
|
@@ -555,7 +551,7 @@ ok(
|
|
|
555
551
|
// a mutant to move it into, and passing a constant would make the decision above unreachable.
|
|
556
552
|
const noticeBody = nativeSrc.slice(
|
|
557
553
|
nativeSrc.indexOf("async function noticeUncitizenedSession"),
|
|
558
|
-
nativeSrc.indexOf("function
|
|
554
|
+
nativeSrc.indexOf("// Extension Export", nativeSrc.indexOf("async function noticeUncitizenedSession")),
|
|
559
555
|
);
|
|
560
556
|
ok(
|
|
561
557
|
"pi-native: the not-a-citizen notice is decided from the REAL flag, on the UI only, never on stderr " +
|