/**
 * `tangle.rollout.v1` — THE canonical rollout serialization, owned by
 * agent-eval. One JSONL line per agent invocation (a solo eval run, a
 * supervisor episode, a worker session, a proposer shot, a judge call, an
 * analyst pass), labeled with its task/split coordinates and a single
 * scalar reward, carrying the FULL message transcript inline.
 *
 * This schema is the reconciliation of two prior producers:
 *   - agent-eval's RunRecord-joined rollout rows (PR #410): identity,
 *     provenance hashes, the realness gate travelling into the reward,
 *     trace-derived steps.
 *   - the bench rollout-ledger (agent-runtime PR #591): the wire shape —
 *     role, task.split/rep, parent_rollout_id, policy provenance, capture
 *     provenance, inline canonical chat-with-tools messages.
 * Where the two conflicted, RunRecord-derived semantics won; the wire
 * field names follow the ledger (snake_case). See `docs/rollout.md` for
 * the field-by-field decision table.
 *
 * Messages are inlined — never referenced — because every harness store a
 * rollout can be recovered from is mutable or garbage-collected. A line
 * must stay a complete training/eval example on its own.
 *
 * `outcome.reward` is THE single scalar (null = no verdict exists — a
 * labeled gap, never 0). `outcome.realness_gated` is the anti-Goodhart
 * flag: a gated line must never export as a positive training example.
 */
declare const ROLLOUT_SCHEMA = "tangle.rollout.v1";
/** `agent` = a solo evaluation run (no multi-agent topology). */
type RolloutRole = 'agent' | 'supervisor' | 'worker' | 'proposer' | 'judge' | 'analyst';
/**
 * Split vocabulary follows `RunRecord.splitTag` ('search' is the pool the
 * optimizer may read — the trainable split), extended with the ledger's
 * 'canary'. 'train' is a legacy alias for 'search' emitted by
 * pre-unification ledgers; it validates and counts as trainable, but new
 * producers must emit 'search'.
 */
type RolloutSplit = 'search' | 'dev' | 'holdout' | 'canary' | 'train';
/** 'mint' = joined live from RunRecord + trace by `mintRolloutRows`. */
type RolloutCapture = 'mint' | 'settle-time' | 'backfill';
type ChatRole = 'system' | 'user' | 'assistant' | 'tool';
interface ChatToolCall {
    id: string;
    type: 'function';
    function: {
        name: string;
        /** JSON-encoded argument object, exactly as the model emitted it. */
        arguments: string;
    };
}
interface ChatMessage {
    role: ChatRole;
    content: string | null;
    /** Reasoning/thinking channel where the harness captured it (full fidelity). */
    reasoning_content?: string;
    tool_calls?: ChatToolCall[];
    /** Required on role:"tool" — the ChatToolCall this result answers. */
    tool_call_id?: string;
    name?: string;
}
interface ToolDef {
    type: 'function';
    function: {
        name: string;
        description?: string;
        parameters?: Record<string, unknown>;
    };
}
/**
 * Compact trace-span projection (llm/tool step) carried alongside the
 * conversation when the line was minted from a trace. Optional: lines
 * recovered from harness stores have no span structure.
 */
interface RolloutStep {
    kind: string;
    name: string;
    /** llm: last-message summary · tool: stringified args. Scrubbed. */
    input?: string;
    /** llm: output text · tool: stringified result. Scrubbed. */
    output?: string;
    status?: 'ok' | 'error';
    durationMs?: number;
}
interface RolloutTask {
    /** Benchmark/suite id (e.g. "swe-bench-verified") or the experiment id. */
    suite: string;
    instance_id: string;
    split: RolloutSplit;
    /** Sampling seed the campaign pinned; null = not recorded. */
    seed: number | null;
    /** Replicate index (0-based). */
    rep: number;
}
interface RolloutPolicy {
    /** Harness that drove the invocation (e.g. "opencode", "claude", "pi-loops"). */
    harness: string | null;
    harness_version: string | null;
    model: string | null;
    provider: string | null;
    /** Commit of the agent profile / candidate under evaluation. */
    profile_commit: string | null;
    /** sha256 of the effective prompt (post-steering), when recorded. */
    prompt_hash?: string | null;
    /** sha256 of the effective run config, when recorded. */
    config_hash?: string | null;
    /** Canonical agent-profile cell identity, when the run carries one. */
    agent_profile_cell_id?: string | null;
    /** Sampling params (temperature, top_p, max_tokens…); null = not recorded. */
    sampling: Record<string, unknown> | null;
}
interface RolloutOutcome {
    /**
     * THE single scalar training signal — the official verdict.
     * null = no verdict exists for this invocation (a labeled gap, never 0).
     */
    reward: number | null;
    /** Where the reward came from (judge id; "/inherited" = parent episode's). */
    reward_source: string | null;
    /** Raw judge verdict record, verbatim. */
    verdict: unknown;
    /** Everything that is NOT the scalar reward. */
    metrics: Record<string, unknown>;
    is_completed: boolean;
    is_truncated: boolean;
    error: string | null;
    /**
     * Anti-Goodhart flag from `RunRecord.outcome.realness.gated`: the run
     * faked its success signal. Reward is forced to 0 at mint time and the
     * line never qualifies for SFT. Optional on the wire (absent = false)
     * so pre-unification ledgers stay readable.
     */
    realness_gated?: boolean;
}
interface RolloutCostBlock {
    usd: number | null;
    tokens_in: number | null;
    tokens_out: number | null;
    tokens_reasoning: number | null;
    cache_read: number | null;
    cache_write: number | null;
    wall_s: number | null;
}
interface RolloutArtifacts {
    patch_path: string | null;
    run_dir: string | null;
    /** Source-of-truth transcript pointer (session id / jsonl path) for audit. */
    transcript_ref: string | null;
}
interface RolloutProvenance {
    captured_at: string;
    capture: RolloutCapture;
    /** Present on gap lines: why `messages` could not be recovered. */
    gap?: string;
}
interface RolloutLine {
    schema: typeof ROLLOUT_SCHEMA;
    rollout_id: string;
    /** Spawning invocation within the same episode (worker → supervisor). */
    parent_rollout_id: string | null;
    run_id: string;
    /** Logical experiment grouping from `RunRecord.experimentId`. Optional on
     *  the wire (pre-unification ledgers lack it); null = not recorded. */
    experiment_id?: string | null;
    /** Stable candidate identity from `RunRecord.candidateId`; null = not recorded. */
    candidate_id?: string | null;
    /** Improvement-loop generation (-1 = baseline); null = not an improvement loop. */
    generation: number | null;
    /** Improvement-loop candidate index (-1 = baseline); null = not an improvement loop. */
    candidate_index: number | null;
    role: RolloutRole;
    task: RolloutTask;
    policy: RolloutPolicy;
    /** Full transcript, inline. [] = gap line (see provenance.gap). */
    messages: ChatMessage[];
    tool_defs: ToolDef[];
    /** Trace-span projections, when minted from a trace. */
    steps?: RolloutStep[];
    outcome: RolloutOutcome;
    cost: RolloutCostBlock;
    artifacts: RolloutArtifacts;
    provenance: RolloutProvenance;
}

/**
 * Supervisor-run analysis — the multi-agent analogue of single-rollout trace
 * analysis. A solo rollout is one invocation with a transcript; a supervisor
 * run is a TREE of invocations (a brain that spawns, steers, and settles
 * workers) plus the event timeline that connects them. `src/trace-analyst`
 * answers "what happened inside one session"; this module answers "what did
 * the tree do" — did the brain steer anyone mid-task, how many spawn waves,
 * how concurrent, how idle, what did each role cost, what came back.
 *
 * The nodes of that tree are NOT a new shape: they are `tangle.rollout.v1`
 * rows (`src/rollout`), keyed by `parent_rollout_id`, with `role` already
 * spanning `supervisor` / `worker`. `supervisorRunRolloutLines` mints them.
 * What rollout rows deliberately do NOT carry is the inter-invocation event
 * timeline (spawn/settle/steer instants), which is what every structural
 * metric here is computed from — so the reader consumes the journal event
 * stream and emits rollout rows, rather than maintaining a parallel node type.
 *
 * ## UNAVAILABLE ≠ ZERO
 *
 * Every metric whose backing artifact can be missing is typed
 * `Measured<T> = T | { unavailable: reason }`. A supervisor that steered
 * nobody reports `steers: 0`; a supervisor whose worker logs were never
 * written reports `steers: unavailable — <reason>`. The two have driven
 * opposite conclusions about the same architecture, so they never collapse.
 */

/** A metric that could not be computed, with the reason its artifact was missing. */
interface Unavailable {
    readonly unavailable: string;
}
/** A metric value, or the reason it is unknown. NEVER collapse `unavailable` to 0. */
type Measured<T> = T | Unavailable;
declare function unavailable(reason: string): Unavailable;
declare function isUnavailable(v: unknown): v is Unavailable;
/** Render a measured scalar for the markdown/headline: `0` and `unavailable` stay distinct. */
declare function showMeasured(v: Measured<number | string | boolean | null>): string;
/** One worker's logs, as read. `null` = the artifact did not exist. */
interface WorkerLogSource {
    readonly label: string;
    /** Worker event stream — started / progress / finished / message events (JSONL). */
    readonly events: string | null;
    /** The durable steer queue — one line per steer request (JSONL). */
    readonly inbox: string | null;
    /** Worker patch byte length, or null when absent. */
    readonly patchBytes: number | null;
    /** Where this worker's transcript lives, for the rollout row. Null = no such artifact. */
    readonly transcriptRef?: string | null;
    /** Where this worker's delivered patch lives. Null = the store keeps no patch per worker. */
    readonly patchPath?: string | null;
    /** This worker's own inference tokens, when the store records them per worker. */
    readonly tokensIn?: number | null;
    readonly tokensOut?: number | null;
    readonly cacheRead?: number | null;
    readonly cacheWrite?: number | null;
}
/**
 * Facts a SOURCE structurally cannot express, each with the reason.
 *
 * The difference between "the artifact is missing" and "this store never
 * records that fact" is the difference between a run that spent $0 and a
 * harness that does not price inference — and the second harness is where a
 * loops-shaped assumption becomes a fabricated zero. A reader declares its
 * limits once; the analyzer reports `unavailable` for everything downstream.
 *
 * `null` on a field means the source DOES carry that fact.
 */
interface SourceLimits {
    /** Reason inference spend has no price in this store (null = the store prices it). */
    readonly spendUsd: string | null;
    /** Reason workers carry no pass/fail verdict (null = verdicts are recorded). */
    readonly workerVerdicts: string | null;
    /** Reason no delivered artifact (patch/diff) is retained per worker (null = retained). */
    readonly deliverables: string | null;
}
/** A source that carries every fact the analyzer can use. */
declare const NO_SOURCE_LIMITS: SourceLimits;
/**
 * Everything the pure analyzer reads — already-read bytes, never paths. Each
 * field is `null` when its artifact was absent, which is what turns the
 * dependent metrics into `unavailable` rather than 0.
 *
 * This is the whole input contract. Any store that can produce these strings
 * (an on-disk loops run, an object-store archive, a database, a test fixture)
 * is a valid source; `loopsSupervisorRunReader` is ONE implementation.
 */
interface SupervisorRunSources {
    /** Stable identity of the run being analyzed (a directory, a run id, a URL). */
    readonly runRef: string;
    readonly instanceId: string | null;
    /** Which arm/variant of a comparison this run is, when the run belongs to one. */
    readonly arm: string | null;
    /** Identity of the supervision-tree store this was read from; null = none found. */
    readonly supRunDir: string | null;
    /** Supervision journal — spawned / settled / cancelled / metered events (JSONL). */
    readonly journal: string | null;
    /** Per-brain-call tap (JSONL): finish_reason, completion tokens, requested max tokens. */
    readonly brainLog: string | null;
    /** Supervisor state document (JSON). */
    readonly state: string | null;
    /** Supervisor progress stream (JSONL). */
    readonly progress: string | null;
    /** Per-worker logs; `null` = the worker log store itself was missing. */
    readonly workers: readonly WorkerLogSource[] | null;
    /** Why `workers` is null (only set when it is). */
    readonly workersMissingReason: string | null;
    /** Run result document (JSON). */
    readonly result: string | null;
    /**
     * Judge verdict document (JSON), or the matching ledger row re-encoded as
     * one. Runners that write the verdict straight to a ledger leave no judge
     * document, so the ledger row is the same fact from the same run — not a
     * substitute measurement.
     */
    readonly judge: string | null;
    /** Where `judge` came from, for the report's provenance line. */
    readonly judgeSource: string | null;
    /** Delivered unified-diff patch text. */
    readonly patch: string | null;
    /** Outer-driver log (used for the driver's steer verbs + deadline evidence). */
    readonly driverLog: string | null;
    /**
     * Worker tokens recovered from a harness session store; null = store unavailable.
     * `store` names the store in the report's provenance line (e.g. `opencode`).
     */
    readonly harnessWorkerTokens: {
        store: string;
        sessions: number;
        input: number;
        output: number;
        /** Cached prompt tokens, when the store counts them separately. */
        cacheRead?: number;
        cacheWrite?: number;
    } | null;
    readonly harnessMissingReason: string | null;
    /** What this store structurally cannot record. See `SourceLimits`. */
    readonly limits: SourceLimits;
    /**
     * Where the ROOT invocation's transcript lives. Undefined lets the rollout
     * minter fall back to the loops layout (`<supRunDir>/journal.jsonl`); any
     * other store must say, or the row points at a path that never existed.
     */
    readonly rootTranscriptRef?: string | null;
    /**
     * The `traces` CLI command that covers this run's harness-session layer.
     * Null falls back to the analyzer's default (an opencode worker fleet).
     */
    readonly traceCommand: string | null;
}
/**
 * A source of supervisor-run bytes. Implementations own their storage layout;
 * the analyzer only ever sees `SupervisorRunSources`.
 */
interface SupervisorRunReader {
    /** Stable identity of what this reader points at (for logs and report labels). */
    readonly runRef: string;
    read(): Promise<SupervisorRunSources>;
}
declare const SUPERVISOR_RUN_SCHEMA = "tangle.supervisor-run@1";
declare const SUPERVISOR_RUN_ROLLUP_SCHEMA = "tangle.supervisor-run-rollup@1";
interface SteerBreakdown {
    readonly worker: string;
    /** Steer requests durably queued to this worker's inbox. */
    readonly queued: number;
    /** Steers the worker's executor actually accepted (control event `delivered:true`). */
    readonly delivered: number;
}
interface OrchestrationMetrics {
    readonly workersSpawned: Measured<number>;
    readonly workersSettled: Measured<number>;
    readonly workersCancelled: Measured<number>;
    /** THE HEADLINE: mid-task steers the brain sent to live workers. 0 ≠ unavailable. */
    readonly steers: Measured<number>;
    readonly steersDelivered: Measured<number>;
    readonly steersByWorker: Measured<readonly SteerBreakdown[]>;
    /** Outer-driver `supervisor_steer` tool calls seen in the driver log (a second steer path). */
    readonly driverSteerCalls: Measured<number>;
    /**
     * Spawn waves. A wave is a maximal run of worker spawns with no settle/cancel between
     * them: wave N+1 begins at the first spawn issued after at least one worker from an
     * earlier wave has settled. Structural, not a time threshold — no tunable constant.
     */
    readonly waves: Measured<number>;
    readonly waveSizes: Measured<readonly number[]>;
    readonly maxConcurrency: Measured<number>;
    /** Worker spawns issued after the first settlement — the retry/respawn tail. */
    readonly respawns: Measured<number>;
    /** Labels spawned more than once (a literal retry of the same subtask). */
    readonly repeatedLabels: Measured<readonly string[]>;
    /** Longest parent chain below the root, in worker hops. */
    readonly delegationDepth: Measured<number>;
    readonly timeToFirstSpawnMs: Measured<number>;
    readonly supervisorWallMs: Measured<number>;
    /** Wall time inside the supervisor run with ZERO live workers. */
    readonly idleMs: Measured<number>;
    readonly idlePct: Measured<number>;
    /** sum(worker wall) / supervisor wall. >1 means real parallelism. */
    readonly workerUtilization: Measured<number>;
}
interface DecisionMetrics {
    readonly settledByStatus: Measured<Record<string, number>>;
    readonly settledVerdicts: Measured<Record<string, number>>;
    /** Worker verified its own work green AND produced a patch. */
    readonly accepted: Measured<number>;
    /** Worker settled with a failing verify. */
    readonly rejected: Measured<number>;
    /** Worker verified green but delivered no patch bytes — output with nothing to accept. */
    readonly emptyPass: Measured<number>;
    /** Settlements the brain observed before issuing its next spawn (evidence→respawn). */
    readonly observeThenRespawn: Measured<number>;
    /** Respawns with no settled evidence in front of them. */
    readonly respawnWithoutEvidence: Measured<number>;
    /** Steer + question traffic on the live down/up legs — the only "review while running" signal. */
    readonly reviewActions: Measured<number>;
    readonly workerEvidenceBytes: Measured<number>;
}
interface RoleSpend {
    readonly tokensIn: Measured<number>;
    readonly tokensOut: Measured<number>;
    /**
     * Cached prompt tokens read/written. On a harness that caches aggressively
     * these dwarf `tokensIn`, so a report that omits them understates the context
     * each invocation actually consumed. `unavailable` = the store has no such counter.
     */
    readonly cacheRead: Measured<number>;
    readonly cacheWrite: Measured<number>;
    readonly usd: Measured<number>;
    readonly source: string;
}
interface PerWorkerRow {
    readonly worker: string;
    readonly wallMs: number | null;
    /** `null` = this store does not attribute tokens per worker (NOT "zero tokens"). */
    readonly tokensIn: number | null;
    readonly tokensOut: number | null;
    readonly usd: number | null;
    readonly patchBytes: number | null;
    readonly passed: boolean | null;
}
interface WallDistribution {
    readonly n: number;
    readonly min: number;
    readonly p50: number;
    readonly p90: number;
    readonly max: number;
    readonly sum: number;
}
interface EconomicsMetrics {
    /** Driver/brain inference — journal `metered` events. */
    readonly brain: RoleSpend;
    /**
     * Brain completions that came back `finish_reason: "length"` — output TRUNCATED. Any value
     * above 0 means the supervisor planned into a wall and then acted on the half-written plan,
     * which is a defect and not a cost figure. The journal's `metered` rows carry token counts
     * but no finish reason, so this reads the per-call brain tap; a run whose supervisor
     * predates that tap reports `unavailable`, never 0.
     */
    readonly brainTruncations: Measured<number>;
    /** Worker inference — journal `settled` spend plus the harness session join. */
    readonly workers: RoleSpend;
    readonly totalUsd: Measured<number>;
    /**
     * Where `totalUsd` came from. CLI-backend workers never price their own inference into
     * the journal, so on those arms the total is BRAIN-ONLY and the worker row's token
     * counts (recovered from the harness store) are the honest worker-side figure.
     */
    readonly totalUsdSource: string;
    readonly costPerAcceptedPatchUsd: Measured<number>;
    readonly workerWallMsDistribution: Measured<WallDistribution>;
    readonly perWorker: Measured<readonly PerWorkerRow[]>;
}
interface PatchStats {
    readonly files: number;
    readonly linesAdded: number;
    readonly linesRemoved: number;
    readonly testFilesTouched: readonly string[];
}
interface OutcomeMetrics {
    readonly supStatus: Measured<string>;
    readonly supVerdict: Measured<string>;
    readonly delivered: Measured<boolean>;
    readonly judgeResolved: Measured<boolean | null>;
    readonly judgeScore: Measured<number | null>;
    readonly judgePassed: Measured<number | null>;
    readonly judgeTotal: Measured<number | null>;
    readonly verifyPass: Measured<boolean>;
    readonly verifyRc: Measured<number>;
    readonly patch: Measured<PatchStats>;
    /** Which document the judge fields came from (a judge file, a ledger row, or nothing). */
    readonly judgeSource: string | null;
}
interface SupervisorRunReport {
    readonly schema: typeof SUPERVISOR_RUN_SCHEMA;
    /** The `runRef` of the sources this report was computed from. */
    readonly runRef: string;
    readonly instanceId: string | null;
    readonly arm: string | null;
    readonly supervisorId: Measured<string>;
    readonly generatedAt: string;
    readonly orchestration: OrchestrationMetrics;
    readonly decision: DecisionMetrics;
    readonly economics: EconomicsMetrics;
    readonly outcome: OutcomeMetrics;
    /** Artifacts that were missing, in read order — the provenance of every `unavailable`. */
    readonly gaps: readonly string[];
    /** The `traces` CLI command that covers the harness-session layer for this run. */
    readonly traceCommand: string;
}
interface RollupCellRow {
    readonly instanceId: string | null;
    readonly arm: string | null;
    readonly steers: Measured<number>;
    readonly waves: Measured<number>;
    readonly utilization: Measured<number>;
    readonly idlePct: Measured<number>;
    readonly resolved: Measured<boolean | null>;
    readonly usd: Measured<number>;
}
interface SupervisorRunRollup {
    readonly schema: typeof SUPERVISOR_RUN_ROLLUP_SCHEMA;
    readonly cells: number;
    readonly steersTotal: Measured<number>;
    readonly cellsWithSteers: Measured<number>;
    readonly cellsWithUnavailableSteers: number;
    readonly wavesMean: Measured<number>;
    readonly maxConcurrencyMax: Measured<number>;
    readonly utilizationMean: Measured<number>;
    readonly idlePctMean: Measured<number>;
    readonly workersSpawnedTotal: Measured<number>;
    readonly acceptedTotal: Measured<number>;
    readonly usdTotal: Measured<number>;
    readonly resolvedCount: Measured<number>;
    readonly perCell: readonly RollupCellRow[];
}
/**
 * A supervision tree expressed in the canonical rollout row type: one
 * `RolloutLine` per invocation, joined by `parent_rollout_id`. The root row
 * carries `role: 'supervisor'`; every spawned worker carries `role: 'worker'`
 * with the root as its parent.
 */
interface SupervisorRunTree {
    readonly rootId: string | null;
    readonly nodes: readonly RolloutLine[];
    /** Why a node could not be recovered, in read order. */
    readonly gaps: readonly string[];
}

/**
 * The pure analyzer. Takes already-read bytes (`SupervisorRunSources`) and
 * returns the report — every metric derivable from a synthetic journal string
 * with no filesystem, no process, and no network. All I/O lives in a reader
 * (`loops-reader.ts` is one).
 */

interface Tokens {
    input: number;
    output: number;
    cacheRead: number;
    cacheWrite: number;
    /** False when the event carried no cache counters at all — not "zero cached". */
    hasCache: boolean;
}
interface SpendLike {
    tokens: Tokens;
    usd: number;
}
interface SpawnRow {
    id: string;
    parent: string | null;
    label: string;
    at: number | null;
}
interface CloseRow {
    id: string;
    kind: 'settled' | 'cancelled';
    status: string | null;
    verdict: string | null;
    at: number | null;
    spend: SpendLike;
    /** False when the close event carried no spend object — not "spent nothing". */
    hasSpend: boolean;
}
interface WorkerLogFacts {
    started: number | null;
    /** True once a `finished` event was seen — independent of whether its `at` parsed. */
    finished: boolean;
    finishedAt: number | null;
    passed: boolean | null;
    /** `patchBytes` as reported by the finished event (not the patch file's size). */
    finishedPatchBytes: number | null;
    evidenceBytes: number;
    steersQueued: number;
    steersDelivered: number;
    questions: number;
}
/**
 * The tree + timeline the report is computed from, exposed because the rollout-row
 * minter needs exactly the same parse (one parser, two consumers).
 */
interface SupervisorTreeFacts {
    readonly rootId: string | null;
    readonly spawns: readonly SpawnRow[];
    readonly closes: readonly CloseRow[];
    readonly workerSpawns: readonly SpawnRow[];
    readonly workerCloses: readonly CloseRow[];
    readonly brain: {
        tokensIn: number;
        tokensOut: number;
        cacheRead: number;
        cacheWrite: number;
        /** False when no metered event carried cache counters — not "nothing cached". */
        hasCache: boolean;
        usd: number;
        meteredCount: number;
    };
    readonly workerLogs: ReadonlyMap<string, WorkerLogFacts>;
    readonly startedAt: number | null;
    readonly completedAt: number | null;
}
declare function parseSupervisorTree(src: SupervisorRunSources): SupervisorTreeFacts;
/**
 * Analyze already-read supervisor-run bytes. Pure and synchronous: same bytes
 * in, same report out (modulo `generatedAt`, which `now` pins in tests).
 */
declare function analyzeSupervisorRunSources(src: SupervisorRunSources, now?: () => number): SupervisorRunReport;
/** Unified-diff stats. Counts `+++ b/<path>` targets, body +/- lines, and test-file touches. */
declare function parsePatch(text: string): PatchStats;
/**
 * Aggregate many supervisor-run reports. A metric no run could measure stays
 * `unavailable` rather than becoming a 0-valued mean, and cells whose steer
 * count was unavailable are counted separately from cells that measured zero.
 */
declare function rollupSupervisorRuns(reports: readonly SupervisorRunReport[]): SupervisorRunRollup;

/**
 * Supervision-tree reader over a THIRD-PARTY harness: Claude Code.
 *
 * `loops-reader.ts` reads a supervisor we wrote, whose journal was designed
 * for this analysis. This reader reads a harness we do not control, whose
 * transcript was designed for replaying a chat — and recovers the same tree
 * from it. If both produce a `SupervisorRunSources`, the tree model is a
 * property of multi-agent runs, not of our journal format.
 *
 * ## Where the tree hides in a Claude Code transcript
 *
 * | Tree fact | Claude Code evidence |
 * |---|---|
 * | spawn      | assistant `tool_use` (`Agent` / `Task`), answered by a `tool_result` whose `toolUseResult.agentId` names the child |
 * | settle     | a `<task-notification>` block in a later user line: `<task-id>` = agentId, `<status>` |
 * | steer      | assistant `tool_use` (`SendMessage`) with `input.to` = agentId — mid-task, to a LIVE child |
 * | delivered  | that steer's `tool_result` carrying `success` / `resumedAgentId` |
 * | cancel     | assistant `tool_use` (`TaskStop`) targeting an agentId |
 * | brain spend| `message.usage` on the main thread's assistant lines |
 * | worker spend| `message.usage` inside `<session>/subagents/agent-<id>.jsonl` |
 * | depth      | a child transcript that itself contains `Agent` tool_use lines |
 *
 * Every one of those is read through `parseClaudeEntries` — the SAME line
 * parser `src/rollout/readers/claude-jsonl.ts` uses for solo rollouts. There
 * is no second transcript parser.
 *
 * ## What Claude Code cannot say
 *
 * It records tokens but never a price, runs no per-worker verify, and keeps no
 * per-worker patch. Those are declared once in `limits`, so the analyzer
 * reports `unavailable — <reason>` instead of the $0 / 0-accepted that summing
 * an empty field would produce. See `SourceLimits`.
 *
 * ## Metric coverage vs the loops journal
 *
 * Measured on a real 52-agent session (fixture:
 * `tests/fixtures/supervisor-run/claude-code-session-*`).
 *
 * | Metric | loops | Claude Code | Why |
 * |---|---|---|---|
 * | workersSpawned / Settled / Cancelled | full | full | spawn tool_use + task-notification + TaskStop |
 * | steers / steersDelivered / steersByWorker | full | full | `SendMessage`; delivery from its tool_result |
 * | waves / waveSizes / maxConcurrency | full | full | derived from spawn/settle instants |
 * | respawns / repeatedLabels | full | full | same derivation |
 * | delegationDepth | full | full | a child transcript's own spawn calls |
 * | timeToFirstSpawn / supervisorWall | full | full | transcript instants |
 * | idleMs / idlePct / workerUtilization | full | PARTIAL | an agent that never notifies is counted live to the end of the transcript |
 * | observeThenRespawn / respawnWithoutEvidence | full | full | ordering of spawn vs settle instants |
 * | workerEvidenceBytes | full | PARTIAL | the child's closing message; 0 for pruned transcripts |
 * | brain tokens in/out + cache | full | full | main-thread `message.usage` |
 * | worker tokens in/out + cache | via harness join | PARTIAL | only for retained subagent transcripts |
 * | perWorker wall | full | full | spawn → settle instants |
 * | accepted / rejected / emptyPass / settledVerdicts | full | NONE | no per-worker verify step exists |
 * | brain/worker/total usd, costPerAcceptedPatch | full | NONE | transcripts carry no price |
 * | patch stats, delivered, verifyPass/Rc | full | NONE | no diff is handed back |
 * | judgeResolved / Score / Passed / Total | full | NONE | no judge in the loop |
 * | driverSteerCalls, brainTruncations | full | NONE | no outer driver log, no per-call finish_reason tap |
 */

/** Tool names that spawn a child agent. `Task` is the older name for `Agent`. */
declare const DEFAULT_SPAWN_TOOLS: readonly ["Agent", "Task"];
/** Tool names that deliver a message to an ALREADY-RUNNING child agent. */
declare const DEFAULT_STEER_TOOLS: readonly ["SendMessage"];
/** Tool names that stop a running child agent. */
declare const DEFAULT_CANCEL_TOOLS: readonly ["TaskStop", "KillAgent"];
interface ClaudeCodeReaderOptions {
    /** The main session transcript: `~/.claude/projects/<slug>/<sessionId>.jsonl`. */
    readonly transcriptPath: string;
    /**
     * Directory of child transcripts. Defaults to `<transcript-dir>/<sessionId>/subagents`.
     * `null` skips the join, and every per-worker token count becomes unavailable.
     */
    readonly subagentsDir?: string | null;
    readonly runRef?: string;
    readonly instanceId?: string | null;
    readonly arm?: string | null;
    readonly spawnTools?: readonly string[];
    readonly steerTools?: readonly string[];
    readonly cancelTools?: readonly string[];
}
/**
 * Read a Claude Code session (plus its subagent transcripts) as supervision-tree
 * source bytes. Never throws on a missing artifact.
 */
declare function readClaudeCodeSupervisorRun(opts: ClaudeCodeReaderOptions): Promise<SupervisorRunSources>;
/** A `SupervisorRunReader` over a Claude Code session — the same contract loops implements. */
declare function claudeCodeSupervisorRunReader(opts: ClaudeCodeReaderOptions): SupervisorRunReader;

/**
 * ONE implementation of `SupervisorRunReader`: the on-disk layout the loops
 * supervisor writes — `<runDir>/ws/.loops/supervisor/<id>/{journal.jsonl,
 * state.json, progress.ndjson, workers/*.ndjson}` alongside the run's
 * `result.json` / `judge.json` / `driver.log` / delivered patch.
 *
 * Nothing in `analyze.ts` knows this layout exists. A different store (an
 * archive, an object bucket, a database) implements the same interface and
 * gets the same report.
 *
 * Worker token recovery reuses the rollout module's opencode reader rather
 * than opening a second sqlite path — one store client, one corruption policy.
 */

/** Locate the (single) supervisor run dir under `<ws>/.loops/supervisor`. */
declare function findSupervisorRunDirIn(ws: string): Promise<string | null>;
interface LoopsReaderOptions {
    /** Override the workspace dir (default `<runDir>/ws`). */
    readonly ws?: string;
    /** Delivered patch path (default: `patchPath` from result.json). */
    readonly patchPath?: string;
    /** opencode sqlite store; set to `null` to skip the worker-token join entirely. */
    readonly opencodeDb?: string | null;
    /** Ledger to fall back to when the run has no `judge.json` (matched on iid + arm + runDir). */
    readonly ledgerPath?: string;
}
/**
 * Read a loops supervisor run directory into source bytes. Never throws on a
 * missing artifact — an absent file becomes a `null` field, which is what makes
 * the dependent metric `unavailable` instead of 0.
 */
declare function readLoopsSupervisorRun(runDir: string, opts?: LoopsReaderOptions): Promise<SupervisorRunSources>;
/** The loops on-disk layout, as a `SupervisorRunReader`. */
declare function loopsSupervisorRunReader(runDir: string, opts?: LoopsReaderOptions): SupervisorRunReader;
/**
 * Analyze a supervisor run. Accepts a run directory (read through the loops
 * reader), any `SupervisorRunReader`, or already-read source bytes — so a
 * caller with its own store never has to touch the filesystem layout.
 */
declare function analyzeSupervisorRun(input: string | SupervisorRunReader | SupervisorRunSources, opts?: LoopsReaderOptions): Promise<SupervisorRunReport>;
interface WriteSupervisorRunOptions extends LoopsReaderOptions {
    /** Append the headline block here (the experiment's run log). */
    readonly appendHeadlineTo?: string;
    /** Also console.log the headline (default true). */
    readonly echo?: boolean;
    /**
     * Write `run-report.{json,md}` here instead of into the run dir. Set when
     * reporting over a run directory that must stay READ-ONLY (a live run, an
     * archived generation).
     */
    readonly reportDir?: string;
}
/**
 * Read a completed run, write `run-report.json` + `run-report.md` beside its
 * artifacts, and append the headline block to the run log. Never throws on a
 * missing artifact — a run that produced nothing still yields a report whose
 * every metric says why.
 */
declare function writeSupervisorRunReport(runDir: string, opts?: WriteSupervisorRunOptions): Promise<SupervisorRunReport>;
/**
 * File stem for out-of-tree reports. Built from the run path's identifying
 * segments — candidate tag (the segment under `arm-runs/`), rep, instance, arm
 * — so two runs of the same instance from different candidates/reps never
 * overwrite each other.
 */
declare function supervisorReportStem(runDir: string): string;
/**
 * Best-effort wrapper for a hot path: a reporting failure must never kill a run
 * that already produced real work. Returns null and logs the reason instead.
 */
declare function writeSupervisorRunReportSafe(runDir: string, opts?: WriteSupervisorRunOptions): Promise<SupervisorRunReport | null>;
/**
 * Report every run under an experiment `outDir` (any depth of
 * `runs/<iid>/<arm>`), write each run's report, and write the rollup at
 * `<outDir>/run-report-round.{json,md}`.
 */
declare function reportSupervisorRound(outDir: string, opts?: WriteSupervisorRunOptions & {
    title?: string;
}): Promise<SupervisorRunRollup>;
/** Every `<...>/runs/<iid>/<arm>` directory under `root`. */
declare function findSupervisorRunDirs(root: string): Promise<string[]>;

/**
 * Human-readable renderings of a supervisor-run report. Zero and unavailable
 * render differently on purpose (`0` vs `unavailable — <reason>`), because the
 * two have driven opposite conclusions about the same architecture.
 */

/**
 * The block appended to a run log after every run — the answers an operator asks
 * for, in the log tail, with no extra command.
 */
declare function renderSupervisorRunHeadline(r: SupervisorRunReport): string;
declare function renderSupervisorRunMarkdown(r: SupervisorRunReport): string;
declare function renderSupervisorRollupMarkdown(rollup: SupervisorRunRollup, title?: string): string;

/**
 * The supervision tree as `tangle.rollout.v1` rows.
 *
 * A supervisor run IS a tree of rollouts, so its nodes are not a new shape:
 * the root becomes one `RolloutLine` with `role: 'supervisor'`, every spawned
 * worker becomes a `RolloutLine` with `role: 'worker'` and
 * `parent_rollout_id` pointing at its spawner. The rows append to the same
 * ledger as solo-agent rollouts and join to them with the same keys.
 *
 * What the journal CANNOT supply is the transcript: a worker's messages live
 * in its harness store (opencode sqlite, Claude Code jsonl), which the
 * `src/rollout/readers/*` intake readers own. Rows minted here are therefore
 * GAP lines (`messages: []`, `provenance.gap` set) carrying identity,
 * structure, outcome and cost; hydrating them with messages is the readers'
 * job, keyed on `artifacts.transcript_ref`.
 *
 * Timing lives in `outcome.metrics` (`spawned_at` / `settled_at` / `wall_ms`)
 * rather than a schema field: `tangle.rollout.v1` describes ONE invocation,
 * and the inter-invocation event timeline — which is what waves, concurrency,
 * idle and utilization are computed from — is a property of the journal, not
 * of any single row. The analyzer reads that timeline; these rows carry the
 * per-node facts.
 */

interface SupervisorRolloutOptions {
    /** Benchmark/suite id for `task.suite`. Defaults to `'supervisor-run'`. */
    readonly suite?: string;
    /** `task.split`. Defaults to `'search'` (the trainable pool). */
    readonly split?: RolloutSplit;
    /** Replicate index. Defaults to 0. */
    readonly rep?: number;
    /** Sampling seed the campaign pinned. Defaults to null (not recorded). */
    readonly seed?: number | null;
    /** `run_id` for every node. Defaults to the supervisor root id, else `runRef`. */
    readonly runId?: string;
    /** Harness that drove the supervisor. */
    readonly supervisorHarness?: string | null;
    /** Harness that drove the workers. */
    readonly workerHarness?: string | null;
    /** Model the supervisor ran on. */
    readonly supervisorModel?: string | null;
    /** Model the workers ran on. */
    readonly workerModel?: string | null;
    readonly experimentId?: string | null;
    readonly candidateId?: string | null;
    readonly generation?: number | null;
    readonly candidateIndex?: number | null;
    /** Pins `provenance.captured_at`; defaults to now. */
    readonly capturedAt?: string;
}
/**
 * Mint the supervision tree as rollout rows. Returns the rows plus the gaps
 * that made any of them incomplete — same unavailable-vs-zero discipline as
 * the report: a row with no transcript says WHY, it never pretends to be empty.
 */
declare function supervisorRunRolloutLines(src: SupervisorRunSources, opts?: SupervisorRolloutOptions): SupervisorRunTree;

export { type ClaudeCodeReaderOptions, type CloseRow, DEFAULT_CANCEL_TOOLS, DEFAULT_SPAWN_TOOLS, DEFAULT_STEER_TOOLS, type DecisionMetrics, type EconomicsMetrics, type LoopsReaderOptions, type Measured, NO_SOURCE_LIMITS, type OrchestrationMetrics, type OutcomeMetrics, type PatchStats, type PerWorkerRow, type RoleSpend, type RollupCellRow, SUPERVISOR_RUN_ROLLUP_SCHEMA, SUPERVISOR_RUN_SCHEMA, type SourceLimits, type SpawnRow, type SteerBreakdown, type SupervisorRolloutOptions, type SupervisorRunReader, type SupervisorRunReport, type SupervisorRunRollup, type SupervisorRunSources, type SupervisorRunTree, type SupervisorTreeFacts, type Unavailable, type WallDistribution, type WorkerLogFacts, type WorkerLogSource, type WriteSupervisorRunOptions, analyzeSupervisorRun, analyzeSupervisorRunSources, claudeCodeSupervisorRunReader, findSupervisorRunDirIn, findSupervisorRunDirs, isUnavailable, loopsSupervisorRunReader, parsePatch, parseSupervisorTree, readClaudeCodeSupervisorRun, readLoopsSupervisorRun, renderSupervisorRollupMarkdown, renderSupervisorRunHeadline, renderSupervisorRunMarkdown, reportSupervisorRound, rollupSupervisorRuns, showMeasured, supervisorReportStem, supervisorRunRolloutLines, unavailable, writeSupervisorRunReport, writeSupervisorRunReportSafe };
