agentfootprint
Version:
The explainable agent framework — backtrack a wrong answer to the exact context that caused it (evidence, not guesses). Built on footprintjs.
76 lines (75 loc) • 3.74 kB
TypeScript
/**
* bisectCulprits — multi-culprit bisection over the ranked suspect set
* (RFC-003 Part B, block D9). The "git bisect" of the localizer.
*
* When single-suspect ablations don't flip the outcome — redundant causes
* (two facts that EACH justify the wrong answer), or interacting ones —
* the culprit is a SET. This harness finds a minimal culprit set by
* recursive halving over the ranked suspects (delta-debugging style,
* Zeller's ddmin specialized to two-way splits), then keeps searching the
* remainder for INDEPENDENT culprits until the remainder stops flipping.
*
* Probe semantics (the D9 discipline):
* - every probe = N seeded reruns of the consumer's `AblationRunner`
* with the probe's combined specs; "flipped" = MAJORITY of runs
* changed outcome; similarity mean ± spread is always reported —
* never single-run verdicts;
* - probe 0 is the BASELINE (no ablation): if it flips, the scenario
* itself is unstable and the result is honestly `'inconclusive'`;
* - probes are cached by spec-set, and budgeted (`maxProbes`) — running
* out of budget yields `'inconclusive'`, never a partial claim
* dressed up as a finding.
*
* §B2: the returned `verdict`/`culprits` are CAUSAL claims — they rest
* exclusively on counterfactual reruns. The input ranking only chooses
* the SEARCH ORDER (better ranking = fewer probes), it never decides the
* outcome.
*/
import type { AblationRerun, AblationRunStats, Embedder, Suspect } from './types.js';
/** One executed probe — full variance evidence, kept for the report. */
export interface BisectionProbe {
/** Labels of the suspects ablated together ([] = the baseline probe). */
readonly ablated: readonly string[];
readonly stats: AblationRunStats;
/** Majority-of-N outcome flip. */
readonly flipped: boolean;
}
export interface BisectionResult {
/**
* `'confirmed'` — a minimal culprit set was found and verified by
* counterfactual reruns. `'not-reproducible'` — ablating EVERY ranked
* suspect together does not flip the outcome: the bug's cause is not
* in the ranked set (look at the report's honesty flags — the slice
* may be incomplete). `'inconclusive'` — unstable baseline or probe
* budget exhausted.
*/
readonly verdict: 'confirmed' | 'not-reproducible' | 'inconclusive';
/**
* Minimal culprit set(s): each inner array is one minimal set whose
* JOINT ablation flips the outcome. Independent culprits appear as
* separate sets; redundant/interacting causes appear together in one.
*/
readonly culprits: readonly (readonly Suspect[])[];
/** Every probe executed, in order (baseline first). */
readonly probes: readonly BisectionProbe[];
/** Total consumer-runner invocations (probes × samples). */
readonly runsUsed: number;
}
export interface BisectCulpritsOptions {
/** Ranked suspects — only those carrying an applicable ablation spec
* participate ('arg' and 'stage' suspects are skipped: nothing the
* harness can remove for the consumer). */
readonly suspects: readonly Suspect[];
readonly rerun: AblationRerun;
/** Embedder for similarity stats (and the default flip comparator). */
readonly embedder: Embedder;
/** Probe budget. Default 24. Exhaustion → 'inconclusive'. */
readonly maxProbes?: number;
/** Max independent culprit sets to search for. Default 4. */
readonly maxCulprits?: number;
}
/**
* Find minimal culprit set(s) by seeded counterfactual bisection. See
* module docs for semantics and the §B2 claim tier.
*/
export declare function bisectCulprits(options: BisectCulpritsOptions): Promise<BisectionResult>;