@junghanacs/entwurf 0.13.0 → 0.14.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 +52 -17
- package/BASELINE.md +60 -206
- package/CHANGELOG.md +64 -0
- package/CONTRIBUTING.md +14 -10
- package/DELIVERY.md +118 -261
- package/README.md +91 -431
- package/VERIFY.md +74 -95
- package/demo/README.md +1 -1
- package/demo/demo-baseline.sh +1 -3
- package/demo/demo.sh +2 -5
- package/docs/acp-backend-rail.md +236 -0
- package/docs/external-mcp-host.md +131 -0
- package/docs/fresh-cut-policy.md +99 -0
- package/docs/setup-clean-host.md +124 -328
- package/mcp/entwurf-bridge/dist/mcp/entwurf-bridge/src/index.js +160 -67
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/acp-client.js +12 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/backend-adapter.js +11 -10
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/config.js +1 -1
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/engraving.js +42 -1
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/models.js +4 -4
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/overlay.js +11 -9
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-control-rpc.js +7 -5
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-core.js +13 -14
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-resume-args.js +45 -40
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-contract.js +117 -95
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-decider.js +23 -57
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-lock.js +16 -7
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-production.js +5 -53
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-release.js +21 -36
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-runner.js +3 -15
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-send-fallback.js +12 -11
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-send.js +2 -7
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-surface.js +30 -67
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-visible-resume.js +256 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/meta-session.js +91 -5
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/mux-fresh-call.js +258 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/mux-launch.js +202 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/mux-placement.js +289 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/mux-resume-call.js +170 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/resume-launch-identity.js +136 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/session-id.js +8 -5
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/socket-discovery.js +3 -3
- package/mcp/entwurf-bridge/dist/scripts/meta-facts.js +51 -0
- package/mcp/entwurf-bridge/dist/scripts/new-session-id.js +9 -4
- package/mcp/entwurf-bridge/src/index.ts +176 -67
- package/mcp/entwurf-bridge/start.sh +2 -2
- package/mcp/entwurf-bridge/test.sh +23 -9
- package/mcp/entwurf-bridge/tsconfig.build.json +11 -2
- package/package.json +25 -11
- package/pi-extensions/entwurf-control.ts +220 -71
- package/pi-extensions/lib/acp/acp-client.ts +22 -0
- package/pi-extensions/lib/acp/augment.ts +42 -3
- package/pi-extensions/lib/acp/backend-adapter.ts +12 -11
- package/pi-extensions/lib/acp/backend.ts +366 -38
- package/pi-extensions/lib/acp/config.ts +1 -1
- package/pi-extensions/lib/acp/engraving.ts +43 -1
- package/pi-extensions/lib/acp/event-mapper.ts +16 -13
- package/pi-extensions/lib/acp/models.ts +4 -4
- package/pi-extensions/lib/acp/overlay.ts +11 -9
- package/pi-extensions/lib/entwurf-control-rpc.ts +7 -5
- package/pi-extensions/lib/entwurf-core.ts +15 -15
- package/pi-extensions/lib/entwurf-resume-args.ts +41 -52
- package/pi-extensions/lib/entwurf-v2-contract-schema.ts +1 -1
- package/pi-extensions/lib/entwurf-v2-contract.ts +120 -99
- package/pi-extensions/lib/entwurf-v2-decider.ts +30 -91
- package/pi-extensions/lib/entwurf-v2-lock.ts +16 -7
- package/pi-extensions/lib/entwurf-v2-production.ts +4 -78
- package/pi-extensions/lib/entwurf-v2-release.ts +25 -49
- package/pi-extensions/lib/entwurf-v2-runner.ts +6 -21
- package/pi-extensions/lib/entwurf-v2-send-fallback.ts +12 -11
- package/pi-extensions/lib/entwurf-v2-send.ts +2 -7
- package/pi-extensions/lib/entwurf-v2-surface.ts +36 -76
- package/pi-extensions/lib/entwurf-v2-visible-resume.ts +370 -0
- package/pi-extensions/lib/meta-session.ts +93 -5
- package/pi-extensions/lib/mux-fresh-call.ts +328 -0
- package/pi-extensions/lib/mux-launch.ts +267 -0
- package/pi-extensions/lib/mux-placement.ts +387 -0
- package/pi-extensions/lib/mux-resume-call.ts +221 -0
- package/pi-extensions/lib/resume-launch-identity.ts +162 -0
- package/pi-extensions/lib/session-id.js +8 -5
- package/pi-extensions/lib/socket-discovery.ts +3 -3
- package/prompts/engraving.md +13 -5
- package/run.sh +688 -282
- package/scripts/agy-bridge-config.py +5 -1
- package/scripts/check-acp-backend-preflight.ts +1 -1
- package/scripts/check-acp-carrier-augment.ts +246 -10
- package/scripts/check-acp-cortex.ts +5 -5
- package/scripts/check-acp-overlay.ts +13 -3
- package/scripts/check-acp-prompt-lifecycle.ts +565 -0
- package/scripts/check-acp-stop-reason.ts +342 -0
- package/scripts/check-acp-stream-hooks.ts +504 -0
- package/scripts/check-elapsed.sh +25 -0
- package/scripts/check-entwurf-bridge-boot.ts +51 -4
- package/scripts/check-entwurf-bridge-pi-free.ts +6 -5
- package/scripts/check-entwurf-control-rpc.ts +4 -3
- package/scripts/check-entwurf-resume-args.ts +72 -70
- package/scripts/check-entwurf-session-identity.ts +14 -10
- package/scripts/check-entwurf-v2-contract.ts +34 -59
- package/scripts/check-entwurf-v2-decider.ts +17 -177
- package/scripts/check-entwurf-v2-lock.ts +5 -2
- package/scripts/check-entwurf-v2-matrix.ts +3 -53
- package/scripts/check-entwurf-v2-production.ts +2 -91
- package/scripts/check-entwurf-v2-release.ts +10 -105
- package/scripts/check-entwurf-v2-runner.ts +4 -85
- package/scripts/check-entwurf-v2-send-fallback.ts +5 -6
- package/scripts/check-entwurf-v2-send.ts +0 -28
- package/scripts/check-entwurf-v2-surface.ts +198 -128
- package/scripts/check-entwurf-v2-visible-resume.ts +445 -0
- package/scripts/check-fresh-cut-gate.sh +1 -1
- package/scripts/check-gate-qualification.ts +101 -7
- package/scripts/check-install-container.sh +10 -2
- package/scripts/check-install-surface.ts +1 -1
- package/scripts/check-keyset-overlap.py +1 -1
- package/scripts/check-meta-facts.ts +249 -0
- package/scripts/check-meta-identity-consumers.ts +1 -1
- package/scripts/check-meta-session.ts +169 -0
- package/scripts/check-mux-launch-tmux.ts +316 -0
- package/scripts/check-mux-launch.ts +288 -0
- package/scripts/check-mux-launcher-fence.ts +264 -0
- package/scripts/check-mux-parent-artifact.ts +195 -0
- package/scripts/check-mux-placement-tmux.ts +322 -0
- package/scripts/check-mux-placement.ts +323 -0
- package/scripts/check-mux-resume-call.ts +282 -0
- package/scripts/check-probe-cli-shim.ts +25 -22
- package/scripts/check-probe-ordering.ts +121 -81
- package/scripts/check-release-gate-outcomes.ts +452 -0
- package/scripts/check-resume-launch-identity.ts +244 -0
- package/scripts/check-socket-discovery.ts +1 -1
- package/scripts/fixtures/mux-parent-transcript.scrubbed.jsonl +3 -0
- package/scripts/inventory-verification-surface.ts +349 -0
- package/scripts/lib/claude-launcher-fence.ts +322 -0
- package/scripts/lib/live-skip.ts +33 -0
- package/scripts/lib/mutation-qualify.ts +109 -3
- package/scripts/lib/probe-acp-turn.ts +33 -12
- package/scripts/lib/step-outcome.sh +88 -0
- package/scripts/meta-bridge-doctor.sh +6 -8
- package/scripts/meta-facts.ts +60 -0
- package/scripts/mutants/acp-augment.json +106 -0
- package/scripts/mutants/acp-cortex.json +2 -2
- package/scripts/mutants/acp-overlay.json +17 -0
- package/scripts/mutants/acp-prompt-lifecycle.json +100 -0
- package/scripts/mutants/acp-stop-reason.json +80 -0
- package/scripts/mutants/acp-stream-hooks.json +158 -0
- package/scripts/mutants/bridge-boot-resume.json +45 -0
- package/scripts/mutants/meta-facts.json +50 -0
- package/scripts/mutants/meta-identity.json +36 -0
- package/scripts/mutants/mux-boundary.json +196 -0
- package/scripts/mutants/mux-fresh-call.json +185 -0
- package/scripts/mutants/mux-launcher-fence.json +123 -0
- package/scripts/mutants/mux-parent-artifact.json +39 -0
- package/scripts/mutants/mux-resume-call.json +148 -0
- package/scripts/mutants/probe-ordering.json +6 -1021
- package/scripts/mutants/release-gate.json +140 -0
- package/scripts/mutants/resume-args.json +76 -0
- package/scripts/mutants/resume-launch-identity.json +96 -0
- package/scripts/mutants/v2-surface.json +74 -23
- package/scripts/mutants/v2-visible-resume.json +215 -0
- package/scripts/new-session-id.ts +9 -4
- package/scripts/smoke-acp-bundled-mcp-live.ts +4 -3
- package/scripts/smoke-acp-carrier-augment-live.ts +2 -2
- package/scripts/smoke-acp-cortex-live.ts +8 -14
- package/scripts/smoke-acp-long-turn-live.ts +185 -0
- package/scripts/smoke-acp-mcp-live.ts +2 -2
- package/scripts/smoke-acp-memory-containment-live.ts +2 -2
- package/scripts/smoke-acp-ordering-probe-live.ts +2 -2
- package/scripts/smoke-acp-overlay-live.ts +2 -2
- package/scripts/smoke-acp-provider-live.ts +2 -2
- package/scripts/smoke-acp-raw-turn-live.ts +3 -3
- package/scripts/smoke-acp-session-reuse-live.ts +2 -2
- package/scripts/smoke-acp-skill-live.ts +2 -2
- package/scripts/smoke-acp-socket-citizen-live.ts +4 -3
- package/scripts/smoke-acp-v2-send-live.ts +4 -3
- package/scripts/smoke-agy-native-push-live.ts +10 -20
- package/scripts/smoke-claude-native-resume-live.sh +13 -3
- package/scripts/smoke-entwurf-chain-live.ts +352 -0
- package/scripts/smoke-entwurf-v2-matrix-live.ts +3 -3
- package/scripts/smoke-meta-honesty.sh +1 -1
- package/scripts/smoke-meta-install-state.sh +4 -0
- package/scripts/smoke-mux-fresh-call-live.ts +365 -0
- package/scripts/smoke-mux-lifecycle-live.ts +1136 -0
- package/scripts/smoke-pi-attach.ts +1 -1
- package/scripts/smoke-user-scope-citizen.sh +1 -1
- package/scripts/tsconfig.json +1 -0
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-preflight.js +0 -160
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-spawn-production.js +0 -273
- package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-spawn.js +0 -216
- package/pi-extensions/lib/entwurf-v2-spawn-production.ts +0 -373
- package/pi-extensions/lib/entwurf-v2-spawn.ts +0 -323
- package/scripts/check-acp-sdk-surface.ts +0 -236
- package/scripts/check-entwurf-v2-spawn-production.ts +0 -551
- package/scripts/check-entwurf-v2-spawn.ts +0 -399
- package/scripts/smoke-entwurf-v2-spawn-live.ts +0 -188
- package/scripts/smoke-entwurf-v2-spawn-resume-live.ts +0 -469
|
@@ -53,13 +53,15 @@
|
|
|
53
53
|
// channel — 8d pins that wiring.
|
|
54
54
|
//
|
|
55
55
|
// Kill-proof, stated at its honest strength: scripts/mutants/probe-ordering.json
|
|
56
|
-
// qualifies
|
|
57
|
-
//
|
|
58
|
-
//
|
|
59
|
-
//
|
|
60
|
-
//
|
|
61
|
-
//
|
|
62
|
-
//
|
|
56
|
+
// now qualifies exactly ONE claim for THIS gate — the product-subject
|
|
57
|
+
// NO-PRODUCTION-PROMPT-CUTOFF replant, whose [QK:] signature appears EXACTLY
|
|
58
|
+
// once below and whose mutant check-gate-qualification proves dies there.
|
|
59
|
+
// Every [CHECK:<claim>] label below is a DIRECT assertion: enforced on every
|
|
60
|
+
// run, deliberately no longer replant-qualified (issue #70 verification
|
|
61
|
+
// subtraction — the dropped qualification replants are ledgered there; the
|
|
62
|
+
// instrument's assertions and behavior are unchanged). The remaining [QK:]
|
|
63
|
+
// token and its qualified claim stay 1:1, so "killed claim IDs, never
|
|
64
|
+
// assertion counts" stays readable.
|
|
63
65
|
// Properties that are review-pinned only (no deterministic mutant exists):
|
|
64
66
|
// - the write-CALLBACK timing of tools_list_response_forwarded (cross-process
|
|
65
67
|
// microsecond ordering; existence/attribution IS proven);
|
|
@@ -67,7 +69,7 @@
|
|
|
67
69
|
// in this handshake; the callback rejects and never stamps — source-visible);
|
|
68
70
|
// - the writer's SINGLE clock read (a two-read millisecond straddle is a rare
|
|
69
71
|
// race, so it is pinned in source and made loud by the parser's ts↔tsMs
|
|
70
|
-
// equality rule, which
|
|
72
|
+
// equality rule, which is directly asserted below).
|
|
71
73
|
|
|
72
74
|
import { strict as assert } from "node:assert";
|
|
73
75
|
import { type ChildProcessByStdio, spawn } from "node:child_process";
|
|
@@ -94,6 +96,7 @@ import { terminateChild } from "./lib/acp-child-cleanup.ts";
|
|
|
94
96
|
import {
|
|
95
97
|
driveProbeTurn,
|
|
96
98
|
PROBE_PHASE_TIMEOUTS,
|
|
99
|
+
PROBE_PROMPT_OBSERVATION_MS,
|
|
97
100
|
type ProbeAdapterSeam,
|
|
98
101
|
type ProbeMcpEnricher,
|
|
99
102
|
ProbePhaseError,
|
|
@@ -237,7 +240,7 @@ function makeRecordingConnection(calls: RecordedCall[]): AcpConnectionLike {
|
|
|
237
240
|
clientCapabilities: {},
|
|
238
241
|
clientInfo: { name: "entwurf", version: "s2d" },
|
|
239
242
|
},
|
|
240
|
-
"initialize params match backend.ts byte-shape [
|
|
243
|
+
"initialize params match backend.ts byte-shape [CHECK:PROBE-SEQ-ORDER]",
|
|
241
244
|
);
|
|
242
245
|
const newSessionParams = calls[1].params as { cwd: string; mcpServers: unknown; _meta?: unknown };
|
|
243
246
|
assert.equal(newSessionParams.cwd, "/scratch/gate", "newSession carries the run cwd");
|
|
@@ -290,7 +293,7 @@ function makeRecordingConnection(calls: RecordedCall[]): AcpConnectionLike {
|
|
|
290
293
|
`set-model start/end are marked between newSession end and prompt start (got ${eventOrder.join(",")})`,
|
|
291
294
|
);
|
|
292
295
|
|
|
293
|
-
//
|
|
296
|
+
// Carrier-less shape: an undefined session meta omits the `_meta` KEY entirely.
|
|
294
297
|
const calls2: RecordedCall[] = [];
|
|
295
298
|
await driveProbeTurn(makeRecordingConnection(calls2), {
|
|
296
299
|
cwd: "/scratch/gate2",
|
|
@@ -305,7 +308,7 @@ function makeRecordingConnection(calls: RecordedCall[]): AcpConnectionLike {
|
|
|
305
308
|
});
|
|
306
309
|
assert.ok(
|
|
307
310
|
!Object.hasOwn(calls2.find((c) => c.method === "newSession")?.params as object, "_meta"),
|
|
308
|
-
"undefined session meta omits the _meta KEY entirely (
|
|
311
|
+
"undefined session meta omits the _meta KEY entirely (carrier-less shape)",
|
|
309
312
|
);
|
|
310
313
|
|
|
311
314
|
// --- production source pins ---------------------------------------------
|
|
@@ -319,15 +322,37 @@ function makeRecordingConnection(calls: RecordedCall[]): AcpConnectionLike {
|
|
|
319
322
|
initializeMs: PROBE_PHASE_TIMEOUTS.initializeMs,
|
|
320
323
|
newSessionMs: PROBE_PHASE_TIMEOUTS.newSessionMs,
|
|
321
324
|
setModelMs: PROBE_PHASE_TIMEOUTS.setModelMs,
|
|
322
|
-
promptMs: PROBE_PHASE_TIMEOUTS.promptMs,
|
|
323
325
|
},
|
|
324
326
|
{
|
|
325
327
|
initializeMs: timeoutOf("INITIALIZE_TIMEOUT_MS"),
|
|
326
328
|
newSessionMs: timeoutOf("NEW_SESSION_TIMEOUT_MS"),
|
|
327
329
|
setModelMs: timeoutOf("SET_MODEL_TIMEOUT_MS"),
|
|
328
|
-
promptMs: timeoutOf("PROMPT_TIMEOUT_MS"),
|
|
329
330
|
},
|
|
330
|
-
"probe
|
|
331
|
+
"probe BOOTSTRAP timeouts EQUAL backend.ts's — a bootstrap-phase D is only readable against production " +
|
|
332
|
+
"boundaries [CHECK:PROBE-BOOTSTRAP-TIMEOUTS-MATCH-PRODUCTION]",
|
|
333
|
+
);
|
|
334
|
+
|
|
335
|
+
// --- the prompt phase has NO production boundary to match --------------
|
|
336
|
+
//
|
|
337
|
+
// backend.ts ends a prompt on lifecycle events only. The probe still needs a
|
|
338
|
+
// bounded observation horizon so a measurement RUN cannot hang forever, but
|
|
339
|
+
// that horizon is the harness's own: pinning it to a production number (or
|
|
340
|
+
// letting production grow one back) would make the probe report a
|
|
341
|
+
// harness-invented cutoff as if it were the turn contract.
|
|
342
|
+
const promptCutoffTraces = [/\bPROMPT_TIMEOUT_MS\b/, /withTimeout\(\s*\n?\s*"prompt"/].filter((re) =>
|
|
343
|
+
re.test(BACKEND_SRC),
|
|
344
|
+
);
|
|
345
|
+
assert.equal(
|
|
346
|
+
promptCutoffTraces.length,
|
|
347
|
+
0,
|
|
348
|
+
"[QK:NO-PRODUCTION-PROMPT-CUTOFF] backend.ts carries NO prompt wall-clock — neither a PROMPT_TIMEOUT_MS constant " +
|
|
349
|
+
'nor a withTimeout("prompt", …) race. A running turn is not a failed turn for being long, and the old 600s ' +
|
|
350
|
+
`cutoff also fed pi's transient-retry dictionary. Found: ${promptCutoffTraces.join(", ")}`,
|
|
351
|
+
);
|
|
352
|
+
assert.ok(
|
|
353
|
+
PROBE_PROMPT_OBSERVATION_MS > 0 && !("promptMs" in PROBE_PHASE_TIMEOUTS),
|
|
354
|
+
"[CHECK:PROMPT-HORIZON-NOT-PRODUCTION] the prompt horizon lives OUTSIDE the production-pinned bootstrap set — it is " +
|
|
355
|
+
"the harness's own observation bound, and folding it back in would report a harness number as the turn contract",
|
|
331
356
|
);
|
|
332
357
|
|
|
333
358
|
const idx = (needle: string): number => {
|
|
@@ -338,11 +363,19 @@ function makeRecordingConnection(calls: RecordedCall[]): AcpConnectionLike {
|
|
|
338
363
|
const iInit = idx("connection.initialize({");
|
|
339
364
|
const iNew = idx("connection.newSession(newSessionArgs)");
|
|
340
365
|
const iEnforce = idx("adapter.enforceModel({");
|
|
341
|
-
|
|
366
|
+
// The prompt no longer sits inline: runNewTurn hands the wire call to
|
|
367
|
+
// awaitAcpPromptTurn (the lifecycle-bounded driver), so the ordering pin
|
|
368
|
+
// follows the dispatch, and the driver's own `session.connection.prompt(...)`
|
|
369
|
+
// is the single place the wire call lives.
|
|
370
|
+
const iPrompt = BACKEND_SRC.indexOf("await awaitAcpPromptTurn(", iEnforce);
|
|
342
371
|
assert.ok(
|
|
343
372
|
iInit < iNew && iNew < iEnforce && iEnforce < iPrompt && iPrompt !== -1,
|
|
344
373
|
"backend.ts runNewTurn keeps initialize → newSession → enforceModel → prompt; the probe mirrors THIS sequence",
|
|
345
374
|
);
|
|
375
|
+
assert.ok(
|
|
376
|
+
BACKEND_SRC.includes("await Promise.race([session.connection.prompt(promptArgs), lifecycle])"),
|
|
377
|
+
"the prompt driver races the wire call against LIFECYCLE endings only — the probe mirrors that same wire call",
|
|
378
|
+
);
|
|
346
379
|
assert.ok(
|
|
347
380
|
BACKEND_SRC.includes('clientInfo: { name: "entwurf", version: "s2d" }'),
|
|
348
381
|
"backend.ts still sends the clientInfo the probe mirrors",
|
|
@@ -390,7 +423,7 @@ function makeRecordingConnection(calls: RecordedCall[]): AcpConnectionLike {
|
|
|
390
423
|
const scanSites = RUNNER_SRC.split("scanStructuredNoSuchTool").length - 1;
|
|
391
424
|
assert.ok(
|
|
392
425
|
scanSites === 2 && !/collectedText \+= t;[^}]*scanStructured/s.test(RUNNER_SRC),
|
|
393
|
-
"No-such-tool is scanned ONLY off structured tool frames, never agent prose [
|
|
426
|
+
"No-such-tool is scanned ONLY off structured tool frames, never agent prose [CHECK:PROBE-NO-PROSE-ERROR-SCAN]",
|
|
394
427
|
);
|
|
395
428
|
|
|
396
429
|
// A broken event log is run-invalidating on BOTH doors: a malformed line could
|
|
@@ -408,7 +441,7 @@ function makeRecordingConnection(calls: RecordedCall[]): AcpConnectionLike {
|
|
|
408
441
|
// "deadline-sufficient". It is a constant, and no env may reach it.
|
|
409
442
|
assert.ok(
|
|
410
443
|
RUNNER_SRC.includes("const POST_DELAY_SLACK_MS = 5_000;") && !RUNNER_SRC.includes("PROBE_POST_DELAY_SLACK_MS"),
|
|
411
|
-
"the observation window's post-delay slack is a CONSTANT with no env override [
|
|
444
|
+
"the observation window's post-delay slack is a CONSTANT with no env override [CHECK:PROBE-WINDOW-SLACK-IS-CONSTANT]",
|
|
412
445
|
);
|
|
413
446
|
|
|
414
447
|
// The exit contract asks three separate questions. Failing the run on the
|
|
@@ -419,14 +452,14 @@ function makeRecordingConnection(calls: RecordedCall[]): AcpConnectionLike {
|
|
|
419
452
|
RUNNER_SRC.includes("do not read it as a complete series") &&
|
|
420
453
|
RUNNER_SRC.includes('classification.status.orderingMeasurement !== "measured"') &&
|
|
421
454
|
RUNNER_SRC.includes("NOT a claim about server wait behavior"),
|
|
422
|
-
"the runner's exit contract separates fatal validity from the two axes, and claims no server-wait conclusion [
|
|
455
|
+
"the runner's exit contract separates fatal validity from the two axes, and claims no server-wait conclusion [CHECK:RUNNER-EXIT-CONTRACT-SPLIT]",
|
|
423
456
|
);
|
|
424
457
|
|
|
425
458
|
assert.ok(
|
|
426
459
|
RUNNER_SRC.includes("if (malformed.length > 0 || sequenceViolations.length > 0) {") &&
|
|
427
460
|
RUNNER_SRC.includes("run INVALIDATED") &&
|
|
428
461
|
RUNNER_SRC.includes('verdict: "INVALIDATED"'),
|
|
429
|
-
"the runner refuses to judge a log with malformed lines OR per-writer order violations, and writes an INVALIDATED classification [
|
|
462
|
+
"the runner refuses to judge a log with malformed lines OR per-writer order violations, and writes an INVALIDATED classification [CHECK:PROBE-MALFORMED-INVALIDATES]",
|
|
430
463
|
);
|
|
431
464
|
}
|
|
432
465
|
|
|
@@ -490,7 +523,8 @@ function makeRecordingConnection(calls: RecordedCall[]): AcpConnectionLike {
|
|
|
490
523
|
await driveProbeTurn(makeRecordingConnection(calls), {
|
|
491
524
|
...base,
|
|
492
525
|
adapter: { buildSessionMeta: () => undefined, enforceModel: () => new Promise<never>(() => {}) },
|
|
493
|
-
timeouts: { initializeMs: 1000, newSessionMs: 1000, setModelMs: 50
|
|
526
|
+
timeouts: { initializeMs: 1000, newSessionMs: 1000, setModelMs: 50 },
|
|
527
|
+
promptObservationMs: 1000,
|
|
494
528
|
});
|
|
495
529
|
assert.fail("hung set-model must time the turn out");
|
|
496
530
|
} catch (err) {
|
|
@@ -583,7 +617,7 @@ const INIT_PARAMS = {
|
|
|
583
617
|
assert.equal(tools[0].name, "probe_nonce", "the tool is probe_nonce");
|
|
584
618
|
assert.ok(
|
|
585
619
|
tools[0].inputSchema?.required?.includes("probeRunId"),
|
|
586
|
-
"probe mode REQUIRES probeRunId [
|
|
620
|
+
"probe mode REQUIRES probeRunId [CHECK:PROBE-FIXTURE-RUNID-REQUIRED] — the §11-7 cross-layer join key",
|
|
587
621
|
);
|
|
588
622
|
|
|
589
623
|
const good = await fx.request({
|
|
@@ -625,7 +659,7 @@ const INIT_PARAMS = {
|
|
|
625
659
|
PROBE_EVENTS.fixtureToolsCallReceived,
|
|
626
660
|
PROBE_EVENTS.fixtureToolsCallReplied,
|
|
627
661
|
]) {
|
|
628
|
-
assert.ok(names.includes(expected), `fixture stamps ${expected} [
|
|
662
|
+
assert.ok(names.includes(expected), `fixture stamps ${expected} [CHECK:PROBE-FIXTURE-WIRE-MARKER]`);
|
|
629
663
|
}
|
|
630
664
|
const delayStart = events.find((e) => e.event === PROBE_EVENTS.fixtureDelayStart);
|
|
631
665
|
const delayEnd = events.find((e) => e.event === PROBE_EVENTS.fixtureDelayEnd);
|
|
@@ -724,7 +758,7 @@ const INIT_PARAMS = {
|
|
|
724
758
|
});
|
|
725
759
|
assert.ok(
|
|
726
760
|
refusedEveryKey && !existsSync(authPath),
|
|
727
|
-
"a payload carrying a reserved envelope key is REFUSED and nothing is written [
|
|
761
|
+
"a payload carrying a reserved envelope key is REFUSED and nothing is written [CHECK:PROBE-LOG-ENVELOPE-AUTHORITY]",
|
|
728
762
|
);
|
|
729
763
|
|
|
730
764
|
const STAMP_MS = 1_700_000_000_000;
|
|
@@ -737,7 +771,7 @@ const INIT_PARAMS = {
|
|
|
737
771
|
const vocab = readProbeEvents(vocabPath);
|
|
738
772
|
assert.ok(
|
|
739
773
|
vocab.events.length === 0 && vocab.malformed.length === 1,
|
|
740
|
-
"an unknown event name is a MISSING marker, never a quiet extra line [
|
|
774
|
+
"an unknown event name is a MISSING marker, never a quiet extra line [CHECK:PROBE-LOG-EVENT-VOCABULARY]",
|
|
741
775
|
);
|
|
742
776
|
|
|
743
777
|
// Structural envelope: the shared sort axis and its readable twin. A tsMs
|
|
@@ -760,7 +794,7 @@ const INIT_PARAMS = {
|
|
|
760
794
|
const broken = readProbeEvents(brokenPath);
|
|
761
795
|
assert.ok(
|
|
762
796
|
broken.events.length === 0 && broken.malformed.length === brokenLines.length,
|
|
763
|
-
"a JSON-valid line with a broken envelope is MALFORMED, never an event [
|
|
797
|
+
"a JSON-valid line with a broken envelope is MALFORMED, never an event [CHECK:PROBE-LOG-ENVELOPE-SCHEMA]",
|
|
764
798
|
);
|
|
765
799
|
|
|
766
800
|
// Payload contract: the envelope can be perfect and the line still a lie,
|
|
@@ -784,7 +818,7 @@ const INIT_PARAMS = {
|
|
|
784
818
|
const payloadBroken = readProbeEvents(payloadPath);
|
|
785
819
|
assert.ok(
|
|
786
820
|
payloadBroken.events.length === 0 && payloadBroken.malformed.length === badPayloads.length,
|
|
787
|
-
"a valid envelope with a payload the classifier cannot judge on is MALFORMED [
|
|
821
|
+
"a valid envelope with a payload the classifier cannot judge on is MALFORMED [CHECK:PROBE-LOG-PAYLOAD-SCHEMA]",
|
|
788
822
|
);
|
|
789
823
|
|
|
790
824
|
// …and the readings the writer legitimately omits are NOT malformed: the
|
|
@@ -820,7 +854,7 @@ const INIT_PARAMS = {
|
|
|
820
854
|
const wire = readProbeEvents(wirePath);
|
|
821
855
|
assert.ok(
|
|
822
856
|
wire.events.length === 0 && wire.malformed.length === wireLines.length,
|
|
823
|
-
"a wire marker that does not name the expected tool is not wire-availability [
|
|
857
|
+
"a wire marker that does not name the expected tool is not wire-availability [CHECK:PROBE-LOG-WIRE-MARKER-NAMES-TOOL]",
|
|
824
858
|
);
|
|
825
859
|
const goodWirePath = join(tmp, "payload-wire-ok.ndjson");
|
|
826
860
|
appendFileSync(
|
|
@@ -895,7 +929,7 @@ const INIT_PARAMS = {
|
|
|
895
929
|
const win = readProbeEvents(winPath);
|
|
896
930
|
assert.ok(
|
|
897
931
|
win.malformed.length === 2 && win.events.length === 1,
|
|
898
|
-
"an unknown window reason and a non-boolean markerSeen are MALFORMED; the closed vocabulary passes [
|
|
932
|
+
"an unknown window reason and a non-boolean markerSeen are MALFORMED; the closed vocabulary passes [CHECK:PROBE-LOG-WINDOW-REASON-VOCAB]",
|
|
899
933
|
);
|
|
900
934
|
}
|
|
901
935
|
|
|
@@ -935,7 +969,7 @@ const INIT_PARAMS = {
|
|
|
935
969
|
const dupSeq = stream([line(1, 5, 100), line(1, 5, 100)]);
|
|
936
970
|
assert.ok(
|
|
937
971
|
dupSeq.sequenceViolations.length === 1 && /seq 5 does not exceed/.test(dupSeq.sequenceViolations[0]),
|
|
938
|
-
"a repeated per-pid seq is a stream violation [
|
|
972
|
+
"a repeated per-pid seq is a stream violation [CHECK:PROBE-LOG-SEQ-STRICTLY-INCREASING]",
|
|
939
973
|
);
|
|
940
974
|
const backSeq = stream([line(1, 5, 100), line(1, 4, 101)]);
|
|
941
975
|
assert.equal(backSeq.sequenceViolations.length, 1, "a per-pid seq going backwards is a stream violation");
|
|
@@ -948,7 +982,7 @@ const INIT_PARAMS = {
|
|
|
948
982
|
const outOfOrderInFile = stream([line(1, 9, 900), line(1, 2, 200)]);
|
|
949
983
|
assert.ok(
|
|
950
984
|
outOfOrderInFile.sequenceViolations.length > 0,
|
|
951
|
-
"per-writer order is judged on the RAW file order, not after the sort has rewritten it [
|
|
985
|
+
"per-writer order is judged on the RAW file order, not after the sort has rewritten it [CHECK:PROBE-LOG-RAW-ORDER-BEFORE-SORT]",
|
|
952
986
|
);
|
|
953
987
|
assert.deepEqual(
|
|
954
988
|
outOfOrderInFile.events.map((e) => e.seq),
|
|
@@ -961,7 +995,7 @@ const INIT_PARAMS = {
|
|
|
961
995
|
const backTs = stream([line(1, 0, 500), line(1, 1, 499)]);
|
|
962
996
|
assert.ok(
|
|
963
997
|
backTs.sequenceViolations.length === 1 && /runs BACKWARDS/.test(backTs.sequenceViolations[0]),
|
|
964
|
-
"a per-pid tsMs regression is a stream violation [
|
|
998
|
+
"a per-pid tsMs regression is a stream violation [CHECK:PROBE-LOG-TS-NO-REGRESSION]",
|
|
965
999
|
);
|
|
966
1000
|
// Different pids are independent: cross-process stamps are not comparable this
|
|
967
1001
|
// way, and demanding it would flag every normal interleaving.
|
|
@@ -984,7 +1018,7 @@ const INIT_PARAMS = {
|
|
|
984
1018
|
assert.equal(
|
|
985
1019
|
pidReuse.sequenceViolations.length,
|
|
986
1020
|
0,
|
|
987
|
-
"a reused pid restarting its counter in a LATER run is not a violation — the writer key is (runId, pid) [
|
|
1021
|
+
"a reused pid restarting its counter in a LATER run is not a violation — the writer key is (runId, pid) [CHECK:PROBE-LOG-WRITER-KEY-PER-RUN]",
|
|
988
1022
|
);
|
|
989
1023
|
// …but within one run the same pid is still held to the rule.
|
|
990
1024
|
assert.equal(
|
|
@@ -1190,7 +1224,7 @@ function intervention(
|
|
|
1190
1224
|
}),
|
|
1191
1225
|
};
|
|
1192
1226
|
const res2 = classifyProbe([ctl2.record, d1.record], [...ctl2.events, ...d1.events]);
|
|
1193
|
-
assert.equal(res2.verdict, "P0", "control without the callability marker → P0 [
|
|
1227
|
+
assert.equal(res2.verdict, "P0", "control without the callability marker → P0 [CHECK:VERDICT-P0-CONTROL-FAIL]");
|
|
1194
1228
|
assert.equal(res2.control.p0Reason, "tool-unavailable", "P0 names tool-unavailable");
|
|
1195
1229
|
}
|
|
1196
1230
|
|
|
@@ -1199,7 +1233,7 @@ function intervention(
|
|
|
1199
1233
|
const ctl = passingControl();
|
|
1200
1234
|
const d1 = intervention("d1", 2000, 10_000, { failPhase: "initialize" });
|
|
1201
1235
|
const res = classifyProbe([ctl.record, d1.record], [...ctl.events, ...d1.events]);
|
|
1202
|
-
assert.equal(res.verdict, "I0", "intervention initialize failure → I0, never D [
|
|
1236
|
+
assert.equal(res.verdict, "I0", "intervention initialize failure → I0, never D [CHECK:VERDICT-I0-NEVER-D]");
|
|
1203
1237
|
}
|
|
1204
1238
|
|
|
1205
1239
|
// --- D: phase-qualified fail-loud readings ----------------------------------
|
|
@@ -1228,7 +1262,7 @@ function intervention(
|
|
|
1228
1262
|
assert.equal(
|
|
1229
1263
|
resA.verdict,
|
|
1230
1264
|
"inconclusive",
|
|
1231
|
-
"absence without the wire marker is a handshake/fixture/config candidate, never B [
|
|
1265
|
+
"absence without the wire marker is a handshake/fixture/config candidate, never B [CHECK:VERDICT-NOWIRE-CANDIDATE]",
|
|
1232
1266
|
);
|
|
1233
1267
|
assert.equal(resA.promotable, false, "…and never promotes");
|
|
1234
1268
|
|
|
@@ -1244,7 +1278,7 @@ function intervention(
|
|
|
1244
1278
|
assert.equal(
|
|
1245
1279
|
resB.verdict,
|
|
1246
1280
|
"B",
|
|
1247
|
-
"marker-complete absence reads B — the runtime No-such-tool ladder OWNS runtime-error runs; no other channel may stand in for it [
|
|
1281
|
+
"marker-complete absence reads B — the runtime No-such-tool ladder OWNS runtime-error runs; no other channel may stand in for it [CHECK:VERDICT-RUNTIME-B-LADDER-OWNS]",
|
|
1248
1282
|
);
|
|
1249
1283
|
assert.equal(resB.promotable, true, "exact measured-id No-such-tool promotes");
|
|
1250
1284
|
|
|
@@ -1263,7 +1297,7 @@ function intervention(
|
|
|
1263
1297
|
assert.equal(
|
|
1264
1298
|
resB2.verdict,
|
|
1265
1299
|
"inconclusive",
|
|
1266
|
-
"exact-id absence WITHOUT running ahead of wire-availability is not delta-B [
|
|
1300
|
+
"exact-id absence WITHOUT running ahead of wire-availability is not delta-B [CHECK:VERDICT-B-REQUIRES-RANAHEAD]",
|
|
1267
1301
|
);
|
|
1268
1302
|
assert.equal(resB2.promotable, false, "…and never promotes");
|
|
1269
1303
|
|
|
@@ -1277,7 +1311,7 @@ function intervention(
|
|
|
1277
1311
|
nonceEchoed: false,
|
|
1278
1312
|
});
|
|
1279
1313
|
const resC = classifyProbe([ctl.record, alias.record], [...ctl.events, ...alias.events]);
|
|
1280
|
-
assert.equal(resC.verdict, "inconclusive", "alias-mismatch absence is not B [
|
|
1314
|
+
assert.equal(resC.verdict, "inconclusive", "alias-mismatch absence is not B [CHECK:VERDICT-B-PROMOTION-RULES]");
|
|
1281
1315
|
assert.equal(resC.promotable, false, "alias mismatch never promotes");
|
|
1282
1316
|
}
|
|
1283
1317
|
|
|
@@ -1326,7 +1360,11 @@ function intervention(
|
|
|
1326
1360
|
nonceEchoed: true,
|
|
1327
1361
|
});
|
|
1328
1362
|
const one = classifyProbe([ctl.record, d1.record], [...ctl.events, ...d1.events]);
|
|
1329
|
-
assert.equal(
|
|
1363
|
+
assert.equal(
|
|
1364
|
+
one.verdict,
|
|
1365
|
+
"A-withheld",
|
|
1366
|
+
"one nonzero delay → wait verdict WITHHELD [CHECK:VERDICT-A-NEEDS-TWO-DELAYS]",
|
|
1367
|
+
);
|
|
1330
1368
|
const two = classifyProbe([ctl.record, d1.record, d2.record], [...ctl.events, ...d1.events, ...d2.events]);
|
|
1331
1369
|
assert.equal(two.verdict, "A", "two tracking delays → A");
|
|
1332
1370
|
|
|
@@ -1361,7 +1399,7 @@ function intervention(
|
|
|
1361
1399
|
assert.equal(
|
|
1362
1400
|
over.verdict,
|
|
1363
1401
|
"A-withheld",
|
|
1364
|
-
"excess overshooting the [0.8·D, D+slack] band is not tracking — A withheld [
|
|
1402
|
+
"excess overshooting the [0.8·D, D+slack] band is not tracking — A withheld [CHECK:VERDICT-A-TRACKING-BAND]",
|
|
1365
1403
|
);
|
|
1366
1404
|
|
|
1367
1405
|
// In-band per point but NOT growing with D (4.0s → 6.4s excess for 2s → 8s
|
|
@@ -1398,7 +1436,7 @@ function intervention(
|
|
|
1398
1436
|
assert.equal(
|
|
1399
1437
|
res.verdict,
|
|
1400
1438
|
"inconclusive",
|
|
1401
|
-
"a same-ms wire/newSession-end tie is unordered — neither kept nor ahead [
|
|
1439
|
+
"a same-ms wire/newSession-end tie is unordered — neither kept nor ahead [CHECK:VERDICT-SAMEMS-AMBIGUOUS]",
|
|
1402
1440
|
);
|
|
1403
1441
|
|
|
1404
1442
|
// A tie combined with an exact-id No-such-tool must not read B either —
|
|
@@ -1441,7 +1479,8 @@ function intervention(
|
|
|
1441
1479
|
const resNoWindow = classifyProbe([ctl.record, noWindow.record], [...ctl.events, ...noWindow.events]);
|
|
1442
1480
|
// Two independent nets cover this — the exactly-once inventory below AND the
|
|
1443
1481
|
// explicit windowReason guard in the classifier — so no SINGLE mutation can
|
|
1444
|
-
// kill it and it carries no [QK:] token. The inventory itself
|
|
1482
|
+
// kill it and it carries no [QK:] token. The inventory itself remains a
|
|
1483
|
+
// direct assertion below (no longer replant-qualified — issue #70 subtraction).
|
|
1445
1484
|
assert.ok(
|
|
1446
1485
|
resNoWindow.verdict === "INVALIDATED" && resNoWindow.interventions[0].invalidReason === "topology",
|
|
1447
1486
|
"a run with no observation-window marker is INVALIDATED for TOPOLOGY — absence cannot be told from our own teardown",
|
|
@@ -1451,7 +1490,7 @@ function intervention(
|
|
|
1451
1490
|
assert.deepEqual(
|
|
1452
1491
|
[...RUNNER_EXACTLY_ONCE].sort(),
|
|
1453
1492
|
[PROBE_EVENTS.observationWindowEnd, PROBE_EVENTS.runEnd, PROBE_EVENTS.runStart].sort(),
|
|
1454
|
-
"the runner-owned exactly-once marker set is exactly run_start, the window close, and run_end [
|
|
1493
|
+
"the runner-owned exactly-once marker set is exactly run_start, the window close, and run_end [CHECK:VERDICT-RUNNER-EXACTLY-ONCE-INVENTORY]",
|
|
1455
1494
|
);
|
|
1456
1495
|
|
|
1457
1496
|
// THE regression the first LIVE pair produced: D2's child was torn down while
|
|
@@ -1469,7 +1508,7 @@ function intervention(
|
|
|
1469
1508
|
assert.equal(
|
|
1470
1509
|
resCensored.interventions[0].invalidReason,
|
|
1471
1510
|
"observation-window-closed",
|
|
1472
|
-
"a window closed by child-exit with the marker unseen is CENSORED, not a handshake/fixture/config candidate [
|
|
1511
|
+
"a window closed by child-exit with the marker unseen is CENSORED, not a handshake/fixture/config candidate [CHECK:VERDICT-CENSORED-NOT-CANDIDATE]",
|
|
1473
1512
|
);
|
|
1474
1513
|
assert.equal(
|
|
1475
1514
|
resCensored.interventions[0].ordering,
|
|
@@ -1488,7 +1527,7 @@ function intervention(
|
|
|
1488
1527
|
assert.deepEqual(
|
|
1489
1528
|
resCensored.status.invalidRuns,
|
|
1490
1529
|
[{ runId: "w1", reason: "observation-window-closed" }],
|
|
1491
|
-
"a fatal status still names the discarded run and its reason [
|
|
1530
|
+
"a fatal status still names the discarded run and its reason [CHECK:VERDICT-STATUS-NAMES-INVALID-RUNS]",
|
|
1492
1531
|
);
|
|
1493
1532
|
|
|
1494
1533
|
// The SAME absence under a window we kept open to its deadline IS a reading:
|
|
@@ -1503,7 +1542,7 @@ function intervention(
|
|
|
1503
1542
|
assert.equal(
|
|
1504
1543
|
resSufficient.interventions[0].failure,
|
|
1505
1544
|
"candidate-handshake",
|
|
1506
|
-
"the same absence under a SUFFICIENT window is a handshake/fixture/config candidate (
|
|
1545
|
+
"the same absence under a SUFFICIENT window is a handshake/fixture/config candidate (one condition, directly asserted; its qualification replant was dropped — issue #70)",
|
|
1507
1546
|
);
|
|
1508
1547
|
assert.equal(resSufficient.verdict, "inconclusive", "…still not promotable, and still not B");
|
|
1509
1548
|
|
|
@@ -1517,7 +1556,7 @@ function intervention(
|
|
|
1517
1556
|
assert.equal(
|
|
1518
1557
|
resDup.verdict,
|
|
1519
1558
|
"INVALIDATED",
|
|
1520
|
-
"a duplicated runner-owned marker INVALIDATES the run [
|
|
1559
|
+
"a duplicated runner-owned marker INVALIDATES the run [CHECK:VERDICT-RUNNER-TOPOLOGY-EXACTLY-ONCE]",
|
|
1521
1560
|
);
|
|
1522
1561
|
assert.match(resDup.interventions[0].evidence, /run_end appears 2 times/, "…and the evidence names the duplicate");
|
|
1523
1562
|
|
|
@@ -1570,7 +1609,7 @@ function intervention(
|
|
|
1570
1609
|
assert.equal(
|
|
1571
1610
|
resLying.verdict,
|
|
1572
1611
|
"INVALIDATED",
|
|
1573
|
-
"a window close claiming markerSeen=true with no wire marker in the log is INVALIDATED, never a candidate [
|
|
1612
|
+
"a window close claiming markerSeen=true with no wire marker in the log is INVALIDATED, never a candidate [CHECK:VERDICT-WINDOW-MARKER-COHERENCE]",
|
|
1574
1613
|
);
|
|
1575
1614
|
assert.match(resLying.interventions[0].evidence, /contradicts its own evidence/, "…named as a self-contradiction");
|
|
1576
1615
|
|
|
@@ -1601,7 +1640,7 @@ function intervention(
|
|
|
1601
1640
|
const resLyingCtl = classifyProbe([lyingControl.record, d1ok.record], [...lyingControl.events, ...d1ok.events]);
|
|
1602
1641
|
assert.ok(
|
|
1603
1642
|
resLyingCtl.verdict === "INVALIDATED" && resLyingCtl.control.pass === false,
|
|
1604
|
-
"a CONTROL whose window close contradicts its own log is INVALIDATED before P0 is even considered — the baseline is held to the same bar [
|
|
1643
|
+
"a CONTROL whose window close contradicts its own log is INVALIDATED before P0 is even considered — the baseline is held to the same bar [CHECK:VERDICT-CONTROL-HELD-TO-COHERENCE]",
|
|
1605
1644
|
);
|
|
1606
1645
|
assert.equal(resLyingCtl.interventions.length, 0, "…and no intervention is judged against a baseline that lied");
|
|
1607
1646
|
|
|
@@ -1646,7 +1685,7 @@ function intervention(
|
|
|
1646
1685
|
assert.equal(
|
|
1647
1686
|
resTrans.verdict,
|
|
1648
1687
|
"INVALIDATED",
|
|
1649
|
-
"transposed phases are a topology violation even though every start/end pair is intact [
|
|
1688
|
+
"transposed phases are a topology violation even though every start/end pair is intact [CHECK:VERDICT-PHASE-SEQUENTIAL]",
|
|
1650
1689
|
);
|
|
1651
1690
|
assert.match(
|
|
1652
1691
|
resTrans.interventions[0].evidence,
|
|
@@ -1680,7 +1719,7 @@ function intervention(
|
|
|
1680
1719
|
assert.equal(
|
|
1681
1720
|
resHole.verdict,
|
|
1682
1721
|
"INVALIDATED",
|
|
1683
|
-
"a skipped phase is a topology violation — a failed run is a PREFIX of the production order, never a hole [
|
|
1722
|
+
"a skipped phase is a topology violation — a failed run is a PREFIX of the production order, never a hole [CHECK:VERDICT-PHASE-PRODUCTION-ORDER]",
|
|
1684
1723
|
);
|
|
1685
1724
|
assert.match(resHole.interventions[0].evidence, /prefix of the production order/, "…named as a prefix violation");
|
|
1686
1725
|
}
|
|
@@ -1715,7 +1754,7 @@ function intervention(
|
|
|
1715
1754
|
assert.equal(
|
|
1716
1755
|
res.ordering.summary,
|
|
1717
1756
|
"prompt-request-ahead-of-wire",
|
|
1718
|
-
"…and the (a) axis is reported on its OWN terms rather than being folded into that verdict [
|
|
1757
|
+
"…and the (a) axis is reported on its OWN terms rather than being folded into that verdict [CHECK:VERDICT-ORDERING-AXIS-REPORTED]",
|
|
1719
1758
|
);
|
|
1720
1759
|
// Diagnosability: §11-7-b's first artifact classified D1 correctly and still
|
|
1721
1760
|
// left a reader unable to SEE the ran-ahead or the turn time left after it.
|
|
@@ -1723,7 +1762,7 @@ function intervention(
|
|
|
1723
1762
|
assert.match(
|
|
1724
1763
|
r.evidence,
|
|
1725
1764
|
/\d+ms of turn remained after wire/,
|
|
1726
|
-
"the evidence exposes how much turn was left after the wire marker [
|
|
1765
|
+
"the evidence exposes how much turn was left after the wire marker [CHECK:VERDICT-EVIDENCE-EXPOSES-DELTAS]",
|
|
1727
1766
|
);
|
|
1728
1767
|
|
|
1729
1768
|
// The axis split is load-bearing for B/C, not cosmetic. Here the wire lands
|
|
@@ -1744,7 +1783,7 @@ function intervention(
|
|
|
1744
1783
|
assert.equal(
|
|
1745
1784
|
resBetween.verdict,
|
|
1746
1785
|
"inconclusive",
|
|
1747
|
-
"exact-id absence with the wire available before the prompt is NOT delta-B — B's window is promptStart, not newSession end [
|
|
1786
|
+
"exact-id absence with the wire available before the prompt is NOT delta-B — B's window is promptStart, not newSession end [CHECK:VERDICT-B-WINDOW-IS-PROMPT]",
|
|
1748
1787
|
);
|
|
1749
1788
|
assert.equal(
|
|
1750
1789
|
rb.ordering,
|
|
@@ -1799,7 +1838,7 @@ function intervention(
|
|
|
1799
1838
|
assert.equal(
|
|
1800
1839
|
await reasonOf({ [AMBIENT_OVERRIDE_ENV]: "/somewhere/claude" }, fakeBin),
|
|
1801
1840
|
"ambient-override-present",
|
|
1802
|
-
"an ambient CLAUDE_CODE_EXECUTABLE is REFUSED before resolution — claudeCliPath() would return it verbatim [
|
|
1841
|
+
"an ambient CLAUDE_CODE_EXECUTABLE is REFUSED before resolution — claudeCliPath() would return it verbatim [CHECK:PROBE-TARGET-AMBIENT-REFUSED]",
|
|
1803
1842
|
);
|
|
1804
1843
|
// KEY PRESENCE is the predicate: upstream's `??` treats "" as set and passes
|
|
1805
1844
|
// it on while a truthy check treats it as unset — the probe refuses the
|
|
@@ -1817,7 +1856,7 @@ function intervention(
|
|
|
1817
1856
|
assert.equal(
|
|
1818
1857
|
await reasonOf({}, fakeScript),
|
|
1819
1858
|
"target-script-suffix",
|
|
1820
|
-
"a script-suffixed target is refused — the SDK would take the node|bun branch, which this seam asserts against instead of reproducing [
|
|
1859
|
+
"a script-suffixed target is refused — the SDK would take the node|bun branch, which this seam asserts against instead of reproducing [CHECK:PROBE-TARGET-NATIVE-BRANCH-ONLY]",
|
|
1821
1860
|
);
|
|
1822
1861
|
assert.equal(
|
|
1823
1862
|
await reasonOf({}, join(tmp, "no-such-claude")),
|
|
@@ -1962,7 +2001,7 @@ function intervention(
|
|
|
1962
2001
|
Object.values(PROBE_SHIM_ENV).every((v) => SHIM_SCRUB_ENV_VARS.includes(v)) &&
|
|
1963
2002
|
SHIM_SCRUB_ENV_VARS.length === 1 + Object.values(PROBE_SHIM_ENV).length &&
|
|
1964
2003
|
SHIM_SCRUB_ENV_VARS.every((v) => /^[A-Z][A-Z0-9_]*$/.test(v)),
|
|
1965
|
-
"the shim scrub list is the exact enumerated allowlist — the override plus every probe-private var by literal name, no wildcard/prefix semantics [
|
|
2004
|
+
"the shim scrub list is the exact enumerated allowlist — the override plus every probe-private var by literal name, no wildcard/prefix semantics [CHECK:PROBE-SCRUB-EXACT-ALLOWLIST]",
|
|
1966
2005
|
);
|
|
1967
2006
|
// The shim env names must not collide with the fixture's — two processes,
|
|
1968
2007
|
// two channels, one shared log.
|
|
@@ -1973,18 +2012,19 @@ function intervention(
|
|
|
1973
2012
|
);
|
|
1974
2013
|
}
|
|
1975
2014
|
|
|
1976
|
-
// --- 8d) runner pins — one assert per claim,
|
|
1977
|
-
//
|
|
2015
|
+
// --- 8d) runner pins — one assert per claim, each under its own label, so a
|
|
2016
|
+
// source pin stays a DIRECT assertion naming exactly one contract
|
|
2017
|
+
// (bundling four claims under one token let one mutant
|
|
1978
2018
|
// stand in for all of them; GPT review 2026-07-29) ----------------------
|
|
1979
2019
|
{
|
|
1980
2020
|
assert.ok(
|
|
1981
2021
|
RUNNER_SRC.includes("assertNoAmbientOverride(spawnEnv, `composed acp child env for ${runId}`);"),
|
|
1982
|
-
"the COMPOSED spawn env of every ACP child is asserted override-free — launch defaults / overlay overrides could inject what process.env did not carry [
|
|
2022
|
+
"the COMPOSED spawn env of every ACP child is asserted override-free — launch defaults / overlay overrides could inject what process.env did not carry [CHECK:RUNNER-TARGET-PRECONDITION-PINNED]",
|
|
1983
2023
|
);
|
|
1984
2024
|
assert.ok(
|
|
1985
2025
|
RUNNER_SRC.includes("snapshotInstrumented: true") && !RUNNER_SRC.includes("snapshotInstrumented: false"),
|
|
1986
2026
|
"the snapshot channel is ARMED and no run is left declaring otherwise — a roster mixing armed and unarmed runs " +
|
|
1987
|
-
"would let a run whose shim never reported in pass as an ordinary absence [
|
|
2027
|
+
"would let a run whose shim never reported in pass as an ordinary absence [CHECK:RUNNER-SNAPSHOT-CHANNEL-ARMED]",
|
|
1988
2028
|
);
|
|
1989
2029
|
// Pinned as the CONTIGUOUS roster-record shape: the same two stamps also ride
|
|
1990
2030
|
// the run_start payload (forensics), so field-by-field includes() would stay
|
|
@@ -1993,13 +2033,13 @@ function intervention(
|
|
|
1993
2033
|
RUNNER_SRC.includes(
|
|
1994
2034
|
"snapshotInstrumented: true,\n\t\tcliTargetPath: CLI_TARGET.path,\n\t\tcliTargetSha256: CLI_TARGET.sha256,",
|
|
1995
2035
|
),
|
|
1996
|
-
"the pair's expected CLI target identity rides EVERY roster record so the classifier can consume it (condition 5) [
|
|
2036
|
+
"the pair's expected CLI target identity rides EVERY roster record so the classifier can consume it (condition 5) [CHECK:RUNNER-TARGET-IDENTITY-IN-ROSTER]",
|
|
1997
2037
|
);
|
|
1998
2038
|
assert.ok(
|
|
1999
2039
|
RUNNER_SRC.includes("rehash = hashFileSha256(CLI_TARGET.path);") &&
|
|
2000
2040
|
RUNNER_SRC.includes('reason: "cli-target-drift"') &&
|
|
2001
2041
|
RUNNER_SRC.includes('reason: "cli-target-unreadable"'),
|
|
2002
|
-
"the target is RE-HASHED after the pair, and both drift and unreadability write a named INVALIDATED classification [
|
|
2042
|
+
"the target is RE-HASHED after the pair, and both drift and unreadability write a named INVALIDATED classification [CHECK:RUNNER-DRIFT-REHASH-PINNED]",
|
|
2003
2043
|
);
|
|
2004
2044
|
assert.ok(
|
|
2005
2045
|
RUNNER_SRC.includes("CLI_TARGET = await resolveProbeCliTarget({") &&
|
|
@@ -2019,7 +2059,7 @@ function intervention(
|
|
|
2019
2059
|
"the ambient-override refusal runs against the env as PRODUCTION composed it, and the probe installs its own " +
|
|
2020
2060
|
"override only AFTER. Inverted, the checkpoint would inspect the override the probe itself just injected and " +
|
|
2021
2061
|
"REFUSE every run — loudly, but for the wrong reason, and the operator's ambient environment would never be " +
|
|
2022
|
-
"examined at all [
|
|
2062
|
+
"examined at all [CHECK:RUNNER-ARMING-ORDER]",
|
|
2023
2063
|
);
|
|
2024
2064
|
assert.ok(
|
|
2025
2065
|
RUNNER_SRC.includes(
|
|
@@ -2030,7 +2070,7 @@ function intervention(
|
|
|
2030
2070
|
),
|
|
2031
2071
|
"the injection is exactly four names: the override pointing at the SHIM, and the three probe-private vars the " +
|
|
2032
2072
|
"shim reads — the target it must exec (resolved HERE, never by the shim), the shared log, and this run's id. " +
|
|
2033
|
-
"All four are on the shim's scrub list, so none of them reach the real CLI [
|
|
2073
|
+
"All four are on the shim's scrub list, so none of them reach the real CLI [CHECK:RUNNER-SHIM-OVERRIDE-EXACT]",
|
|
2034
2074
|
);
|
|
2035
2075
|
assert.ok(
|
|
2036
2076
|
RUNNER_SRC.includes(
|
|
@@ -2039,7 +2079,7 @@ function intervention(
|
|
|
2039
2079
|
"the instrument passes the SAME precondition asserts as the stimulus — absolute, native branch, present regular " +
|
|
2040
2080
|
"file, executable — and a refusal is a NAMED classification on the artifact. A shim that fails any of those " +
|
|
2041
2081
|
"either never runs or runs on the OTHER launch branch, and the pair would measure something else " +
|
|
2042
|
-
"[
|
|
2082
|
+
"[CHECK:RUNNER-SHIM-PRECONDITION-PINNED]",
|
|
2043
2083
|
);
|
|
2044
2084
|
// The path the runner points at is checked on DISK too, not just in source: a
|
|
2045
2085
|
// pin proves the runner asks for the right file, not that the file can run.
|
|
@@ -2056,7 +2096,7 @@ function intervention(
|
|
|
2056
2096
|
RUNNER_SRC.includes("JSON.stringify(shimRehash) !== JSON.stringify(SHIM_RUNTIME)"),
|
|
2057
2097
|
"the instrument's runtime graph is pinned before the first run and RE-HASHED after the last, on its own axis " +
|
|
2058
2098
|
"with its own two names — an edit to the implementation landing between control and intervention is " +
|
|
2059
|
-
"invisible to every other check, including the shim's own boot marker [
|
|
2099
|
+
"invisible to every other check, including the shim's own boot marker [CHECK:RUNNER-SHIM-RUNTIME-PINNED]",
|
|
2060
2100
|
);
|
|
2061
2101
|
{
|
|
2062
2102
|
// The runner's list must equal the STATIC LOCAL IMPORT CLOSURE of the
|
|
@@ -2093,7 +2133,7 @@ function intervention(
|
|
|
2093
2133
|
[...closure].sort(),
|
|
2094
2134
|
"the runner's pinned instrument list is EXACTLY the launcher's static local-import closure — a helper added " +
|
|
2095
2135
|
"to the shim without being pinned would otherwise be free to change mid-pair, and a stale entry would " +
|
|
2096
|
-
"pin a file the instrument no longer reads [
|
|
2136
|
+
"pin a file the instrument no longer reads [CHECK:RUNNER-SHIM-RUNTIME-GRAPH-EXACT]",
|
|
2097
2137
|
);
|
|
2098
2138
|
}
|
|
2099
2139
|
// The path is read OUT OF THE RUNNER rather than restated here, so this
|
|
@@ -2109,7 +2149,7 @@ function intervention(
|
|
|
2109
2149
|
SDK_SCRIPT_SUFFIXES.every((suffix) => !shimPath.endsWith(suffix)),
|
|
2110
2150
|
"the shim the runner arms is present, executable and extensionless ON DISK — the pair is asserted onto the " +
|
|
2111
2151
|
"direct-spawn branch, and a script suffix (or a path pointing at nothing) would silently move the " +
|
|
2112
|
-
"instrument to `node|bun <path>` or break the spawn outright [
|
|
2152
|
+
"instrument to `node|bun <path>` or break the spawn outright [CHECK:RUNNER-SHIM-ON-DISK-NATIVE]",
|
|
2113
2153
|
);
|
|
2114
2154
|
}
|
|
2115
2155
|
}
|
|
@@ -2134,7 +2174,7 @@ function intervention(
|
|
|
2134
2174
|
const door = readProbeEvents(doorPath);
|
|
2135
2175
|
assert.ok(
|
|
2136
2176
|
door.malformed.length === 4 && door.events.length === 3,
|
|
2137
|
-
"shim payload rules hold at the door: missing target hash, ordinal<1, non-array tools, and receivedAtMs AFTER the envelope stamp are MALFORMED; the well-formed trio passes [
|
|
2177
|
+
"shim payload rules hold at the door: missing target hash, ordinal<1, non-array tools, and receivedAtMs AFTER the envelope stamp are MALFORMED; the well-formed trio passes [CHECK:PROBE-LOG-SNAPSHOT-PAYLOAD]",
|
|
2138
2178
|
);
|
|
2139
2179
|
}
|
|
2140
2180
|
|
|
@@ -2209,7 +2249,7 @@ function intervention(
|
|
|
2209
2249
|
assert.equal(
|
|
2210
2250
|
res.verdict,
|
|
2211
2251
|
"B-name-snapshot",
|
|
2212
|
-
"snapshot absence of the measured id + promptRanAhead + wire strictly before the interval + calibrated control → B-name-snapshot [
|
|
2252
|
+
"snapshot absence of the measured id + promptRanAhead + wire strictly before the interval + calibrated control → B-name-snapshot [CHECK:VERDICT-SNAPSHOT-PROMOTES]",
|
|
2213
2253
|
);
|
|
2214
2254
|
assert.ok(res.promotable && res.status.failureVerdict === "B-name-snapshot", "…and it is promotable on axis (b)");
|
|
2215
2255
|
assert.notEqual(
|
|
@@ -2231,7 +2271,7 @@ function intervention(
|
|
|
2231
2271
|
assert.equal(
|
|
2232
2272
|
res.control.p0Reason,
|
|
2233
2273
|
"snapshot-instrument-absent",
|
|
2234
|
-
"…and the reason NAMES the missing instrument (a hijacked/replaced override looks exactly like this) [
|
|
2274
|
+
"…and the reason NAMES the missing instrument (a hijacked/replaced override looks exactly like this) [CHECK:VERDICT-SNAPSHOT-INSTRUMENT-ABSENT]",
|
|
2235
2275
|
);
|
|
2236
2276
|
}
|
|
2237
2277
|
|
|
@@ -2250,7 +2290,7 @@ function intervention(
|
|
|
2250
2290
|
const res = classifyProbe([ctl.record, i1.record], [...ctl.events, ...i1.events]);
|
|
2251
2291
|
assert.ok(
|
|
2252
2292
|
res.verdict === "P0" && res.control.p0Reason === "snapshot-calibration",
|
|
2253
|
-
"a control snapshot that cannot SEE the measured id fails calibration — absence readings need a baseline that shows presence [
|
|
2293
|
+
"a control snapshot that cannot SEE the measured id fails calibration — absence readings need a baseline that shows presence [CHECK:VERDICT-SNAPSHOT-CALIBRATION]",
|
|
2254
2294
|
);
|
|
2255
2295
|
}
|
|
2256
2296
|
|
|
@@ -2266,7 +2306,7 @@ function intervention(
|
|
|
2266
2306
|
const res = classifyProbe([ctl.record, i1.record], [...ctl.events, ...i1.events]);
|
|
2267
2307
|
assert.ok(
|
|
2268
2308
|
res.verdict === "inconclusive" && !res.promotable,
|
|
2269
|
-
"snapshot absence WITHOUT promptRanAhead is not the delayed-window failure mode — never promoted [
|
|
2309
|
+
"snapshot absence WITHOUT promptRanAhead is not the delayed-window failure mode — never promoted [CHECK:VERDICT-SNAPSHOT-REQUIRES-RANAHEAD]",
|
|
2270
2310
|
);
|
|
2271
2311
|
}
|
|
2272
2312
|
|
|
@@ -2283,7 +2323,7 @@ function intervention(
|
|
|
2283
2323
|
res.verdict === "inconclusive" &&
|
|
2284
2324
|
!res.promotable &&
|
|
2285
2325
|
res.interventions[0].evidence.includes("INSIDE the snapshot interval"),
|
|
2286
|
-
"a wire marker inside the received↔forwarded interval is UNORDERED — only wire strictly before the interval reads as after [
|
|
2326
|
+
"a wire marker inside the received↔forwarded interval is UNORDERED — only wire strictly before the interval reads as after [CHECK:VERDICT-SNAPSHOT-INTERVAL-UNORDERED]",
|
|
2287
2327
|
);
|
|
2288
2328
|
}
|
|
2289
2329
|
|
|
@@ -2333,7 +2373,7 @@ function intervention(
|
|
|
2333
2373
|
res.verdict === "inconclusive" &&
|
|
2334
2374
|
!res.promotable &&
|
|
2335
2375
|
res.interventions[0].evidence.includes("snapshot-channel-violation"),
|
|
2336
|
-
"reinit/set-model re-emission making the binding ambiguous is a NAMED channel violation — the (b) reading is unavailable, never a pick-first promotion [
|
|
2376
|
+
"reinit/set-model re-emission making the binding ambiguous is a NAMED channel violation — the (b) reading is unavailable, never a pick-first promotion [CHECK:VERDICT-SNAPSHOT-ORDINAL-EXACTLY-ONE]",
|
|
2337
2377
|
);
|
|
2338
2378
|
assert.notEqual(res.status.orderingMeasurement, "unobserved", "…while axis (a) still carries its comparison");
|
|
2339
2379
|
}
|
|
@@ -2354,7 +2394,7 @@ function intervention(
|
|
|
2354
2394
|
const res = classifyProbe([ctl.record, i1.record], [...ctl.events, ...i1.events]);
|
|
2355
2395
|
assert.ok(
|
|
2356
2396
|
res.verdict === "inconclusive" && !res.promotable && res.status.failureVerdict !== "B-name-snapshot",
|
|
2357
|
-
"shim-shaped evidence in the log of an UNARMED run is ignored — the roster declares the instrument, evidence alone never promotes [
|
|
2397
|
+
"shim-shaped evidence in the log of an UNARMED run is ignored — the roster declares the instrument, evidence alone never promotes [CHECK:VERDICT-SNAPSHOT-NEEDS-INSTRUMENT-FLAG]",
|
|
2358
2398
|
);
|
|
2359
2399
|
}
|
|
2360
2400
|
|
|
@@ -2376,7 +2416,7 @@ function intervention(
|
|
|
2376
2416
|
res.verdict === "inconclusive" &&
|
|
2377
2417
|
!res.promotable &&
|
|
2378
2418
|
res.interventions[0].evidence.includes("snapshot-channel-violation"),
|
|
2379
|
-
"an init RECEIVED before the prompt frame is not a candidate even when its append lands after — the receive axis, not the append/callback axis, binds; zero candidates is a named violation [
|
|
2419
|
+
"an init RECEIVED before the prompt frame is not a candidate even when its append lands after — the receive axis, not the append/callback axis, binds; zero candidates is a named violation [CHECK:VERDICT-SNAPSHOT-BINDING-RECEIVE-AXIS]",
|
|
2380
2420
|
);
|
|
2381
2421
|
}
|
|
2382
2422
|
|
|
@@ -2394,7 +2434,7 @@ function intervention(
|
|
|
2394
2434
|
res.status.invalidRuns.some((r) => r.reason === "snapshot-topology") &&
|
|
2395
2435
|
res.ordering.summary === "unobserved" &&
|
|
2396
2436
|
res.status.orderingMeasurement !== "measured",
|
|
2397
|
-
"an armed intervention whose shim never reported in is INVALIDATED (snapshot-topology) and votes on NEITHER axis — not a (b)-only degradation [
|
|
2437
|
+
"an armed intervention whose shim never reported in is INVALIDATED (snapshot-topology) and votes on NEITHER axis — not a (b)-only degradation [CHECK:VERDICT-SNAPSHOT-STRUCTURAL-INVALIDATES]",
|
|
2398
2438
|
);
|
|
2399
2439
|
}
|
|
2400
2440
|
|
|
@@ -2412,7 +2452,7 @@ function intervention(
|
|
|
2412
2452
|
const res = classifyProbe([ctl.record, i1.record], [...ctl.events, ...i1.events]);
|
|
2413
2453
|
assert.ok(
|
|
2414
2454
|
res.verdict === "INVALIDATED" && res.status.invalidRuns.some((r) => r.reason === "snapshot-topology"),
|
|
2415
|
-
"a shim boot whose target path+sha does not match the roster's expected identity INVALIDATES the run — identity is verified, not merely recorded [
|
|
2455
|
+
"a shim boot whose target path+sha does not match the roster's expected identity INVALIDATES the run — identity is verified, not merely recorded [CHECK:VERDICT-SNAPSHOT-TARGET-IDENTITY]",
|
|
2416
2456
|
);
|
|
2417
2457
|
}
|
|
2418
2458
|
|
|
@@ -2432,7 +2472,7 @@ function intervention(
|
|
|
2432
2472
|
res.verdict === "inconclusive" &&
|
|
2433
2473
|
!res.promotable &&
|
|
2434
2474
|
res.interventions[0].evidence.includes("CONTAINS the measured id"),
|
|
2435
|
-
"a snapshot that CONTAINS the measured id reads model-compliance, never absence — the contains-id rung blocks promotion [
|
|
2475
|
+
"a snapshot that CONTAINS the measured id reads model-compliance, never absence — the contains-id rung blocks promotion [CHECK:VERDICT-SNAPSHOT-CONTAINS-ID-BLOCKS]",
|
|
2436
2476
|
);
|
|
2437
2477
|
}
|
|
2438
2478
|
}
|