/**
 * Pipeline step: pre-publish run-provenance ownership gate (T2).
 *
 * The runner-side companion to the API gateway gate (T1). Both call the same
 * pure `evaluateProvenance` evaluator, so the verdict a local/dispatched
 * publish faces is identical to the one the gateway enforced — "one rule, two
 * call sites".
 *
 * Why a separate, non-optional step rather than logic inside
 * `PublishReportStep`: the orchestrator aborts the run only on a *non-optional*
 * step that returns `status: "failed"`. `PublishReportStep` is `optional = true`
 * (P5 — a store write never fails the run), so a check inside it could not make
 * the run fail. This step runs immediately before it and is non-optional, so an
 * invalid-attribution rejection aborts the run *before* any write is attempted.
 *
 * P5 carve-out: P5 protects against the store being *unreachable* (transient,
 * non-fatal). Refusing to publish a run with invalid attribution is different —
 * a policy rejection of bad input. So this gate fails only when a publish is
 * actually intended (publishing enabled *and* a report store is wired). A run
 * that never writes (publish disabled, or no token) emits warnings and exits 0.
 *
 * @see docs/design-docs/run-provenance-ownership-enforcement.md (T2)
 * @see packages/core/src/services/provenance-policy.ts — the shared evaluator
 */
import { evaluateProvenance, mergeProvenancePolicy, SERVER_FLOOR, } from "../../_vendor/ailf-core/index.js";
import { resolveProvenanceFields } from "../../pipeline/run-context.js";
export class ProvenanceGateStep {
    name = "provenance-gate";
    // Non-optional by design: when a publish is actually intended and the
    // provenance is invalid, this step's failure must abort the run before the
    // store write. The orchestrator only aborts on a failed non-optional step.
    optional = false;
    check() {
        return [];
    }
    async execute(ctx, _state) {
        const start = Date.now();
        // Resolve the same classification/owner/purpose the report will carry,
        // via the one shared resolution path (no drift between gate and report).
        const { classification, owner, purpose } = resolveProvenanceFields(ctx.config.callerEnvelope, ctx.logger);
        // The effective policy is the server floor, optionally strengthened by the
        // project's `.ailf/config.yaml` `provenance` block (never weakened). On
        // dispatched/remote runs `ctx.config.provenance` is absent → floor only.
        const policy = mergeProvenancePolicy(SERVER_FLOOR, ctx.config.provenance);
        const verdict = evaluateProvenance({ classification, owner, purpose }, policy);
        // A publish is "actually intended" only when publishing is enabled AND a
        // report store is wired (a token is present). Without a store the publish
        // step skips the write (P5 offline) — invalid provenance is then a
        // warning, not a failure.
        const willPublish = ctx.config.publishEnabled && ctx.reportStore != null;
        if (!verdict.ok && willPublish) {
            const blocking = verdict.violations.filter((v) => v.severity === "error");
            const error = [
                "Run cannot be published — invalid run provenance.",
                ...blocking.map((v) => `\n✗ ${v.message}\n${v.remediation}`),
            ].join("\n");
            return { status: "failed", durationMs: Date.now() - start, error };
        }
        // Not publishing (or valid): surface every violation as a warning only.
        // This includes the "you would be rejected if you published" nudge for a
        // local dry-run, and soft warnings (e.g. an unknown but well-formed team).
        for (const v of verdict.violations) {
            ctx.logger.warn(`[provenance] ${v.message}`);
        }
        const summary = verdict.ok
            ? `Provenance OK (team: ${verdict.normalizedTeam ?? owner.team}, classification: ${classification})`
            : `Provenance has ${verdict.violations.length} issue(s); not failing (publish not intended)`;
        return { status: "success", durationMs: Date.now() - start, summary };
    }
}
