/**
 * Run classification, ownership, executor, and environment metadata.
 *
 * These fields extend `RunContext` to capture run *intent*, *attribution*,
 * and *reproducibility* — orthogonal to the *mechanism* captured by
 * `RunTrigger`. A scheduled run can be experimental; a manual run can be
 * official; a PR-triggered run is executed by GH Actions but attributable
 * to the PR author.
 *
 * @see docs/decisions/D0037-run-classification-and-ownership-taxonomy.md
 * @see docs/design-docs/run-classification-and-ownership.md
 */
export const RUN_CLASSIFICATIONS = [
    "official",
    "adhoc",
    "experimental",
    "test",
    "external",
];
export function isRunClassification(value) {
    return (typeof value === "string" &&
        RUN_CLASSIFICATIONS.includes(value));
}
/**
 * Lowercase legacy spelling → canonical classification. The `RunClassification`
 * type has long been canonical `adhoc`, but historical report data carries the
 * hyphenated `ad-hoc` spelling (D0059 §Context). Only observed drift belongs
 * here.
 */
const RUN_CLASSIFICATION_ALIASES = {
    "ad-hoc": "adhoc",
};
/**
 * Normalize a free-form classification value to a canonical
 * {@link RunClassification}.
 *
 * - Trims and lowercases.
 * - Maps the legacy `ad-hoc` spelling onto canonical `adhoc`.
 * - Defaults empty / unknown input to `adhoc` — D0037's documented
 *   default bucket, biased away from the canonical `official` series.
 *
 * Pure and deterministic — reused by the detector (`W-report-validity-detector`)
 * and the backfill (`W-backfill-report-validity`).
 *
 * @see docs/decisions/D0059-report-validity-axis-and-trustworthiness-gate.md
 */
export function normalizeRunClassification(value) {
    if (!value)
        return "adhoc";
    const trimmed = value.trim().toLowerCase();
    if (!trimmed)
        return "adhoc";
    const canonical = RUN_CLASSIFICATION_ALIASES[trimmed] ?? trimmed;
    return isRunClassification(canonical) ? canonical : "adhoc";
}
/**
 * Lowercased executor-name spelling → canonical identity slug. One human
 * appears under several spellings in the historical report store
 * (D0059 §Context: `Gabe Wyatt` / `GabeStah` / `gabewyatt`); collapsing
 * them lets attribution and `classification` queries treat them as one
 * person. Only observed drift belongs here — unknown names pass through.
 */
const EXECUTOR_IDENTITY_ALIASES = {
    "gabe wyatt": "gabe-wyatt",
    gabestah: "gabe-wyatt",
    gabewyatt: "gabe-wyatt",
};
/**
 * Collapse a free-form executor name onto its canonical identity slug.
 *
 * - Trims and lowercases.
 * - Maps known spellings (above) to one identity.
 * - Passes unknown names through (trimmed + lowercased).
 * - Returns `undefined` for empty / nullish input.
 *
 * Pure and deterministic — used by the validity detector
 * (`W-report-validity-detector`) to recognize a known human before the
 * generated-id heuristic runs, and by the backfill to de-drift
 * `provenance.executor.name`.
 *
 * @see docs/decisions/D0059-report-validity-axis-and-trustworthiness-gate.md
 */
export function canonicalizeExecutorIdentity(name) {
    if (!name)
        return undefined;
    const trimmed = name.trim().toLowerCase();
    if (!trimmed)
        return undefined;
    return EXECUTOR_IDENTITY_ALIASES[trimmed] ?? trimmed;
}
/** Whether an executor name collapses to a recognized human identity. */
export function isKnownExecutorIdentity(name) {
    if (!name)
        return false;
    return name.trim().toLowerCase() in EXECUTOR_IDENTITY_ALIASES;
}
/**
 * Heuristic: does an executor name look like a *generated* handle/id rather
 * than a human name? (D0059 §Context flagged ids like `gDVzuuHam`,
 * `gL78msEDh` in the report store.)
 *
 * Deterministic and deliberately conservative — it judges *shape* only:
 * a single token (no whitespace) of length 7–12, alphanumeric, mixing
 * upper- and lower-case, and either containing a digit or showing ≥4
 * upper/lower transitions. The transition floor is calibrated against the
 * observed sample so two-word PascalCase names ("JohnSmith" — 3
 * transitions) are NOT flagged; the generated ids (≥4 transitions or a
 * digit) are. Known identities are excluded by the caller
 * ({@link isKnownExecutorIdentity}) before this runs, so collapsed
 * spellings like `GabeStah` never reach it as a positive.
 *
 * False positives are tolerable: the detector only uses this to propose an
 * `experimental` classification, which is reversible (label-and-exclude,
 * never delete) and surfaced for human review during the backfill.
 */
export function looksLikeGeneratedExecutorId(name) {
    if (!name)
        return false;
    const token = name.trim();
    if (token.length < 7 || token.length > 12)
        return false;
    if (!/^[A-Za-z0-9]+$/.test(token))
        return false;
    if (!/[A-Z]/.test(token) || !/[a-z]/.test(token))
        return false;
    if (/[0-9]/.test(token))
        return true;
    let transitions = 0;
    for (let i = 1; i < token.length; i++) {
        if (/[A-Z]/.test(token[i - 1]) !== /[A-Z]/.test(token[i]))
            transitions++;
    }
    return transitions >= 4;
}
export const RUN_EXECUTOR_SURFACES = [
    "cli",
    "studio",
    "api",
];
