@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/README.md +78 -0
- package/dist/index.cjs +218 -1525
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +20 -2961
- package/dist/index.d.ts +20 -2961
- package/dist/index.js +219 -1527
- package/dist/index.js.map +1 -1
- package/package.json +6 -6
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
|
-
//
|
|
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
|
-
//
|
|
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
|
|
521
|
-
|
|
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
|
-
|
|
549
|
-
|
|
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
|
-
|
|
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
|
|
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:
|
|
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
|
|
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
|
|
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
|
|
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
|
|
960
|
-
const
|
|
961
|
-
const
|
|
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
|
|
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
|
|
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 =
|
|
1227
|
+
verdicts2 = result.verdicts;
|
|
1526
1228
|
for (const parts of result.authoredParts) authoredParts.set(parts.perTest, parts);
|
|
1527
1229
|
}
|
|
1528
|
-
const
|
|
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
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
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:
|
|
1543
|
-
parts:
|
|
1244
|
+
verdicts: stamped,
|
|
1245
|
+
parts: sentenceParts
|
|
1544
1246
|
});
|
|
1545
|
-
return { runId: payload.runId, 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
|
-
//
|
|
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
|
-
//
|
|
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
|
|
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
|
-
//
|
|
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:
|
|
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
|
-
|
|
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
|
|
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
|
-
//
|
|
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
|
|
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
|
-
//
|
|
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
|
-
...
|
|
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
|
-
//
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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}
|
|
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
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
3714
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
3875
|
-
//
|
|
3876
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
|
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("; ")}
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
//
|
|
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
|
|
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
|
|
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
|
-
//
|
|
4723
|
-
//
|
|
4724
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
*
|
|
5887
|
-
*
|
|
5888
|
-
*
|
|
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
|
-
//
|
|
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
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
//
|
|
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
|
|
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
|
-
//
|
|
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
|
|
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
|
|
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
|
-
//
|
|
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
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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:
|
|
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
|
-
//
|
|
8308
|
-
//
|
|
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,
|
|
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}
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
7794
|
+
// Parsed evidence uses the detail and blob readers together.
|
|
8818
7795
|
runsEvidenceSpec,
|
|
8819
|
-
//
|
|
7796
|
+
// The mark write binds only where the host supplies feedback.
|
|
8820
7797
|
runsMarkSpec,
|
|
8821
|
-
//
|
|
7798
|
+
// Binds through `feedbackReads`, never through the writer above.
|
|
8822
7799
|
runsMarksHistorySpec,
|
|
8823
|
-
//
|
|
7800
|
+
// Reads `runs.mark`'s shared plan through `feedbackReads`, never the writer.
|
|
8824
7801
|
runsMarkPreviewSpec,
|
|
8825
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
9107
|
+
// Compound: the canon pair, live lane.
|
|
10369
9108
|
entry({ id: "run-42-and-digest", class: "compound", lane: "live", kind: "should_execute" }),
|
|
10370
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
|
|
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,
|