/**
 * Port: Where task definitions come from.
 *
 * Adapters:
 * - ContentLakeTaskSource — GROQ query against Sanity Content Lake
 * - RepoTaskSource — reads .ailf/tasks/*.task.ts files
 * - TsTaskFileLoader — reads tasks/{mode}/*.task.ts (eval package)
 *
 * The key invariant: the pipeline orchestrator and all downstream steps
 * work with GeneralizedTaskDefinition[] regardless of where they came from.
 */
import { baseDocumentId } from "../../ailf-shared/index.js";
// ---------------------------------------------------------------------------
// Type guards — doc refs
// ---------------------------------------------------------------------------
/** Check if a doc ref resolves by slug.
 *
 * Excludes IdDocRef (which may carry an optional `slug` for display).
 * When both `id` and `slug` are present, it's an IdDocRef, not a SlugDocRef.
 */
export function isSlugRef(ref) {
    return "slug" in ref && !("id" in ref);
}
/** Check if a doc ref resolves by path.
 *
 * Excludes IdDocRef (which may carry an optional `path` for display).
 * When both `id` and `path` are present, it's an IdDocRef, not a PathDocRef.
 */
export function isPathRef(ref) {
    return "path" in ref && !("id" in ref);
}
/** Check if a doc ref resolves by document ID.
 *
 * Uses `"id" in ref` as the primary discriminator. IdDocRef may also carry
 * optional `slug` and `path` for display purposes, so we cannot exclude
 * on those keys. When both `id` and `slug` are present, `id` wins.
 */
export function isIdRef(ref) {
    return "id" in ref;
}
/** Check if a doc ref resolves by content release perspective */
export function isPerspectiveRef(ref) {
    return "perspective" in ref;
}
/**
 * Extract a display identifier from any doc ref.
 * Useful for logging, error messages, and retrieval metrics.
 */
export function canonicalDocRefLabel(ref) {
    if ("id" in ref) {
        // Show slug/path annotation if available for readability
        const hint = "slug" in ref ? ` (${ref.slug})` : "path" in ref ? ` (${ref.path})` : "";
        return `id:${ref.id}${hint}`;
    }
    if ("slug" in ref)
        return `slug:${ref.slug}`;
    if ("path" in ref)
        return `path:${ref.path}`;
    if ("perspective" in ref)
        return `perspective:${ref.perspective}`;
    return "unknown";
}
/**
 * Resolve a canonical doc ref to its cross-version **document key** (D0084) —
 * the `baseDocumentId` of the logical document it names, anchor-relative.
 *
 * This is the single ref→document-identity resolver for the surfaces that join
 * a task's `context.docs` to the run's document manifest. It closes the
 * slug-bridge blind spot (`docs/design-docs/doctype-agnostic-eval.md`,
 * "Implementation correction"): an `IdDocRef` resolves on its `_id` directly,
 * so a slugless non-article pin is reachable, where a slug-only bridge dropped
 * it. Resolution strategy by ref kind:
 *
 * - **{@link IdDocRef}** → `baseDocumentId(ref.id)` directly. Type-agnostic, no
 *   slug needed. The informational `slug`/`path` annotations are ignored (they
 *   are author hints, never the manifest's actual slug — D-09).
 * - **{@link SlugDocRef}** → `slugToDocumentKey.get(ref.slug)`. The
 *   slug→document-key bridge is supplied by the caller (built from the manifest)
 *   because a bare editorial slug carries no id.
 * - **{@link PathDocRef}** → the last path segment (the editorial slug) through
 *   the same bridge.
 * - **{@link PerspectiveDocRef}** → `undefined`. A perspective ref expands
 *   one-to-many at fetch time; its document keys are unknown from the ref alone
 *   and are attributed separately via fetch-time expansion metadata.
 *
 * Returns `undefined` when an editorial handle was never resolved (typo'd /
 * out-of-corpus, so absent from the bridge) or for a perspective ref. The
 * returned key matches a manifest indexed by `baseDocumentId(documentId)`.
 *
 * Consolidation target: the other ref→identity extractors —
 * `extractDocumentKeysFromTask` (eval `fetch-docs-step.ts`, release auto-scope;
 * the freshly-written sibling, intentionally not migrated here),
 * `loadCanonicalDocs` (eval `retrieval-metrics.ts`, dormant), and
 * `extractInlineMappings` (eval `resolve-mappings.ts`, legacy) — each re-derive
 * this slug/id dispatch. Route them through this helper as they are touched so
 * the slug-only assumption cannot silently regrow.
 */
export function resolveDocRefToDocumentKey(ref, slugToDocumentKey) {
    if (isIdRef(ref)) {
        return baseDocumentId(ref.id);
    }
    if (isSlugRef(ref)) {
        return slugToDocumentKey.get(ref.slug);
    }
    if (isPathRef(ref)) {
        // Path refs use the last segment as the editorial slug
        // (e.g. "groq/groq-introduction" → "groq-introduction").
        const segments = ref.path.split("/");
        const slug = segments[segments.length - 1];
        return slugToDocumentKey.get(slug);
    }
    // PerspectiveDocRef — one-to-many fetch-time expansion; no key from the ref.
    return undefined;
}
// ---------------------------------------------------------------------------
// Type guards — assertions
// ---------------------------------------------------------------------------
/** Check if an assertion uses the templated format (template + criteria) */
export function isTemplatedAssertion(entry) {
    return (entry.type === "llm-rubric" &&
        "template" in entry &&
        typeof entry.template === "string" &&
        "criteria" in entry &&
        Array.isArray(entry.criteria));
}
/** Narrow a `TaskSource` to one that also supports the viability probe. */
export function supportsViabilityProbe(source) {
    return (typeof source.probeTaskPublishState ===
        "function");
}
