@junghanacs/entwurf 0.25.0 → 0.30.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 +1 -1
- package/BASELINE.md +1 -1
- package/CHANGELOG.md +182 -0
- package/CONTRIBUTING.md +1 -1
- package/DELIVERY.md +1 -1
- package/README.md +178 -674
- package/VERIFY.md +17 -16
- package/demo/README.md +1 -1
- package/docs/acp-backend-rail.md +6 -2
- 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 +12 -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 +11 -11
- package/pi-extensions/entwurf-control.ts +50 -637
- package/pi-extensions/lib/acp/backend-adapter.ts +13 -1
- 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 +168 -54
- 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 +2741 -157
- 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/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/lib/qualification-acceptance.ts +146 -0
- package/scripts/lib/qualification-artifact.ts +359 -0
- package/scripts/lib/qualification-collection.ts +384 -0
- package/scripts/lib/qualification-gh.ts +231 -0
- package/scripts/lib/qualification-ledger.ts +669 -0
- package/scripts/lib/qualification-oracle-core.ts +279 -0
- package/scripts/lib/qualification-receipt.ts +561 -0
- package/scripts/lib/qualification-release.ts +271 -0
- package/scripts/meta-bridge-doctor.sh +16 -5
- package/scripts/mutants/acp-foreground-only.json +17 -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/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/qualification-ledger.json +489 -0
- package/scripts/mutants/v2-surface.json +6 -6
- package/scripts/qualification-oracle.ts +124 -0
- 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-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-raw-install-live.sh +8 -3
- package/scripts/smoke-mux-lifecycle-live.ts +1 -1
- package/scripts/smoke-resident-garden-guard.sh +3 -1
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* qualification-release — compose a RELEASE ledger from CI collections along a
|
|
3
|
+
* first-parent chain (#124 step 2). PURE: no network, no git, no fs. The caller hands
|
|
4
|
+
* in what it has already fetched and verified elsewhere; this function never claims its
|
|
5
|
+
* inputs came from anywhere in particular, and it produces no acceptance class.
|
|
6
|
+
*
|
|
7
|
+
* Inputs:
|
|
8
|
+
* - finalSha and firstParent (`git rev-list --first-parent finalSha`, finalSha first);
|
|
9
|
+
* - runs: one entry per CI run attempt — runId, runAttempt, headSha, and the unpacked
|
|
10
|
+
* collection files (name → bytes);
|
|
11
|
+
* - independent: the candidate recomputed by the caller from the final tree ITSELF
|
|
12
|
+
* (surface, groups, manifest claims, CORE_FILES fingerprints). Its node/node_modules
|
|
13
|
+
* part cannot be recomputed off the CI runner and is not used;
|
|
14
|
+
* - operatorDeclared: extra groups a human requires to be measured on the final SHA.
|
|
15
|
+
*
|
|
16
|
+
* Contract errors (any → RED): a run that is not CI provenance (runId/runAttempt not
|
|
17
|
+
* digits), a headSha off the first-parent chain, the same (runId, runAttempt) twice, a
|
|
18
|
+
* collection whose own runId/runAttempt/head disagree with the run, no collection at the
|
|
19
|
+
* final SHA, an incomplete final collection, or a final candidate the independent
|
|
20
|
+
* recomputation contradicts.
|
|
21
|
+
*
|
|
22
|
+
* Per SHA only the newest (runId, then runAttempt) run counts; older ones are listed as
|
|
23
|
+
* superseded. An incomplete ancestor collection is excluded from the basis with its
|
|
24
|
+
* reason. Order across runs is first-parent position, then collection index — never a
|
|
25
|
+
* clock. Release blockers on top of the local ones: every group needs a usable pass
|
|
26
|
+
* receipt somewhere in the chain (UNRUN blocks), and the runner group, every group whose
|
|
27
|
+
* newest receipt in the chain is red, and every operator-declared group must be
|
|
28
|
+
* exact-MEASURED on the final SHA. A red on an ancestor that the final SHA re-measured
|
|
29
|
+
* green stays in history and contributes no FAIL.
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
import { type Collection, checkCollectionData, mapReader } from "./qualification-collection.ts";
|
|
33
|
+
import {
|
|
34
|
+
type ContentCandidate,
|
|
35
|
+
type CurrentCandidate,
|
|
36
|
+
composeLedger,
|
|
37
|
+
type Ledger,
|
|
38
|
+
type LoadedReceipt,
|
|
39
|
+
} from "./qualification-ledger.ts";
|
|
40
|
+
import { canonicalJson } from "./qualification-receipt.ts";
|
|
41
|
+
|
|
42
|
+
export interface CiRun {
|
|
43
|
+
runId: string;
|
|
44
|
+
runAttempt: string;
|
|
45
|
+
headSha: string;
|
|
46
|
+
files: Record<string, Buffer>;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface ReleaseInput {
|
|
50
|
+
finalSha: string;
|
|
51
|
+
firstParent: string[];
|
|
52
|
+
runs: CiRun[];
|
|
53
|
+
/** The final tree's content projection — no C0 environment part (see ContentCandidate). */
|
|
54
|
+
independent: ContentCandidate;
|
|
55
|
+
operatorDeclared: string[][];
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export interface RunRef {
|
|
59
|
+
runId: string;
|
|
60
|
+
runAttempt: string;
|
|
61
|
+
headSha: string;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export interface ReleaseLedger extends Ledger {
|
|
65
|
+
release: {
|
|
66
|
+
finalSha: string;
|
|
67
|
+
contractErrors: string[];
|
|
68
|
+
crossCheck: string[];
|
|
69
|
+
used: RunRef[];
|
|
70
|
+
excluded: (RunRef & { reason: string })[];
|
|
71
|
+
required: string[][];
|
|
72
|
+
denominator: {
|
|
73
|
+
groups: number;
|
|
74
|
+
claims: number;
|
|
75
|
+
measuredOnFinal: { groups: number; claims: number; list: string[] };
|
|
76
|
+
inheritedCore: { gate: string; from: RunRef | null; reason: string }[];
|
|
77
|
+
flagged: string[];
|
|
78
|
+
unrun: number;
|
|
79
|
+
fail: number;
|
|
80
|
+
};
|
|
81
|
+
/** What this ledger can and cannot vouch for. */
|
|
82
|
+
provenance: string[];
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const DIGITS = /^[0-9]+$/;
|
|
87
|
+
const refOf = (r: CiRun): RunRef => ({ runId: r.runId, runAttempt: r.runAttempt, headSha: r.headSha });
|
|
88
|
+
const newer = (a: CiRun, b: CiRun): boolean =>
|
|
89
|
+
BigInt(a.runId) !== BigInt(b.runId) ? BigInt(a.runId) > BigInt(b.runId) : BigInt(a.runAttempt) > BigInt(b.runAttempt);
|
|
90
|
+
|
|
91
|
+
/** Content fields a CI-claimed candidate must share with the independent recomputation. */
|
|
92
|
+
export function crossCheckCandidate(claimed: CurrentCandidate, independent: ContentCandidate): string[] {
|
|
93
|
+
const out: string[] = [];
|
|
94
|
+
if (claimed.head !== independent.head) out.push(`head: CI ${claimed.head} vs tree ${independent.head}`);
|
|
95
|
+
if (claimed.workSurfaceSha256 !== independent.workSurfaceSha256) out.push("workSurface");
|
|
96
|
+
if (canonicalJson(claimed.manifestClaims) !== canonicalJson(independent.manifestClaims)) out.push("manifestClaims");
|
|
97
|
+
if (canonicalJson(claimed.groups) !== canonicalJson(independent.groups)) out.push("groups");
|
|
98
|
+
if (!claimed.coreFiles || !independent.coreFiles) out.push("coreFiles: missing on one side");
|
|
99
|
+
else if (canonicalJson(claimed.coreFiles) !== canonicalJson(independent.coreFiles)) out.push("coreFiles");
|
|
100
|
+
return out;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function composeRelease(input: ReleaseInput): ReleaseLedger {
|
|
104
|
+
const contractErrors: string[] = [];
|
|
105
|
+
const excluded: ReleaseLedger["release"]["excluded"] = [];
|
|
106
|
+
const chainPos = new Map(input.firstParent.map((sha, i) => [sha, i]));
|
|
107
|
+
if (input.firstParent[0] !== input.finalSha) contractErrors.push("firstParent must start with finalSha");
|
|
108
|
+
|
|
109
|
+
// 1. Admission: provenance shape, chain membership, uniqueness, collection integrity.
|
|
110
|
+
const seenAttempt = new Set<string>();
|
|
111
|
+
const admitted: { run: CiRun; collection: Collection; complete: boolean; errors: string[] }[] = [];
|
|
112
|
+
for (const run of input.runs) {
|
|
113
|
+
const tag = `run ${run.runId}#${run.runAttempt}@${run.headSha.slice(0, 12)}`;
|
|
114
|
+
if (!DIGITS.test(run.runId) || !DIGITS.test(run.runAttempt)) {
|
|
115
|
+
contractErrors.push(`${tag}: runId/runAttempt are not CI provenance`);
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
const key = `${run.runId}#${run.runAttempt}`;
|
|
119
|
+
if (seenAttempt.has(key)) {
|
|
120
|
+
contractErrors.push(`${tag}: the same run attempt is supplied twice`);
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
seenAttempt.add(key);
|
|
124
|
+
if (!chainPos.has(run.headSha)) {
|
|
125
|
+
contractErrors.push(`${tag}: headSha is not on the first-parent chain of ${input.finalSha.slice(0, 12)}`);
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
const { errors, collection } = checkCollectionData(mapReader(run.files), Object.keys(run.files).sort());
|
|
129
|
+
if (!collection) {
|
|
130
|
+
contractErrors.push(`${tag}: ${errors.join("; ")}`);
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
if (collection.runId !== run.runId || collection.runAttempt !== run.runAttempt) {
|
|
134
|
+
contractErrors.push(`${tag}: the collection records run ${collection.runId}#${collection.runAttempt}`);
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
if (collection.candidate.head !== run.headSha) {
|
|
138
|
+
contractErrors.push(`${tag}: the collection measured head ${collection.candidate.head}`);
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
admitted.push({ run, collection, complete: errors.length === 0, errors });
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// 2. One run per SHA: the newest attempt wins; the rest are superseded, never mixed in.
|
|
145
|
+
const perSha = new Map<string, (typeof admitted)[number]>();
|
|
146
|
+
for (const a of admitted) {
|
|
147
|
+
const cur = perSha.get(a.run.headSha);
|
|
148
|
+
if (!cur || newer(a.run, cur.run)) {
|
|
149
|
+
if (cur) excluded.push({ ...refOf(cur.run), reason: `superseded by run ${a.run.runId}#${a.run.runAttempt}` });
|
|
150
|
+
perSha.set(a.run.headSha, a);
|
|
151
|
+
} else {
|
|
152
|
+
excluded.push({ ...refOf(a.run), reason: `superseded by run ${cur.run.runId}#${cur.run.runAttempt}` });
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
const finalEntry = perSha.get(input.finalSha);
|
|
156
|
+
if (!finalEntry) contractErrors.push(`no CI collection at the final SHA ${input.finalSha.slice(0, 12)}`);
|
|
157
|
+
else if (!finalEntry.complete)
|
|
158
|
+
contractErrors.push(`final-SHA collection incomplete: ${finalEntry.errors.join("; ")}`);
|
|
159
|
+
const used: (typeof admitted)[number][] = [];
|
|
160
|
+
for (const [sha, a] of perSha) {
|
|
161
|
+
if (sha !== input.finalSha && !a.complete) {
|
|
162
|
+
excluded.push({ ...refOf(a.run), reason: `incomplete ancestor collection, not a basis: ${a.errors.join("; ")}` });
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
used.push(a);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// 3. The final candidate: content from the independent recomputation, C0 environment
|
|
169
|
+
// part (node, node_modules) as the final run witnessed it.
|
|
170
|
+
const crossCheck = finalEntry ? crossCheckCandidate(finalEntry.collection.candidate, input.independent) : [];
|
|
171
|
+
const current: CurrentCandidate = {
|
|
172
|
+
...input.independent,
|
|
173
|
+
head: input.finalSha,
|
|
174
|
+
// porcelainClean stays the INDEPENDENT candidate's own: a clean final tree is the
|
|
175
|
+
// oracle's to produce and prove, never this function's to assume.
|
|
176
|
+
coreSha256: finalEntry ? finalEntry.collection.candidate.coreSha256 : "unwitnessed",
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
// 4. Receipts in chain order (older first-parent position first), collection index next.
|
|
180
|
+
const maxPos = input.firstParent.length;
|
|
181
|
+
const loaded: LoadedReceipt[] = [];
|
|
182
|
+
const receiptRun = new Map<string, RunRef>();
|
|
183
|
+
const finalSources = new Set<string>();
|
|
184
|
+
for (const a of used) {
|
|
185
|
+
const base = (maxPos - (chainPos.get(a.run.headSha) ?? maxPos)) * 1_000_000;
|
|
186
|
+
a.collection.attempted.forEach((att, i) => {
|
|
187
|
+
if (att.outcome !== "receipt-written" || !att.file) return;
|
|
188
|
+
const bytes = a.run.files[att.file];
|
|
189
|
+
if (!bytes) return;
|
|
190
|
+
if (a.run.headSha === input.finalSha) finalSources.add(`run ${a.run.runId}#${a.run.runAttempt}/${att.file}`);
|
|
191
|
+
loaded.push({
|
|
192
|
+
source: `run ${a.run.runId}#${a.run.runAttempt}/${att.file}`,
|
|
193
|
+
text: bytes.toString("utf8"),
|
|
194
|
+
seq: base + i,
|
|
195
|
+
});
|
|
196
|
+
try {
|
|
197
|
+
receiptRun.set(JSON.parse(bytes.toString("utf8")).receiptSha256, refOf(a.run));
|
|
198
|
+
} catch {
|
|
199
|
+
// unparseable: composeLedger reports it as INVALID
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// 5. The required set on the final SHA: runner, every group whose newest receipt among
|
|
205
|
+
// the ANCESTOR runs is red (the section that broke — computed before the final run,
|
|
206
|
+
// so re-measuring it green does not erase the requirement), operator-declared.
|
|
207
|
+
const newestPassed = new Map<string, boolean>();
|
|
208
|
+
for (const l of [...loaded].filter((x) => !finalSources.has(x.source)).sort((x, y) => (x.seq ?? 0) - (y.seq ?? 0))) {
|
|
209
|
+
try {
|
|
210
|
+
const r = JSON.parse(l.text);
|
|
211
|
+
newestPassed.set(JSON.stringify(r.group.gate), r.result.passed === true);
|
|
212
|
+
} catch {
|
|
213
|
+
// INVALID, reported by composeLedger
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
const requiredKeys = new Set<string>([JSON.stringify(input.independent.coreGroupGate)]);
|
|
217
|
+
for (const [k, passed] of newestPassed) if (!passed) requiredKeys.add(k);
|
|
218
|
+
for (const argv of input.operatorDeclared) requiredKeys.add(JSON.stringify(argv));
|
|
219
|
+
const required = [...requiredKeys].map((k) => JSON.parse(k) as string[]);
|
|
220
|
+
|
|
221
|
+
const ledger = composeLedger(loaded, current, undefined, { mode: "release", required });
|
|
222
|
+
const red = contractErrors.length > 0 || crossCheck.length > 0;
|
|
223
|
+
const verdict = red ? "RED" : ledger.verdict;
|
|
224
|
+
const summary = [
|
|
225
|
+
...(red ? [`[qualification-release] RED on ${input.finalSha.slice(0, 12)}`] : []),
|
|
226
|
+
...contractErrors.map((e) => ` CONTRACT ${e}`),
|
|
227
|
+
...crossCheck.map((e) => ` CROSS-CHECK ${e}: the CI-claimed candidate contradicts the final tree`),
|
|
228
|
+
...excluded.map((e) => ` EXCLUDED run ${e.runId}#${e.runAttempt}@${e.headSha.slice(0, 12)}: ${e.reason}`),
|
|
229
|
+
...ledger.summary,
|
|
230
|
+
];
|
|
231
|
+
const measured = ledger.groups.filter((g) => g.state === "MEASURED" && g.provenance === "exact");
|
|
232
|
+
return {
|
|
233
|
+
...ledger,
|
|
234
|
+
verdict,
|
|
235
|
+
summary,
|
|
236
|
+
release: {
|
|
237
|
+
finalSha: input.finalSha,
|
|
238
|
+
contractErrors,
|
|
239
|
+
crossCheck,
|
|
240
|
+
used: used.map((a) => refOf(a.run)),
|
|
241
|
+
excluded,
|
|
242
|
+
required,
|
|
243
|
+
denominator: {
|
|
244
|
+
groups: ledger.counts.groups.total,
|
|
245
|
+
claims: ledger.counts.claims.total,
|
|
246
|
+
measuredOnFinal: {
|
|
247
|
+
groups: measured.length,
|
|
248
|
+
claims: measured.reduce((n, g) => n + g.claims.length, 0),
|
|
249
|
+
list: measured.map((g) => g.gate.join(" ")),
|
|
250
|
+
},
|
|
251
|
+
inheritedCore: ledger.groups
|
|
252
|
+
.filter((g) => g.state === "INHERITED-UNVERIFIED" && g.inheritKind === "c-core")
|
|
253
|
+
.map((g) => ({
|
|
254
|
+
gate: g.gate.join(" "),
|
|
255
|
+
from: (g.decidedBy && receiptRun.get(g.decidedBy)) || null,
|
|
256
|
+
reason: g.reasons[0],
|
|
257
|
+
})),
|
|
258
|
+
flagged: ledger.groups
|
|
259
|
+
.filter((g) => g.flags.length > 0)
|
|
260
|
+
.map((g) => `${g.gate.join(" ")}: ${g.flags.join("; ")}`),
|
|
261
|
+
unrun: ledger.counts.groups.UNRUN,
|
|
262
|
+
fail: ledger.counts.groups.FAIL,
|
|
263
|
+
},
|
|
264
|
+
provenance: [
|
|
265
|
+
"inputs are whatever the caller supplied; this function does not fetch or authenticate them",
|
|
266
|
+
"node/node_modules identity is the final run's own witness, not recomputed here",
|
|
267
|
+
"no acceptance class is produced: RISK-PENDING awaits a human decision",
|
|
268
|
+
],
|
|
269
|
+
},
|
|
270
|
+
};
|
|
271
|
+
}
|
|
@@ -1252,12 +1252,23 @@ V2_MCP_SRC="$REPO/mcp/entwurf-bridge/src/index.ts"
|
|
|
1252
1252
|
if [ -f "$V2_CONTROL_SRC" ] && grep -q 'ENTWURF_FLAG = "entwurf-control"' "$V2_CONTROL_SRC" && grep -q 'registerFlag(ENTWURF_FLAG' "$V2_CONTROL_SRC"; then
|
|
1253
1253
|
ok "pi-native --entwurf-control flag registered (ENTWURF_FLAG)"
|
|
1254
1254
|
else
|
|
1255
|
-
bad "pi-native --entwurf-control flag not found in entwurf-control.ts — the
|
|
1255
|
+
bad "pi-native --entwurf-control flag not found in entwurf-control.ts — record birth and the MCP registration depend on it"
|
|
1256
1256
|
fi
|
|
1257
|
-
|
|
1258
|
-
|
|
1257
|
+
# #125: a pi session no longer carries native Entwurf tools — the extension registers the compiled
|
|
1258
|
+
# entwurf-bridge with Pi's built-in MCP after record birth. This is STATIC DETECTION only: it proves
|
|
1259
|
+
# the shipped source makes that registration and the compiled entry it points at shipped. It does NOT
|
|
1260
|
+
# prove a running pi spawned the child or that a same-name mcp.json entry did not replace it (Pi's
|
|
1261
|
+
# `/mcp` reports that) — runtime reachability is the MCP verb check below and `doctor-pi-provider`.
|
|
1262
|
+
V2_BRIDGE_DIST="$REPO/mcp/entwurf-bridge/dist/mcp/entwurf-bridge/src/index.js"
|
|
1263
|
+
if [ -f "$V2_CONTROL_SRC" ] && grep -q 'pi.registerMcpServer(' "$V2_CONTROL_SRC" && grep -q 'ENTWURF_MCP_SERVER_NAME' "$V2_CONTROL_SRC"; then
|
|
1264
|
+
ok "pi-native: entwurf-control registers the entwurf-bridge MCP server (static detection)"
|
|
1259
1265
|
else
|
|
1260
|
-
bad "pi-native
|
|
1266
|
+
bad "pi-native: entwurf-control.ts does not register the entwurf-bridge MCP server — a pi session would have no Entwurf verbs"
|
|
1267
|
+
fi
|
|
1268
|
+
if [ -f "$V2_BRIDGE_DIST" ]; then
|
|
1269
|
+
ok "pi-native: compiled entwurf-bridge entry present (the path the registration points at)"
|
|
1270
|
+
else
|
|
1271
|
+
bad "pi-native: compiled entwurf-bridge entry missing at $V2_BRIDGE_DIST — run: pnpm run build-bridge"
|
|
1261
1272
|
fi
|
|
1262
1273
|
if [ -f "$V2_MCP_SRC" ] && grep -q 'server.tool(' "$V2_MCP_SRC" && grep -q '"entwurf_v2"' "$V2_MCP_SRC"; then
|
|
1263
1274
|
ok "MCP entwurf_v2 verb registered"
|
|
@@ -1282,7 +1293,7 @@ case "$REPO" in
|
|
|
1282
1293
|
;;
|
|
1283
1294
|
*)
|
|
1284
1295
|
if [ -f "$V2_SURFACE_SRC" ] && (cd "$REPO" && node --experimental-strip-types scripts/check-entwurf-v2-surface.ts >/dev/null 2>&1); then
|
|
1285
|
-
ok "check-entwurf-v2-surface gate passes (surface adapter +
|
|
1296
|
+
ok "check-entwurf-v2-surface gate passes (surface adapter + MCP wiring + pi receiver)"
|
|
1286
1297
|
else
|
|
1287
1298
|
bad "check-entwurf-v2-surface gate FAILED or surface adapter missing — run: ./run.sh check-entwurf-v2-surface"
|
|
1288
1299
|
fi
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"lane": "acp-foreground-only",
|
|
4
|
+
"mutants": [
|
|
5
|
+
{
|
|
6
|
+
"claim": "ACP-CLAUDE-FOREGROUND-ONLY",
|
|
7
|
+
"title": "the claude launch env loses the background-task switch — a backgrounded command outlives an ACP cancel and is orphaned to init when the child exits",
|
|
8
|
+
"subject": "pi-extensions/lib/acp/backend-adapter.ts",
|
|
9
|
+
"find": ["\t\treturn { ...claudeLaunchEnvDefaults(), ...CLAUDE_FOREGROUND_ONLY_ENV };"],
|
|
10
|
+
"replace": ["\t\treturn claudeLaunchEnvDefaults();"],
|
|
11
|
+
"gate": ["bash", "run.sh", "check-acp-foreground-only"],
|
|
12
|
+
"timeoutSeconds": 60,
|
|
13
|
+
"signature": "[QK:ACP-CLAUDE-FOREGROUND-ONLY]",
|
|
14
|
+
"signatureSource": "pi-extensions/lib/acp/backend-adapter.test.ts"
|
|
15
|
+
}
|
|
16
|
+
]
|
|
17
|
+
}
|
|
@@ -393,17 +393,6 @@
|
|
|
393
393
|
"signature": "[QK:FRESHCALL-CODEX-PREMUTATION-MCP]",
|
|
394
394
|
"signatureSource": "test/fresh-call-surfaces.contract.test.ts"
|
|
395
395
|
},
|
|
396
|
-
{
|
|
397
|
-
"claim": "FRESHCALL-CODEX-PREMUTATION-PI",
|
|
398
|
-
"title": "the native pi public surface relabels a Codex request as pi at the shared dispatcher seam, bypassing the same central preflight from the other public door",
|
|
399
|
-
"subject": "pi-extensions/entwurf-control.ts",
|
|
400
|
-
"find": ["\t\t\t\t\tbackend: params.backend,"],
|
|
401
|
-
"replace": ["\t\t\t\t\tbackend: \"pi\","],
|
|
402
|
-
"gate": ["bash", "run.sh", "check-mux-fresh-call"],
|
|
403
|
-
"timeoutSeconds": 180,
|
|
404
|
-
"signature": "[QK:FRESHCALL-CODEX-PREMUTATION-PI]",
|
|
405
|
-
"signatureSource": "test/fresh-call-surfaces.contract.test.ts"
|
|
406
|
-
},
|
|
407
396
|
{
|
|
408
397
|
"claim": "CODEX-BIRTH-DOCTOR-REQUIRES-TRUST",
|
|
409
398
|
"title": "the doctor treats the vendor trust receipt as optional, so a declared hook the vendor will never run reports green",
|
|
@@ -648,6 +637,17 @@
|
|
|
648
637
|
"signature": "[QK:CODEX-LIVE-REPORT-TOKEN-EXACT]",
|
|
649
638
|
"signatureSource": "test/codex-fresh-live-protocol.test.ts"
|
|
650
639
|
},
|
|
640
|
+
{
|
|
641
|
+
"claim": "CODEX-LIVE-PI-MCP-SOURCE-DIALECT",
|
|
642
|
+
"title": "Pi's current qualified fresh-call receipt no longer reaches semantic launch and cleanup authority, recreating the Pi 1.0 timeout",
|
|
643
|
+
"subject": "scripts/lib/codex-fresh-source-receipts.ts",
|
|
644
|
+
"find": ["\t[\"mcp__entwurf_bridge__entwurf_fresh_call\", \"entwurf_fresh_call\"],"],
|
|
645
|
+
"replace": ["\t[\"mcp__entwurf_bridge__entwurf_fresh_call\", \"unrecognized_pi_fresh_call\"],"],
|
|
646
|
+
"gate": ["bash", "run.sh", "check-mux-fresh-call"],
|
|
647
|
+
"timeoutSeconds": 180,
|
|
648
|
+
"signature": "[QK:CODEX-LIVE-PI-MCP-SOURCE-DIALECT]",
|
|
649
|
+
"signatureSource": "test/codex-fresh-live-protocol.test.ts"
|
|
650
|
+
},
|
|
651
651
|
{
|
|
652
652
|
"claim": "CODEX-LIVE-SOURCE-JOIN",
|
|
653
653
|
"title": "a Pi tool result can be joined to a call whose tool name differs, turning an unrelated result body into launch authority",
|
|
@@ -174,14 +174,15 @@
|
|
|
174
174
|
"gate": ["bash", "run.sh", "check-tests-beside-behavior"],
|
|
175
175
|
"timeoutSeconds": 600,
|
|
176
176
|
"signature": "[QK:VITEST-TEST-DIR-ON-THE-FLOOR]",
|
|
177
|
-
"signatureSource": "run.sh"
|
|
177
|
+
"signatureSource": "run.sh",
|
|
178
|
+
"build": { "argv": ["bash", "scripts/build-bridge.sh"], "output": "mcp/entwurf-bridge/dist" }
|
|
178
179
|
},
|
|
179
180
|
{
|
|
180
181
|
"claim": "PI-CORE-PIN-TRACKS-FLOOR",
|
|
181
182
|
"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",
|
|
182
183
|
"subject": "package.json",
|
|
183
|
-
"find": ["\"@earendil-works/pi-agent-core\": \"0.
|
|
184
|
-
"replace": ["\"@earendil-works/pi-agent-core\": \"0.
|
|
184
|
+
"find": ["\"@earendil-works/pi-agent-core\": \"1.0.0\","],
|
|
185
|
+
"replace": ["\"@earendil-works/pi-agent-core\": \"0.87.1\","],
|
|
185
186
|
"gate": ["bash", "run.sh", "check-dep-versions"],
|
|
186
187
|
"timeoutSeconds": 300,
|
|
187
188
|
"signature": "[QK:PI-CORE-PIN-TRACKS-FLOOR]",
|
|
@@ -218,7 +219,8 @@
|
|
|
218
219
|
"gate": ["bash", "run.sh", "check-tests-beside-behavior"],
|
|
219
220
|
"timeoutSeconds": 600,
|
|
220
221
|
"signature": "[QK:VITEST-TEST-DIR-HAS-COORDINATE]",
|
|
221
|
-
"signatureSource": "run.sh"
|
|
222
|
+
"signatureSource": "run.sh",
|
|
223
|
+
"build": { "argv": ["bash", "scripts/build-bridge.sh"], "output": "mcp/entwurf-bridge/dist" }
|
|
222
224
|
},
|
|
223
225
|
{
|
|
224
226
|
"claim": "SEND-OUTCOME-LIST-NAMES-EVERY-RAIL",
|
|
@@ -101,7 +101,8 @@
|
|
|
101
101
|
"gate": ["bash", "run.sh", "check-meta-doctor-oracle"],
|
|
102
102
|
"timeoutSeconds": 600,
|
|
103
103
|
"signature": "[QK:HOOK-LOG-RAIL-SCOPED]",
|
|
104
|
-
"signatureSource": "scripts/check-meta-doctor-oracle.sh"
|
|
104
|
+
"signatureSource": "scripts/check-meta-doctor-oracle.sh",
|
|
105
|
+
"build": { "argv": ["bash", "scripts/build-bridge.sh"], "output": "mcp/entwurf-bridge/dist" }
|
|
105
106
|
},
|
|
106
107
|
{
|
|
107
108
|
"claim": "COPILOT-BIRTH-DOES-NOT-ARM-RECEIVER",
|
|
@@ -75,8 +75,8 @@
|
|
|
75
75
|
"signatureSource": "scripts/check-fresh-call-dispatch.ts"
|
|
76
76
|
},
|
|
77
77
|
{
|
|
78
|
-
"claim": "FCD-SURFACE-
|
|
79
|
-
"title": "the
|
|
78
|
+
"claim": "FCD-SURFACE-ROOT-ONLY",
|
|
79
|
+
"title": "the one public fresh-call surface goes back to choosing a rail itself instead of reaching the composition root, so rail choice lives outside the root again",
|
|
80
80
|
"subject": "mcp/entwurf-bridge/src/index.ts",
|
|
81
81
|
"find": [
|
|
82
82
|
"\t\t\tconst dispatched = await dispatchFreshCall({\n\t\t\t\tbackend,\n\t\t\t\tmodel,\n\t\t\t\ttask,\n\t\t\t\tcwd,\n\t\t\t\tplacement,\n\t\t\t\tcallerGardenId,\n\t\t\t\tcallerNativeSessionId,\n\t\t\t\tcallerCwd,\n\t\t\t});"
|
|
@@ -86,18 +86,7 @@
|
|
|
86
86
|
],
|
|
87
87
|
"gate": ["bash", "run.sh", "check-fresh-call-dispatch"],
|
|
88
88
|
"timeoutSeconds": 120,
|
|
89
|
-
"signature": "[QK:FCD-SURFACE-
|
|
90
|
-
"signatureSource": "scripts/check-fresh-call-dispatch.ts"
|
|
91
|
-
},
|
|
92
|
-
{
|
|
93
|
-
"claim": "FCD-PI-DYNAMIC-IMPORT",
|
|
94
|
-
"title": "pi reaches the composition root by a literal path again, which is how the lazy import fence erodes",
|
|
95
|
-
"subject": "pi-extensions/entwurf-control.ts",
|
|
96
|
-
"find": ["const FRESH_CALL_DISPATCH_MODULE = \"./lib/fresh-call-dispatch.ts\";"],
|
|
97
|
-
"replace": ["const FRESH_CALL_DISPATCH_MODULE = \"./lib/fresh-call-dispatch-renamed.ts\";"],
|
|
98
|
-
"gate": ["bash", "run.sh", "check-fresh-call-dispatch"],
|
|
99
|
-
"timeoutSeconds": 120,
|
|
100
|
-
"signature": "[QK:FCD-PI-DYNAMIC-IMPORT]",
|
|
89
|
+
"signature": "[QK:FCD-SURFACE-ROOT-ONLY]",
|
|
101
90
|
"signatureSource": "scripts/check-fresh-call-dispatch.ts"
|
|
102
91
|
},
|
|
103
92
|
{
|
|
@@ -23,6 +23,65 @@
|
|
|
23
23
|
"timeoutSeconds": 120,
|
|
24
24
|
"signature": "[QK:LANE-INVENTORY-DECLARED]",
|
|
25
25
|
"signatureSource": "scripts/check-gate-qualification.ts"
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"claim": "QUALIFY-BUILD-TRACKS-MUTATION",
|
|
29
|
+
"title": "the runner keeps the baseline build output across a mutation instead of rebuilding it from the mutated bytes — a gate that reads only the compiled output judges source it was never handed, and every mutant planted in the build's source survives (#125: the compiled entwurf-bridge a pi citizen registers)",
|
|
30
|
+
"subject": "scripts/lib/mutation-qualify.ts",
|
|
31
|
+
"find": ["\t\t\t\t\tconst b = await buildOutput(build, outputAbs, m.timeoutSeconds);"],
|
|
32
|
+
"replace": ["\t\t\t\t\tconst b = { run: pre, digest: baseline };"],
|
|
33
|
+
"gate": ["bash", "run.sh", "check-gate-manifests"],
|
|
34
|
+
"timeoutSeconds": 120,
|
|
35
|
+
"signature": "[QK:QUALIFY-BUILD-TRACKS-MUTATION]",
|
|
36
|
+
"signatureSource": "scripts/check-gate-qualification.ts"
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"claim": "QUALIFY-BUILD-RED-NOT-A-KILL",
|
|
40
|
+
"title": "a mutation that breaks the declared build is attributed like a gate run — the build's own failure output is read for the claim token, so a broken build is certified as the gate killing the defect",
|
|
41
|
+
"subject": "scripts/lib/mutation-qualify.ts",
|
|
42
|
+
"find": [
|
|
43
|
+
"\t\t\t\tsignatureOnFailureLine: !buildRed && signatureAttributedToFailure(run.output, m.signature, run.failedTitles),"
|
|
44
|
+
],
|
|
45
|
+
"replace": [
|
|
46
|
+
"\t\t\t\tsignatureOnFailureLine: signatureAttributedToFailure(run.output, m.signature, run.failedTitles),"
|
|
47
|
+
],
|
|
48
|
+
"gate": ["bash", "run.sh", "check-gate-manifests"],
|
|
49
|
+
"timeoutSeconds": 120,
|
|
50
|
+
"signature": "[QK:QUALIFY-BUILD-RED-NOT-A-KILL]",
|
|
51
|
+
"signatureSource": "scripts/check-gate-qualification.ts"
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
"claim": "QUALIFY-BUILD-SCOPED-TO-GROUP",
|
|
55
|
+
"title": "the declared build output outlives its group — a later group runs against an input it never declared, and the ignored output is left in the snapshot",
|
|
56
|
+
"subject": "scripts/lib/mutation-qualify.ts",
|
|
57
|
+
"find": ["\t\t\tif (outputAbs) fs.rmSync(outputAbs, { recursive: true, force: true });"],
|
|
58
|
+
"replace": ["\t\t\tif (outputAbs) void 0;"],
|
|
59
|
+
"gate": ["bash", "run.sh", "check-gate-manifests"],
|
|
60
|
+
"timeoutSeconds": 120,
|
|
61
|
+
"signature": "[QK:QUALIFY-BUILD-SCOPED-TO-GROUP]",
|
|
62
|
+
"signatureSource": "scripts/check-gate-qualification.ts"
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
"claim": "QUALIFY-BUILD-OUTPUT-GUARDED",
|
|
66
|
+
"title": "a gate that writes into its declared build output keeps its KILLED verdict — the next run is judged against bytes no build produced",
|
|
67
|
+
"subject": "scripts/lib/mutation-qualify.ts",
|
|
68
|
+
"find": ["\t\t\t\t\t\toutputWritten = wroteOutput(b.digest);"],
|
|
69
|
+
"replace": ["\t\t\t\t\t\toutputWritten = false;"],
|
|
70
|
+
"gate": ["bash", "run.sh", "check-gate-manifests"],
|
|
71
|
+
"timeoutSeconds": 120,
|
|
72
|
+
"signature": "[QK:QUALIFY-BUILD-OUTPUT-GUARDED]",
|
|
73
|
+
"signatureSource": "scripts/check-gate-qualification.ts"
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
"claim": "QUALIFY-BUILD-GROUP-CONSISTENT",
|
|
77
|
+
"title": "mutants of one gate may declare different builds (or one none) — the same gate is handed different inputs depending on run order, and a group's control no longer describes all of its mutants",
|
|
78
|
+
"subject": "scripts/lib/mutation-qualify.ts",
|
|
79
|
+
"find": ["\t\tif (mine !== first) {"],
|
|
80
|
+
"replace": ["\t\tif (mine !== first && false) {"],
|
|
81
|
+
"gate": ["bash", "run.sh", "check-gate-manifests"],
|
|
82
|
+
"timeoutSeconds": 120,
|
|
83
|
+
"signature": "[QK:QUALIFY-BUILD-GROUP-CONSISTENT]",
|
|
84
|
+
"signatureSource": "scripts/check-gate-qualification.ts"
|
|
26
85
|
}
|
|
27
86
|
]
|
|
28
87
|
}
|
|
@@ -121,8 +121,8 @@
|
|
|
121
121
|
"signatureSource": "test/mux-fresh-call.test.ts"
|
|
122
122
|
},
|
|
123
123
|
{
|
|
124
|
-
"claim": "FRESHCALL-CWD-SURFACE
|
|
125
|
-
"title": "
|
|
124
|
+
"claim": "FRESHCALL-CWD-SURFACE",
|
|
125
|
+
"title": "the fresh-call surface drops the optional cwd from its schema, so cross-repo fresh silently cannot exist from any caller — pi included, which reaches this bridge since #125",
|
|
126
126
|
"subject": "mcp/entwurf-bridge/src/index.ts",
|
|
127
127
|
"find": [
|
|
128
128
|
"\t\tcwd: z\n\t\t\t.string()\n\t\t\t.optional()\n\t\t\t.describe(\n\t\t\t\t\"Optional literal ABSOLUTE path of an existing directory to start the sibling in (cross-repo fresh). Omit or pass \\\"\\\" to start where the CALLER is — this agent's own cwd on BOTH rails, or for a Codex caller its own record directory, because this bridge is the app-server's child and that process's directory is not the caller's. Taken exactly as given: no trim, no realpath, no project-name resolution. '#' is refused on the tmux rail only, because tmux format-expands a start directory; inside herdr it is an ordinary path character. The receipt echoes the directory that was REQUESTED or the caller record it came from, never an observation of where the pane landed.\",\n\t\t\t),\n"
|
|
@@ -130,31 +130,7 @@
|
|
|
130
130
|
"replace": [""],
|
|
131
131
|
"gate": ["bash", "run.sh", "check-mux-fresh-call"],
|
|
132
132
|
"timeoutSeconds": 120,
|
|
133
|
-
"signature": "[QK:FRESHCALL-CWD-SURFACE
|
|
134
|
-
"signatureSource": "test/fresh-call-surfaces.contract.test.ts"
|
|
135
|
-
},
|
|
136
|
-
{
|
|
137
|
-
"claim": "FRESHCALL-PI-SURFACE-IDENTITY",
|
|
138
|
-
"title": "native pi takes the caller garden id from the environment instead of its own resident closure — the uuidv7 confusion, reintroduced",
|
|
139
|
-
"subject": "pi-extensions/entwurf-control.ts",
|
|
140
|
-
"find": ["\t\t\t\t\tcallerGardenId: residentGardenId,"],
|
|
141
|
-
"replace": ["\t\t\t\t\tcallerGardenId: process.env.PI_SESSION_ID ?? null,"],
|
|
142
|
-
"gate": ["bash", "run.sh", "check-mux-fresh-call"],
|
|
143
|
-
"timeoutSeconds": 120,
|
|
144
|
-
"signature": "[QK:FRESHCALL-PI-SURFACE-IDENTITY]",
|
|
145
|
-
"signatureSource": "test/fresh-call-surfaces.contract.test.ts"
|
|
146
|
-
},
|
|
147
|
-
{
|
|
148
|
-
"claim": "FRESHCALL-PI-MODEL-SCHEMA",
|
|
149
|
-
"title": "native pi drops the required model field from fresh-call while the composition still expects one",
|
|
150
|
-
"subject": "pi-extensions/entwurf-control.ts",
|
|
151
|
-
"find": [
|
|
152
|
-
"\t\t\tmodel: Type.String({\n\t\t\t\tminLength: 1,\n\t\t\t\tmaxLength: 200,\n\t\t\t\tpattern: \"^[A-Za-z0-9][A-Za-z0-9._/:\\\\[\\\\]-]*$\",\n\t\t\t\tdescription:\n\t\t\t\t\t\"Required runtime model: canonical provider/model for pi, a Claude Code model id/alias, or a Copilot/OMP/Codex model name.\",\n\t\t\t}),\n"
|
|
153
|
-
],
|
|
154
|
-
"replace": [""],
|
|
155
|
-
"gate": ["bash", "run.sh", "check-mux-fresh-call"],
|
|
156
|
-
"timeoutSeconds": 120,
|
|
157
|
-
"signature": "[QK:FRESHCALL-PI-MODEL-SCHEMA]",
|
|
133
|
+
"signature": "[QK:FRESHCALL-CWD-SURFACE]",
|
|
158
134
|
"signatureSource": "test/fresh-call-surfaces.contract.test.ts"
|
|
159
135
|
},
|
|
160
136
|
{
|
|
@@ -196,10 +172,10 @@
|
|
|
196
172
|
},
|
|
197
173
|
{
|
|
198
174
|
"claim": "FRESHCALL-MODEL-PATTERN-PROVIDER-VALID",
|
|
199
|
-
"title": "the
|
|
200
|
-
"subject": "
|
|
201
|
-
"find": ["\t\t\t
|
|
202
|
-
"replace": ["\t\t\t
|
|
175
|
+
"title": "the #62 unescape planted on the bridge's zod pattern rides Pi's built-in MCP spread and pi-ai's real anthropic-messages conversion into the request body as an invalid Rust-family pattern",
|
|
176
|
+
"subject": "mcp/entwurf-bridge/src/index.ts",
|
|
177
|
+
"find": ["\t\t\t.regex(/^[A-Za-z0-9][A-Za-z0-9._/:\\[\\]-]*$/)"],
|
|
178
|
+
"replace": ["\t\t\t.regex(/^[A-Za-z0-9][A-Za-z0-9._/:[\\]-]*$/)"],
|
|
203
179
|
"gate": ["bash", "run.sh", "check-mux-fresh-call"],
|
|
204
180
|
"timeoutSeconds": 120,
|
|
205
181
|
"signature": "[QK:FRESHCALL-MODEL-PATTERN-PROVIDER-VALID]",
|
|
@@ -284,6 +260,39 @@
|
|
|
284
260
|
"signature": "[QK:FRESHCALL-COPILOT-CALLBACK-TOOL]",
|
|
285
261
|
"signatureSource": "test/mux-fresh-call.test.ts"
|
|
286
262
|
},
|
|
263
|
+
{
|
|
264
|
+
"claim": "FRESHCALL-PI-CALLBACK-DIALECT",
|
|
265
|
+
"title": "pi callback rolls back to the 0.99.1 hyphen spelling, which a 0.99.2 host no longer declares — the first turn is spent on a tool that is not there",
|
|
266
|
+
"subject": "pi-extensions/lib/fresh-call-composition.ts",
|
|
267
|
+
"find": ["\tpi: \"mcp__entwurf_bridge__entwurf_callback\","],
|
|
268
|
+
"replace": ["\tpi: \"mcp__entwurf-bridge__entwurf_callback\","],
|
|
269
|
+
"gate": ["bash", "run.sh", "check-mux-fresh-call"],
|
|
270
|
+
"timeoutSeconds": 180,
|
|
271
|
+
"signature": "[QK:FRESHCALL-PI-CALLBACK-DIALECT]",
|
|
272
|
+
"signatureSource": "test/mux-fresh-call.test.ts"
|
|
273
|
+
},
|
|
274
|
+
{
|
|
275
|
+
"claim": "FRESHCALL-PI-DELIVERY-DIALECT",
|
|
276
|
+
"title": "pi delivery rolls back to the 0.99.1 hyphen spelling — the result is sent to a tool a 0.99.2 host does not declare",
|
|
277
|
+
"subject": "pi-extensions/lib/fresh-call-composition.ts",
|
|
278
|
+
"find": ["\tpi: \"mcp__entwurf_bridge__entwurf_v2\","],
|
|
279
|
+
"replace": ["\tpi: \"mcp__entwurf-bridge__entwurf_v2\","],
|
|
280
|
+
"gate": ["bash", "run.sh", "check-mux-fresh-call"],
|
|
281
|
+
"timeoutSeconds": 180,
|
|
282
|
+
"signature": "[QK:FRESHCALL-PI-DELIVERY-DIALECT]",
|
|
283
|
+
"signatureSource": "test/mux-fresh-call.test.ts"
|
|
284
|
+
},
|
|
285
|
+
{
|
|
286
|
+
"claim": "FRESHCALL-PI-PEERS-DIALECT",
|
|
287
|
+
"title": "pi peers offer rolls back to the 0.99.1 hyphen spelling — the corroboration tool is named in a spelling the sibling cannot reach",
|
|
288
|
+
"subject": "pi-extensions/lib/fresh-call-composition.ts",
|
|
289
|
+
"find": ["\tpi: \"mcp__entwurf_bridge__entwurf_peers\","],
|
|
290
|
+
"replace": ["\tpi: \"mcp__entwurf-bridge__entwurf_peers\","],
|
|
291
|
+
"gate": ["bash", "run.sh", "check-mux-fresh-call"],
|
|
292
|
+
"timeoutSeconds": 180,
|
|
293
|
+
"signature": "[QK:FRESHCALL-PI-PEERS-DIALECT]",
|
|
294
|
+
"signatureSource": "test/mux-fresh-call.test.ts"
|
|
295
|
+
},
|
|
287
296
|
{
|
|
288
297
|
"claim": "FRESHCALL-COPILOT-PREFLIGHT-PREMUTATION",
|
|
289
298
|
"title": "the capability preflight is removed — a host missing the birth, MCP, receiver or footer unit gets a real window and a launch receipt for a sibling that can never call home",
|
|
@@ -430,17 +439,6 @@
|
|
|
430
439
|
"signature": "[QK:FRESHCALL-COPILOT-EXPLICIT-POLICY]",
|
|
431
440
|
"signatureSource": "test/mux-fresh-call.test.ts"
|
|
432
441
|
},
|
|
433
|
-
{
|
|
434
|
-
"claim": "FRESHCALL-BACKEND-SET-SURFACE-PARITY-PI",
|
|
435
|
-
"title": "a backend the composition can open is missing from the native pi surface enum — an in-process pi session cannot name a capability the module already opens",
|
|
436
|
-
"subject": "pi-extensions/entwurf-control.ts",
|
|
437
|
-
"find": ["\t\t\tbackend: StringEnum([\"pi\", \"claude-code\", \"copilot\", \"omp\", \"codex\"], {"],
|
|
438
|
-
"replace": ["\t\t\tbackend: StringEnum([\"pi\", \"claude-code\", \"copilot\", \"omp\"], {"],
|
|
439
|
-
"gate": ["bash", "run.sh", "check-mux-fresh-call"],
|
|
440
|
-
"timeoutSeconds": 180,
|
|
441
|
-
"signature": "[QK:FRESHCALL-BACKEND-SET-SURFACE-PARITY-PI]",
|
|
442
|
-
"signatureSource": "test/fresh-call-surfaces.contract.test.ts"
|
|
443
|
-
},
|
|
444
442
|
{
|
|
445
443
|
"claim": "TMUXSESSION-LOOKUP-ENGINE",
|
|
446
444
|
"title": "the seat lookup goes back to the `-f` filter engine, which was measured to match EVERY session when the name contains a '}' — a fresh sibling then opens in whichever session tmux listed first",
|
|
@@ -510,8 +508,8 @@
|
|
|
510
508
|
"signatureSource": "test/mux-fresh-call.test.ts"
|
|
511
509
|
},
|
|
512
510
|
{
|
|
513
|
-
"claim": "FRESHCALL-PLACEMENT-SURFACE
|
|
514
|
-
"title": "
|
|
511
|
+
"claim": "FRESHCALL-PLACEMENT-SURFACE",
|
|
512
|
+
"title": "the fresh-call surface drops the optional seat from its schema, so opening a sibling in the operator's project session silently cannot exist from any caller",
|
|
515
513
|
"subject": "mcp/entwurf-bridge/src/index.ts",
|
|
516
514
|
"find": [
|
|
517
515
|
"\t\tplacement: z\n\t\t\t.object({\n\t\t\t\ttmuxSession: z\n\t\t\t\t\t.string()\n\t\t\t\t\t.describe(\n\t\t\t\t\t\t\"EXACT name of an EXISTING session on this agent's own tmux server. Nothing is created: an absent session is refused as tmux-session-missing, and a name outside [A-Za-z0-9][A-Za-z0-9_-]* as tmux-session-name-invalid.\",\n\t\t\t\t\t),\n\t\t\t})\n\t\t\t.optional()\n\t\t\t.describe(\n\t\t\t\t\"Optional expert seat override, TMUX ONLY: open the sibling in ONE EXISTING tmux session of this agent's own server, and it always wins. When omitted the seat follows the CALLER, never the backend being opened: a Codex CALLER opens beside its own TUI pane, matched by thread-id in that pane's terminal title (0 or 2+ matching panes refuse, never fall back); every other caller opens in its own session. A pane title is a placement input only — never an address, liveness or delivery fact. Nothing is ever created. Inside herdr this field is refused by name — placement there belongs to herdr, and a tmux session name would silently place the sibling somewhere else. Independent of cwd; neither is inferred from the other. The receipt reports the selected name (absent for the caller-pane rule, which observed a session rather than requesting a name), its source, and resolved target session id.\",\n\t\t\t),\n"
|
|
@@ -519,7 +517,7 @@
|
|
|
519
517
|
"replace": [""],
|
|
520
518
|
"gate": ["bash", "run.sh", "check-mux-fresh-call"],
|
|
521
519
|
"timeoutSeconds": 180,
|
|
522
|
-
"signature": "[QK:FRESHCALL-PLACEMENT-SURFACE
|
|
520
|
+
"signature": "[QK:FRESHCALL-PLACEMENT-SURFACE]",
|
|
523
521
|
"signatureSource": "test/fresh-call-surfaces.contract.test.ts"
|
|
524
522
|
},
|
|
525
523
|
{
|