onto-mcp 0.4.11 → 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 +153 -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 +202 -49
- 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/nesting-batch-worker-contract.md +1 -1
- 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 +679 -18
- 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 +131 -0
- package/dist/core-runtime/cli/claude-code-review-unit-executor.js +21 -9
- package/dist/core-runtime/cli/claude-nesting-batch-worker.js +4 -2
- 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 +289 -25
- 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 +326 -0
- package/dist/core-runtime/effort-calibration-reconstruct.js +47 -0
- package/dist/core-runtime/effort-calibration-review.js +22 -0
- package/dist/core-runtime/effort-calibration-sweep.js +110 -0
- package/dist/core-runtime/effort-frontier.js +134 -0
- package/dist/core-runtime/llm/claude-bin.js +77 -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 +316 -35
- 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 +37 -0
- package/dist/core-runtime/reconstruct/benchmark-evidence.js +16 -0
- package/dist/core-runtime/reconstruct/claim-projection-validation.js +19 -5
- 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/directive-validation.js +4 -3
- package/dist/core-runtime/reconstruct/dispatch-fallback-artifacts.js +640 -0
- package/dist/core-runtime/reconstruct/execution-telemetry.js +84 -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 +20 -5
- package/dist/core-runtime/reconstruct/material-profile-validation.js +24 -6
- package/dist/core-runtime/reconstruct/materialize-preparation.js +380 -23
- package/dist/core-runtime/reconstruct/maturation-validation.js +1522 -67
- package/dist/core-runtime/reconstruct/mock-llm-realization.js +120 -1
- package/dist/core-runtime/reconstruct/obligation-assertion.js +18 -0
- package/dist/core-runtime/reconstruct/ontology-seed-validation.js +39 -7
- package/dist/core-runtime/reconstruct/output-budget.js +6 -0
- package/dist/core-runtime/reconstruct/pipeline-execution-ledger.js +112 -5
- package/dist/core-runtime/reconstruct/post-seed-validation.js +122 -11
- package/dist/core-runtime/reconstruct/proof-authority-validation.js +2 -6
- package/dist/core-runtime/reconstruct/purpose-authority-validation.js +119 -7
- package/dist/core-runtime/reconstruct/record.js +49 -3
- package/dist/core-runtime/reconstruct/registry-verification-validation.js +41 -5
- package/dist/core-runtime/reconstruct/run-control-validation.js +1019 -37
- package/dist/core-runtime/reconstruct/run.js +5409 -517
- package/dist/core-runtime/reconstruct/seed-authoring-readiness-validation.js +56 -8
- package/dist/core-runtime/reconstruct/semantic-quality-gate.js +112 -7
- package/dist/core-runtime/reconstruct/source-observation-delta-validation.js +53 -5
- package/dist/core-runtime/reconstruct/source-observations.js +166 -0
- package/dist/core-runtime/reconstruct/source-safety-validation.js +77 -188
- package/dist/core-runtime/reconstruct/source-scout-pack-validation.js +50 -6
- package/dist/core-runtime/reconstruct/terminal-validation.js +145 -13
- 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 +219 -7
- 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 +27 -5
- package/dist/mcp/tool-schemas.js +8 -0
- 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 +20 -3
- package/settings.example.json +77 -16
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* S3 — the `input_corruption/v1` negative-control transform for the B4
|
|
3
|
+
* `synthesize-cert/v1` bench (design 20260706-b4-r8-harness-design v3 §6/§15.3).
|
|
4
|
+
*
|
|
5
|
+
* The negative arm is defined by INPUT mutation only (every arm runs the same
|
|
6
|
+
* prompt), realized as this ONE deterministic named transform: the record cites
|
|
7
|
+
* `mutation_kind` + `mutation_params`, this implementation owns boundedness.
|
|
8
|
+
* Both judged metrics are targeted (§6.2-3) through two content-changing levers:
|
|
9
|
+
*
|
|
10
|
+
* - grounding lever = `relabel/v1`: every format_cluster string and every
|
|
11
|
+
* child_summary is deterministically rewritten (seed-based), so the sorted
|
|
12
|
+
* cluster list / child prose CONTENT changes — the sha moves and the arm
|
|
13
|
+
* authors from wrong facts, which the judge (against the ORIGINAL packet)
|
|
14
|
+
* can flag. Child summaries rotate between siblings when that changes bytes
|
|
15
|
+
* (plausible-but-wrong prose); otherwise a seed suffix guarantees the change.
|
|
16
|
+
* - boundary lever = `seam_offset/v1`: every value_shape seam row moves by a
|
|
17
|
+
* seed-picked offset ≥2 where the node range permits (|Δ|=1 only as the
|
|
18
|
+
* tight-range fallback) — ≥2 escapes the production ±1 anchor tolerance, so
|
|
19
|
+
* a mutated-seam-following output cannot silently re-anchor to the original.
|
|
20
|
+
*
|
|
21
|
+
* Per-metric provenance (§6 G/I): the result reports which levers ACTUALLY
|
|
22
|
+
* changed the packet — on a no-seam input the boundary lever is inert and the
|
|
23
|
+
* row targets grounding only ("no spurious boundary" is what boundary then
|
|
24
|
+
* judges). An input with NO applicable lever (no clusters, no children, no
|
|
25
|
+
* seams) is REJECTED fail-closed: it cannot host a negative row, and the
|
|
26
|
+
* sampler/loop must exclude it (§6 레버 보장).
|
|
27
|
+
*
|
|
28
|
+
* Whether the corruption REALLY degrades the metrics is a semantic question —
|
|
29
|
+
* R7 human curation (§13.3), never re-enforced deterministically here.
|
|
30
|
+
*/
|
|
31
|
+
import { createHash } from "node:crypto";
|
|
32
|
+
import { SYNTHESIZE_CERT_METRICS, } from "./synthesize-cert-record.js";
|
|
33
|
+
import { synthesizeCertInputSha256 } from "./synthesize-cert-sampler.js";
|
|
34
|
+
export const SYNTHESIZE_CERT_MUTATION_KIND = "input_corruption/v1";
|
|
35
|
+
export const INPUT_CORRUPTION_GROUNDING_LEVER = "relabel/v1";
|
|
36
|
+
export const INPUT_CORRUPTION_BOUNDARY_LEVER = "seam_offset/v1";
|
|
37
|
+
const sha8 = (text) => createHash("sha256").update(text).digest("hex").slice(0, 8);
|
|
38
|
+
/**
|
|
39
|
+
* Applies `input_corruption/v1` to a frozen packet. Throws on an input with no
|
|
40
|
+
* applicable lever (the §6 negative-sample exclusion), so "a negative row that
|
|
41
|
+
* silently ran the original packet" is structurally impossible.
|
|
42
|
+
*/
|
|
43
|
+
export function applyInputCorruptionV1(packet, params) {
|
|
44
|
+
if (typeof params.seed !== "string" || params.seed.length === 0) {
|
|
45
|
+
throw new Error("input_corruption/v1: seed must be a non-empty string");
|
|
46
|
+
}
|
|
47
|
+
const originalSha = synthesizeCertInputSha256(packet); // envelope-asserts the input too
|
|
48
|
+
const hasClusters = packet.format_clusters.length > 0;
|
|
49
|
+
const hasChildren = packet.child_summaries.length > 0;
|
|
50
|
+
const hasSeams = packet.value_shape_seams.length > 0;
|
|
51
|
+
if (!hasClusters && !hasChildren && !hasSeams) {
|
|
52
|
+
throw new Error("input_corruption/v1: input has no applicable lever (no format_clusters, no child_summaries, no seams) — it cannot host a negative row and must be excluded from the negative sample (§6)");
|
|
53
|
+
}
|
|
54
|
+
// ── grounding lever: relabel/v1 ─────────────────────────────────────────────
|
|
55
|
+
const mutatedClusters = packet.format_clusters.map((s) => `${s}~${sha8(`${params.seed}|cluster|${s}`)}`);
|
|
56
|
+
let mutatedChildren = packet.child_summaries.map((c) => ({ key: c.key, summary: c.summary }));
|
|
57
|
+
if (hasChildren) {
|
|
58
|
+
const n = mutatedChildren.length;
|
|
59
|
+
const rotated = mutatedChildren.map((c, i) => ({
|
|
60
|
+
key: c.key,
|
|
61
|
+
summary: mutatedChildren[(i + 1) % n].summary,
|
|
62
|
+
}));
|
|
63
|
+
const rotationChanged = rotated.some((c, i) => c.summary !== mutatedChildren[i].summary);
|
|
64
|
+
mutatedChildren = rotationChanged
|
|
65
|
+
? rotated // plausible-but-wrong: each subtree now carries a SIBLING's prose
|
|
66
|
+
: mutatedChildren.map((c) => ({
|
|
67
|
+
key: c.key,
|
|
68
|
+
summary: `${c.summary} ~${sha8(`${params.seed}|child|${c.key}`)}`,
|
|
69
|
+
}));
|
|
70
|
+
}
|
|
71
|
+
const groundingApplied = hasClusters || hasChildren;
|
|
72
|
+
// ── boundary lever: seam_offset/v1 ─────────────────────────────────────────
|
|
73
|
+
const r = packet.node_ref;
|
|
74
|
+
const preferred = 2 + (parseInt(sha8(`${params.seed}|seam`), 16) % 3); // 2..4
|
|
75
|
+
const offsetRow = (row) => {
|
|
76
|
+
for (const delta of [preferred, -preferred, 1, -1]) {
|
|
77
|
+
const moved = row + delta;
|
|
78
|
+
if (moved !== row && moved > r.row_start && moved <= r.row_end)
|
|
79
|
+
return moved;
|
|
80
|
+
}
|
|
81
|
+
return row - 1; // degenerate 1-row range: still a guaranteed change
|
|
82
|
+
};
|
|
83
|
+
const mutatedSeams = packet.value_shape_seams.map((s) => ({
|
|
84
|
+
row: offsetRow(s.row),
|
|
85
|
+
prev_shape: s.prev_shape,
|
|
86
|
+
new_shape: s.new_shape,
|
|
87
|
+
}));
|
|
88
|
+
const mutated = {
|
|
89
|
+
node_ref: { sheet: r.sheet, column_index: r.column_index, row_start: r.row_start, row_end: r.row_end },
|
|
90
|
+
format_clusters: mutatedClusters,
|
|
91
|
+
value_shape_seams: mutatedSeams,
|
|
92
|
+
child_summaries: mutatedChildren,
|
|
93
|
+
};
|
|
94
|
+
const mutatedSha = synthesizeCertInputSha256(mutated);
|
|
95
|
+
if (mutatedSha === originalSha) {
|
|
96
|
+
throw new Error("input_corruption/v1: mutation left the packet sha unchanged (impossible state — transform guarantee broken)");
|
|
97
|
+
}
|
|
98
|
+
return {
|
|
99
|
+
mutated,
|
|
100
|
+
mutated_input_sha256: mutatedSha,
|
|
101
|
+
levers_applied: { grounding: groundingApplied, boundary: hasSeams },
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
/** The record's `negative_arm` block for this transform — single source so the
|
|
105
|
+
* harness cannot cite the kind while running something else (§6.3). */
|
|
106
|
+
export function buildInputCorruptionV1NegativeArm(seed) {
|
|
107
|
+
return {
|
|
108
|
+
arm: "negative_control",
|
|
109
|
+
mutation_kind: SYNTHESIZE_CERT_MUTATION_KIND,
|
|
110
|
+
mutation_params: {
|
|
111
|
+
grounding_lever: INPUT_CORRUPTION_GROUNDING_LEVER,
|
|
112
|
+
boundary_lever: INPUT_CORRUPTION_BOUNDARY_LEVER,
|
|
113
|
+
seed,
|
|
114
|
+
},
|
|
115
|
+
targeted_metrics: [...SYNTHESIZE_CERT_METRICS],
|
|
116
|
+
};
|
|
117
|
+
}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { assertSynthesisInputBounded, assertSynthesisOutputBounded, buildSynthesisInputForNode, } from "../reconstruct/comprehension-semantic-map.js";
|
|
2
|
+
import { synthesizeCertDeterministicFactsSha256, synthesizeCertInputSha256, } from "./synthesize-cert-sampler.js";
|
|
3
|
+
/**
|
|
4
|
+
* Freezes one packet per sampled manifest entry. For each entry the pipeline's
|
|
5
|
+
* deterministic facts are re-derived and bound to the manifest's
|
|
6
|
+
* `deterministic_facts_sha256` (fail-closed: a wrong workbook, a changed
|
|
7
|
+
* reduce config, or a stale manifest cannot silently freeze). Merge entries
|
|
8
|
+
* get reference-authored child summaries (memoized per pipeline); leaf entries
|
|
9
|
+
* freeze with `child_summaries: []` and spend nothing.
|
|
10
|
+
*/
|
|
11
|
+
export async function freezeSynthesizeCertPackets(args) {
|
|
12
|
+
const summaryCaches = new Map();
|
|
13
|
+
let referenceCalls = 0;
|
|
14
|
+
/** Post-order reference authoring of `key`'s summary (and its non-subsumed
|
|
15
|
+
* descendants'), memoized — mirrors the production bridge walk. */
|
|
16
|
+
const authorSummary = async (pipeline, cache, key) => {
|
|
17
|
+
if (cache.has(key))
|
|
18
|
+
return;
|
|
19
|
+
const tnode = pipeline.trace.nodes.get(key);
|
|
20
|
+
if (!tnode)
|
|
21
|
+
throw new Error(`synthesize-cert-packet: trace node missing for ${key} (reference walk).`);
|
|
22
|
+
const consumed = tnode.child_keys.filter((k) => pipeline.modes.get(k) !== "subsumed");
|
|
23
|
+
for (const child of consumed)
|
|
24
|
+
await authorSummary(pipeline, cache, child);
|
|
25
|
+
const input = buildSynthesisInputForNode(pipeline.trace, pipeline.nodesByKey, pipeline.modes, key, cache);
|
|
26
|
+
assertSynthesisInputBounded(input);
|
|
27
|
+
referenceCalls += 1; // attempt-counted at dispatch (W2-X7-001 discipline).
|
|
28
|
+
const out = await args.referenceSynthesize(input);
|
|
29
|
+
assertSynthesisOutputBounded(out);
|
|
30
|
+
cache.set(key, out.semantic_summary);
|
|
31
|
+
};
|
|
32
|
+
const packets = [];
|
|
33
|
+
for (const entry of args.entries) {
|
|
34
|
+
const pipeline = args.resolvePipeline(entry);
|
|
35
|
+
if (!pipeline.trace.nodes.has(entry.node_key)) {
|
|
36
|
+
throw new Error(`synthesize-cert-packet: manifest entry ${entry.input_id} names node ${entry.node_key}, absent from its resolved pipeline`);
|
|
37
|
+
}
|
|
38
|
+
let cache = summaryCaches.get(pipeline);
|
|
39
|
+
if (!cache) {
|
|
40
|
+
cache = new Map();
|
|
41
|
+
summaryCaches.set(pipeline, cache);
|
|
42
|
+
}
|
|
43
|
+
// The manifest's merge flag must be TRUE about the resolved pipeline — a
|
|
44
|
+
// flipped flag would either skip authoring (caught below) or silently
|
|
45
|
+
// misfile the row's stratum in the record.
|
|
46
|
+
const mode = pipeline.modes.get(entry.node_key);
|
|
47
|
+
if ((mode === "accumulating") !== entry.stratum.merge) {
|
|
48
|
+
throw new Error(`synthesize-cert-packet: ${entry.input_id} declares merge=${entry.stratum.merge} but the pipeline classifies ${entry.node_key} as '${mode}' (fail-closed)`);
|
|
49
|
+
}
|
|
50
|
+
// Manifest ↔ pipeline binding (§4): the deterministic facts the pipeline
|
|
51
|
+
// yields NOW must hash to what the manifest froze at sampling time.
|
|
52
|
+
const tnode = pipeline.trace.nodes.get(entry.node_key);
|
|
53
|
+
const consumed = tnode.child_keys.filter((k) => pipeline.modes.get(k) !== "subsumed");
|
|
54
|
+
const factsProbe = buildSynthesisInputForNode(pipeline.trace, pipeline.nodesByKey, pipeline.modes, entry.node_key, new Map(consumed.map((k) => [k, ""])));
|
|
55
|
+
const facts = {
|
|
56
|
+
node_ref: factsProbe.node_ref,
|
|
57
|
+
format_clusters: factsProbe.format_clusters,
|
|
58
|
+
value_shape_seams: factsProbe.value_shape_seams,
|
|
59
|
+
};
|
|
60
|
+
const factsSha = synthesizeCertDeterministicFactsSha256(facts);
|
|
61
|
+
if (factsSha !== entry.deterministic_facts_sha256) {
|
|
62
|
+
throw new Error(`synthesize-cert-packet: ${entry.input_id} deterministic facts recompute to ${factsSha} but the manifest froze ${entry.deterministic_facts_sha256} — wrong workbook, changed reduce config, or stale manifest (fail-closed)`);
|
|
63
|
+
}
|
|
64
|
+
// Reference child authoring (merge only; a frontier node consumes no
|
|
65
|
+
// children and the builder returns child_summaries: []).
|
|
66
|
+
for (const child of consumed) {
|
|
67
|
+
if (entry.stratum.merge)
|
|
68
|
+
await authorSummary(pipeline, cache, child);
|
|
69
|
+
}
|
|
70
|
+
const packet = buildSynthesisInputForNode(pipeline.trace, pipeline.nodesByKey, pipeline.modes, entry.node_key, cache);
|
|
71
|
+
assertSynthesisInputBounded(packet);
|
|
72
|
+
packets.push({
|
|
73
|
+
fixture_id: entry.fixture_id,
|
|
74
|
+
input_id: entry.input_id,
|
|
75
|
+
node_key: entry.node_key,
|
|
76
|
+
stratum: entry.stratum,
|
|
77
|
+
deterministic_facts_sha256: entry.deterministic_facts_sha256,
|
|
78
|
+
input_sha256: synthesizeCertInputSha256(packet),
|
|
79
|
+
packet,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
return { packets, reference_synthesize_calls: referenceCalls };
|
|
83
|
+
}
|
|
84
|
+
// ── --resume checkpoint (live-wiring cut, owner decision D1) ─────────────────
|
|
85
|
+
//
|
|
86
|
+
// Freeze is the ONE paid phase a resumed run must never repeat: this contract
|
|
87
|
+
// durably serializes exactly what `freezeSynthesizeCertPackets` produced, so a
|
|
88
|
+
// crashed/interrupted --go run can reload the identical frozen packets instead
|
|
89
|
+
// of re-spending the reference-authoring calls. The orchestrator script owns
|
|
90
|
+
// the file I/O (write right after freeze completes, read at --resume start);
|
|
91
|
+
// this module owns the contract shape and its fail-closed verification.
|
|
92
|
+
export const SYNTHESIZE_CERT_FREEZE_CHECKPOINT_CONTRACT = "synthesize-cert-freeze-checkpoint/v1";
|
|
93
|
+
/** Pure projection of a freeze result into the durable checkpoint shape — no
|
|
94
|
+
* I/O; the caller JSON.stringifies and writes it. */
|
|
95
|
+
export function serializeSynthesizeCertFreezeCheckpoint(frozen, manifestIdentitySha256) {
|
|
96
|
+
return {
|
|
97
|
+
checkpoint_contract: SYNTHESIZE_CERT_FREEZE_CHECKPOINT_CONTRACT,
|
|
98
|
+
manifest_identity_sha256: manifestIdentitySha256,
|
|
99
|
+
reference_synthesize_calls: frozen.reference_synthesize_calls,
|
|
100
|
+
packets: frozen.packets,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Fail-closed parse + re-verification of a persisted checkpoint. Every check
|
|
105
|
+
* throws (never silently coerces or drops): contract tag mismatch, manifest
|
|
106
|
+
* identity mismatch, a malformed packet shape, a duplicate input_id, the
|
|
107
|
+
* checkpoint's input_id SET disagreeing with the expected manifest (extra OR
|
|
108
|
+
* missing — no silent scope-shrink/grow), and — the tamper/corruption guard —
|
|
109
|
+
* each packet's `input_sha256` failing to recompute via the SAME canonical
|
|
110
|
+
* hash (`synthesizeCertInputSha256`) `freezeSynthesizeCertPackets` used to
|
|
111
|
+
* produce it. Reuses `assertSynthesisInputBounded` (the shipped source-safe
|
|
112
|
+
* envelope guard) rather than re-implementing packet shape checks.
|
|
113
|
+
*/
|
|
114
|
+
export function parseSynthesizeCertFreezeCheckpoint(raw, args) {
|
|
115
|
+
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
|
|
116
|
+
throw new Error("synthesize-cert-packet: freeze checkpoint must be a JSON object (fail-closed)");
|
|
117
|
+
}
|
|
118
|
+
const obj = raw;
|
|
119
|
+
if (obj.checkpoint_contract !== SYNTHESIZE_CERT_FREEZE_CHECKPOINT_CONTRACT) {
|
|
120
|
+
throw new Error(`synthesize-cert-packet: freeze checkpoint carries contract ${JSON.stringify(obj.checkpoint_contract)}, expected ${SYNTHESIZE_CERT_FREEZE_CHECKPOINT_CONTRACT} (fail-closed)`);
|
|
121
|
+
}
|
|
122
|
+
if (obj.manifest_identity_sha256 !== args.expectedManifestIdentitySha256) {
|
|
123
|
+
throw new Error(`synthesize-cert-packet: freeze checkpoint manifest identity ${JSON.stringify(obj.manifest_identity_sha256)} disagrees with the expected manifest ${args.expectedManifestIdentitySha256} (fail-closed — same --fixture args in same order?)`);
|
|
124
|
+
}
|
|
125
|
+
if (typeof obj.reference_synthesize_calls !== "number" ||
|
|
126
|
+
!Number.isSafeInteger(obj.reference_synthesize_calls) ||
|
|
127
|
+
obj.reference_synthesize_calls < 0) {
|
|
128
|
+
throw new Error("synthesize-cert-packet: freeze checkpoint reference_synthesize_calls must be a nonnegative safe integer (fail-closed)");
|
|
129
|
+
}
|
|
130
|
+
if (!Array.isArray(obj.packets)) {
|
|
131
|
+
throw new Error("synthesize-cert-packet: freeze checkpoint packets must be an array (fail-closed)");
|
|
132
|
+
}
|
|
133
|
+
const seenInputIds = new Set();
|
|
134
|
+
const packets = obj.packets.map((rawPacket, index) => {
|
|
135
|
+
if (typeof rawPacket !== "object" || rawPacket === null || Array.isArray(rawPacket)) {
|
|
136
|
+
throw new Error(`synthesize-cert-packet: freeze checkpoint packets[${index}] must be an object (fail-closed)`);
|
|
137
|
+
}
|
|
138
|
+
const p = rawPacket;
|
|
139
|
+
const { fixture_id, input_id, node_key, stratum, deterministic_facts_sha256, input_sha256, packet } = p;
|
|
140
|
+
if (typeof fixture_id !== "string" ||
|
|
141
|
+
typeof input_id !== "string" ||
|
|
142
|
+
typeof node_key !== "string" ||
|
|
143
|
+
typeof deterministic_facts_sha256 !== "string" ||
|
|
144
|
+
typeof input_sha256 !== "string" ||
|
|
145
|
+
typeof stratum !== "object" || stratum === null || Array.isArray(stratum) ||
|
|
146
|
+
typeof stratum.seam !== "boolean" ||
|
|
147
|
+
typeof stratum.merge !== "boolean" ||
|
|
148
|
+
typeof packet !== "object" || packet === null || Array.isArray(packet)) {
|
|
149
|
+
throw new Error(`synthesize-cert-packet: freeze checkpoint packets[${index}] has a malformed shape (fail-closed)`);
|
|
150
|
+
}
|
|
151
|
+
if (seenInputIds.has(input_id)) {
|
|
152
|
+
throw new Error(`synthesize-cert-packet: freeze checkpoint lists input_id ${input_id} more than once (fail-closed)`);
|
|
153
|
+
}
|
|
154
|
+
seenInputIds.add(input_id);
|
|
155
|
+
// Reuse the shipped source-safe envelope guard (§13.6) instead of a second
|
|
156
|
+
// hand-rolled shape check — a malformed/enriched packet fails here.
|
|
157
|
+
assertSynthesisInputBounded(packet);
|
|
158
|
+
const recomputedSha = synthesizeCertInputSha256(packet);
|
|
159
|
+
if (recomputedSha !== input_sha256) {
|
|
160
|
+
throw new Error(`synthesize-cert-packet: freeze checkpoint packet ${input_id} recomputes input_sha256 ${recomputedSha}, checkpoint declares ${input_sha256} (fail-closed — tampered or corrupted checkpoint)`);
|
|
161
|
+
}
|
|
162
|
+
return {
|
|
163
|
+
fixture_id,
|
|
164
|
+
input_id,
|
|
165
|
+
node_key: node_key,
|
|
166
|
+
stratum: {
|
|
167
|
+
seam: stratum.seam,
|
|
168
|
+
merge: stratum.merge,
|
|
169
|
+
},
|
|
170
|
+
deterministic_facts_sha256,
|
|
171
|
+
input_sha256,
|
|
172
|
+
packet: packet,
|
|
173
|
+
};
|
|
174
|
+
});
|
|
175
|
+
const expectedIds = new Set(args.expectedInputIds);
|
|
176
|
+
const extra = [...seenInputIds].filter((id) => !expectedIds.has(id));
|
|
177
|
+
const missing = [...expectedIds].filter((id) => !seenInputIds.has(id));
|
|
178
|
+
if (extra.length > 0 || missing.length > 0) {
|
|
179
|
+
throw new Error(`synthesize-cert-packet: freeze checkpoint input_id set disagrees with the expected manifest (extra=[${extra.join(", ")}] missing=[${missing.join(", ")}]) — fail-closed, scope-shrink/grow forbidden`);
|
|
180
|
+
}
|
|
181
|
+
return { packets, reference_synthesize_calls: obj.reference_synthesize_calls };
|
|
182
|
+
}
|