agentfootprint
Version:
The explainable agent framework — backtrack a wrong answer to the exact context that caused it (evidence, not guesses). Built on footprintjs.
95 lines • 4.32 kB
JavaScript
/**
* Pure opt-in findings adapter. Role: bind one literal declaration to an exact
* authorized result, then ask the shared evaluator; return a row for the EXISTING
* findings writer. No ledger, evidence discovery, callbacks on restore or I/O.
*/
import { copyJson, ownData } from '../../../lib/boundedJson.js';
import { bindClaim } from '../../../integrity/claim-binding/bindEvidence.js';
import { boundedText } from '../../../integrity/claim-binding/scope.js';
import { evaluateClaimPair } from '../../../integrity/unsupported-claim/evaluate.js';
import { claimIdentityOf, readClaimVerificationRow, VERIFICATION_LIMITS, } from './verificationRow.js';
/**
* Invalid/missing JSON value throws explicitly: replacing it with null would
* misrecord the declaration. A malformed optional check is a recorded refusal.
* `supported` is possible only after bindClaim establishes an eligible pair;
* the standalone evaluator's no-conflict result is never sufficient.
*/
export function checkFindingClaim(input) {
const request = ownData(input.request);
if (!Object.hasOwn(request, 'value'))
throw new TypeError('Finding claim has no declared value.');
const declaredValue = copyJson(request.value, VERIFICATION_LIMITS.valueBytes, true);
let declaredRule;
let checkProblem;
if (!Object.hasOwn(request, 'check'))
checkProblem = 'missing-check';
else {
try {
const check = ownData(request.check);
if (boundedText(check.rule))
declaredRule = check.rule;
if (Object.keys(check).some((key) => key !== 'rule') || declaredRule === undefined)
checkProblem = 'malformed-check';
}
catch {
checkProblem = 'malformed-check';
}
}
if (Object.keys(request).some((key) => !['value', 'check', 'subject', 'predicate'].includes(key)))
checkProblem = 'malformed-check';
const base = {
kind: 'claim-verification',
claimId: claimIdentityOf(input.identity),
declaredOn: input.declaredOn,
standing: input.standing,
iteration: input.iteration,
...(input.turn !== undefined && { turn: input.turn }),
checker: { id: 'claim-pair', version: 1 },
...(input.parentResult !== undefined && { declaredResult: input.parentResult }),
...(declaredRule !== undefined && { declaredRule }),
declaredValue,
};
const unverified = (reason) => readClaimVerificationRow({ ...base, outcome: 'unverified', reason });
if (input.standing === 'noise')
return unverified('ineligible-noise');
if (checkProblem !== undefined)
return unverified(checkProblem);
if (input.parentResult === undefined)
return unverified('missing-result');
const bound = bindClaim({ result: input.parentResult, value: declaredValue, rule: declaredRule }, input.snapshot, input.rules, input.intendedScope);
if (bound.kind === 'unverified')
return unverified(bound.reason === 'missing-result' && input.retentionLimited === true
? 'retention-limit'
: bound.reason);
if (Object.hasOwn(request, 'subject') || Object.hasOwn(request, 'predicate')) {
try {
const subject = ownData(request.subject, ['kind', 'id']);
if (subject.kind !== bound.claim.subject.kind ||
subject.id !== bound.claim.subject.id ||
request.predicate !== bound.claim.predicate)
return unverified('assertion-mismatch');
}
catch {
return unverified('assertion-mismatch');
}
}
const reason = evaluateClaimPair(bound.evidence, bound.claim);
if (reason !== 'no-conflict' && reason !== 'numeric-leniency' && reason !== 'conflict')
return unverified(reason);
const resolved = {
resultId: bound.resultId,
rule: bound.rule,
scope: bound.scope,
coverage: bound.coverage,
...(bound.period !== undefined && { period: bound.period }),
evidence: bound.evidence,
claim: bound.claim,
};
return readClaimVerificationRow({
...base,
outcome: reason === 'conflict' ? 'contradicted' : 'supported',
reason,
resolved,
});
}
//# sourceMappingURL=verificationCheck.js.map