diff --git a/src/domain/reconstructionCoverage.fixture.ts b/src/domain/reconstructionCoverage.fixture.ts index b3052af438..22f012f8e4 100644 --- a/src/domain/reconstructionCoverage.fixture.ts +++ b/src/domain/reconstructionCoverage.fixture.ts @@ -1,14 +1,46 @@ -import { expect } from "vitest"; +import { createHash } from "node:crypto"; + +import canonicalize from "canonicalize"; +import type { z } from "zod"; import { createEvidence } from "./evidence.js"; import { createEvidenceBundle } from "./evidenceBundle.js"; import { - createReconstructionCoverageData, - createReconstructionVerifierContract, evaluateParsedReconstructionClosure, readReconstructionCoverageData, + reconstructionCoverageDataSchema, + type ReconstructionCoverageData, + type ReconstructionVerifierContract, } from "./reconstructionCoverage.js"; +const verifierContractInputSchema = + reconstructionCoverageDataSchema.shape.verifier_contracts.element.omit({ + contract_sha256: true, + }); + +/** Author test coverage through the live authentication and reference owner. */ +export const reconstructionCoverageDataFixture = ( + input: unknown, +): ReconstructionCoverageData => { + const parsed = readReconstructionCoverageData(input); + if (!parsed.ok) throw parsed.error; + return parsed.value.coverage; +}; + +/** Commit schema-owned verifier fields with an independent canonical hash. */ +export const reconstructionVerifierContractFixture = ( + input: z.input, +): ReconstructionVerifierContract => { + const contract = verifierContractInputSchema.parse(input); + const encoded = canonicalize(contract); + if (encoded === undefined) + throw new TypeError("Unserializable fixture verifier contract"); + return { + ...contract, + contract_sha256: createHash("sha256").update(encoded).digest("hex"), + }; +}; + const digest = (character: string): string => character.repeat(64); const EVIDENCE_RECORDS = ["1", "2", "3", "4", "5"].map((character) => @@ -45,7 +77,7 @@ export const reconstructionCoverageEvidenceId = (character: string): string => { /** Build one fully closed reconstruction-coverage workspace. */ export const completeReconstructionCoverageData = () => { - const contract = createReconstructionVerifierContract({ + const contract = reconstructionVerifierContractFixture({ verifier_id: "verify.cli.help", claim_ids: ["claim.cli.help"], dimensions: ["output", "exit"], @@ -55,7 +87,7 @@ export const completeReconstructionCoverageData = () => { normalization_sha256: digest("4"), normalization_removes_dimensions: false, }); - return createReconstructionCoverageData({ + return reconstructionCoverageDataFixture({ evidence_bundle: createEvidenceBundle(EVIDENCE_RECORDS), artifacts: [ { @@ -160,21 +192,13 @@ export const assertReconstructionCoverageEvaluation = ( nowEpochMs: number, ) => { const parsed = readReconstructionCoverageData(input, ["coverage"]); - expect(parsed.ok).toBe(true); - if (!parsed.ok) - throw new Error("Expected valid reconstruction coverage fixture", { - cause: parsed.error, - }); + if (!parsed.ok) throw parsed.error; const result = evaluateParsedReconstructionClosure( parsed.value, boundaryId, nowEpochMs, ); - expect(result.ok).toBe(true); - if (!result.ok) - throw new Error("Expected reconstruction coverage evaluation success", { - cause: result.error, - }); + if (!result.ok) throw result.error; return result.value; }; diff --git a/src/domain/reconstructionCoverage.test.ts b/src/domain/reconstructionCoverage.test.ts index b70a0327f1..62fb9d2540 100644 --- a/src/domain/reconstructionCoverage.test.ts +++ b/src/domain/reconstructionCoverage.test.ts @@ -7,10 +7,10 @@ import { assertReconstructionCoverageEvaluation, RECONSTRUCTION_COVERAGE_NOW, reconstructionCoverageEvidenceId, + reconstructionCoverageDataFixture, + reconstructionVerifierContractFixture, } from "./reconstructionCoverage.fixture.js"; import { - createReconstructionCoverageData, - createReconstructionVerifierContract, evaluateParsedReconstructionClosure, readReconstructionCoverageData, type ReconstructionCoverageData, @@ -61,7 +61,6 @@ describe("reconstruction coverage closure", () => { reconstructionCoverageEvidenceId("5"), ].sort(), }); - expect(createReconstructionCoverageData(workspace)).toEqual(workspace); }); it("keeps an incomplete inventory partial despite every registered verifier passing", () => { @@ -69,7 +68,7 @@ describe("reconstruction coverage closure", () => { const semantic = workspace; const boundary = workspace.boundaries[0]; if (boundary === undefined) throw new Error("Expected fixture boundary"); - const incomplete = createReconstructionCoverageData({ + const incomplete = reconstructionCoverageDataFixture({ ...semantic, boundaries: [ { @@ -112,7 +111,7 @@ describe("reconstruction coverage closure", () => { { length: 10_001 }, (_, index) => `missing.surface.${index}`, ); - const incomplete = createReconstructionCoverageData({ + const incomplete = reconstructionCoverageDataFixture({ ...workspace, boundaries: [{ ...boundary, required_surface_ids: missingSurfaces }], }); @@ -133,7 +132,7 @@ describe("reconstruction coverage closure", () => { if (owner === undefined || owner.ownership.disposition !== "implemented") throw new Error("Expected implemented fixture owner"); const semantic = workspace; - const changed = createReconstructionCoverageData({ + const changed = reconstructionCoverageDataFixture({ ...semantic, owners: [ { @@ -166,7 +165,7 @@ describe("reconstruction coverage closure", () => { it("does not let stale verifier results satisfy current claims", () => { const workspace = completeReconstructionCoverageData(); const semantic = workspace; - const stale = createReconstructionCoverageData({ + const stale = reconstructionCoverageDataFixture({ ...semantic, verifier_results: workspace.verifier_results.map((result) => ({ ...result, @@ -202,7 +201,7 @@ describe("reconstruction coverage verifier observations", () => { ) throw new Error("Expected complete reconstruction coverage fixture"); const secondClaimId = "claim.cli.version"; - const sharedContract = createReconstructionVerifierContract({ + const sharedContract = reconstructionVerifierContractFixture({ verifier_id: contract.verifier_id, claim_ids: [...contract.claim_ids, secondClaimId], dimensions: contract.dimensions, @@ -218,7 +217,7 @@ describe("reconstruction coverage verifier observations", () => { contract_sha256: sharedContract.contract_sha256, covered_claim_ids: [...result.covered_claim_ids, secondClaimId], }; - const shared = createReconstructionCoverageData({ + const shared = reconstructionCoverageDataFixture({ ...workspace, claims: [ ...workspace.claims, @@ -259,7 +258,7 @@ describe("reconstruction coverage verifier selection", () => { boundary === undefined ) throw new Error("Expected complete reconstruction coverage fixture"); - const alternateContract = createReconstructionVerifierContract({ + const alternateContract = reconstructionVerifierContractFixture({ verifier_id: "verify.cli.help.alternate", claim_ids: contract.claim_ids, dimensions: contract.dimensions, @@ -270,7 +269,7 @@ describe("reconstruction coverage verifier selection", () => { normalization_removes_dimensions: contract.normalization_removes_dimensions, }); - const ambiguous = createReconstructionCoverageData({ + const ambiguous = reconstructionCoverageDataFixture({ ...workspace, verifier_contracts: [...workspace.verifier_contracts, alternateContract], verifier_results: [ @@ -295,7 +294,7 @@ describe("reconstruction coverage verifier selection", () => { }), ]); - const missing = createReconstructionCoverageData({ + const missing = reconstructionCoverageDataFixture({ ...workspace, verifier_results: [], }); @@ -323,11 +322,11 @@ describe("reconstruction coverage conflicting latest verifier observations", () status: "fail" as const, evidence_ids: [reconstructionCoverageEvidenceId("1")], }; - const firstPass = createReconstructionCoverageData({ + const firstPass = reconstructionCoverageDataFixture({ ...workspace, verifier_results: [result, failed], }); - const firstFail = createReconstructionCoverageData({ + const firstFail = reconstructionCoverageDataFixture({ ...workspace, verifier_results: [failed, result], }); @@ -378,11 +377,11 @@ describe("reconstruction coverage equivalent latest verifier observations", () = owner_sha256s: [...result.owner_sha256s].reverse(), evidence_ids: [reconstructionCoverageEvidenceId("1")], }; - const forward = createReconstructionCoverageData({ + const forward = reconstructionCoverageDataFixture({ ...workspace, verifier_results: [first, equivalent], }); - const reversed = createReconstructionCoverageData({ + const reversed = reconstructionCoverageDataFixture({ ...workspace, verifier_results: [equivalent, first], }); @@ -417,7 +416,7 @@ describe("reconstruction coverage verifier timestamp selection", () => { ...result, observed_at: "2026-07-16T11:30:00.000Z", }; - const changed = createReconstructionCoverageData({ + const changed = reconstructionCoverageDataFixture({ ...workspace, verifier_results: [result, failed, latestPass], }); @@ -434,7 +433,7 @@ describe("reconstruction coverage verifier timestamp selection", () => { /** Evaluate a workspace whose newest verifier observation fails the closure. */ const expectNewestObservationFails = (passedAt: string, failedAt: string) => { const { workspace, result } = completeReconstructionCoverageFixture(); - const changed = createReconstructionCoverageData({ + const changed = reconstructionCoverageDataFixture({ ...workspace, verifier_results: [ { ...result, observed_at: passedAt }, @@ -472,7 +471,7 @@ describe("reconstruction coverage verifier timestamp selection", () => { describe("reconstruction coverage verifier future timestamps", () => { it("treats an observation beyond millisecond-precision now as future", () => { const { workspace, result } = completeReconstructionCoverageFixture(); - const changed = createReconstructionCoverageData({ + const changed = reconstructionCoverageDataFixture({ ...workspace, verifier_results: [ { ...result, observed_at: "2026-07-16T12:00:00.1239Z" }, @@ -494,7 +493,6 @@ describe("reconstruction coverage verifier future timestamps", () => { it("rejects a non-finite evaluation clock", () => { const workspace = completeReconstructionCoverageData(); const parsed = readReconstructionCoverageData(workspace, ["coverage"]); - expect(parsed.ok).toBe(true); if (!parsed.ok) throw parsed.error; expect(() => evaluateParsedReconstructionClosure( @@ -508,7 +506,7 @@ describe("reconstruction coverage verifier future timestamps", () => { it("rejects verifier observations from the future", () => { const { workspace, result } = completeReconstructionCoverageFixture(); const semantic = workspace; - const changed = createReconstructionCoverageData({ + const changed = reconstructionCoverageDataFixture({ ...semantic, verifier_results: [ { ...result, observed_at: "2026-07-17T12:00:00.000Z" }, @@ -530,7 +528,7 @@ describe("reconstruction coverage verifier future timestamps", () => { it("rejects green results and package proofs that omit current commitments", () => { const workspace = completeReconstructionCoverageData(); const semantic = workspace; - const incomplete = createReconstructionCoverageData({ + const incomplete = reconstructionCoverageDataFixture({ ...semantic, verifier_results: workspace.verifier_results.map((result) => ({ ...result, diff --git a/src/domain/reconstructionCoverage.ts b/src/domain/reconstructionCoverage.ts index 5b3c144249..46f67833ae 100644 --- a/src/domain/reconstructionCoverage.ts +++ b/src/domain/reconstructionCoverage.ts @@ -98,7 +98,7 @@ const claimSchema = z.strictObject({ ]), }); -const verifierContractBaseSchema = z.strictObject({ +const verifierContractSchema = z.strictObject({ verifier_id: stableIdSchema, claim_ids: z.array(stableIdSchema).min(1), dimensions: z.array(stableIdSchema).min(1), @@ -112,9 +112,6 @@ const verifierContractBaseSchema = z.strictObject({ minimum_repeats: z.int().min(1), normalization_sha256: digestSchema, normalization_removes_dimensions: z.literal(false), -}); - -const verifierContractSchema = verifierContractBaseSchema.extend({ contract_sha256: digestSchema, }); @@ -214,15 +211,6 @@ export interface ParsedReconstructionCoverage { >; } -/** Parse inline coverage data and validate every evidence and authority link. */ -export const createReconstructionCoverageData = ( - input: unknown, -): ReconstructionCoverageData => { - const parsed = readReconstructionCoverageData(input); - if (!parsed.ok) throw parsed.error; - return parsed.value.coverage; -}; - const coverageEnvelopeSchema = reconstructionCoverageDataSchema.extend({ evidence_bundle: z.unknown(), }); @@ -315,17 +303,6 @@ export type ReconstructionClosureResult = z.infer< type ClosureReason = z.infer; -/** Build a verifier contract whose digest prevents retrospective coverage edits. */ -export const createReconstructionVerifierContract = ( - input: z.input, -): ReconstructionVerifierContract => { - const parsed = verifierContractBaseSchema.parse(input); - return verifierContractSchema.parse({ - ...parsed, - contract_sha256: digestCanonicalValue(parsed, "Reconstruction coverage"), - }); -}; - /** Evaluate the owner's authenticated workspace and lookup facts. */ export const evaluateParsedReconstructionClosure = ( parsed: ParsedReconstructionCoverage, diff --git a/src/domain/reconstructionCoverageEvidence.test.ts b/src/domain/reconstructionCoverageEvidence.test.ts index 6b0212ecb0..5d585ef121 100644 --- a/src/domain/reconstructionCoverageEvidence.test.ts +++ b/src/domain/reconstructionCoverageEvidence.test.ts @@ -6,12 +6,10 @@ import { completeReconstructionCoverageFixture, assertReconstructionCoverageEvaluation, RECONSTRUCTION_COVERAGE_NOW, + reconstructionCoverageDataFixture, + reconstructionVerifierContractFixture, } from "./reconstructionCoverage.fixture.js"; -import { - createReconstructionCoverageData, - createReconstructionVerifierContract, - type ReconstructionCoverageData, -} from "./reconstructionCoverage.js"; +import type { ReconstructionCoverageData } from "./reconstructionCoverage.js"; const proof = ( authority: Evidence["authority"], @@ -35,7 +33,7 @@ const proof = ( const withProofs = (records: readonly Evidence[]) => { const { workspace, result } = completeReconstructionCoverageFixture(); - return createReconstructionCoverageData({ + return reconstructionCoverageDataFixture({ ...workspace, evidence_bundle: createEvidenceBundle([ ...workspace.evidence_bundle.records, @@ -56,7 +54,7 @@ const withPackageProof = ( status: ReconstructionCoverageData["package_proofs"][number]["status"] = "pass", ) => { const { workspace } = completeReconstructionCoverageFixture(); - return createReconstructionCoverageData({ + return reconstructionCoverageDataFixture({ ...workspace, evidence_bundle: createEvidenceBundle([ ...workspace.evidence_bundle.records, @@ -144,7 +142,7 @@ it("does not fall back to older observed proof when the latest result only cites const latest = coverage.verifier_results[0]; if (latest === undefined) throw new Error("Expected verifier result"); const result = evaluate( - createReconstructionCoverageData({ + reconstructionCoverageDataFixture({ ...coverage, verifier_results: [ previous, @@ -173,11 +171,11 @@ it.each([ if (original === undefined || result === undefined) throw new Error("Expected verifier contract"); const { contract_sha256: _digest, ...input } = original; - const contract = createReconstructionVerifierContract({ + const contract = reconstructionVerifierContractFixture({ ...input, authority: required, }); - const changed = createReconstructionCoverageData({ + const changed = reconstructionCoverageDataFixture({ ...coverage, claims: coverage.claims.map((claim) => ({ ...claim, diff --git a/tests/boundary/mcp/reconstructionCoverageMcp.test.ts b/tests/boundary/mcp/reconstructionCoverageMcp.test.ts index 5919c250a2..d1478f5101 100644 --- a/tests/boundary/mcp/reconstructionCoverageMcp.test.ts +++ b/tests/boundary/mcp/reconstructionCoverageMcp.test.ts @@ -6,8 +6,8 @@ import { describe, expect, it } from "vitest"; import { completeReconstructionCoverageData, reconstructionCoverageEvidenceId, + reconstructionCoverageDataFixture, } from "../../../src/domain/reconstructionCoverage.fixture.js"; -import { createReconstructionCoverageData } from "../../../src/domain/reconstructionCoverage.js"; import { createEvidence } from "../../../src/domain/evidence.js"; import { createEvidenceBundle } from "../../../src/domain/evidenceBundle.js"; import { createTestBinarySession } from "../../fixtures/binarySession.js"; @@ -47,7 +47,7 @@ describe("reconstruction coverage inline Evidence", () => { confidence: "inferred", }, ); - const weakCoverage = createReconstructionCoverageData({ + const weakCoverage = reconstructionCoverageDataFixture({ ...coverage, evidence_bundle: createEvidenceBundle([ ...coverage.evidence_bundle.records, @@ -85,7 +85,7 @@ describe("reconstruction coverage inline Evidence", () => { confidence: "inferred", }, ); - const weakCoverage = createReconstructionCoverageData({ + const weakCoverage = reconstructionCoverageDataFixture({ ...coverage, evidence_bundle: createEvidenceBundle([ ...coverage.evidence_bundle.records, @@ -165,7 +165,7 @@ describe("reconstruction coverage selection", () => { const latest = coverage.verifier_results[0]; if (latest === undefined) throw new Error("Expected fixture verifier result"); - const ambiguousCoverage = createReconstructionCoverageData({ + const ambiguousCoverage = reconstructionCoverageDataFixture({ ...coverage, verifier_results: [ latest, @@ -295,7 +295,7 @@ const withCoverageMcp = async ( const currentFixtureCoverage = () => { const coverage = completeReconstructionCoverageData(); - return createReconstructionCoverageData({ + return reconstructionCoverageDataFixture({ ...coverage, verifier_results: coverage.verifier_results.map((result) => ({ ...result,