@junghanacs/entwurf 0.50.0 → 0.52.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/CHANGELOG.md +140 -0
- package/ENTWURF.json +10 -4
- package/README.md +16 -8
- package/VERIFY.md +16 -9
- package/demo/README.md +1 -1
- package/docs/acp-backend-rail.md +1 -1
- package/docs/external-mcp-host.md +2 -2
- package/docs/setup-clean-host.md +92 -35
- package/mcp/entwurf-bridge/dist/ENTWURF.json +10 -4
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-control-rpc.js +4 -1
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-contract.js +4 -4
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/meta-mailbox-body.js +1 -1
- package/package.json +11 -18
- package/pi/pi-durable/carrier/harness-setup.js +1 -1
- package/pi/pi-durable/carrier/runtime.js +221 -50
- package/pi/pi-durable/carrier/subagent.js +6 -3
- package/pi/pi-durable/carrier/tui.js +60 -22
- package/pi/pi-durable/overlay/upstream-pin.json +19 -18
- package/pi-extensions/lib/entwurf-control-rpc.ts +5 -1
- package/pi-extensions/lib/entwurf-v2-contract-schema.ts +73 -69
- package/pi-extensions/lib/entwurf-v2-contract.ts +4 -4
- package/pi-extensions/lib/meta-mailbox-body.ts +1 -1
- package/pi-extensions/meta-bridge-pi-durable.ts +1 -1
- package/run.sh +148 -46
- package/scripts/check-entwurf-v2-contract.ts +4 -4
- package/scripts/check-entwurf-v2-surface.ts +7 -21
- package/scripts/check-gate-qualification.ts +3 -3
- package/scripts/check-pi-durable-send.ts +4 -1
- package/scripts/lib/codex-fresh-source-receipts.ts +12 -6
- package/scripts/mutants/agent-guidance.json +2 -2
- package/scripts/mutants/codex-native.json +2 -2
- package/scripts/mutants/control-send-receipt.json +47 -7
- package/scripts/mutants/pack-install.json +4 -4
- package/scripts/mutants/pi-durable.json +189 -3
- package/scripts/mutants/pi-floor.json +20 -5
- package/scripts/mutants/v2-surface.json +4 -6
- package/scripts/smoke-acp-cortex-live.ts +1 -1
- package/scripts/smoke-acp-v2-send-live.ts +1 -1
package/run.sh
CHANGED
|
@@ -1120,7 +1120,7 @@ check_entwurf_v2_contract() {
|
|
|
1120
1120
|
# visible-first cut, single verdict per cell, 1 allow / 2 reject. N1
|
|
1121
1121
|
# indeterminate never dispatches, and nothing on any cell starts a process.
|
|
1122
1122
|
# R5 taxonomy covers table reasons + pre-claims bad-target/target-locked. Plus
|
|
1123
|
-
# a schema↔types drift guard on the
|
|
1123
|
+
# a schema↔types drift guard on the pi-schema input/receipt. Pure, no API.
|
|
1124
1124
|
run_ts scripts/check-entwurf-v2-contract.ts
|
|
1125
1125
|
}
|
|
1126
1126
|
|
|
@@ -1156,8 +1156,10 @@ check_control_send_receipt() {
|
|
|
1156
1156
|
# test titles `run_vitest` emits. Narrow file filter, same reason as check-entwurf-v2-send.
|
|
1157
1157
|
# It stays inside check:contracts even though the discovery door also runs this file — see
|
|
1158
1158
|
# check-release-gate-outcomes: a gate a mutant names must be reachable outside qualification.
|
|
1159
|
-
|
|
1160
|
-
|
|
1159
|
+
# #159 adds the three delivery-input seams here as a shared mutant coordinate; discovery remains
|
|
1160
|
+
# the beside glob. This reuses the existing gate instead of adding a second help/catalog surface.
|
|
1161
|
+
section "control-socket acceptance and received guidance (mutant execution coordinate)"
|
|
1162
|
+
run_vitest pi-extensions/lib/control-send-receipt.test.ts pi-extensions/entwurf-control.test.ts pi-extensions/lib/entwurf-v2-mailbox.test.ts pi-extensions/lib/entwurf-v2-native-push.test.ts
|
|
1161
1163
|
}
|
|
1162
1164
|
|
|
1163
1165
|
check_control_retire() {
|
|
@@ -2340,21 +2342,64 @@ function stripBumpLedger(file, text) {
|
|
|
2340
2342
|
return kept.join('\n');
|
|
2341
2343
|
}
|
|
2342
2344
|
|
|
2343
|
-
|
|
2345
|
+
// PUBLISHED-SCOPE REGIONS (#155). Between an SDK bump in this checkout and the release that ships it,
|
|
2346
|
+
// an operator doc must keep telling a reader how to install the package that IS on npm — whose Pi
|
|
2347
|
+
// range is the previous floor — while the checkout's own sentences already name the new one. Those
|
|
2348
|
+
// are two different subjects in one file, so the doc marks the published one explicitly:
|
|
2349
|
+
// <!-- entwurf:published @junghanacs/entwurf@<version> pi <sdk> --> … <!-- /entwurf:published -->
|
|
2350
|
+
// Inside a region every scan below binds to <sdk>, outside it to the pinned floor. The region is not
|
|
2351
|
+
// a skip: it is scanned, it must hold at least one declaration, and after the four-surface assertion
|
|
2352
|
+
// below the published-scope claim holds its pair to PUBLISHED_ARTIFACT — a published package's own
|
|
2353
|
+
// manifest, frozen here. It is deliberately NOT derived from this checkout (package.json's version,
|
|
2354
|
+
// the workspace closure): a published package's contract stays true when the checkout moves on, so a
|
|
2355
|
+
// release preparation must not falsify it. `[측정 2026-11-03]` the source-only qualification snapshot
|
|
2356
|
+
// has no .git, so a release tag cannot be that oracle either.
|
|
2357
|
+
const PUBLISHED_OPEN = /<!-- entwurf:published @junghanacs\/entwurf@(\d+\.\d+\.\d+) pi (\d+\.\d+\.\d+) -->/g;
|
|
2358
|
+
const PUBLISHED_CLOSE = '<!-- /entwurf:published -->';
|
|
2359
|
+
function publishedRegions(text) {
|
|
2360
|
+
const regions = [], problems = [];
|
|
2361
|
+
let from = 0;
|
|
2362
|
+
for (const open of text.matchAll(PUBLISHED_OPEN)) {
|
|
2363
|
+
if (open.index < from) { problems.push(`a region opens inside the previous one at offset ${open.index}`); continue; }
|
|
2364
|
+
const end = text.indexOf(PUBLISHED_CLOSE, open.index);
|
|
2365
|
+
const next = text.slice(open.index + open[0].length).search(/<!-- entwurf:published /);
|
|
2366
|
+
if (end < 0 || (next >= 0 && open.index + open[0].length + next < end)) { problems.push(`the region opened by "${open[0]}" is not closed before the next one`); continue; }
|
|
2367
|
+
regions.push({ start: open.index, end, version: open[1], sdk: open[2], body: text.slice(open.index, end) });
|
|
2368
|
+
from = end + PUBLISHED_CLOSE.length;
|
|
2369
|
+
}
|
|
2370
|
+
const opens = [...text.matchAll(/<!-- entwurf:published /g)].length;
|
|
2371
|
+
const closes = text.split(PUBLISHED_CLOSE).length - 1;
|
|
2372
|
+
if (opens !== regions.length + problems.length) problems.push(`${opens - regions.length} published marker(s) do not have the exact opening shape`);
|
|
2373
|
+
if (closes !== regions.length) problems.push(`${closes} closing marker(s) for ${regions.length} region(s)`);
|
|
2374
|
+
return { regions, problems };
|
|
2375
|
+
}
|
|
2376
|
+
const floorAt = (regions, index) => regions.find((r) => index > r.start && index < r.end)?.sdk ?? piAi;
|
|
2377
|
+
const nextMinor = (v) => { const [a, b] = v.split('.').map(Number); return `${a}.${b + 1}`; };
|
|
2378
|
+
|
|
2379
|
+
let rangeDecls = 0, exactDecls = 0, sourceRangeDecls = 0;
|
|
2344
2380
|
for (const file of BASELINE_DOCS) {
|
|
2345
2381
|
const text = stripBumpLedger(file, readFileSync(file, 'utf8'));
|
|
2382
|
+
const { regions } = publishedRegions(text);
|
|
2346
2383
|
// Closed-range declarations: `>=<floor> <<major>.<ceiling>` (spaces optional).
|
|
2347
|
-
for (const
|
|
2384
|
+
for (const m of text.matchAll(/>=\s?(\d+\.\d+\.\d+)\s?<\s?(\d+)\.(\d+)/g)) {
|
|
2385
|
+
const [decl, floor, ceilMajor, ceilMinor] = m;
|
|
2386
|
+
const want = floorAt(regions, m.index);
|
|
2348
2387
|
rangeDecls++;
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
`${file}: declared pi
|
|
2388
|
+
if (want === piAi) sourceRangeDecls++;
|
|
2389
|
+
assert.equal(floor, want, want === piAi
|
|
2390
|
+
? `${file}: declared pi floor in "${decl}" is ${floor}, but the pinned SDK is ${piAi} — a baseline doc may not advertise a version no gate drives`
|
|
2391
|
+
: `${file}: declared pi floor in "${decl}" is ${floor}, but its published-scope region names pi ${want}`);
|
|
2392
|
+
assert.equal(`${ceilMajor}.${ceilMinor}`, nextMinor(want),
|
|
2393
|
+
`${file}: declared pi ceiling in "${decl}" must be the next minor (${nextMinor(want)})`);
|
|
2353
2394
|
}
|
|
2354
2395
|
// Exact install pins: `@earendil-works/pi-<pkg>@<version>`.
|
|
2355
|
-
for (const
|
|
2396
|
+
for (const m of text.matchAll(/@earendil-works\/pi-(?:ai|coding-agent|tui)@(\d+\.\d+\.\d+)/g)) {
|
|
2397
|
+
const [decl, ver] = m;
|
|
2398
|
+
const want = floorAt(regions, m.index);
|
|
2356
2399
|
exactDecls++;
|
|
2357
|
-
assert.equal(ver,
|
|
2400
|
+
assert.equal(ver, want, want === piAi
|
|
2401
|
+
? `${file}: install example "${decl}" pins ${ver}, but the pinned SDK is ${piAi}`
|
|
2402
|
+
: `${file}: install example "${decl}" pins ${ver}, but its published-scope region names pi ${want}`);
|
|
2358
2403
|
}
|
|
2359
2404
|
}
|
|
2360
2405
|
// Prose declarations carry the pin in sentences the two patterns above cannot
|
|
@@ -2366,9 +2411,11 @@ const PROSE_DECLS = [
|
|
|
2366
2411
|
['ROADMAP.md', /floor = \*\*(\d+\.\d+\.\d+)\*\*/, 'floor = **<version>**'],
|
|
2367
2412
|
];
|
|
2368
2413
|
for (const [file, re, shape] of PROSE_DECLS) {
|
|
2369
|
-
const
|
|
2414
|
+
const text = readFileSync(file, 'utf8');
|
|
2415
|
+
const m = text.match(re);
|
|
2370
2416
|
assert.ok(m, `${file}: the baseline sentence "${shape}" is gone — restore it or update check-dep-versions; a doc reword must not silently drop the pin from the gate`);
|
|
2371
|
-
|
|
2417
|
+
const want = floorAt(publishedRegions(text).regions, m.index);
|
|
2418
|
+
assert.equal(m[1], want, `${file}: "${shape}" declares ${m[1]}, but the ${want === piAi ? 'pinned SDK' : 'published-scope region'} is ${want}`);
|
|
2372
2419
|
}
|
|
2373
2420
|
// ── #120 P4: ONE FLOOR, FOUR MECHANICAL SURFACES ──────────────────────────
|
|
2374
2421
|
// The bump is mostly a number substitution, and that is exactly how a bump goes
|
|
@@ -2390,6 +2437,8 @@ const PI_CONSTELLATION = [
|
|
|
2390
2437
|
'@earendil-works/pi-codemode', '@earendil-works/pi-mcp',
|
|
2391
2438
|
// #130: the durable library the pi-durable carrier imports — a production dependency since 0.32.
|
|
2392
2439
|
'@earendil-works/pi-durable',
|
|
2440
|
+
// `[측정 2026-11-03]` 1.5.0 (#155): pi-schema joins — declared by chord, pi-agent-core, pi-ai and pi-durable.
|
|
2441
|
+
'@earendil-works/pi-schema',
|
|
2393
2442
|
];
|
|
2394
2443
|
const sameSet = (got, want, where) => {
|
|
2395
2444
|
const missing = want.filter((n) => !got.includes(n));
|
|
@@ -2404,7 +2453,7 @@ for (const [, name, ver] of runSh.matchAll(/"(@earendil-works\/[a-z-]+)@(\d+\.\d
|
|
|
2404
2453
|
if (ver !== piAi) floorGaps.push(`run.sh install constellation: ${name}@${ver} is not the floor ${piAi}`);
|
|
2405
2454
|
}
|
|
2406
2455
|
sameSet(installed, PI_CONSTELLATION, 'run.sh install constellation');
|
|
2407
|
-
// pnpm-workspace.yaml minimumReleaseAgeExclude: the same
|
|
2456
|
+
// pnpm-workspace.yaml minimumReleaseAgeExclude: the same constellation, each ADMITTING the
|
|
2408
2457
|
// current floor. History stays (`0.85.0 || 0.85.1 || …`) — only the newest term is
|
|
2409
2458
|
// the contract, because that is what an install resolves against.
|
|
2410
2459
|
const closure = [...readFileSync('pnpm-workspace.yaml', 'utf8').matchAll(/^\s*-\s*'(@earendil-works\/[a-z-]+)@([^']+)'/gm)];
|
|
@@ -2437,22 +2486,65 @@ const CURRENT_CONTRACT_DECLS = [
|
|
|
2437
2486
|
['docs/acp-backend-rail.md', /SDK exact `(\d+\.\d+\.\d+)`, host `(>=[\d.]+ <\d+\.\d+)`/, "the ACP support row's exact+range pair"],
|
|
2438
2487
|
];
|
|
2439
2488
|
for (const [file, re, shape] of CURRENT_CONTRACT_DECLS) {
|
|
2440
|
-
const
|
|
2489
|
+
const text = readFileSync(file, 'utf8');
|
|
2490
|
+
const m = text.match(re);
|
|
2441
2491
|
if (!m) { floorGaps.push(`${file}: ${shape} is gone — a reworded current-contract sentence must fail loud, not drop out silently`); continue; }
|
|
2492
|
+
// A sentence inside a published-scope region states the published package's contract; it is
|
|
2493
|
+
// held to that region's pi here and the region itself to its oracles below.
|
|
2494
|
+
const floor = floorAt(publishedRegions(text).regions, m.index);
|
|
2495
|
+
const range = `>=${floor} <${nextMinor(floor)}`;
|
|
2442
2496
|
for (const captured of m.slice(1)) {
|
|
2443
|
-
const expected = captured.startsWith('>=') ?
|
|
2444
|
-
if (captured !== expected) floorGaps.push(`${file}: ${shape} declares "${captured}", but the floor is ${
|
|
2497
|
+
const expected = captured.startsWith('>=') ? range : floor;
|
|
2498
|
+
if (captured !== expected) floorGaps.push(`${file}: ${shape} declares "${captured}", but the ${floor === piAi ? 'floor' : 'published-scope region'} is ${floor} (${range})`);
|
|
2445
2499
|
}
|
|
2446
2500
|
}
|
|
2447
2501
|
assert.deepEqual(floorGaps, [],
|
|
2448
2502
|
`[QK:PI-FLOOR-SINGLE-SOURCE] the pi floor is declared on four mechanical surfaces — the durable pin's SDK set with package.json's exact production dependencies, run.sh's install constellation, the pnpm workspace closure, and the current-contract sentences in the docs — and every one of them must name the SAME floor. A bump that moves three of the four is the ordinary way this breaks, and the surface left behind is the one nothing else reads`);
|
|
2449
2503
|
|
|
2504
|
+
// The published-scope regions' own oracle (see PUBLISHED-SCOPE REGIONS above). Checked after the
|
|
2505
|
+
// four surfaces so a stranded closure row is still attributed to the claim above.
|
|
2506
|
+
// PUBLISHED_ARTIFACT is the one published pair a region may name, copied from the artifact, not from
|
|
2507
|
+
// this checkout. Read 2026-11-11 from package/package.json in the preserved registry-accepted
|
|
2508
|
+
// 0.51.0 release TGZ (TGZ SHA256 04fcce26d0a6dec8b8bb9a7f14a843eee0defb313aedafa34080600853d36e4d;
|
|
2509
|
+
// raw manifest SHA256 b5043ba8edde6f2b9d13052a98c3cd808a3b30f1023da41b22ee022470a96120).
|
|
2510
|
+
// Its ten @earendil-works production dependencies are exact 1.5.0, independent of this checkout.
|
|
2511
|
+
const PUBLISHED_ARTIFACT = {
|
|
2512
|
+
version: '0.51.0',
|
|
2513
|
+
dependencies: {
|
|
2514
|
+
'@earendil-works/chord': '1.5.0', '@earendil-works/pi-agent-core': '1.5.0', '@earendil-works/pi-ai': '1.5.0',
|
|
2515
|
+
'@earendil-works/pi-codemode': '1.5.0', '@earendil-works/pi-coding-agent': '1.5.0', '@earendil-works/pi-durable': '1.5.0',
|
|
2516
|
+
'@earendil-works/pi-mcp': '1.5.0', '@earendil-works/pi-schema': '1.5.0', '@earendil-works/pi-telemetry': '1.5.0', '@earendil-works/pi-tui': '1.5.0',
|
|
2517
|
+
},
|
|
2518
|
+
};
|
|
2519
|
+
const publishedSdk = [...new Set(Object.values(PUBLISHED_ARTIFACT.dependencies))];
|
|
2520
|
+
const publishedGaps = [];
|
|
2521
|
+
if (publishedSdk.length !== 1) publishedGaps.push(`PUBLISHED_ARTIFACT ${PUBLISHED_ARTIFACT.version} carries no single SDK generation (${publishedSdk.join(', ')})`);
|
|
2522
|
+
let publishedRegionCount = 0;
|
|
2523
|
+
const PUBLISHED_REGION_MAX_LINES = 40;
|
|
2524
|
+
for (const file of [...new Set([...BASELINE_DOCS, ...CURRENT_CONTRACT_DECLS.map(([f]) => f)])]) {
|
|
2525
|
+
const { regions, problems } = publishedRegions(readFileSync(file, 'utf8'));
|
|
2526
|
+
for (const p of problems) publishedGaps.push(`${file}: ${p}`);
|
|
2527
|
+
for (const r of regions) {
|
|
2528
|
+
publishedRegionCount++;
|
|
2529
|
+
const where = `${file}: region "@junghanacs/entwurf@${r.version} pi ${r.sdk}"`;
|
|
2530
|
+
if (r.version !== PUBLISHED_ARTIFACT.version) publishedGaps.push(`${where} names a package no frozen published manifest records (only @junghanacs/entwurf@${PUBLISHED_ARTIFACT.version})`);
|
|
2531
|
+
else if (publishedSdk.length !== 1 || r.sdk !== publishedSdk[0]) publishedGaps.push(`${where} names pi ${r.sdk}, but the published ${r.version} manifest ships pi ${publishedSdk.join(' | ')}`);
|
|
2532
|
+
const decls = [...r.body.matchAll(/>=\s?\d+\.\d+\.\d+\s?<\s?\d+\.\d+|@earendil-works\/pi-(?:ai|coding-agent|tui)@\d+\.\d+\.\d+/g)].length;
|
|
2533
|
+
if (decls === 0) publishedGaps.push(`${where} holds no pi declaration — an empty region only hides prose from the scan`);
|
|
2534
|
+
const lines = r.body.split('\n').length;
|
|
2535
|
+
if (lines > PUBLISHED_REGION_MAX_LINES) publishedGaps.push(`${where} spans ${lines} lines (max ${PUBLISHED_REGION_MAX_LINES}) — mark the table, example or sentence, not the section around it`);
|
|
2536
|
+
}
|
|
2537
|
+
}
|
|
2538
|
+
if (publishedRegionCount > 0 && sourceRangeDecls < 2) publishedGaps.push(`only ${sourceRangeDecls} range declaration(s) remain outside published-scope regions — the checkout's own floor must still be declared where this gate reads it`);
|
|
2539
|
+
assert.deepEqual(publishedGaps, [],
|
|
2540
|
+
`[QK:PI-FLOOR-PUBLISHED-SCOPE] a published-scope region in a baseline doc names a published package and the SDK its frozen manifest ships (PUBLISHED_ARTIFACT, independent of this checkout's version and closure), holds at least one declaration, stays narrow, and leaves the checkout's floor declared outside it`);
|
|
2541
|
+
|
|
2450
2542
|
// Guard the guard: if the patterns ever stop matching, the loops above pass
|
|
2451
2543
|
// vacuously and the docs fall back OUT of the gate without a word.
|
|
2452
2544
|
assert.ok(rangeDecls >= 5, `expected at least 5 pi range declarations across the baseline docs, found ${rangeDecls} — the doc scan matched (almost) nothing and would pass vacuously`);
|
|
2453
2545
|
assert.ok(exactDecls >= 1, `expected at least 1 exact pi install pin in the baseline docs, found ${exactDecls}`);
|
|
2454
2546
|
|
|
2455
|
-
console.log(`[check-dep-versions] ok — pi ${piAi} is coherent across the durable pin's SDK set, package.json (exact production dependencies, no dev/peer duplicate), run.sh (install pins), and ${BASELINE_DOCS.length} baseline docs (${rangeDecls} range + ${exactDecls} exact + ${PROSE_DECLS.length} prose declarations)`);
|
|
2547
|
+
console.log(`[check-dep-versions] ok — pi ${piAi} is coherent across the durable pin's SDK set, package.json (exact production dependencies, no dev/peer duplicate), run.sh (install pins), and ${BASELINE_DOCS.length} baseline docs (${rangeDecls} range + ${exactDecls} exact + ${PROSE_DECLS.length} prose declarations; ${publishedRegionCount} published-scope region(s))`);
|
|
2456
2548
|
EOF
|
|
2457
2549
|
)
|
|
2458
2550
|
}
|
|
@@ -4040,7 +4132,7 @@ check_pack_win32_carrier() {
|
|
|
4040
4132
|
# which is exactly what property (1)'s `(_|$)` absorbs; all three shapes are fixtures
|
|
4041
4133
|
# below so a future suffix change cannot pass vacuously.)
|
|
4042
4134
|
pack_install_leaked_pi() {
|
|
4043
|
-
grep '^@earendil-works+' | grep -Ev '@1\.
|
|
4135
|
+
grep '^@earendil-works+' | grep -Ev '@1\.5\.2(_|$)' || true
|
|
4044
4136
|
}
|
|
4045
4137
|
|
|
4046
4138
|
# Matcher self-test on SYNTHETIC lookalikes: a healthy install tree cannot exercise either
|
|
@@ -4057,18 +4149,18 @@ check_pack_pin_matcher() {
|
|
|
4057
4149
|
# Cell 1 — the version boundary. Every measured peer-hash shape is a fixture: the
|
|
4058
4150
|
# 0.84.4-era `_@modelcontextprotocol+sdk@…`, the `_ws@8.21.3` observed on a real
|
|
4059
4151
|
# 0.85.1 tree, and `[측정 2026-09-20]` the combined suffix a 0.86.0 install carries.
|
|
4060
|
-
# None may leak; the two lookalikes must — a PREFIX-EXTENDED version (`1.
|
|
4152
|
+
# None may leak; the two lookalikes must — a PREFIX-EXTENDED version (`1.5.2-beta.1`,
|
|
4061
4153
|
# the prerelease shape that an unanchored match would bless) and an off-pin version.
|
|
4062
4154
|
matcher_probe=$(printf '%s\n' \
|
|
4063
|
-
'@earendil-works+pi-ai@1.
|
|
4064
|
-
'@earendil-works+pi-ai@1.
|
|
4065
|
-
'@earendil-works+pi-ai@1.
|
|
4066
|
-
'@earendil-works+pi-ai@1.
|
|
4067
|
-
'@earendil-works+pi-ai@1.
|
|
4155
|
+
'@earendil-works+pi-ai@1.5.2' \
|
|
4156
|
+
'@earendil-works+pi-ai@1.5.2_@modelcontextprotocol+sdk@1.29.0_zod@4.3.6' \
|
|
4157
|
+
'@earendil-works+pi-ai@1.5.2_ws@8.21.3' \
|
|
4158
|
+
'@earendil-works+pi-ai@1.5.2_@modelcontextprotocol+sdk@1.29.0_zod@4.3.6__ws@8.21.3_zod@4.3.6' \
|
|
4159
|
+
'@earendil-works+pi-ai@1.5.2-beta.1' \
|
|
4068
4160
|
'@earendil-works+pi-agent-core@0.87.1' | pack_install_leaked_pi)
|
|
4069
|
-
if [ "$matcher_probe" != '@earendil-works+pi-ai@1.
|
|
4161
|
+
if [ "$matcher_probe" != '@earendil-works+pi-ai@1.5.2-beta.1
|
|
4070
4162
|
@earendil-works+pi-agent-core@0.87.1' ]; then
|
|
4071
|
-
fail "[QK:PACK-INSTALL-PIN-MATCHER-BOUNDED] the pin-leak matcher must flag the prefix-extended 1.
|
|
4163
|
+
fail "[QK:PACK-INSTALL-PIN-MATCHER-BOUNDED] the pin-leak matcher must flag the prefix-extended 1.5.2-beta.1 and the off-pin 0.87.1 lookalikes, and pass 1.5.2 bare or with any measured peer-hash — got: ${matcher_probe:-<nothing leaked>}"
|
|
4072
4164
|
return 1
|
|
4073
4165
|
fi
|
|
4074
4166
|
|
|
@@ -4076,9 +4168,9 @@ check_pack_pin_matcher() {
|
|
|
4076
4168
|
# (0.85.0 onward, still true at 0.99.1). An off-pin chord MUST leak; the pinned one must not. A matcher narrowed
|
|
4077
4169
|
# back to `^@earendil-works+pi-` sees nothing here and dies at this signature.
|
|
4078
4170
|
matcher_probe=$(printf '%s\n' \
|
|
4079
|
-
'@earendil-works+chord@1.
|
|
4171
|
+
'@earendil-works+chord@1.5.2' \
|
|
4080
4172
|
'@earendil-works+chord@0.87.1' \
|
|
4081
|
-
'@earendil-works+pi-ai@1.
|
|
4173
|
+
'@earendil-works+pi-ai@1.5.2' | pack_install_leaked_pi)
|
|
4082
4174
|
if [ "$matcher_probe" != '@earendil-works+chord@0.87.1' ]; then
|
|
4083
4175
|
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 was 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; `[측정 2026-10-13]` at 1.2.0 pi-durable alone declares it) — got: ${matcher_probe:-<nothing leaked>}"
|
|
4084
4176
|
return 1
|
|
@@ -4291,7 +4383,9 @@ ORACLE
|
|
|
4291
4383
|
# SDK 1.4 pi-durable imports pi-ai's new `utils/identity`, absent from the older pi-ai control versions
|
|
4292
4384
|
# checked here (1.0.3 and 1.3.0 both fail to load); pi-telemetry 1.0.3/1.3.0 and chord 1.3.0 lack exports
|
|
4293
4385
|
# it imports. pi-codemode 1.3.0 loads, and depends on no SDK member, so this tree carries exactly one
|
|
4294
|
-
# depth-2 mismatch (L4 keeps depth 3).
|
|
4386
|
+
# depth-2 mismatch (L4 keeps depth 3). `[측정 2026-11-03]` SDK 1.5.0 pi-durable still loads beside
|
|
4387
|
+
# pi-codemode 1.3.0 (registry tarball, integrity-matched) and 1.4.0 (#155 import probe; its codemode
|
|
4388
|
+
# imports are the root, /portable, /source and /declarations entries), so the control version holds.
|
|
4295
4389
|
printf '%s\n' "{\"name\":\"pd-mismatch\",\"version\":\"1.0.0\",\"private\":true,\"overrides\":{\"@earendil-works/pi-durable\":{\"@earendil-works/pi-codemode\":\"1.3.0\"}}}" > "$box/pd-mismatch/package.json"
|
|
4296
4390
|
out=$(cd "$box/pd-mismatch" && npm install "$tgz" --ignore-scripts --no-audit --no-fund 2>&1) || {
|
|
4297
4391
|
fail "[check-pack-install] pi-durable L3 (mismatch control) install failed:"; echo "$out" | tail -10 | sed 's/^/ /' >&2; return 1; }
|
|
@@ -4421,7 +4515,13 @@ ORACLE
|
|
|
4421
4515
|
local pin_minor="${pin_ver#*.}"; pin_minor="${pin_minor%%.*}"
|
|
4422
4516
|
local pin_ceiling="${pin_ver%%.*}.$((pin_minor + 1))"
|
|
4423
4517
|
mkdir -p "$host_old"
|
|
4424
|
-
|
|
4518
|
+
# The old-host fixture installs the members its own generation had. `[측정 2026-11-03]` pi-schema's
|
|
4519
|
+
# registry versions are 0.0.0-stage, 0.0.0 and 1.5.0 (#155: born with the 1.5 SDK), so it has no
|
|
4520
|
+
# release at the fixed old version — the only member skipped here; the pinned/aligned cells keep all ten.
|
|
4521
|
+
for spec in "${host_specs[@]}"; do
|
|
4522
|
+
[ "${spec%@*}" = '@earendil-works/pi-schema' ] && continue
|
|
4523
|
+
host_old_specs+=("${spec%@*}@$host_old_ver")
|
|
4524
|
+
done
|
|
4425
4525
|
out=$(cd "$host_old" && npm install "${host_old_specs[@]}" --ignore-scripts --no-audit --no-fund 2>&1) || {
|
|
4426
4526
|
fail "[check-pack-install] could not install the previous-host pi fixture ($host_old_ver):"; echo "$out" | tail -10 | sed 's/^/ /' >&2; return 1; }
|
|
4427
4527
|
for cell in old aligned; do
|
|
@@ -4986,20 +5086,22 @@ _check_pack_install_impl() {
|
|
|
4986
5086
|
# oracle walked 10 member edges with 0 mismatches (.tmp-verify/145-stage1/08-pack-install.log).
|
|
4987
5087
|
# `[측정 2026-10-22]` at 1.4.0 the nine members are again exact 1.4.0 edges (registry manifests; #148 S0),
|
|
4988
5088
|
# and pi-tui 1.4.0 adds one third-party dependency (marked-cjk-friendly 0.1.2) outside the closure.
|
|
4989
|
-
|
|
5089
|
+
# `[측정 2026-11-03]` at 1.5.0 the set grows to ten: pi-schema 1.5.0 is new and declared by chord,
|
|
5090
|
+
# pi-agent-core, pi-ai and pi-durable; every member edge is exact 1.5.0 (registry manifests; #155).
|
|
5091
|
+
echo "[check-pack-install] pnpm add into $tmp (with the aligned 1.5.x SDK + chord + codemode + mcp + durable + schema)"
|
|
4990
5092
|
local install_log
|
|
4991
5093
|
install_log=$(cd "$tmp" && pnpm add \
|
|
4992
5094
|
"$tgz_path" \
|
|
4993
|
-
"@earendil-works/pi-ai@1.
|
|
4994
|
-
"@earendil-works/pi-coding-agent@1.
|
|
4995
|
-
"@earendil-works/pi-tui@1.
|
|
4996
|
-
"@earendil-works/pi-agent-core@1.
|
|
4997
|
-
"@earendil-works/pi-telemetry@1.
|
|
4998
|
-
"@earendil-works/chord@1.
|
|
4999
|
-
"@earendil-works/pi-codemode@1.
|
|
5000
|
-
"@earendil-works/pi-mcp@1.
|
|
5001
|
-
"@earendil-works/pi-durable@1.
|
|
5002
|
-
"
|
|
5095
|
+
"@earendil-works/pi-ai@1.5.2" \
|
|
5096
|
+
"@earendil-works/pi-coding-agent@1.5.2" \
|
|
5097
|
+
"@earendil-works/pi-tui@1.5.2" \
|
|
5098
|
+
"@earendil-works/pi-agent-core@1.5.2" \
|
|
5099
|
+
"@earendil-works/pi-telemetry@1.5.2" \
|
|
5100
|
+
"@earendil-works/chord@1.5.2" \
|
|
5101
|
+
"@earendil-works/pi-codemode@1.5.2" \
|
|
5102
|
+
"@earendil-works/pi-mcp@1.5.2" \
|
|
5103
|
+
"@earendil-works/pi-durable@1.5.2" \
|
|
5104
|
+
"@earendil-works/pi-schema@1.5.2" \
|
|
5003
5105
|
--ignore-workspace --ignore-scripts 2>&1) || {
|
|
5004
5106
|
fail "[check-pack-install] pnpm add failed:"
|
|
5005
5107
|
echo "$install_log" | tail -10 | sed 's/^/ /' >&2
|
|
@@ -5008,17 +5110,17 @@ _check_pack_install_impl() {
|
|
|
5008
5110
|
|
|
5009
5111
|
# A pin is a wish until the resolved tree is read back. Assert it: EVERY
|
|
5010
5112
|
# @earendil-works package present — direct or transitive, top level or nested,
|
|
5011
|
-
# chord included — must be the pinned
|
|
5113
|
+
# chord included — must be the pinned 1.5.2. Anything else means an unpinned caret
|
|
5012
5114
|
# floated and the rest of this gate would be exercising a runtime nobody verified,
|
|
5013
|
-
# while still printing
|
|
5115
|
+
# while still printing a pinned runtime. Fail loud instead of proving the wrong floor.
|
|
5014
5116
|
local leaked_pi
|
|
5015
5117
|
leaked_pi=$(ls "$tmp/node_modules/.pnpm" 2>/dev/null | pack_install_leaked_pi)
|
|
5016
5118
|
if [ -n "$leaked_pi" ]; then
|
|
5017
|
-
fail "[check-pack-install] UNVERIFIED pi runtime resolved into the install tree (expected only 1.
|
|
5119
|
+
fail "[check-pack-install] UNVERIFIED pi runtime resolved into the install tree (expected only 1.5.2):"
|
|
5018
5120
|
printf '%s\n' "$leaked_pi" | sed 's/^/ /' >&2
|
|
5019
5121
|
return 1
|
|
5020
5122
|
fi
|
|
5021
|
-
echo "[check-pack-install] pi runtime tree pin verified: every @earendil-works package is 1.
|
|
5123
|
+
echo "[check-pack-install] pi runtime tree pin verified: every @earendil-works package is 1.5.2 (chord, pi-durable and pi-schema included)"
|
|
5022
5124
|
|
|
5023
5125
|
# Resolve the installed package.json and confirm pi.extensions
|
|
5024
5126
|
# arrived intact. If pi.extensions is empty or missing, the
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
* - R5: taxonomy covers the table reasons + pre-claims bad-target /
|
|
26
26
|
* target-locked (so bucket B F2 won't reopen it), and
|
|
27
27
|
* is PRE-DISPATCH only (no send-fail-fallback member).
|
|
28
|
-
* - schema ↔ types drift guard: the
|
|
28
|
+
* - schema ↔ types drift guard: the pi-schema input/receipt property keysets and
|
|
29
29
|
* the StringEnum members match the hand-written contract constants.
|
|
30
30
|
*/
|
|
31
31
|
|
|
@@ -82,7 +82,7 @@ function eq(label: string, actual: unknown, expected: unknown): void {
|
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
// Pull the literal members out of a StringEnum / Optional(StringEnum) schema,
|
|
85
|
-
// whichever shape
|
|
85
|
+
// whichever shape the schema builder emits — so the schema↔const drift guard is real.
|
|
86
86
|
function enumValues(schema: unknown): string[] {
|
|
87
87
|
const s = schema as { enum?: unknown[]; anyOf?: { const?: unknown }[] };
|
|
88
88
|
if (Array.isArray(s?.enum)) return s.enum.map(String);
|
|
@@ -626,8 +626,8 @@ ok(
|
|
|
626
626
|
!("action" in rej) && !("transport" in rej) && !("ownership" in rej),
|
|
627
627
|
);
|
|
628
628
|
eq("receipt.reject.reason enum === ENTWURF_V2_REJECT_REASONS", enumValues(rej.reason), [...ENTWURF_V2_REJECT_REASONS]);
|
|
629
|
-
// ?6: reject.observedLiveness is a required-nullable union —
|
|
630
|
-
// (FACT_LIVENESSES),
|
|
629
|
+
// ?6: reject.observedLiveness is a required-nullable union — union([StringEnum
|
|
630
|
+
// (FACT_LIVENESSES), nul()]) — NOT Optional and NOT a bare StringEnum. The
|
|
631
631
|
// key is always present (required), the value may be a 4-value liveness OR null.
|
|
632
632
|
const rejLiveness = rej.observedLiveness as { anyOf?: { type?: string }[] };
|
|
633
633
|
eq("?6: receipt.reject.observedLiveness is a 2-branch union (liveness | null)", rejLiveness.anyOf?.length, 2);
|
|
@@ -92,7 +92,7 @@ function assertRailSemantics(tag: string, longDescRaw: string, intentDescRaw: st
|
|
|
92
92
|
);
|
|
93
93
|
ok(
|
|
94
94
|
`${tag} — long description names the THIRD result for an unsupported citizen`,
|
|
95
|
-
/mailbox-undeliverable/.test(longDesc) && /THIRD RESULT/.test(longDesc),
|
|
95
|
+
/mailbox-undeliverable/.test(longDesc) && /target without a live socket, the THIRD RESULT/.test(longDesc),
|
|
96
96
|
);
|
|
97
97
|
ok(
|
|
98
98
|
`${tag} — intent param names a rail's reject, not only its allow cell`,
|
|
@@ -139,30 +139,16 @@ function assertRailSemantics(tag: string, longDescRaw: string, intentDescRaw: st
|
|
|
139
139
|
* whose subject (two distinct OWNED rejects) left with the `owned-outcome` intent.
|
|
140
140
|
*
|
|
141
141
|
* After the visible-first cut a dormant socket-domain citizen rejects delivery by v2, and
|
|
142
|
-
*
|
|
143
|
-
*
|
|
144
|
-
*
|
|
145
|
-
*
|
|
146
|
-
* an operator who read last week's docs with no way to find out what happened;
|
|
147
|
-
* - say it was `withdrawn`, ADJACENTLY to that name. Presence alone is not enough: naming a
|
|
148
|
-
* retired intent without the retirement verb beside it reads as an offer, which is exactly
|
|
149
|
-
* the "advertised as selectable" failure this claim exists to forbid;
|
|
150
|
-
* - carry NEITHER retired advertisement form (`owned-outcome = …` enum styling, or the old
|
|
151
|
-
* "DORMANT socket-domain citizen … never auto-converted" sentence that described picking it).
|
|
152
|
-
*
|
|
153
|
-
* ORDER IS LOAD-BEARING, and not for readability. `ok` throws on the FIRST failure, and the kill
|
|
154
|
-
* signature is read off the failing line — so the `[QK:…]` label must sit on the assertion this
|
|
155
|
-
* claim's mutant reaches FIRST. That mutant drops the reason AND the withdrawal in one edit; with
|
|
156
|
-
* the reason check leading, an UNLABELLED line would throw first and the kill would read as
|
|
157
|
-
* WRONG-REASON. The adjacency check therefore goes first and carries the only token; the other two
|
|
158
|
-
* run unlabelled behind it — still real assertions, just not this claim's signature.
|
|
142
|
+
* both caller-facing strings must independently name the actual dormant rejection and say that
|
|
143
|
+
* delivery never launches or resumes a process. Neither may advertise the retired offer. History
|
|
144
|
+
* belongs in the issue and literal amendment, not in the current caller contract. The labelled
|
|
145
|
+
* assertion comes first because qualification reads the first failing line as the kill signature.
|
|
159
146
|
*/
|
|
160
147
|
function assertDormantHonesty(tag: string, rawText: string): void {
|
|
161
148
|
const text = rawText;
|
|
162
|
-
const at = text.indexOf("owned-outcome");
|
|
163
149
|
ok(
|
|
164
|
-
`${tag} —
|
|
165
|
-
|
|
150
|
+
`${tag} — delivery never launches or resumes a process and names the dormant rejection [QK:V2SURF-DORMANT-HONESTY]`,
|
|
151
|
+
/This delivery verb never launches or resumes a process/.test(text) && /dormant-fire-forget-unsupported/.test(text),
|
|
166
152
|
);
|
|
167
153
|
ok(`${tag} — names the reason a dormant target actually returns`, /dormant-fire-forget-unsupported/.test(text));
|
|
168
154
|
ok(
|
|
@@ -3460,7 +3460,7 @@ let manifestCount: number;
|
|
|
3460
3460
|
"pi-retire-request": 6,
|
|
3461
3461
|
"retirement-event": 16,
|
|
3462
3462
|
"control-retire": 9,
|
|
3463
|
-
"control-send-receipt":
|
|
3463
|
+
"control-send-receipt": 23,
|
|
3464
3464
|
"codex-native": 95,
|
|
3465
3465
|
"native-push-paused": 1,
|
|
3466
3466
|
"chain-terminus-publication": 1,
|
|
@@ -3473,11 +3473,11 @@ let manifestCount: number;
|
|
|
3473
3473
|
"copilot-birth": 19,
|
|
3474
3474
|
"copilot-launch": 14,
|
|
3475
3475
|
"pi-launch": 9,
|
|
3476
|
-
"pi-floor":
|
|
3476
|
+
"pi-floor": 4,
|
|
3477
3477
|
"pi-bridge-sender": 4,
|
|
3478
3478
|
"pi-mcp-bridge": 2,
|
|
3479
3479
|
"pi-mcp-register": 3,
|
|
3480
|
-
"pi-durable":
|
|
3480
|
+
"pi-durable": 186,
|
|
3481
3481
|
"copilot-receive": 20,
|
|
3482
3482
|
"entwurf-peers": 1,
|
|
3483
3483
|
"fresh-call-dispatch": 18,
|
|
@@ -900,7 +900,10 @@ async function cell(): Promise<void> {
|
|
|
900
900
|
hostReceiver.ownerKind === "pi-durable-host";
|
|
901
901
|
ok(
|
|
902
902
|
`[QK:${QK}-SENDER-REPLYABILITY] the host's receiver marker is armed and owned by the spawned host, and the landed body says what that predicts: replyable (armed ${hostArmed})`,
|
|
903
|
-
hostArmed &&
|
|
903
|
+
hostArmed &&
|
|
904
|
+
body.includes(
|
|
905
|
+
`session: ${hostGid} (meta-session, replyable — when a reply is requested, send the result via entwurf_v2`,
|
|
906
|
+
),
|
|
904
907
|
);
|
|
905
908
|
const hostRecords = fs
|
|
906
909
|
.readdirSync(dirs.sessions)
|
|
@@ -452,14 +452,20 @@ export function codexSourceToolReceipts(thread: unknown): SourceToolReceipt[] {
|
|
|
452
452
|
}
|
|
453
453
|
|
|
454
454
|
function exactSenderInfoSuffix(text: string, nonce: string): string | null {
|
|
455
|
-
|
|
455
|
+
// Independent expected frame: never import the producer's guidance as the receipt oracle.
|
|
456
|
+
const guidance =
|
|
457
|
+
"\n\n[entwurf: When a reply is requested, send the result via entwurf_v2 to the requested recipient (default: sender_info.sessionId); a local final is not delivery. Honor routing and no-send instructions; an absent or false wants_reply does not cancel a request. A replyable address alone needs no acknowledgment.]";
|
|
458
|
+
const open = "<sender_info>";
|
|
456
459
|
const suffix = "</sender_info>";
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
+
const at = text.lastIndexOf(open);
|
|
461
|
+
if (at < 0 || !text.endsWith(suffix)) return null;
|
|
462
|
+
const json = text.slice(at + open.length, -suffix.length);
|
|
463
|
+
if (json.includes("\n") || json.includes(open)) return null;
|
|
460
464
|
try {
|
|
461
|
-
const sender = JSON.parse(json) as { sessionId?: unknown };
|
|
462
|
-
|
|
465
|
+
const sender = JSON.parse(json) as { sessionId?: unknown; replyable?: unknown };
|
|
466
|
+
const prefix = `${nonce}${sender.replyable === true ? guidance : ""}\n\n${open}`;
|
|
467
|
+
if (!text.startsWith(prefix) || !text.endsWith(suffix)) return null;
|
|
468
|
+
return at === prefix.length - open.length && typeof sender.sessionId === "string" ? sender.sessionId : null;
|
|
463
469
|
} catch {
|
|
464
470
|
return null;
|
|
465
471
|
}
|
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
"claim": "GUIDANCE-REVIEWED-REVISION",
|
|
7
7
|
"title": "a valid Root revision advances beyond the independently reviewed payload revision while every history entry and decoded tool string remains valid",
|
|
8
8
|
"subject": "ENTWURF.json",
|
|
9
|
-
"find": ["\t\"revision\":
|
|
10
|
-
"replace": ["\t\"revision\":
|
|
9
|
+
"find": ["\t\"revision\": 20,", "\t\"history\": ["],
|
|
10
|
+
"replace": ["\t\"revision\": 21,", "\t\"history\": ["],
|
|
11
11
|
"gate": ["bash", "run.sh", "check-agent-guidance-beside"],
|
|
12
12
|
"timeoutSeconds": 120,
|
|
13
13
|
"signature": "[QK:GUIDANCE-REVIEWED-REVISION]",
|
|
@@ -715,9 +715,9 @@
|
|
|
715
715
|
"claim": "CODEX-LIVE-RAW-CALLBACK-REQUIRED",
|
|
716
716
|
"title": "a Pi callback with arbitrary bytes after its sender envelope is accepted as exact",
|
|
717
717
|
"subject": "scripts/lib/codex-fresh-source-receipts.ts",
|
|
718
|
-
"find": ["\
|
|
718
|
+
"find": ["\tconst suffix = \"</sender_info>\";"],
|
|
719
719
|
"replace": [
|
|
720
|
-
"\
|
|
720
|
+
"\tconst suffix = \"</sender_info>\";\n\tconst suffixAt = text.indexOf(suffix);\n\tif (suffixAt >= 0) text = text.slice(0, suffixAt + suffix.length); // mutant: discard callback suffix"
|
|
721
721
|
],
|
|
722
722
|
"gate": ["bash", "run.sh", "check-mux-fresh-call"],
|
|
723
723
|
"timeoutSeconds": 180,
|
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
"gate": ["bash", "run.sh", "check-control-send-receipt"],
|
|
12
12
|
"timeoutSeconds": 600,
|
|
13
13
|
"signature": "[QK:SEND-RECEIPT-IDLE-IS-SENT]",
|
|
14
|
-
"signatureSource": "pi-extensions/lib/control-send-receipt.test.ts"
|
|
14
|
+
"signatureSource": "pi-extensions/lib/control-send-receipt.test.ts",
|
|
15
|
+
"build": { "argv": ["bash", "scripts/build-bridge.sh"], "output": "mcp/entwurf-bridge/dist" }
|
|
15
16
|
},
|
|
16
17
|
{
|
|
17
18
|
"claim": "SEND-RECEIPT-BUSY-STEER-IS-QUEUED",
|
|
@@ -22,7 +23,8 @@
|
|
|
22
23
|
"gate": ["bash", "run.sh", "check-control-send-receipt"],
|
|
23
24
|
"timeoutSeconds": 600,
|
|
24
25
|
"signature": "[QK:SEND-RECEIPT-BUSY-STEER-IS-QUEUED]",
|
|
25
|
-
"signatureSource": "pi-extensions/lib/control-send-receipt.test.ts"
|
|
26
|
+
"signatureSource": "pi-extensions/lib/control-send-receipt.test.ts",
|
|
27
|
+
"build": { "argv": ["bash", "scripts/build-bridge.sh"], "output": "mcp/entwurf-bridge/dist" }
|
|
26
28
|
},
|
|
27
29
|
{
|
|
28
30
|
"claim": "SEND-RECEIPT-BUSY-FOLLOWUP-IS-QUEUED",
|
|
@@ -33,7 +35,8 @@
|
|
|
33
35
|
"gate": ["bash", "run.sh", "check-control-send-receipt"],
|
|
34
36
|
"timeoutSeconds": 600,
|
|
35
37
|
"signature": "[QK:SEND-RECEIPT-BUSY-FOLLOWUP-IS-QUEUED]",
|
|
36
|
-
"signatureSource": "pi-extensions/lib/control-send-receipt.test.ts"
|
|
38
|
+
"signatureSource": "pi-extensions/lib/control-send-receipt.test.ts",
|
|
39
|
+
"build": { "argv": ["bash", "scripts/build-bridge.sh"], "output": "mcp/entwurf-bridge/dist" }
|
|
37
40
|
},
|
|
38
41
|
{
|
|
39
42
|
"claim": "SEND-RECEIPT-UNKNOWN-IS-ACCEPTED-NOT-SENT",
|
|
@@ -44,7 +47,8 @@
|
|
|
44
47
|
"gate": ["bash", "run.sh", "check-control-send-receipt"],
|
|
45
48
|
"timeoutSeconds": 600,
|
|
46
49
|
"signature": "[QK:SEND-RECEIPT-UNKNOWN-IS-ACCEPTED-NOT-SENT]",
|
|
47
|
-
"signatureSource": "pi-extensions/lib/control-send-receipt.test.ts"
|
|
50
|
+
"signatureSource": "pi-extensions/lib/control-send-receipt.test.ts",
|
|
51
|
+
"build": { "argv": ["bash", "scripts/build-bridge.sh"], "output": "mcp/entwurf-bridge/dist" }
|
|
48
52
|
},
|
|
49
53
|
{
|
|
50
54
|
"claim": "SEND-BOUNDARY-CARRIED",
|
|
@@ -194,7 +198,7 @@
|
|
|
194
198
|
"claim": "PI-CORE-PIN-TRACKS-FLOOR",
|
|
195
199
|
"title": "the queue oracle's pi-agent-core pin drifts off the floor the rest of the pi runtime is pinned to, so the oracle keeps answering honestly — about a pi nobody in this repo runs",
|
|
196
200
|
"subject": "package.json",
|
|
197
|
-
"find": ["\"@earendil-works/pi-agent-core\": \"1.
|
|
201
|
+
"find": ["\"@earendil-works/pi-agent-core\": \"1.5.2\","],
|
|
198
202
|
"replace": ["\"@earendil-works/pi-agent-core\": \"0.87.1\","],
|
|
199
203
|
"gate": ["bash", "run.sh", "check-dep-versions"],
|
|
200
204
|
"timeoutSeconds": 300,
|
|
@@ -240,15 +244,51 @@
|
|
|
240
244
|
"title": "the caller-facing outcome list drops the native-push rail, so a caller reading the one blurb it reads before dispatching is told the control rail's vocabulary covers a rail that answers with a direct injection instead",
|
|
241
245
|
"subject": "ENTWURF.json",
|
|
242
246
|
"find": [
|
|
243
|
-
"native-push INJECTION, a rejection, or lock-dirty.
|
|
247
|
+
"native-push INJECTION, a rejection, or lock-dirty. A receipt is not a reply or proof of reading. A local final is not delivery:"
|
|
244
248
|
],
|
|
245
249
|
"replace": [
|
|
246
|
-
"a rejection, or lock-dirty.
|
|
250
|
+
"a rejection, or lock-dirty. A receipt is not a reply or proof of reading. A local final is not delivery:"
|
|
247
251
|
],
|
|
248
252
|
"gate": ["bash", "run.sh", "check-entwurf-v2-surface"],
|
|
249
253
|
"timeoutSeconds": 300,
|
|
250
254
|
"signature": "[QK:SEND-OUTCOME-LIST-NAMES-EVERY-RAIL]",
|
|
251
255
|
"signatureSource": "scripts/check-entwurf-v2-surface.ts"
|
|
256
|
+
},
|
|
257
|
+
{
|
|
258
|
+
"claim": "PI-RECEIVED-REPLY-GUIDANCE",
|
|
259
|
+
"title": "the Pi receiver omits guidance for a replyable sender, so a local final can be mistaken for a reply",
|
|
260
|
+
"subject": "pi-extensions/lib/entwurf-control-rpc.ts",
|
|
261
|
+
"find": ["sender.replyable === true"],
|
|
262
|
+
"replace": ["sender.replyable === false"],
|
|
263
|
+
"gate": ["bash", "run.sh", "check-control-send-receipt"],
|
|
264
|
+
"timeoutSeconds": 300,
|
|
265
|
+
"signature": "[QK:PI-RECEIVED-REPLY-GUIDANCE]",
|
|
266
|
+
"signatureSource": "pi-extensions/entwurf-control.test.ts",
|
|
267
|
+
"build": { "argv": ["bash", "scripts/build-bridge.sh"], "output": "mcp/entwurf-bridge/dist" }
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
"claim": "MAILBOX-RECEIVED-REPLY-GUIDANCE",
|
|
271
|
+
"title": "the mailbox receiver's guidance drops the local-final boundary",
|
|
272
|
+
"subject": "pi-extensions/lib/meta-mailbox-body.ts",
|
|
273
|
+
"find": ["a local final is not delivery."],
|
|
274
|
+
"replace": ["a local final is delivery."],
|
|
275
|
+
"gate": ["bash", "run.sh", "check-control-send-receipt"],
|
|
276
|
+
"timeoutSeconds": 300,
|
|
277
|
+
"signature": "[QK:MAILBOX-RECEIVED-REPLY-GUIDANCE]",
|
|
278
|
+
"signatureSource": "pi-extensions/lib/entwurf-v2-mailbox.test.ts",
|
|
279
|
+
"build": { "argv": ["bash", "scripts/build-bridge.sh"], "output": "mcp/entwurf-bridge/dist" }
|
|
280
|
+
},
|
|
281
|
+
{
|
|
282
|
+
"claim": "NATIVE-PUSH-RECEIVED-REPLY-GUIDANCE",
|
|
283
|
+
"title": "the native-push receiver's guidance drops the no-send boundary",
|
|
284
|
+
"subject": "pi-extensions/lib/meta-mailbox-body.ts",
|
|
285
|
+
"find": ["Honor routing and no-send instructions;"],
|
|
286
|
+
"replace": ["Ignore routing and no-send instructions;"],
|
|
287
|
+
"gate": ["bash", "run.sh", "check-control-send-receipt"],
|
|
288
|
+
"timeoutSeconds": 300,
|
|
289
|
+
"signature": "[QK:NATIVE-PUSH-RECEIVED-REPLY-GUIDANCE]",
|
|
290
|
+
"signatureSource": "pi-extensions/lib/entwurf-v2-native-push.test.ts",
|
|
291
|
+
"build": { "argv": ["bash", "scripts/build-bridge.sh"], "output": "mcp/entwurf-bridge/dist" }
|
|
252
292
|
}
|
|
253
293
|
]
|
|
254
294
|
}
|