@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.
Files changed (193) hide show
  1. package/AGENTS.md +52 -17
  2. package/BASELINE.md +60 -206
  3. package/CHANGELOG.md +64 -0
  4. package/CONTRIBUTING.md +14 -10
  5. package/DELIVERY.md +118 -261
  6. package/README.md +91 -431
  7. package/VERIFY.md +74 -95
  8. package/demo/README.md +1 -1
  9. package/demo/demo-baseline.sh +1 -3
  10. package/demo/demo.sh +2 -5
  11. package/docs/acp-backend-rail.md +236 -0
  12. package/docs/external-mcp-host.md +131 -0
  13. package/docs/fresh-cut-policy.md +99 -0
  14. package/docs/setup-clean-host.md +124 -328
  15. package/mcp/entwurf-bridge/dist/mcp/entwurf-bridge/src/index.js +160 -67
  16. package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/acp-client.js +12 -0
  17. package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/backend-adapter.js +11 -10
  18. package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/config.js +1 -1
  19. package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/engraving.js +42 -1
  20. package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/models.js +4 -4
  21. package/mcp/entwurf-bridge/dist/pi-extensions/lib/acp/overlay.js +11 -9
  22. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-control-rpc.js +7 -5
  23. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-core.js +13 -14
  24. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-resume-args.js +45 -40
  25. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-contract.js +117 -95
  26. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-decider.js +23 -57
  27. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-lock.js +16 -7
  28. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-production.js +5 -53
  29. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-release.js +21 -36
  30. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-runner.js +3 -15
  31. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-send-fallback.js +12 -11
  32. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-send.js +2 -7
  33. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-surface.js +30 -67
  34. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-visible-resume.js +256 -0
  35. package/mcp/entwurf-bridge/dist/pi-extensions/lib/meta-session.js +91 -5
  36. package/mcp/entwurf-bridge/dist/pi-extensions/lib/mux-fresh-call.js +258 -0
  37. package/mcp/entwurf-bridge/dist/pi-extensions/lib/mux-launch.js +202 -0
  38. package/mcp/entwurf-bridge/dist/pi-extensions/lib/mux-placement.js +289 -0
  39. package/mcp/entwurf-bridge/dist/pi-extensions/lib/mux-resume-call.js +170 -0
  40. package/mcp/entwurf-bridge/dist/pi-extensions/lib/resume-launch-identity.js +136 -0
  41. package/mcp/entwurf-bridge/dist/pi-extensions/lib/session-id.js +8 -5
  42. package/mcp/entwurf-bridge/dist/pi-extensions/lib/socket-discovery.js +3 -3
  43. package/mcp/entwurf-bridge/dist/scripts/meta-facts.js +51 -0
  44. package/mcp/entwurf-bridge/dist/scripts/new-session-id.js +9 -4
  45. package/mcp/entwurf-bridge/src/index.ts +176 -67
  46. package/mcp/entwurf-bridge/start.sh +2 -2
  47. package/mcp/entwurf-bridge/test.sh +23 -9
  48. package/mcp/entwurf-bridge/tsconfig.build.json +11 -2
  49. package/package.json +25 -11
  50. package/pi-extensions/entwurf-control.ts +220 -71
  51. package/pi-extensions/lib/acp/acp-client.ts +22 -0
  52. package/pi-extensions/lib/acp/augment.ts +42 -3
  53. package/pi-extensions/lib/acp/backend-adapter.ts +12 -11
  54. package/pi-extensions/lib/acp/backend.ts +366 -38
  55. package/pi-extensions/lib/acp/config.ts +1 -1
  56. package/pi-extensions/lib/acp/engraving.ts +43 -1
  57. package/pi-extensions/lib/acp/event-mapper.ts +16 -13
  58. package/pi-extensions/lib/acp/models.ts +4 -4
  59. package/pi-extensions/lib/acp/overlay.ts +11 -9
  60. package/pi-extensions/lib/entwurf-control-rpc.ts +7 -5
  61. package/pi-extensions/lib/entwurf-core.ts +15 -15
  62. package/pi-extensions/lib/entwurf-resume-args.ts +41 -52
  63. package/pi-extensions/lib/entwurf-v2-contract-schema.ts +1 -1
  64. package/pi-extensions/lib/entwurf-v2-contract.ts +120 -99
  65. package/pi-extensions/lib/entwurf-v2-decider.ts +30 -91
  66. package/pi-extensions/lib/entwurf-v2-lock.ts +16 -7
  67. package/pi-extensions/lib/entwurf-v2-production.ts +4 -78
  68. package/pi-extensions/lib/entwurf-v2-release.ts +25 -49
  69. package/pi-extensions/lib/entwurf-v2-runner.ts +6 -21
  70. package/pi-extensions/lib/entwurf-v2-send-fallback.ts +12 -11
  71. package/pi-extensions/lib/entwurf-v2-send.ts +2 -7
  72. package/pi-extensions/lib/entwurf-v2-surface.ts +36 -76
  73. package/pi-extensions/lib/entwurf-v2-visible-resume.ts +370 -0
  74. package/pi-extensions/lib/meta-session.ts +93 -5
  75. package/pi-extensions/lib/mux-fresh-call.ts +328 -0
  76. package/pi-extensions/lib/mux-launch.ts +267 -0
  77. package/pi-extensions/lib/mux-placement.ts +387 -0
  78. package/pi-extensions/lib/mux-resume-call.ts +221 -0
  79. package/pi-extensions/lib/resume-launch-identity.ts +162 -0
  80. package/pi-extensions/lib/session-id.js +8 -5
  81. package/pi-extensions/lib/socket-discovery.ts +3 -3
  82. package/prompts/engraving.md +13 -5
  83. package/run.sh +688 -282
  84. package/scripts/agy-bridge-config.py +5 -1
  85. package/scripts/check-acp-backend-preflight.ts +1 -1
  86. package/scripts/check-acp-carrier-augment.ts +246 -10
  87. package/scripts/check-acp-cortex.ts +5 -5
  88. package/scripts/check-acp-overlay.ts +13 -3
  89. package/scripts/check-acp-prompt-lifecycle.ts +565 -0
  90. package/scripts/check-acp-stop-reason.ts +342 -0
  91. package/scripts/check-acp-stream-hooks.ts +504 -0
  92. package/scripts/check-elapsed.sh +25 -0
  93. package/scripts/check-entwurf-bridge-boot.ts +51 -4
  94. package/scripts/check-entwurf-bridge-pi-free.ts +6 -5
  95. package/scripts/check-entwurf-control-rpc.ts +4 -3
  96. package/scripts/check-entwurf-resume-args.ts +72 -70
  97. package/scripts/check-entwurf-session-identity.ts +14 -10
  98. package/scripts/check-entwurf-v2-contract.ts +34 -59
  99. package/scripts/check-entwurf-v2-decider.ts +17 -177
  100. package/scripts/check-entwurf-v2-lock.ts +5 -2
  101. package/scripts/check-entwurf-v2-matrix.ts +3 -53
  102. package/scripts/check-entwurf-v2-production.ts +2 -91
  103. package/scripts/check-entwurf-v2-release.ts +10 -105
  104. package/scripts/check-entwurf-v2-runner.ts +4 -85
  105. package/scripts/check-entwurf-v2-send-fallback.ts +5 -6
  106. package/scripts/check-entwurf-v2-send.ts +0 -28
  107. package/scripts/check-entwurf-v2-surface.ts +198 -128
  108. package/scripts/check-entwurf-v2-visible-resume.ts +445 -0
  109. package/scripts/check-fresh-cut-gate.sh +1 -1
  110. package/scripts/check-gate-qualification.ts +101 -7
  111. package/scripts/check-install-container.sh +10 -2
  112. package/scripts/check-install-surface.ts +1 -1
  113. package/scripts/check-keyset-overlap.py +1 -1
  114. package/scripts/check-meta-facts.ts +249 -0
  115. package/scripts/check-meta-identity-consumers.ts +1 -1
  116. package/scripts/check-meta-session.ts +169 -0
  117. package/scripts/check-mux-launch-tmux.ts +316 -0
  118. package/scripts/check-mux-launch.ts +288 -0
  119. package/scripts/check-mux-launcher-fence.ts +264 -0
  120. package/scripts/check-mux-parent-artifact.ts +195 -0
  121. package/scripts/check-mux-placement-tmux.ts +322 -0
  122. package/scripts/check-mux-placement.ts +323 -0
  123. package/scripts/check-mux-resume-call.ts +282 -0
  124. package/scripts/check-probe-cli-shim.ts +25 -22
  125. package/scripts/check-probe-ordering.ts +121 -81
  126. package/scripts/check-release-gate-outcomes.ts +452 -0
  127. package/scripts/check-resume-launch-identity.ts +244 -0
  128. package/scripts/check-socket-discovery.ts +1 -1
  129. package/scripts/fixtures/mux-parent-transcript.scrubbed.jsonl +3 -0
  130. package/scripts/inventory-verification-surface.ts +349 -0
  131. package/scripts/lib/claude-launcher-fence.ts +322 -0
  132. package/scripts/lib/live-skip.ts +33 -0
  133. package/scripts/lib/mutation-qualify.ts +109 -3
  134. package/scripts/lib/probe-acp-turn.ts +33 -12
  135. package/scripts/lib/step-outcome.sh +88 -0
  136. package/scripts/meta-bridge-doctor.sh +6 -8
  137. package/scripts/meta-facts.ts +60 -0
  138. package/scripts/mutants/acp-augment.json +106 -0
  139. package/scripts/mutants/acp-cortex.json +2 -2
  140. package/scripts/mutants/acp-overlay.json +17 -0
  141. package/scripts/mutants/acp-prompt-lifecycle.json +100 -0
  142. package/scripts/mutants/acp-stop-reason.json +80 -0
  143. package/scripts/mutants/acp-stream-hooks.json +158 -0
  144. package/scripts/mutants/bridge-boot-resume.json +45 -0
  145. package/scripts/mutants/meta-facts.json +50 -0
  146. package/scripts/mutants/meta-identity.json +36 -0
  147. package/scripts/mutants/mux-boundary.json +196 -0
  148. package/scripts/mutants/mux-fresh-call.json +185 -0
  149. package/scripts/mutants/mux-launcher-fence.json +123 -0
  150. package/scripts/mutants/mux-parent-artifact.json +39 -0
  151. package/scripts/mutants/mux-resume-call.json +148 -0
  152. package/scripts/mutants/probe-ordering.json +6 -1021
  153. package/scripts/mutants/release-gate.json +140 -0
  154. package/scripts/mutants/resume-args.json +76 -0
  155. package/scripts/mutants/resume-launch-identity.json +96 -0
  156. package/scripts/mutants/v2-surface.json +74 -23
  157. package/scripts/mutants/v2-visible-resume.json +215 -0
  158. package/scripts/new-session-id.ts +9 -4
  159. package/scripts/smoke-acp-bundled-mcp-live.ts +4 -3
  160. package/scripts/smoke-acp-carrier-augment-live.ts +2 -2
  161. package/scripts/smoke-acp-cortex-live.ts +8 -14
  162. package/scripts/smoke-acp-long-turn-live.ts +185 -0
  163. package/scripts/smoke-acp-mcp-live.ts +2 -2
  164. package/scripts/smoke-acp-memory-containment-live.ts +2 -2
  165. package/scripts/smoke-acp-ordering-probe-live.ts +2 -2
  166. package/scripts/smoke-acp-overlay-live.ts +2 -2
  167. package/scripts/smoke-acp-provider-live.ts +2 -2
  168. package/scripts/smoke-acp-raw-turn-live.ts +3 -3
  169. package/scripts/smoke-acp-session-reuse-live.ts +2 -2
  170. package/scripts/smoke-acp-skill-live.ts +2 -2
  171. package/scripts/smoke-acp-socket-citizen-live.ts +4 -3
  172. package/scripts/smoke-acp-v2-send-live.ts +4 -3
  173. package/scripts/smoke-agy-native-push-live.ts +10 -20
  174. package/scripts/smoke-claude-native-resume-live.sh +13 -3
  175. package/scripts/smoke-entwurf-chain-live.ts +352 -0
  176. package/scripts/smoke-entwurf-v2-matrix-live.ts +3 -3
  177. package/scripts/smoke-meta-honesty.sh +1 -1
  178. package/scripts/smoke-meta-install-state.sh +4 -0
  179. package/scripts/smoke-mux-fresh-call-live.ts +365 -0
  180. package/scripts/smoke-mux-lifecycle-live.ts +1136 -0
  181. package/scripts/smoke-pi-attach.ts +1 -1
  182. package/scripts/smoke-user-scope-citizen.sh +1 -1
  183. package/scripts/tsconfig.json +1 -0
  184. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-preflight.js +0 -160
  185. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-spawn-production.js +0 -273
  186. package/mcp/entwurf-bridge/dist/pi-extensions/lib/entwurf-v2-spawn.js +0 -216
  187. package/pi-extensions/lib/entwurf-v2-spawn-production.ts +0 -373
  188. package/pi-extensions/lib/entwurf-v2-spawn.ts +0 -323
  189. package/scripts/check-acp-sdk-surface.ts +0 -236
  190. package/scripts/check-entwurf-v2-spawn-production.ts +0 -551
  191. package/scripts/check-entwurf-v2-spawn.ts +0 -399
  192. package/scripts/smoke-entwurf-v2-spawn-live.ts +0 -188
  193. 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 63 claims for THIS gate — each carries a [QK:...] signature appearing
57
- // EXACTLY once below, and check-gate-qualification proves its mutant dies at that
58
- // signature. (The lane also carries the §11-7-c PRODUCER claims, whose signatures
59
- // live in check-probe-cli-shim.ts; one lane, two consuming gates.)
60
- // [QK:*] tokens and qualified claims are 1:1 BY DESIGN: an assertion without a
61
- // mutant carries a plain message, so "killed claim IDs, never assertion counts"
62
- // stays readable. The remaining assertions are enforced-but-not-mutant-qualified.
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 IS mutant-qualified).
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 [QK:PROBE-SEQ-ORDER]",
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
- // §9-4 shape: an undefined session meta omits the `_meta` KEY entirely.
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 (§9-4)",
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 phase timeouts EQUAL backend.ts's — D is only readable against production boundaries [QK:PROBE-TIMEOUTS-MATCH-PRODUCTION]",
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
- const iPrompt = BACKEND_SRC.indexOf("connection.prompt({", iEnforce);
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 [QK:PROBE-NO-PROSE-ERROR-SCAN]",
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 [QK:PROBE-WINDOW-SLACK-IS-CONSTANT]",
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 [QK:RUNNER-EXIT-CONTRACT-SPLIT]",
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 [QK:PROBE-MALFORMED-INVALIDATES]",
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, promptMs: 1000 },
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 [QK:PROBE-FIXTURE-RUNID-REQUIRED] — the §11-7 cross-layer join key",
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} [QK:PROBE-FIXTURE-WIRE-MARKER]`);
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 [QK:PROBE-LOG-ENVELOPE-AUTHORITY]",
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 [QK:PROBE-LOG-EVENT-VOCABULARY]",
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 [QK:PROBE-LOG-ENVELOPE-SCHEMA]",
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 [QK:PROBE-LOG-PAYLOAD-SCHEMA]",
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 [QK:PROBE-LOG-WIRE-MARKER-NAMES-TOOL]",
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 [QK:PROBE-LOG-WINDOW-REASON-VOCAB]",
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 [QK:PROBE-LOG-SEQ-STRICTLY-INCREASING]",
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 [QK:PROBE-LOG-RAW-ORDER-BEFORE-SORT]",
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 [QK:PROBE-LOG-TS-NO-REGRESSION]",
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) [QK:PROBE-LOG-WRITER-KEY-PER-RUN]",
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 [QK:VERDICT-P0-CONTROL-FAIL]");
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 [QK:VERDICT-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 [QK:VERDICT-NOWIRE-CANDIDATE]",
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 [QK:VERDICT-RUNTIME-B-LADDER-OWNS]",
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 [QK:VERDICT-B-REQUIRES-RANAHEAD]",
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 [QK:VERDICT-B-PROMOTION-RULES]");
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(one.verdict, "A-withheld", "one nonzero delay → wait verdict WITHHELD [QK:VERDICT-A-NEEDS-TWO-DELAYS]");
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 [QK:VERDICT-A-TRACKING-BAND]",
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 [QK:VERDICT-SAMEMS-AMBIGUOUS]",
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 is qualified.
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 [QK:VERDICT-RUNNER-EXACTLY-ONCE-INVENTORY]",
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 [QK:VERDICT-CENSORED-NOT-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 [QK:VERDICT-STATUS-NAMES-INVALID-RUNS]",
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 (the mutant for this condition is VERDICT-CENSORED-NOT-CANDIDATE one condition, one kill)",
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 [QK:VERDICT-RUNNER-TOPOLOGY-EXACTLY-ONCE]",
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 [QK:VERDICT-WINDOW-MARKER-COHERENCE]",
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 [QK:VERDICT-CONTROL-HELD-TO-COHERENCE]",
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 [QK:VERDICT-PHASE-SEQUENTIAL]",
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 [QK:VERDICT-PHASE-PRODUCTION-ORDER]",
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 [QK:VERDICT-ORDERING-AXIS-REPORTED]",
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 [QK:VERDICT-EVIDENCE-EXPOSES-DELTAS]",
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 [QK:VERDICT-B-WINDOW-IS-PROMPT]",
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 [QK:PROBE-TARGET-AMBIENT-REFUSED]",
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 [QK:PROBE-TARGET-NATIVE-BRANCH-ONLY]",
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 [QK:PROBE-SCRUB-EXACT-ALLOWLIST]",
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, so each [QK:] names exactly what
1977
- // its mutant kills (bundling four claims under one token let one mutant
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 [QK:RUNNER-TARGET-PRECONDITION-PINNED]",
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 [QK:RUNNER-SNAPSHOT-CHANNEL-ARMED]",
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) [QK:RUNNER-TARGET-IDENTITY-IN-ROSTER]",
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 [QK:RUNNER-DRIFT-REHASH-PINNED]",
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 [QK:RUNNER-ARMING-ORDER]",
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 [QK:RUNNER-SHIM-OVERRIDE-EXACT]",
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
- "[QK:RUNNER-SHIM-PRECONDITION-PINNED]",
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 [QK:RUNNER-SHIM-RUNTIME-PINNED]",
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 [QK:RUNNER-SHIM-RUNTIME-GRAPH-EXACT]",
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 [QK:RUNNER-SHIM-ON-DISK-NATIVE]",
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 [QK:PROBE-LOG-SNAPSHOT-PAYLOAD]",
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 [QK:VERDICT-SNAPSHOT-PROMOTES]",
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) [QK:VERDICT-SNAPSHOT-INSTRUMENT-ABSENT]",
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 [QK:VERDICT-SNAPSHOT-CALIBRATION]",
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 [QK:VERDICT-SNAPSHOT-REQUIRES-RANAHEAD]",
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 [QK:VERDICT-SNAPSHOT-INTERVAL-UNORDERED]",
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 [QK:VERDICT-SNAPSHOT-ORDINAL-EXACTLY-ONE]",
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 [QK:VERDICT-SNAPSHOT-NEEDS-INSTRUMENT-FLAG]",
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 [QK:VERDICT-SNAPSHOT-BINDING-RECEIVE-AXIS]",
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 [QK:VERDICT-SNAPSHOT-STRUCTURAL-INVALIDATES]",
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 [QK:VERDICT-SNAPSHOT-TARGET-IDENTITY]",
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 [QK:VERDICT-SNAPSHOT-CONTAINS-ID-BLOCKS]",
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
  }