@vostride/agent-qa-agent 0.2.2 → 0.2.4

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/dist/index.js CHANGED
@@ -61,17 +61,7 @@ var FunctionNotBoundError = class extends Error {
61
61
  var AGENT_ASK_FUNCTION_ID = "agent.ask";
62
62
  var agentAskSpec = {
63
63
  id: AGENT_ASK_FUNCTION_ID,
64
- // THE SUMMARY SAYS "ENTRY POINT", and that wording is a fix rather than a flourish (392-09, from
65
- // the first live gate run). D-07 projects every registry op 1:1 into the planner's tool set, so
66
- // this op is a tool the planner is offered WHILE IT IS INSIDE ONE OF ITS OWN TURNS. Described as
67
- // "run one conversational turn", it read to the model as the natural home for any conversational
68
- // request: a plain request to list runs was planned as `agent.ask` twice out of two live runs,
69
- // and the recursive turn then failed — so a person asking for a capability in prose got a failed
70
- // turn instead of the capability. The repair is DESCRIPTION, not exclusion. Hiding a tool would
71
- // break the always-send-all law and D-07's registry projection, and it would be the wrong
72
- // diagnosis anyway: the tool was not misclassified, it was misdescribed. Every consumer of this
73
- // string — the catalogue, `fn --list`, 396's MCP listing, the planner — is better served by the
74
- // sentence that says what the op is FOR.
64
+ // Worded as an entry point, because a summary saying "one turn" made the planner recurse into it.
75
65
  summary: "The chat entry point: begin ONE new conversational turn from outside a conversation. Surfaces (a REPL, the ask verb, an MCP client) call this to START a turn \u2014 it is not a way to carry out a request while a turn is already running. It routes the utterance, executes what it resolves to, and answers with per-clause receipts.",
76
66
  resultKind: "unary",
77
67
  // Bound where a composition assembles a turn engine. A host with no engine lists this
@@ -80,34 +70,9 @@ var agentAskSpec = {
80
70
  boundThrough: "chatTurn",
81
71
  input: AgentAskInputSchema,
82
72
  output: AgentAskResultSchema,
83
- // `readOnly: false`, and the honesty matters more here than on any other op in the catalogue: a
84
- // turn is a variable-reach capability, so describing it as a read would let a risk ladder treat
85
- // the door to every write verb as the cheapest thing in the registry. (2026-10-01, 432-26: the
86
- // descriptor is a classification; no gate reads it to ask or deny.)
73
+ // Not read-only: a turn can reach any write verb.
87
74
  risk: { name: `fn:${AGENT_ASK_FUNCTION_ID}`, recognized: true, readOnly: false },
88
- /**
89
- * A TURN's own report of how it went (2026-08-20, Phase 399.1 D-01/D-02/D-12).
90
- *
91
- * A TURN IS A COMPOSITION, SO ITS VERDICT IS A FOLD OF ITS CLAUSES' VERDICTS. This used to fold
92
- * the turn STATUS alone, on the argument that the per-clause verdicts were already receipts and
93
- * reading them here would be a second opinion. That argument is the recorded belief that produced
94
- * gap 1: a turn whose only clause was REFUSED by a credential guardrail reported `executed` one
95
- * composition layer up, and the person reading the outer line was told a blocked mutation had been
96
- * carried out. The receipts are not a second opinion about the turn — they ARE the turn, and the
97
- * status is a summary of them. Success is the one claim a fold may never default to (D-03's
98
- * asymmetry): where the clauses do not support it, the outer word must say what they do support.
99
- *
100
- * `fail_closed` IS `failed` AND NOT `refused`. D-02 minted `refused` for a GUARDRAIL declining an
101
- * op; a fail-closed turn is the ENGINE abandoning the clauses after one of them broke, which is
102
- * an operation that did not complete. Reporting it `refused` would tell a person a control
103
- * declined their request when what happened is that something went wrong.
104
- *
105
- * AND THERE IS DELIBERATELY NO `failed` SCAN OVER THE RECEIPTS HERE (D-12). Post-399.1-09 the
106
- * engine derives its aggregate from the same vocabulary, so it can no longer answer `completed`
107
- * over a `failed` receipt; a second mechanism second-guessing the first is the redundant-mechanism
108
- * failure mode this project has already paid for, and it would hide a regression in the engine
109
- * rather than surface it.
110
- */
75
+ // A turn's verdict folds its clauses' receipts, and `fail_closed` is `failed`, never `refused`.
111
76
  classify(output) {
112
77
  switch (output.status) {
113
78
  case "completed": {
@@ -148,7 +113,6 @@ import {
148
113
  exercisedAnchors as exercisedAnchors2,
149
114
  failingStepOf as failingStepOf4,
150
115
  failureFingerprint as failureFingerprint2,
151
- projectVerdictMisdirection,
152
116
  resolveRunVerdicts as resolveRunVerdicts2,
153
117
  slugBranch as slugBranch2
154
118
  } from "@vostride/agent-qa-schema";
@@ -280,18 +244,14 @@ function renderVerdictSentence(verdict, parts, provider, query) {
280
244
 
281
245
  // src/jobs/kinds.ts
282
246
  import {
283
- asRecordId,
284
247
  canonicalJson as canonicalJson3,
285
248
  CURRENT_ANALYSIS_SCHEMA_VERSION as CURRENT_ANALYSIS_SCHEMA_VERSION2,
286
249
  CurationSnapshotSchema as CurationSnapshotSchema2,
287
250
  errorTextOf as errorTextOf2,
288
251
  failingStepOf as failingStepOf3,
289
- MisdirectionRecordResultSchema,
290
- RunNotificationSchemaError as RunNotificationSchemaError2,
291
252
  sha256hex,
292
253
  slugBranch,
293
- stepOutcomeUnproven,
294
- validateAnalysisAgainstHandoff as validateAnalysisAgainstHandoff2
254
+ stepOutcomeUnproven
295
255
  } from "@vostride/agent-qa-schema";
296
256
  import { deterministicVerdict, UNKNOWN_SIGNAL_RATIONALE } from "@vostride/agent-qa-runner";
297
257
 
@@ -517,73 +477,17 @@ function misdirectionPathSteps(verdict, parts, test) {
517
477
  if (failing !== void 0) indexes.add(failing.stepIndex);
518
478
  return test.steps.filter((step) => indexes.has(step.stepIndex));
519
479
  }
520
- function misdirectionValidationOptions(evidence, rootPosture) {
521
- return evidence === void 0 ? {} : { gatedProducerEvidence: true, rootPosture };
522
- }
523
- function exposureKey(stepIndex, recordId) {
524
- return `${String(stepIndex)}${recordId}`;
525
- }
526
- function summarizeCrossCheck(rows, sites) {
527
- if (rows.some((row) => row.crossCheck === "flipped")) return "flipped";
528
- if (rows.some((row) => row.crossCheck === "inconclusive")) return "inconclusive";
529
- const covered = new Set(rows.map((row) => exposureKey(row.stepIndex, String(row.recordId))));
530
- const complete = sites.every(
531
- (site) => covered.has(exposureKey(site.stepIndex, String(site.recordId)))
532
- );
533
- return complete && rows.every((row) => row.crossCheck === "held") ? "held" : "not-run";
534
- }
535
- function postureReason(posture) {
536
- if (posture === "no-eval-root") {
537
- return "no evaluation root existed, so no record on this path could be cross-checked";
538
- }
539
- if (posture === "broken-eval-root") {
540
- return "the evaluation root could not be consulted, so no record on this path was cross-checked";
541
- }
542
- return void 0;
543
- }
544
- function stampVerdictMisdirectionEvidence(input) {
545
- const { verdict, parts, test, evidence, rootPosture } = input;
546
- if (evidence === void 0 || test === void 0) return verdict;
480
+ function stampVerdictMemoryOnPath(input) {
481
+ const { verdict, parts, test } = input;
482
+ if (test === void 0) return verdict;
547
483
  const steps = misdirectionPathSteps(verdict, parts, test);
548
- const sites = deriveMemoryOnPathSites(steps);
549
- if (sites.length === 0) {
550
- if (verdict.memoryOnPath === void 0 && verdict.misdirectionRuledOut === void 0) {
551
- return verdict;
552
- }
484
+ if (deriveMemoryOnPathSites(steps).length === 0) {
485
+ if (verdict.memoryOnPath === void 0) return verdict;
553
486
  const bare = { ...verdict };
554
487
  delete bare.memoryOnPath;
555
- delete bare.misdirectionRuledOut;
556
488
  return bare;
557
489
  }
558
- const keys = new Set(sites.map((site) => exposureKey(site.stepIndex, String(site.recordId))));
559
- const rows = evidence.filter(
560
- (row) => row.perTest === verdict.perTest && keys.has(exposureKey(row.stepIndex, String(row.recordId)))
561
- );
562
- const reason = postureReason(rootPosture);
563
- const block = {
564
- crossCheck: summarizeCrossCheck(rows, sites),
565
- results: rows,
566
- ...reason === void 0 ? {} : { reason },
567
- // D-19: the ONE exemption is ABSENCE, and only absence. A broken root publishes the same
568
- // empty ledger and earns nothing. The `rows.length === 0` clause is not belt-and-braces: the
569
- // validator answers on the backing BEFORE it reads the rows, so a marker beside a real ledger
570
- // would skip the ledger entirely.
571
- ...rootPosture === "no-eval-root" && rows.length === 0 ? { backing: "declared-only" } : {}
572
- };
573
- return {
574
- ...verdict,
575
- memoryOnPath: deriveMemoryOnPath(steps),
576
- misdirectionRuledOut: block
577
- };
578
- }
579
- function flippedRecordsOf(verdict) {
580
- const rows = verdict.misdirectionRuledOut?.results ?? [];
581
- const records = [];
582
- for (const row of rows) {
583
- if (row.crossCheck !== "flipped") continue;
584
- if (!records.includes(String(row.recordId))) records.push(String(row.recordId));
585
- }
586
- return records;
490
+ return { ...verdict, memoryOnPath: deriveMemoryOnPath(steps) };
587
491
  }
588
492
  var GroundingQuoteWire = z2.union([
589
493
  z2.object({ stepIndex: z2.number(), quote: z2.string() }),
@@ -700,23 +604,7 @@ function historyLine(pack, history) {
700
604
  const window = record.lastOutcomes.map((entry2) => `build ${entry2.buildOrdinal}: ${entry2.passed ? "passed" : "failed"}`).join(", ");
701
605
  return `- test ${pack.testIndex} (${pack.testName}): ${record.passes} passes, ${record.failures} failures, ${record.heals} heals, flakeCount ${record.flakeCount}` + (window === "" ? "" : `; recent: ${window}`);
702
606
  }
703
- function crossCheckLine(row) {
704
- const samples = row.samples === void 0 ? "" : `; k=${String(row.samples.k)}`;
705
- const reason = row.reason === void 0 ? "" : `; ${row.reason}`;
706
- return `- test ${String(row.perTest)}, step ${String(row.stepIndex)}, record ${String(row.recordId)}: ${row.crossCheck}${reason}${samples}`;
707
- }
708
- function crossCheckTier(packs, evidence) {
709
- if (evidence === void 0) return [];
710
- const analyzed = new Set(packs.map((pack) => pack.testIndex));
711
- const rows = evidence.filter((row) => analyzed.has(row.perTest));
712
- return [
713
- {
714
- title: "Memory misdirection cross-check (reasoning input)",
715
- body: "The deterministic removal cross-check already ran for the low-trust records this run served on the failing paths below. `flipped` means removing the record changed the outcome \u2014 the served memory is implicated. `held` means it did not. `inconclusive` and `not-run` mean nothing was established:\n" + (rows.length === 0 ? "- no record on an analyzed test\u2019s failing path was cross-checked" : rows.map(crossCheckLine).join("\n"))
716
- }
717
- ];
718
- }
719
- function contextTier(handoff, packs, history, evidence) {
607
+ function contextTier(handoff, packs, history) {
720
608
  return [
721
609
  {
722
610
  title: "Run facts",
@@ -725,8 +613,7 @@ function contextTier(handoff, packs, history, evidence) {
725
613
  {
726
614
  title: "Failure history (reasoning input)",
727
615
  body: "Recorded outcome history per analyzed test \u2014 evidence for flake-vs-app_bug reasoning, not prose to restate:\n" + packs.map((pack) => historyLine(pack, history)).join("\n")
728
- },
729
- ...crossCheckTier(packs, evidence)
616
+ }
730
617
  ];
731
618
  }
732
619
  function volatileTier(packs) {
@@ -788,8 +675,7 @@ function evaluateAttempt(text, pending, input) {
788
675
  const reason = "the output was not one parseable JSON object matching the contract: " + (error instanceof Error ? error.message : String(error));
789
676
  return {
790
677
  accepted: /* @__PURE__ */ new Map(),
791
- rejects: [...pending].map((perTest) => ({ perTest, reason })),
792
- citedByTest: /* @__PURE__ */ new Map()
678
+ rejects: [...pending].map((perTest) => ({ perTest, reason }))
793
679
  };
794
680
  }
795
681
  const byTest = /* @__PURE__ */ new Map();
@@ -799,9 +685,7 @@ function evaluateAttempt(text, pending, input) {
799
685
  }
800
686
  const rejects = [];
801
687
  const candidates = /* @__PURE__ */ new Map();
802
- const citedByTest = /* @__PURE__ */ new Map();
803
688
  const testsByIndex = new Map(handoff.tests.map((test) => [test.testIndex, test]));
804
- const rootPosture = input.rootPosture ?? "eval-root";
805
689
  for (const perTest of pending) {
806
690
  const entry2 = byTest.get(perTest);
807
691
  if (entry2 === void 0) {
@@ -809,15 +693,12 @@ function evaluateAttempt(text, pending, input) {
809
693
  continue;
810
694
  }
811
695
  const pair = candidateOf(entry2);
812
- citedByTest.set(perTest, [...pair.verdict.evidence.citedSteps]);
813
696
  candidates.set(perTest, {
814
697
  ...pair,
815
- verdict: stampVerdictMisdirectionEvidence({
698
+ verdict: stampVerdictMemoryOnPath({
816
699
  verdict: pair.verdict,
817
700
  parts: pair.parts,
818
- test: testsByIndex.get(perTest),
819
- evidence: input.misdirectionEvidence,
820
- rootPosture
701
+ test: testsByIndex.get(perTest)
821
702
  })
822
703
  });
823
704
  }
@@ -830,8 +711,7 @@ function evaluateAttempt(text, pending, input) {
830
711
  verdicts: ordered.map((pair) => pair.verdict),
831
712
  parts: ordered.map((pair) => pair.parts)
832
713
  },
833
- handoff,
834
- misdirectionValidationOptions(input.misdirectionEvidence, rootPosture)
714
+ handoff
835
715
  );
836
716
  if (!validation.ok) {
837
717
  const rejectedTests = new Set(validation.rejects.map((reject) => reject.perTest));
@@ -839,7 +719,7 @@ function evaluateAttempt(text, pending, input) {
839
719
  for (const perTest of rejectedTests) candidates.delete(perTest);
840
720
  }
841
721
  }
842
- return { accepted: candidates, rejects, citedByTest };
722
+ return { accepted: candidates, rejects };
843
723
  }
844
724
  function repairFeedback(rejects) {
845
725
  return [
@@ -865,7 +745,7 @@ async function authorRunAnalysis(model, input) {
865
745
  const baseRequest = buildTieredPrompt({
866
746
  agent_context: input.agentContext ?? "analyze-run: authoring root-cause verdicts and sentence parts for one completed run",
867
747
  stable: stableTier(),
868
- context: contextTier(input.handoff, compacted.packs, input.history, input.misdirectionEvidence),
748
+ context: contextTier(input.handoff, compacted.packs, input.history),
869
749
  volatile: volatileTier(compacted.packs)
870
750
  });
871
751
  const images = collectImages(compacted.packs, input.readEvidenceBlob, model.vision === true);
@@ -873,7 +753,6 @@ async function authorRunAnalysis(model, input) {
873
753
  const accepted = /* @__PURE__ */ new Map();
874
754
  let pending = new Set(analyzed);
875
755
  let lastRejects = [];
876
- const lastCited = /* @__PURE__ */ new Map();
877
756
  let modelCalls = 0;
878
757
  for (let attempt = 0; attempt <= AUTHOR_REPAIR_CAP && pending.size > 0; attempt += 1) {
879
758
  const attemptRequest = attempt === 0 ? request : { ...request, prompt: `${request.prompt}
@@ -882,7 +761,6 @@ ${repairFeedback(lastRejects)}` };
882
761
  const output = await model.generate(attemptRequest);
883
762
  modelCalls += 1;
884
763
  const outcome = evaluateAttempt(output.text, pending, input);
885
- for (const [perTest, cited] of outcome.citedByTest) lastCited.set(perTest, cited);
886
764
  for (const [perTest, pair] of outcome.accepted) accepted.set(perTest, pair);
887
765
  pending = new Set([...pending].filter((perTest) => !accepted.has(perTest)));
888
766
  lastRejects = outcome.rejects;
@@ -901,7 +779,6 @@ ${repairFeedback(lastRejects)}` };
901
779
  estimatedTokens: compacted.estimatedTokens
902
780
  },
903
781
  degradedTests: analyzed.filter((testIndex) => !accepted.has(testIndex)),
904
- degradedPaths: analyzed.filter((testIndex) => !accepted.has(testIndex)).map((perTest) => ({ perTest, citedSteps: lastCited.get(perTest) ?? [] })),
905
782
  usage: {
906
783
  modelCalls,
907
784
  repairCalls: Math.max(0, modelCalls - 1),
@@ -918,22 +795,13 @@ import {
918
795
  } from "@vostride/agent-qa-schema";
919
796
  var POST_RUN_CHAIN_TERMINAL_KINDS = Object.freeze([
920
797
  "analyze-run",
921
- "curate-memory",
922
- "propose-improvements"
798
+ "curate-memory"
923
799
  ]);
924
800
  function chainJobIdsForRun(runId, idSource) {
925
801
  const analyzeRun = idSource.jobIdFor("analyze-run", runId);
926
- const curateMemory = idSource.jobIdFor(
927
- "curate-memory",
928
- `${String(analyzeRun)}->curate-memory`
929
- );
930
802
  return {
931
803
  analyzeRun,
932
- curateMemory,
933
- proposeImprovements: idSource.jobIdFor(
934
- "propose-improvements",
935
- `${String(curateMemory)}->propose-improvements`
936
- )
804
+ curateMemory: idSource.jobIdFor("curate-memory", `${String(analyzeRun)}->curate-memory`)
937
805
  };
938
806
  }
939
807
  var UNKNOWN_VERDICTS_MARKER = "unknown";
@@ -944,8 +812,7 @@ function isCount(value) {
944
812
  return typeof value === "number" && Number.isInteger(value) && value >= 0;
945
813
  }
946
814
  function readVerdictSummary(payload) {
947
- const direct = asRecord(payload.verdictSummary);
948
- const summary = isCount(direct.total) ? direct : asRecord(asRecord(payload.upstream).verdictSummary);
815
+ const summary = asRecord(payload.verdictSummary);
949
816
  const byClassSource = asRecord(summary.byClass);
950
817
  if (!isCount(summary.total)) return { total: 0, byClass: {} };
951
818
  const byClass = {};
@@ -956,11 +823,9 @@ function readVerdictSummary(payload) {
956
823
  }
957
824
  function buildChainTerminalNotification(input) {
958
825
  const payload = asRecord(input.payload);
959
- const upstream = asRecord(payload.upstream);
960
- const analysisFailed = payload.verdicts === UNKNOWN_VERDICTS_MARKER || asRecord(payload.upstream).analysisStatus === "failed" || input.finishingKind === "analyze-run" && input.finished === "failed";
961
- const proposalTerminal = input.finishingKind === "propose-improvements";
962
- const committed = proposalTerminal ? asRecord(upstream.curation) : asRecord(input.publishedValues.get("bundle-committed"));
963
- const curationSucceeded = proposalTerminal || input.finishingKind === "curate-memory" && input.finished === "done";
826
+ const analysisFailed = payload.verdicts === UNKNOWN_VERDICTS_MARKER || input.finishingKind === "analyze-run" && input.finished === "failed";
827
+ const committed = asRecord(input.publishedValues.get("bundle-committed"));
828
+ const curationSucceeded = input.finishingKind === "curate-memory" && input.finished === "done";
964
829
  const snapshot = curationSucceeded && committed.curationSnapshot !== void 0 ? CurationSnapshotSchema.safeParse(committed.curationSnapshot) : void 0;
965
830
  if (snapshot !== void 0 && !snapshot.success) {
966
831
  throw new RunNotificationSchemaError("its curation snapshot is invalid");
@@ -973,22 +838,14 @@ function buildChainTerminalNotification(input) {
973
838
  ...isCount(committed.deprecated) ? { deprecated: committed.deprecated } : {},
974
839
  ...snapshot?.success ? { curationSnapshot: snapshot.data } : {}
975
840
  } : { status: "failed" };
976
- const authored = asRecord(input.publishedValues.get("proposals-authored"));
977
- const proposalsSucceeded = proposalTerminal && input.finished === "done";
978
- const proposals = !proposalTerminal ? void 0 : proposalsSucceeded ? {
979
- status: "done",
980
- ...isCount(authored.proposed) ? { proposed: authored.proposed } : {},
981
- ...isCount(authored.skipped) ? { skipped: authored.skipped } : {}
982
- } : { status: "failed" };
983
841
  return {
984
842
  schemaVersion: CURRENT_RUN_NOTIFICATION_SCHEMA_VERSION,
985
843
  runId: input.runId,
986
- outcome: analysisFailed || !curationSucceeded || proposalTerminal && !proposalsSucceeded ? "failed" : "done",
844
+ outcome: analysisFailed || !curationSucceeded ? "failed" : "done",
987
845
  verdictSummary: readVerdictSummary(payload),
988
846
  stages: {
989
847
  analysis: { status: analysisFailed ? "failed" : "done" },
990
- curation,
991
- ...proposals === void 0 ? {} : { proposals }
848
+ curation
992
849
  }
993
850
  };
994
851
  }
@@ -1246,7 +1103,6 @@ function analyzeRunFallback(input) {
1246
1103
  );
1247
1104
  }
1248
1105
  var ANALYZE_RUN_SUCCESSORS = ["curate-memory"];
1249
- var CURATE_MEMORY_SUCCESSORS = ["propose-improvements"];
1250
1106
  function asRecord2(value) {
1251
1107
  return value !== null && typeof value === "object" && !Array.isArray(value) ? value : {};
1252
1108
  }
@@ -1261,51 +1117,9 @@ function readAnalyzeRunPayload(value) {
1261
1117
  handoffSchemaVersion: record.handoffSchemaVersion,
1262
1118
  executionContext: typeof record.executionContext === "string" ? record.executionContext : void 0,
1263
1119
  ...record.gateRun === true ? { gateRun: true } : {},
1264
- ...record.excludedFromOutcomes === true ? { excludedFromOutcomes: true } : {},
1265
- ...readCarriedMisdirectionEvidence(record.misdirectionEvidence)
1120
+ ...record.excludedFromOutcomes === true ? { excludedFromOutcomes: true } : {}
1266
1121
  };
1267
1122
  }
1268
- function readCarriedMisdirectionEvidence(value) {
1269
- if (value === void 0) return {};
1270
- if (!Array.isArray(value)) return { misdirectionEvidenceUnreadable: true };
1271
- const rows = [];
1272
- for (const candidate of value) {
1273
- const parsed = MisdirectionRecordResultSchema.safeParse(candidate);
1274
- if (!parsed.success) return { misdirectionEvidenceUnreadable: true };
1275
- rows.push(parsed.data);
1276
- }
1277
- return { misdirectionEvidence: rows };
1278
- }
1279
- function readProposeImprovementsPayload(value) {
1280
- const record = asRecord2(value);
1281
- if (typeof record.runId !== "string" || record.runId === "") return void 0;
1282
- const upstreamSource = asRecord2(record.upstream);
1283
- const curationSource = asRecord2(upstreamSource.curation);
1284
- const summary = asRecord2(upstreamSource.verdictSummary);
1285
- const snapshot = curationSource.curationSnapshot === void 0 ? void 0 : CurationSnapshotSchema2.safeParse(curationSource.curationSnapshot);
1286
- if (snapshot !== void 0 && !snapshot.success) {
1287
- throw new RunNotificationSchemaError2("its curation snapshot is invalid");
1288
- }
1289
- const curation = {
1290
- ...isCount2(curationSource.curated) ? { curated: curationSource.curated } : {},
1291
- ...isCount2(curationSource.deprecated) ? { deprecated: curationSource.deprecated } : {},
1292
- ...curationSource.complete === true ? { complete: true } : {},
1293
- ...snapshot?.success ? { curationSnapshot: snapshot.data } : {}
1294
- };
1295
- return {
1296
- runId: record.runId,
1297
- executionContext: typeof record.executionContext === "string" ? record.executionContext : void 0,
1298
- candidateIds: Array.isArray(record.candidateIds) ? record.candidateIds.filter((id) => typeof id === "string") : [],
1299
- upstream: {
1300
- ...isCount2(summary.total) ? { verdictSummary: summary } : {},
1301
- ...upstreamSource.analysisStatus === "done" || upstreamSource.analysisStatus === "failed" ? { analysisStatus: upstreamSource.analysisStatus } : {},
1302
- ...Object.keys(curation).length === 0 ? {} : { curation }
1303
- }
1304
- };
1305
- }
1306
- function isCount2(value) {
1307
- return typeof value === "number" && Number.isInteger(value) && value >= 0;
1308
- }
1309
1123
  function permanently(reason) {
1310
1124
  return { status: "permanent", reason };
1311
1125
  }
@@ -1353,97 +1167,6 @@ function templateSentenceParts(verdict, handoff) {
1353
1167
  links: []
1354
1168
  };
1355
1169
  }
1356
- function readMisdirectionPosture(capabilities, runId) {
1357
- try {
1358
- return capabilities.readMisdirectionRootPosture?.(runId) ?? "eval-root";
1359
- } catch {
1360
- return "eval-root";
1361
- }
1362
- }
1363
- function honestUnknownFloor(refused2, test) {
1364
- const floor = deterministicVerdict(
1365
- "unknown",
1366
- UNKNOWN_SIGNAL_RATIONALE,
1367
- capturedStepsOnly(refused2.evidence.citedSteps, test),
1368
- refused2.perTest
1369
- );
1370
- return { ...floor, memoryMisdirectionRuledOut: false };
1371
- }
1372
- function capturedStepsOnly(cited, test) {
1373
- if (test === void 0) return [];
1374
- const captured = new Set(test.steps.map((step) => step.stepIndex));
1375
- return test.steps.map((step) => step.stepIndex).filter((stepIndex) => captured.has(stepIndex) && cited.includes(stepIndex));
1376
- }
1377
- function widenRefusedFloorPaths(verdicts, degradedPaths, handoff) {
1378
- if (degradedPaths.length === 0) return [...verdicts];
1379
- const testsByIndex = new Map(handoff.tests.map((test) => [test.testIndex, test]));
1380
- return verdicts.map((verdict) => {
1381
- const refused2 = degradedPaths.find((path) => path.perTest === verdict.perTest);
1382
- if (refused2 === void 0 || verdict.class !== "unknown") return verdict;
1383
- const widened = capturedStepsOnly(
1384
- [...verdict.evidence.citedSteps, ...refused2.citedSteps],
1385
- testsByIndex.get(verdict.perTest)
1386
- );
1387
- if (widened.length === verdict.evidence.citedSteps.length) return verdict;
1388
- return {
1389
- ...verdict,
1390
- evidence: { ...verdict.evidence, citedSteps: widened }
1391
- };
1392
- });
1393
- }
1394
- function enforceMisdirectionFloor(input) {
1395
- const { handoff, runId, evidence, rootPosture, partsOf } = input;
1396
- const testsByIndex = new Map(handoff.tests.map((test) => [test.testIndex, test]));
1397
- const stampAll = (verdicts, parts2) => verdicts.map(
1398
- (verdict, index) => stampVerdictMisdirectionEvidence({
1399
- verdict,
1400
- parts: parts2[index],
1401
- test: testsByIndex.get(verdict.perTest),
1402
- evidence,
1403
- rootPosture
1404
- })
1405
- );
1406
- const parts = partsOf(input.verdicts);
1407
- if (evidence === void 0) return { verdicts: [...input.verdicts], parts };
1408
- const stamped = stampAll(input.verdicts, parts);
1409
- const artifact = {
1410
- schemaVersion: CURRENT_ANALYSIS_SCHEMA_VERSION2,
1411
- runId,
1412
- verdicts: stamped,
1413
- parts
1414
- };
1415
- const gated = validateAnalysisAgainstHandoff2(
1416
- artifact,
1417
- handoff,
1418
- misdirectionValidationOptions(evidence, rootPosture)
1419
- );
1420
- let downgraded = stamped;
1421
- if (!gated.ok) {
1422
- const historical = validateAnalysisAgainstHandoff2(artifact, handoff);
1423
- const preExisting = new Set(historical.ok ? [] : historical.rejects.map((r) => r.perTest));
1424
- const newlyRefused = new Set(
1425
- gated.rejects.map((reject) => reject.perTest).filter((perTest) => !preExisting.has(perTest))
1426
- );
1427
- if (newlyRefused.size > 0) {
1428
- downgraded = input.verdicts.map(
1429
- (verdict) => newlyRefused.has(verdict.perTest) ? honestUnknownFloor(verdict, testsByIndex.get(verdict.perTest)) : verdict
1430
- );
1431
- }
1432
- }
1433
- const finalParts = partsOf(downgraded);
1434
- const finalVerdicts = stampAll(downgraded, finalParts).map(withCausalAttribution);
1435
- return { verdicts: finalVerdicts, parts: finalParts };
1436
- }
1437
- function withCausalAttribution(verdict) {
1438
- if (verdict.class !== "unknown") return verdict;
1439
- const flipped = flippedRecordsOf(verdict);
1440
- if (flipped.length === 0) return verdict;
1441
- return {
1442
- ...verdict,
1443
- memoryMisdirectionRuledOut: false,
1444
- attribution: { records: flipped.map((record) => asRecordId(record)), anchors: [] }
1445
- };
1446
- }
1447
1170
  function realAnalyzeRunHandler(capabilities) {
1448
1171
  return async (context) => {
1449
1172
  const payload = readAnalyzeRunPayload(context.payload);
@@ -1470,26 +1193,9 @@ function realAnalyzeRunHandler(capabilities) {
1470
1193
  `the handoff for ${payload.runId} no longer digests to the bytes this job was enqueued against, so analysing it would answer about a different run`
1471
1194
  );
1472
1195
  }
1473
- if (payload.misdirectionEvidenceUnreadable === true) {
1474
- return permanently(
1475
- `the analyze-run payload for ${payload.runId} carries a circularity ledger this build cannot read, and a claim that cannot be checked is not a claim that may be ignored`
1476
- );
1477
- }
1478
- const publishedEvidence = handoff.misdirectionEvidence;
1479
- if (payload.misdirectionEvidence !== void 0 && (publishedEvidence === void 0 || canonicalJson3(payload.misdirectionEvidence) !== canonicalJson3(publishedEvidence))) {
1480
- return permanently(
1481
- `the analyze-run payload for ${payload.runId} carries circularity evidence that is not the evidence in the analysed handoff bytes; the digest covers the handoff, so a copy that disagrees with it is not about these bytes`
1482
- );
1483
- }
1484
- const rootPosture = readMisdirectionPosture(capabilities, payload.runId);
1485
1196
  const authored = await context.recordLlmAuthorship(
1486
1197
  "verdicts-authored",
1487
- // The full author (389 plan 04): the model capability arrives ONLY as this
1488
- // callback's argument. Unknown floor members are authored through
1489
- // `authorRunAnalysis` — one whole-run call, one repair, per-test degrade — and
1490
- // everything else passes through byte-identical; a floor with no unknown members
1491
- // (or no failures at all) reaches no model. Signature, artifact-write position and
1492
- // returned shape are final from plan 01.
1198
+ // The model arrives only as this argument; a floor with no unknown members reaches no model.
1493
1199
  async (model) => {
1494
1200
  const floor = projectHandoffVerdicts(handoff);
1495
1201
  const limitStopped = new Set(
@@ -1512,37 +1218,33 @@ function realAnalyzeRunHandler(capabilities) {
1512
1218
  floor,
1513
1219
  packs: buildEvidencePacks(handoff, unknownTests, history),
1514
1220
  history,
1515
- // The ledger reaches the author as DATA and the gate as AUTHORITY (D-20). Absent, it
1516
- // does neither: the request is byte-identical to the pre-422 one and the author's own
1517
- // validation runs under the historical options.
1518
- ...publishedEvidence === void 0 ? {} : { misdirectionEvidence: publishedEvidence, rootPosture },
1519
1221
  ...capabilities.analysisTokenBudget === void 0 ? {} : { tokenBudget: capabilities.analysisTokenBudget },
1520
1222
  // runId-curried so the author sees the plain `(sha) => bytes` shape.
1521
1223
  ...capabilities.readEvidenceBlob === void 0 ? {} : {
1522
1224
  readEvidenceBlob: (sha) => capabilities.readEvidenceBlob?.(payload.runId, sha)
1523
1225
  }
1524
1226
  });
1525
- verdicts2 = publishedEvidence === void 0 ? result.verdicts : widenRefusedFloorPaths(result.verdicts, result.degradedPaths, handoff);
1227
+ verdicts2 = result.verdicts;
1526
1228
  for (const parts of result.authoredParts) authoredParts.set(parts.perTest, parts);
1527
1229
  }
1528
- const partsOf = (candidates) => candidates.map(
1230
+ const sentenceParts = verdicts2.map(
1529
1231
  (verdict) => verdict.producer === "analyze-run" ? authoredParts.get(verdict.perTest) ?? templateSentenceParts(verdict, handoff) : templateSentenceParts(verdict, handoff)
1530
1232
  );
1531
- const guarded = enforceMisdirectionFloor({
1532
- verdicts: verdicts2,
1533
- handoff,
1534
- runId: payload.runId,
1535
- evidence: publishedEvidence,
1536
- rootPosture,
1537
- partsOf
1538
- });
1233
+ const testsByIndex = new Map(handoff.tests.map((test) => [test.testIndex, test]));
1234
+ const stamped = verdicts2.map(
1235
+ (verdict, index) => stampVerdictMemoryOnPath({
1236
+ verdict,
1237
+ parts: sentenceParts[index],
1238
+ test: testsByIndex.get(verdict.perTest)
1239
+ })
1240
+ );
1539
1241
  capabilities.persistAnalysis({
1540
1242
  schemaVersion: CURRENT_ANALYSIS_SCHEMA_VERSION2,
1541
1243
  runId: payload.runId,
1542
- verdicts: guarded.verdicts,
1543
- parts: guarded.parts
1244
+ verdicts: stamped,
1245
+ parts: sentenceParts
1544
1246
  });
1545
- return { runId: payload.runId, verdicts: guarded.verdicts };
1247
+ return { runId: payload.runId, verdicts: stamped };
1546
1248
  }
1547
1249
  );
1548
1250
  const verdicts = authored.verdicts;
@@ -1553,9 +1255,7 @@ function realAnalyzeRunHandler(capabilities) {
1553
1255
  { kind: "analyze-run", allowedSuccessors: ANALYZE_RUN_SUCCESSORS },
1554
1256
  String(context.job.id),
1555
1257
  {
1556
- // Pitfall 6: a successor payload without these two is the DAEM-02 bug class — the
1557
- // daemon resolves a claimed job's posture from `payload.executionContext` (386
1558
- // D-09/D-12), and curation cannot find a run directory it was never told about.
1258
+ // Without these the daemon misreads the posture and curation misses the run.
1559
1259
  runId: payload.runId,
1560
1260
  executionContext: payload.executionContext ?? handoff.executionContext,
1561
1261
  verdictSummary: summarizeVerdicts(verdicts)
@@ -1592,275 +1292,12 @@ function realCurateMemoryHandler(capabilities) {
1592
1292
  deprecated: counts.deprecated,
1593
1293
  ...curationSnapshot === void 0 ? {} : { curationSnapshot }
1594
1294
  });
1595
- return {
1596
- status: "complete",
1597
- next: [
1598
- ...successorsOf(
1599
- { kind: "curate-memory", allowedSuccessors: CURATE_MEMORY_SUCCESSORS },
1600
- String(context.job.id),
1601
- {
1602
- // Pitfall 6 / 386 D-09, one edge further along: without these two the
1603
- // daemon-claimed successor resolves a drain-scoped posture and cannot address
1604
- // the run it is proposing about.
1605
- runId: record.runId,
1606
- ...record.executionContext === void 0 ? {} : { executionContext: record.executionContext },
1607
- // Absent when the fold reported none — see the capability's own comment for why the
1608
- // empty list and the missing field are deliberately not the same answer.
1609
- ...counts.candidateIds === void 0 ? {} : { candidateIds: [...counts.candidateIds] },
1610
- upstream: {
1611
- ...record.verdictSummary === void 0 ? {} : { verdictSummary: record.verdictSummary },
1612
- // The dead-analysis marker, restated where the next stage will look for it. The
1613
- // root of THIS payload belongs to the analysis; the root of the next belongs to
1614
- // the fold, so the fact has to be re-carried or it is lost at this edge.
1615
- analysisStatus: record.verdicts === UNKNOWN_VERDICTS_MARKER ? "failed" : "done",
1616
- curation: {
1617
- curated: counts.curated,
1618
- deprecated: counts.deprecated,
1619
- complete: true,
1620
- ...curationSnapshot === void 0 ? {} : { curationSnapshot }
1621
- }
1622
- }
1623
- }
1624
- )
1625
- ]
1626
- };
1627
- };
1628
- }
1629
- var PROPOSE_GATE1_ALLOWANCE_CHECKPOINT = "gate1-allowance-reserved";
1630
- var PROPOSE_DEFAULT_MAX_GATE1_PER_JOB = 4;
1631
- var GATE1_EXCLUDED_RECORD_KINDS = ["procedure", "policy"];
1632
- function isPositiveInteger(value) {
1633
- return typeof value === "number" && Number.isInteger(value) && value > 0;
1634
- }
1635
- function parseProposeGate1Allowance(outcome) {
1636
- if (!outcome.found) return { kind: "absent" };
1637
- const value = asRecord2(outcome.payload?.value);
1638
- const cap = asRecord2(value.cap);
1639
- const ids = value.selectedCandidateIds;
1640
- const unreadable = (reason) => ({ kind: "unreadable", reason });
1641
- if (typeof value.jobId !== "string" || value.jobId === "" || typeof value.runId !== "string" || value.runId === "" || typeof value.product !== "string" || value.product === "" || typeof value.corpusDigest !== "string" || value.corpusDigest === "" || !isPositiveInteger(cap.value) || cap.source !== "config" && cap.source !== "default" || !Array.isArray(ids) || !ids.every((id) => typeof id === "string" && id !== "")) {
1642
- return unreadable("the persisted Gate-1 allowance is not a readable charge");
1643
- }
1644
- if (ids.length > cap.value) {
1645
- return unreadable("the persisted Gate-1 allowance charges more slots than its own cap");
1646
- }
1647
- return {
1648
- kind: "claimed",
1649
- allowance: {
1650
- jobId: value.jobId,
1651
- runId: value.runId,
1652
- product: value.product,
1653
- corpusDigest: value.corpusDigest,
1654
- cap: { value: cap.value, source: cap.source },
1655
- selectedCandidateIds: [...ids]
1656
- }
1657
- };
1658
- }
1659
- function standingGate1(standing) {
1660
- for (const receipt of standing) if (receipt.gate === 1) return receipt;
1661
- return void 0;
1662
- }
1663
- function orderProposalCandidates(bundles, payloadCandidateIds) {
1664
- const byId = /* @__PURE__ */ new Map();
1665
- for (const bundle of bundles) if (!byId.has(bundle.bundleId)) byId.set(bundle.bundleId, bundle);
1666
- const named = [];
1667
- const seen = /* @__PURE__ */ new Set();
1668
- for (const id of payloadCandidateIds) {
1669
- if (seen.has(id)) continue;
1670
- seen.add(id);
1671
- const row = byId.get(id);
1672
- if (row !== void 0) named.push(row);
1673
- }
1674
- const product = named[0]?.product;
1675
- if (product === void 0) return { product: void 0, ordered: [] };
1676
- const sweep = bundles.filter(
1677
- (bundle) => bundle.product === product && !seen.has(bundle.bundleId) && bundle.status === "candidate"
1678
- ).sort(
1679
- (left, right) => left.openedAtOrdinal - right.openedAtOrdinal || (left.bundleId < right.bundleId ? -1 : left.bundleId > right.bundleId ? 1 : 0)
1680
- );
1681
- return { product, ordered: [...named, ...sweep] };
1682
- }
1683
- var NO_GATE1_WORK = {
1684
- reservedGate1: [],
1685
- screenedGate1: [],
1686
- recoveredGate1Receipts: [],
1687
- interruptedGate1: [],
1688
- ungatedRemaining: [],
1689
- kindExcluded: [],
1690
- stale: []
1691
- };
1692
- async function reconcileGate1Allowance(allowance, gateReceiptsFor) {
1693
- const recovered = [];
1694
- const interrupted = [];
1695
- for (const candidateId of allowance.selectedCandidateIds) {
1696
- let standing;
1697
- try {
1698
- const gates = await gateReceiptsFor({ candidateId });
1699
- standing = gates.kind === "found" ? standingGate1(gates.standing) : void 0;
1700
- } catch {
1701
- standing = void 0;
1702
- }
1703
- if (standing !== void 0 && standing.corpusVersion.corpusDigest === allowance.corpusDigest) {
1704
- recovered.push({ candidateId, verdict: standing.verdict });
1705
- } else {
1706
- interrupted.push(candidateId);
1707
- }
1708
- }
1709
- return {
1710
- ...NO_GATE1_WORK,
1711
- disposition: "resumed",
1712
- cap: allowance.cap,
1713
- reservedGate1: [...allowance.selectedCandidateIds],
1714
- recoveredGate1Receipts: recovered,
1715
- interruptedGate1: interrupted
1716
- };
1717
- }
1718
- async function screenGate1(capabilities, context, payload) {
1719
- const runReplayGate = capabilities.runReplayGate;
1720
- const readAllowance = capabilities.readProposeGate1Allowance;
1721
- const listBundles = capabilities.listBundles;
1722
- const gateReceiptsFor = capabilities.gateReceiptsFor;
1723
- const readCorpusPosture = capabilities.readProposeCorpusPosture;
1724
- if (runReplayGate === void 0 || readAllowance === void 0 || listBundles === void 0 || gateReceiptsFor === void 0 || readCorpusPosture === void 0) {
1725
- return { ...NO_GATE1_WORK, disposition: "unbound" };
1726
- }
1727
- const jobId = String(context.job.id);
1728
- let read;
1729
- try {
1730
- read = await readAllowance(jobId);
1731
- } catch (error) {
1732
- return {
1733
- ...NO_GATE1_WORK,
1734
- disposition: "allowance-unavailable",
1735
- reason: `the persisted Gate-1 allowance could not be read: ${errorTextOfUnknown(error)}`
1736
- };
1737
- }
1738
- if (read.kind === "unreadable") {
1739
- return { ...NO_GATE1_WORK, disposition: "allowance-unavailable", reason: read.reason };
1740
- }
1741
- if (read.kind === "claimed") {
1742
- const allowance2 = read.allowance;
1743
- if (allowance2.jobId !== jobId || allowance2.runId !== payload.runId) {
1744
- return {
1745
- ...NO_GATE1_WORK,
1746
- disposition: "allowance-unavailable",
1747
- reason: "the persisted Gate-1 allowance was charged for another job or run"
1748
- };
1749
- }
1750
- return await reconcileGate1Allowance(allowance2, gateReceiptsFor);
1751
- }
1752
- const cap = isPositiveInteger(capabilities.proposeGate1MaxPerJob) ? { value: capabilities.proposeGate1MaxPerJob, source: "config" } : { value: PROPOSE_DEFAULT_MAX_GATE1_PER_JOB, source: "default" };
1753
- const { product, ordered } = orderProposalCandidates(await listBundles(), payload.candidateIds);
1754
- if (product === void 0) {
1755
- return { ...NO_GATE1_WORK, disposition: "no-candidates" };
1756
- }
1757
- const posture = await readCorpusPosture({ product });
1758
- if (posture.kind !== "resolved") {
1759
- return {
1760
- ...NO_GATE1_WORK,
1761
- disposition: "posture-unavailable",
1762
- cap,
1763
- reason: posture.kind === "no-root" ? "no committed corpus root, so nothing can be screened against one" : posture.reason
1764
- };
1765
- }
1766
- const kindExcluded = [];
1767
- const stale = [];
1768
- const ungated = [];
1769
- for (const row of ordered) {
1770
- if (GATE1_EXCLUDED_RECORD_KINDS.includes(row.kind)) {
1771
- kindExcluded.push(row.bundleId);
1772
- continue;
1773
- }
1774
- if (row.status !== "candidate") continue;
1775
- const gates = await gateReceiptsFor({ candidateId: row.bundleId });
1776
- if (gates.kind !== "found") continue;
1777
- if (GATE1_EXCLUDED_RECORD_KINDS.includes(gates.recordKind)) {
1778
- kindExcluded.push(row.bundleId);
1779
- continue;
1780
- }
1781
- const standing = standingGate1(gates.standing);
1782
- if (standing !== void 0 && standing.corpusVersion.corpusDigest === posture.corpusDigest) {
1783
- continue;
1784
- }
1785
- if (standing !== void 0) stale.push(row.bundleId);
1786
- ungated.push(row.bundleId);
1787
- }
1788
- const selected = ungated.slice(0, cap.value);
1789
- const remaining = ungated.slice(cap.value);
1790
- if (selected.length === 0) {
1791
- return {
1792
- ...NO_GATE1_WORK,
1793
- disposition: "claimed",
1794
- cap,
1795
- ungatedRemaining: remaining,
1796
- kindExcluded,
1797
- stale
1798
- };
1799
- }
1800
- const allowance = {
1801
- jobId,
1802
- runId: payload.runId,
1803
- product,
1804
- corpusDigest: posture.corpusDigest,
1805
- cap,
1806
- selectedCandidateIds: selected
1807
- };
1808
- await context.publishCheckpoint(PROPOSE_GATE1_ALLOWANCE_CHECKPOINT, allowance);
1809
- const screened = [];
1810
- const interrupted = [];
1811
- for (const candidateId of selected) {
1812
- try {
1813
- const receipt = await runReplayGate(asRecordId(candidateId));
1814
- screened.push({ candidateId, verdict: receipt.verdict });
1815
- } catch {
1816
- interrupted.push(candidateId);
1817
- }
1818
- }
1819
- return {
1820
- disposition: "claimed",
1821
- cap,
1822
- reservedGate1: [...selected],
1823
- screenedGate1: screened,
1824
- recoveredGate1Receipts: [],
1825
- interruptedGate1: interrupted,
1826
- ungatedRemaining: remaining,
1827
- kindExcluded,
1828
- stale
1829
- };
1830
- }
1831
- function errorTextOfUnknown(error) {
1832
- return error instanceof Error ? error.message : String(error);
1833
- }
1834
- function realProposeImprovementsHandler(capabilities) {
1835
- return async (context) => {
1836
- const payload = readProposeImprovementsPayload(context.payload);
1837
- if (payload === void 0) {
1838
- return permanently(
1839
- "the propose-improvements payload names no run, so there is no curated run to propose improvements about"
1840
- );
1841
- }
1842
- const gate1 = await screenGate1(capabilities, context, payload);
1843
- const staged = capabilities.stageAdversarialProposal === void 0 ? void 0 : await capabilities.stageAdversarialProposal({
1844
- runId: payload.runId,
1845
- executionContext: payload.executionContext,
1846
- candidateIds: payload.candidateIds,
1847
- upstream: payload.upstream
1848
- });
1849
- const checkpoint = {
1850
- runId: payload.runId,
1851
- candidateIds: [...payload.candidateIds],
1852
- ...staged === void 0 ? {} : { proposed: staged.proposed, skipped: staged.skipped },
1853
- ...gate1.disposition === "unbound" ? {} : { gate1 }
1854
- };
1855
- await context.publishCheckpoint("proposals-authored", checkpoint);
1856
1295
  return { status: "complete", next: [] };
1857
1296
  };
1858
1297
  }
1859
1298
  function declarationsFor(capabilities) {
1860
1299
  return {
1861
- // JOBQ-04 / C9. Chat is NOT a checkpointed job and never becomes one: its state of record
1862
- // is the append-only session log, and the queue's checkpoint discipline covers background
1863
- // work only. One attempt, because there is no durable progress for a second one to resume.
1300
+ // Chat is never checkpointed: its record is the session log, so one attempt.
1864
1301
  chat: {
1865
1302
  kind: "chat",
1866
1303
  checkpointed: false,
@@ -1883,25 +1320,21 @@ function declarationsFor(capabilities) {
1883
1320
  onPermanentFailure: analyzeRunFallback,
1884
1321
  handler: capabilities === void 0 ? standInHandler("analyze-run", ["verdicts-authored"], ANALYZE_RUN_SUCCESSORS) : realAnalyzeRunHandler(capabilities)
1885
1322
  },
1886
- // Two checkpoints, mirroring the v51 curation lifecycle: candidates are authored, then the
1887
- // bundle is committed. The second is the one whose recovery actually matters.
1323
+ // Two checkpoints: candidates are authored, then the bundle is committed.
1888
1324
  //
1889
- // 422 D-13: it now chains onward to the proposal stage, and `halt-chain` survives that
1890
- // unchanged — deliberately. A fold that gave up permanently committed no bundle, so there is
1891
- // nothing for a proposal stage to propose about; handing it a successor would mint a job whose
1892
- // whole premise is a fold that did not happen.
1325
+ // It declares no successor: its finish is the chain's terminal.
1893
1326
  "curate-memory": {
1894
1327
  kind: "curate-memory",
1895
1328
  checkpointed: true,
1896
1329
  checkpointSchedule: ["candidates-authored", "bundle-committed"],
1897
- allowedSuccessors: CURATE_MEMORY_SUCCESSORS,
1330
+ allowedSuccessors: [],
1898
1331
  maxAttempts: 3,
1899
1332
  backoff: { kind: "exponential", initialDelayMs: 1e3, factor: 2, maxDelayMs: 3e4 },
1900
1333
  onPermanentFailure: "halt-chain",
1901
1334
  handler: capabilities === void 0 ? standInHandler(
1902
1335
  "curate-memory",
1903
1336
  ["candidates-authored", "bundle-committed"],
1904
- CURATE_MEMORY_SUCCESSORS
1337
+ []
1905
1338
  ) : realCurateMemoryHandler(capabilities)
1906
1339
  },
1907
1340
  consolidate: {
@@ -1913,28 +1346,6 @@ function declarationsFor(capabilities) {
1913
1346
  backoff: { kind: "fixed", delayMs: 5e3 },
1914
1347
  onPermanentFailure: "halt-chain",
1915
1348
  handler: standInHandler("consolidate", ["consolidation-authored"], [])
1916
- },
1917
- // The chain's tail since 422 D-13. It declares NO successor — a fourth stage would be one
1918
- // nothing derives an id for — and halts its chain on permanent failure, which here means only
1919
- // that nothing further is enqueued: the curation behind it is committed and stays committed.
1920
- //
1921
- // 422 D-14 adds `gate1-allowance-reserved` AHEAD of `proposals-authored`, and the order is the
1922
- // contract rather than a listing convention: the charge is what a resumed attempt reads to
1923
- // learn what a previous one may already have spent, so it has to be durable before any call
1924
- // is made and therefore before the checkpoint that reports the result.
1925
- "propose-improvements": {
1926
- kind: "propose-improvements",
1927
- checkpointed: true,
1928
- checkpointSchedule: [PROPOSE_GATE1_ALLOWANCE_CHECKPOINT, "proposals-authored"],
1929
- allowedSuccessors: [],
1930
- maxAttempts: 2,
1931
- backoff: { kind: "fixed", delayMs: 5e3 },
1932
- onPermanentFailure: "halt-chain",
1933
- handler: capabilities === void 0 ? standInHandler(
1934
- "propose-improvements",
1935
- [PROPOSE_GATE1_ALLOWANCE_CHECKPOINT, "proposals-authored"],
1936
- []
1937
- ) : realProposeImprovementsHandler(capabilities)
1938
1349
  }
1939
1350
  };
1940
1351
  }
@@ -2000,12 +1411,7 @@ function toRunTestDetail(test, resolved) {
2000
1411
  filePath: test.filePath,
2001
1412
  testIndex: test.testIndex,
2002
1413
  outcome: test.outcome,
2003
- // Step evidence stays in the handoff document; 389's evidence links address it by
2004
- // runId + testIndex. Projecting it here would make the cheapest read the most expensive.
2005
- // The same law covers sentences (389 D-11): this projection carries class/producer/
2006
- // confidence from the resolved verdict and NEVER a rendered sentence — an unbound
2007
- // analysis port answers as artifact-absent (the deterministic floor), so the cheapest
2008
- // read stays cheap in every composition.
1414
+ // Step evidence and sentences stay out, so the cheapest read stays cheap in every composition.
2009
1415
  stepCount: test.steps.length,
2010
1416
  ...resolved === void 0 ? {} : {
2011
1417
  rootCause: {
@@ -2044,12 +1450,7 @@ var runsGetSpec = {
2044
1450
  input: RunsGetInputSchema,
2045
1451
  output: RunDetailSchema,
2046
1452
  risk: { name: `fn:${RUNS_GET_FUNCTION_ID}`, recognized: true, readOnly: true },
2047
- /**
2048
- * A run that came back is a run that was found (399.1 D-01).
2049
- *
2050
- * Absence is `RunNotFoundError`, a throw, which already reaches the engine's `failed` mapping.
2051
- * The verdict a run ENDED with is not this clause's verdict: a failing run is a successful read.
2052
- */
1453
+ // A failing run is a successful read; absence throws `RunNotFoundError`.
2053
1454
  classify(output) {
2054
1455
  return {
2055
1456
  status: "executed",
@@ -2086,20 +1487,12 @@ var analysisGetSpec = {
2086
1487
  id: ANALYSIS_GET_FUNCTION_ID,
2087
1488
  summary: "Read one run's resolved verdicts, rendered sentences and failure clusters.",
2088
1489
  resultKind: "unary",
2089
- // Bound exactly where a composition supplies the artifact reader (D-16 idiom). The
2090
- // OPTIONAL `recurrence` dep is deliberately NOT the binding key: a host without history
2091
- // still serves sentences — the renderer answers the explicit no-history clause — so tying
2092
- // the binding to it would refuse a capability this host can honestly run.
1490
+ // `recurrence` is not the binding key, because a host without history still serves sentences.
2093
1491
  boundThrough: "analysis",
2094
1492
  input: AnalysisGetInputSchema,
2095
1493
  output: AnalysisGetOutputSchema,
2096
1494
  risk: { name: `fn:${ANALYSIS_GET_FUNCTION_ID}`, recognized: true, readOnly: true },
2097
- /**
2098
- * An analysis is an ANSWER, and its verdicts are what it answered (399.1 D-01).
2099
- *
2100
- * COUNTS AND THE RUN REF, never a sentence out of the artifact: the receipt is a map (392's
2101
- * charter) and every verdict sentence is on `results[]`, which D-04 renders in full beside it.
2102
- */
1495
+ // Counts and the run ref only; verdict sentences ride on `results[]`.
2103
1496
  classify(output) {
2104
1497
  return {
2105
1498
  status: "executed",
@@ -2125,8 +1518,7 @@ var analysisGetSpec = {
2125
1518
  const sentence = renderVerdictSentence(
2126
1519
  verdict,
2127
1520
  partsFor(verdict, artifact, document),
2128
- // Recurrence is a LIVE join through the D-08 seam, per call — an absent dep renders
2129
- // the explicit no-history clause, never a dropped section.
1521
+ // Recurrence is a live join per call; an absent dep renders the no-history clause.
2130
1522
  deps.recurrence,
2131
1523
  {
2132
1524
  product,
@@ -2142,15 +1534,10 @@ var analysisGetSpec = {
2142
1534
  const sentenceByPerTest = new Map(sentences.map((entry2) => [entry2.perTest, entry2.sentence]));
2143
1535
  return {
2144
1536
  runId,
2145
- // The verdict's OWN evidence, projected (406 D-01) — cited steps and cited artifacts as the
2146
- // resolved verdict already held them, so the failure view's links are this op's refs and not
2147
- // a second join over `analysis.json`. Presence is consulted OPPORTUNISTICALLY through the
2148
- // optional blob port: `boundThrough` stays `analysis`, because a host with no runtime root
2149
- // still serves a complete analysis — it simply cannot say whether the screenshots survive.
1537
+ // Presence is optional enrichment, so a host with no runtime root still serves the analysis.
2150
1538
  verdicts: await Promise.all(resolved.map(async (verdict) => {
2151
1539
  const test = document.tests.find((candidate) => candidate.testIndex === verdict.perTest);
2152
1540
  const cited = verdict.evidence.citedArtifacts;
2153
- const misdirection = projectVerdictMisdirection(verdict);
2154
1541
  return {
2155
1542
  perTest: verdict.perTest,
2156
1543
  testId: test?.testId ?? "",
@@ -2161,13 +1548,16 @@ var analysisGetSpec = {
2161
1548
  citedSteps: [...verdict.evidence.citedSteps],
2162
1549
  citedArtifacts: await annotatePresence(deps.evidenceBlobs, input.runId, cited)
2163
1550
  },
2164
- ...misdirection === void 0 ? {} : { misdirection }
1551
+ ...verdict.memoryOnPath === void 0 ? {} : {
1552
+ memoryOnPath: verdict.memoryOnPath.map((entry2) => ({
1553
+ recordId: String(entry2.recordId),
1554
+ trust: entry2.trust
1555
+ }))
1556
+ }
2165
1557
  };
2166
1558
  })),
2167
1559
  sentences,
2168
- // Render-time clustering through the ONE pure fold (D-14); the representative carries
2169
- // its already-composed sentence (D-15) — looked up, never re-rendered, so cluster and
2170
- // per-test presentation cannot drift.
1560
+ // The representative's sentence is looked up, never re-rendered, so it cannot drift.
2171
1561
  clusters: clusterVerdicts(resolved, document).map((cluster) => ({
2172
1562
  fingerprint: cluster.fingerprint,
2173
1563
  count: cluster.count,
@@ -2375,7 +1765,7 @@ function addsValueGate(input) {
2375
1765
  return {
2376
1766
  pass: true,
2377
1767
  finding: { novelElements: [...new Set(input.draftElements)], comparedTests: [] },
2378
- reason: "the suite is empty \u2014 no fingerprint coverage and no comparable text, so every element is uncovered (D-16)"
1768
+ reason: "the suite is empty \u2014 no fingerprint coverage and no comparable text, so every element is uncovered"
2379
1769
  };
2380
1770
  }
2381
1771
  const novelElements = novelElementsOf(input.draftElements, union);
@@ -2419,22 +1809,22 @@ function addsValueGate(input) {
2419
1809
  }
2420
1810
  function gateReason(input) {
2421
1811
  if (input.pass && input.unionSize > 0) {
2422
- const decided = `${input.novelElements.length} element(s) absent from the suite union of ${input.unionSize} \u2014 the fingerprint lane decided (D-14)`;
1812
+ const decided = `${input.novelElements.length} element(s) absent from the suite union of ${input.unionSize} \u2014 the fingerprint lane decided`;
2423
1813
  if (input.textCovered.length === 0) {
2424
- return `${decided}, and no evidence-less suite test resembles the draft (D-16)`;
1814
+ return `${decided}, and no evidence-less suite test resembles the draft`;
2425
1815
  }
2426
- return `${decided}; ${input.textCovered.length} evidence-less suite test(s) resemble the draft on the degraded text lane and are recorded per compared test without deciding (D-16)`;
1816
+ return `${decided}; ${input.textCovered.length} evidence-less suite test(s) resemble the draft on the degraded text lane and are recorded per compared test without deciding`;
2427
1817
  }
2428
1818
  if (input.pass) {
2429
- return "the suite fingerprint index is empty, so the degraded text lane decided: no evidence-less suite test resembles the draft (D-16)";
1819
+ return "the suite fingerprint index is empty, so the degraded text lane decided: no evidence-less suite test resembles the draft";
2430
1820
  }
2431
1821
  if (input.unionSize === 0 && input.textCovered.length > 0) {
2432
- return "the text fallback lane matched " + input.textCovered.map((entry2) => `${entry2.filePath}::${entry2.testName}`).join(", ") + ` at or above ${TEXT_FALLBACK_SIMILARITY_THRESHOLD} similarity \u2014 an evidence-less suite test this draft resembles, flagged toward already-covered (D-16)`;
1822
+ return "the text fallback lane matched " + input.textCovered.map((entry2) => `${entry2.filePath}::${entry2.testName}`).join(", ") + ` at or above ${TEXT_FALLBACK_SIMILARITY_THRESHOLD} similarity \u2014 an evidence-less suite test this draft resembles, flagged toward already-covered`;
2433
1823
  }
2434
1824
  if (input.draftElementCount === 0) {
2435
1825
  return "the draft produced no fingerprint elements, so nothing could be shown to be novel \u2014 not a finding that it duplicates the suite (check gate 1 first)";
2436
1826
  }
2437
- return `every element the draft exercised (${input.draftElementCount}) is already in the suite union of ${input.unionSize} (D-14)`;
1827
+ return `every element the draft exercised (${input.draftElementCount}) is already in the suite union of ${input.unionSize}`;
2438
1828
  }
2439
1829
 
2440
1830
  // src/authoring/dedup.ts
@@ -3398,26 +2788,18 @@ async function compileDraft(input, brief, shelf, read) {
3398
2788
  title: "Role",
3399
2789
  body: "You author agent-qa test documents. Emit ONE complete YAML test document and nothing else \u2014 no prose, no fences, no partial edit. Steps are natural language, one per line, and at least one of them must assert concrete expected content. " + REQUEST_WINS_INSTRUCTION
3400
2790
  },
3401
- // The document schema, on the same placement argument and for WINDOWS 111's reason: a brief
3402
- // serving no exemplars must still teach the format. See the constant's own header.
2791
+ // The document format sits in the stable tier, so a brief with no exemplars still teaches it.
3403
2792
  {
3404
2793
  title: TEST_DOCUMENT_FORMAT_BLOCK_TITLE,
3405
2794
  body: TEST_DOCUMENT_FORMAT_INSTRUCTION
3406
2795
  },
3407
- // D-17's syntactic half, in the STABLE tier beside the role it qualifies. It is a standing
3408
- // rule about how any step is written rather than a fact about this intent, so it belongs
3409
- // where the byte prefix is shared across invocations.
2796
+ // Stable tier: a standing rule about writing any step, shared across invocations.
3410
2797
  { title: "Procedures", body: PROCEDURE_BY_REFERENCE_INSTRUCTION }
3411
2798
  ],
3412
2799
  context: [
3413
2800
  { title: "Intent", body: input.intent },
3414
2801
  { title: "Scope", body: `${input.scope.tier}:${input.scope.key}` },
3415
- // WR-07, AND IT RIDES BESIDE THE BLOCK IT CORRECTS. `Scope` renders `${tier}:${key}` and
3416
- // that key is a PRODUCT — `deriveDefaultAuthoringScope` answers `target.product ??
3417
- // targetName` — so a stage asked for a `target` while served only the Scope block was being
3418
- // asked to guess on every project where the two differ. CONTEXT tier and not stable: these
3419
- // names are per-project facts, and the stable tier's shared byte prefix must stay
3420
- // project-independent for the prompt cache the tier law exists to enable.
2802
+ // `Scope` renders a product, not a target name, so the configured targets ride beside it.
3421
2803
  {
3422
2804
  title: CONFIGURED_TARGETS_BLOCK_TITLE,
3423
2805
  body: configuredTargetsBlockBody(targetNames)
@@ -3710,28 +3092,13 @@ var authoringDraftEditSpec = {
3710
3092
  boundThrough: "draftShelf",
3711
3093
  input: AuthoringDraftEditInputSchema,
3712
3094
  output: AuthoringDraftEditOutputSchema,
3713
- // `mutating-local`, and the contrast with `authoring.draft.write` is deliberate. That op declares
3714
- // `mutating-shared` because it spends up to nine child `agent-qa run` processes of a person's
3715
- // compute (D-10). This op takes ONE model call, starts zero runs, and writes one file — the test
3716
- // it edited, over the exact bytes it read (432 D-06). 2026-10-01 (432-19/432-24, D-12/D-15): the
3717
- // tiers once decided who was asked and where an unanswerable ask denied; nothing asks or denies
3718
- // on a tier now, and the tier stays the honest classification of what the op touches.
3719
- //
3720
- // The mechanics: `recognized: true` with no `readOnly` puts the classifier's floor at
3721
- // mutating-local, `declaredTier` states that tier rather than relying on the floor, and the name
3722
- // tokens (fn · authoring · draft · edit) hit neither destructive lexicon.
3095
+ // This op takes one model call, starts zero runs, and writes one file: the test it edited, over
3096
+ // the exact bytes it read.
3723
3097
  risk: {
3724
3098
  name: `fn:${AUTHORING_DRAFT_EDIT_FUNCTION_ID}`,
3725
- recognized: true,
3726
- declaredTier: "mutating-local"
3099
+ recognized: true
3727
3100
  },
3728
- /**
3729
- * An edit that returned STAGED a draft — there is no arm where it did not (399.1 D-01).
3730
- *
3731
- * Every refusal this op can make is a THROW (the never-guess binder, the region fence, the
3732
- * parser), which already reaches the engine's own `failed` mapping. What returns is one shape,
3733
- * and the honest report of it is what was edited and how much changed.
3734
- */
3101
+ // Every refusal throws, so a returned value always staged a draft.
3735
3102
  classify(output) {
3736
3103
  const file = output.written;
3737
3104
  if (file.kind === "not-written") {
@@ -3854,7 +3221,7 @@ function projectStop(outcome) {
3854
3221
  ...point.verdictSource === void 0 ? {} : { verdictSource: String(point.verdictSource) }
3855
3222
  },
3856
3223
  evidenceRefs: outcome.route.evidenceRefs,
3857
- // ONE SPREAD, so the kind and the spend are present on exactly the same payloads (411 D-08).
3224
+ // One spread, so the kind and the spend appear on exactly the same payloads.
3858
3225
  ...outcome.abandonment === void 0 ? {} : {
3859
3226
  repairAbandonment: outcome.abandonment.kind,
3860
3227
  repairIterations: outcome.abandonment.iterations
@@ -3871,97 +3238,14 @@ var authoringDraftWriteSpec = {
3871
3238
  boundThrough: "draftShelf",
3872
3239
  input: AuthoringDraftWriteInputSchema,
3873
3240
  output: AuthoringDraftWriteOutputSchema,
3874
- // `mutating-shared`, AND THE CONTRAST WITH `authoring.draft.verify` IS THE POINT (394-08, D-03).
3875
- //
3876
- // The mechanics first: `recognized: true` with no `readOnly` puts the classifier's floor at
3877
- // mutating-local, `declaredTier` raises it from there, and the name tokens (fn · authoring ·
3878
- // draft · write) hit neither destructive lexicon — so the declared tier is what the gate sees.
3879
- //
3880
- // `authoring-verify-draft.ts` chose `mutating-local` and its argument was correct FOR IT: an
3881
- // ask-tier op denies in every fail-closed context, and an evaluator that cannot verify a draft in
3882
- // CI is a keep-filter that stops working exactly where it needs to run unattended. Verifying is
3883
- // all cost and no consequence — gate-runs are `excludedFromOutcomes`, so they leave no trace a
3884
- // colleague reads.
3885
- //
3886
- // THIS OP IS GENUINELY DIFFERENT, and it differs in the two ways that decide a tier. It SPENDS a
3887
- // person's compute and model budget on their own machine — up to nine full child `agent-qa run`
3888
- // processes against their application (D-10) — and since 432 D-06 the artifact it produces is
3889
- // written straight into their suite.
3890
- //
3891
- // 2026-10-01 (432-19/432-23, 432 D-12/D-14/D-15): THE TIER NO LONGER ASKS. It was raised so a
3892
- // dispatch confirm would show the fan-out before the loop started, and so a fail-closed context
3893
- // (CI, the daemon) denied it with the gate's ways out (the session trust flag, a trust credential,
3894
- // a standing approval). The gate, its confirm and those ways out are deleted: every door runs this
3895
- // op without a prompt, and WTST-08's honest report on the verdict below is the control that
3896
- // remains. One call starts a loop that runs to its end; there is no mid-loop approval state and
3897
- // no approval token threaded through the stages.
3898
- //
3899
- // DO NOT HARMONISE THE TWO TIERS. They are different classifications because the ops differ;
3900
- // the tier still says honestly which one shares more.
3241
+ // Every door runs this op without a prompt. One call starts a loop that runs to its end, spending
3242
+ // up to nine child `agent-qa run` processes against the person's application, and writes the
3243
+ // draft straight into their suite; there is no mid-loop approval state.
3901
3244
  risk: {
3902
3245
  name: `fn:${AUTHORING_DRAFT_WRITE_FUNCTION_ID}`,
3903
- recognized: true,
3904
- declaredTier: "mutating-shared"
3246
+ recognized: true
3905
3247
  },
3906
- // 2026-10-02 (432-42, D-14/D-15): the per-invocation `disclose` method is deleted. It rendered the
3907
- // fan-out preview for the dispatch confirm, which 432-19 deleted, and nothing called it since; the
3908
- // fan-out report on the verdict below is what a caller reads.
3909
- /**
3910
- * This op's own outcome enum, folded into a clause verdict (2026-08-20, Phase 399.1 D-01/D-02),
3911
- * carrying the invocation's FAN-OUT REPORT (394-14 · WTST-08 · D-18's one exception).
3912
- *
3913
- * `refused` IS THE MEASURED WITNESS. 399's live acceptance recorded this op returning its stage-2
3914
- * dedup refusal while chat printed `executed authoring.draft.write completed`: the screen declined
3915
- * to author, nothing was written, and the person was told it had been. The screen WORKED, so the
3916
- * honest word is `refused` and not `failed` — the same reading D-02 minted the status for.
3917
- *
3918
- * THE OUTCOME CARRIES THE REFERENCE, because 394 D-04's requirement is refuse AND LINK: a person
3919
- * told only "duplicate" has nothing to open, and `matchedRef` is precisely the string
3920
- * `entities.resolve` takes. The refusal payload is optional on the contract, so an arm that says
3921
- * `refused` without one still says `refused` — the status is the claim that must not be lost, and
3922
- * a missing link degrades the sentence rather than the verdict.
3923
- *
3924
- * THE OTHER THREE ARMS RAN TO COMPLETION. `drafted`, `repaired` and `stopped` all end with a draft
3925
- * and a receipt — a correctly-failing draft is a VERDICT about the application, not a failure of
3926
- * this op — so each is `executed`, and the outcome names the test that was authored. A `stopped`
3927
- * invocation therefore renders `stopped <testName> — <fan-out sentence>` under a status chat and
3928
- * the parity walk already read: a spelling, never a shape (411 D-11).
3929
- *
3930
- * ---- the fan-out, migrated here from `turn-engine.ts` by 399.1-03 -----------------------------
3931
- *
3932
- * IT ARRIVES ON THE VERDICT AND NOT ON A RENDER FOLD, and the choice is load-bearing rather than
3933
- * incidental. 394-14's second cell requires the sentence to reach the APPEND-ONLY LOG — "the
3934
- * return value is what one caller sees; the log is the triage report several surfaces render" —
3935
- * and the log carries `ClauseReceipt.outcome`, not the turn's answer. A `renderResult` fold would
3936
- * have put this disclosure on the answer channel only, silently costing it the half that is
3937
- * readable after the fact. `summariseOutcome` composed a RECEIPT LINE, so the seat it vacates is
3938
- * this one; `explainAnswerLines` composed ANSWER TEXT, so its seat is `renderResult`. The two
3939
- * hardcodes were never the same kind of thing.
3940
- *
3941
- * IT IS ADMITTED BY THE CHARTER RATHER THAN EXCEPTED FROM IT. D-18 says the receipt is a MAP, and
3942
- * `FanOutReport` is counts, run REFERENCES and one boolean — no draft body, no receipt payload, no
3943
- * step evidence. The written file and its receipt ride the op's `written` field, not this line.
3944
- *
3945
- * IT IS ONE FIELD ON ONE OP, AND NOW BY CONSTRUCTION. That used to be enforced by an id
3946
- * comparison inside a shared function — a claim a later edit could widen by deleting one line. It
3947
- * is a member of THIS spec now: no other op's line is touched, because no other op declares this.
3948
- *
3949
- * WHY THE DISCLOSURE IS THE WHOLE CONTROL. WTST-08's owner decision was preview plus honest
3950
- * reporting with NO enforced budget, so the number a person is shown is all there is. The same
3951
- * surface rendered the dispatch confirm they approved; a receipt omitting what was actually spent
3952
- * would be the half that makes the other half checkable, missing. They agreed to nine runs and
3953
- * had no way to learn eleven were taken.
3954
- *
3955
- * PARSED, NEVER CAST, AND THE PARSE CANNOT MOVE THE STATUS. `FanOutReportSchema` is `.strict()`,
3956
- * so a malformed result cannot smuggle prose into a file several surfaces render; an unparsable
3957
- * report costs the SENTENCE and never the verdict, which is `classify`'s fail-closed contract read
3958
- * correctly — the disclosure degrades, the claim about what happened does not.
3959
- *
3960
- * THE SENTENCE IS `renderFanOutReport`'S AND NOTHING ELSE. A line composed here would be a second
3961
- * prose form of one report, free to drift from the confirm's own wording the moment a phrase
3962
- * moves — and mutation R of the phase's mutation driver (since removed) was the differential
3963
- * that caught it.
3964
- */
3248
+ // A refused dedup is `refused` with its matched ref; every other arm is `executed`.
3965
3249
  classify(output) {
3966
3250
  const report = FanOutReportSchema.safeParse(output.fanOut);
3967
3251
  const spent = report.success ? ` \u2014 ${renderFanOutReport(report.data)}` : "";
@@ -4007,10 +3291,7 @@ var authoringDraftWriteSpec = {
4007
3291
  intent: input.intent,
4008
3292
  scope,
4009
3293
  ...input.draftMode === void 0 ? {} : { draftMode: input.draftMode },
4010
- // 394-17 / D-12: handed on exactly as supplied, and this op invents nothing. The session
4011
- // is a fact about the SURFACE that dispatched the call — the chat engine stamps its own
4012
- // id at the invoke boundary — so a default here would attribute a CLI draft to a session
4013
- // that never asked for it.
3294
+ // Passed as supplied: the dispatching surface stamps its own session, so no default here.
4014
3295
  ...input.sessionRef === void 0 ? {} : { sessionRef: input.sessionRef }
4015
3296
  },
4016
3297
  { read, shelf }
@@ -4101,23 +3382,12 @@ var authoringFunnelGetSpec = {
4101
3382
  id: AUTHORING_FUNNEL_GET_FUNCTION_ID,
4102
3383
  summary: "Report the authoring funnel for a window \u2014 built, passed stably, added value, write-eligible \u2014 and flag a keep rate above the ceiling as the loop failing to filter.",
4103
3384
  resultKind: "unary",
4104
- // Bound exactly where a composition supplies the receipts store (the proposals inbox left in 432-17).
4105
- // An unbound host answering an all-zero funnel would say "this session authored nothing" about
4106
- // a store it has never been able to read — and worse, it would say the loop filtered
4107
- // everything, which is the one reading of a zero funnel that flatters the loop. So it refuses
4108
- // by name instead (D-09).
3385
+ // An unbound host refuses rather than answer a zero funnel that flatters the loop.
4109
3386
  boundThrough: "authoringFunnel",
4110
3387
  input: AuthoringFunnelGetInputSchema,
4111
3388
  output: AuthoringFunnelGetOutputSchema,
4112
3389
  risk: { name: `fn:${AUTHORING_FUNNEL_GET_FUNCTION_ID}`, recognized: true, readOnly: true },
4113
- /**
4114
- * The funnel's OWN one line, not a second one composed here (399.1 D-01 · WTST-05).
4115
- *
4116
- * `line` is the single rendered sentence every surface prints, and it is screened against the
4117
- * forbidden phrasings before the contract will carry it. Composing a receipt line out of the
4118
- * counters instead would be a second funnel rendering — unscreened, and free to call a high keep
4119
- * rate an achievement, which is the exact misreading D-13 exists to prevent.
4120
- */
3390
+ // The receipt carries the funnel's own screened line, never one composed from the counters.
4121
3391
  classify(output) {
4122
3392
  return { status: "executed", outcome: output.line };
4123
3393
  },
@@ -4356,7 +3626,7 @@ function classifyDraftSteps(steps) {
4356
3626
  function gateReason2(rows) {
4357
3627
  const assertions = rows.filter((row) => row.role === "assertion");
4358
3628
  if (assertions.length === 0) {
4359
- return "no step classifies as an assertion: the draft records what it did, never what should be true (D-09 clause (i))";
3629
+ return "no step classifies as an assertion: the draft records what it did, never what should be true";
4360
3630
  }
4361
3631
  if (assertions.some((row) => row.falsifiable)) {
4362
3632
  return `${assertions.filter((row) => row.falsifiable).length} of ${assertions.length} assertion steps reference concrete expected content`;
@@ -4375,7 +3645,7 @@ function gateReason2(rows) {
4375
3645
  "no quoted expected value, and no element noun this gate knows stands before an expected-state term with only function words between them"
4376
3646
  );
4377
3647
  }
4378
- return `${assertions.length} assertion step(s), none falsifiable: ${[...causes].sort().join("; ")} (D-09 clause (ii))`;
3648
+ return `${assertions.length} assertion step(s), none falsifiable: ${[...causes].sort().join("; ")}`;
4379
3649
  }
4380
3650
  function assertionStrengthGate(draft) {
4381
3651
  const table = classifyDraftSteps(draft.steps);
@@ -4407,7 +3677,7 @@ import { DRAFT_STABILITY_RUNS_N as DRAFT_STABILITY_RUNS_N2 } from "@vostride/age
4407
3677
  var GateRunNotFlaggedError = class extends Error {
4408
3678
  constructor(runId, acknowledged) {
4409
3679
  super(
4410
- `runs.start accepted ${runId} without both gate-run markers (gateRun=${String(acknowledged.gateRun)}, excludedFromOutcomes=${String(acknowledged.excludedFromOutcomes)}): an unflagged gate-run pollutes the outcome projection and the suite fingerprint union the draft is later compared against (WTST-06).`
3680
+ `runs.start accepted ${runId} without both gate-run markers (gateRun=${String(acknowledged.gateRun)}, excludedFromOutcomes=${String(acknowledged.excludedFromOutcomes)}): an unflagged gate-run pollutes the outcome projection and the suite fingerprint union the draft is later compared against.`
4411
3681
  );
4412
3682
  this.runId = runId;
4413
3683
  this.acknowledged = acknowledged;
@@ -4435,39 +3705,39 @@ function classifyGateRun(member) {
4435
3705
  if (!DEFINITIVE_MEMBER_OUTCOMES.has(member.outcome)) {
4436
3706
  return {
4437
3707
  definitive: false,
4438
- reason: `member outcome '${member.outcome}' is not a final pass or fail (D-17)`
3708
+ reason: `member outcome '${member.outcome}' is not a final pass or fail`
4439
3709
  };
4440
3710
  }
4441
3711
  for (const step of member.steps) {
4442
3712
  if (step.planningFailure !== void 0) {
4443
3713
  return {
4444
3714
  definitive: false,
4445
- reason: `planning failed structurally on step ${step.stepIndex}: the draft was never executed to a verdict, so the enum below describes the attempt, not the app (D-17)`
3715
+ reason: `planning failed structurally on step ${step.stepIndex}: the draft was never executed to a verdict, so the enum below describes the attempt, not the app`
4446
3716
  };
4447
3717
  }
4448
3718
  if (step.limitStop !== void 0) {
4449
3719
  const { limit, used, configured } = step.limitStop;
4450
3720
  return {
4451
3721
  definitive: false,
4452
- reason: `step ${step.stepIndex} stopped at its ${limit} limit (${used} of ${configured} used): the run ran out of limit, not into an answer (D-12)`
3722
+ reason: `step ${step.stepIndex} stopped at its ${limit} limit (${used} of ${configured} used): the run ran out of limit, not into an answer`
4453
3723
  };
4454
3724
  }
4455
3725
  if (step.cycle !== void 0) {
4456
3726
  return {
4457
3727
  definitive: false,
4458
- reason: `a cycle was flagged on step ${step.stepIndex} (${step.cycle.reason}): the run stopped revisiting state rather than reaching an answer (D-17)`
3728
+ reason: `a cycle was flagged on step ${step.stepIndex} (${step.cycle.reason}): the run stopped revisiting state rather than reaching an answer`
4459
3729
  };
4460
3730
  }
4461
3731
  if (step.termination !== void 0) {
4462
3732
  return {
4463
3733
  definitive: false,
4464
- reason: `step ${step.stepIndex} terminated '${step.termination.kind}' (${step.termination.reason}) \u2014 indefinitive despite member outcome '${member.outcome}', which reads definitive on its own (393-01 F1)`
3734
+ reason: `step ${step.stepIndex} terminated '${step.termination.kind}' (${step.termination.reason}) \u2014 indefinitive despite member outcome '${member.outcome}', which reads definitive on its own`
4465
3735
  };
4466
3736
  }
4467
3737
  }
4468
3738
  return {
4469
3739
  definitive: true,
4470
- reason: `member outcome '${member.outcome}' with no planning failure, limit stop, cycle flag or termination on any step \u2014 a final answer about the draft` + (member.outcome === "healed" ? "; a healed pass IS a pass (393-01 F3)" : "")
3740
+ reason: `member outcome '${member.outcome}' with no planning failure, limit stop, cycle flag or termination on any step \u2014 a final answer about the draft` + (member.outcome === "healed" ? "; a healed pass is a pass" : "")
4471
3741
  };
4472
3742
  }
4473
3743
  function gateRunStartInput(draft) {
@@ -4518,8 +3788,7 @@ async function runStabilityGates(input) {
4518
3788
  const distinctOutcomes = [...new Set(observedVerdicts)];
4519
3789
  return {
4520
3790
  executesDefinitive: gateRuns.length > 0 && gateRuns.every((row) => row.definitive),
4521
- // An evaluation that observed no verdict at all has not shown stability; it has shown nothing.
4522
- // D-12's tune-toward-reject applies — a gate with no evidence behind it is not a pass.
3791
+ // No observed verdict shows nothing, so the gate does not pass.
4523
3792
  stableVerdict: distinctOutcomes.length === 1,
4524
3793
  gateRuns,
4525
3794
  runsCompleted: gateRuns.length,
@@ -4630,7 +3899,7 @@ async function evaluateDraft(input, ports) {
4630
3899
  return receipt;
4631
3900
  }
4632
3901
  function stabilityGateOneReason(pass, runCount) {
4633
- return pass ? `all ${runCount} gate-run(s) reached a final verdict about the draft (D-17)` : `${runCount} gate-run(s) taken; at least one reached no final verdict \u2014 see gateRuns[].reason`;
3902
+ return pass ? `all ${runCount} gate-run(s) reached a final verdict about the draft` : `${runCount} gate-run(s) taken; at least one reached no final verdict \u2014 see gateRuns[].reason`;
4634
3903
  }
4635
3904
  async function consultSuiteStability(draft, targetSuitePath, distinctOutcomes, ports) {
4636
3905
  if (distinctOutcomes.length <= 1) return void 0;
@@ -4656,7 +3925,7 @@ function stabilityGateTwoReason(distinctOutcomes, earlyStopped, suiteStability)
4656
3925
  if (distinctOutcomes.length <= 1) {
4657
3926
  return `one verdict across the completed runs: ${distinctOutcomes[0] ?? "none observed"}`;
4658
3927
  }
4659
- return `the verdict flipped across runs (${distinctOutcomes.join(" -> ")})` + (earlyStopped ? " \u2014 remaining runs skipped, the flip is already proven (D-17)" : "") + (suiteStability === void 0 ? "" : ` \u2014 ${suiteStability.message}`);
3928
+ return `the verdict flipped across runs (${distinctOutcomes.join(" -> ")})` + (earlyStopped ? " \u2014 remaining runs skipped, the flip is already proven" : "") + (suiteStability === void 0 ? "" : ` \u2014 ${suiteStability.message}`);
4660
3929
  }
4661
3930
 
4662
3931
  // src/functions/ops/authoring-verify-draft.ts
@@ -4711,40 +3980,18 @@ function hasDraftExtension(draftPath) {
4711
3980
  var authoringVerifyDraftSpec = {
4712
3981
  id: AUTHORING_DRAFT_VERIFY_FUNCTION_ID,
4713
3982
  summary: "Evaluate a draft test against the four keep-gates and return its receipt.",
4714
- // 2026-10-01 (432-23, D-12): no scopes. The evaluation STARTS runs and READS the completed
4715
- // records back, and the composition invokes those sibling ops on a caller it mints (D-02,
4716
- // op-calls-op). The `run` + `read` pair once declared here kept a nested call from widening a
4717
- // grant; with no grants to widen, every door can call this op and its siblings alike.
4718
3983
  resultKind: "unary",
4719
3984
  boundThrough: "draftEvaluation",
4720
3985
  input: AuthoringVerifyDraftInputSchema,
4721
3986
  output: DraftReceiptSchema2,
4722
- // The `runs.start` tier rationale, adapted — and it is the same argument because this op's whole
4723
- // effect is the runs it takes:
4724
- // `recognized: true` with NO `readOnly` puts the classifier's floor at mutating-local;
4725
- // `declaredTier` states that tier explicitly rather than relying on the floor alone;
4726
- // the name tokens (fn · authoring · draft · verify) hit neither destructive lexicon, so
4727
- // nothing raises the tier above what is declared.
4728
- // The tier was chosen when `mutating-shared` would have ASKED, and an ask in a fail-closed
4729
- // context denied, leaving 394's authoring loop unable to verify a draft in CI, the one context
4730
- // where the keep-filter most needs to run unattended. 2026-10-01 (432-19/432-23, D-12/D-15):
4731
- // nothing asks or denies on a tier any more; the tier stays the honest classification.
4732
- // Verifying a draft really is local: it spends this machine's resources, writes into this
4733
- // project's run directory and its gitignored receipts store, and — because every gate-run is
4734
- // flagged `excludedFromOutcomes` (WTST-06) — deliberately leaves no trace in the shared signals
4735
- // a colleague reads.
3987
+ // Verifying a draft spends this machine's resources, writes into this project's run directory and
3988
+ // its gitignored receipts store, and leaves no trace in the shared signals a colleague reads,
3989
+ // because every gate-run is flagged `excludedFromOutcomes`.
4736
3990
  risk: {
4737
3991
  name: `fn:${AUTHORING_DRAFT_VERIFY_FUNCTION_ID}`,
4738
- recognized: true,
4739
- declaredTier: "mutating-local"
3992
+ recognized: true
4740
3993
  },
4741
- /**
4742
- * A RECEIPT'S VERDICT ABOUT A DRAFT IS AN ANSWER, not a refusal (399.1 D-01, plan 03's razor).
4743
- *
4744
- * `rejected` here is the gate saying what it found about somebody's draft — the verification ran
4745
- * and produced its finding. `refused` is for a control declining an act it would otherwise have
4746
- * carried out, and nothing was declined: the act WAS carrying out the verification.
4747
- */
3994
+ // A rejection is the verification's finding, an answer, never `refused`.
4748
3995
  classify(output) {
4749
3996
  return {
4750
3997
  status: "executed",
@@ -4822,8 +4069,6 @@ import {
4822
4069
  CROSS_BOUNDARY_REASONS,
4823
4070
  isSecretAdjacentConfigField
4824
4071
  } from "@vostride/agent-qa-schema";
4825
- var ALLOW_PRODUCTION_PATH = "allowProduction";
4826
- var TARGET_ENVIRONMENT_PATH = /^registry\.targets\.[^.]+\.environment$/;
4827
4072
  function originOf(value) {
4828
4073
  if (typeof value !== "string") return void 0;
4829
4074
  let parsed;
@@ -4841,12 +4086,6 @@ function computeCrossBoundary(facts) {
4841
4086
  if (isSecretAdjacentConfigField(edit.configPath)) found.add("secret-adjacent-field");
4842
4087
  const newOrigin = originOf(edit.newValue);
4843
4088
  if (newOrigin !== void 0 && newOrigin !== originOf(edit.oldValue)) found.add("new-origin");
4844
- if (edit.configPath === ALLOW_PRODUCTION_PATH && edit.newValue === true && edit.oldValue !== true) {
4845
- found.add("production-opt-in");
4846
- }
4847
- if (TARGET_ENVIRONMENT_PATH.test(edit.configPath) && edit.oldValue === "production" && edit.newValue !== "production") {
4848
- found.add("production-declassified");
4849
- }
4850
4089
  }
4851
4090
  if (facts.addsHook) found.add("hook-added");
4852
4091
  const pinned = new Set(facts.pinnedFileSet);
@@ -4947,9 +4186,7 @@ async function changeConfigField(input, deps) {
4947
4186
  ...edit.previousValue === void 0 ? {} : { oldValue: edit.previousValue },
4948
4187
  newValue: input.value
4949
4188
  }],
4950
- // A field set writes a config value; it does not add a hook. `config.hook.add` is its own op and
4951
- // supplies this fact itself. Stated rather than inferred, because inferring "a hook was added"
4952
- // from a path would be the per-op map D-02 refused.
4189
+ // Stated, not inferred: a field set adds no hook; `config.hook.add` states its own.
4953
4190
  addsHook: false,
4954
4191
  candidate: candidateSet(set, [{ path: target.path, bytes: edit.bytes }])
4955
4192
  }, {
@@ -5129,7 +4366,7 @@ async function addConfigHook(input, deps) {
5129
4366
  subject: `add hook ${input.name}`,
5130
4367
  summaryLine: `add ${input.runtime} hook ${input.name} running ${input.file}`,
5131
4368
  fieldEdits: flattenScalarEdits(`${HOOKS_PATH}.${at}`, entry2, void 0),
5132
- // STATED, NOT INFERRED (D-02). This is the op that adds hooks; nothing has to guess.
4369
+ // Stated, not inferred: this op adds hooks.
5133
4370
  addsHook: true,
5134
4371
  candidate: candidateSet(set, [{ path: hooksMember.path, bytes: written.bytes }])
5135
4372
  }, { kind: "files" }, set, deps);
@@ -5167,8 +4404,7 @@ async function scaffoldConfigProject(input, deps) {
5167
4404
  targetName: verdict.targetName,
5168
4405
  baseUrl: verdict.baseUrl,
5169
4406
  ...gitignore?.bytes === void 0 ? {} : { existingGitignore: gitignore.bytes },
5170
- // MERGE MODE, AND ONLY WHEN THERE ARE BYTES TO MERGE INTO. Absent here is the from-zero road,
5171
- // whose rendered bytes are byte-identical to what 395-08 shipped and what the goldens pin.
4407
+ // Merge mode only when there are bytes to merge into; goldens pin the from-zero output.
5172
4408
  ...existingConfig.bytes === void 0 ? {} : {
5173
4409
  existingConfig: {
5174
4410
  path: existingConfig.path,
@@ -5190,9 +4426,7 @@ async function scaffoldConfigProject(input, deps) {
5190
4426
  { configPath: `${TARGETS_PATH}.${verdict.targetName}.platform`, newValue: "web" },
5191
4427
  { configPath: `${TARGETS_PATH}.${verdict.targetName}.url`, newValue: verdict.baseUrl }
5192
4428
  ],
5193
- // The scaffolded hooks file declares one hook, which is code this project would then run.
5194
- // Stated by the op, as D-02 requires, and true exactly when the file did not exist a moment
5195
- // ago — see `addsHook`'s derivation above.
4429
+ // True exactly when the hooks file did not exist; see `addsHook`'s derivation above.
5196
4430
  addsHook,
5197
4431
  candidate: candidateSet(set, [...rendered])
5198
4432
  }, { kind: "files" }, set, deps);
@@ -5459,14 +4693,7 @@ var configAuthSetSpec = {
5459
4693
  recognized: true,
5460
4694
  readOnly: false
5461
4695
  },
5462
- /**
5463
- * This op's own union, folded into a clause verdict (2026-08-20, Phase 399.1 D-01/D-02).
5464
- *
5465
- * THE FOLD IS SHARED BECAUSE THE UNION IS. Six ops answer with `ConfigChangeOutput` (432-11), and a
5466
- * per-op copy of the mapping would be a guardrail refusal that reports `refused` on some doors
5467
- * and `executed` on others the first time one copy is edited. See `config-propose-verdict.ts`
5468
- * for why `blocked` and `refused` are the same status and different words.
5469
- */
4696
+ // Shared, so every door reports a guardrail refusal alike; see `config-propose-verdict.ts`.
5470
4697
  classify: classifyConfigChangeOutput,
5471
4698
  async invoke(input, deps) {
5472
4699
  const { configFiles, configValidate, configWrite } = deps;
@@ -5495,15 +4722,7 @@ var configFieldSetSpec = {
5495
4722
  recognized: true,
5496
4723
  readOnly: false
5497
4724
  },
5498
- /**
5499
- * This op's own union, folded into a clause verdict (2026-08-20, Phase 399.1 D-01/D-02).
5500
- *
5501
- * THE MAPPING MOVED TO `config-propose-verdict.ts` IN 399.1-03, unchanged, when the other six ops
5502
- * answering with this same union got classifiers of their own. It was written here first because
5503
- * this op was the tracer; leaving a private copy behind would have made a guardrail refusal report
5504
- * `refused` on this door and `executed` on the next one the first time somebody edited one copy.
5505
- * The arms, and the reasoning for which of them is `refused`, are documented at the fold.
5506
- */
4725
+ // Shared, so every door reports a guardrail refusal alike; see `config-propose-verdict.ts`.
5507
4726
  classify: classifyConfigChangeOutput,
5508
4727
  async invoke(input, deps) {
5509
4728
  const { configFiles, configValidate, configWrite } = deps;
@@ -5532,14 +4751,7 @@ var configHookAddSpec = {
5532
4751
  recognized: true,
5533
4752
  readOnly: false
5534
4753
  },
5535
- /**
5536
- * This op's own union, folded into a clause verdict (2026-08-20, Phase 399.1 D-01/D-02).
5537
- *
5538
- * THE FOLD IS SHARED BECAUSE THE UNION IS. Six ops answer with `ConfigChangeOutput` (432-11), and a
5539
- * per-op copy of the mapping would be a guardrail refusal that reports `refused` on some doors
5540
- * and `executed` on others the first time one copy is edited. See `config-propose-verdict.ts`
5541
- * for why `blocked` and `refused` are the same status and different words.
5542
- */
4754
+ // Shared, so every door reports a guardrail refusal alike; see `config-propose-verdict.ts`.
5543
4755
  classify: classifyConfigChangeOutput,
5544
4756
  async invoke(input, deps) {
5545
4757
  const { configFiles, configValidate, configWrite } = deps;
@@ -5563,13 +4775,7 @@ var configGetSpec = {
5563
4775
  input: ConfigGetInputSchema,
5564
4776
  output: MaskedResolvedConfigSchema,
5565
4777
  risk: { name: `fn:${CONFIG_GET_FUNCTION_ID}`, recognized: true, readOnly: true },
5566
- /**
5567
- * A read that answered — the count of what it resolved, and nothing out of it (399.1 D-01).
5568
- *
5569
- * NOT ONE ENTRY'S VALUE, ever. This op's result carries a person's configuration, placeholder-
5570
- * masked; the receipt is a map and the masked payload travels on `results[]` where the surface's
5571
- * own unmasker decides what a human may see (D-04's boundary, not this fold's).
5572
- */
4778
+ // Counts only, never an entry's value; the masked payload travels on `results[]`.
5573
4779
  classify(output) {
5574
4780
  const count = output.entries.length;
5575
4781
  return {
@@ -5597,14 +4803,7 @@ var configTargetCreateSpec = {
5597
4803
  recognized: true,
5598
4804
  readOnly: false
5599
4805
  },
5600
- /**
5601
- * This op's own union, folded into a clause verdict (2026-08-20, Phase 399.1 D-01/D-02).
5602
- *
5603
- * THE FOLD IS SHARED BECAUSE THE UNION IS. Six ops answer with `ConfigChangeOutput` (432-11), and a
5604
- * per-op copy of the mapping would be a guardrail refusal that reports `refused` on some doors
5605
- * and `executed` on others the first time one copy is edited. See `config-propose-verdict.ts`
5606
- * for why `blocked` and `refused` are the same status and different words.
5607
- */
4806
+ // Shared, so every door reports a guardrail refusal alike; see `config-propose-verdict.ts`.
5608
4807
  classify: classifyConfigChangeOutput,
5609
4808
  async invoke(input, deps) {
5610
4809
  const { configFiles, configValidate, configWrite } = deps;
@@ -5633,14 +4832,7 @@ var configProjectScaffoldSpec = {
5633
4832
  recognized: true,
5634
4833
  readOnly: false
5635
4834
  },
5636
- /**
5637
- * This op's own union, folded into a clause verdict (2026-08-20, Phase 399.1 D-01/D-02).
5638
- *
5639
- * THE FOLD IS SHARED BECAUSE THE UNION IS. Six ops answer with `ConfigChangeOutput` (432-11), and a
5640
- * per-op copy of the mapping would be a guardrail refusal that reports `refused` on some doors
5641
- * and `executed` on others the first time one copy is edited. See `config-propose-verdict.ts`
5642
- * for why `blocked` and `refused` are the same status and different words.
5643
- */
4835
+ // Shared, so every door reports a guardrail refusal alike; see `config-propose-verdict.ts`.
5644
4836
  classify: classifyConfigChangeOutput,
5645
4837
  async invoke(input, deps) {
5646
4838
  const { configFiles, configValidate, configWrite, scaffoldContent } = deps;
@@ -5669,14 +4861,7 @@ var configTargetEditSpec = {
5669
4861
  recognized: true,
5670
4862
  readOnly: false
5671
4863
  },
5672
- /**
5673
- * This op's own union, folded into a clause verdict (2026-08-20, Phase 399.1 D-01/D-02).
5674
- *
5675
- * THE FOLD IS SHARED BECAUSE THE UNION IS. Six ops answer with `ConfigChangeOutput` (432-11), and a
5676
- * per-op copy of the mapping would be a guardrail refusal that reports `refused` on some doors
5677
- * and `executed` on others the first time one copy is edited. See `config-propose-verdict.ts`
5678
- * for why `blocked` and `refused` are the same status and different words.
5679
- */
4864
+ // Shared, so every door reports a guardrail refusal alike; see `config-propose-verdict.ts`.
5680
4865
  classify: classifyConfigChangeOutput,
5681
4866
  async invoke(input, deps) {
5682
4867
  const { configFiles, configValidate, configWrite } = deps;
@@ -5776,13 +4961,7 @@ var knowledgeBriefWhySpec = {
5776
4961
  input: BriefWhyInputSchema,
5777
4962
  output: BriefWhyResultSchema,
5778
4963
  risk: { name: `fn:${KNOWLEDGE_BRIEF_WHY_FUNCTION_ID}`, recognized: true, readOnly: true },
5779
- /**
5780
- * `no-trace` IS THE HONEST ABSENCE THIS VERB EXISTS FOR (399.1 D-01).
5781
- *
5782
- * KREV-02's whole posture is that a brief whose trace is gone must be SAID to be gone rather than
5783
- * re-derived; the op that answers it is given no serving capability at all. That is an answer the
5784
- * verb is proud of, not a control declining, so the arm is `executed` with the store's own reason.
5785
- */
4964
+ // `no-trace` is the honest absence, so it folds to `executed` with the store's reason.
5786
4965
  classify(output) {
5787
4966
  switch (output.kind) {
5788
4967
  case "answered":
@@ -5812,9 +4991,7 @@ var knowledgeRecordShowSpec = {
5812
4991
  input: KnowledgeRecordShowInputSchema,
5813
4992
  output: KnowledgeRecordShowResultSchema,
5814
4993
  risk: { name: `fn:${KNOWLEDGE_RECORD_SHOW_FUNCTION_ID}`, recognized: true, readOnly: true },
5815
- /**
5816
- * "Absence is a RESULT rather than a refusal" — the contract's own words (399.1 D-01).
5817
- */
4994
+ // Absence is a result, not a refusal.
5818
4995
  classify(output) {
5819
4996
  switch (output.kind) {
5820
4997
  case "found":
@@ -5867,27 +5044,14 @@ var runsStartSpec = {
5867
5044
  boundThrough: "runInvocation",
5868
5045
  input: RunsStartInputSchema,
5869
5046
  output: RunStartAcknowledgementSchema,
5870
- // Pitfall 10, and every field here is load-bearing:
5871
- // `recognized: true` with NO `readOnly` puts the classifier's floor at mutating-local;
5872
- // `declaredTier` states that tier explicitly rather than relying on the floor alone;
5873
- // the name tokens (fn · runs · start) hit neither destructive lexicon, so nothing
5874
- // raises the tier above what is declared.
5875
- // The tier was chosen when `mutating-shared` would have asked, and an ask in a fail-closed
5876
- // context denied, leaving the authoring loop unable to start a run in CI. 2026-10-01 (432-19/
5877
- // 432-23, D-12/D-15): nothing asks or denies on a tier any more; the tier stays the honest
5878
- // classification. Starting a run really is local: it spends this machine's resources and
5879
- // writes into this project's run directory. It touches nothing another party shares.
5880
5047
  risk: {
5881
5048
  name: `fn:${RUNS_START_FUNCTION_ID}`,
5882
- recognized: true,
5883
- declaredTier: "mutating-local"
5049
+ recognized: true
5884
5050
  },
5885
5051
  /**
5886
- * AN ACKNOWLEDGEMENT IS A START, NOT AN OUTCOME (399.1 D-01).
5887
- *
5888
- * This op returns when the run has been ACCEPTED, long before it has a verdict — so the outcome
5889
- * names the run a person can follow and how much it took on, and says nothing about how it went.
5890
- * A refusal to start is a throw and already reaches the engine's `failed` mapping.
5052
+ * The op answers when the run has its verdict. The outcome line names the run, how many tests it
5053
+ * ran and its scope, and leaves how it went to the run's own record. A refusal to start is a
5054
+ * throw.
5891
5055
  */
5892
5056
  classify(output) {
5893
5057
  return {
@@ -5951,12 +5115,7 @@ var ENTITY_ACTION_TABLES = {
5951
5115
  inputSeed: { recordId: descriptor.machineId }
5952
5116
  },
5953
5117
  {
5954
- // D-11's table completion (387-08). 2026-09-30, 432-12 (D-05/D-09): forget is the explicit
5955
- // removal and it applies in the call, so the action says what it does. The record is
5956
- // tombstoned, never destroyed, and the card at this address then reads `tombstoned`. The
5957
- // seed addresses the RECORD grain because a descriptor carries the machine id it was
5958
- // sighted under, and the bundle grain takes a short-ID whose resolution is the writer's job
5959
- // rather than the card's.
5118
+ // Forget tombstones the record in the call; the seed addresses the record grain.
5960
5119
  functionId: KNOWLEDGE_FORGET_FUNCTION_ID,
5961
5120
  label: "Forget this record",
5962
5121
  inputSeed: { target: { grain: "record", recordId: descriptor.machineId } }
@@ -6054,7 +5213,7 @@ function projectKeyFacts(input) {
6054
5213
  var ForgetReceiptKindError = class extends TypeError {
6055
5214
  constructor(kind) {
6056
5215
  super(
6057
- `a forget receipt describes a MEM knowledge bundle (D-19); it cannot render a ${kind} card`
5216
+ `a forget receipt describes a MEM knowledge bundle; it cannot render a ${kind} card`
6058
5217
  );
6059
5218
  this.kind = kind;
6060
5219
  this.name = "ForgetReceiptKindError";
@@ -6122,13 +5281,7 @@ var entitiesGetSpec = {
6122
5281
  input: EntitiesGetInputSchema,
6123
5282
  output: EntityCardSchema2,
6124
5283
  risk: { name: `fn:${ENTITIES_GET_FUNCTION_ID}`, recognized: true, readOnly: true },
6125
- /**
6126
- * A card that came back is a card that was found (399.1 D-01).
6127
- *
6128
- * Absence is a THROW here (`EntityNotFoundError`), not an arm, so the one returning shape is a
6129
- * hit. THE ADDRESS AND NOTHING ELSE: `kind` and `id` are the card's own name, and every fact on
6130
- * it travels on `results[]`.
6131
- */
5284
+ // Absence throws, so the only returning shape is a hit.
6132
5285
  classify(output) {
6133
5286
  return { status: "executed", outcome: `found ${output.kind} ${output.id}` };
6134
5287
  },
@@ -6315,61 +5468,22 @@ function abstainAndRecord(port, query, rows) {
6315
5468
  }
6316
5469
  var explainSpec = {
6317
5470
  id: EXPLAIN_FUNCTION_ID,
6318
- // One line, and it is the line the catalog RENDERS for this row — descriptions live on the spec
6319
- // so one source feeds chat, MCP and the catalogue (396 D-16 / 398 D-08).
5471
+ // The line the catalog renders for this row.
6320
5472
  summary: "Answer a question about what this product can do by citing the capability catalog, or say plainly that the catalog does not ground an answer.",
6321
- // 2026-10-01 (432-26, D-12): no scope. This op once declared `read` alone so that it stayed
6322
- // listable in safe mode; safe mode and scopes are deleted, and every caller lists and calls it.
6323
5473
  resultKind: "unary",
6324
- // D-09: the catalog reaches this op through a port bound at `composeLocalAgent()` (385 D-15). A
6325
- // composition without one lists the capability `unbound` BY NAME rather than degrading to a
6326
- // model-only answer — an explain that silently lost its catalog is precisely an explain that
6327
- // answers from model memory, which is the one behaviour XPLN-02 exists to forbid.
5474
+ // Unbound without a catalog, so an explain never answers from model memory.
6328
5475
  boundThrough: "capabilityCatalog",
6329
5476
  input: ExplainInputSchema,
6330
5477
  output: ExplainOutputSchema,
6331
5478
  // Read-only and recognized: it starts nothing, writes nothing a caller can address, and the one
6332
5479
  // byte it appends is a local coverage line about a question it was asked.
6333
5480
  risk: { name: `fn:${EXPLAIN_FUNCTION_ID}`, recognized: true, readOnly: true },
6334
- /**
6335
- * The answer lines this op composed, on their way to the turn's `answer` field (398-13 · D-21).
6336
- *
6337
- * MIGRATED FROM `turn-engine.ts` BY 399.1-03, ARGUMENT INTACT. It lived there as
6338
- * `explainAnswerLines`, an explicit comparison of the running clause's `functionId` against this
6339
- * op's id — a render rule in the engine, which by construction could only ever render what
6340
- * somebody had thought to name there. D-04's promote decision moved it here: this op knows what
6341
- * its own result deserves to have said about it, and the engine no longer knows this op exists.
6342
- *
6343
- * THE RECEIPT CHARTER IS UNTOUCHED, and that is still the whole reason these lines take THIS
6344
- * channel rather than the receipt's. The receipt stays the MAP — the plain `agent.explain
6345
- * completed` line — and the territory travels on the field `renderTurn` already prints and `ask`
6346
- * already shares. A second carrier on the receipt would put the answer inside the map.
6347
- *
6348
- * PARSED, NEVER CAST, and a parse failure renders NOTHING extra: the fold fails OPEN by contract
6349
- * (`foldFunctionResult`), so an unreadable result costs a person legibility and never the record.
6350
- * Every character of `renderedLines` was quoted out of a committed catalog row by the renderer
6351
- * (D-10), so nothing a model wrote can ride this channel — which is why the parse is here and not
6352
- * a cast: a doctored result must not become a door for prose.
6353
- *
6354
- * BOTH ARMS RENDER. An abstention's lines are the honest "the catalog does not ground an answer",
6355
- * and XPLN-02's whole claim is that saying so plainly is an ANSWER a person is owed, not a silence.
6356
- */
5481
+ // Parsed, never cast, so a doctored result cannot carry prose; a parse failure adds nothing.
6357
5482
  renderResult(output) {
6358
5483
  const parsed = ExplainOutputSchema.safeParse(output);
6359
5484
  return parsed.success ? parsed.data.renderedLines : [];
6360
5485
  },
6361
- /**
6362
- * AN ABSTENTION IS AN HONEST ANSWER, NOT A CONTROL DECLINING (399.1 D-01/D-02, plan 03's razor).
6363
- *
6364
- * XPLN-02's whole claim is that "the catalog does not ground an answer" is a result this op is
6365
- * PROUD of. Folding it to `refused` would tell a person a guardrail stopped their question when
6366
- * what happened is that the question was answered truthfully.
6367
- *
6368
- * COUNTS AND NO TERRITORY, and here that is load-bearing rather than stylistic. The answer text
6369
- * is this op's `renderResult` output and reaches the turn's `answer` field; a receipt outcome
6370
- * carrying a cited row's description would put the territory back inside the map, which is the
6371
- * one thing `explain.test.ts`'s receipt-charter cell exists to red on.
6372
- */
5486
+ // An abstention is an answer, never `refused`; the receipt carries counts only.
6373
5487
  classify(output) {
6374
5488
  switch (output.kind) {
6375
5489
  case "answer":
@@ -6450,14 +5564,7 @@ var entitiesResolveSpec = {
6450
5564
  input: EntitiesResolveInputSchema,
6451
5565
  output: EntityCardResolutionSchema,
6452
5566
  risk: { name: `fn:${ENTITIES_RESOLVE_FUNCTION_ID}`, recognized: true, readOnly: true },
6453
- /**
6454
- * THREE ARMS, ALL ANSWERS (399.1 D-01, plan 03's razor).
6455
- *
6456
- * `ambiguous` and `none` are the resolver refusing to GUESS, which is the never-guess contract
6457
- * doing exactly what it promises — a resolution it will not invent is the answer to the question
6458
- * asked, not a control declining an act. Reporting either as `refused` would send a person
6459
- * looking for a guardrail that never fired.
6460
- */
5567
+ // `ambiguous` and `none` are answers, never `refused`; no guardrail fired.
6461
5568
  classify(output) {
6462
5569
  switch (output.kind) {
6463
5570
  case "resolved":
@@ -6477,9 +5584,7 @@ var entitiesResolveSpec = {
6477
5584
  const resolution = await deps.entities.resolve(input.ref);
6478
5585
  return renderResolution(
6479
5586
  resolution,
6480
- // The descriptors this render is ABOUT to produce cards for — every arm of them, so an
6481
- // ambiguous set containing one test reads the ledger once and every candidate is rendered
6482
- // against the same fold (390). A `none` arm renders nothing and reads nothing.
5587
+ // Every arm's descriptors, so an ambiguous set reads the ledger once and shares one fold.
6483
5588
  await readEntityCardContext(deps, descriptorsOf(resolution))
6484
5589
  );
6485
5590
  }
@@ -6514,7 +5619,7 @@ var knowledgeAcceptSpec = {
6514
5619
  output: KnowledgeChangeOutputSchema,
6515
5620
  boundThrough: "knowledgeReview",
6516
5621
  risk: { name: `fn:${KNOWLEDGE_ACCEPT_FUNCTION_ID}`, recognized: true, readOnly: false },
6517
- // The fold the three knowledge changes share (2026-10-03, quick 261003-cpv, 432 review IN-04).
5622
+ // The fold every knowledge change op shares.
6518
5623
  classify: classifyKnowledgeChangeOutput,
6519
5624
  invoke({ operationId, ...input }, deps) {
6520
5625
  return deps.knowledgeReview.change({
@@ -6538,9 +5643,7 @@ var knowledgeBundlesListSpec = {
6538
5643
  input: KnowledgeBundlesListInputSchema,
6539
5644
  output: KnowledgeBundlesListOutputSchema,
6540
5645
  risk: { name: `fn:${KNOWLEDGE_BUNDLES_LIST_FUNCTION_ID}`, recognized: true, readOnly: true },
6541
- /**
6542
- * A listing that answered — the count, and nothing out of a bundle (399.1 D-01).
6543
- */
5646
+ // The count only, nothing out of a bundle.
6544
5647
  classify(output) {
6545
5648
  return { status: "executed", outcome: `${output.length} knowledge bundle(s)` };
6546
5649
  },
@@ -6564,7 +5667,7 @@ var KnowledgeGatesUnboundError = class extends Error {
6564
5667
  };
6565
5668
  var knowledgeGatesListSpec = {
6566
5669
  id: KNOWLEDGE_GATES_LIST_FUNCTION_ID,
6567
- summary: "Read a knowledge record\u2019s gate history, standing receipts and current promotion verdict.",
5670
+ summary: "Read a knowledge record\u2019s mark and approval receipts, promotion lines and live promotion verdict.",
6568
5671
  resultKind: "unary",
6569
5672
  input: KnowledgeGatesListInputSchema,
6570
5673
  output: KnowledgeGatesListResultSchema,
@@ -6592,7 +5695,7 @@ var knowledgeForgetSpec = {
6592
5695
  output: KnowledgeChangeOutputSchema2,
6593
5696
  boundThrough: "knowledgeReview",
6594
5697
  risk: { name: `fn:${KNOWLEDGE_FORGET_FUNCTION_ID}`, recognized: true, readOnly: false },
6595
- // The fold the three knowledge changes share (2026-10-03, quick 261003-cpv, 432 review IN-04).
5698
+ // The fold every knowledge change op shares.
6596
5699
  classify: classifyKnowledgeChangeOutput,
6597
5700
  invoke({ operationId, ...input }, deps) {
6598
5701
  return deps.knowledgeReview.change({
@@ -6619,13 +5722,7 @@ var knowledgeRecordsDiffSpec = {
6619
5722
  input: KnowledgeRecordsDiffInputSchema,
6620
5723
  output: KnowledgeRecordsDiffOutputSchema,
6621
5724
  risk: { name: `fn:${KNOWLEDGE_RECORDS_DIFF_FUNCTION_ID}`, recognized: true, readOnly: true },
6622
- /**
6623
- * A diff that answered, WITH ITS BASELINE (399.1 D-01).
6624
- *
6625
- * The ordinal rides the outcome for the contract's own reason: "a change list with no baseline is
6626
- * a list whose meaning depends on remembering the question", and a receipt line is exactly the
6627
- * artifact that gets pasted somewhere the question is gone.
6628
- */
5725
+ // The ordinal rides the outcome, because a change list without its baseline loses its meaning.
6629
5726
  classify(output) {
6630
5727
  return {
6631
5728
  status: "executed",
@@ -6661,9 +5758,7 @@ var knowledgeRecordsInspectSpec = {
6661
5758
  input: KnowledgeRecordsInspectInputSchema,
6662
5759
  output: KnowledgeRecordsInspectOutputSchema,
6663
5760
  risk: { name: `fn:${KNOWLEDGE_RECORDS_INSPECT_FUNCTION_ID}`, recognized: true, readOnly: true },
6664
- /**
6665
- * An inspection that answered — the count, and no record content (399.1 D-01).
6666
- */
5761
+ // The count only, no record content.
6667
5762
  classify(output) {
6668
5763
  return { status: "executed", outcome: `${output.records.length} record(s) inspected` };
6669
5764
  },
@@ -6689,7 +5784,7 @@ var knowledgeRejectSpec = {
6689
5784
  output: KnowledgeChangeOutputSchema3,
6690
5785
  boundThrough: "knowledgeReview",
6691
5786
  risk: { name: `fn:${KNOWLEDGE_REJECT_FUNCTION_ID}`, recognized: true, readOnly: false },
6692
- // The fold the three knowledge changes share (2026-10-03, quick 261003-cpv, 432 review IN-04).
5787
+ // The fold every knowledge change op shares.
6693
5788
  classify: classifyKnowledgeChangeOutput,
6694
5789
  invoke({ operationId, ...input }, deps) {
6695
5790
  return deps.knowledgeReview.change({
@@ -6714,12 +5809,7 @@ var quarantineAddSpec = {
6714
5809
  input: QuarantineAddInputSchema,
6715
5810
  output: QuarantineAddOutputSchema,
6716
5811
  risk: { name: `fn:${QUARANTINE_ADD_FUNCTION_ID}`, recognized: true, readOnly: false },
6717
- /**
6718
- * `renewed` IS IDEMPOTENCY, AND THE CONTRACT CALLS IT INFORMATION (399.1 D-01).
6719
- *
6720
- * "Already quarantined is information an operator acts on, not an error" — so both arms are
6721
- * `executed` and the outcome says which gesture the ledger recorded.
6722
- */
5812
+ // Already quarantined is information, so both arms are `executed`.
6723
5813
  classify(output) {
6724
5814
  switch (output.kind) {
6725
5815
  case "entered":
@@ -6749,17 +5839,12 @@ var quarantineListSpec = {
6749
5839
  id: QUARANTINE_LIST_FUNCTION_ID,
6750
5840
  summary: "List every quarantined test with its owner, expiry and lifecycle state.",
6751
5841
  resultKind: "unary",
6752
- // Bound exactly where a composition supplies the quarantine port. Listing quarantine state
6753
- // is not something a host can approximate: an unbound host answering `[]` would say "nothing
6754
- // is quarantined" about a ledger it has never been able to read, which is a wrong answer
6755
- // wearing a correct one's clothes. So it refuses by name instead (D-09).
5842
+ // An unbound host refuses rather than answer `[]` about a ledger it cannot read.
6756
5843
  boundThrough: "quarantine",
6757
5844
  input: QuarantineListInputSchema,
6758
5845
  output: QuarantineListOutputSchema,
6759
5846
  risk: { name: `fn:${QUARANTINE_LIST_FUNCTION_ID}`, recognized: true, readOnly: true },
6760
- /**
6761
- * A ledger listing that answered — the count, and no entry's identity legs (399.1 D-01).
6762
- */
5847
+ // The count only, no entry's identity legs.
6763
5848
  classify(output) {
6764
5849
  const count = output.entries.length;
6765
5850
  return {
@@ -6790,9 +5875,7 @@ var quarantineReleaseSpec = {
6790
5875
  input: QuarantineReleaseInputSchema,
6791
5876
  output: QuarantineReleaseOutputSchema,
6792
5877
  risk: { name: `fn:${QUARANTINE_RELEASE_FUNCTION_ID}`, recognized: true, readOnly: false },
6793
- /**
6794
- * `no-entry` IS "an ordinary answer to an ordinary question" — the contract's words (399.1 D-01).
6795
- */
5878
+ // `no-entry` is an ordinary answer, not a refusal.
6796
5879
  classify(output) {
6797
5880
  switch (output.kind) {
6798
5881
  case "released":
@@ -6842,26 +5925,7 @@ var runsEventsSpec = {
6842
5925
  input: RunsEventsInputSchema,
6843
5926
  output: RunEventEnvelopeSchema,
6844
5927
  risk: { name: `fn:${RUNS_EVENTS_FUNCTION_ID}`, recognized: true, readOnly: true },
6845
- /**
6846
- * A STREAM'S VERDICT IS ABOUT THE ACQUISITION, AND IT MUST NOT READ AN ELEMENT (399.1 D-01/D-03).
6847
- *
6848
- * WHAT THIS OP CAN HONESTLY SAY. Every refusal it makes is a THROW before the iterable exists —
6849
- * `StreamReplayFloorError` when the requested resume point is below what the log can still serve,
6850
- * which is the whole reason the spec's header insists the stream is ACQUIRED inside `invoke`
6851
- * rather than lazily on first iteration. So a stream that came back is a replay that opened, and
6852
- * that is the fact this fold reports.
6853
- *
6854
- * IT DELIBERATELY IGNORES ITS ARGUMENT. Reading an element would mean CONSUMING the caller's
6855
- * stream — a side effect, on a member both this file and {@link FunctionSpec.classify} contract as
6856
- * pure, and one that would silently eat the first event of every replay. The argument is named
6857
- * `_stream` so the intent is legible at the signature rather than in a comment somebody may not
6858
- * reach.
6859
- *
6860
- * IT IS DECLARED HERE AND NOT INHERITED. `registry.ts` registers this spec through
6861
- * `runsEventsSpec as FunctionSpec`, and an `as`-cast satisfies `tsc` while shipping an op with no
6862
- * fold — which is precisely the hole the conformance census (`classifier-census.test.ts`) exists
6863
- * to close, and precisely why the required member alone is not the whole enforcement.
6864
- */
5928
+ // Must not read an element: consuming the stream would eat the caller's first event.
6865
5929
  classify(_stream) {
6866
5930
  return { status: "executed", outcome: "replay opened \u2014 the recorded events are streaming" };
6867
5931
  },
@@ -7024,17 +6088,7 @@ var runsLiveUrlSpec = {
7024
6088
  input: RunsLiveUrlInputSchema,
7025
6089
  output: RunsLiveUrlResultSchema,
7026
6090
  risk: { name: `fn:${RUNS_LIVE_URL_FUNCTION_ID}`, recognized: true, readOnly: true },
7027
- /**
7028
- * THE OUTCOME SENTENCE NEVER CONTAINS THE TOKEN OR THE URL (LIVE-02, D-04).
7029
- *
7030
- * A clause verdict is a thing surfaces PRINT — into chat transcripts, receipts and the audit
7031
- * sink — so interpolating the result here would put a live credential into every durable record
7032
- * this op touches, from one line of "helpful" formatting. What a person needs to know is that a
7033
- * link was minted and for which handle; the link itself is already in the result they asked for.
7034
- *
7035
- * Declared explicitly rather than inherited: `registry.ts` registers this through a
7036
- * `FunctionSpec` cast, and the classifier census is what makes a missing fold red.
7037
- */
6091
+ // The outcome never holds the token or URL, because surfaces print it into durable records.
7038
6092
  classify(result) {
7039
6093
  return { status: "executed", outcome: `liveUrl minted for handle ${result.handle}` };
7040
6094
  },
@@ -7176,8 +6230,7 @@ async function readNoVerdictSkipped(runId, deps) {
7176
6230
  function projectStageDetails(state, mask) {
7177
6231
  const rows = {
7178
6232
  analysis: state?.analyzeRun,
7179
- curation: state?.curateMemory,
7180
- proposals: state?.proposeImprovements
6233
+ curation: state?.curateMemory
7181
6234
  };
7182
6235
  const details = {};
7183
6236
  for (const stage of POST_PROCESSING_DETAIL_STAGES) {
@@ -7260,9 +6313,7 @@ var runsPostProcessingSpec = {
7260
6313
  id: RUNS_POST_PROCESSING_FUNCTION_ID,
7261
6314
  summary: "Read one run's post-processing chip statuses from durable job state.",
7262
6315
  resultKind: "unary",
7263
- // Bound exactly where a composition supplies durable queue storage (D-16 idiom). A host that
7264
- // cannot see the queue lists this by name as `unbound` rather than answering all-pending
7265
- // about a chain it has no way to observe.
6316
+ // Bound where the composition supplies durable queue storage; otherwise listed `unbound`.
7266
6317
  boundThrough: "postProcessing",
7267
6318
  input: RunsPostProcessingInputSchema,
7268
6319
  output: PostProcessingStatusSchema,
@@ -7369,13 +6420,7 @@ var runsDigestSpec = {
7369
6420
  input: RunsDigestInputSchema,
7370
6421
  output: RunDigestSchema,
7371
6422
  risk: { name: `fn:${RUNS_DIGEST_FUNCTION_ID}`, recognized: true, readOnly: true },
7372
- /**
7373
- * REFS AND COUNTS, which is this op's own law rather than a house style (399.1 D-01 · 391 D-18).
7374
- *
7375
- * The digest's `lines` are its rendered territory and they travel on `results[]`, which D-04
7376
- * prints in full. A receipt line reaching into them would put a run's story inside the map — the
7377
- * exact thing D-18 pinned this op's whole shape around.
7378
- */
6423
+ // Refs and counts only; the digest's lines travel on `results[]`.
7379
6424
  classify(output) {
7380
6425
  return {
7381
6426
  status: "executed",
@@ -7394,12 +6439,10 @@ var runsDigestSpec = {
7394
6439
  const skipped = status === void 0 ? projectPostProcessingSkipped(document) : status.skipped;
7395
6440
  const sections = {
7396
6441
  runId,
7397
- // VERBATIM — the same cluster views, carrying the same already-composed representative
7398
- // sentences the one renderer produced inside `analysis.get` (389 D-05/D-15).
6442
+ // Verbatim cluster views, with the representative sentences `analysis.get` composed.
7399
6443
  verdictClusters: analysis?.clusters ?? [],
7400
6444
  chain: {
7401
- // `undefined` answers exactly as an empty chain does: nothing was enqueued for this
7402
- // run, which is what a host with no queue database can honestly say (388's own note).
6445
+ // `undefined` answers as an empty chain: nothing was enqueued for this run.
7403
6446
  ...projectPostProcessingChips(void 0),
7404
6447
  ...status === void 0 ? {} : status.chips,
7405
6448
  ...status?.notification === void 0 ? {} : { outcome: status.notification.outcome },
@@ -7407,19 +6450,14 @@ var runsDigestSpec = {
7407
6450
  },
7408
6451
  ...markChanges === void 0 ? {} : { markChanges },
7409
6452
  candidates: {
7410
- // Absent counts read as zero HERE and only here: the row omits them when curation never
7411
- // committed a bundle (388 D-11), and a digest section is a place a person reads a
7412
- // number rather than a place a distinction between "none" and "not yet" survives — the
7413
- // chain chips one line up already carry that distinction.
6453
+ // Absent counts read as zero only here; the chain chips carry none versus not yet.
7414
6454
  curated: curation?.curated ?? 0,
7415
6455
  deprecated: curation?.deprecated ?? 0
7416
6456
  },
7417
6457
  quarantine: {
7418
6458
  quarantined: quarantine.filter((entry2) => entry2.state === "quarantined").length,
7419
6459
  expired: quarantine.filter((entry2) => entry2.state === "expired").length,
7420
- // `released` entries are deliberately absent from both the counts and the refs: a
7421
- // released test is one somebody already dealt with, and listing it every run would be
7422
- // the graveyard 390 legislated against, one surface later.
6460
+ // Released entries stay out of counts and refs; somebody already dealt with them.
7423
6461
  refs: quarantine.filter((entry2) => entry2.state === "quarantined").map(quarantineRef)
7424
6462
  }
7425
6463
  };
@@ -7645,19 +6683,12 @@ var runsDetailSpec = {
7645
6683
  id: RUNS_DETAIL_FUNCTION_ID,
7646
6684
  summary: "Read one run's durable members, steps and sub-actions as the dashboard projected them.",
7647
6685
  resultKind: "unary",
7648
- // Bound exactly where a composition owns the dashboard's projection database (the D-16 idiom). A
7649
- // host that holds no such database lists this by name as `unbound` rather than answering an
7650
- // empty run about a projection it has no way to observe.
6686
+ // Bound where the composition owns the projection database; otherwise listed `unbound`.
7651
6687
  boundThrough: "runDetail",
7652
6688
  input: RunsDetailInputSchema,
7653
6689
  output: RunDetailProjectionSchema,
7654
6690
  risk: { name: `fn:${RUNS_DETAIL_FUNCTION_ID}`, recognized: true, readOnly: true },
7655
- /**
7656
- * The three counts, which are what a receipt can honestly say about a read like this one.
7657
- *
7658
- * NOT THE TOKEN SUM: a receipt line quoting a number the caller is about to render would be a
7659
- * second place that number is stated, and D-06 exists because two producers of one total drift.
7660
- */
6691
+ // Counts only, not the token sum, so the total has one producer.
7661
6692
  classify(output) {
7662
6693
  return {
7663
6694
  status: "executed",
@@ -7795,7 +6826,7 @@ var runsEvidenceSpec = {
7795
6826
  boundThrough: "runDetail",
7796
6827
  input: RunsEvidenceInputSchema,
7797
6828
  output: RunsEvidenceResultSchema,
7798
- // 431-03 (D-03/D-07): the declared output is the canonical document; validation stays `output`.
6829
+ // The declared output is the canonical document; validation stays on `output`.
7799
6830
  outputRepresentation: RunsEvidenceOutputContract,
7800
6831
  risk: { name: `fn:${RUNS_EVIDENCE_FUNCTION_ID}`, recognized: true, readOnly: true },
7801
6832
  classify(output) {
@@ -7997,9 +7028,7 @@ function projectStep(testId, testIndex, evidence) {
7997
7028
  channel3: evidence.servedBrief.channel3.map(cautionLine)
7998
7029
  },
7999
7030
  trace: traceArmOf(evidence),
8000
- // PICKED, not spread (CR-01) — see `planSourceOf`. Spread-away absence, never a `null` and
8001
- // never a zero-fill: a step that replayed no procedure carries NO `planSource` key, which is
8002
- // a different byte from one that carries an empty source (the layer's own idiom).
7031
+ // Picked, not spread: a step that replayed nothing carries no `planSource` key.
8003
7032
  ...evidence.planSource === void 0 ? {} : { planSource: planSourceOf(evidence.planSource) },
8004
7033
  ...rung === void 0 ? {} : { retrievalRung: rung }
8005
7034
  };
@@ -8074,16 +7103,7 @@ var runsKnowledgeSpec = {
8074
7103
  input: RunsKnowledgeInputSchema,
8075
7104
  output: RunKnowledgeProjectionSchema,
8076
7105
  risk: { name: `fn:${RUNS_KNOWLEDGE_FUNCTION_ID}`, recognized: true, readOnly: true },
8077
- /**
8078
- * COUNTS, NOT CONTENT (399.1 D-01 · 391 D-18's rule applied here).
8079
- *
8080
- * A receipt line names how much a run used and learned; the brief summaries, step text and
8081
- * record ids themselves stay in `results[]` where a reader asked for them. Interpolating a
8082
- * served summary here would put producer prose — possibly masked material — into an audit map.
8083
- *
8084
- * `evidence-incomplete` is `executed` and not a refusal, on `knowledge.brief.why`'s reasoning:
8085
- * a pruned handoff is an answer this verb is proud of, not a control declining.
8086
- */
7106
+ // Counts only, so no served summary or masked material reaches an audit map.
8087
7107
  classify(output) {
8088
7108
  if (output.kind === "evidence-incomplete") {
8089
7109
  return {
@@ -8191,8 +7211,7 @@ var runsKnowledgeSpec = {
8191
7211
  ...runRung === void 0 ? {} : { retrievalRung: runRung },
8192
7212
  steps: joinedSteps,
8193
7213
  curation: {
8194
- // `undefined` answers exactly as an empty chain does — nothing was enqueued for this run
8195
- // — which is what a host with no queue database can honestly say (388's own note).
7214
+ // `undefined` answers as an empty chain: nothing was enqueued for this run.
8196
7215
  status: status?.chips.curation ?? projectPostProcessingChips(void 0).curation,
8197
7216
  ...countsPresent && curationRow !== void 0 ? {
8198
7217
  counts: {
@@ -8200,8 +7219,7 @@ var runsKnowledgeSpec = {
8200
7219
  deprecated: curationRow.deprecated
8201
7220
  }
8202
7221
  } : {},
8203
- // 2026-10-18 (444-26, D-61): the run's own handoff says why its chain was never enqueued,
8204
- // by the fold runs.postprocessing calls, so it holds on a host with no queue database too.
7222
+ // The handoff's own skip reason, so it holds on a host with no queue database.
8205
7223
  ...skipped === void 0 ? {} : { skipped: { reason: skipped.reason } }
8206
7224
  },
8207
7225
  learned: {
@@ -8261,20 +7279,12 @@ var runsListSpec = {
8261
7279
  summary: "List completed runs, newest finished first (20 by default, at most 200), each naming its tests; filter by verdict, test id or title, recorded target, or completion time.",
8262
7280
  resultKind: "unary",
8263
7281
  input: RunsListInputSchema,
8264
- // 2026-08-16 (396-04, D-16): the NAMED array schema, not `z.array(RunSummarySchema)` composed
8265
- // here. The two are the same shape; only one of them is an object a surface can also name, and
8266
- // D-16's whole claim is that the advertised schema and the validated schema are one object.
7282
+ // The named array schema, so the advertised and validated schema are one object.
8267
7283
  output: RunsListOutputSchema,
8268
- // 431-03 (D-03, A1): the canonical companion, tied to `output` above. It changes only what a
8269
- // declaration publishes; `output` is still what `invokeFunction` validates with.
7284
+ // The canonical companion changes only what a declaration publishes; `output` still validates.
8270
7285
  outputRepresentation: RunsListOutputContract,
8271
- // Read ops declare `readOnly` so the static classifier lands them on the read-only
8272
- // tier — allow in every execution context. The verdict is still computed and audited
8273
- // (D-11): there is no read bypass, only a read classification.
8274
7286
  risk: { name: `fn:${RUNS_LIST_FUNCTION_ID}`, recognized: true, readOnly: true },
8275
- /**
8276
- * A listing that answered — the count, and nothing out of a run (399.1 D-01).
8277
- */
7287
+ // The count only, nothing out of a run.
8278
7288
  classify(output) {
8279
7289
  return { status: "executed", outcome: `${output.length} completed run(s)` };
8280
7290
  },
@@ -8299,20 +7309,17 @@ import { RunsMarkInputSchema, RunsMarkOutcomeSchema } from "@vostride/agent-qa-s
8299
7309
  var RUNS_MARK_FUNCTION_ID = "runs.mark";
8300
7310
  var runsMarkSpec = {
8301
7311
  id: RUNS_MARK_FUNCTION_ID,
8302
- summary: "Mark a run or step false-fail / false-pass / confirmed-bug: freeze the marked decision as gold, triage, and apply the fix (a knowledge change or a test edit) in the call.",
7312
+ summary: "Mark a run or step false-fail / false-pass / confirmed-bug: triage it and apply the fix (a knowledge change or a test edit) in the call.",
8303
7313
  resultKind: "unary",
8304
7314
  boundThrough: "feedback",
8305
7315
  input: RunsMarkInputSchema,
8306
7316
  output: RunsMarkOutcomeSchema,
8307
- // D-13: a mark writes the committed eval root and, since 432-09, applies its fix to knowledge or
8308
- // a test file. mutating-local would understate its risk.
8309
- // No requiresHumanOrigin and, since 432-21 (D-12), no bearer: every caller on every door reaches it.
8310
- // 2026-10-01 (432-24, D-12): the `knowledge-write` scope it declared is gone too.
7317
+ // A mark applies its fix to committed knowledge or a test file. No requiresHumanOrigin, bearer or
7318
+ // scope: every caller on every door reaches it.
8311
7319
  risk: {
8312
7320
  name: `fn:${RUNS_MARK_FUNCTION_ID}`,
8313
7321
  recognized: true,
8314
- readOnly: false,
8315
- declaredTier: "mutating-shared"
7322
+ readOnly: false
8316
7323
  },
8317
7324
  classify(output) {
8318
7325
  switch (output.kind) {
@@ -8328,8 +7335,6 @@ var runsMarkSpec = {
8328
7335
  return { status: "refused", outcome: `step ${output.stepIndex} not found at member ${output.testIndex}` };
8329
7336
  case "ambiguous-address":
8330
7337
  return { status: "refused", outcome: "ambiguous run address: specify a member" };
8331
- case "ambiguous-correction":
8332
- return { status: "refused", outcome: "executable correction requires exactly one recorded decision" };
8333
7338
  default:
8334
7339
  return output;
8335
7340
  }
@@ -8358,17 +7363,12 @@ function maskResult(result, mask) {
8358
7363
  effects: record.evidence.effects.map((effect) => effect.direction === "confirm" ? effect : { ...effect, overturnsEvidenceToken: mask(effect.overturnsEvidenceToken) })
8359
7364
  }
8360
7365
  }))
8361
- },
8362
- groundTruth: result.groundTruth.map((item) => ({
8363
- ...item,
8364
- recordedVerdict: { ...item.recordedVerdict, reasoning: mask(item.recordedVerdict.reasoning) },
8365
- ...item.correctedExpectation === void 0 ? {} : { correctedExpectation: mask(item.correctedExpectation) }
8366
- }))
7366
+ }
8367
7367
  };
8368
7368
  }
8369
7369
  var runsMarkPreviewSpec = {
8370
7370
  id: RUNS_MARK_PREVIEW_FUNCTION_ID,
8371
- summary: "Preview what runs.mark would do for this exact input: its refusals, resolved step, labels, the knowledge records it reaches with their content and evidence effects, and the fix it would apply, without writing.",
7371
+ summary: "Preview what runs.mark would do for this exact input: its refusals, resolved step, the knowledge records it reaches with their content and evidence effects, and the fix it would apply, without writing.",
8372
7372
  resultKind: "unary",
8373
7373
  boundThrough: "feedbackReads",
8374
7374
  input: RunsMarkInputSchema2,
@@ -8377,7 +7377,7 @@ var runsMarkPreviewSpec = {
8377
7377
  classify(output) {
8378
7378
  switch (output.kind) {
8379
7379
  case "consequences":
8380
- return { status: "executed", outcome: `preview: ${output.markKind} would apply ${output.draft.kind} over ${output.markedDecisionCount} ${output.markedDecisionCount === 1 ? "decision" : "decisions"} (nothing written)` };
7380
+ return { status: "executed", outcome: `preview: ${output.markKind} would apply ${output.draft.kind} at step ${output.resolvedStep.stepIndex} (nothing written)` };
8381
7381
  case "quarantined":
8382
7382
  return { status: "executed", outcome: `preview: ${output.markKind} would be quarantined as ${output.class} (nothing written)` };
8383
7383
  case "already-marked":
@@ -8386,7 +7386,6 @@ var runsMarkPreviewSpec = {
8386
7386
  case "member-not-found":
8387
7387
  case "step-not-found":
8388
7388
  case "ambiguous-address":
8389
- case "ambiguous-correction":
8390
7389
  return runsMarkSpec.classify(output);
8391
7390
  default:
8392
7391
  return output;
@@ -8426,16 +7425,11 @@ function projectEntry(receipt, changedRecords, supersededBy, mask) {
8426
7425
  ordinal: receipt.ordinal,
8427
7426
  markedAt: receipt.markedAt,
8428
7427
  ...receipt.note === void 0 ? {} : { note: mask(receipt.note) },
8429
- corrected: {
8430
- expectation: receipt.correctedExpectation !== void 0,
8431
- action: receipt.correctedAction !== void 0
8432
- },
8433
7428
  ...receipt.rootCause === void 0 ? {} : {
8434
7429
  rootCause: { class: receipt.rootCause.class, producer: mask(receipt.rootCause.producer) }
8435
7430
  },
8436
7431
  ...receipt.isRerun === void 0 ? {} : { isRerun: receipt.isRerun },
8437
7432
  disposition: receipt.disposition,
8438
- gold: receipt.gold,
8439
7433
  evidence: receipt.evidence,
8440
7434
  ...receipt.supersedes === void 0 ? {} : { supersedes: receipt.supersedes },
8441
7435
  active: supersededBy === void 0,
@@ -8701,8 +7695,7 @@ function projectLearned(knowledge, document) {
8701
7695
  curation: {
8702
7696
  status: curation.status,
8703
7697
  ...curation.counts === void 0 ? {} : { counts: { ...curation.counts } },
8704
- // 2026-10-18 (444-26, D-61): the run's own handoff says why its chain was never enqueued, by
8705
- // the fold runs.postprocessing serves; an ordinary run carries no key.
7698
+ // The handoff's own skip reason; an ordinary run carries no key.
8706
7699
  ...skipped === void 0 ? {} : { skipped: { reason: skipped.reason } }
8707
7700
  },
8708
7701
  snapshot,
@@ -8788,237 +7781,75 @@ var BUILT_IN_FUNCTION_SPECS = [
8788
7781
  runsListSpec,
8789
7782
  runsGetSpec,
8790
7783
  runsEventsSpec,
8791
- // 403 D-03: the ONE producer of live-view capability tokens. Bound through the OPTIONAL
8792
- // `liveView` dep, so a host with no served origin lists it `unbound` BY NAME rather than
8793
- // handing back a URL that resolves to nothing.
7784
+ // Bound through optional `liveView`; a host with no served origin lists it `unbound`.
8794
7785
  runsLiveUrlSpec,
8795
- // 2026-09-25 (428 D-07): the editor subject's minter, through the same `liveView` dep; a host
8796
- // that serves no live editor refuses it by name. Unprojected from MCP by ruling.
7786
+ // Same `liveView` dep; not projected to MCP.
8797
7787
  editorLiveUrlSpec,
8798
- // Bound exactly in the compositions that supply a `runInvocation` port (D-16); listed
8799
- // either way, so a host that cannot start a run reports a visible `unbound` rather than a
8800
- // hole in the catalog, and `invokeFunction` refuses it by name. `composeLocalAgent()` in
8801
- // `@vostride/agent-qa-agent-local` is the binding site.
7788
+ // Bound where a composition supplies `runInvocation`; `composeLocalAgent()` is the binding site.
8802
7789
  runsStartSpec,
8803
- // 388 D-14: THE chip-truth read. Registered here rather than beside a surface because it is
8804
- // the single producer three surfaces share — the CLI's exit summary line, 392's chat and M5's
8805
- // dashboard are all thin clients of this one op, and the 397 parity suite inherits it by
8806
- // being in the catalogue. Bound through the OPTIONAL `postProcessing` dep, so a host with no
8807
- // durable queue lists it `unbound` BY NAME instead of answering all-pending about a chain it
8808
- // cannot see.
7790
+ // Bound through optional `postProcessing`, so a host with no durable queue lists it `unbound`.
8809
7791
  runsPostProcessingSpec,
8810
- // 2026-08-27 (405 D-03): THE durable run-detail read — members, steps, sub-actions and the D-06
8811
- // token sum in ONE result. Registered here rather than beside the dashboard because it is the
8812
- // fourth-door law applied to the page that used to read SQLite from inside a route handler: the
8813
- // browser, the CLI, MCP and chat now ask one registry the same question. Bound through the
8814
- // OPTIONAL `runDetail` dep, so a host that owns no dashboard projection database lists it
8815
- // `unbound` BY NAME instead of answering an empty run about a projection it cannot see.
7792
+ // Bound through optional `runDetail`, so a host with no projection database lists it `unbound`.
8816
7793
  runsDetailSpec,
8817
- // 423 D-04: parsed evidence uses the existing detail and blob readers together.
7794
+ // Parsed evidence uses the detail and blob readers together.
8818
7795
  runsEvidenceSpec,
8819
- // 419 GATE3-01: the sovereign mark write binds only where the host supplies feedback.
7796
+ // The mark write binds only where the host supplies feedback.
8820
7797
  runsMarkSpec,
8821
- // 423 D-19: the mark history read binds through feedbackReads, never through the writer above.
7798
+ // Binds through `feedbackReads`, never through the writer above.
8822
7799
  runsMarksHistorySpec,
8823
- // 423 D-16: the consequence preview reads runs.mark's shared plan through feedbackReads, never the writer.
7800
+ // Reads `runs.mark`'s shared plan through `feedbackReads`, never the writer.
8824
7801
  runsMarkPreviewSpec,
8825
- // 2026-08-27 (405-05 D-12/D-13): THE ref-resolving read — one run's content-addressed evidence
8826
- // blob, by sha, with the content type sniffed from the bytes themselves. Registered beside
8827
- // `runs.detail` because the two are the halves of one claim: the projection carries an
8828
- // `artifactRef`, and this is how that ref becomes an image on a page. It is here rather than in
8829
- // a route for `runs.detail`'s reason — a route constructing the evidence store would be a
8830
- // private store read no roster row covers, and 408's embed and MCP would each need their own.
8831
- // Bound through the OPTIONAL `evidenceBlobs` dep, so a host with no runtime root lists it
8832
- // `unbound` BY NAME instead of reporting every artifact in the product as absent.
7802
+ // Bound through optional `evidenceBlobs`, so a host with no runtime root lists it `unbound`.
8833
7803
  runsArtifactSpec,
8834
- // 389 D-11: THE analysis read — resolved verdicts, rendered sentences, clusters, one
8835
- // producer for the CLI print, the run page and chat. Bound through the OPTIONAL `analysis`
8836
- // dep (the artifact reader), so a host that cannot see run directories lists it `unbound`
8837
- // BY NAME; the optional `recurrence` dep only enriches the sentence's recurrence clause
8838
- // and never gates the binding.
7804
+ // Bound through optional `analysis`; `recurrence` only enriches the sentence, never the binding.
8839
7805
  analysisGetSpec,
8840
7806
  knowledgeBundlesListSpec,
8841
- // 416 D-17: gate-and-promotion history, through the required knowledge port's
8842
- // optional member; missing bindings answer with a named refusal.
7807
+ // Gate history through the knowledge port's optional member; an absent one is a named refusal.
8843
7808
  knowledgeGatesListSpec,
8844
- // 387 KREV-01: the read half of the review-verb family. All three bind through the REQUIRED
8845
- // `knowledge` port, so — unlike the mutating half 387-08 registers through the optional
8846
- // `knowledgeReview` dep — there is no composition that lists these and cannot run them.
7809
+ // Bound through the required `knowledge` port, so every composition can run these.
8847
7810
  knowledgeRecordsInspectSpec,
8848
7811
  knowledgeRecordsDiffSpec,
8849
7812
  knowledgeRecordShowSpec,
8850
- // KREV-02: the honest verb. Read-tier like its four siblings, and bound everywhere for the
8851
- // same reason — its substrate is the RUNS port, which is required.
7813
+ // Bound everywhere, because its substrate is the required runs port.
8852
7814
  knowledgeBriefWhySpec,
8853
- // 2026-08-29 (407 D-05/D-06): the Knowledge panel's ONE read — what a run USED (served brief
8854
- // channels, serve-time trace arms, procedure replay/hint, the recorded retrieval rung) and what
8855
- // it LEARNED (curation counts, the record changes attributable to it, canary outcomes). Placed
8856
- // directly after `knowledge.brief.why` because it is that verb widened from one step to a whole
8857
- // run, and BOUND EVERYWHERE for the identical reason: its substrate is the RUNS port, which is
8858
- // required. The three optional deps it consults only enrich the Learned half, so binding
8859
- // through any of them would leave a host with no queue database unable to see what its own runs
8860
- // used. A pruned handoff answers `evidence-incomplete` — a result the page renders, never a
8861
- // refusal it has to catch (D-06).
7815
+ // Bound everywhere on the runs port; its optional deps only enrich the Learned half.
8862
7816
  runsKnowledgeSpec,
8863
- // 431 D-06: one completed run's address map, composed from the handoff, the run-detail rows and
8864
- // the runs.knowledge projection. Bound through `runDetail`, so a host with no projection lists
8865
- // it `unbound` BY NAME instead of mapping a run it cannot see.
7817
+ // Bound through `runDetail`, so a host with no projection lists it `unbound`.
8866
7818
  runsOverviewSpec,
8867
- // 387 KREV-01 D-17: the MUTATING half. Registered here and bound only where a composition
8868
- // supplies `knowledgeReview`, so a read-only host lists all three as `unbound` BY NAME rather
8869
- // than omitting them: a capability that is absent from a catalogue is indistinguishable from one
8870
- // that never existed, and D-09 requires the two to look different.
8871
- // 2026-09-30, 432-12 (D-05): the three changes apply in the call; the `.propose` ids are gone
8872
- // with no alias. 2026-10-01, 432-17 (D-05/D-22): `knowledge.proposal.apply` is deleted with the
8873
- // inbox; old proposal files are never read again.
7819
+ // Bound only where a composition supplies `knowledgeReview`; otherwise listed `unbound`.
8874
7820
  knowledgeAcceptSpec,
8875
7821
  knowledgeRejectSpec,
8876
7822
  knowledgeForgetSpec,
8877
7823
  configGetSpec,
8878
- // 395 D-01/D-03 · CFGM-01: the config editor's first action. Registered as a SIBLING of
8879
- // `config.get` rather than in a family of its own, because it is the write half of a story whose
8880
- // read half already ships here — and registering it is what makes it chat-reachable (392 D-07),
8881
- // projectable over MCP (396) and walkable by the parity suite (397) with no harness change.
8882
- //
8883
- // 2026-09-30, 432-11 (D-05/D-07): the config ops write in the call through the direct writer.
8884
- // 2026-10-01, 432-17 (D-05/D-09): `config.proposal.apply` and `config.undo.propose` are deleted
8885
- // with the inbox; a written change is reverted with git.
8886
- //
8887
- // They bind through OPTIONAL deps, so a host with no config files lists them `unbound` BY NAME
8888
- // rather than answering about a project it cannot read.
7824
+ // Config ops write in the call through optional deps; a host with no config lists them `unbound`.
8889
7825
  configFieldSetSpec,
8890
- // 395-05 D-01: the rest of the closed action class, registered as SIBLINGS on exactly the footing
8891
- // above. One op per action is what makes the class legible in the registry itself — the planner
8892
- // picks an action by tool name, 396 projects each one as its own MCP tool, and 397's parity walk
8893
- // enumerates them — where a single union op carrying a mode string would have projected as one
8894
- // tool whose real vocabulary lives inside a field nothing validates.
7826
+ // One op per action, because the planner and MCP pick actions by tool name.
8895
7827
  configAuthSetSpec,
8896
7828
  configHookAddSpec,
8897
7829
  configTargetCreateSpec,
8898
7830
  configTargetEditSpec,
8899
- // 395-08 D-13/D-15 · CFGM-05: the sixth config op, and the only one that configures a project
8900
- // that has no configuration. Registered rather than folded into `config.target.create` because
8901
- // the two refuse each other's case by name — a create is never an upsert, and a scaffold is
8902
- // never an overwrite — and because the planner picks actions by tool name.
7831
+ // Not folded into `config.target.create`: a create never upserts and a scaffold never overwrites.
8903
7832
  configProjectScaffoldSpec,
8904
- // 390 D-12/D-13: a new `quarantine.` domain — a taxonomy extension exactly as `entities.`
8905
- // was, because quarantine state is neither a run, a memory nor an analysis. All three bind
8906
- // through the OPTIONAL `quarantine` dep, so a host with no knowledge root lists them
8907
- // `unbound` BY NAME rather than answering an empty list about a ledger it cannot read.
8908
- // `list` is read-tier; `release` and `add` are DIRECT gated writes (no propose+apply pair —
8909
- // that idiom is for learned-knowledge mutations, and this is operational state).
7833
+ // Bound through optional `quarantine`; `release` and `add` write directly.
8910
7834
  quarantineListSpec,
8911
7835
  quarantineReleaseSpec,
8912
7836
  quarantineAddSpec,
8913
- // 391 PROP-03 / D-15 added a `proposals.` domain (list, show, reject, accept). 2026-10-01, 432-17
8914
- // (D-05/D-22): the domain is deleted with the inbox, with no alias and no hidden follow-up; old
8915
- // proposal files are never read again.
8916
- // 391 PROP-01 / D-03 added a `grants.` domain (list, revoke) over the committed standing-grant
8917
- // ledger. 2026-10-01, 432-18 (D-14/D-15/D-22): the domain is deleted with the ledger; an old
8918
- // `grants.jsonl` is never read again.
8919
- // 391 PROP-04 / D-16: the per-run digest. Filed under `runs.` beside `runs.postprocessing`
8920
- // because what it answers is a fact about a RUN. Read-tier, and it registers NO new dep: the
8921
- // projection composes from capabilities this catalogue already had (since 432-09 its substrate
8922
- // is the required runs port, and since 432-17 there is no proposals inbox to read).
7837
+ // Filed under `runs.` because it answers a fact about a run; it adds no dep.
8923
7838
  runsDigestSpec,
8924
- // 387 D-09: a new `entities.` domain, a taxonomy extension exactly as `knowledge.` was.
8925
- // Both read-tier, both bound in every composition — the directory is a REQUIRED dep, so
8926
- // there is no host that lists these and cannot run them.
7839
+ // The directory is a required dep, so every composition can run these.
8927
7840
  entitiesResolveSpec,
8928
7841
  entitiesGetSpec,
8929
- // 392 D-24: a new `agent.` domain, and the taxonomy extension that matters most — the chat turn
8930
- // entry is itself a catalogued, gated capability rather than a power the REPL holds privately
8931
- // (§3's L10 law). It binds through the OPTIONAL `chatTurn` dep, so a host with no engine lists
8932
- // it `unbound` BY NAME rather than answering an empty turn about a request it understood.
8933
- //
8934
- // `agent.chat`, the session-stateful sibling the milestone also names, is deliberately absent
8935
- // until a consumer exists: a registered entry nothing drives would still oblige 397's parity
8936
- // suite to stub it on every surface.
7842
+ // Bound through optional `chatTurn`, so a host with no engine lists it `unbound`.
8937
7843
  agentAskSpec,
8938
- // 2026-08-17 (398 D-09 · XPLN-02): the SECOND member of the `agent.` domain, and the first op
8939
- // this project has added since the 397 conformance suite closed. Registering it is the decision
8940
- // rather than a filing choice: "what can you do" is a question every surface wants to answer, and
8941
- // a surface that answered it privately would be a capability the catalogue does not list — the
8942
- // private-power pattern §3's L10 law abolishes and the census exists to make impossible. As a
8943
- // registered op it is chat-reachable through the 1:1 planner binding and `/fn` with no harness
8944
- // change, adjudicated in `mapping.ts` like every op, and walked by the parity suite for free.
8945
- //
8946
- // Bound through the OPTIONAL `capabilityCatalog` port, whose binding site is `composeLocalAgent()`
8947
- // (385 D-15 — therefore both the CLI and the daemon). A host with no catalog lists it `unbound` BY
8948
- // NAME rather than answering from model memory, which is the exact failure XPLN-02 exists to
8949
- // forbid: an explain that quietly lost its grounding data still SOUNDS like an explain.
7844
+ // Bound through optional `capabilityCatalog`, so a host never explains from model memory.
8950
7845
  explainSpec,
8951
- // 2026-08-10 (393 D-01): a new `authoring.` domain, and the keep-filter's entry point. The four
8952
- // gates that decide whether a generated test is worth keeping are a CATALOGUED capability rather
8953
- // than a private power of whatever writes tests — so 393's corpus proof drives the real gated
8954
- // entry point (the produced-not-literal law), 396's MCP projection and 397's parity suite inherit
8955
- // it from this census with no harness change, and the standalone question ("is this existing test
8956
- // worth keeping?") is answerable from every surface.
8957
- //
8958
- // Bound through the OPTIONAL `draftEvaluation` bundle, whose binding site is `composeLocalAgent()`
8959
- // in `@vostride/agent-qa-agent-local` (385 D-15 — one composition root, therefore both the CLI and
8960
- // the daemon). The bundle is supplied only where the host can BOTH parse a test file and start a
8961
- // run, so a host missing either half lists this capability `unbound` BY NAME rather than leaving a
8962
- // hole in the catalogue — and rather than returning a receipt whose four booleans were computed
8963
- // from no runs at all, which is the one answer that could get a draft kept on the strength of
8964
- // nothing.
8965
- //
8966
- // Registering it does NOT flip 392's `write_test` fail-closed scenario: that tool name stays
8967
- // absent from the catalogue until 394 (D-01), and nothing here mints it.
7846
+ // Bound where a host can both parse a test and start a run; otherwise listed `unbound`.
8968
7847
  authoringVerifyDraftSpec,
8969
- // 2026-08-11 (394 D-01 · WTST-01): the authoring LOOP's one public entry, beside the evaluator
8970
- // it composes. All six stages — brief, dedup, compile, verify, route, compound — run INSIDE this
8971
- // op, because the stage order is an invariant and registering a stage each would make it a hope
8972
- // about the plan the chat planner happened to make rather than a property of the code. Everything
8973
- // under the surface still crosses the function layer: stage 4 dispatches `authoring.draft.verify`
8974
- // through `invokeFunction` on a caller minted with exactly that op's declared scopes.
8975
- //
8976
- // Bound through the OPTIONAL `draftShelf` bundle — the model and the shelf — whose binding site
8977
- // is `composeLocalAgent()` in `@vostride/agent-qa-agent-local` (385 D-15, therefore both the CLI
8978
- // and the daemon). The read half (`authoringRead`) is supplied with it or not at all and the
8979
- // invoke refuses on either by the same name, so a host that cannot serve an authoring brief says
8980
- // `unbound` BY NAME rather than compiling a test against an empty one — a plausible draft
8981
- // authored against nothing this project knows, which is the failure that looks most like success.
8982
- //
8983
- // Inverting the entry above: registering THIS one DOES flip 392's `write_test` fail-closed chat
8984
- // scenarios, because the tool name they were waiting on now exists in the catalogue the planner
8985
- // binds 1:1. Plan 11 performs the flip and pins planned == executed.
7848
+ // Every stage runs inside this op, so a planner cannot reorder or skip one.
8986
7849
  authoringDraftWriteSpec,
8987
- // 2026-08-11 (394-07 D-12/D-13 · WTST-05): the honesty funnel's ONE read, beside the loop it
8988
- // measures. It is registered rather than left a private computation for the same reason the two
8989
- // entries above it are: the number that says whether the keep-filter is filtering must be the
8990
- // SAME number in chat, in the CLI and in the per-run digest, and a projection reachable only
8991
- // from whichever surface owned it would become three answers the moment the second surface
8992
- // wanted one. Read-tier and `readOnly` — it starts nothing and writes nothing, which is D-14
8993
- // expressed as a risk descriptor rather than as a promise.
8994
- //
8995
- // Bound through the OPTIONAL `authoringFunnel` bundle, whose binding site is
8996
- // `composeLocalAgent()` in `@vostride/agent-qa-agent-local` (385 D-15 — therefore both the CLI
8997
- // and the daemon). A host with no runtime root lists it `unbound` BY NAME rather than answering
8998
- // an all-zero funnel: zero built with zero write-eligible does not read as "nothing happened", it
8999
- // reads as a loop that filtered everything, which is the one wrong answer that would make the
9000
- // acceptance-failure flag unable to fire on the very hosts that most need it.
7850
+ // An unbound host refuses rather than answer a zero funnel that reads as filtering everything.
9001
7851
  authoringFunnelGetSpec,
9002
- // 2026-08-11 (394-09 D-21 · WTST-07): the user-facing edit, and the patch contract's SECOND real
9003
- // consumer. Until this op existed, `applyPatchSetToTestDoc` and the RFC-6902 vocabulary had one
9004
- // caller — the repair loop — and a contract with one consumer is a contract shaped like its one
9005
- // caller. That is not a style objection: this project has twice paid for a one-sided contract
9006
- // that looked correct because only one side ever exercised it (the two-sided contract law).
9007
- //
9008
- // It is NOT a stage of `authoring.draft.write`, which is the argument `loop-ordering.test.ts`
9009
- // requires beside a new `authoring.` id. That pin forbids promoting a loop stage to its own
9010
- // registered op, because a planner holding one could reorder or skip the rest. This verb takes an
9011
- // existing COMMITTED test and a change to make to it; it has no position in the six-stage order,
9012
- // and invoking it before, during or after a loop invocation answers the same question.
9013
- //
9014
- // `mutating-local` and NOT the write op's `mutating-shared`: it takes one model call, starts zero
9015
- // runs, and writes only to the gitignored draft shelf. The consent that matters for an edit is
9016
- // the inbox accept that turns the proposal into a commit, which 391 already owns.
9017
- //
9018
- // Bound through the OPTIONAL `draftShelf` bundle; `draftEvaluation` is checked by the same name
9019
- // inside the invoke, because resolving the target's bytes and applying the patch set are two
9020
- // halves of one capability and a host that had only one of them would list this op `bound` and
9021
- // then fail far from whichever half was missing.
7852
+ // Not a loop stage: it edits a committed test; `draftEvaluation` is checked inside the invoke.
9022
7853
  authoringDraftEditSpec
9023
7854
  ];
9024
7855
  var REGISTRY_INTERNALS = /* @__PURE__ */ new WeakMap();
@@ -9040,16 +7871,7 @@ function declarationView(spec, deps) {
9040
7871
  return Object.freeze({
9041
7872
  id: spec.id,
9042
7873
  summary: spec.summary,
9043
- // 2026-10-01 (432-20, D-10/D-12): no `requiredScopes` and no `requiresHumanOrigin`. Both were
9044
- // projected here so a listing and the dispatch checks read one field; the checks are deleted,
9045
- // and 432-39 deleted both members from the metadata type. The view is built field by field, so
9046
- // an old key smuggled onto a spec object still cannot ride through by accident.
9047
- // 2026-10-02 (432-42, D-14): no `discloses` column; the per-op disclosure is deleted.
9048
- // 399.1 D-01/D-04: the two 399.1 folds, projected as conditional spreads so absent stays
9049
- // absent — the callables stay on the spec, and what crosses to a listing is the
9050
- // boolean fact that this op folds its own result. The golden ledger reads both columns off
9051
- // here, so "which ops classify their own refusals" is a census over produced facts rather than
9052
- // over somebody's reading of forty-three op files.
7874
+ // Conditional spreads keep absent absent; only the boolean fact crosses to a listing.
9053
7875
  ...spec.classify === void 0 ? {} : { classifies: true },
9054
7876
  ...spec.renderResult === void 0 ? {} : { rendersResult: true },
9055
7877
  resultKind: spec.resultKind,
@@ -9356,24 +8178,12 @@ var CHAT_RESOLVER_TABLE = Object.freeze([
9356
8178
  }),
9357
8179
  rule({
9358
8180
  alias: "status",
9359
- // ADJUDICATION (the plan asks for it recorded here): the candidates were `runs.list` and
9360
- // `runs.get`. `runs.get` addresses exactly one run BY ID and refuses without one, so it
9361
- // cannot answer a person who typed a bare word — the id is the very thing they did not
9362
- // supply. `runs.list` takes `{}`, answers the 20 most recently finished runs newest first
9363
- // (431 D-08), and is the only read in the registry whose contract answers "what has happened
9364
- // here lately" without being told what to look at. `status <run>` is not minted:
9365
- // `digest <run>` already owns "tell me about that one run", and two verbs for it would be
9366
- // two answers.
8181
+ // `runs.list` takes `{}` and answers recent runs; `runs.get` refuses without the run id.
9367
8182
  resolve: (clause) => isExactly("status")(clause) ? { kind: "op", functionId: RUNS_LIST_FUNCTION_ID, input: {} } : void 0
9368
8183
  }),
9369
8184
  rule({
9370
8185
  alias: "test-ref",
9371
- // 387 D-05's never-guess ladder does the resolving, and the ref it receives is WHAT THE
9372
- // PERSON TYPED, verbatim. `entities.resolve` accepts "any reference at all — short-ID, URI,
9373
- // or free text", so pre-chewing `test 42` into `42` here would be this file inventing a
9374
- // second resolution grammar in front of the one that exists to be the only one. Two forms
9375
- // match: the `test <n>` phrase and a bare well-formed short-ID, the latter recognised through
9376
- // the schema's own parser rather than a regex spelled here.
8186
+ // The ref passes verbatim to `entities.resolve`, which owns the only resolution grammar.
9377
8187
  resolve: (clause) => {
9378
8188
  const trimmed = clause.trim();
9379
8189
  const named = argumentAfter("test", clause);
@@ -9395,8 +8205,6 @@ var CHAT_RESOLVER_TABLE = Object.freeze([
9395
8205
  return { kind: "op", functionId: RUNS_DIGEST_FUNCTION_ID, input: { runId: reference } };
9396
8206
  }
9397
8207
  }),
9398
- // 432-13 (D-05/D-14): the `proposals` inbox alias is gone. Changes apply in the call, so there is
9399
- // no pending-decision inbox to route a bare word to. 432-17 deleted the inbox and its ops too.
9400
8208
  rule({
9401
8209
  alias: "model",
9402
8210
  resolve: (clause) => {
@@ -9406,10 +8214,7 @@ var CHAT_RESOLVER_TABLE = Object.freeze([
9406
8214
  }),
9407
8215
  rule({
9408
8216
  alias: "fn",
9409
- // THE PASSTHROUGH (D-06). The op id is NOT validated here — deliberately. `invokeFunction`'s
9410
- // step 1 raises `UnknownFunctionError` naming the id before any dependency is touched, so a
9411
- // typo and a capability that has not shipped yet both come back named, for free, from the one
9412
- // place that knows what is registered. A check here would be a second catalogue.
8217
+ // The op id is unchecked here: `invokeFunction` names an unknown id before touching anything.
9413
8218
  resolve: (clause) => {
9414
8219
  const rest = argumentAfter("/fn", clause);
9415
8220
  if (rest === void 0) return void 0;
@@ -9444,11 +8249,7 @@ var CHAT_HELP_ENTRIES = Object.freeze([
9444
8249
  Object.freeze({ alias: "run", syntax: "run <test>", summary: "start a run for a test" }),
9445
8250
  Object.freeze({ alias: "status", syntax: "status", summary: "the recent runs" }),
9446
8251
  Object.freeze({ alias: "test-ref", syntax: "test <n>", summary: "show a test by reference" }),
9447
- // 2026-08-17 (398 plan 09): this line used to read `digest` / "the latest run digest", and both
9448
- // halves were false. The rule fifteen lines up requires a reference and treats a bare `digest` as
9449
- // a near-miss precisely so the table never guesses which run somebody meant — so the help text
9450
- // was advertising a guess the resolver refuses to make. The argument-contract cell in
9451
- // `help-census.test.ts` is what now holds the two together.
8252
+ // `digest` needs a run reference: the resolver treats a bare `digest` as a near-miss.
9452
8253
  Object.freeze({ alias: "digest", syntax: "digest <run>", summary: "that run's digest" }),
9453
8254
  Object.freeze({
9454
8255
  alias: "fn",
@@ -9472,26 +8273,6 @@ var CHAT_HELP_TEXT = [
9472
8273
  "Anything else is planned by the model and reconciled against the table before anything runs."
9473
8274
  ].join("\n");
9474
8275
 
9475
- // src/chat/retired-chat-verbs.ts
9476
- var RETIRED_PIN = "/pin";
9477
- var RETIRED_INBOX = "proposals";
9478
- function isRetiredPin(lowered) {
9479
- if (lowered === RETIRED_PIN) return true;
9480
- if (!lowered.startsWith(RETIRED_PIN)) return false;
9481
- const next = lowered.charAt(RETIRED_PIN.length);
9482
- return next === " " || next === " ";
9483
- }
9484
- function retiredChatVerbReason(clause) {
9485
- const lowered = clause.trim().toLowerCase();
9486
- if (isRetiredPin(lowered)) {
9487
- return "/pin was retired in this version: nothing asks before an operation runs, so there is nothing to pin. Nothing in this request was run.";
9488
- }
9489
- if (lowered === RETIRED_INBOX) {
9490
- return "the proposals inbox was retired in this version: changes apply when they are made, so nothing waits for a decision. Nothing in this request was run.";
9491
- }
9492
- return void 0;
9493
- }
9494
-
9495
8276
  // src/chat/session-repair.ts
9496
8277
  import { guardChatSessionEvent } from "@vostride/agent-qa-schema";
9497
8278
  var TURN_CLOSING_KINDS = ["turn_complete", "aborted"];
@@ -9598,11 +8379,6 @@ function refusalName(error) {
9598
8379
  function refusalMessage(error) {
9599
8380
  return error instanceof Error && error.message !== "" ? error.message : refusalName(error);
9600
8381
  }
9601
- function refusalSentenceOf(error) {
9602
- if (typeof error !== "object" || error === null) return void 0;
9603
- const sentence = error.neutralReason;
9604
- return typeof sentence === "string" && sentence.trim() !== "" ? sentence : void 0;
9605
- }
9606
8382
  function crossBoundaryLines(answer) {
9607
8383
  if (answer.crossBoundary !== true) return [];
9608
8384
  const reasons = answer.crossBoundaryReasons ?? [];
@@ -9742,12 +8518,6 @@ async function runTurnThrough(deps, opened, input) {
9742
8518
  // make "stage 1 did not recognise this" and "this build cannot do it" the same bytes.
9743
8519
  outcome: mapped.length + directives.length === clauses.length ? "resolved" : "unresolved"
9744
8520
  });
9745
- for (const clause of clauses) {
9746
- if (mapped.some((resolved) => resolved.clause.index === clause.index)) continue;
9747
- if (directives.some((entry2) => entry2.clause.index === clause.index)) continue;
9748
- const retired = retiredChatVerbReason(clause.text);
9749
- if (retired !== void 0) return refuse(clause.text, retired);
9750
- }
9751
8521
  for (const entry2 of directives) {
9752
8522
  const notBound = directiveBindingRefusal(directiveHandlers, entry2);
9753
8523
  if (notBound !== void 0) return refuse(entry2.clause.text, notBound);
@@ -9791,15 +8561,7 @@ async function runTurnThrough(deps, opened, input) {
9791
8561
  turnId,
9792
8562
  at: deps.now(),
9793
8563
  stage: "planner",
9794
- // The stage RESOLVED when the model produced something to reconcile — a plan, or an
9795
- // answer. It did not when the model was unreachable or said nothing. Whether what it
9796
- // produced survives reconciliation is the next stage's question, and answering it in
9797
- // this field would make "the model proposed nothing" and "the reconciler refused the
9798
- // proposal" the same bytes for every reader of the log.
9799
- //
9800
- // ONE TERM, NOT TWO: an `answer_only` call IS a tool call, so the disjunction this used to
9801
- // spell (`|| planned.answer !== undefined`) named the same set twice through a projection
9802
- // that no longer exists (392-14).
8564
+ // Resolved when the model produced a plan or an answer, whatever reconciliation decides.
9803
8565
  outcome: planned.failure === void 0 && planned.toolCalls.length > 0 ? "resolved" : "unresolved"
9804
8566
  });
9805
8567
  }
@@ -9932,8 +8694,6 @@ async function runTurnThrough(deps, opened, input) {
9932
8694
  status: "failed",
9933
8695
  outcome: refusalName(error)
9934
8696
  };
9935
- const refusal = refusalSentenceOf(error);
9936
- if (refusal !== void 0) answerLines.push(refusal);
9937
8697
  }
9938
8698
  }
9939
8699
  if (receipt.status === "failed") failed = true;
@@ -10313,82 +9073,46 @@ function ceilingExceeded(counts) {
10313
9073
  var SCENARIO_CORPUS_VERSION = 10;
10314
9074
  var entry = (value) => value;
10315
9075
  var SCENARIO_CORPUS = Object.freeze([
10316
- // --- deterministic: the ten canon aliases (392-02) -------------------------------------------
9076
+ // Deterministic: the canon aliases.
10317
9077
  entry({ id: "run", class: "deterministic", lane: "hermetic", kind: "should_execute" }),
10318
9078
  entry({ id: "status", class: "deterministic", lane: "hermetic", kind: "should_execute" }),
10319
9079
  entry({ id: "test-ref", class: "deterministic", lane: "hermetic", kind: "should_execute" }),
10320
9080
  entry({ id: "digest", class: "deterministic", lane: "hermetic", kind: "should_execute" }),
10321
- // 432-13: the inbox alias is retired; the cell is its measured absence (v10).
9081
+ // `proposals` is an ordinary clause stage 1 does not resolve, so the hermetic turn refuses it.
10322
9082
  entry({ id: "proposals", class: "deterministic", lane: "hermetic", kind: "fail_closed" }),
10323
9083
  entry({ id: "fn", class: "deterministic", lane: "hermetic", kind: "should_execute" }),
10324
9084
  // The directives. `executed: 0` on a `should_execute` arm is the SHAPE, not a compromise: a
10325
9085
  // directive names no capability, so it crosses the function layer zero times on a turn that did
10326
- // exactly what was asked (392-08's flip). 432-13 retired `/pin`; its cell is its absence (v10).
9086
+ // exactly what was asked. `/pin` is not a directive but an ordinary clause stage 1 does not
9087
+ // resolve, so the hermetic turn refuses it.
10327
9088
  entry({ id: "model", class: "deterministic", lane: "hermetic", kind: "should_execute" }),
10328
9089
  entry({ id: "pin", class: "deterministic", lane: "hermetic", kind: "fail_closed" }),
10329
9090
  entry({ id: "help", class: "deterministic", lane: "hermetic", kind: "should_execute" }),
10330
9091
  entry({ id: "quit", class: "deterministic", lane: "hermetic", kind: "should_execute" }),
10331
- // --- deterministic: the three stage-1 edge scenarios (392-02) ---------------------------------
9092
+ // Deterministic: the stage-1 edge scenarios.
10332
9093
  entry({ id: "quoted-conjunction", class: "deterministic", lane: "hermetic", kind: "should_execute" }),
10333
9094
  entry({ id: "near-miss-alias", class: "deterministic", lane: "hermetic", kind: "fail_closed" }),
10334
- // DID NOT FLIP, and the reason is a measurement rather than an oversight (394-11). This line was
10335
- // written to flip when the authoring loop registered `write_test`. 394 registered the loop as
10336
- // `authoring.draft.write` — `write_test` is the spec's noun for the capability and is not a
10337
- // function id anywhere in the registry — so the utterance still carries an id nothing registers,
10338
- // and the cell still proves the one thing it was admitted for: `/fn` validates no op id, and
10339
- // `invokeFunction` names an unknown one before any dependency is touched. A phase that registers
10340
- // ops can make an unknown-op test pass for the wrong reason; this one did not.
9095
+ // `write_test` is not a registered id, so `/fn` reaches `invokeFunction` with an unknown op.
10341
9096
  entry({ id: "fn-unknown-op", class: "deterministic", lane: "hermetic", kind: "fail_closed" }),
10342
- // --- planner: natural language, live lane (392-09) --------------------------------------------
9097
+ // Planner: natural language, live lane.
10343
9098
  entry({ id: "list-recent-runs", class: "planner", lane: "live", kind: "should_execute" }),
10344
9099
  entry({ id: "explain-flaky", class: "planner", lane: "live", kind: "should_execute" }),
10345
- // D-08's LIVE arm, FLIPPED (394-11). It was admitted as the cell proving the planner could not
10346
- // invent an authoring capability out of prose; the loop shipping makes the same prose routable,
10347
- // and the flip is what says so. Its file pins planned == executed, so a turn that planned the
10348
- // capability without reaching it reds here rather than reading as arrival.
9100
+ // Pins planned equals executed, so a turn that planned authoring without reaching it fails.
10349
9101
  entry({ id: "write-test-for-checkout", class: "planner", lane: "live", kind: "should_execute" }),
10350
- // --- planner: 395's config arrival, ADMITTED (395-09) ------------------------------------------
10351
- // The first member this corpus has ever gained for a capability rather than for a defect. 392
10352
- // D-08 designed this as a flip and there was nothing to flip: no config cell was ever admitted,
10353
- // which 395's research established by enumerating every file above. So the handoff is executed as
10354
- // an ADMISSION — the size moves as well as the denominator, and the pair of pins in
10355
- // `scenario-manifest.test.ts` is what makes the two movements distinguishable after the fact.
10356
- // The cell pins planned == executed == 1 on `config.field.set`, the propose op D-03 gave any
10357
- // schema-addressable path; it deliberately stops short of `config.proposal.apply`, which is the
10358
- // verb that changes a person's git history and the only one in the family that is gated.
9102
+ // Pins one planned and executed `config.field.set`, the propose op for any schema path.
10359
9103
  entry({ id: "config-edit-timeout", class: "planner", lane: "live", kind: "should_execute" }),
10360
- // --- planner: 398's explain arrival, ADMITTED (398-07) -----------------------------------------
10361
- // The corpus's second admission for a capability rather than for a defect, and the first pair.
10362
- // `agent.explain` answers a question about what this product can do BY CITING the committed
10363
- // capability catalog, so these two cells are what make "never from model memory" checkable at the
10364
- // routing level: a turn that answered either utterance in prose completes without executing
10365
- // anything, and `planned == executed == 1` on the op id is what tells the two apart.
9104
+ // A prose answer executes nothing, so one executed `agent.explain` proves the catalog was cited.
10366
9105
  entry({ id: "what-can-you-do", class: "planner", lane: "live", kind: "should_execute" }),
10367
9106
  entry({ id: "how-do-i-explain", class: "planner", lane: "live", kind: "should_execute" }),
10368
- // --- compound: the canon pair, live lane (392-02) ---------------------------------------------
9107
+ // Compound: the canon pair, live lane.
10369
9108
  entry({ id: "run-42-and-digest", class: "compound", lane: "live", kind: "should_execute" }),
10370
- // The spec's own example, FLIPPED (394-11). Both clauses are routable now, so D-10's pre-flight
10371
- // atomicity has a plan it can admit instead of a whole turn it must refuse.
9109
+ // Both clauses route, so pre-flight atomicity has a plan it can admit.
10372
9110
  entry({ id: "write-test-2fa-and-run", class: "compound", lane: "live", kind: "should_execute" }),
10373
- // --- compound: the two gap-round regression cells (392-11) -------------------------------------
10374
- // The verifier's own probes, admitted so neither defect can return silently. Both are
10375
- // `fail_closed`, so neither enters the ceiling denominator: what they add is the ability to SEE a
10376
- // refused turn that carried something out, which `executed == 0` alone never could.
9111
+ // Fail-closed cells that see a refused turn which carried something out.
10377
9112
  entry({ id: "run-42-and-delete-root", class: "compound", lane: "live", kind: "fail_closed" }),
10378
- // STAYS REFUSING THROUGH 394's FLIP (394-11). Its second clause is a write-test request, so a
10379
- // reader who found the other two write-test cells flipped could reasonably take this one for an
10380
- // oversight. It is not. What this cell measures is CR-04 — a refused turn that had ALREADY
10381
- // recorded a pin and an `approval_decision` row — and its refusal comes from the hermetic lane's
10382
- // tripwire model, not from the authoring capability being absent. Flipping it would remove the
10383
- // corpus's only cell naming a directive on a turn that fails, to prove something two live cells
10384
- // already prove.
9113
+ // Its refusal comes from the hermetic lane's tripwire model, not from authoring being absent.
10385
9114
  entry({ id: "pin-and-write-test", class: "compound", lane: "hermetic", kind: "fail_closed" }),
10386
- // --- compound: the second gap round's two cells (392-14) ---------------------------------------
10387
- // What the round-2 verification found the corpus still could not see: a directive BATCH whose
10388
- // later entry refuses (the earlier one's pin and its `approval_decision` row survived), and the
10389
- // empty-prose `answer_only` that erased a clause on a `completed` turn. Both `fail_closed`, both
10390
- // HERMETIC — the lanes are named exceptions in `scenario-manifest.test.ts`, and the denominators
10391
- // are unmoved at 11 hermetic / 3 live.
9115
+ // A refused later directive in a batch, and an empty-prose `answer_only`.
10392
9116
  entry({
10393
9117
  id: "run-42-and-delete-root-empty-answer",
10394
9118
  class: "compound",
@@ -10396,10 +9120,7 @@ var SCENARIO_CORPUS = Object.freeze([
10396
9120
  kind: "fail_closed"
10397
9121
  }),
10398
9122
  entry({ id: "pin-and-pin-nonsense", class: "compound", lane: "hermetic", kind: "fail_closed" }),
10399
- // --- compound: the third gap round's cell (392-15) ---------------------------------------------
10400
- // The mixed-mapping configuration NO cell in either lane had: a table-mapped clause beside a
10401
- // planner-bound one on a turn that completed, with the model making an extra call about the
10402
- // mapped clause (CR-01). `fail_closed` and hermetic, so the denominators are unmoved again.
9123
+ // A table-mapped clause beside a planner-bound one, plus a model call about the mapped one.
10403
9124
  entry({
10404
9125
  id: "run-42-and-summarise-extra-call",
10405
9126
  class: "compound",
@@ -10420,10 +9141,7 @@ var ROLE_BLOCK = {
10420
9141
  var PLANNING_BLOCK = {
10421
9142
  title: "How to answer",
10422
9143
  body: (
10423
- // "OF THE LIST BELOW", not "of the request". The `Request` block carries the WHOLE utterance
10424
- // while `Clauses to answer` carries only what stage 1 could not bind, so a count scoped to the
10425
- // request asks for calls about clauses that are already resolved — and every one of those is a
10426
- // call reconciliation cannot attribute to anything (392-VERIFICATION round 3).
9144
+ // Scoped to the list, because the request also holds clauses stage 1 already bound.
10427
9145
  "Call one action tool per clause of the list below, in utterance order. Order is meaning: later clauses routinely depend on earlier ones, so the sequence you emit is the sequence that will be considered.\n\nUse `answer_only` ONLY when no tool you were offered can produce what was asked for. You have no knowledge of this project beyond what the tools return, so a question a tool can answer must be routed to that tool: answering it yourself would put a plausible-looking invention where the project data belongs. A turn answered this way performs zero actions and returns your text. Name the clause you are answering in `clause`, copied verbatim from the list above: the call covers that clause and no other, so a request with two clauses needs one call per clause you answer.\n\nUse `mark_unhandled` when no available tool can carry out a clause, naming that clause and saying which capability would be needed. This refuses the whole turn without performing any of it, and that is the correct outcome. Never substitute a tool that looks close: a request nobody can satisfy and a request satisfied by the wrong action are not the same event, and the person can only recover from the first one.\n\nA tool's required inputs are exactly the `required` list in its schema. Never mark a clause unhandled because a field outside that list is missing: omit the field, or fill it from the person's own words when they name it.\n\nBind arguments only from what the person actually supplied. If a clause needs an identifier and none was given, that clause is unhandled \u2014 do not invent one."
10428
9146
  )
10429
9147
  };
@@ -10500,18 +9218,11 @@ function createPlannerStage(deps) {
10500
9218
  import { jsonSchema, tool } from "ai";
10501
9219
  import { z as z3 } from "zod";
10502
9220
  var ANSWER_ONLY_INPUT_SCHEMA = z3.object({
10503
- // NON-EMPTY for the same reason `text` is, one field over (392-15, WR-01). `reconcile.ts` has
10504
- // rejected `clause: ""` since the field was required — a claim that names nothing covers nothing
10505
- // — while the contract the model was SHOWN still admitted it. The two halves of one field cannot
10506
- // be allowed to disagree in the direction that lets the model emit a call the reader will refuse.
9221
+ // Non-empty, matching the reader in `reconcile.ts`, so the model is not shown a call it refuses.
10507
9222
  clause: z3.string().min(1).describe(
10508
9223
  "The part of the request, quoted verbatim from the clauses you were given, that this answer is about."
10509
9224
  ),
10510
- // NON-EMPTY, and that is the model-facing half of the coverage predicate (392-14). `z.string()`
10511
- // admitted `""`, so the one call that erases a clause — naming it, answering nothing — was
10512
- // inside the contract the model was shown. The reader in `reconcile.ts` moved in the SAME commit
10513
- // and is deliberately one notch stricter (it also rejects whitespace-only prose): the two halves
10514
- // of a claim's meaning cannot be allowed to disagree in the direction that completes more.
9225
+ // Non-empty; the reader in `reconcile.ts` is one notch stricter and rejects whitespace-only text.
10515
9226
  text: z3.string().min(1).describe("The answer, in prose, for a turn that asks for information.")
10516
9227
  });
10517
9228
  var MARK_UNHANDLED_INPUT_SCHEMA = z3.object({
@@ -10603,18 +9314,6 @@ function projectPlannerTools(source, caller) {
10603
9314
  return tools;
10604
9315
  }
10605
9316
 
10606
- // src/ports/gates.ts
10607
- var Gate1ReceiptNotPersistedError = class extends Error {
10608
- constructor(candidateId, recorded) {
10609
- super(
10610
- `Gate 1 for candidate ${candidateId} produced a receipt the writer did not acknowledge (${recorded}). The replay work was spent and nothing was persisted, so this call establishes no screening result for that candidate.`
10611
- );
10612
- this.candidateId = candidateId;
10613
- this.recorded = recorded;
10614
- }
10615
- name = "Gate1ReceiptNotPersistedError";
10616
- };
10617
-
10618
9317
  // src/ports/job-store.ts
10619
9318
  var StaleJobStoreEpochError = class extends Error {
10620
9319
  constructor(heldEpoch, observedEpoch) {
@@ -10742,10 +9441,7 @@ function createAgentJobQueue(options) {
10742
9441
  const ids = chainJobIdsForRun(runId, idSource);
10743
9442
  return {
10744
9443
  dedupeKey: runId,
10745
- // All THREE derived ids since 422 D-13. The store's rule is stated over the candidates that
10746
- // EXIST, so a chain halted at the fold — whose proposal row was never written — still
10747
- // terminates, while a chain that enqueued the proposal job cannot notify before it lands.
10748
- candidateJobIds: [ids.analyzeRun, ids.curateMemory, ids.proposeImprovements],
9444
+ candidateJobIds: [ids.analyzeRun, ids.curateMemory],
10749
9445
  payloadJson: canonicalJson4(
10750
9446
  buildChainTerminalNotification({
10751
9447
  runId,
@@ -11027,7 +9723,6 @@ export {
11027
9723
  EvidenceBlobAbsentError,
11028
9724
  FunctionInputError,
11029
9725
  FunctionNotBoundError,
11030
- Gate1ReceiptNotPersistedError,
11031
9726
  IncompleteJobKindDeclarationError,
11032
9727
  InconsistentJobKindDeclarationError,
11033
9728
  JobCancellationNotRequestedError,
@@ -11041,8 +9736,6 @@ export {
11041
9736
  MARK_UNHANDLED_CEILING,
11042
9737
  MissingIdempotencyKeyError,
11043
9738
  OPTIONAL_FUNCTION_DEP_KEYS,
11044
- PROPOSE_DEFAULT_MAX_GATE1_PER_JOB,
11045
- PROPOSE_GATE1_ALLOWANCE_CHECKPOINT,
11046
9739
  QUARANTINE_ADD_FUNCTION_ID,
11047
9740
  QUARANTINE_LIST_FUNCTION_ID,
11048
9741
  QUARANTINE_RELEASE_FUNCTION_ID,
@@ -11102,7 +9795,6 @@ export {
11102
9795
  liveUrlOf,
11103
9796
  maybeCompact,
11104
9797
  mintLiveUrlToken,
11105
- parseProposeGate1Allowance,
11106
9798
  parseScenario,
11107
9799
  projectAuthoringFunnel,
11108
9800
  projectPlannerTools,