/**
 * Application context — the dependency carrier for all pipeline operations.
 *
 * Passed to every pipeline step, domain service, and adapter that needs
 * access to infrastructure. Replaces process.env as the state-passing
 * mechanism.
 *
 * Created once per CLI invocation by createAppContext() (composition root).
 * Created per-test by createTestContext().
 *
 * Fields marked optional are transitional — they will become required
 * as downstream consumers are converted to use them.
 */
import type { EventType, LiteracyVariant, RunClassification, RunExecutorSurface } from "../../ailf-shared/index.d.ts";
import type { RunId } from "../types/branded-ids.js";
import type { GeneralizedDocRef } from "../types/generalized-task.js";
import type { DebugOptions, EvalMode, PluginRegistry } from "../types/index.js";
import type { RepoProvenanceConfig } from "../types/repo-config.js";
import type { ArtifactWriter } from "./artifact-writer.js";
import type { CacheStore } from "./cache-store.js";
import type { DocFetcher } from "./doc-fetcher.js";
import type { EvalRunner } from "./eval-runner.js";
import type { LLMClient } from "./llm-client.js";
import type { Logger } from "./logger.js";
import type { NotificationServicePort } from "./notification.js";
import type { PackageSurfaceResolver } from "./package-surface-resolver.js";
import type { ProgressReporter } from "./progress-reporter.js";
import type { TaskSource } from "./task-source.js";
/**
 * Resolved pipeline configuration — the typed, validated result of
 * merging CLI flags, env vars, config files, and defaults.
 *
 * This replaces the ~25 process.env variables that applyEnvironment()
 * currently sets. Fields are added incrementally as steps are converted.
 */
export interface ResolvedConfig {
    /** Eval package root directory */
    rootDir: string;
    /** Evaluation mode — canonical name (e.g., "literacy", "knowledge-probe") */
    mode: EvalMode;
    /**
     * Literacy variant — only meaningful when mode is "literacy".
     *
     * When a user passes `--mode baseline`, the CLI normalizes this to
     * `mode: "literacy", variant: "baseline"`. This keeps the pipeline
     * mode-agnostic while preserving literacy's multi-variant behavior.
     *
     * Undefined means "use the default variant for the mode" (baseline for literacy).
     */
    variant?: LiteracyVariant;
    /** Debug options */
    debug?: DebugOptions;
    /**
     * Filter the evaluated cohort to a subset of the configured model IDs.
     *
     * Each entry must match the `id` of a model declared in
     * `config/models.ts`. Unknown IDs are dropped at the runner with a
     * structured warning AND surfaced on the job's `error` field so callers
     * can detect typos — silent strips are not acceptable.
     */
    models?: string[];
    /** Feature area filter */
    areas?: string[];
    /** Task ID filter */
    tasks?: string[];
    /** Tag filter — tasks must have at least one matching tag */
    tags?: string[];
    /** Changed doc slugs for impact scoping */
    changedDocs?: string[];
    /** Doc refs to append to every in-scope task's context (doc-impact eval) */
    injectContextDocs?: GeneralizedDocRef[];
    /** Documentation source name */
    source?: string;
    /** Whether to skip the fetch-docs step */
    skipFetch: boolean;
    /** Whether to skip the eval step */
    skipEval: boolean;
    /** Whether comparison is enabled */
    compareEnabled: boolean;
    /** Comparison noise threshold */
    compareThreshold?: number;
    /** Comparison baseline path */
    compareBaseline?: string;
    /**
     * Comparison baseline expressed as a previously-published
     * `ailf.report` document id. Takes precedence over `compareBaseline`
     * when both are set.
     */
    compareBaselineReportId?: string;
    /** Whether gap analysis is enabled */
    gapAnalysisEnabled: boolean;
    /** Whether publishing is enabled */
    publishEnabled: boolean;
    /** Publish tag */
    publishTag?: string;
    /** Whether to disable local cache */
    noCache: boolean;
    /** Whether to disable remote (Content Lake) cache */
    noRemoteCache: boolean;
    /** Grader replications for consistency measurement */
    graderReplications?: number;
    /**
     * Replications per borderline judgment for the GRAD-04 intra-grader
     * consensus pass (re-grade scores within ±5 of the severity boundaries
     * 30/50/60). Sourced from `RepoConfig.execution.borderlineReplications`
     * or the equivalent CLI/env surface. When unset, the runner factory
     * falls back to its built-in default (3 replications).
     */
    borderlineReplications?: number;
    /**
     * Grader context policy. Controls whether canonical reference docs are
     * injected into the grader's assertion `rubricPrompt`. `"rubric-only"`
     * sends only the rubric template + criteria; `"with-docs"` adds the
     * canonical doc content as authoritative ground truth.
     *
     * Sourced from EvalConfig `grader.context` or the equivalent CLI/env
     * surface. Defaults to `"with-docs"` at the EvalConfig boundary
     * (flipped in W0200 after DOC-2117 validation).
     */
    graderContext?: "rubric-only" | "with-docs";
    /** Base directory for user-facing pipeline output artifacts. */
    outputDir: string;
    /** Output path override */
    outputPath?: string;
    /** Doc source URL overrides */
    urls?: string[];
    /** Custom headers for doc fetching */
    headers?: Record<string, string>;
    /** Allowed origins for agentic mode */
    allowedOrigins?: string[];
    /** Search mode for agentic mode */
    searchMode: "off" | "open" | "origin-only";
    /** Eval concurrency */
    concurrency?: number;
    /**
     * Maximum wall-clock time per eval step in ms.
     * When exceeded, the subprocess is killed and partial results are used.
     * Sourced from models config `evalBudgetMs`.
     */
    evalBudgetMs?: number;
    /** Promptfoo URL from eval output */
    promptfooUrl?: string;
    /** Sanity dataset override */
    datasetOverride?: string;
    /** Sanity project ID override */
    projectIdOverride?: string;
    /** Sanity perspective override */
    perspectiveOverride?: string;
    /** Sanity studio origin override */
    studioOriginOverride?: string;
    /**
     * Documentation base-URL override, sourced from the repo config
     * `source.baseUrl` (or the `DOC_BASE_URL` env var). Distinct from the
     * `--url` flag captured in `urls`; `urls[0]` still wins when both are set.
     */
    baseUrlOverride?: string;
    /** Sanity document filter args */
    sanityDocumentArgs?: string[];
    /** Report ID that triggered this re-run (flows to provenance.lineage.rerunOf) */
    sourceReportId?: string;
    /** Disable release-aware auto-scoping (evaluate all tasks even when perspective is set) */
    noAutoScope: boolean;
    /** Before option for comparison */
    beforeOption?: string;
    /** Task source adapter selection */
    taskSourceType?: "content-lake" | "repo";
    /** Path to repo-based tasks directory (e.g., .ailf/tasks/) */
    repoTasksPath?: string;
    /**
     * Opt-in registration of the submitted task suite into the Content Lake
     * (external-task mirroring). Read by `MirrorRepoTasksStep` to gate the
     * cross-repo (`taskSourceType: "repo"`) mirror path. Omitted/false keeps
     * that path's mirror disabled (no registration on the repo path); the
     * dogfood path (`taskSourceType` unset) mirrors regardless.
     */
    registerTasks?: boolean;
    /** Report store project ID from .ailf/config.yaml reportStore block */
    reportStoreProjectId?: string;
    /** Report store dataset from .ailf/config.yaml reportStore block */
    reportStoreDataset?: string;
    /**
     * Git metadata from the *calling* repository (cross-repo evaluations).
     * When set, this overrides the CI env var-based git detection in provenance,
     * ensuring the report attributes to the caller — not the AILF core repo.
     */
    callerGit?: {
        branch?: string;
        prNumber?: number;
        repo: string;
        sha?: string;
    };
    /**
     * Caller-provided D0037 provenance envelope (classification, owner,
     * executor, purpose, labels). Set on --remote submissions so the
     * caller's `AILF_*` env vars / CLI flags survive the API boundary.
     * When set, buildRunContext prefers these over server-env detection —
     * same pattern as callerGit.
     *
     * @see docs/decisions/D0037-run-classification-and-ownership-taxonomy.md
     */
    callerEnvelope?: {
        classification?: RunClassification;
        owner?: {
            team: string;
            individual?: string;
        };
        executor?: {
            type: "user";
            surface: RunExecutorSurface;
            name?: string;
            githubActor?: string;
        };
        purpose?: string;
        labels?: string[];
    };
    /**
     * Project-level run-provenance policy overrides from `.ailf/config.yaml`
     * (strengthen-only). Merged onto `SERVER_FLOOR` by the T2 pre-publish gate.
     * Populated on the local run path; absent on dispatched/remote runs (the
     * consumer repo's config isn't present server-side), where the gate
     * enforces the floor only.
     */
    provenance?: RepoProvenanceConfig;
    /** Callback URL configuration for API-triggered evaluations */
    callback?: {
        url: string;
        headers?: Record<string, string>;
        events?: EventType[];
    };
    /** Job ID for tracking API-triggered evaluations in the Content Lake */
    jobId?: string;
    /** Whether to run in remote mode (submit to AILF API) */
    remote: boolean;
    /** AILF API base URL */
    apiUrl: string;
    /** AILF API key (from AILF_API_KEY env var) */
    apiKey?: string;
    /** External preset file paths or npm package names to load */
    presets?: string[];
    /**
     * D0033 unified artifact surface. Consumed by the writer factory to
     * decide whether to attach a writer at all, where it writes to, and
     * what to skip. Legacy `capture*` fields were retired in W0052.
     */
    /** Disables all artifact writers — `--no-artifacts`. */
    artifactsDisabled?: boolean;
    /** Root directory for local artifact output — `--artifacts-dir`. */
    artifactsDir?: string;
    /** Run writers in dry-run mode — `--artifacts-dry-run`. */
    artifactsDryRun?: boolean;
    /** Comma-separated artifact types to skip — `--artifacts-exclude`. */
    artifactsExclude?: readonly string[];
    /**
     * GCS bucket for report artifact uploads. Defaults to "ailf-artifacts"
     * at the composition root — only set this to override (e.g., self-hosted
     * deployment with a different bucket). Read access is governed by the
     * gateway's signing credentials, so alternate bucket names require
     * reconfiguring the gateway as well (D0030).
     */
    artifactGcsBucket?: string;
    /**
     * Selects the `LLMClient` adapter wired by the composition root (D0051).
     *
     * - `undefined` (default): auto — use Anthropic when `ANTHROPIC_API_KEY` is
     *   present, otherwise OpenAI when `OPENAI_API_KEY` is present, otherwise
     *   leave `llmClient` unset.
     * - `"anthropic" | "openai"`: explicit selection. The composition root still
     *   reads the corresponding env var; if it's missing, `llmClient` is unset.
     */
    llmProvider?: "anthropic" | "openai";
    /**
     * Controls whether the ArtifactUploader is constructed.
     *
     * - `undefined` (default): auto — construct when credentials are available
     *   (ADC for direct GCS, or AILF_API_KEY for gateway-signed URLs).
     * - `true`: force-enable — still a no-op if no credentials are present (P5).
     * - `false`: force-disable — skip artifact upload even when credentials exist.
     *
     * Sourced from AILF_ARTIFACT_UPLOAD env var or `artifactUpload` in ailf.config.ts.
     */
    artifactUpload?: boolean;
    /**
     * Post-run diagnosis summary policy (Phase 6 / DIAG-06). Carried from
     * `.ailf/config.yaml` summary.onRun via RepoConfigSchema (CLI path) or
     * EvalConfigSchema (file-config path). Precedence resolution is deferred to
     * shouldRunPostSummary() at execution time — this field carries only the
     * config-file signal.
     */
    summaryOnRun?: "auto" | "always" | "never";
}
/**
 * Application context — the complete dependency carrier.
 *
 * Every pipeline step, domain service, and adapter receives this
 * as its first parameter. It replaces:
 * - process.env (environment-based state passing)
 * - Module-level singletons (implicit global state)
 * - Direct constructor calls (ad-hoc dependency creation)
 *
 * Created once per CLI invocation by createAppContext().
 * Created per-test by createTestContext().
 */
export interface AppContext {
    /**
     * Artifact writer — writes run artifacts + manifest to local fs (D0033
     * M4: always on) and optionally to GCS (D0032, layered via
     * FanoutArtifactWriter). Required post-W0050 — the composition root
     * always provides a writer (NoOpArtifactWriter when `--no-artifacts`).
     */
    readonly artifactWriter: ArtifactWriter;
    /** Evaluation caching (filesystem + optional Content Lake fallback) */
    readonly cache?: CacheStore;
    /** Resolved pipeline configuration */
    readonly config: ResolvedConfig;
    /** Documentation context fetcher */
    readonly docFetcher?: DocFetcher;
    /** LLM evaluation runner (Promptfoo adapter) */
    readonly evalRunner: EvalRunner;
    /**
     * LLM access for non-grader features (D0051). Optional during rollout —
     * the composition root wires it when an OpenAI or Anthropic API key is
     * available. Consumers (diagnosis cards, meta-eval) assert presence at
     * their own call sites.
     */
    readonly llmClient?: LLMClient;
    /** Structured logger */
    readonly logger: Logger;
    /**
     * Package-surface resolver for the W0198 symbol-resolution preflight.
     * Optional during the staged rollout — Phase 1 wires the adapter, later
     * phases consume it. When unset, the preflight step (Phase 4+) treats
     * every binding as `unresolved` so candidates are not penalized.
     */
    readonly packageSurfaceResolver?: PackageSurfaceResolver;
    /**
     * Progress reporter — carries `phase-start / phase-progress / phase-complete`
     * events for long-running pipeline spans (W0053). The composition root always
     * wires one; adapters default to `NoOpProgressReporter` for quiet / JSON /
     * test loggers.
     */
    readonly progress: ProgressReporter;
    /** Plugin registry — mode handlers, assertions, rubric templates, etc. */
    readonly registry: PluginRegistry;
    /**
     * Persistent report store (Sanity Content Lake).
     * Optional — not all commands need it. Commands that publish or
     * query reports (publish, weekly-digest, pipeline with --publish)
     * require this field.
     */
    readonly reportStore?: ReportStorePort;
    /**
     * Identity for this pipeline run. Generated once at composition-root
     * time; every step reads it from the context rather than regenerating.
     * Artifacts anchor to this id (`gs://…/runs/{runId}/…`) regardless of
     * whether the run eventually publishes a report.
     *
     * @see docs/decisions/D0032-run-anchored-artifact-store.md
     */
    readonly runId: RunId;
    /**
     * Report delivery sinks (Slack, BigQuery, webhooks).
     * Empty array when no sinks are configured.
     */
    readonly sinks?: ReportSinkPort[];
    /**
     * Team-channel notification dispatcher (D0055 Phase 4). Wired by the
     * composition root only when `SLACK_BOT_TOKEN` is set — mirrors `sinks`.
     * When unset, the publish step's dispatch call is a no-op and report
     * publishing is unaffected.
     */
    readonly notifications?: NotificationServicePort;
    /**
     * Global HMAC signing secret for outbound webhook delivery (per-run
     * callbacks and team webhook subscriptions). Set by the composition root
     * from `AILF_WEBHOOK_SIGNING_SECRET`; when unset, webhook delivery is a no-op.
     */
    readonly webhookSigningSecret?: string;
    /** Task definition source (YAML, Content Lake, repo) */
    readonly taskSource: TaskSource;
}
/**
 * Discriminated result for `ReportStorePort.loadBaselineFromReport`.
 *
 * Lets the compare step distinguish a genuine 404 (the pinned report
 * doesn't exist — skip with a clear reason) from a transport failure
 * (Sanity 5xx, network blew up — fail the step so the user knows the
 * pinned baseline didn't actually compare). The `baseline` payload is
 * typed as `unknown` to keep the port surface decoupled from the eval
 * package's `ComparableSummary` type — concrete implementations return
 * a more specific shape, which is sound.
 */
export type LoadBaselineResult = {
    kind: "ok";
    baseline: unknown;
} | {
    kind: "not_found";
} | {
    kind: "error";
    message: string;
};
/**
 * Minimal report store interface used by AppContext.
 *
 * The concrete ReportStore class in packages/eval implements this.
 * Defined as a structural interface so the domain kernel has no
 * import dependency on the concrete class.
 */
export interface ReportStorePort {
    /** Auto-compare against the most recent comparable baseline */
    autoCompare(currentSummary: unknown, provenance: unknown, completedAt: unknown, scopedAreas?: Set<string>): Promise<unknown>;
    /** Find a report by its eval fingerprint (for cache lookup) */
    findByFingerprint(fingerprint: string): Promise<null | unknown>;
    /** Find the most recent comparable baseline for auto-comparison */
    findComparableBaseline(query: unknown): Promise<null | unknown>;
    /** Write a report to the store */
    write(report: unknown): Promise<unknown>;
    /** Read a report by its ID (used by the post-run diagnosis hook). */
    read(id: string): Promise<null | unknown>;
    /**
     * Load a previously-published report's score summary as a baseline
     * for the `compare` step. Returns a discriminated result so callers
     * can distinguish a genuine 404 (skip with a clear reason) from a
     * transport failure (fail the step — the user pinned a baseline and
     * deserves to know it didn't actually compare).
     */
    loadBaselineFromReport(reportId: string): Promise<LoadBaselineResult>;
    /** Patch synthesis telemetry onto a published report (Phase 6 / DIAG-06). */
    patchSynthesis(id: string, telemetry: unknown): Promise<void>;
    /**
     * Patch a single artifact-manifest entry onto a published report.
     *
     * Used by deferred commands (e.g. `ailf interpret`) whose post-hoc writer
     * produces a new ArtifactRef after the doc was already published. The
     * pipeline path lifts the full manifest at publish time
     * (publish-report-step); this is the post-hoc equivalent for one slot.
     * Non-fatal on Sanity failure — mirrors `patchSynthesis`.
     */
    patchArtifactManifest(id: string, slot: string, ref: unknown): Promise<void>;
}
/**
 * Minimal report sink interface used by AppContext.
 *
 * The concrete ReportSink type in packages/eval/src/sinks/ implements this.
 * Defined structurally to avoid coupling the domain kernel to sink implementations.
 */
export interface ReportSinkPort {
    /** Human-readable name for logging */
    readonly name: string;
    /** Deliver a report to this sink */
    publish(report: unknown): Promise<unknown>;
}
