Completeness and evidence projection rules
The policy implementation follows.
ts
export function defineEvidenceChecklist<Observation, Requirement extends EvidenceRequirement>(
definitions: readonly EvidenceProvider<Observation, Requirement>[],
) {
const providers = new Map<string, EvidenceProvider<Observation, Requirement>>();
for (const definition of definitions) {
const predicate = definition.isSatisfied;
breakWhile([
{
occur: () => providers.has(definition.kind),
error: () => `Duplicate evidence kind: ${definition.kind}`,
},
{
occur: () => typeof predicate !== 'function',
error: () => `Missing evidence provider: ${definition.kind}`,
},
]);
providers.set(
definition.kind,
Object.freeze({
kind: definition.kind,
description: definition.description,
isSatisfied: predicate.bind(definition),
}),
);
}
const satisfied = (observations: readonly Observation[], requirement: Requirement): boolean => {
const provider = providers.get(requirement.kind);
breakWhile(!provider, () => `Unknown evidence kind: ${requirement.kind}`);
return observations.some((observation) =>
booleanResult(
provider!.isSatisfied(observation, requirement),
'Evidence predicates must be synchronous',
'Evidence predicates must return boolean',
),
);
};
return Object.freeze({
isSatisfied: (request: { observations: readonly Observation[]; requirement: Requirement }) =>
satisfied(request.observations, request.requirement),
isComplete: (request: {
observations: readonly Observation[];
requirements: readonly Requirement[];
}) => request.requirements.every((requirement) => satisfied(request.observations, requirement)),
missing({
observations,
requirements,
}: {
observations: readonly Observation[];
requirements: readonly Requirement[];
}) {
return requirements.filter((requirement) => !satisfied(observations, requirement));
},
});
}
export function defineEvidenceProjection<Observation, Source>(
definition: EvidenceProjectionDefinition<Observation, Source>,
) {
const sourceId = definition.sourceId.bind(definition);
const toolId =
definition.toolId?.bind(definition) ??
((observation: Observation) => {
// Fallback tool selector: accept either the current `toolId` field or the legacy `tool`.
const value = observation as { toolId?: string; tool?: string };
return value.toolId ?? value.tool!;
});
const providers = new Map<string, EvidenceProjectionProvider<Observation, Source>>();
for (const [tool, provider] of Object.entries(definition.providers)) {
breakWhile(
![provider.sources, provider.withSources].every((handler) => typeof handler === 'function'),
`Invalid evidence projection: ${tool}`,
);
providers.set(
tool,
Object.freeze({
sources: provider.sources.bind(provider),
withSources: provider.withSources.bind(provider),
}),
);
}
return Object.freeze({
project({
observations,
sourceIds = [],
}: {
observations: Observation[];
sourceIds?: readonly string[];
}): Observation[] {
// Unknown/empty claims still need review against every available candidate.
if (!sourceIds.length) return observations;
return observations.map((observation) => {
const provider = providers.get(
synchronous(toolId(observation), 'Evidence tool selectors must be synchronous'),
);
if (!provider) return observation;
const sources = synchronous(
provider.sources(observation),
'Evidence source projection must be synchronous',
).filter((source) =>
sourceIds.includes(
synchronous(sourceId(source), 'Evidence source selectors must be synchronous'),
),
);
return synchronous(
provider.withSources(observation, sources),
'Evidence observations must be synchronous',
);
});
},
});
}