agentfootprint
Version:
The explainable agent framework — backtrack a wrong answer to the exact context that caused it (evidence, not guesses). Built on footprintjs.
100 lines (99 loc) • 4.73 kB
TypeScript
/**
* unsupported-claim — the answer's typed claims against the run's settled
* facts, at the CLAIM seam.
*
* Pattern: pure function over (declared contract, ledger rows, parsed
* answer); same algebra, last seam.
* Role: the check the evidence gate states it cannot do — "a FALSE
* CLAIM ASSEMBLED FROM REAL VALUES" passes token grounding. Typed
* facts (semantics envelopes) make the contradiction
* representable; the DECLARED contract (`.claims()`, the
* `argumentsFrom` precedent: the author names the edge, the
* checker joins and never infers) makes it decidable.
*
* THE FENCES, because they are the check's honesty:
* - TYPED STRATUM ONLY. The check reads the schema-validated answer
* object; prose files nothing, ever.
* - LATEST SETTLES, EARLIER QUOTES. Only the last ledger row for a fact
* asserts; earlier rows ride as `quoted` witnesses — history is
* quotation, so claiming a superseded value is a contradiction WITH the
* history on the record.
* - UNCOLLECTED IS UNREACHABLE. A declared claim whose fact no envelope
* ever carried is incomparable — never an accusation. Same for a ledger
* value that is itself an unknown/not-applicable Claim: honest absence
* propagates (`participates`).
* - DOUBT IS AN ADVISORY, NOT A DEFECT. An answer of `null` or the literal
* string 'unknown' where the ledger holds a verified value files a
* finding marked `advisory: true` and counted APART — the model
* declining to claim is a softer fact than the model claiming wrong.
* - ONE STATED LENIENCY: a numeric fact answered as its exact string
* ('30' for 30) is not a contradiction — models stringify numbers, and
* on an accusation boundary a representational difference is not a
* difference. One direction only, string-of-number toward number.
*/
import type { Assertion, SubjectRef } from '../assertion/types.js';
import type { ContextError } from '../finding/types.js';
import type { Disposition } from '../disposition/types.js';
import { type ClaimPairEvaluation } from './evaluate.js';
/** One entry of the operator's declared contract: answer field → ledger fact. */
export interface DeclaredClaim {
/** Dot-path into the parsed answer object ('nav_count', 'report.nav_count'). */
readonly answerField: string;
readonly entity: string;
readonly field: string;
}
/** One settled fact extracted from a semantics envelope during the run. */
export interface ClaimLedgerRow {
readonly entity: string;
readonly field: string;
readonly value: unknown;
readonly toolName: string;
readonly toolCallId: string;
readonly iteration: number;
/** The envelope's provenance.measured_at, when it carried one. */
readonly measuredAt?: string | number;
}
export interface ClaimCheckOutcome {
readonly findings: readonly ContextError[];
/** One disposition per declared claim — silence is never an option. */
readonly dispositions: readonly {
claim: string;
disposition: Disposition;
}[];
}
/**
* One declared claim, compared — the selection and the evaluation every
* reader of a claim derives from (`unsupportedClaimsOf` here; the delivery
* verdict in `delivered.ts`, which answer validation's rows read), so a row
* can never accuse where the detector does not.
*
* - `omitted` — the answer does not state the field (the schema allowed it).
* - `uncollected` — no ledger row settled the declared fact.
* - `compared` — the latest row asserts, earlier rows ride as `rows`;
* `evaluation` is the shared pair evaluator's verdict (`evaluate.ts`).
*/
export type ClaimComparison = {
readonly kind: 'omitted';
} | {
readonly kind: 'uncollected';
} | {
readonly kind: 'compared';
readonly evaluation: ClaimPairEvaluation;
readonly claimed: unknown;
readonly rows: readonly ClaimLedgerRow[];
readonly settledRow: ClaimLedgerRow;
readonly subject: SubjectRef;
readonly ledgerProvenance: string;
readonly evidence: Assertion;
readonly assertion: Assertion;
};
/**
* Compare ONE declared claim of the parsed answer against the ledger. Pure.
* Throws exactly what the pair evaluator throws (a value JSON cannot render).
*/
export declare function compareClaim(claim: DeclaredClaim, ledger: readonly ClaimLedgerRow[], answer: unknown, epoch: number): ClaimComparison;
/**
* Compare the parsed answer's declared claims against the ledger.
* Pure; the caller files findings and dispositions.
*/
export declare function unsupportedClaimsOf(declared: readonly DeclaredClaim[], ledger: readonly ClaimLedgerRow[], answer: unknown, epoch: number): ClaimCheckOutcome;