UNPKG

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 • 3.87 kB
/** * judge — the typed door's rows: one row per entry of the app's negatives field, its verdict * mapped to the disposition the validation door already reads (design § 5.4), and the * library's statement beside an established one. * * Pattern: `citation.ts` decides; this file names the row (`agent:negative-claims:<field>#<n>`), * maps the verdict to a disposition and composes the statement (`statement.ts`). An * established claim CLEARS the claim (`not-applicable`) — it never verifies the answer, * so no row is `checked-pass`. * Role: integrity/ layer (L3). Not wired: PR 4 mounts it behind `.negativeClaims()` and * `agentChecks: ['negative-claims']`. * * A row carries identities, tool names and codes — never a value and never a sentence; the * statement travels beside it. */ import { stableJson } from '../../lib/stableJson.js'; import { judgeCitation, MAX_CITED_READS } from './citation.js'; import { MAX_NEGATIVE_CLAIMS } from './limits.js'; import { composeStatement } from './statement.js'; export { MAX_CITED_READS }; export { MAX_NEGATIVE_CLAIMS }; const TOO_MANY = Object.freeze({ kind: 'unknown', code: 'too-many-claims', results: [], tools: [], reads: [], }); const CHECKED_FAIL = new Set([ 'contradicted-by-rows', 'wider-search-has-rows', 'search-incomplete', 'superseded-by-write', 'later-read-failed', ]); /** The disposition a typed verdict files (§ 5.4). */ export function dispositionOf(code) { if (code === 'established') return 'not-applicable'; return CHECKED_FAIL.has(code) ? 'checked-fail' : 'unreachable'; } /** A repeated claim is judged once (its stable JSON is the key); a key that cannot be made, every time. */ function judgeOnce(cache, entry, judge) { let key; try { key = stableJson(entry); } catch { key = undefined; } if (key === undefined) return judge(entry); const known = cache.get(key); if (known !== undefined) return known; const verdict = judge(entry); cache.set(key, verdict); return verdict; } /** * Judge every citation of the field. No field, or an empty list, is no claim. A field that is * not a list is one unresolved citation; a field of more than {@link MAX_NEGATIVE_CLAIMS} * claims withholds every one of them (`too-many-claims`). */ export function judgeTypedNegatives(input) { const { field, negatives, record, citable, interpretation } = input; if (negatives === undefined || negatives === null) return []; const entries = Array.isArray(negatives) ? negatives : [negatives]; const tooMany = entries.length > MAX_NEGATIVE_CLAIMS; const cache = new Map(); return entries.map((entry, index) => { const verdict = tooMany ? TOO_MANY : Array.isArray(negatives) ? judgeOnce(cache, entry, (e) => judgeCitation(e, record, citable, interpretation)) : judgeCitation(undefined, record, citable, interpretation); const row = { id: `agent:negative-claims:${field}#${index}`, path: `${field}[${index}]`, index, kind: verdict.kind, code: verdict.code, disposition: dispositionOf(verdict.code), tools: verdict.tools, results: verdict.results, ...(verdict.staleSince === undefined ? {} : { staleSince: verdict.staleSince }), }; if (verdict.code !== 'established' || verdict.kind === 'unknown') return { row }; const reads = verdict.reads.flatMap((call) => call.row === undefined ? [] : [{ call, row: call.row, tool: record.tools[call.toolName] }]); return { row, statement: composeStatement(verdict.kind, reads, record.clock) }; }); } //# sourceMappingURL=judge.js.map