/**
 * variable-recall — footprintjs's VARIABLE slices, joined to agent vocabulary.
 *
 * footprintjs answers "what happened to this variable?" in the address space it
 * owns: commit indices, runtimeStageIds, state keys (`keyTimeline` /
 * `forwardSliceForKey`, `foottrace`). An agent debugger asks the same
 * question in a DIFFERENT vocabulary: which LOOP was that, which injected fact
 * or tool result is that write, and what would REMOVING it cost. This module is
 * the join between the two — and nothing more:
 *
 *   - **Pure assembly.** Every fact here was already recorded. No new capture,
 *     no new scorer, no embedder, no LLM. `joinVariableSlice` re-labels; it
 *     never measures.
 *   - **Loop lifting.** Each moment gets the `loopIndex` of the frame that
 *     contains its `runtimeStageId` (the walk's own `buildWriterFrameIndex` —
 *     one resolver, not two).
 *   - **Agent identity.** A WRITE whose value the trajectory carries is run
 *     through the SAME `defaultSuspectClassifier` the localizer uses, so a
 *     write joins 1:1 with a `Suspect` (`injectionId` / `toolName`) and gets
 *     the `AblationSpec` that would remove it. Writers the classifier cannot
 *     name get NO hook — an honest absence, never a fabricated one.
 *   - **Honesty rides along verbatim.** footprintjs's `HonestyNote`s, the
 *     missing-reason, and `readsCoverage` are copied, not re-worded.
 *
 * The one JUDGEMENT this module makes is {@link DataflowCoverage} — see the
 * long comment on `dataflowCoverage()`. It is what lets `walkToRoot` replace a
 * text-similarity guess with a recorded dataflow edge, and it is deliberately
 * strict.
 *
 * @beta Beta feature (RFC-003 Part B). The API works and is tested, but may
 * change before GA.
 */
import type { ForwardSlice, HonestyNote, KeyMoment, KeyTimeline, MissingSliceReason, ReadsCoverage, StateKey } from 'foottrace';
import { type SuspectClassifier } from './localize.js';
import { type Trajectory } from './trajectory.js';
import type { AblationSpec, ContextBugArtifacts, SuspectKind } from './types.js';
/**
 * How exactly this log records what a key's value FED — the one judgement in
 * this module, and the gate on `walkToRoot`'s deterministic hop.
 *
 * - `'exact'` — every recorded `fed` edge for this key carries per-write
 *   provenance (`FedBasis: 'per-write'`, the `writeProvenance: 'reads-prefix'`
 *   dial): a downstream write is linked to this value BECAUSE that write's
 *   recorded read-prefix names the key, and a write whose prefix omits it is
 *   excluded exactly.
 * - `'conservative'` — at least one edge is stage-level co-occurrence
 *   (`'conservative-fed-edges'`), or the log carries no recorded reads at all.
 *   Sound over-approximation; NEVER presented as exact.
 * - `'unknown'` — no forward slice was supplied, OR the key has no recorded
 *   `fed` edge at all. The second case is the trap: with zero edges there is
 *   also zero conservative edge, so "no conservative edges" would be VACUOUSLY
 *   true and a key nothing ever read would masquerade as the most exact key in
 *   the run. It is demoted here instead.
 */
export type DataflowCoverage = 'exact' | 'conservative' | 'unknown';
/**
 * One moment in a key's life ({@link KeyMoment}), lifted into agent vocabulary.
 * The footprintjs fields are carried through UNCHANGED — this type only ADDS.
 */
export interface AgentKeyMoment {
    readonly kind: 'write' | 'read';
    /** Commit ARRAY position — the write's own, or the READING stage's. */
    readonly commitIdx: number;
    readonly runtimeStageId: string;
    readonly stageId: string;
    readonly stageName: string;
    /** `'write'` moments only: the trace verb. */
    readonly verb?: KeyMoment['verb'];
    /** `'read'` moments only: the commitIdx of the write whose value it saw. */
    readonly fromWriteIdx?: number;
    /**
     * The ReAct loop this moment happened in. ABSENT when the step is not in any
     * frame (run prelude, root-seeded setup) — absence is information, not a gap.
     */
    readonly loopIndex?: number;
    /**
     * `'write'` moments the classifier recognized: what KIND of agent source this
     * write produced. When one write produced several sources (an injection slot
     * commits an array of records), this is the FIRST one's kind and
     * {@link AgentVariableSlice.ablations} carries them all.
     */
    readonly suspectKind?: SuspectKind;
    /** The suspect identity (`injectionId` / `toolName`) — joins a `Suspect` 1:1. */
    readonly suspectId?: string;
}
/**
 * The counterfactual attached to one classifiable WRITE: "to test this write,
 * re-run without this source". A HOOK, not a verdict — running it is the
 * consumer's `AblationRunner` (§B2: only ablation verdicts are causal claims).
 */
export interface VariableAblationHook {
    /** runtimeStageId of the write that introduced the source. */
    readonly writerId: string;
    /** `injectionId` / `toolName` — the localizer's suspect identity. */
    readonly suspectId: string;
    readonly kind: SuspectKind;
    /** What to remove for the counterfactual re-run. */
    readonly spec: AblationSpec;
}
/**
 * One variable's recorded life, in agent vocabulary — the artifact BOTH a human
 * board ({@link variableToBacktrackTrace}) and the walk read.
 *
 * JSON-safe by construction (flat moments, no graph, no live references): the
 * same object can be persisted, sent over the wire, or handed to an LLM tool.
 */
export interface AgentVariableSlice {
    /** The state key asked about (footprintjs's normalised string form). */
    readonly key: string;
    /** Writes and reads in commit order. EMPTY when `missing`. */
    readonly moments: readonly AgentKeyMoment[];
    /** Present ONLY when there are no moments — footprintjs's honest absence. */
    readonly missing?: MissingSliceReason;
    /** How exactly this log records what the value fed — see {@link DataflowCoverage}. */
    readonly coverage: DataflowCoverage;
    /** One per classifiable write — see {@link VariableAblationHook}. */
    readonly ablations: readonly VariableAblationHook[];
    /** footprintjs's honesty notes, VERBATIM (both doors' notes, de-duplicated). */
    readonly notes: readonly HonestyNote[];
    /** Which `KeysReadSource` strategy resolved reads (honesty/debug). */
    readonly keysReadKind: string;
    /** Reads telemetry — `stepsWithReads === 0` means reads were not recorded. */
    readonly readsCoverage?: ReadsCoverage;
}
export interface JoinVariableSliceOptions {
    /**
     * The FORWARD slice of the same key (`forwardSliceForKey`) — the ONLY carrier
     * of per-edge exactness (`ForwardEdge.basis`). Without it `coverage` is
     * `'unknown'` and the walk will not take a dataflow hop.
     */
    readonly forward?: ForwardSlice;
    /** Override the suspect classifier (default `defaultSuspectClassifier`). */
    readonly classify?: SuspectClassifier;
}
export interface TraceVariableOptions extends JoinVariableSliceOptions {
    /** Reuse an already-assembled trajectory (avoids re-slicing the run). */
    readonly trajectory?: Trajectory;
    /** Exclusive commit-array-index bound — the variable as it stood BEFORE this idx. */
    readonly before?: number;
}
/**
 * Join a footprintjs variable slice to the agent's loops and sources.
 *
 * @param slice A `keyTimeline(...)` result — the flat, JSON-safe life of one
 *   key. (Chosen over the backward `sliceForKey` deliberately: the walk needs
 *   moments in commit order, and a timeline cannot be mistaken for a DAG.)
 * @param trajectory `assembleTrajectory(artifacts)` — supplies loop frames AND
 *   the committed values used for classification.
 * @param opts `forward` (the exactness carrier) and `classify`.
 *
 * Nothing here is measured: every field is a re-label of something already
 * recorded. See the module doc.
 */
export declare function joinVariableSlice(slice: KeyTimeline, trajectory: Trajectory, opts?: JoinVariableSliceOptions): AgentVariableSlice;
/**
 * One call, from a recorded run to a variable's joined life: `keyTimeline` +
 * `forwardSliceForKey` + `assembleTrajectory` + {@link joinVariableSlice}.
 *
 * Reads come from the snapshot's execution tree (zero setup, `readTracking` ≠
 * `'off'`); exactness comes from the run's `writeProvenance` dial. Both facts
 * ride out on the result (`readsCoverage`, `coverage`) rather than being
 * assumed.
 *
 * @example
 * ```ts
 * const life = traceVariable({ snapshot }, 'systemPromptInjections');
 * life.coverage;                    // 'exact' | 'conservative' | 'unknown'
 * life.moments.filter((m) => m.kind === 'write').map((m) => m.loopIndex);
 * ```
 */
export declare function traceVariable(artifacts: ContextBugArtifacts, key: StateKey, opts?: TraceVariableOptions): AgentVariableSlice;
