fix(reconstruction): bind proof evidence to claims
This commit is contained in:
parent
9f9f9c3968
commit
83b3368d8d
|
|
@ -4,37 +4,13 @@ import type { ReconstructionObligation } from "../domain/reconstructionObligatio
|
|||
type ProofAuthority = ReconstructionObligation["required_fixture_authority"];
|
||||
type OriginalAuthority =
|
||||
ReconstructionObligation["required_original_authority"];
|
||||
type ProofEvidenceShape = readonly [predicate: string, operation: string];
|
||||
|
||||
const proofEvidenceShapes: Readonly<
|
||||
Record<ProofAuthority, readonly ProofEvidenceShape[]>
|
||||
> = {
|
||||
unit: [["rea.fixture-verification/v1", "run_fixture_verifier"]],
|
||||
integration: [["rea.fixture-verification/v1", "run_fixture_verifier"]],
|
||||
protocol: [["rea.protocol-observation/v1", "observe_protocol"]],
|
||||
renderer: [
|
||||
["rea.browser-scenario-capture/v1", "capture_browser_scenario"],
|
||||
["rea.electron-page-inspection/v1", "inspect_electron_page"],
|
||||
],
|
||||
"packaged-process": [
|
||||
["rea.process-capture/v4", "capture_process_scenario"],
|
||||
[
|
||||
"rea.reconstruction-readiness-fixture/v1",
|
||||
"run_reconstruction_readiness_fixture",
|
||||
],
|
||||
],
|
||||
"native-abi": [
|
||||
["rea.managed-native-verification/v1", "verify_managed_native_boundaries"],
|
||||
],
|
||||
"live-observation": [
|
||||
["javascript-controlled-replay-observation", "run_controlled_replay"],
|
||||
["rea.browser-scenario-capture/v1", "capture_browser_scenario"],
|
||||
["rea.electron-page-inspection/v1", "inspect_electron_page"],
|
||||
["rea.javascript-runtime-observation/v1", "observe_javascript_runtime"],
|
||||
["rea.runtime-characterization/v1", "execute_node_characterization"],
|
||||
],
|
||||
external: [["rea.protocol-observation/v1", "observe_protocol"]],
|
||||
};
|
||||
interface ExpectedProof {
|
||||
readonly obligationId: string;
|
||||
readonly fixtureId?: string;
|
||||
readonly caseKind?: string;
|
||||
readonly verifierId?: string;
|
||||
readonly claimId?: string;
|
||||
}
|
||||
|
||||
const proofEvidenceAuthorities: Readonly<
|
||||
Record<ProofAuthority, readonly Evidence["authority"][]>
|
||||
|
|
@ -88,13 +64,30 @@ export const proofAuthoritySatisfies = (
|
|||
export const evidenceAuthoritySupportsProof = (
|
||||
evidence: Evidence,
|
||||
authority: ProofAuthority,
|
||||
): boolean =>
|
||||
evidence.confidence === "observed" &&
|
||||
proofEvidenceAuthorities[authority].includes(evidence.authority) &&
|
||||
proofEvidenceShapes[authority].some(
|
||||
([predicate, operation]) =>
|
||||
evidence.predicate_type === predicate && evidence.operation === operation,
|
||||
expected: ExpectedProof,
|
||||
): boolean => {
|
||||
if (
|
||||
evidence.confidence !== "observed" ||
|
||||
!proofEvidenceAuthorities[authority].includes(evidence.authority) ||
|
||||
evidence.predicate_type !== "rea.reconstruction-proof/v1" ||
|
||||
evidence.operation !== "verify_reconstruction_obligations"
|
||||
)
|
||||
return false;
|
||||
const result = evidence.normalized_result;
|
||||
if (typeof result !== "object" || result === null || Array.isArray(result))
|
||||
return false;
|
||||
const includes = (key: string, value: string | undefined): boolean =>
|
||||
value === undefined ||
|
||||
(Array.isArray(result[key]) && result[key].includes(value));
|
||||
return (
|
||||
result.passed === true &&
|
||||
includes("obligation_ids", expected.obligationId) &&
|
||||
includes("fixture_ids", expected.fixtureId) &&
|
||||
includes("case_kinds", expected.caseKind) &&
|
||||
includes("verifier_ids", expected.verifierId) &&
|
||||
includes("claim_ids", expected.claimId)
|
||||
);
|
||||
};
|
||||
|
||||
/** Check that Evidence observes the original at the required boundary. */
|
||||
export const evidenceSupportsOriginalAuthority = (
|
||||
|
|
|
|||
|
|
@ -46,7 +46,6 @@ interface EvaluationContext {
|
|||
readonly contradictions: ReadonlyMap<string, readonly string[]>;
|
||||
}
|
||||
|
||||
/** Evaluate candidates and manifest bindings into one canonical fail-closed ledger. */
|
||||
export const evaluateReconstructionObligationLedger = ({
|
||||
candidates: candidatesInput,
|
||||
bundle: bundleInput,
|
||||
|
|
@ -68,12 +67,10 @@ export const evaluateReconstructionObligationLedger = ({
|
|||
const candidates = allCandidates.slice(0, maxObligations);
|
||||
const omittedCount = allCandidates.length - candidates.length;
|
||||
const context = createEvaluationContext(candidates, bundle, manifest);
|
||||
const preliminary = candidates.map((candidate) =>
|
||||
evaluateObligation(candidate, context),
|
||||
const obligations = applyDependencyDiagnostics(
|
||||
candidates.map((candidate) => evaluateObligation(candidate, context)),
|
||||
context,
|
||||
);
|
||||
const obligations = applyDependencyDiagnostics(preliminary, context);
|
||||
const summary = summarizeObligations(obligations);
|
||||
const reports = obligationReports(obligations);
|
||||
const ownershipGraph = obligations
|
||||
.flatMap((obligation) =>
|
||||
obligation.binding === null
|
||||
|
|
@ -109,8 +106,8 @@ export const evaluateReconstructionObligationLedger = ({
|
|||
);
|
||||
const semantic = {
|
||||
coverage,
|
||||
summary,
|
||||
reports,
|
||||
summary: summarizeObligations(obligations),
|
||||
reports: obligationReports(obligations),
|
||||
ownership_graph: ownershipGraph,
|
||||
dependency_graph: dependencyGraph,
|
||||
obligations,
|
||||
|
|
@ -327,7 +324,11 @@ const evaluateBinding = (
|
|||
const evidence = context.evidence.get(id);
|
||||
return (
|
||||
evidence !== undefined &&
|
||||
evidenceAuthoritySupportsProof(evidence, fixture.authority)
|
||||
evidenceAuthoritySupportsProof(evidence, fixture.authority, {
|
||||
obligationId: candidate.obligation_id,
|
||||
fixtureId: fixture.fixture_id,
|
||||
caseKind: fixture.case_kind,
|
||||
})
|
||||
);
|
||||
}),
|
||||
)
|
||||
|
|
@ -375,7 +376,11 @@ const evaluateVerifier = (
|
|||
candidate.required_verifier_authority,
|
||||
) ||
|
||||
(resultEvidence !== undefined &&
|
||||
!evidenceAuthoritySupportsProof(resultEvidence, verifier.authority))
|
||||
!evidenceAuthoritySupportsProof(resultEvidence, verifier.authority, {
|
||||
obligationId: candidate.obligation_id,
|
||||
verifierId: verifier.verifier_id,
|
||||
claimId: verifier.claim_id,
|
||||
}))
|
||||
)
|
||||
addDiagnostic(
|
||||
diagnostics,
|
||||
|
|
|
|||
|
|
@ -44,6 +44,37 @@ const processEvidence = () =>
|
|||
authority: "controlled-replay",
|
||||
});
|
||||
|
||||
const boundProofEvidence = (
|
||||
id: string,
|
||||
result: {
|
||||
readonly obligation_ids: readonly string[];
|
||||
readonly fixture_ids: readonly string[];
|
||||
readonly case_kinds: readonly string[];
|
||||
readonly verifier_ids: readonly string[];
|
||||
readonly claim_ids: readonly string[];
|
||||
},
|
||||
authority: "shipped-artifact" | "controlled-replay" = "controlled-replay",
|
||||
) =>
|
||||
createEvidence(
|
||||
undefined,
|
||||
{ id: `proof-${id}`, name: "Reconstruction proof", version: "1" },
|
||||
{
|
||||
predicateType: "rea.reconstruction-proof/v1",
|
||||
operation: "verify_reconstruction_obligations",
|
||||
parameters: {},
|
||||
result: {
|
||||
passed: true,
|
||||
obligation_ids: [...result.obligation_ids],
|
||||
fixture_ids: [...result.fixture_ids],
|
||||
case_kinds: [...result.case_kinds],
|
||||
verifier_ids: [...result.verifier_ids],
|
||||
claim_ids: [...result.claim_ids],
|
||||
},
|
||||
confidence: "observed",
|
||||
authority,
|
||||
},
|
||||
);
|
||||
|
||||
const request = (
|
||||
records: readonly Evidence[],
|
||||
overrides: Partial<ReconstructionObligationLedgerInput> = {},
|
||||
|
|
@ -260,8 +291,18 @@ describe("reconstruction obligation ledger", () => {
|
|||
const obligation = initial.obligations[0];
|
||||
if (obligation === undefined)
|
||||
throw new Error("Expected process obligation");
|
||||
const fixtureIds = obligation.required_case_kinds.map(
|
||||
(caseKind) => `packaged.${caseKind}`,
|
||||
);
|
||||
const proof = boundProofEvidence("packaged", {
|
||||
obligation_ids: [obligation.obligation_id],
|
||||
fixture_ids: fixtureIds,
|
||||
case_kinds: obligation.required_case_kinds,
|
||||
verifier_ids: ["verifier.packaged"],
|
||||
claim_ids: ["claim.process"],
|
||||
});
|
||||
const result = build(
|
||||
request([capture], {
|
||||
request([capture, proof], {
|
||||
manifest: {
|
||||
schema_version: 1,
|
||||
bindings: [
|
||||
|
|
@ -278,7 +319,7 @@ describe("reconstruction obligation ledger", () => {
|
|||
fixture_id: `packaged.${caseKind}`,
|
||||
case_kind: caseKind,
|
||||
authority: "packaged-process",
|
||||
evidence_ids: [capture.evidence_id],
|
||||
evidence_ids: [proof.evidence_id],
|
||||
})),
|
||||
verifier: {
|
||||
verifier_id: "verifier.packaged",
|
||||
|
|
@ -286,7 +327,7 @@ describe("reconstruction obligation ledger", () => {
|
|||
command: "npm run verify:package -- process",
|
||||
authority: "packaged-process",
|
||||
status: "pass",
|
||||
result_evidence_id: capture.evidence_id,
|
||||
result_evidence_id: proof.evidence_id,
|
||||
enumerated_obligation_ids: [obligation.obligation_id],
|
||||
nondeterminism: {
|
||||
mode: "partial-order",
|
||||
|
|
@ -365,19 +406,18 @@ describe("reconstruction obligation ledger", () => {
|
|||
});
|
||||
|
||||
it("propagates an open dependency through the full obligation chain", () => {
|
||||
const evidence = createEvidence(
|
||||
undefined,
|
||||
{ id: "fixture-chain", name: "Fixture chain", version: "1" },
|
||||
{
|
||||
predicateType: "rea.fixture-verification/v1",
|
||||
operation: "run_fixture_verifier",
|
||||
parameters: {},
|
||||
result: { passed: true },
|
||||
confidence: "observed",
|
||||
authority: "shipped-artifact",
|
||||
},
|
||||
);
|
||||
const ids = ["obl.chain.a", "obl.chain.b", "obl.chain.c"] as const;
|
||||
const evidence = boundProofEvidence(
|
||||
"fixture-chain",
|
||||
{
|
||||
obligation_ids: ids,
|
||||
fixture_ids: ids.map((id) => `fixture.${id}`),
|
||||
case_kinds: ["positive"],
|
||||
verifier_ids: ids.map((id) => `verifier.${id}`),
|
||||
claim_ids: ids.map((id) => `claim.${id}`),
|
||||
},
|
||||
"shipped-artifact",
|
||||
);
|
||||
const reviewed = ids.map((id, index) => ({
|
||||
...reviewedObligation(id, evidence.evidence_id),
|
||||
dependency_obligation_ids:
|
||||
|
|
|
|||
Loading…
Reference in New Issue