@junghanacs/entwurf 0.50.0 → 0.51.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 +80 -0
- package/README.md +12 -4
- 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 +65 -18
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-contract.js +4 -4
- package/package.json +11 -18
- package/pi/pi-durable/carrier/runtime.js +221 -50
- package/pi/pi-durable/carrier/subagent.js +5 -2
- package/pi/pi-durable/carrier/tui.js +60 -22
- package/pi/pi-durable/overlay/upstream-pin.json +17 -16
- package/pi-extensions/lib/entwurf-v2-contract-schema.ts +73 -69
- package/pi-extensions/lib/entwurf-v2-contract.ts +4 -4
- package/pi-extensions/meta-bridge-pi-durable.ts +1 -1
- package/run.sh +141 -42
- package/scripts/check-entwurf-v2-contract.ts +4 -4
- package/scripts/check-gate-qualification.ts +2 -2
- package/scripts/mutants/control-send-receipt.json +1 -1
- package/scripts/mutants/pack-install.json +4 -4
- package/scripts/mutants/pi-durable.json +189 -3
- package/scripts/mutants/pi-floor.json +22 -5
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* entwurf-v2-contract-schema — the pi-side
|
|
3
|
-
* `entwurf_v2` contract. This module exists ONLY to carry the pi
|
|
4
|
-
*
|
|
2
|
+
* entwurf-v2-contract-schema — the pi-side JSON Schema REPRESENTATION of the frozen
|
|
3
|
+
* `entwurf_v2` contract. This module exists ONLY to carry the pi schema builders
|
|
4
|
+
* (pi-ai's `StringEnum`, pi-schema's `obj`/`str`/…) out of the pi-free core
|
|
5
5
|
* (`entwurf-v2-contract.ts`); the constants/types/decision logic live there.
|
|
6
6
|
*
|
|
7
7
|
* ⚠️ pi LANE ONLY — the MCP bridge (`mcp/entwurf-bridge/src/index.ts`) MUST NOT
|
|
@@ -11,18 +11,21 @@
|
|
|
11
11
|
* the contract CONSTANTS + `resolveDispatch` (pi-free core), never these schemas.
|
|
12
12
|
* Consumer: `check-entwurf-v2-contract` ONLY. This is a gate-side REPRESENTATION of the
|
|
13
13
|
* frozen contract, not a shipped tool schema — measured 2026-07-27, nothing else in the
|
|
14
|
-
* import graph reaches it. The
|
|
15
|
-
* `
|
|
16
|
-
*
|
|
17
|
-
*
|
|
14
|
+
* import graph reaches it. The surface a model actually reads builds its own:
|
|
15
|
+
* `mcp/entwurf-bridge/src/index.ts` (Zod, descriptions from ENTWURF.json), which a pi citizen
|
|
16
|
+
* reaches through Pi's built-in MCP since the native pi tools were retired (2f7c883, Pi 0.99).
|
|
17
|
+
* Change a model-facing description THERE; changing it here reaches no model.
|
|
18
18
|
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* `
|
|
19
|
+
* Pi SDK 1.5 (#155) removed pi-ai's TypeBox re-export (`Type`); its builders are now
|
|
20
|
+
* `@earendil-works/pi-schema`, which emits plain JSON Schema (obj() closed by default,
|
|
21
|
+
* opt() leaves a property out of `required`, lit() is `{const}`), and pi-ai's
|
|
22
|
+
* `StringEnum` still emits `{type:"string", enum}`. The logic types in the core are
|
|
23
|
+
* hand-written unions, NOT `Static<>` inferences; `check-entwurf-v2-contract` keeps
|
|
24
|
+
* these schemas and the core types in lockstep.
|
|
23
25
|
*/
|
|
24
26
|
|
|
25
|
-
import { StringEnum
|
|
27
|
+
import { StringEnum } from "@earendil-works/pi-ai";
|
|
28
|
+
import { bool, lit, nul, obj, opt, type Properties, type Schema, str, union } from "@earendil-works/pi-schema";
|
|
26
29
|
import {
|
|
27
30
|
ENTWURF_INTENTS,
|
|
28
31
|
ENTWURF_V2_ACTIONS,
|
|
@@ -34,36 +37,43 @@ import {
|
|
|
34
37
|
} from "./entwurf-v2-contract.ts";
|
|
35
38
|
import { SESSION_ID_RE } from "./session-id.js";
|
|
36
39
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
40
|
+
/**
|
|
41
|
+
* The face the drift guard reads. pi-schema types a schema as `{[keyword]: unknown}`; obj() always emits
|
|
42
|
+
* `properties`, so this names that one keyword without asserting anything else about the shape.
|
|
43
|
+
*/
|
|
44
|
+
type ObjectSchema = Schema & { readonly properties: Readonly<Record<string, Schema>> };
|
|
45
|
+
|
|
46
|
+
/** obj() — closed, so it emits `additionalProperties: false` — under the face above. */
|
|
47
|
+
const closedObject = (properties: Properties): ObjectSchema => obj(properties) as ObjectSchema;
|
|
48
|
+
|
|
49
|
+
export const EntwurfV2InputSchema = closedObject({
|
|
50
|
+
// R2/F6 executable: the garden-id shape is enforced by pattern, not prose —
|
|
51
|
+
// a malformed/typo gid fails the schema (→ bad-target) and can never reach a
|
|
52
|
+
// spawn. SSOT regex = SESSION_ID_RE (pi-extensions/lib/session-id.js).
|
|
53
|
+
target: str({
|
|
54
|
+
pattern: SESSION_ID_RE.source,
|
|
55
|
+
description:
|
|
56
|
+
"garden-id of an EXISTING citizen (pattern-enforced). Fresh sibling creation is out of scope on every current surface — there is no fallback verb that still spawns. A malformed/typo gid is bad-target, never a new spawn.",
|
|
57
|
+
}),
|
|
58
|
+
intent: StringEnum(ENTWURF_INTENTS, {
|
|
59
|
+
description:
|
|
60
|
+
"caller's declared outcome contract (F1). ONE member since the visible-first cut: fire-and-forget = ack only. The second, owned-outcome (caller owns completion), resumed a dormant citizen by launching a hidden background child and was withdrawn — a dormant citizen is currently unreachable by this verb.",
|
|
61
|
+
}),
|
|
62
|
+
mode: opt(
|
|
63
|
+
StringEnum(ENTWURF_V2_MODES, {
|
|
44
64
|
description:
|
|
45
|
-
"
|
|
65
|
+
"delivery mode — NOT the ownership axis (F1) nor liveness routing. steer (ask the receiver's steering queue) or follow_up (ask its follow-up queue). NEITHER is an interrupt: a busy receiver drains steering after each turn and follow-ups only when its inner loop ends, so a later steer overtakes every earlier follow_up and there is no order between the two. Both queues are volatile process memory — an abort drops what is in them. The receipt names which queue accepted the message (`queued-steer` / `queued-follow-up`); an idle receiver answers `sent`, meaning the turn was TRIGGERED, not that the model has seen the text. MEANINGLESS on the meta-mailbox transport (F-mailbox): a mailbox ack is enqueue+doorbell, not a turn injection, so steer/follow_up does not apply when the verdict transport is meta-mailbox.",
|
|
46
66
|
}),
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
67
|
+
),
|
|
68
|
+
wantsReply: opt(
|
|
69
|
+
bool({
|
|
70
|
+
description: "conversation etiquette only — NOT ownership; never triggers an auto-send (Q2).",
|
|
50
71
|
}),
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
),
|
|
57
|
-
wantsReply: Type.Optional(
|
|
58
|
-
Type.Boolean({
|
|
59
|
-
description: "conversation etiquette only — NOT ownership; never triggers an auto-send (Q2).",
|
|
60
|
-
}),
|
|
61
|
-
),
|
|
62
|
-
// `additionalProperties: false` — a frozen contract input is exact; an unknown
|
|
63
|
-
// key is a caller error, not silently ignored.
|
|
64
|
-
},
|
|
65
|
-
{ additionalProperties: false },
|
|
66
|
-
);
|
|
72
|
+
),
|
|
73
|
+
// `additionalProperties: false` — a frozen contract input is exact; an unknown
|
|
74
|
+
// key is a caller error, not silently ignored. obj() is closed by default (it
|
|
75
|
+
// emits `additionalProperties: false` unless `open: true`).
|
|
76
|
+
});
|
|
67
77
|
|
|
68
78
|
// Receipt = a DISCRIMINATED union on `ok` (R3/F6) — NOT one flat object with
|
|
69
79
|
// optionals. Each branch is EXACT (`additionalProperties: false`): without it,
|
|
@@ -72,35 +82,29 @@ export const EntwurfV2InputSchema = Type.Object(
|
|
|
72
82
|
// With it, success carries action/transport/ownership and rejects a stray reason;
|
|
73
83
|
// reject carries reason and rejects any allow facet — the branches are mutually
|
|
74
84
|
// exclusive at the schema level, not merely by declared-property convention.
|
|
75
|
-
export const EntwurfV2ReceiptSuccessSchema =
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
},
|
|
85
|
-
{ additionalProperties: false },
|
|
86
|
-
);
|
|
85
|
+
export const EntwurfV2ReceiptSuccessSchema = closedObject({
|
|
86
|
+
ok: lit(true),
|
|
87
|
+
action: StringEnum(ENTWURF_V2_ACTIONS),
|
|
88
|
+
transport: StringEnum(ENTWURF_V2_TRANSPORTS),
|
|
89
|
+
ownership: StringEnum(ENTWURF_V2_OWNERSHIPS),
|
|
90
|
+
observedLiveness: StringEnum(FACT_LIVENESSES, {
|
|
91
|
+
description: "the 4-value fact liveness the verdict was computed from (R1/R3).",
|
|
92
|
+
}),
|
|
93
|
+
});
|
|
87
94
|
|
|
88
|
-
export const EntwurfV2ReceiptRejectSchema =
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
},
|
|
103
|
-
{ additionalProperties: false },
|
|
104
|
-
);
|
|
95
|
+
export const EntwurfV2ReceiptRejectSchema = closedObject({
|
|
96
|
+
ok: lit(false),
|
|
97
|
+
reason: StringEnum(ENTWURF_V2_REJECT_REASONS),
|
|
98
|
+
// ?6: required-nullable, NOT optional — a reject branch ALWAYS carries the
|
|
99
|
+
// key, and it is `null` for the pre-probe rejects (PRE_PROBE_REJECT_REASONS)
|
|
100
|
+
// and a real FactLiveness otherwise. Optional would lose the "key always
|
|
101
|
+
// present, value may be null" shape and weaken the discriminated union; the
|
|
102
|
+
// reason-dependent null/non-null rule is enforced semantically (the gate's
|
|
103
|
+
// rejectObservedLivenessWellFormed fixture), not by this blanket union.
|
|
104
|
+
observedLiveness: union([StringEnum(FACT_LIVENESSES), nul()], {
|
|
105
|
+
description:
|
|
106
|
+
"the 4-value fact liveness the reject was computed from (R1/R3); null for the pre-probe rejects (bad-target / target-locked / target-address-conflict) where no probe ran.",
|
|
107
|
+
}),
|
|
108
|
+
});
|
|
105
109
|
|
|
106
|
-
export const EntwurfV2ReceiptSchema =
|
|
110
|
+
export const EntwurfV2ReceiptSchema = union([EntwurfV2ReceiptSuccessSchema, EntwurfV2ReceiptRejectSchema]);
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* verb (0.11 Stage 0 step 4-pre / 동결결정 10). PURE pi-FREE core: the
|
|
4
4
|
* intent×liveness decision table + the reject taxonomy + a pure resolver.
|
|
5
5
|
* NO runtime dispatch, NO spawn/send, NO I/O — step 5 wires this to transports.
|
|
6
|
-
* The pi-
|
|
6
|
+
* The pi-schema REPRESENTATION of this contract lives in the separate
|
|
7
7
|
* `entwurf-v2-contract-schema.ts` (0.12.1 B-1) so this module — which the
|
|
8
8
|
* harness-neutral MCP bridge reaches at boot — carries no pi dependency.
|
|
9
9
|
*
|
|
@@ -516,9 +516,9 @@ export function resolveNativePushDispatch(intent: EntwurfIntent, liveness: Nativ
|
|
|
516
516
|
};
|
|
517
517
|
}
|
|
518
518
|
|
|
519
|
-
// ──
|
|
520
|
-
// MOVED to `entwurf-v2-contract-schema.ts` (0.12.1 B-1): the pi
|
|
521
|
-
// builders (StringEnum
|
|
519
|
+
// ── JSON schemas ───────────────────────────────────────────────────────────
|
|
520
|
+
// MOVED to `entwurf-v2-contract-schema.ts` (0.12.1 B-1): the pi schema
|
|
521
|
+
// builders (pi-ai StringEnum, pi-schema since #155) are a pi-lane dependency, so they cannot live in
|
|
522
522
|
// this pi-free core — the MCP bridge reaches this module at boot and must stay
|
|
523
523
|
// harness-neutral (check-entwurf-bridge-pi-free). The schemas import the
|
|
524
524
|
// constants/types above; pi-side consumers import the schemas from there.
|
|
@@ -349,7 +349,7 @@ export interface PiDurableRoots {
|
|
|
349
349
|
// ---------------------------------------------------------------------------
|
|
350
350
|
// Durable surface, typed NARROWLY and locally — read at pi-durable 1.0.2 @cd32f77 (#129,
|
|
351
351
|
// `harness/types.ts`; historical coordinates, not re-anchored here). The current supply is the
|
|
352
|
-
// carrier emitted from Entwurf's owned source (pi/pi-durable/src, #141) against the 1.
|
|
352
|
+
// carrier emitted from Entwurf's owned source (pi/pi-durable/src, #141) against the 1.5.0 SDK set
|
|
353
353
|
// `pi/pi-durable/overlay/upstream-pin.json` names (origin `abe508e`). `[read 2026-10-13]` at
|
|
354
354
|
// v1.2.0 one shape typed below moved: `ToolExecutionResult.content` is now `output`, beside the new
|
|
355
355
|
// `structuredOutput` (`harness/types.ts`, its CHANGELOG's breaking changes). An omitted `output`
|
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
|
|
|
@@ -2340,21 +2340,64 @@ function stripBumpLedger(file, text) {
|
|
|
2340
2340
|
return kept.join('\n');
|
|
2341
2341
|
}
|
|
2342
2342
|
|
|
2343
|
-
|
|
2343
|
+
// PUBLISHED-SCOPE REGIONS (#155). Between an SDK bump in this checkout and the release that ships it,
|
|
2344
|
+
// an operator doc must keep telling a reader how to install the package that IS on npm — whose Pi
|
|
2345
|
+
// range is the previous floor — while the checkout's own sentences already name the new one. Those
|
|
2346
|
+
// are two different subjects in one file, so the doc marks the published one explicitly:
|
|
2347
|
+
// <!-- entwurf:published @junghanacs/entwurf@<version> pi <sdk> --> … <!-- /entwurf:published -->
|
|
2348
|
+
// Inside a region every scan below binds to <sdk>, outside it to the pinned floor. The region is not
|
|
2349
|
+
// a skip: it is scanned, it must hold at least one declaration, and after the four-surface assertion
|
|
2350
|
+
// below the published-scope claim holds its pair to PUBLISHED_ARTIFACT — a published package's own
|
|
2351
|
+
// manifest, frozen here. It is deliberately NOT derived from this checkout (package.json's version,
|
|
2352
|
+
// the workspace closure): a published package's contract stays true when the checkout moves on, so a
|
|
2353
|
+
// release preparation must not falsify it. `[측정 2026-11-03]` the source-only qualification snapshot
|
|
2354
|
+
// has no .git, so a release tag cannot be that oracle either.
|
|
2355
|
+
const PUBLISHED_OPEN = /<!-- entwurf:published @junghanacs\/entwurf@(\d+\.\d+\.\d+) pi (\d+\.\d+\.\d+) -->/g;
|
|
2356
|
+
const PUBLISHED_CLOSE = '<!-- /entwurf:published -->';
|
|
2357
|
+
function publishedRegions(text) {
|
|
2358
|
+
const regions = [], problems = [];
|
|
2359
|
+
let from = 0;
|
|
2360
|
+
for (const open of text.matchAll(PUBLISHED_OPEN)) {
|
|
2361
|
+
if (open.index < from) { problems.push(`a region opens inside the previous one at offset ${open.index}`); continue; }
|
|
2362
|
+
const end = text.indexOf(PUBLISHED_CLOSE, open.index);
|
|
2363
|
+
const next = text.slice(open.index + open[0].length).search(/<!-- entwurf:published /);
|
|
2364
|
+
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; }
|
|
2365
|
+
regions.push({ start: open.index, end, version: open[1], sdk: open[2], body: text.slice(open.index, end) });
|
|
2366
|
+
from = end + PUBLISHED_CLOSE.length;
|
|
2367
|
+
}
|
|
2368
|
+
const opens = [...text.matchAll(/<!-- entwurf:published /g)].length;
|
|
2369
|
+
const closes = text.split(PUBLISHED_CLOSE).length - 1;
|
|
2370
|
+
if (opens !== regions.length + problems.length) problems.push(`${opens - regions.length} published marker(s) do not have the exact opening shape`);
|
|
2371
|
+
if (closes !== regions.length) problems.push(`${closes} closing marker(s) for ${regions.length} region(s)`);
|
|
2372
|
+
return { regions, problems };
|
|
2373
|
+
}
|
|
2374
|
+
const floorAt = (regions, index) => regions.find((r) => index > r.start && index < r.end)?.sdk ?? piAi;
|
|
2375
|
+
const nextMinor = (v) => { const [a, b] = v.split('.').map(Number); return `${a}.${b + 1}`; };
|
|
2376
|
+
|
|
2377
|
+
let rangeDecls = 0, exactDecls = 0, sourceRangeDecls = 0;
|
|
2344
2378
|
for (const file of BASELINE_DOCS) {
|
|
2345
2379
|
const text = stripBumpLedger(file, readFileSync(file, 'utf8'));
|
|
2380
|
+
const { regions } = publishedRegions(text);
|
|
2346
2381
|
// Closed-range declarations: `>=<floor> <<major>.<ceiling>` (spaces optional).
|
|
2347
|
-
for (const
|
|
2382
|
+
for (const m of text.matchAll(/>=\s?(\d+\.\d+\.\d+)\s?<\s?(\d+)\.(\d+)/g)) {
|
|
2383
|
+
const [decl, floor, ceilMajor, ceilMinor] = m;
|
|
2384
|
+
const want = floorAt(regions, m.index);
|
|
2348
2385
|
rangeDecls++;
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
`${file}: declared pi
|
|
2386
|
+
if (want === piAi) sourceRangeDecls++;
|
|
2387
|
+
assert.equal(floor, want, want === piAi
|
|
2388
|
+
? `${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`
|
|
2389
|
+
: `${file}: declared pi floor in "${decl}" is ${floor}, but its published-scope region names pi ${want}`);
|
|
2390
|
+
assert.equal(`${ceilMajor}.${ceilMinor}`, nextMinor(want),
|
|
2391
|
+
`${file}: declared pi ceiling in "${decl}" must be the next minor (${nextMinor(want)})`);
|
|
2353
2392
|
}
|
|
2354
2393
|
// Exact install pins: `@earendil-works/pi-<pkg>@<version>`.
|
|
2355
|
-
for (const
|
|
2394
|
+
for (const m of text.matchAll(/@earendil-works\/pi-(?:ai|coding-agent|tui)@(\d+\.\d+\.\d+)/g)) {
|
|
2395
|
+
const [decl, ver] = m;
|
|
2396
|
+
const want = floorAt(regions, m.index);
|
|
2356
2397
|
exactDecls++;
|
|
2357
|
-
assert.equal(ver,
|
|
2398
|
+
assert.equal(ver, want, want === piAi
|
|
2399
|
+
? `${file}: install example "${decl}" pins ${ver}, but the pinned SDK is ${piAi}`
|
|
2400
|
+
: `${file}: install example "${decl}" pins ${ver}, but its published-scope region names pi ${want}`);
|
|
2358
2401
|
}
|
|
2359
2402
|
}
|
|
2360
2403
|
// Prose declarations carry the pin in sentences the two patterns above cannot
|
|
@@ -2366,9 +2409,11 @@ const PROSE_DECLS = [
|
|
|
2366
2409
|
['ROADMAP.md', /floor = \*\*(\d+\.\d+\.\d+)\*\*/, 'floor = **<version>**'],
|
|
2367
2410
|
];
|
|
2368
2411
|
for (const [file, re, shape] of PROSE_DECLS) {
|
|
2369
|
-
const
|
|
2412
|
+
const text = readFileSync(file, 'utf8');
|
|
2413
|
+
const m = text.match(re);
|
|
2370
2414
|
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
|
-
|
|
2415
|
+
const want = floorAt(publishedRegions(text).regions, m.index);
|
|
2416
|
+
assert.equal(m[1], want, `${file}: "${shape}" declares ${m[1]}, but the ${want === piAi ? 'pinned SDK' : 'published-scope region'} is ${want}`);
|
|
2372
2417
|
}
|
|
2373
2418
|
// ── #120 P4: ONE FLOOR, FOUR MECHANICAL SURFACES ──────────────────────────
|
|
2374
2419
|
// The bump is mostly a number substitution, and that is exactly how a bump goes
|
|
@@ -2390,6 +2435,8 @@ const PI_CONSTELLATION = [
|
|
|
2390
2435
|
'@earendil-works/pi-codemode', '@earendil-works/pi-mcp',
|
|
2391
2436
|
// #130: the durable library the pi-durable carrier imports — a production dependency since 0.32.
|
|
2392
2437
|
'@earendil-works/pi-durable',
|
|
2438
|
+
// `[측정 2026-11-03]` 1.5.0 (#155): pi-schema joins — declared by chord, pi-agent-core, pi-ai and pi-durable.
|
|
2439
|
+
'@earendil-works/pi-schema',
|
|
2393
2440
|
];
|
|
2394
2441
|
const sameSet = (got, want, where) => {
|
|
2395
2442
|
const missing = want.filter((n) => !got.includes(n));
|
|
@@ -2404,7 +2451,7 @@ for (const [, name, ver] of runSh.matchAll(/"(@earendil-works\/[a-z-]+)@(\d+\.\d
|
|
|
2404
2451
|
if (ver !== piAi) floorGaps.push(`run.sh install constellation: ${name}@${ver} is not the floor ${piAi}`);
|
|
2405
2452
|
}
|
|
2406
2453
|
sameSet(installed, PI_CONSTELLATION, 'run.sh install constellation');
|
|
2407
|
-
// pnpm-workspace.yaml minimumReleaseAgeExclude: the same
|
|
2454
|
+
// pnpm-workspace.yaml minimumReleaseAgeExclude: the same constellation, each ADMITTING the
|
|
2408
2455
|
// current floor. History stays (`0.85.0 || 0.85.1 || …`) — only the newest term is
|
|
2409
2456
|
// the contract, because that is what an install resolves against.
|
|
2410
2457
|
const closure = [...readFileSync('pnpm-workspace.yaml', 'utf8').matchAll(/^\s*-\s*'(@earendil-works\/[a-z-]+)@([^']+)'/gm)];
|
|
@@ -2437,22 +2484,64 @@ const CURRENT_CONTRACT_DECLS = [
|
|
|
2437
2484
|
['docs/acp-backend-rail.md', /SDK exact `(\d+\.\d+\.\d+)`, host `(>=[\d.]+ <\d+\.\d+)`/, "the ACP support row's exact+range pair"],
|
|
2438
2485
|
];
|
|
2439
2486
|
for (const [file, re, shape] of CURRENT_CONTRACT_DECLS) {
|
|
2440
|
-
const
|
|
2487
|
+
const text = readFileSync(file, 'utf8');
|
|
2488
|
+
const m = text.match(re);
|
|
2441
2489
|
if (!m) { floorGaps.push(`${file}: ${shape} is gone — a reworded current-contract sentence must fail loud, not drop out silently`); continue; }
|
|
2490
|
+
// A sentence inside a published-scope region states the published package's contract; it is
|
|
2491
|
+
// held to that region's pi here and the region itself to its oracles below.
|
|
2492
|
+
const floor = floorAt(publishedRegions(text).regions, m.index);
|
|
2493
|
+
const range = `>=${floor} <${nextMinor(floor)}`;
|
|
2442
2494
|
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 ${
|
|
2495
|
+
const expected = captured.startsWith('>=') ? range : floor;
|
|
2496
|
+
if (captured !== expected) floorGaps.push(`${file}: ${shape} declares "${captured}", but the ${floor === piAi ? 'floor' : 'published-scope region'} is ${floor} (${range})`);
|
|
2445
2497
|
}
|
|
2446
2498
|
}
|
|
2447
2499
|
assert.deepEqual(floorGaps, [],
|
|
2448
2500
|
`[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
2501
|
|
|
2502
|
+
// The published-scope regions' own oracle (see PUBLISHED-SCOPE REGIONS above). Checked after the
|
|
2503
|
+
// four surfaces so a stranded closure row is still attributed to the claim above.
|
|
2504
|
+
// PUBLISHED_ARTIFACT is the one published pair a region may name, copied from the artifact, not from
|
|
2505
|
+
// this checkout. `[측정 2026-11-03]` the global install of @junghanacs/entwurf@0.50.0 whose bytes #155
|
|
2506
|
+
// HOP1 accepted against the registry tarball declares exactly these nine @earendil-works dependencies
|
|
2507
|
+
// in its package.json (sha256 20c572534fcfc03e8f89890a0853a25048166f7c998e433945289a5d20ee1234).
|
|
2508
|
+
const PUBLISHED_ARTIFACT = {
|
|
2509
|
+
version: '0.50.0',
|
|
2510
|
+
dependencies: {
|
|
2511
|
+
'@earendil-works/chord': '1.4.0', '@earendil-works/pi-agent-core': '1.4.0', '@earendil-works/pi-ai': '1.4.0',
|
|
2512
|
+
'@earendil-works/pi-codemode': '1.4.0', '@earendil-works/pi-coding-agent': '1.4.0', '@earendil-works/pi-durable': '1.4.0',
|
|
2513
|
+
'@earendil-works/pi-mcp': '1.4.0', '@earendil-works/pi-telemetry': '1.4.0', '@earendil-works/pi-tui': '1.4.0',
|
|
2514
|
+
},
|
|
2515
|
+
};
|
|
2516
|
+
const publishedSdk = [...new Set(Object.values(PUBLISHED_ARTIFACT.dependencies))];
|
|
2517
|
+
const publishedGaps = [];
|
|
2518
|
+
if (publishedSdk.length !== 1) publishedGaps.push(`PUBLISHED_ARTIFACT ${PUBLISHED_ARTIFACT.version} carries no single SDK generation (${publishedSdk.join(', ')})`);
|
|
2519
|
+
let publishedRegionCount = 0;
|
|
2520
|
+
const PUBLISHED_REGION_MAX_LINES = 40;
|
|
2521
|
+
for (const file of [...new Set([...BASELINE_DOCS, ...CURRENT_CONTRACT_DECLS.map(([f]) => f)])]) {
|
|
2522
|
+
const { regions, problems } = publishedRegions(readFileSync(file, 'utf8'));
|
|
2523
|
+
for (const p of problems) publishedGaps.push(`${file}: ${p}`);
|
|
2524
|
+
for (const r of regions) {
|
|
2525
|
+
publishedRegionCount++;
|
|
2526
|
+
const where = `${file}: region "@junghanacs/entwurf@${r.version} pi ${r.sdk}"`;
|
|
2527
|
+
if (r.version !== PUBLISHED_ARTIFACT.version) publishedGaps.push(`${where} names a package no frozen published manifest records (only @junghanacs/entwurf@${PUBLISHED_ARTIFACT.version})`);
|
|
2528
|
+
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(' | ')}`);
|
|
2529
|
+
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;
|
|
2530
|
+
if (decls === 0) publishedGaps.push(`${where} holds no pi declaration — an empty region only hides prose from the scan`);
|
|
2531
|
+
const lines = r.body.split('\n').length;
|
|
2532
|
+
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`);
|
|
2533
|
+
}
|
|
2534
|
+
}
|
|
2535
|
+
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`);
|
|
2536
|
+
assert.deepEqual(publishedGaps, [],
|
|
2537
|
+
`[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`);
|
|
2538
|
+
|
|
2450
2539
|
// Guard the guard: if the patterns ever stop matching, the loops above pass
|
|
2451
2540
|
// vacuously and the docs fall back OUT of the gate without a word.
|
|
2452
2541
|
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
2542
|
assert.ok(exactDecls >= 1, `expected at least 1 exact pi install pin in the baseline docs, found ${exactDecls}`);
|
|
2454
2543
|
|
|
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)`);
|
|
2544
|
+
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
2545
|
EOF
|
|
2457
2546
|
)
|
|
2458
2547
|
}
|
|
@@ -4040,7 +4129,7 @@ check_pack_win32_carrier() {
|
|
|
4040
4129
|
# which is exactly what property (1)'s `(_|$)` absorbs; all three shapes are fixtures
|
|
4041
4130
|
# below so a future suffix change cannot pass vacuously.)
|
|
4042
4131
|
pack_install_leaked_pi() {
|
|
4043
|
-
grep '^@earendil-works+' | grep -Ev '@1\.
|
|
4132
|
+
grep '^@earendil-works+' | grep -Ev '@1\.5\.0(_|$)' || true
|
|
4044
4133
|
}
|
|
4045
4134
|
|
|
4046
4135
|
# Matcher self-test on SYNTHETIC lookalikes: a healthy install tree cannot exercise either
|
|
@@ -4057,18 +4146,18 @@ check_pack_pin_matcher() {
|
|
|
4057
4146
|
# Cell 1 — the version boundary. Every measured peer-hash shape is a fixture: the
|
|
4058
4147
|
# 0.84.4-era `_@modelcontextprotocol+sdk@…`, the `_ws@8.21.3` observed on a real
|
|
4059
4148
|
# 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.
|
|
4149
|
+
# None may leak; the two lookalikes must — a PREFIX-EXTENDED version (`1.5.0-beta.1`,
|
|
4061
4150
|
# the prerelease shape that an unanchored match would bless) and an off-pin version.
|
|
4062
4151
|
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.
|
|
4152
|
+
'@earendil-works+pi-ai@1.5.0' \
|
|
4153
|
+
'@earendil-works+pi-ai@1.5.0_@modelcontextprotocol+sdk@1.29.0_zod@4.3.6' \
|
|
4154
|
+
'@earendil-works+pi-ai@1.5.0_ws@8.21.3' \
|
|
4155
|
+
'@earendil-works+pi-ai@1.5.0_@modelcontextprotocol+sdk@1.29.0_zod@4.3.6__ws@8.21.3_zod@4.3.6' \
|
|
4156
|
+
'@earendil-works+pi-ai@1.5.0-beta.1' \
|
|
4068
4157
|
'@earendil-works+pi-agent-core@0.87.1' | pack_install_leaked_pi)
|
|
4069
|
-
if [ "$matcher_probe" != '@earendil-works+pi-ai@1.
|
|
4158
|
+
if [ "$matcher_probe" != '@earendil-works+pi-ai@1.5.0-beta.1
|
|
4070
4159
|
@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.
|
|
4160
|
+
fail "[QK:PACK-INSTALL-PIN-MATCHER-BOUNDED] the pin-leak matcher must flag the prefix-extended 1.5.0-beta.1 and the off-pin 0.87.1 lookalikes, and pass 1.5.0 bare or with any measured peer-hash — got: ${matcher_probe:-<nothing leaked>}"
|
|
4072
4161
|
return 1
|
|
4073
4162
|
fi
|
|
4074
4163
|
|
|
@@ -4076,9 +4165,9 @@ check_pack_pin_matcher() {
|
|
|
4076
4165
|
# (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
4166
|
# back to `^@earendil-works+pi-` sees nothing here and dies at this signature.
|
|
4078
4167
|
matcher_probe=$(printf '%s\n' \
|
|
4079
|
-
'@earendil-works+chord@1.
|
|
4168
|
+
'@earendil-works+chord@1.5.0' \
|
|
4080
4169
|
'@earendil-works+chord@0.87.1' \
|
|
4081
|
-
'@earendil-works+pi-ai@1.
|
|
4170
|
+
'@earendil-works+pi-ai@1.5.0' | pack_install_leaked_pi)
|
|
4082
4171
|
if [ "$matcher_probe" != '@earendil-works+chord@0.87.1' ]; then
|
|
4083
4172
|
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
4173
|
return 1
|
|
@@ -4291,7 +4380,9 @@ ORACLE
|
|
|
4291
4380
|
# SDK 1.4 pi-durable imports pi-ai's new `utils/identity`, absent from the older pi-ai control versions
|
|
4292
4381
|
# 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
4382
|
# 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).
|
|
4383
|
+
# depth-2 mismatch (L4 keeps depth 3). `[측정 2026-11-03]` SDK 1.5.0 pi-durable still loads beside
|
|
4384
|
+
# pi-codemode 1.3.0 (registry tarball, integrity-matched) and 1.4.0 (#155 import probe; its codemode
|
|
4385
|
+
# imports are the root, /portable, /source and /declarations entries), so the control version holds.
|
|
4295
4386
|
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
4387
|
out=$(cd "$box/pd-mismatch" && npm install "$tgz" --ignore-scripts --no-audit --no-fund 2>&1) || {
|
|
4297
4388
|
fail "[check-pack-install] pi-durable L3 (mismatch control) install failed:"; echo "$out" | tail -10 | sed 's/^/ /' >&2; return 1; }
|
|
@@ -4421,7 +4512,13 @@ ORACLE
|
|
|
4421
4512
|
local pin_minor="${pin_ver#*.}"; pin_minor="${pin_minor%%.*}"
|
|
4422
4513
|
local pin_ceiling="${pin_ver%%.*}.$((pin_minor + 1))"
|
|
4423
4514
|
mkdir -p "$host_old"
|
|
4424
|
-
|
|
4515
|
+
# The old-host fixture installs the members its own generation had. `[측정 2026-11-03]` pi-schema's
|
|
4516
|
+
# 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
|
|
4517
|
+
# release at the fixed old version — the only member skipped here; the pinned/aligned cells keep all ten.
|
|
4518
|
+
for spec in "${host_specs[@]}"; do
|
|
4519
|
+
[ "${spec%@*}" = '@earendil-works/pi-schema' ] && continue
|
|
4520
|
+
host_old_specs+=("${spec%@*}@$host_old_ver")
|
|
4521
|
+
done
|
|
4425
4522
|
out=$(cd "$host_old" && npm install "${host_old_specs[@]}" --ignore-scripts --no-audit --no-fund 2>&1) || {
|
|
4426
4523
|
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
4524
|
for cell in old aligned; do
|
|
@@ -4986,20 +5083,22 @@ _check_pack_install_impl() {
|
|
|
4986
5083
|
# oracle walked 10 member edges with 0 mismatches (.tmp-verify/145-stage1/08-pack-install.log).
|
|
4987
5084
|
# `[측정 2026-10-22]` at 1.4.0 the nine members are again exact 1.4.0 edges (registry manifests; #148 S0),
|
|
4988
5085
|
# and pi-tui 1.4.0 adds one third-party dependency (marked-cjk-friendly 0.1.2) outside the closure.
|
|
4989
|
-
|
|
5086
|
+
# `[측정 2026-11-03]` at 1.5.0 the set grows to ten: pi-schema 1.5.0 is new and declared by chord,
|
|
5087
|
+
# pi-agent-core, pi-ai and pi-durable; every member edge is exact 1.5.0 (registry manifests; #155).
|
|
5088
|
+
echo "[check-pack-install] pnpm add into $tmp (with the aligned 1.5.x SDK + chord + codemode + mcp + durable + schema)"
|
|
4990
5089
|
local install_log
|
|
4991
5090
|
install_log=$(cd "$tmp" && pnpm add \
|
|
4992
5091
|
"$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
|
-
"
|
|
5092
|
+
"@earendil-works/pi-ai@1.5.0" \
|
|
5093
|
+
"@earendil-works/pi-coding-agent@1.5.0" \
|
|
5094
|
+
"@earendil-works/pi-tui@1.5.0" \
|
|
5095
|
+
"@earendil-works/pi-agent-core@1.5.0" \
|
|
5096
|
+
"@earendil-works/pi-telemetry@1.5.0" \
|
|
5097
|
+
"@earendil-works/chord@1.5.0" \
|
|
5098
|
+
"@earendil-works/pi-codemode@1.5.0" \
|
|
5099
|
+
"@earendil-works/pi-mcp@1.5.0" \
|
|
5100
|
+
"@earendil-works/pi-durable@1.5.0" \
|
|
5101
|
+
"@earendil-works/pi-schema@1.5.0" \
|
|
5003
5102
|
--ignore-workspace --ignore-scripts 2>&1) || {
|
|
5004
5103
|
fail "[check-pack-install] pnpm add failed:"
|
|
5005
5104
|
echo "$install_log" | tail -10 | sed 's/^/ /' >&2
|
|
@@ -5014,11 +5113,11 @@ _check_pack_install_impl() {
|
|
|
5014
5113
|
local leaked_pi
|
|
5015
5114
|
leaked_pi=$(ls "$tmp/node_modules/.pnpm" 2>/dev/null | pack_install_leaked_pi)
|
|
5016
5115
|
if [ -n "$leaked_pi" ]; then
|
|
5017
|
-
fail "[check-pack-install] UNVERIFIED pi runtime resolved into the install tree (expected only 1.
|
|
5116
|
+
fail "[check-pack-install] UNVERIFIED pi runtime resolved into the install tree (expected only 1.5.0):"
|
|
5018
5117
|
printf '%s\n' "$leaked_pi" | sed 's/^/ /' >&2
|
|
5019
5118
|
return 1
|
|
5020
5119
|
fi
|
|
5021
|
-
echo "[check-pack-install] pi runtime tree pin verified: every @earendil-works package is 1.
|
|
5120
|
+
echo "[check-pack-install] pi runtime tree pin verified: every @earendil-works package is 1.5.0 (chord, pi-durable and pi-schema included)"
|
|
5022
5121
|
|
|
5023
5122
|
# Resolve the installed package.json and confirm pi.extensions
|
|
5024
5123
|
# 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);
|
|
@@ -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,
|
|
@@ -194,7 +194,7 @@
|
|
|
194
194
|
"claim": "PI-CORE-PIN-TRACKS-FLOOR",
|
|
195
195
|
"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
196
|
"subject": "package.json",
|
|
197
|
-
"find": ["\"@earendil-works/pi-agent-core\": \"1.
|
|
197
|
+
"find": ["\"@earendil-works/pi-agent-core\": \"1.5.0\","],
|
|
198
198
|
"replace": ["\"@earendil-works/pi-agent-core\": \"0.87.1\","],
|
|
199
199
|
"gate": ["bash", "run.sh", "check-dep-versions"],
|
|
200
200
|
"timeoutSeconds": 300,
|
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
"claim": "PACK-INSTALL-PIN-MATCHER-BOUNDED",
|
|
7
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
|
-
"find": [" grep '^@earendil-works+' | grep -Ev '@1\\.
|
|
10
|
-
"replace": [" grep '^@earendil-works+' | grep -v '@1\\.
|
|
9
|
+
"find": [" grep '^@earendil-works+' | grep -Ev '@1\\.5\\.0(_|$)' || true"],
|
|
10
|
+
"replace": [" grep '^@earendil-works+' | grep -v '@1\\.5\\.0' || true"],
|
|
11
11
|
"gate": ["bash", "run.sh", "check-pack-pin-matcher"],
|
|
12
12
|
"timeoutSeconds": 120,
|
|
13
13
|
"signature": "[QK:PACK-INSTALL-PIN-MATCHER-BOUNDED]",
|
|
@@ -17,8 +17,8 @@
|
|
|
17
17
|
"claim": "PACK-INSTALL-PIN-MATCHER-COVERS-CLOSURE",
|
|
18
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
|
-
"find": [" grep '^@earendil-works+' | grep -Ev '@1\\.
|
|
21
|
-
"replace": [" grep '^@earendil-works+pi-' | grep -Ev '@1\\.
|
|
20
|
+
"find": [" grep '^@earendil-works+' | grep -Ev '@1\\.5\\.0(_|$)' || true"],
|
|
21
|
+
"replace": [" grep '^@earendil-works+pi-' | grep -Ev '@1\\.5\\.0(_|$)' || true"],
|
|
22
22
|
"gate": ["bash", "run.sh", "check-pack-pin-matcher"],
|
|
23
23
|
"timeoutSeconds": 120,
|
|
24
24
|
"signature": "[QK:PACK-INSTALL-PIN-MATCHER-COVERS-CLOSURE]",
|