/** * rerunWithoutSources — the counterfactual re-run as ONE call: the product * loop over the existing ablation machinery. * * Take a finished run's `ContextBugReport`, name the sources to ignore * (plain ids — injection id, tool name, or step id), and hand it the SAME * `AblationRunner` the localizer's causal mode already uses. It resolves the * ids to their `AblationSpec`s, runs the existing `runAblationProbe` (N ≥ 2 * seeded reruns — never a single-run diff), and returns * `{ answer, answers, removed, whatChanged, runs }`. Companion * `removableSources(report)` gives a UI the toggle vocabulary. * * No new machinery: this file never calls `applyAblations` itself — the * resolved specs go to the consumer's runner, whose body applies them at * agent construction (the documented seam; see `applyAblations`' example). * Works with mock providers ($0) and real ones alike. * * ## Honesty tiers (§B2, unchanged) * * Without `checkBaseline: true` the result reports what was OBSERVED * (`whatChanged.answerFlipped`, similarity, a plain summary) and carries NO * `verdict` — an observation, not a conviction. With it, the unchanged * scenario is probed too and `verdictFor` produces the causal-tier claim — * the same discipline as `localizeContextBug`'s causal mode. */ import type { Embedder } from '../influence-core/index.js'; import type { AblationRunner, AblationRunStats, AblationSpec, AblationVerdict, ContextBugReport, OutcomeComparator, SimilarityStats, SuspectKind } from './types.js'; /** A source to remove: a plain id (resolved against the report's suspects) or an explicit spec. */ export type IgnoredSource = string | AblationSpec; export interface RerunWithoutSourcesOptions { /** The completed run's localizer report — where the suspects and their ablation specs live. */ readonly report: ContextBugReport; /** * The sources to IGNORE on the re-run. A string matches a suspect by * `detail.injectionId`, then `detail.toolName`, then `source` (runtimeStageId) — * first ranked match wins. Unknown ids THROW (listing what `removableSources` * offers) — a silent no-op re-run would lie. An explicit `AblationSpec` * passes through untouched (escape hatch, incl. `kind: 'arg'` for runners * that handle input overrides). */ readonly ignore: readonly IgnoredSource[]; /** * The consumer's counterfactual runner — the SAME `AblationRunner` contract * the localizer's causal mode uses (fresh agent + provider per call, apply * the specs with `applyAblations` at construction, thread `seed`). Works * with mock providers ($0) and real ones alike. */ readonly runner: AblationRunner; /** The original run's answer the re-runs are compared against. */ readonly originalAnswer: string; /** Embedder for the similarity readout + the default comparator. `mockEmbedder()` is fine offline. */ readonly embedder: Embedder; /** Seeded re-runs. Default 3; clamped to ≥ 2 (never single-run claims). */ readonly samples?: number; /** Did the answer CHANGE? Default: embedding similarity < `flipThreshold`. * Strongly recommended with `mockEmbedder`: pass a domain comparator. */ readonly answerChanged?: OutcomeComparator; /** Similarity floor for the default comparator. Default 0.8. */ readonly flipThreshold?: number; /** * Also probe the UNCHANGED scenario (empty specs) to check it reproduces * stably. Costs `samples` extra runs; unlocks the causal-tier `verdict`. * Default false — the result then reports observations only, and says so. */ readonly checkBaseline?: boolean; } /** What changed between the original run and the re-runs — OBSERVED tier. */ export interface WhatChanged { /** A MAJORITY of the seeded re-runs produced a changed answer. */ readonly answerFlipped: boolean; /** Re-runs whose answer changed / total re-runs. */ readonly flips: number; readonly samples: number; /** Embedding similarity of each re-run answer to the original (variance always reported). */ readonly similarityToOriginal: SimilarityStats; /** Whether the un-ablated baseline was probed for stability (`checkBaseline`). */ readonly baselineChecked: boolean; /** Plain-language recap. PRESENTATION ONLY — read the fields as data, never parse this. */ readonly summary: string; } export interface RerunWithoutSourcesResult { /** The re-run's answer — seed 0 of the seeded re-runs. */ readonly answer: string; /** Every seeded re-run's answer, in seed order (`answers[0] === answer`). */ readonly answers: readonly string[]; /** The resolved ablation specs the runner received — one per distinct ignored source. */ readonly removed: readonly AblationSpec[]; readonly whatChanged: WhatChanged; /** The raw probe stats behind `whatChanged` (incl. `cost` when the runner reports `RunCost`). */ readonly runs: AblationRunStats; /** Baseline (nothing-removed) probe stats — present only with `checkBaseline: true`. */ readonly baseline?: AblationRunStats; /** * The CAUSAL-tier claim — present only with `checkBaseline: true` (§B2: a * causal verdict needs seeded reruns AND a stable baseline). Without it the * result stops at `whatChanged`, honestly observational. */ readonly verdict?: AblationVerdict; } export declare function rerunWithoutSources(options: RerunWithoutSourcesOptions): Promise; /** One source a UI can offer as an "ignore" toggle. */ export interface RemovableSource { /** The id `rerunWithoutSources` accepts in `ignore` (injectionId ?? toolName ?? runtimeStageId). */ readonly id: string; /** `'tool' | 'injection' | 'memory'` — the library-filterable kinds only. */ readonly kind: SuspectKind; /** Human label (via `suspectLabel`) for the toggle row. */ readonly label: string; /** runtimeStageId — drillable with the trace-toolpack tools. */ readonly source: string; readonly stageName: string; /** The suspect's proxy score (correlational — sizes an Influence Map node, never convicts). */ readonly score: number; /** The spec `rerunWithoutSources` will send for this id. */ readonly spec: AblationSpec; } /** * The report's removable sources, ranked order, de-duplicated by id (first = * highest-ranked occurrence wins). Excludes kind `'stage'` (nothing to remove) * and `'arg'` (the library cannot filter run input — the runner must override * it; pass an explicit `kind: 'arg'` spec to `ignore` if yours does). */ export declare function removableSources(report: ContextBugReport): readonly RemovableSource[];