perf(reconstruction): index coverage evaluation (#983)
This commit is contained in:
1 parent
ea4db4c9f7
commit
f26c8b5b8c
2 files changed
+259
-40
No files matched your search
@@ -7,6 +7,7 @@ import {
|
||||
} from "./reconstructionCoverage.fixture.js";
|
||||
import {
|
||||
createReconstructionCoverageData,
|
||||
createReconstructionVerifierContract,
|
||||
evaluateReconstructionClosure,
|
||||
} from "./reconstructionCoverage.js";
|
||||
|
||||
@@ -159,6 +160,168 @@ describe("reconstruction coverage closure", () => {
|
||||
});
|
||||
|
||||
describe("reconstruction coverage verifier observations", () => {
|
||||
it("uses one contract and result for each claim covered by a shared verifier", () => {
|
||||
const workspace = completeReconstructionCoverageData();
|
||||
const contract = workspace.verifier_contracts[0];
|
||||
const result = workspace.verifier_results[0];
|
||||
const claim = workspace.claims[0];
|
||||
const boundary = workspace.boundaries[0];
|
||||
if (
|
||||
contract === undefined ||
|
||||
result === undefined ||
|
||||
claim === undefined ||
|
||||
boundary === undefined
|
||||
)
|
||||
throw new Error("Expected complete reconstruction coverage fixture");
|
||||
const secondClaimId = "claim.cli.version";
|
||||
const sharedContract = createReconstructionVerifierContract({
|
||||
verifier_id: contract.verifier_id,
|
||||
claim_ids: [...contract.claim_ids, secondClaimId],
|
||||
dimensions: contract.dimensions,
|
||||
authority: contract.authority,
|
||||
max_age_ms: contract.max_age_ms,
|
||||
minimum_repeats: contract.minimum_repeats,
|
||||
normalization_sha256: contract.normalization_sha256,
|
||||
normalization_removes_dimensions:
|
||||
contract.normalization_removes_dimensions,
|
||||
});
|
||||
const sharedResult = {
|
||||
...result,
|
||||
contract_sha256: sharedContract.contract_sha256,
|
||||
covered_claim_ids: [...result.covered_claim_ids, secondClaimId],
|
||||
};
|
||||
const shared = createReconstructionCoverageData({
|
||||
...workspace,
|
||||
claims: [
|
||||
...workspace.claims,
|
||||
{ ...claim, claim_id: secondClaimId, title: "Version output matches" },
|
||||
],
|
||||
verifier_contracts: [sharedContract],
|
||||
verifier_results: [sharedResult],
|
||||
boundaries: [
|
||||
{
|
||||
...boundary,
|
||||
required_claim_ids: [...boundary.required_claim_ids, secondClaimId],
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
expect(
|
||||
evaluateReconstructionClosure(
|
||||
shared,
|
||||
boundary.boundary_id,
|
||||
RECONSTRUCTION_COVERAGE_NOW,
|
||||
),
|
||||
).toMatchObject({
|
||||
status: "ready",
|
||||
summary: { required_claims: 2, reasons: 0 },
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("reconstruction coverage verifier selection", () => {
|
||||
it("keeps ambiguous verifier contracts and missing results actionable", () => {
|
||||
const workspace = completeReconstructionCoverageData();
|
||||
const contract = workspace.verifier_contracts[0];
|
||||
const result = workspace.verifier_results[0];
|
||||
const boundary = workspace.boundaries[0];
|
||||
if (
|
||||
contract === undefined ||
|
||||
result === undefined ||
|
||||
boundary === undefined
|
||||
)
|
||||
throw new Error("Expected complete reconstruction coverage fixture");
|
||||
const alternateContract = createReconstructionVerifierContract({
|
||||
verifier_id: "verify.cli.help.alternate",
|
||||
claim_ids: contract.claim_ids,
|
||||
dimensions: contract.dimensions,
|
||||
authority: contract.authority,
|
||||
max_age_ms: contract.max_age_ms,
|
||||
minimum_repeats: contract.minimum_repeats,
|
||||
normalization_sha256: contract.normalization_sha256,
|
||||
normalization_removes_dimensions:
|
||||
contract.normalization_removes_dimensions,
|
||||
});
|
||||
const ambiguous = createReconstructionCoverageData({
|
||||
...workspace,
|
||||
verifier_contracts: [...workspace.verifier_contracts, alternateContract],
|
||||
verifier_results: [
|
||||
...workspace.verifier_results,
|
||||
{
|
||||
...result,
|
||||
verifier_id: alternateContract.verifier_id,
|
||||
contract_sha256: alternateContract.contract_sha256,
|
||||
},
|
||||
],
|
||||
});
|
||||
expect(
|
||||
evaluateReconstructionClosure(
|
||||
ambiguous,
|
||||
boundary.boundary_id,
|
||||
RECONSTRUCTION_COVERAGE_NOW,
|
||||
).reasons,
|
||||
).toEqual([
|
||||
expect.objectContaining({
|
||||
code: "verifier-duplicate",
|
||||
subject_id: "claim.cli.help",
|
||||
}),
|
||||
]);
|
||||
|
||||
const missing = createReconstructionCoverageData({
|
||||
...workspace,
|
||||
verifier_results: [],
|
||||
});
|
||||
expect(
|
||||
evaluateReconstructionClosure(
|
||||
missing,
|
||||
boundary.boundary_id,
|
||||
RECONSTRUCTION_COVERAGE_NOW,
|
||||
).reasons,
|
||||
).toEqual([
|
||||
expect.objectContaining({
|
||||
code: "verifier-result-missing",
|
||||
subject_id: "claim.cli.help",
|
||||
}),
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("reconstruction coverage verifier result ordering", () => {
|
||||
it("preserves the first input result when verifier timestamps tie", () => {
|
||||
const workspace = completeReconstructionCoverageData();
|
||||
const result = workspace.verifier_results[0];
|
||||
const boundary = workspace.boundaries[0];
|
||||
if (result === undefined || boundary === undefined)
|
||||
throw new Error("Expected complete reconstruction coverage fixture");
|
||||
const failed = { ...result, status: "fail" as const };
|
||||
const firstPass = createReconstructionCoverageData({
|
||||
...workspace,
|
||||
verifier_results: [result, failed],
|
||||
});
|
||||
const firstFail = createReconstructionCoverageData({
|
||||
...workspace,
|
||||
verifier_results: [failed, result],
|
||||
});
|
||||
|
||||
expect(
|
||||
evaluateReconstructionClosure(
|
||||
firstPass,
|
||||
boundary.boundary_id,
|
||||
RECONSTRUCTION_COVERAGE_NOW,
|
||||
).status,
|
||||
).toBe("ready");
|
||||
expect(
|
||||
evaluateReconstructionClosure(
|
||||
firstFail,
|
||||
boundary.boundary_id,
|
||||
RECONSTRUCTION_COVERAGE_NOW,
|
||||
),
|
||||
).toMatchObject({
|
||||
status: "failed",
|
||||
reasons: [expect.objectContaining({ code: "verifier-failed" })],
|
||||
});
|
||||
});
|
||||
|
||||
it("orders offset-bearing verifier timestamps chronologically", () => {
|
||||
const workspace = completeReconstructionCoverageData();
|
||||
const result = workspace.verifier_results[0];
|
||||
|
||||
@@ -140,6 +140,7 @@ export const evaluateReconstructionClaims = (
|
||||
context: ReconstructionEvaluationContext,
|
||||
): void => {
|
||||
const claims = indexUnique(context.coverage.claims, "claim_id");
|
||||
const indexes = createVerifierIndexes(context.coverage);
|
||||
for (const claimId of context.boundary.required_claim_ids) {
|
||||
const claim = claims.get(claimId);
|
||||
if (claim === undefined) {
|
||||
@@ -151,9 +152,7 @@ export const evaluateReconstructionClaims = (
|
||||
);
|
||||
continue;
|
||||
}
|
||||
const contracts = context.coverage.verifier_contracts.filter(
|
||||
({ claim_ids: ids }) => ids.includes(claimId),
|
||||
);
|
||||
const contracts = indexes.contractsByClaim.get(claimId) ?? [];
|
||||
if (contracts.length === 0) {
|
||||
addReason(
|
||||
context,
|
||||
@@ -174,22 +173,94 @@ export const evaluateReconstructionClaims = (
|
||||
}
|
||||
const contract = contracts[0];
|
||||
if (contract !== undefined)
|
||||
evaluateVerifierResult(context, claim, contract);
|
||||
evaluateVerifierResult(context, claim, contract, indexes);
|
||||
}
|
||||
};
|
||||
|
||||
interface VerifierIndexes {
|
||||
readonly contractsByClaim: ReadonlyMap<
|
||||
string,
|
||||
readonly ReconstructionVerifierContract[]
|
||||
>;
|
||||
readonly latestResultsByVerifier: ReadonlyMap<
|
||||
string,
|
||||
ReconstructionCoverageData["verifier_results"][number]
|
||||
>;
|
||||
readonly artifactDigests: ReadonlySet<string>;
|
||||
readonly ownerDigests: ReadonlySet<string>;
|
||||
readonly surfaces: ReadonlyMap<
|
||||
string,
|
||||
ReconstructionCoverageData["surfaces"][number]
|
||||
>;
|
||||
readonly artifacts: ReadonlyMap<
|
||||
string,
|
||||
ReconstructionCoverageData["artifacts"][number]
|
||||
>;
|
||||
readonly ownersBySurface: ReadonlyMap<
|
||||
string,
|
||||
readonly ReconstructionCoverageData["owners"][number][]
|
||||
>;
|
||||
}
|
||||
|
||||
const createVerifierIndexes = (
|
||||
coverage: ReconstructionCoverageData,
|
||||
): VerifierIndexes => {
|
||||
const contractsByClaim = new Map<string, ReconstructionVerifierContract[]>();
|
||||
for (const contract of coverage.verifier_contracts)
|
||||
for (const claimId of new Set(contract.claim_ids)) {
|
||||
const contracts = contractsByClaim.get(claimId) ?? [];
|
||||
contracts.push(contract);
|
||||
contractsByClaim.set(claimId, contracts);
|
||||
}
|
||||
|
||||
const latestResultsByVerifier = new Map<
|
||||
string,
|
||||
ReconstructionCoverageData["verifier_results"][number]
|
||||
>();
|
||||
for (const result of coverage.verifier_results) {
|
||||
const latest = latestResultsByVerifier.get(result.verifier_id);
|
||||
// Stable descending sort used to choose the first input record on ties.
|
||||
if (
|
||||
latest === undefined ||
|
||||
Date.parse(result.observed_at) > Date.parse(latest.observed_at)
|
||||
)
|
||||
latestResultsByVerifier.set(result.verifier_id, result);
|
||||
}
|
||||
|
||||
const ownersBySurface = new Map<
|
||||
string,
|
||||
ReconstructionCoverageData["owners"][number][]
|
||||
>();
|
||||
for (const owner of coverage.owners) {
|
||||
const owners = ownersBySurface.get(owner.surface_id) ?? [];
|
||||
owners.push(owner);
|
||||
ownersBySurface.set(owner.surface_id, owners);
|
||||
}
|
||||
|
||||
return {
|
||||
contractsByClaim,
|
||||
latestResultsByVerifier,
|
||||
artifactDigests: new Set(
|
||||
coverage.artifacts.map(({ artifact_sha256: value }) => value),
|
||||
),
|
||||
ownerDigests: new Set(
|
||||
coverage.owners.flatMap(({ ownership }) =>
|
||||
ownership.disposition === "implemented" ? [ownership.owner_sha256] : [],
|
||||
),
|
||||
),
|
||||
surfaces: indexUnique(coverage.surfaces, "surface_id"),
|
||||
artifacts: indexUnique(coverage.artifacts, "artifact_id"),
|
||||
ownersBySurface,
|
||||
};
|
||||
};
|
||||
|
||||
const evaluateVerifierResult = (
|
||||
context: ReconstructionEvaluationContext,
|
||||
claim: ReconstructionCoverageData["claims"][number],
|
||||
contract: ReconstructionVerifierContract,
|
||||
indexes: VerifierIndexes,
|
||||
): void => {
|
||||
const results = context.coverage.verifier_results
|
||||
.filter(({ verifier_id: id }) => id === contract.verifier_id)
|
||||
.sort(
|
||||
(left, right) =>
|
||||
Date.parse(right.observed_at) - Date.parse(left.observed_at),
|
||||
);
|
||||
const result = results[0];
|
||||
const result = indexes.latestResultsByVerifier.get(contract.verifier_id);
|
||||
if (result === undefined) {
|
||||
addReason(
|
||||
context,
|
||||
@@ -200,7 +271,7 @@ const evaluateVerifierResult = (
|
||||
return;
|
||||
}
|
||||
addEvidence(context, result.evidence_ids);
|
||||
if (!verifierResultIsCompatible(context, claim, contract, result)) {
|
||||
if (!verifierResultIsCompatible(claim, contract, result, indexes)) {
|
||||
addReason(
|
||||
context,
|
||||
"verifier-result-incompatible",
|
||||
@@ -239,40 +310,23 @@ const evaluateVerifierResult = (
|
||||
};
|
||||
|
||||
const verifierResultIsCompatible = (
|
||||
context: ReconstructionEvaluationContext,
|
||||
claim: ReconstructionCoverageData["claims"][number],
|
||||
contract: ReconstructionVerifierContract,
|
||||
result: ReconstructionCoverageData["verifier_results"][number],
|
||||
indexes: VerifierIndexes,
|
||||
): boolean => {
|
||||
const artifactDigests = new Set(
|
||||
context.coverage.artifacts.map(({ artifact_sha256: value }) => value),
|
||||
);
|
||||
const ownerDigests = new Set(
|
||||
context.coverage.owners.flatMap(({ ownership }) =>
|
||||
ownership.disposition === "implemented" ? [ownership.owner_sha256] : [],
|
||||
),
|
||||
);
|
||||
const surfaces = new Map(
|
||||
context.coverage.surfaces.map((surface) => [surface.surface_id, surface]),
|
||||
);
|
||||
const artifacts = new Map(
|
||||
context.coverage.artifacts.map((artifact) => [
|
||||
artifact.artifact_id,
|
||||
artifact,
|
||||
]),
|
||||
);
|
||||
const expectedArtifactDigests = claim.surface_ids.flatMap((surfaceId) => {
|
||||
const surface = surfaces.get(surfaceId);
|
||||
const surface = indexes.surfaces.get(surfaceId);
|
||||
const artifact =
|
||||
surface === undefined ? undefined : artifacts.get(surface.artifact_id);
|
||||
surface === undefined
|
||||
? undefined
|
||||
: indexes.artifacts.get(surface.artifact_id);
|
||||
return artifact === undefined ? [] : [artifact.artifact_sha256];
|
||||
});
|
||||
const expectedOwnerDigests = context.coverage.owners.flatMap(
|
||||
({ surface_id: surfaceId, ownership }) =>
|
||||
claim.surface_ids.includes(surfaceId) &&
|
||||
ownership.disposition === "implemented"
|
||||
? [ownership.owner_sha256]
|
||||
: [],
|
||||
const expectedOwnerDigests = claim.surface_ids.flatMap((surfaceId) =>
|
||||
(indexes.ownersBySurface.get(surfaceId) ?? []).flatMap(({ ownership }) =>
|
||||
ownership.disposition === "implemented" ? [ownership.owner_sha256] : [],
|
||||
),
|
||||
);
|
||||
return (
|
||||
result.contract_sha256 === contract.contract_sha256 &&
|
||||
@@ -290,11 +344,13 @@ const verifierResultIsCompatible = (
|
||||
result.covered_dimensions.every((item) =>
|
||||
contract.dimensions.includes(item),
|
||||
) &&
|
||||
result.artifact_sha256s.every((item) => artifactDigests.has(item)) &&
|
||||
result.artifact_sha256s.every((item) =>
|
||||
indexes.artifactDigests.has(item),
|
||||
) &&
|
||||
expectedArtifactDigests.every((item) =>
|
||||
result.artifact_sha256s.includes(item),
|
||||
) &&
|
||||
result.owner_sha256s.every((item) => ownerDigests.has(item)) &&
|
||||
result.owner_sha256s.every((item) => indexes.ownerDigests.has(item)) &&
|
||||
expectedOwnerDigests.every((item) => result.owner_sha256s.includes(item)) &&
|
||||
result.repeats >= contract.minimum_repeats &&
|
||||
claim.required_authority === contract.authority
|
||||
|
||||
Reference in new issue
Block a user