onto-mcp 0.4.12 → 0.4.13
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/.onto/authority/core-lexicon.yaml +1 -1
- package/.onto/authority/supported-models.yaml +137 -0
- package/.onto/domains/accounting-kr/competency_qs.md +153 -153
- package/.onto/processes/reconstruct/obligation-coverage-ledger.yaml +842 -0
- package/.onto/processes/reconstruct/obligation-coverage-recorded.yaml +225 -0
- package/.onto/processes/reconstruct/ontology-seeding-and-maturation-design.md +93 -54
- package/.onto/processes/reconstruct/reconstruct-contract-registry.yaml +169 -8
- package/.onto/processes/reconstruct/reconstruct-execution-ux-contract.md +72 -1
- package/.onto/processes/reconstruct/source-profiles/spreadsheet.md +35 -0
- package/.onto/processes/review/productized-live-path.md +9 -5
- package/.onto/processes/review/prompt-execution-runner-contract.md +32 -8
- package/.onto/processes/review/review-target-profile-contract.md +47 -4
- package/.onto/processes/shared/pipeline-execution-ledger-contract.md +11 -0
- package/.onto/processes/shared/target-material-kind-contract.md +1 -1
- package/README.md +64 -1
- package/dist/cli.js +11 -1
- package/dist/core-api/reconstruct-api.js +575 -24
- package/dist/core-api/review-api.js +70 -5
- package/dist/core-api/review-progress.js +7 -0
- package/dist/core-api/runtime-observation.js +7 -0
- package/dist/core-runtime/artifact-io.js +75 -3
- package/dist/core-runtime/cli/claude-code-review-unit-executor.js +19 -8
- package/dist/core-runtime/cli/materialize-review-prompt-packets.js +68 -14
- package/dist/core-runtime/cli/prepare-review-session.js +34 -1
- package/dist/core-runtime/cli/render-review-final-output.js +2 -1
- package/dist/core-runtime/cli/run-review-prompt-execution.js +758 -48
- package/dist/core-runtime/cli/run-seats.js +18 -0
- package/dist/core-runtime/cli/runtime-submit-context.js +10 -2
- package/dist/core-runtime/cli/unit-resubmit.js +269 -0
- package/dist/core-runtime/cli/worker-structured-output.js +80 -2
- package/dist/core-runtime/discovery/host-detection.js +71 -22
- package/dist/core-runtime/discovery/review-cert-assemble.js +208 -0
- package/dist/core-runtime/discovery/review-cert-record.js +442 -0
- package/dist/core-runtime/discovery/review-execution-units.js +22 -0
- package/dist/core-runtime/discovery/seat-inventory.js +167 -0
- package/dist/core-runtime/discovery/settings-chain.js +311 -52
- package/dist/core-runtime/discovery/supported-models.js +282 -29
- package/dist/core-runtime/discovery/synthesize-cert-assemble.js +197 -0
- package/dist/core-runtime/discovery/synthesize-cert-capsule.js +399 -0
- package/dist/core-runtime/discovery/synthesize-cert-judge.js +340 -0
- package/dist/core-runtime/discovery/synthesize-cert-loop.js +234 -0
- package/dist/core-runtime/discovery/synthesize-cert-mutation.js +117 -0
- package/dist/core-runtime/discovery/synthesize-cert-packet.js +182 -0
- package/dist/core-runtime/discovery/synthesize-cert-record.js +831 -0
- package/dist/core-runtime/discovery/synthesize-cert-sampler.js +336 -0
- package/dist/core-runtime/effort-calibration-ingest.js +135 -0
- package/dist/core-runtime/llm/dispatch-breaker.js +412 -0
- package/dist/core-runtime/llm/dispatch-fallback-adapter-capabilities.js +33 -0
- package/dist/core-runtime/llm/llm-caller.js +188 -47
- package/dist/core-runtime/llm/mock-llm-realization.js +35 -4
- package/dist/core-runtime/llm/model-switcher.js +1 -1
- package/dist/core-runtime/llm/openai-responses-incomplete-error.js +46 -0
- package/dist/core-runtime/llm/sealed-dispatch-capability.js +359 -0
- package/dist/core-runtime/llm/structured-dispatch-error.js +15 -0
- package/dist/core-runtime/onboard/claude-profile-scan.js +65 -0
- package/dist/core-runtime/onboard/cli-host.js +5 -2
- package/dist/core-runtime/onboard/configure-provider.js +33 -1
- package/dist/core-runtime/onboard/host-target.js +28 -3
- package/dist/core-runtime/onboard/register.js +54 -12
- package/dist/core-runtime/pipeline-execution-ledger.js +10 -0
- package/dist/core-runtime/reconstruct/artifact-types.js +35 -0
- package/dist/core-runtime/reconstruct/claim-projection-validation.js +17 -0
- package/dist/core-runtime/reconstruct/competency-projection-contract.js +63 -0
- package/dist/core-runtime/reconstruct/comprehension-artifact.js +321 -0
- package/dist/core-runtime/reconstruct/comprehension-reduce.js +367 -0
- package/dist/core-runtime/reconstruct/comprehension-semantic-map.js +771 -0
- package/dist/core-runtime/reconstruct/contract-registry.js +20 -0
- package/dist/core-runtime/reconstruct/dispatch-fallback-artifacts.js +640 -0
- package/dist/core-runtime/reconstruct/execution-telemetry.js +83 -9
- package/dist/core-runtime/reconstruct/final-output-sections.js +89 -0
- package/dist/core-runtime/reconstruct/governing-snapshot.js +36 -1
- package/dist/core-runtime/reconstruct/leaf-reader.js +305 -0
- package/dist/core-runtime/reconstruct/llm-dispatch-failure.js +270 -0
- package/dist/core-runtime/reconstruct/llm-touch-fingerprint.js +94 -0
- package/dist/core-runtime/reconstruct/material-admission-validation.js +18 -0
- package/dist/core-runtime/reconstruct/material-profile-validation.js +14 -0
- package/dist/core-runtime/reconstruct/materialize-preparation.js +304 -18
- package/dist/core-runtime/reconstruct/maturation-validation.js +1231 -53
- package/dist/core-runtime/reconstruct/mock-llm-realization.js +107 -3
- package/dist/core-runtime/reconstruct/obligation-assertion.js +18 -0
- package/dist/core-runtime/reconstruct/ontology-seed-validation.js +27 -0
- package/dist/core-runtime/reconstruct/output-budget.js +6 -0
- package/dist/core-runtime/reconstruct/pipeline-execution-ledger.js +87 -3
- package/dist/core-runtime/reconstruct/post-seed-validation.js +118 -5
- package/dist/core-runtime/reconstruct/purpose-authority-validation.js +117 -2
- package/dist/core-runtime/reconstruct/record.js +42 -0
- package/dist/core-runtime/reconstruct/registry-verification-validation.js +16 -0
- package/dist/core-runtime/reconstruct/run-control-validation.js +1011 -34
- package/dist/core-runtime/reconstruct/run.js +5198 -536
- package/dist/core-runtime/reconstruct/seed-authoring-readiness-validation.js +54 -3
- package/dist/core-runtime/reconstruct/semantic-quality-gate.js +105 -2
- package/dist/core-runtime/reconstruct/source-observation-delta-validation.js +41 -0
- package/dist/core-runtime/reconstruct/source-observations.js +166 -0
- package/dist/core-runtime/reconstruct/source-safety-validation.js +71 -183
- package/dist/core-runtime/reconstruct/source-scout-pack-validation.js +37 -1
- package/dist/core-runtime/reconstruct/terminal-validation.js +132 -2
- package/dist/core-runtime/review/continuation-plan.js +12 -5
- package/dist/core-runtime/review/issue-artifact-runtime.js +4 -1
- package/dist/core-runtime/review/materializers.js +100 -11
- package/dist/core-runtime/review/nesting-batch.js +6 -2
- package/dist/core-runtime/review/obligation-shardability.js +138 -0
- package/dist/core-runtime/review/pipeline-execution-ledger.js +42 -2
- package/dist/core-runtime/review/review-artifact-utils.js +217 -5
- package/dist/core-runtime/review/review-execution-steps.js +32 -8
- package/dist/core-runtime/review/review-progress-contract.js +8 -0
- package/dist/core-runtime/review/review-prompt-budget.js +97 -0
- package/dist/core-runtime/review/semantic-quality-gate.js +33 -7
- package/dist/core-runtime/review/spreadsheet-review-disposition.js +94 -0
- package/dist/core-runtime/route-identity.js +182 -0
- package/dist/core-runtime/spreadsheet-header-escalation.js +204 -0
- package/dist/core-runtime/spreadsheet-structure-observer.js +2689 -0
- package/dist/core-runtime/target-material-kind.js +58 -0
- package/dist/mcp/server.js +12 -5
- package/dist/tui/app.js +167 -0
- package/dist/tui/data/event-follower.js +116 -0
- package/dist/tui/data/node-detail.js +42 -0
- package/dist/tui/data/projection-poll.js +26 -0
- package/dist/tui/data/session-discovery.js +65 -0
- package/dist/tui/index.js +108 -0
- package/dist/tui/screens/log.js +25 -0
- package/dist/tui/screens/node-detail.js +17 -0
- package/dist/tui/screens/session-selector.js +26 -0
- package/dist/tui/screens/workflow-tree.js +90 -0
- package/dist/tui/view-model/reconstruct-adapter.js +162 -0
- package/dist/tui/view-model/review-adapter.js +270 -0
- package/dist/tui/view-model/tree-view-model.js +19 -0
- package/package.json +18 -3
- package/settings.example.json +77 -16
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The dedicated live judge system prompt (design §7/§9 note, live-wiring cut).
|
|
3
|
+
* A fixed, arm-blind lens: it sees the ORIGINAL frozen packet and one arm's raw
|
|
4
|
+
* output, never which arm produced it. Boundary judgement is deliberately
|
|
5
|
+
* scoped to the SEMANTIC characterization (character_before/after) the
|
|
6
|
+
* deterministic `reconcileBoundaries` step cannot judge — exact row-matching
|
|
7
|
+
* between output boundaries and packet seams is that separate deterministic
|
|
8
|
+
* step's job, not this judge's (§9 note: boundary↔reconcile).
|
|
9
|
+
*/
|
|
10
|
+
export const SYNTHESIZE_CERT_JUDGE_SYSTEM_PROMPT = "You are an INDEPENDENT judge for a spreadsheet-column semantic-synthesis benchmark. You are given the ORIGINAL frozen input packet (node_ref, format_clusters, value_shape_seams, child_summaries) and ONE candidate model's raw output (semantic_summary, boundaries) produced from that packet (or a corrupted variant of it — you never see which). Judge TWO metrics, each pass or fail, using ONLY the given original_packet as ground truth — never trust the output's own claims about itself.\n\n" +
|
|
11
|
+
"GROUNDING: does semantic_summary stay faithful to original_packet's format_clusters, value_shape_seams, and child_summaries — no hallucinated facts, no invented business meaning, no invented shape or child content the packet does not support? Any claim not traceable to the packet fails grounding.\n" +
|
|
12
|
+
"BOUNDARY: judge only the SEMANTIC CHARACTERIZATION of the output's boundaries — is character_before/character_after for each proposed boundary a plausible, coherent reading of original_packet's value_shape_seams (comparing against prev_shape/new_shape)? Do NOT judge exact row alignment or perform deterministic row-matching between output boundaries and seams — a separate deterministic reconciliation step owns that, outside your scope. If original_packet has NO value_shape_seams, boundary passes UNLESS the output proposes a boundary anyway (a spurious boundary with no supporting seam fails boundary).\n\n" +
|
|
13
|
+
"Reply with STRICT JSON only, no prose outside it, no markdown code fences: {\"grounding\": \"pass\"|\"fail\", \"boundary\": \"pass\"|\"fail\"}. No additional fields.";
|
|
14
|
+
const VALID_VERDICT = new Set(["pass", "fail"]);
|
|
15
|
+
/** Fail-closed total-enum guard for a judge realization's returned verdicts —
|
|
16
|
+
* a live LLM judge that emits anything outside {pass, fail} for either metric
|
|
17
|
+
* is a judge-plane failure, never a silently-coerced verdict. */
|
|
18
|
+
export function assertSynthesizeCertJudgeVerdicts(verdicts) {
|
|
19
|
+
const record = verdicts;
|
|
20
|
+
const keys = Object.keys(record);
|
|
21
|
+
if (keys.length !== 2 || !("grounding" in record) || !("boundary" in record)) {
|
|
22
|
+
throw new Error(`synthesize-cert-judge: verdicts must be exactly {grounding, boundary}, got keys [${keys.join(", ")}] (fail-closed)`);
|
|
23
|
+
}
|
|
24
|
+
for (const metric of ["grounding", "boundary"]) {
|
|
25
|
+
const value = record[metric];
|
|
26
|
+
if (typeof value !== "string" || !VALID_VERDICT.has(value)) {
|
|
27
|
+
throw new Error(`synthesize-cert-judge: ${metric} verdict must be 'pass' | 'fail', got ${JSON.stringify(value)} (fail-closed)`);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/** Strips a markdown code fence if the model wrapped its JSON in one, despite
|
|
32
|
+
* the prompt's instruction not to (defensive — mirrors the synthesize
|
|
33
|
+
* author's own fence-tolerance). A no-op on already-bare JSON text. Shared by
|
|
34
|
+
* the judge response parser below AND the judge-replay reconstruction
|
|
35
|
+
* ({@link reconstructSynthesizeCertJudgeReplayInputs}), since a captured raw
|
|
36
|
+
* arm-output call needs the identical fence-tolerant un-wrap before
|
|
37
|
+
* `JSON.parse` — hence the provider-neutral name. */
|
|
38
|
+
function stripLlmResponseFence(text) {
|
|
39
|
+
const trimmed = text.trim();
|
|
40
|
+
const fenced = /^```(?:json)?\s*([\s\S]*?)\s*```$/i.exec(trimmed);
|
|
41
|
+
return fenced?.[1]?.trim() ?? trimmed;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Pure parser: a live judge realization's raw response text → a TOTAL verdict
|
|
45
|
+
* pair, or a thrown Error (never a partial/coerced result) — no LLM touched,
|
|
46
|
+
* so this is unit-testable against fixture response strings. Reuses
|
|
47
|
+
* {@link assertSynthesizeCertJudgeVerdicts} as the single enum-shape authority
|
|
48
|
+
* (fail-closed: an out-of-enum or partial response is never silently
|
|
49
|
+
* coerced). The live dispatch wrapper (scripts side) calls this after
|
|
50
|
+
* receiving raw response text; any throw here is an untyped rejection that
|
|
51
|
+
* the coordinate loop classifies as `judge_error`.
|
|
52
|
+
*/
|
|
53
|
+
export function parseSynthesizeCertJudgeResponseText(text) {
|
|
54
|
+
const jsonText = stripLlmResponseFence(text);
|
|
55
|
+
let parsed;
|
|
56
|
+
try {
|
|
57
|
+
parsed = JSON.parse(jsonText);
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
throw new Error(`synthesize-cert-judge: response is not valid JSON (fail-closed): ${error instanceof Error ? error.message : String(error)}`);
|
|
61
|
+
}
|
|
62
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
63
|
+
throw new Error(`synthesize-cert-judge: response must be a JSON object, got ${JSON.stringify(parsed)} (fail-closed)`);
|
|
64
|
+
}
|
|
65
|
+
assertSynthesizeCertJudgeVerdicts(parsed);
|
|
66
|
+
return parsed;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Reconstructs judge-replay inputs for every row whose synthesize output is
|
|
70
|
+
* `ok`, by CONTENT-HASH identity rather than sequence position: a row's
|
|
71
|
+
* `output_sha256` is exactly what the original coordinate loop computed over
|
|
72
|
+
* the arm's projected output at capture time
|
|
73
|
+
* (`synthesizeCertOutputSha256`, synthesize-cert-loop.ts), so the ONE
|
|
74
|
+
* captured call (of the row's own arm role) whose raw text fence-strips +
|
|
75
|
+
* `JSON.parse`s + projects to that SAME hash IS the row's output — a content
|
|
76
|
+
* join, not a positional guess (a resumed run's capture can carry more calls
|
|
77
|
+
* per role than coordinates, and in no particular order).
|
|
78
|
+
*
|
|
79
|
+
* Judge input always pairs the arm's output with the row's ORIGINAL
|
|
80
|
+
* (never-mutated) frozen packet — `originalPacketsByInputId` must be keyed
|
|
81
|
+
* from the ORIGINAL packets only (e.g. the freeze checkpoint), never a
|
|
82
|
+
* negative-arm mutation, matching how the live coordinate loop itself always
|
|
83
|
+
* judges the negative arm against the original (§7 module doc above).
|
|
84
|
+
*
|
|
85
|
+
* Pure and realization-agnostic: `projectArmOutput`/`hashArmOutput` are
|
|
86
|
+
* caller-injected (mirrors this module's judge-fn injection style) so this
|
|
87
|
+
* stays a plain content join with no hard dependency on any one LLM-output
|
|
88
|
+
* projection — production callers pass the SAME single-source functions the
|
|
89
|
+
* original loop used (`projectSemanticMapSynthesisOutput`,
|
|
90
|
+
* `synthesizeCertOutputSha256`), so a mismatch can only mean a genuinely
|
|
91
|
+
* missing capture, never a divergent re-implementation of the hash.
|
|
92
|
+
*
|
|
93
|
+
* On a sha collision within one arm role (>1 captured call projects to the
|
|
94
|
+
* identical output — e.g. a resumed run's re-dispatch producing
|
|
95
|
+
* byte-identical text), the LOWEST-seq call is used deterministically; since
|
|
96
|
+
* colliding calls are content-IDENTICAL by construction, this only picks a
|
|
97
|
+
* canonical source and never changes the reconstructed output.
|
|
98
|
+
*/
|
|
99
|
+
export function reconstructSynthesizeCertJudgeReplayInputs(args) {
|
|
100
|
+
// role -> output_sha256 -> ascending-seq candidates.
|
|
101
|
+
const byRole = new Map();
|
|
102
|
+
for (const call of args.capturedCalls) {
|
|
103
|
+
if (typeof call.text !== "string")
|
|
104
|
+
continue;
|
|
105
|
+
let output;
|
|
106
|
+
try {
|
|
107
|
+
const parsed = JSON.parse(stripLlmResponseFence(call.text));
|
|
108
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed))
|
|
109
|
+
continue;
|
|
110
|
+
output = args.projectArmOutput(parsed);
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
continue; // unusable capture — never a join error, simply no candidate
|
|
114
|
+
}
|
|
115
|
+
const sha = args.hashArmOutput(output);
|
|
116
|
+
let shaIndex = byRole.get(call.role);
|
|
117
|
+
if (!shaIndex) {
|
|
118
|
+
shaIndex = new Map();
|
|
119
|
+
byRole.set(call.role, shaIndex);
|
|
120
|
+
}
|
|
121
|
+
const bucket = shaIndex.get(sha) ?? [];
|
|
122
|
+
bucket.push({ seq: call.seq, output });
|
|
123
|
+
shaIndex.set(sha, bucket);
|
|
124
|
+
}
|
|
125
|
+
const matched = [];
|
|
126
|
+
const unmatched = [];
|
|
127
|
+
for (const row of args.rows) {
|
|
128
|
+
if (row.candidate_output_status !== "ok")
|
|
129
|
+
continue; // no output to replay
|
|
130
|
+
const originalPacket = args.originalPacketsByInputId.get(row.input_id);
|
|
131
|
+
const bucket = row.output_sha256 ? byRole.get(row.arm)?.get(row.output_sha256) : undefined;
|
|
132
|
+
if (!originalPacket || !bucket || bucket.length === 0) {
|
|
133
|
+
unmatched.push(row);
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
const chosen = [...bucket].sort((a, b) => a.seq - b.seq)[0];
|
|
137
|
+
matched.push({ row, judgeInput: { original_packet: originalPacket, arm_output: chosen.output } });
|
|
138
|
+
}
|
|
139
|
+
return { matched, unmatched };
|
|
140
|
+
}
|
|
141
|
+
/** Parses the trailing `:<start>-<end>` row range off a child_summaries key
|
|
142
|
+
* (the `<sheet>#<col>:<start>-<end>` node-key convention) — end-anchored so
|
|
143
|
+
* a sheet name containing `:` is still handled safely. A key that doesn't
|
|
144
|
+
* match contributes no boundary (never throws): this verifier's job is
|
|
145
|
+
* grounding, not upstream packet-shape validation, which is owned
|
|
146
|
+
* elsewhere. */
|
|
147
|
+
function parseChildKeyRowRange(key) {
|
|
148
|
+
const match = /:(\d+)-(\d+)$/.exec(key);
|
|
149
|
+
if (!match)
|
|
150
|
+
return null;
|
|
151
|
+
return { start: Number(match[1]), end: Number(match[2]) };
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Deterministically verifies a summary's structural claims against the
|
|
155
|
+
* packet's OWN structural facts (value_shape_seams row positions AND
|
|
156
|
+
* child_summaries' own row-range keys — both packet-supplied, equally
|
|
157
|
+
* grounding) — never against format-label TEXT, never against the summary's
|
|
158
|
+
* prose. Pure, no I/O, no LLM. `grounded` is `violations.length === 0`.
|
|
159
|
+
*
|
|
160
|
+
* Two checks, both row-position-only:
|
|
161
|
+
* - `fabricated_boundary_row`: a cited boundary row must be the region
|
|
162
|
+
* start/end, a seam's row, a seam's row − 1 (the last row of the PRIOR
|
|
163
|
+
* shape — summaries commonly cite the ending row, not only the row a new
|
|
164
|
+
* shape begins; both are grounded in the SAME seam), or a child_summaries
|
|
165
|
+
* entry's start/end row (a merge packet's child-partition boundary is a
|
|
166
|
+
* packet fact the arm legitimately saw, exactly as grounding as a seam —
|
|
167
|
+
* R7 Group A case 7).
|
|
168
|
+
* - `fabricated_transition_row`: a cited transition's `at_row` must be
|
|
169
|
+
* SOME seam's row exactly — a "transition" claim is specifically about a
|
|
170
|
+
* format CHANGE point, which only a seam represents (a child-partition
|
|
171
|
+
* row is a subtree split, not necessarily a shape change, so it does NOT
|
|
172
|
+
* ground a transition claim the way it grounds a boundary claim).
|
|
173
|
+
*
|
|
174
|
+
* `cited_format_labels` and each transition's `from`/`to` text are NEVER
|
|
175
|
+
* compared against `packet.format_clusters` or seam shape names — label
|
|
176
|
+
* TEXT matching is out of this verifier's scope (see the module doc above:
|
|
177
|
+
* domain-agnostic-no-static-enums; format naming is an LLM-semantic
|
|
178
|
+
* residual, not a deterministic-code judgment).
|
|
179
|
+
*/
|
|
180
|
+
export function verifyStructuralGrounding(args) {
|
|
181
|
+
const violations = [];
|
|
182
|
+
const validBoundaryRows = new Set([args.regionStart, args.regionEnd]);
|
|
183
|
+
for (const seam of args.packet.value_shape_seams) {
|
|
184
|
+
validBoundaryRows.add(seam.row);
|
|
185
|
+
validBoundaryRows.add(seam.row - 1);
|
|
186
|
+
}
|
|
187
|
+
for (const child of args.packet.child_summaries) {
|
|
188
|
+
const range = parseChildKeyRowRange(child.key);
|
|
189
|
+
if (!range)
|
|
190
|
+
continue;
|
|
191
|
+
validBoundaryRows.add(range.start);
|
|
192
|
+
validBoundaryRows.add(range.end);
|
|
193
|
+
}
|
|
194
|
+
const seamRows = new Set(args.packet.value_shape_seams.map((seam) => seam.row));
|
|
195
|
+
for (const row of args.claims.cited_boundary_rows) {
|
|
196
|
+
if (!validBoundaryRows.has(row)) {
|
|
197
|
+
violations.push({
|
|
198
|
+
code: "fabricated_boundary_row",
|
|
199
|
+
message: `cited boundary row ${row} matches no packet seam (row or row-1), no child_summaries start/end, nor the region start/end (fail-closed)`,
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
for (const transition of args.claims.cited_transitions) {
|
|
204
|
+
if (!seamRows.has(transition.at_row)) {
|
|
205
|
+
violations.push({
|
|
206
|
+
code: "fabricated_transition_row",
|
|
207
|
+
message: `cited transition at row ${transition.at_row} matches no packet seam row (fail-closed; label text is not checked)`,
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
return violations;
|
|
212
|
+
}
|
|
213
|
+
// ── SG2 — extracted-claim schema + extractor honesty guard ──────────────────
|
|
214
|
+
//
|
|
215
|
+
// The extractor (SG3, an independent LLM lens — opus in production) runs at
|
|
216
|
+
// LOW privilege: it may only pull out what the summary text LITERALLY says,
|
|
217
|
+
// never infer or evaluate. `assertClaimsGroundedInText` is the boundary that
|
|
218
|
+
// enforces that privilege split deterministically — an extraction that cites
|
|
219
|
+
// a row/label absent from the summary's own text is an EXTRACTION failure
|
|
220
|
+
// (the extractor invented something), a category error distinct from the
|
|
221
|
+
// packet-grounding violations {@link verifyStructuralGrounding} reports.
|
|
222
|
+
/** The extractor's raw response failed to parse as JSON, or parsed to the
|
|
223
|
+
* wrong shape — an EXTRACTION-plane failure (§ orchestrator classifies
|
|
224
|
+
* `parse_error`), distinct from a grounding violation. */
|
|
225
|
+
export class SynthesizeCertClaimParseFail extends Error {
|
|
226
|
+
}
|
|
227
|
+
/** The extractor cited a row/label absent from the summary text it was
|
|
228
|
+
* extracting from — an EXTRACTION-plane honesty failure (orchestrator
|
|
229
|
+
* classifies `honesty_violation`), distinct from a grounding violation
|
|
230
|
+
* (which compares claims against the PACKET, not the summary text). */
|
|
231
|
+
export class SynthesizeCertClaimHonestyViolation extends Error {
|
|
232
|
+
}
|
|
233
|
+
/** Fail-closed: every number the claims cite as a boundary/transition row,
|
|
234
|
+
* and every format label they cite, must appear verbatim in `summaryText`
|
|
235
|
+
* (rows as a digit substring, labels case-insensitively) — else the
|
|
236
|
+
* extractor fabricated something not present in the text it was extracting
|
|
237
|
+
* from. Throws {@link SynthesizeCertClaimHonestyViolation} (never silently
|
|
238
|
+
* drops) on the first violation found. */
|
|
239
|
+
export function assertClaimsGroundedInText(claims, summaryText) {
|
|
240
|
+
const citedRows = new Set([
|
|
241
|
+
...claims.cited_boundary_rows,
|
|
242
|
+
...claims.cited_transitions.map((t) => t.at_row),
|
|
243
|
+
]);
|
|
244
|
+
for (const row of citedRows) {
|
|
245
|
+
if (!summaryText.includes(String(row))) {
|
|
246
|
+
throw new SynthesizeCertClaimHonestyViolation(`synthesize-cert-judge: extractor cited row ${row} which does not appear in the summary text (fail-closed — extraction, not grounding, failure)`);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
const lowerText = summaryText.toLowerCase();
|
|
250
|
+
const citedLabels = new Set([
|
|
251
|
+
...claims.cited_format_labels,
|
|
252
|
+
...claims.cited_transitions.flatMap((t) => [t.from, t.to]),
|
|
253
|
+
]);
|
|
254
|
+
for (const label of citedLabels) {
|
|
255
|
+
if (!lowerText.includes(label.toLowerCase())) {
|
|
256
|
+
throw new SynthesizeCertClaimHonestyViolation(`synthesize-cert-judge: extractor cited format label '${label}' which does not appear in the summary text (fail-closed — extraction, not grounding, failure)`);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
// ── SG3 — structural-claim extraction prompt + response parser ──────────────
|
|
261
|
+
//
|
|
262
|
+
// The extractor is deliberately NOT a judge: it is told so explicitly, and
|
|
263
|
+
// its prompt asks for verbatim extraction only (no inference, no
|
|
264
|
+
// evaluation). Live dispatch (scripts side) mirrors the judge's dispatch
|
|
265
|
+
// shape exactly — a raw callLlm with this dedicated prompt, parsed by the
|
|
266
|
+
// pure parser below.
|
|
267
|
+
export const SYNTHESIZE_CERT_STRUCTURAL_CLAIM_EXTRACTION_SYSTEM_PROMPT = "You are a STRUCTURAL CLAIM EXTRACTOR, not a judge. You are given ONE semantic-summary text describing a spreadsheet column region. Extract ONLY what the text LITERALLY states — never infer, never evaluate, never add a row number or label the text does not contain, and never judge whether the summary is correct.\n\n" +
|
|
268
|
+
"Extract exactly:\n" +
|
|
269
|
+
"(a) cited_boundary_rows: every row number the text presents as a region boundary or transition point.\n" +
|
|
270
|
+
"(b) cited_format_labels: every format/type label the text names (e.g. INT, DEC, ISO_DATE).\n" +
|
|
271
|
+
"(c) cited_transitions: every {at_row, from, to} transition the text describes, where at_row is the row number and from/to are the two format labels named as changing at that row.\n\n" +
|
|
272
|
+
"Reply with STRICT JSON only, no prose outside it, no markdown code fences: " +
|
|
273
|
+
'{"cited_boundary_rows": [integer, ...], "cited_format_labels": [string, ...], "cited_transitions": [{"at_row": integer, "from": string, "to": string}, ...]}. ' +
|
|
274
|
+
"Empty arrays are honest and acceptable when the text cites nothing of that kind. No additional fields.";
|
|
275
|
+
/** Fail-closed total-shape guard for an extractor realization's returned
|
|
276
|
+
* claims — anything outside {cited_boundary_rows: number[],
|
|
277
|
+
* cited_format_labels: string[], cited_transitions: {at_row:number,
|
|
278
|
+
* from:string, to:string}[]} is an extraction-plane failure, never a
|
|
279
|
+
* silently-coerced result. Throws {@link SynthesizeCertClaimParseFail}. */
|
|
280
|
+
export function assertSynthesizeCertStructuralClaims(claims) {
|
|
281
|
+
const record = claims;
|
|
282
|
+
const keys = Object.keys(record);
|
|
283
|
+
if (keys.length !== 3 ||
|
|
284
|
+
!("cited_boundary_rows" in record) ||
|
|
285
|
+
!("cited_format_labels" in record) ||
|
|
286
|
+
!("cited_transitions" in record)) {
|
|
287
|
+
throw new SynthesizeCertClaimParseFail(`synthesize-cert-judge: claims must be exactly {cited_boundary_rows, cited_format_labels, cited_transitions}, got keys [${keys.join(", ")}] (fail-closed)`);
|
|
288
|
+
}
|
|
289
|
+
const rows = record.cited_boundary_rows;
|
|
290
|
+
if (!Array.isArray(rows) || !rows.every((r) => typeof r === "number" && Number.isFinite(r))) {
|
|
291
|
+
throw new SynthesizeCertClaimParseFail("synthesize-cert-judge: cited_boundary_rows must be an array of numbers (fail-closed)");
|
|
292
|
+
}
|
|
293
|
+
const labels = record.cited_format_labels;
|
|
294
|
+
if (!Array.isArray(labels) || !labels.every((l) => typeof l === "string" && l.length > 0)) {
|
|
295
|
+
throw new SynthesizeCertClaimParseFail("synthesize-cert-judge: cited_format_labels must be an array of non-empty strings (fail-closed)");
|
|
296
|
+
}
|
|
297
|
+
const transitions = record.cited_transitions;
|
|
298
|
+
const validTransition = (t) => {
|
|
299
|
+
if (typeof t !== "object" || t === null || Array.isArray(t))
|
|
300
|
+
return false;
|
|
301
|
+
const obj = t;
|
|
302
|
+
if (Object.keys(obj).length !== 3)
|
|
303
|
+
return false;
|
|
304
|
+
const atRow = obj.at_row;
|
|
305
|
+
const from = obj.from;
|
|
306
|
+
const to = obj.to;
|
|
307
|
+
return (typeof atRow === "number" &&
|
|
308
|
+
Number.isFinite(atRow) &&
|
|
309
|
+
typeof from === "string" &&
|
|
310
|
+
from.length > 0 &&
|
|
311
|
+
typeof to === "string" &&
|
|
312
|
+
to.length > 0);
|
|
313
|
+
};
|
|
314
|
+
if (!Array.isArray(transitions) || !transitions.every(validTransition)) {
|
|
315
|
+
throw new SynthesizeCertClaimParseFail("synthesize-cert-judge: cited_transitions must be an array of {at_row:number, from:string, to:string} (fail-closed)");
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Pure parser: an extractor realization's raw response text → TOTAL claims,
|
|
320
|
+
* or a thrown {@link SynthesizeCertClaimParseFail} (never a partial/coerced
|
|
321
|
+
* result) — no LLM touched, so this is unit-testable against fixture
|
|
322
|
+
* response strings. Reuses {@link stripLlmResponseFence} (shared with the
|
|
323
|
+
* judge parser) and {@link assertSynthesizeCertStructuralClaims} as the
|
|
324
|
+
* single shape authority.
|
|
325
|
+
*/
|
|
326
|
+
export function parseSynthesizeCertStructuralClaimsResponseText(text) {
|
|
327
|
+
const jsonText = stripLlmResponseFence(text);
|
|
328
|
+
let parsed;
|
|
329
|
+
try {
|
|
330
|
+
parsed = JSON.parse(jsonText);
|
|
331
|
+
}
|
|
332
|
+
catch (error) {
|
|
333
|
+
throw new SynthesizeCertClaimParseFail(`synthesize-cert-judge: structural-claim extraction response is not valid JSON (fail-closed): ${error instanceof Error ? error.message : String(error)}`);
|
|
334
|
+
}
|
|
335
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
336
|
+
throw new SynthesizeCertClaimParseFail(`synthesize-cert-judge: structural-claim extraction response must be a JSON object, got ${JSON.stringify(parsed)} (fail-closed)`);
|
|
337
|
+
}
|
|
338
|
+
assertSynthesizeCertStructuralClaims(parsed);
|
|
339
|
+
return parsed;
|
|
340
|
+
}
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* S5 — failure-preserving coordinate loop for the B4 `synthesize-cert/v1`
|
|
3
|
+
* bench (design 20260706-b4-r8-harness-design v3 §8/§15.5, R8).
|
|
4
|
+
*
|
|
5
|
+
* The expected universe is frozen packets × declared_reps × the three arms.
|
|
6
|
+
* EVERY coordinate yields exactly one judgement row — a failed synthesize, a
|
|
7
|
+
* failed judge, and a soft-abort remainder are all rows (never silent drops),
|
|
8
|
+
* with the synthesize OUTPUT plane and the judge EXECUTION plane recorded
|
|
9
|
+
* independently and metrics `not_judged` on any failure. Each arm's execution
|
|
10
|
+
* is a SINGLE synthesize call on the frozen packet (negative: the
|
|
11
|
+
* `input_corruption/v1` mutation of it, computed once per input BEFORE any
|
|
12
|
+
* dispatch so a leverless input fails pre-spend); there is no per-arm subtree
|
|
13
|
+
* walk (owner decision 2a).
|
|
14
|
+
*
|
|
15
|
+
* Resume/re-run semantics (§8): prior rows are keyed by coordinate; a decisive
|
|
16
|
+
* prior row is kept without re-dispatch, a non-decisive one is re-executed and
|
|
17
|
+
* OVERWRITTEN with `attempts` incremented — the published judge_failure_rate's
|
|
18
|
+
* residual-only nature stays visible. The caller re-binds the run to the
|
|
19
|
+
* frozen manifest (S2 facts binding + S7 record assembly); the loop never
|
|
20
|
+
* shrinks or extends the universe it is handed.
|
|
21
|
+
*
|
|
22
|
+
* Realization error classification: an arm realization SHOULD throw the typed
|
|
23
|
+
* errors below so §6.2-1's parse/structural zero-tolerance is attributable;
|
|
24
|
+
* any untyped rejection is an honest transport loss (`not_run`). A judge
|
|
25
|
+
* rejection classifies as `timeout` only via its typed error, else
|
|
26
|
+
* `judge_error`. An output that fails the source-safe envelope or verdicts
|
|
27
|
+
* outside the total enum are structural failures of the respective plane.
|
|
28
|
+
*/
|
|
29
|
+
import { createHash } from "node:crypto";
|
|
30
|
+
import { assertSynthesisOutputBounded, } from "../reconstruct/comprehension-semantic-map.js";
|
|
31
|
+
import { assertSynthesizeCertJudgeVerdicts, } from "./synthesize-cert-judge.js";
|
|
32
|
+
import { applyInputCorruptionV1, } from "./synthesize-cert-mutation.js";
|
|
33
|
+
import { isDecisiveRow, SYNTHESIZE_CERT_ARMS, } from "./synthesize-cert-record.js";
|
|
34
|
+
/** The realization parsed no output from the provider response. */
|
|
35
|
+
export class SynthesizeCertParseFail extends Error {
|
|
36
|
+
}
|
|
37
|
+
/** The realization parsed output of the wrong shape. */
|
|
38
|
+
export class SynthesizeCertStructuralFail extends Error {
|
|
39
|
+
}
|
|
40
|
+
/** The judge realization timed out. */
|
|
41
|
+
export class SynthesizeCertJudgeTimeout extends Error {
|
|
42
|
+
}
|
|
43
|
+
/** Canonical identity of one arm output — capsule per_row binding material
|
|
44
|
+
* (§18) and the optional honesty field on ok rows. */
|
|
45
|
+
export function synthesizeCertOutputSha256(out) {
|
|
46
|
+
return createHash("sha256")
|
|
47
|
+
.update(JSON.stringify({
|
|
48
|
+
semantic_summary: out.semantic_summary,
|
|
49
|
+
boundaries: out.boundaries.map((b) => ({
|
|
50
|
+
row: b.row,
|
|
51
|
+
character_before: b.character_before,
|
|
52
|
+
character_after: b.character_after,
|
|
53
|
+
})),
|
|
54
|
+
}))
|
|
55
|
+
.digest("hex");
|
|
56
|
+
}
|
|
57
|
+
const DEFAULT_MAX_CONSECUTIVE_FAILURES = 5;
|
|
58
|
+
/** Canonical coordinate identity (packet input_id × rep × arm) — the SINGLE
|
|
59
|
+
* source both this loop's own priorRows dedup-guard and the orchestrator's
|
|
60
|
+
* --resume progress fold ({@link foldSynthesizeCertProgressRows}) key against,
|
|
61
|
+
* so the two can never drift into disagreeing key formats. */
|
|
62
|
+
export const coordinateKey = (inputId, rep, arm) => `${inputId} ${rep} ${arm}`;
|
|
63
|
+
/**
|
|
64
|
+
* Folds RAW progress rows — e.g. read line-by-line from an incremental
|
|
65
|
+
* `onRowComplete` sidecar, which can carry more than one line for the same
|
|
66
|
+
* coordinate across a single run's own re-attempts — into one row per
|
|
67
|
+
* coordinate, LAST WRITE WINS. This is the only normalization a --resume
|
|
68
|
+
* caller needs before handing the result to `runSynthesizeCertLoop` as
|
|
69
|
+
* `priorRows`: the loop ITSELF already throws (see above) if `priorRows`
|
|
70
|
+
* still carries a duplicate coordinate, so this function does not duplicate
|
|
71
|
+
* that (or any other) validation — dedup is its only job.
|
|
72
|
+
*
|
|
73
|
+
* Generic over any row shape carrying the three coordinate fields (not just
|
|
74
|
+
* {@link SynthesizeCertJudgementRow}): sibling progress sidecars — e.g. the
|
|
75
|
+
* rejudge and structured-grounding cuts' own per-coordinate progress rows —
|
|
76
|
+
* reuse this SAME fold instead of re-implementing coordinate dedup.
|
|
77
|
+
*/
|
|
78
|
+
export function foldSynthesizeCertProgressRows(rows) {
|
|
79
|
+
const byCoordinate = new Map();
|
|
80
|
+
for (const row of rows) {
|
|
81
|
+
byCoordinate.set(coordinateKey(row.input_id, row.rep, row.arm), row);
|
|
82
|
+
}
|
|
83
|
+
return [...byCoordinate.values()];
|
|
84
|
+
}
|
|
85
|
+
export async function runSynthesizeCertLoop(args) {
|
|
86
|
+
const maxConsecutive = args.maxConsecutiveFailures ?? DEFAULT_MAX_CONSECUTIVE_FAILURES;
|
|
87
|
+
if (!Number.isSafeInteger(args.declaredReps) || args.declaredReps < 1) {
|
|
88
|
+
throw new Error(`synthesize-cert-loop: declaredReps must be a positive safe integer, got ${args.declaredReps}`);
|
|
89
|
+
}
|
|
90
|
+
if (!Number.isSafeInteger(maxConsecutive) || maxConsecutive < 1) {
|
|
91
|
+
throw new Error(`synthesize-cert-loop: maxConsecutiveFailures must be a positive safe integer`);
|
|
92
|
+
}
|
|
93
|
+
const seenInputIds = new Set();
|
|
94
|
+
for (const p of args.packets) {
|
|
95
|
+
if (seenInputIds.has(p.input_id)) {
|
|
96
|
+
throw new Error(`synthesize-cert-loop: duplicate packet input_id ${p.input_id}`);
|
|
97
|
+
}
|
|
98
|
+
seenInputIds.add(p.input_id);
|
|
99
|
+
}
|
|
100
|
+
// Pre-spend mutation of EVERY input (§6 lever guarantee): a leverless input
|
|
101
|
+
// throws here, before any dispatch — never mid-run after paid calls.
|
|
102
|
+
const mutationsByInputId = new Map();
|
|
103
|
+
for (const p of args.packets) {
|
|
104
|
+
mutationsByInputId.set(p.input_id, applyInputCorruptionV1(p.packet, { seed: args.mutationSeed }));
|
|
105
|
+
}
|
|
106
|
+
const priorByCoordinate = new Map();
|
|
107
|
+
for (const prior of args.priorRows ?? []) {
|
|
108
|
+
const key = coordinateKey(prior.input_id, prior.rep, prior.arm);
|
|
109
|
+
if (priorByCoordinate.has(key)) {
|
|
110
|
+
throw new Error(`synthesize-cert-loop: priorRows carry coordinate ${key} more than once`);
|
|
111
|
+
}
|
|
112
|
+
priorByCoordinate.set(key, prior);
|
|
113
|
+
}
|
|
114
|
+
const rows = [];
|
|
115
|
+
const emit = async (row) => {
|
|
116
|
+
rows.push(row);
|
|
117
|
+
await args.onRowComplete?.(row);
|
|
118
|
+
};
|
|
119
|
+
let consecutiveFailures = 0;
|
|
120
|
+
let aborted = null;
|
|
121
|
+
let synthesizeCalls = 0;
|
|
122
|
+
let judgeCalls = 0;
|
|
123
|
+
for (const frozen of args.packets) {
|
|
124
|
+
const mutation = mutationsByInputId.get(frozen.input_id);
|
|
125
|
+
for (let rep = 1; rep <= args.declaredReps; rep += 1) {
|
|
126
|
+
for (const arm of SYNTHESIZE_CERT_ARMS) {
|
|
127
|
+
const isNegative = arm === "negative_control";
|
|
128
|
+
const rowBase = {
|
|
129
|
+
row_id: `${frozen.input_id}.r${rep}.${arm}`,
|
|
130
|
+
fixture_id: frozen.fixture_id,
|
|
131
|
+
input_id: frozen.input_id,
|
|
132
|
+
input_sha256: isNegative ? mutation.mutated_input_sha256 : frozen.input_sha256,
|
|
133
|
+
rep,
|
|
134
|
+
arm,
|
|
135
|
+
stratum: { seam: frozen.stratum.seam, merge: frozen.stratum.merge },
|
|
136
|
+
...(isNegative ? { source_input_id: frozen.input_id } : {}),
|
|
137
|
+
};
|
|
138
|
+
const prior = priorByCoordinate.get(coordinateKey(frozen.input_id, rep, arm));
|
|
139
|
+
if (prior && isDecisiveRow(prior)) {
|
|
140
|
+
await emit(prior); // resume: a decisive coordinate is never re-spent.
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
const attempts = prior ? (prior.attempts ?? 1) + 1 : 1;
|
|
144
|
+
if (aborted) {
|
|
145
|
+
await emit({
|
|
146
|
+
...rowBase,
|
|
147
|
+
candidate_output_status: "not_run",
|
|
148
|
+
judge_status: "not_run",
|
|
149
|
+
metrics: { grounding: "not_judged", boundary: "not_judged" },
|
|
150
|
+
attempts,
|
|
151
|
+
});
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
// ── synthesize plane: ONE call on the frozen (or mutated) packet.
|
|
155
|
+
let output = null;
|
|
156
|
+
let outputStatus = "ok";
|
|
157
|
+
synthesizeCalls += 1; // attempt-counted at dispatch
|
|
158
|
+
let raw = null;
|
|
159
|
+
try {
|
|
160
|
+
raw = await args.arms[arm](isNegative ? mutation.mutated : frozen.packet);
|
|
161
|
+
}
|
|
162
|
+
catch (error) {
|
|
163
|
+
outputStatus =
|
|
164
|
+
error instanceof SynthesizeCertParseFail
|
|
165
|
+
? "parse_fail"
|
|
166
|
+
: error instanceof SynthesizeCertStructuralFail
|
|
167
|
+
? "structural_fail"
|
|
168
|
+
: "not_run"; // untyped rejection = honest transport loss
|
|
169
|
+
}
|
|
170
|
+
if (raw !== null) {
|
|
171
|
+
try {
|
|
172
|
+
assertSynthesisOutputBounded(raw); // envelope: parsed but wrong shape
|
|
173
|
+
output = raw;
|
|
174
|
+
}
|
|
175
|
+
catch {
|
|
176
|
+
outputStatus = "structural_fail";
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
// ── judge plane: original packet reference, negative included (§7).
|
|
180
|
+
let judgeStatus = "not_run";
|
|
181
|
+
let metrics = {
|
|
182
|
+
grounding: "not_judged",
|
|
183
|
+
boundary: "not_judged",
|
|
184
|
+
};
|
|
185
|
+
if (output !== null) {
|
|
186
|
+
judgeCalls += 1; // attempt-counted at dispatch
|
|
187
|
+
try {
|
|
188
|
+
const verdicts = await args.judge({
|
|
189
|
+
original_packet: frozen.packet,
|
|
190
|
+
arm_output: output,
|
|
191
|
+
});
|
|
192
|
+
assertSynthesizeCertJudgeVerdicts(verdicts);
|
|
193
|
+
judgeStatus = "ok";
|
|
194
|
+
metrics = { grounding: verdicts.grounding, boundary: verdicts.boundary };
|
|
195
|
+
}
|
|
196
|
+
catch (error) {
|
|
197
|
+
judgeStatus = error instanceof SynthesizeCertJudgeTimeout ? "timeout" : "judge_error";
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
const row = {
|
|
201
|
+
...rowBase,
|
|
202
|
+
candidate_output_status: outputStatus,
|
|
203
|
+
judge_status: judgeStatus,
|
|
204
|
+
metrics,
|
|
205
|
+
...(output !== null ? { output_sha256: synthesizeCertOutputSha256(output) } : {}),
|
|
206
|
+
attempts,
|
|
207
|
+
};
|
|
208
|
+
await emit(row);
|
|
209
|
+
if (outputStatus === "ok" && judgeStatus === "ok") {
|
|
210
|
+
consecutiveFailures = 0;
|
|
211
|
+
}
|
|
212
|
+
else {
|
|
213
|
+
consecutiveFailures += 1;
|
|
214
|
+
if (consecutiveFailures >= maxConsecutive) {
|
|
215
|
+
aborted = {
|
|
216
|
+
reason: `soft-abort: ${consecutiveFailures} consecutive failed coordinates (threshold ${maxConsecutive})`,
|
|
217
|
+
at_coordinate: row.row_id,
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
return {
|
|
225
|
+
rows,
|
|
226
|
+
negative_mutations: new Map([...mutationsByInputId].map(([inputId, m]) => [
|
|
227
|
+
inputId,
|
|
228
|
+
{ mutated_input_sha256: m.mutated_input_sha256, levers_applied: m.levers_applied },
|
|
229
|
+
])),
|
|
230
|
+
aborted,
|
|
231
|
+
synthesize_calls: synthesizeCalls,
|
|
232
|
+
judge_calls: judgeCalls,
|
|
233
|
+
};
|
|
234
|
+
}
|