import { readFileSync, readdirSync, statSync } from "node:fs";
import { basename, join, resolve } from "node:path";
import { auditBundleSchema, benchmarkArtifactSchema, benchmarkLedgerDocumentSchema, lifecycleArtifactSchema, runComparisonDocumentSchema } from "@h9-foundry/agentforge-schemas";
function parseRunTimestampMs(value) {
    if (typeof value !== "string" || value.length === 0) {
        return undefined;
    }
    const compactDateTimeMatch = value.match(/^(\d{4})-(\d{2})-(\d{2})-(\d{2})(\d{2})(\d{2})$/);
    if (compactDateTimeMatch) {
        const [, year, month, day, hour, minute, second] = compactDateTimeMatch;
        const isoCandidate = `${year}-${month}-${day}T${hour}:${minute}:${second}Z`;
        const parsedCompactDateTime = Date.parse(isoCandidate);
        if (!Number.isNaN(parsedCompactDateTime)) {
            return parsedCompactDateTime;
        }
    }
    const parsedDate = Date.parse(value);
    if (!Number.isNaN(parsedDate)) {
        return parsedDate;
    }
    const timestampPrefix = Number.parseInt(value.split("-")[0] ?? "", 10);
    return Number.isNaN(timestampPrefix) ? undefined : timestampPrefix;
}
function isRecord(value) {
    return typeof value === "object" && value !== null;
}
function readString(value, fallback) {
    return typeof value === "string" && value.length > 0 ? value : fallback;
}
function readStringArray(value) {
    return Array.isArray(value) ? value.filter((entry) => typeof entry === "string" && entry.length > 0) : [];
}
function readRecordArray(value) {
    return Array.isArray(value) ? value.filter((entry) => isRecord(entry)) : [];
}
function readLooseArtifact(value) {
    if (!isRecord(value)) {
        return {};
    }
    const workflow = isRecord(value.workflow)
        ? {
            name: readString(value.workflow.name, "unknown"),
            displayName: typeof value.workflow.displayName === "string" ? value.workflow.displayName : undefined
        }
        : undefined;
    const source = isRecord(value.source)
        ? {
            inputRefs: readStringArray(value.source.inputRefs),
            issueRefs: readStringArray(value.source.issueRefs)
        }
        : undefined;
    return {
        artifactKind: typeof value.artifactKind === "string" ? value.artifactKind : undefined,
        lifecycleDomain: typeof value.lifecycleDomain === "string" ? value.lifecycleDomain : undefined,
        workflow,
        status: typeof value.status === "string" ? value.status : undefined,
        summary: typeof value.summary === "string" ? value.summary : undefined,
        source,
        provenance: isRecord(value.provenance) ? value.provenance : undefined,
        auditLink: isRecord(value.auditLink) ? value.auditLink : undefined,
        payload: value.payload
    };
}
function readLooseAuditBundle(value, runDir) {
    if (!isRecord(value)) {
        return {
            runId: runDir,
            bundlePath: "",
            error: "Bundle root is not an object."
        };
    }
    const status = value.status;
    if (status !== "success" && status !== "partial" && status !== "failed") {
        return {
            runId: runDir,
            bundlePath: "",
            error: "Bundle status is missing or invalid."
        };
    }
    return {
        runId: readString(value.runId, runDir),
        workflow: readString(value.workflow, "unknown"),
        startedAt: readString(value.startedAt, runDir),
        finishedAt: readString(value.finishedAt, readString(value.startedAt, runDir)),
        status,
        lifecycleArtifacts: Array.isArray(value.lifecycleArtifacts) ? value.lifecycleArtifacts.map(readLooseArtifact) : []
    };
}
function stringifyUnknown(value) {
    return JSON.stringify(value, null, 2);
}
function formatSourceRefs(artifact) {
    return artifact.source?.inputRefs ?? [];
}
function formatIssueRefs(artifact) {
    return artifact.source?.issueRefs ?? [];
}
function formatProvenanceEntries(artifact) {
    if (!artifact.provenance) {
        return [];
    }
    return Object.entries(artifact.provenance).map(([key, value]) => `${key}: ${typeof value === "string" ? value : stringifyUnknown(value)}`);
}
function formatAuditEntries(artifact) {
    if (!artifact.auditLink) {
        return [];
    }
    return Object.entries(artifact.auditLink).map(([key, value]) => `${key}: ${typeof value === "string" ? value : stringifyUnknown(value)}`);
}
function formatVerificationChecks(checks) {
    return checks.map((check) => `${check.name}: ${check.status}${check.detail ? ` (${check.detail})` : ""}`);
}
function formatCiEvidenceSummary(entries) {
    return entries.flatMap((entry) => entry.failingChecks.length > 0 ? [entry.statusSummary, `Failing checks: ${entry.failingChecks.join(", ")}`] : [entry.statusSummary]);
}
function toArtifactSections(artifact) {
    switch (artifact.artifactKind) {
        case "planning-brief":
            return [
                { heading: "Objectives", lines: artifact.payload.objectives },
                { heading: "Recommended Next Steps", lines: artifact.payload.recommendedNextSteps },
                { heading: "Risks", lines: artifact.payload.risks ?? [] },
                { heading: "Open Questions", lines: artifact.payload.openQuestions ?? [] }
            ].filter((section) => section.lines.length > 0);
        case "qa-report":
            return [
                {
                    heading: "Findings",
                    lines: artifact.payload.findings.map((finding) => `[${finding.severity}] ${finding.title}: ${finding.summary}`)
                },
                { heading: "Coverage Gaps", lines: artifact.payload.coverageGaps },
                { heading: "Recommended Next Checks", lines: artifact.payload.recommendedNextChecks },
                { heading: "CI Evidence", lines: formatCiEvidenceSummary(artifact.payload.ciEvidenceSummary) },
                { heading: "Release Impact", lines: [artifact.payload.releaseImpact] }
            ].filter((section) => section.lines.length > 0);
        case "release-report":
            return [
                { heading: "Readiness Status", lines: [artifact.payload.readinessStatus] },
                { heading: "Verification Checks", lines: formatVerificationChecks(artifact.payload.verificationChecks) },
                { heading: "CI Evidence", lines: formatCiEvidenceSummary(artifact.payload.ciEvidenceSummary) },
                { heading: "Publishing Plan", lines: artifact.payload.publishingPlan },
                { heading: "Rollback Notes", lines: artifact.payload.rollbackNotes },
                { heading: "Trust Summary", lines: artifact.payload.trustSummary }
            ].filter((section) => section.lines.length > 0);
        case "pipeline-report":
            return [
                { heading: "Review Status", lines: [artifact.payload.reviewStatus] },
                { heading: "Verification Checks", lines: formatVerificationChecks(artifact.payload.verificationChecks) },
                { heading: "CI Evidence", lines: formatCiEvidenceSummary(artifact.payload.ciEvidenceSummary) },
                { heading: "Blockers", lines: artifact.payload.blockers },
                { heading: "Risk Summary", lines: artifact.payload.riskSummary },
                { heading: "Recommended Next Steps", lines: artifact.payload.recommendedNextSteps }
            ].filter((section) => section.lines.length > 0);
        case "deployment-gate-report":
            return [
                { heading: "Gate Status", lines: [artifact.payload.gateStatus] },
                { heading: "Verification Checks", lines: formatVerificationChecks(artifact.payload.verificationChecks) },
                { heading: "CI Evidence", lines: formatCiEvidenceSummary(artifact.payload.ciEvidenceSummary) },
                { heading: "Blockers", lines: artifact.payload.blockers },
                { heading: "Required Follow-Up Checks", lines: artifact.payload.requiredFollowUpChecks }
            ].filter((section) => section.lines.length > 0);
        case "promotion-approval-report":
            return [
                { heading: "Approval Status", lines: [artifact.payload.approvalStatus] },
                { heading: "Verification Checks", lines: formatVerificationChecks(artifact.payload.verificationChecks) },
                { heading: "CI Evidence", lines: formatCiEvidenceSummary(artifact.payload.ciEvidenceSummary) },
                { heading: "Required Approvals", lines: artifact.payload.requiredApprovals },
                { heading: "Recommended Next Steps", lines: artifact.payload.recommendedNextSteps }
            ].filter((section) => section.lines.length > 0);
        case "benchmark-summary":
            return [
                { heading: "Summary Conclusion", lines: [artifact.payload.summaryConclusion] },
                {
                    heading: "Compared Runs",
                    lines: artifact.payload.comparedRuns.map((run) => `${run.runId}: regressions=${run.regressions.length}, improvements=${run.improvements.length}, unchanged=${run.unchangedCount}, non-comparable=${run.nonComparableFindings.length}`)
                }
            ].filter((section) => section.lines.length > 0);
        default:
            return [];
    }
}
function toKnownArtifactPanel(artifact) {
    return {
        id: `${artifact.artifactKind}:${artifact.generatedAt}`,
        artifactKind: artifact.artifactKind,
        lifecycleDomain: artifact.lifecycleDomain,
        workflow: artifact.workflow.name,
        workflowDisplayName: artifact.workflow.displayName,
        status: artifact.status,
        summary: artifact.summary,
        sourceRefs: formatSourceRefs(artifact),
        issueRefs: formatIssueRefs(artifact),
        provenanceEntries: formatProvenanceEntries(artifact),
        auditEntries: formatAuditEntries(artifact),
        sections: toArtifactSections(artifact),
        rawPayload: stringifyUnknown(artifact.payload),
        isKnownArtifact: true
    };
}
function toUnknownArtifactPanel(rawArtifact, error) {
    return {
        id: `${rawArtifact.artifactKind ?? "unknown"}:${rawArtifact.summary ?? "artifact"}`,
        artifactKind: rawArtifact.artifactKind ?? "unknown",
        lifecycleDomain: rawArtifact.lifecycleDomain ?? "unknown",
        workflow: rawArtifact.workflow?.name ?? "unknown",
        workflowDisplayName: rawArtifact.workflow?.displayName,
        status: rawArtifact.status ?? "unknown",
        summary: rawArtifact.summary ?? "Artifact could not be parsed with the current shared schema set.",
        sourceRefs: formatSourceRefs(rawArtifact),
        issueRefs: formatIssueRefs(rawArtifact),
        provenanceEntries: formatProvenanceEntries(rawArtifact),
        auditEntries: formatAuditEntries(rawArtifact),
        sections: [],
        rawPayload: stringifyUnknown(rawArtifact.payload ?? rawArtifact),
        isKnownArtifact: false,
        parseError: error
    };
}
function countBlockedActions(entries) {
    return entries.reduce((total, entry) => total + entry.blockedActions.length, 0);
}
function loadRun(root, runsRoot, runDir) {
    const bundlePath = join(runsRoot, runDir, "bundle.json");
    const summaryPath = join(runsRoot, runDir, "summary.md");
    const rawBundleJson = readFileSync(bundlePath, "utf8");
    let rawBundleCandidate;
    try {
        rawBundleCandidate = JSON.parse(rawBundleJson);
    }
    catch (error) {
        return {
            runId: runDir,
            bundlePath,
            error: error instanceof Error ? error.message : "Failed to parse bundle JSON."
        };
    }
    const looseBundle = readLooseAuditBundle(rawBundleCandidate, runDir);
    if ("error" in looseBundle) {
        return {
            runId: looseBundle.runId,
            bundlePath,
            error: looseBundle.error
        };
    }
    const rawBundle = looseBundle;
    const parsedBundle = auditBundleSchema.safeParse(rawBundleCandidate);
    const bundle = parsedBundle.success ? parsedBundle.data : undefined;
    const rawArtifacts = isRecord(rawBundleCandidate) && Array.isArray(rawBundleCandidate.lifecycleArtifacts) ? rawBundleCandidate.lifecycleArtifacts : [];
    const artifactViews = rawArtifacts.map((artifact, index) => {
        const parsedArtifact = lifecycleArtifactSchema.safeParse(artifact);
        return parsedArtifact.success
            ? toKnownArtifactPanel(parsedArtifact.data)
            : toUnknownArtifactPanel(rawBundle.lifecycleArtifacts[index] ?? readLooseArtifact(artifact), parsedArtifact.error.issues.map((issue) => issue.message).join("; ") || "Unknown artifact shape.");
    });
    return {
        runId: rawBundle.runId,
        runDir,
        bundlePath,
        summaryPath,
        rawBundle,
        bundle,
        rawBundleJson,
        summaryMarkdown: readFileSync(summaryPath, "utf8"),
        artifactViews,
        invalidArtifactCount: artifactViews.filter((artifact) => !artifact.isKnownArtifact).length
    };
}
function sortRunsNewestFirst(left, right) {
    const leftCompletedAt = parseRunTimestampMs(left.rawBundle.finishedAt) ??
        parseRunTimestampMs(left.rawBundle.startedAt) ??
        parseRunTimestampMs(left.runId) ??
        statSync(left.bundlePath).mtimeMs;
    const rightCompletedAt = parseRunTimestampMs(right.rawBundle.finishedAt) ??
        parseRunTimestampMs(right.rawBundle.startedAt) ??
        parseRunTimestampMs(right.runId) ??
        statSync(right.bundlePath).mtimeMs;
    if (leftCompletedAt !== rightCompletedAt) {
        return rightCompletedAt - leftCompletedAt;
    }
    return right.runId.localeCompare(left.runId);
}
function toRunConfigurationSummary(configuration) {
    if (!configuration) {
        return undefined;
    }
    return {
        profile: configuration.selectedControls.profile,
        policyPreset: configuration.selectedControls.policyPreset,
        workflowVariant: configuration.selectedControls.workflowVariant,
        agentBindings: Object.entries(configuration.selectedControls.agentBindings)
            .map(([role, agent]) => `${role}=${agent}`)
            .sort(),
        policyFingerprint: configuration.effective.policyFingerprint,
        repoFitPath: configuration.repoFit?.path,
        repoFitRecommendedProfile: configuration.repoFit?.recommendedProfileId,
        repoFitSelectedProfile: configuration.repoFit?.selectedProfileId,
        repoFitAdoption: configuration.repoFit?.adoption,
        sourceRefs: [...configuration.sourceRefs],
        nodeAgents: Object.entries(configuration.effective.nodeAgents)
            .map(([nodeId, agent]) => ({ nodeId, agent }))
            .sort((left, right) => left.nodeId.localeCompare(right.nodeId)),
        executedNodes: configuration.execution.executedNodes.map((node) => ({
            nodeId: node.nodeId,
            kind: node.kind,
            ...(node.agent ? { agent: node.agent } : {})
        }))
    };
}
function toRunListItemView(run, ledgerEntries = []) {
    const entries = run.bundle?.entries ?? [];
    const findings = run.bundle?.findings ?? [];
    const blockedPlugins = run.bundle?.blockedPlugins ?? [];
    const artifactKinds = run.artifactViews.map((artifact) => artifact.artifactKind);
    const configuration = toRunConfigurationSummary(run.bundle?.configuration);
    const base = {
        runId: run.runId,
        workflow: run.rawBundle.workflow,
        startedAt: run.rawBundle.startedAt,
        finishedAt: run.rawBundle.finishedAt,
        status: run.rawBundle.status,
        findings: findings.length,
        blockedActions: countBlockedActions(entries),
        blockedPlugins: blockedPlugins.length,
        artifactKinds,
        bundlePath: run.bundlePath,
        summaryPath: run.summaryPath,
        hasBenchmarkSummary: artifactKinds.includes("benchmark-summary"),
        invalidArtifactCount: run.invalidArtifactCount,
        riskKinds: [],
        evidenceStatuses: [],
        hasOverride: false,
        configurationProfile: configuration?.profile,
        configurationPolicyPreset: configuration?.policyPreset,
        configurationWorkflowVariant: configuration?.workflowVariant,
        configurationAgentBindings: configuration?.agentBindings ?? []
    };
    const decisionImpact = inferDecisionImpact(base, findings, run.artifactViews, ledgerEntries);
    const evidenceCompleteness = buildEvidenceCompleteness(base.workflow, run.artifactViews);
    const workflowChain = buildWorkflowChain(base.workflow, run.artifactViews);
    const ledgerEntry = findLedgerEntry(base, ledgerEntries);
    const riskKinds = deriveRiskKinds(findings, run.artifactViews, ledgerEntry);
    return {
        ...base,
        decisionImpactKind: decisionImpact.kind,
        riskKinds,
        evidenceStatuses: evidenceCompleteness.flatMap((artifact) => artifact.categories.map((category) => ({ category: category.key, status: category.status }))),
        workflowStage: workflowChain.currentStage,
        hasOverride: ledgerEntry?.friction.override ?? false
    };
}
function matchesFilters(run, filters) {
    if (filters.workflow && run.workflow !== filters.workflow) {
        return false;
    }
    if (filters.status && run.status !== filters.status) {
        return false;
    }
    if (filters.artifactKind && !run.artifactKinds.includes(filters.artifactKind)) {
        return false;
    }
    if (filters.search && !run.runId.toLowerCase().includes(filters.search.toLowerCase())) {
        return false;
    }
    if (filters.decisionImpact && run.decisionImpactKind !== filters.decisionImpact) {
        return false;
    }
    if (filters.riskKind && !run.riskKinds.includes(filters.riskKind)) {
        return false;
    }
    if (filters.evidenceCategory &&
        !run.evidenceStatuses.some((entry) => entry.category === filters.evidenceCategory &&
            (!filters.evidenceStatus || entry.status === filters.evidenceStatus))) {
        return false;
    }
    if (filters.evidenceStatus && !filters.evidenceCategory && !run.evidenceStatuses.some((entry) => entry.status === filters.evidenceStatus)) {
        return false;
    }
    if (filters.hasOverride && String(run.hasOverride) !== filters.hasOverride) {
        return false;
    }
    if (filters.workflowStage && run.workflowStage !== filters.workflowStage) {
        return false;
    }
    return true;
}
export function resolveRunsRoot(workspaceRoot, configuredRunsRoot) {
    return resolve(workspaceRoot, configuredRunsRoot ?? ".agentops/runs");
}
export function loadRunsIndexView(workspaceRoot, runsRoot = resolveRunsRoot(workspaceRoot), filters = {}, benchmarkLedgerPath) {
    try {
        statSync(runsRoot);
    }
    catch {
        return { runs: [], invalidRuns: [] };
    }
    const entries = readdirSync(runsRoot, { withFileTypes: true })
        .filter((entry) => entry.isDirectory())
        .map((entry) => entry.name)
        .filter((entry) => {
        try {
            statSync(join(runsRoot, entry, "bundle.json"));
            return true;
        }
        catch {
            return false;
        }
    });
    const loaded = entries.map((entry) => loadRun(workspaceRoot, runsRoot, entry));
    const invalidRuns = loaded.filter((entry) => "error" in entry);
    const ledgerEntries = loadBenchmarkLedgerView(workspaceRoot, benchmarkLedgerPath).entries;
    const runs = loaded
        .filter((entry) => !("error" in entry))
        .sort(sortRunsNewestFirst)
        .map((run) => toRunListItemView(run, ledgerEntries))
        .filter((run) => matchesFilters(run, filters));
    return { runs, invalidRuns };
}
export function resolveBenchmarkLedgerPath(workspaceRoot, configuredBenchmarkLedgerPath) {
    return resolve(workspaceRoot, configuredBenchmarkLedgerPath ?? ".agentops/benchmark-ledger.json");
}
export function loadRunDetailView(workspaceRoot, runId, runsRoot = resolveRunsRoot(workspaceRoot), benchmarkLedgerPath) {
    try {
        statSync(runsRoot);
    }
    catch {
        return undefined;
    }
    const run = loadRun(workspaceRoot, runsRoot, runId);
    if ("error" in run) {
        return undefined;
    }
    const base = toRunListItemView(run);
    const ledger = loadBenchmarkLedgerView(workspaceRoot, benchmarkLedgerPath);
    const decisionImpact = inferDecisionImpact(base, run.bundle?.findings ?? [], run.artifactViews, ledger.entries);
    const riskSummary = buildRiskSummary(base, run.bundle?.findings ?? [], run.artifactViews, ledger.entries);
    const evidenceCompleteness = buildEvidenceCompleteness(base.workflow, run.artifactViews);
    const workflowChain = buildWorkflowChain(base.workflow, run.artifactViews);
    return {
        ...base,
        entries: run.bundle?.entries ?? [],
        findingsList: run.bundle?.findings ?? [],
        blockedPluginsList: run.bundle?.blockedPlugins ?? [],
        artifacts: run.artifactViews,
        summaryMarkdown: run.summaryMarkdown,
        rawBundleJson: run.rawBundleJson,
        usage: run.bundle?.usage,
        configuration: toRunConfigurationSummary(run.bundle?.configuration),
        decisionImpact,
        riskSummary,
        evidenceCompleteness,
        workflowChain
    };
}
export function loadRunComparisonView(workspaceRoot, leftRunId, rightRunId, runsRoot = resolveRunsRoot(workspaceRoot), benchmarkLedgerPath) {
    const left = loadRunDetailView(workspaceRoot, leftRunId, runsRoot, benchmarkLedgerPath);
    const right = loadRunDetailView(workspaceRoot, rightRunId, runsRoot, benchmarkLedgerPath);
    if (!left || !right) {
        return undefined;
    }
    const leftConfiguration = left.configuration;
    const rightConfiguration = right.configuration;
    const controlChanges = [
        { field: "profile", left: leftConfiguration?.profile, right: rightConfiguration?.profile },
        { field: "policyPreset", left: leftConfiguration?.policyPreset, right: rightConfiguration?.policyPreset },
        { field: "workflowVariant", left: leftConfiguration?.workflowVariant, right: rightConfiguration?.workflowVariant },
        { field: "agentBindings", left: leftConfiguration?.agentBindings, right: rightConfiguration?.agentBindings },
        { field: "policyFingerprint", left: leftConfiguration?.policyFingerprint, right: rightConfiguration?.policyFingerprint },
        { field: "repoFitProfile", left: leftConfiguration?.repoFitSelectedProfile, right: rightConfiguration?.repoFitSelectedProfile },
        { field: "repoFitAdoption", left: leftConfiguration?.repoFitAdoption, right: rightConfiguration?.repoFitAdoption }
    ].filter((change) => JSON.stringify(change.left) !== JSON.stringify(change.right));
    const leftNodeAgents = new Map((leftConfiguration?.nodeAgents ?? []).map((entry) => [entry.nodeId, entry.agent]));
    const rightNodeAgents = new Map((rightConfiguration?.nodeAgents ?? []).map((entry) => [entry.nodeId, entry.agent]));
    const nodeChanges = [...new Set([...leftNodeAgents.keys(), ...rightNodeAgents.keys()])]
        .map((nodeId) => ({
        nodeId,
        leftAgent: leftNodeAgents.get(nodeId),
        rightAgent: rightNodeAgents.get(nodeId)
    }))
        .filter((change) => change.leftAgent !== change.rightAgent);
    return {
        ...runComparisonDocumentSchema.parse({
            leftRunId,
            rightRunId,
            controlChanges,
            executionChanges: {
                workflowChanged: left.workflow !== right.workflow,
                profileChanged: leftConfiguration?.profile !== rightConfiguration?.profile,
                policyPresetChanged: leftConfiguration?.policyPreset !== rightConfiguration?.policyPreset,
                workflowVariantChanged: leftConfiguration?.workflowVariant !== rightConfiguration?.workflowVariant,
                agentBindingChanged: JSON.stringify(leftConfiguration?.agentBindings ?? []) !== JSON.stringify(rightConfiguration?.agentBindings ?? []),
                nodeChanges
            },
            outcomeChanges: {
                findingsDelta: right.findings - left.findings,
                blockedActionsDelta: right.blockedActions - left.blockedActions,
                artifactKindsAdded: right.artifactKinds.filter((kind) => !left.artifactKinds.includes(kind)),
                artifactKindsRemoved: left.artifactKinds.filter((kind) => !right.artifactKinds.includes(kind)),
                changedDecision: left.decisionImpact.kind !== right.decisionImpact.kind || left.decisionImpact.changedDecision !== right.decisionImpact.changedDecision
            }
        }),
        left,
        right
    };
}
export function loadBenchmarkIndexView(workspaceRoot, runsRoot = resolveRunsRoot(workspaceRoot), benchmarkLedgerPath) {
    const runsView = loadRunsIndexView(workspaceRoot, runsRoot);
    const allRuns = runsView.runs;
    const localRunIds = new Set(allRuns.map((run) => run.runId));
    const benchmarks = runsView.runs.flatMap((run) => {
        const detail = loadRunDetailView(workspaceRoot, run.runId, runsRoot, benchmarkLedgerPath);
        if (!detail) {
            return [];
        }
        return detail.artifacts.flatMap((artifact) => {
            if (artifact.artifactKind !== "benchmark-summary") {
                return [];
            }
            const rawBundle = JSON.parse(detail.rawBundleJson);
            const parsed = benchmarkArtifactSchema.safeParse(rawBundle.lifecycleArtifacts?.find((candidate) => isRecord(candidate) && candidate.artifactKind === "benchmark-summary"));
            if (!parsed.success) {
                return [];
            }
            const benchmark = parsed.data;
            return [toBenchmarkSummaryView(detail, benchmark, localRunIds)];
        });
    });
    return {
        benchmarks,
        invalidRuns: runsView.invalidRuns
    };
}
const STAGE_ORDER = [
    "planning",
    "design",
    "implementation",
    "qa",
    "security",
    "review",
    "release",
    "pipeline",
    "deployment",
    "promotion"
];
const STAGE_LABELS = {
    planning: "Planning",
    design: "Design",
    implementation: "Implementation",
    qa: "QA",
    security: "Security",
    review: "PR Review",
    release: "Release",
    pipeline: "Pipeline",
    deployment: "Deployment Gate",
    promotion: "Promotion Approval"
};
const ARTIFACT_STAGE_MAP = {
    "planning-brief": "planning",
    "design-record": "design",
    "implementation-proposal": "implementation",
    "qa-report": "qa",
    "security-report": "security",
    "review-report": "review",
    "release-report": "release",
    "pipeline-report": "pipeline",
    "deployment-gate-report": "deployment",
    "promotion-approval-report": "promotion"
};
const WORKFLOW_STAGE_MAP = {
    "planning-discovery": "planning",
    "architecture-design-review": "design",
    "implementation-proposal": "implementation",
    "qa-review": "qa",
    "security-review": "security",
    "pr-review": "review",
    "release-readiness": "release",
    "pipeline-evidence-review": "pipeline",
    "deployment-gate-review": "deployment",
    "promotion-approval": "promotion"
};
function safeParseJson(value) {
    try {
        return JSON.parse(value);
    }
    catch {
        return undefined;
    }
}
export function loadBenchmarkLedgerView(workspaceRoot, configuredBenchmarkLedgerPath) {
    const ledgerPath = resolveBenchmarkLedgerPath(workspaceRoot, configuredBenchmarkLedgerPath);
    try {
        statSync(ledgerPath);
    }
    catch {
        return {
            path: toRelativeDisplayPath(workspaceRoot, ledgerPath),
            entries: [],
            errors: []
        };
    }
    try {
        const raw = safeParseJson(readFileSync(ledgerPath, "utf8"));
        const parsed = benchmarkLedgerDocumentSchema.safeParse(raw);
        if (!parsed.success) {
            return {
                path: toRelativeDisplayPath(workspaceRoot, ledgerPath),
                entries: [],
                errors: parsed.error.issues.map((issue) => issue.message)
            };
        }
        const entries = parsed.data.entries;
        return {
            path: toRelativeDisplayPath(workspaceRoot, ledgerPath),
            entries,
            errors: []
        };
    }
    catch (error) {
        return {
            path: toRelativeDisplayPath(workspaceRoot, ledgerPath),
            entries: [],
            errors: [error instanceof Error ? error.message : "Failed to parse benchmark ledger."]
        };
    }
}
function findLedgerEntry(run, ledgerEntries) {
    return ledgerEntries.find((entry) => entry.arm === "agentforge" && (entry.runId === run.runId || entry.workflow === run.workflow));
}
function toQueryString(params) {
    const searchParams = new URLSearchParams();
    for (const [key, value] of Object.entries(params)) {
        if (value) {
            searchParams.set(key, value);
        }
    }
    const serialized = searchParams.toString();
    return serialized.length > 0 ? `?${serialized}` : "";
}
function toRunDetailHref(runId, anchor) {
    return `/runs/${encodeURIComponent(runId)}${anchor ? `#${anchor}` : ""}`;
}
function toRunsFilterHref(filters) {
    return `/runs${toQueryString({
        workflow: filters.workflow,
        status: filters.status,
        artifactKind: filters.artifactKind,
        search: filters.search,
        decisionImpact: filters.decisionImpact,
        riskKind: filters.riskKind,
        evidenceCategory: filters.evidenceCategory,
        evidenceStatus: filters.evidenceStatus,
        hasOverride: filters.hasOverride,
        workflowStage: filters.workflowStage
    })}`;
}
function toOutcomesFilterHref(filters, anchor) {
    return `/outcomes${toQueryString({
        panel: filters.panel,
        decision: filters.decision,
        risk: filters.risk,
        workflow: filters.workflow,
        evidenceCategory: filters.evidenceCategory,
        evidenceStatus: filters.evidenceStatus,
        stage: filters.stage
    })}${anchor ? `#${anchor}` : ""}`;
}
function parseArtifactPayload(artifact) {
    const parsed = safeParseJson(artifact.rawPayload);
    return isRecord(parsed) ? parsed : undefined;
}
function deriveStatusFromChecks(checks) {
    if (!Array.isArray(checks) || checks.length === 0) {
        return "missing";
    }
    const hasNonPassing = checks.some((check) => isRecord(check) && typeof check.status === "string" && check.status !== "passed");
    return hasNonPassing ? "partial" : "present";
}
function deriveCiEvidenceStatus(ciEvidenceSummary) {
    if (!Array.isArray(ciEvidenceSummary) || ciEvidenceSummary.length === 0) {
        return "missing";
    }
    const hasFailures = ciEvidenceSummary.some((entry) => isRecord(entry) && Array.isArray(entry.failingChecks) && entry.failingChecks.length > 0);
    return hasFailures ? "partial" : "present";
}
function deriveRiskKinds(findings, artifacts, ledgerEntry) {
    const kinds = new Set();
    for (const finding of findings) {
        kinds.add(`finding:${finding.severity}`);
    }
    if ((ledgerEntry?.confirmedRisks.high ?? 0) > 0) {
        kinds.add("confirmed-high");
    }
    if ((ledgerEntry?.confirmedRisks.medium ?? 0) > 0) {
        kinds.add("confirmed-medium");
    }
    if ((ledgerEntry?.confirmedRisks.noisy ?? 0) > 0) {
        kinds.add("noisy");
    }
    if ((ledgerEntry?.confirmedRisks.unresolved ?? 0) > 0) {
        kinds.add("unresolved");
    }
    if (artifacts.some((artifact) => readStringArray(parseArtifactPayload(artifact)?.blockers).length > 0)) {
        kinds.add("artifact-blocker");
    }
    if (artifacts.some((artifact) => {
        const payload = parseArtifactPayload(artifact);
        return payload?.gateStatus === "blocked" || payload?.approvalStatus === "blocked" || payload?.readinessStatus === "blocked" || payload?.reviewStatus === "blocked";
    })) {
        kinds.add("blocked-approval");
    }
    return [...kinds].sort();
}
function buildEvidenceCompleteness(workflow, artifacts) {
    return artifacts.flatMap((artifact) => {
        const payload = parseArtifactPayload(artifact);
        if (!payload) {
            return [];
        }
        const referencedKinds = readStringArray(payload.referencedArtifactKinds);
        const categories = [];
        const traceRefs = [
            {
                artifactKind: artifact.artifactKind,
                section: "raw-payload",
                note: `Derived from ${artifact.artifactKind} payload.`
            }
        ];
        const addCategory = (key, label, status, detail, fields) => {
            categories.push({
                key,
                label,
                status,
                detail,
                traceRefs,
                reason: {
                    source: "inferred",
                    rule: `${label} classification`,
                    fields,
                    summary: detail
                }
            });
        };
        switch (artifact.artifactKind) {
            case "qa-report":
                addCategory("evidence-sources", "Evidence Sources", readStringArray(payload.evidenceSources).length > 0 ? "present" : "missing", readStringArray(payload.evidenceSources).length > 0 ? "Referenced QA evidence is present." : "No QA evidence sources were recorded.", ["payload.evidenceSources"]);
                addCategory("executed-checks", "Executed Checks", readStringArray(payload.executedChecks).length > 0 ? "present" : "missing", readStringArray(payload.executedChecks).length > 0 ? "Validation commands were captured." : "No validation commands were captured.", ["payload.executedChecks"]);
                addCategory("ci-evidence", "CI Evidence", deriveCiEvidenceStatus(payload.ciEvidenceSummary), deriveCiEvidenceStatus(payload.ciEvidenceSummary) === "missing"
                    ? "No CI evidence summary was recorded."
                    : deriveCiEvidenceStatus(payload.ciEvidenceSummary) === "partial"
                        ? "CI evidence exists but includes failing checks."
                        : "CI evidence summary is present and green.", ["payload.ciEvidenceSummary", "payload.ciEvidenceSummary[].failingChecks"]);
                break;
            case "release-report":
                addCategory("version-targets", "Version Targets", Array.isArray(payload.versionTargets) && payload.versionTargets.length > 0 ? "present" : "missing", Array.isArray(payload.versionTargets) && payload.versionTargets.length > 0 ? "Version targets are recorded." : "No version targets were recorded.", ["payload.versionTargets"]);
                addCategory("verification-checks", "Verification Checks", deriveStatusFromChecks(payload.verificationChecks), deriveStatusFromChecks(payload.verificationChecks) === "missing" ? "No verification checks were recorded." : deriveStatusFromChecks(payload.verificationChecks) === "partial" ? "Verification checks include non-passing results." : "Verification checks are present and passed.", ["payload.verificationChecks", "payload.verificationChecks[].status"]);
                addCategory("ci-evidence", "CI Evidence", deriveCiEvidenceStatus(payload.ciEvidenceSummary), deriveCiEvidenceStatus(payload.ciEvidenceSummary) === "missing" ? "No CI evidence summary was recorded." : deriveCiEvidenceStatus(payload.ciEvidenceSummary) === "partial" ? "CI evidence includes failing checks." : "CI evidence summary is present and green.", ["payload.ciEvidenceSummary", "payload.ciEvidenceSummary[].failingChecks"]);
                addCategory("provenance", "Provenance Refs", readStringArray(payload.provenanceRefs).length > 0 ? "present" : "missing", readStringArray(payload.provenanceRefs).length > 0 ? "Release provenance refs were captured." : "No provenance refs were captured.", ["payload.provenanceRefs"]);
                break;
            case "pipeline-report":
                addCategory("ci-evidence", "CI Evidence", deriveCiEvidenceStatus(payload.ciEvidenceSummary), deriveCiEvidenceStatus(payload.ciEvidenceSummary) === "missing" ? "No CI evidence summary was recorded." : deriveCiEvidenceStatus(payload.ciEvidenceSummary) === "partial" ? "CI evidence includes failing checks." : "CI evidence summary is present and green.", ["payload.ciEvidenceSummary", "payload.ciEvidenceSummary[].failingChecks"]);
                addCategory("qa-report", "QA Report", referencedKinds.includes("qa-report") ? "present" : "missing", referencedKinds.includes("qa-report") ? "A QA report reference is present." : "No QA report reference was recorded.", ["payload.referencedArtifactKinds"]);
                addCategory("security-report", "Security Report", referencedKinds.includes("security-report") ? "present" : "missing", referencedKinds.includes("security-report") ? "A security report reference is present." : "No security report reference was recorded.", ["payload.referencedArtifactKinds"]);
                addCategory("release-report", "Release Report", referencedKinds.includes("release-report") ? "present" : "missing", referencedKinds.includes("release-report") ? "A release report reference is present." : "No release report reference was recorded.", ["payload.referencedArtifactKinds"]);
                addCategory("verification-checks", "Verification Checks", deriveStatusFromChecks(payload.verificationChecks), deriveStatusFromChecks(payload.verificationChecks) === "missing" ? "No verification checks were recorded." : deriveStatusFromChecks(payload.verificationChecks) === "partial" ? "Verification checks include non-passing results." : "Verification checks are present and passed.", ["payload.verificationChecks", "payload.verificationChecks[].status"]);
                break;
            case "deployment-gate-report":
                addCategory("ci-evidence", "CI Evidence", deriveCiEvidenceStatus(payload.ciEvidenceSummary), deriveCiEvidenceStatus(payload.ciEvidenceSummary) === "missing" ? "No CI evidence summary was recorded." : deriveCiEvidenceStatus(payload.ciEvidenceSummary) === "partial" ? "CI evidence includes failing checks." : "CI evidence summary is present and green.", ["payload.ciEvidenceSummary", "payload.ciEvidenceSummary[].failingChecks"]);
                addCategory("qa-report", "QA Report", referencedKinds.includes("qa-report") ? "present" : "missing", referencedKinds.includes("qa-report") ? "A QA report reference is present." : "No QA report reference was recorded.", ["payload.referencedArtifactKinds"]);
                addCategory("security-report", "Security Report", referencedKinds.includes("security-report") ? "present" : "missing", referencedKinds.includes("security-report") ? "A security report reference is present." : "No security report reference was recorded.", ["payload.referencedArtifactKinds"]);
                addCategory("release-report", "Release Report", referencedKinds.includes("release-report") ? "present" : "missing", referencedKinds.includes("release-report") ? "A release report reference is present." : "No release report reference was recorded.", ["payload.referencedArtifactKinds"]);
                addCategory("pipeline-report", "Pipeline Report", referencedKinds.includes("pipeline-report") ? "present" : "missing", referencedKinds.includes("pipeline-report") ? "A pipeline report reference is present." : "No pipeline report reference was recorded.", ["payload.referencedArtifactKinds"]);
                break;
            case "promotion-approval-report":
                addCategory("ci-evidence", "CI Evidence", deriveCiEvidenceStatus(payload.ciEvidenceSummary), deriveCiEvidenceStatus(payload.ciEvidenceSummary) === "missing" ? "No CI evidence summary was recorded." : deriveCiEvidenceStatus(payload.ciEvidenceSummary) === "partial" ? "CI evidence includes failing checks." : "CI evidence summary is present and green.", ["payload.ciEvidenceSummary", "payload.ciEvidenceSummary[].failingChecks"]);
                addCategory("qa-report", "QA Report", referencedKinds.includes("qa-report") ? "present" : "missing", referencedKinds.includes("qa-report") ? "A QA report reference is present." : "No QA report reference was recorded.", ["payload.referencedArtifactKinds"]);
                addCategory("security-report", "Security Report", referencedKinds.includes("security-report") ? "present" : "missing", referencedKinds.includes("security-report") ? "A security report reference is present." : "No security report reference was recorded.", ["payload.referencedArtifactKinds"]);
                addCategory("release-report", "Release Report", referencedKinds.includes("release-report") ? "present" : "missing", referencedKinds.includes("release-report") ? "A release report reference is present." : "No release report reference was recorded.", ["payload.referencedArtifactKinds"]);
                addCategory("deployment-gate-report", "Deployment Gate", referencedKinds.includes("deployment-gate-report") ? "present" : "missing", referencedKinds.includes("deployment-gate-report") ? "A deployment gate report reference is present." : "No deployment gate report reference was recorded.", ["payload.referencedArtifactKinds"]);
                addCategory("required-approvals", "Required Approvals", readStringArray(payload.requiredApprovals).length > 0 ? "present" : "missing", readStringArray(payload.requiredApprovals).length > 0 ? "Required approvals were captured." : "No required approvals were captured.", ["payload.requiredApprovals"]);
                break;
            default:
                break;
        }
        return categories.length > 0 ? [{ workflow, categories }] : [];
    });
}
function buildWorkflowChain(workflow, artifacts) {
    const primaryArtifact = artifacts.find((artifact) => ARTIFACT_STAGE_MAP[artifact.artifactKind]);
    const currentStage = primaryArtifact ? ARTIFACT_STAGE_MAP[primaryArtifact.artifactKind] : WORKFLOW_STAGE_MAP[workflow] ?? "review";
    const payload = primaryArtifact ? parseArtifactPayload(primaryArtifact) : undefined;
    const referencedKinds = payload ? new Set(readStringArray(payload.referencedArtifactKinds)) : new Set();
    const currentStageIndex = STAGE_ORDER.indexOf(currentStage);
    return {
        currentStage,
        stages: STAGE_ORDER.map((stage) => {
            const artifactKind = Object.entries(ARTIFACT_STAGE_MAP).find(([, mappedStage]) => mappedStage === stage)?.[0];
            const stageIndex = STAGE_ORDER.indexOf(stage);
            const status = stage === currentStage ? "current" : artifactKind && referencedKinds.has(artifactKind) ? "present" : "missing";
            const required = stageIndex < currentStageIndex;
            const detail = status === "current"
                ? `Current run is operating at the ${STAGE_LABELS[stage]} stage.`
                : status === "present"
                    ? `A ${artifactKind} reference is present for this stage.`
                    : required
                        ? `No upstream ${STAGE_LABELS[stage]} evidence is referenced from this run.`
                        : `This downstream stage has not run yet from the current evidence chain.`;
            const traceRefs = artifactKind && referencedKinds.has(artifactKind)
                ? [{ artifactKind, section: "referenced-artifact-kinds", note: `${artifactKind} is referenced by the current workflow artifact.` }]
                : primaryArtifact
                    ? [{ artifactKind: primaryArtifact.artifactKind, section: "referenced-artifact-kinds", note: detail }]
                    : [{ note: detail }];
            return {
                stage,
                label: STAGE_LABELS[stage],
                status,
                detail,
                required,
                traceRefs,
                reason: {
                    source: "inferred",
                    rule: status === "current" ? "current-stage" : status === "present" ? "referenced-upstream-stage" : required ? "missing-upstream-evidence" : "downstream-stage-not-run",
                    fields: artifactKind ? ["payload.referencedArtifactKinds", "artifact.artifactKind"] : ["workflow"],
                    summary: detail
                }
            };
        })
    };
}
function extractRunIdFromRef(ref) {
    const match = ref.match(/\.agentops\/runs\/([^/]+)\/bundle\.json$/);
    return match?.[1];
}
function extractRunIdFromText(text) {
    const match = text.match(/\.agentops\/runs\/([^/]+)\/bundle\.json/);
    return match?.[1];
}
function extractReferencedRunIds(artifacts) {
    return [...new Set(artifacts.flatMap((artifact) => artifact.sourceRefs.map(extractRunIdFromRef).filter((value) => Boolean(value))))];
}
function isReleaseChainStage(stage) {
    return stage === "release" || stage === "pipeline" || stage === "deployment" || stage === "promotion";
}
function resolveReleaseChainRootRunId(run, runsById, visited = new Set()) {
    if (!isReleaseChainStage(run.workflowChain.currentStage)) {
        return run.runId;
    }
    if (run.workflowChain.currentStage === "release" || visited.has(run.runId)) {
        return run.runId;
    }
    const nextVisited = new Set(visited);
    nextVisited.add(run.runId);
    const referencedRuns = extractReferencedRunIds(run.artifacts)
        .map((runId) => runsById.get(runId))
        .filter((candidate) => Boolean(candidate));
    const releaseAncestor = referencedRuns.find((candidate) => candidate.workflowChain.currentStage === "release");
    if (releaseAncestor) {
        return releaseAncestor.runId;
    }
    const releaseChainAncestor = referencedRuns.find((candidate) => isReleaseChainStage(candidate.workflowChain.currentStage));
    if (releaseChainAncestor) {
        return resolveReleaseChainRootRunId(releaseChainAncestor, runsById, nextVisited);
    }
    return run.runId;
}
function describeBlockedApproval(run, artifact) {
    const payload = parseArtifactPayload(artifact);
    const blockerMessages = [
        ...readStringArray(payload?.blockers),
        ...readRecordArray(payload?.verificationChecks)
            .filter((check) => readString(check.status, "") === "failed")
            .map((check) => readString(check.detail, ""))
            .filter((detail) => detail.length > 0)
    ];
    const blockerRunId = blockerMessages.map(extractRunIdFromText).find((value) => Boolean(value));
    if (artifact.artifactKind === "pipeline-report") {
        const failingEvidence = blockerMessages.find((message) => message.includes("Imported CI evidence still reports failing checks"));
        if (failingEvidence) {
            return {
                summary: "Derived as blocked approval because the pipeline stage still has failing imported CI evidence in the current release chain.",
                traceRefs: [
                    {
                        runId: run.runId,
                        artifactKind: artifact.artifactKind,
                        section: "blockers",
                        note: failingEvidence
                    }
                ]
            };
        }
    }
    if (artifact.artifactKind === "deployment-gate-report") {
        const blockedPipelineMessage = blockerMessages.find((message) => message.includes("Pipeline report"));
        if (blockedPipelineMessage) {
            return {
                summary: "Derived as blocked approval because this deployment gate still depends on a blocked pipeline report in the same release chain.",
                traceRefs: [
                    {
                        runId: blockerRunId,
                        artifactKind: "pipeline-report",
                        section: "decision-impact",
                        note: blockedPipelineMessage
                    }
                ]
            };
        }
    }
    if (artifact.artifactKind === "promotion-approval-report") {
        const blockedDeploymentMessage = blockerMessages.find((message) => message.includes("Deployment gate report"));
        if (blockedDeploymentMessage) {
            return {
                summary: "Derived as blocked approval because promotion still depends on a blocked deployment gate report in the same release chain.",
                traceRefs: [
                    {
                        runId: blockerRunId,
                        artifactKind: "deployment-gate-report",
                        section: "decision-impact",
                        note: blockedDeploymentMessage
                    }
                ]
            };
        }
    }
    return {
        summary: `Derived as blocked approval because ${artifact.artifactKind} reported a blocked state.`,
        traceRefs: [
            {
                runId: blockerRunId ?? run.runId,
                artifactKind: artifact.artifactKind,
                section: "decision-impact",
                note: blockerMessages[0] ?? `Blocked status derived from ${artifact.artifactKind}.`
            }
        ]
    };
}
function buildRiskSummary(run, findings, artifacts, ledgerEntries) {
    const ledgerEntry = findLedgerEntry(run, ledgerEntries);
    const high = ledgerEntry?.confirmedRisks.high ?? findings.filter((finding) => finding.severity === "high").length;
    const medium = ledgerEntry?.confirmedRisks.medium ?? findings.filter((finding) => finding.severity === "medium").length;
    const low = ledgerEntry?.confirmedRisks.low ?? findings.filter((finding) => finding.severity === "low").length;
    const noisy = ledgerEntry?.confirmedRisks.noisy ?? 0;
    const blockerCount = artifacts.reduce((total, artifact) => {
        const payload = parseArtifactPayload(artifact);
        return total + readStringArray(payload?.blockers).length;
    }, 0);
    const blockedApprovalPrevented = artifacts.some((artifact) => {
        const payload = parseArtifactPayload(artifact);
        const statusCandidates = [
            artifact.status,
            typeof payload?.readinessStatus === "string" ? payload.readinessStatus : undefined,
            typeof payload?.reviewStatus === "string" ? payload.reviewStatus : undefined,
            typeof payload?.gateStatus === "string" ? payload.gateStatus : undefined,
            typeof payload?.approvalStatus === "string" ? payload.approvalStatus : undefined
        ];
        return statusCandidates.includes("blocked");
    });
    const unresolved = ledgerEntry?.confirmedRisks.unresolved ?? high + medium + blockerCount;
    const summary = [];
    if (high + medium > 0) {
        summary.push(`${high + medium} medium/high risk item(s) were surfaced for this run.`);
    }
    if (blockedApprovalPrevented) {
        summary.push("This run prevented a blocked approval or release decision from appearing ready.");
    }
    if (noisy > 0) {
        summary.push(`${noisy} finding(s) were later judged as noise in the benchmark ledger.`);
    }
    if (summary.length === 0) {
        summary.push("No significant risk signals were recorded for this run.");
    }
    const traceRefs = [];
    for (const finding of findings) {
        traceRefs.push({
            runId: run.runId,
            findingId: finding.id,
            section: "risk-summary",
            note: `[${finding.severity}] ${finding.title}`
        });
    }
    for (const artifact of artifacts) {
        const payload = parseArtifactPayload(artifact);
        for (const blocker of readStringArray(payload?.blockers)) {
            traceRefs.push({
                runId: run.runId,
                artifactKind: artifact.artifactKind,
                section: "blockers",
                note: blocker
            });
        }
    }
    return {
        high,
        medium,
        low,
        noisy,
        unresolved,
        blockedApprovalPrevented,
        blockerCount,
        summary,
        traceRefs,
        reason: {
            source: findLedgerEntry(run, ledgerEntries) ? "ledger" : traceRefs.length > 0 ? "artifact-blockers" : "run-findings",
            rule: findLedgerEntry(run, ledgerEntries) ? "benchmark-ledger-risk-overlay" : blockerCount > 0 ? "artifact-blockers-and-findings" : "findings-only",
            fields: findLedgerEntry(run, ledgerEntries)
                ? ["benchmark-ledger.confirmedRisks", "benchmark-ledger.confirmedRiskRefs"]
                : blockerCount > 0
                    ? ["bundle.findings", "artifact.payload.blockers"]
                    : ["bundle.findings"],
            summary: summary[0] ?? "No significant risk signals were recorded for this run."
        }
    };
}
function inferDecisionImpact(run, findings, artifacts, ledgerEntries) {
    const ledgerEntry = findLedgerEntry(run, ledgerEntries);
    if (ledgerEntry?.decisionOutcome) {
        return {
            kind: ledgerEntry.decisionOutcome,
            changedDecision: ledgerEntry.agentforgeChangedDecision ?? !["added_confidence", "no_meaningful_change"].includes(ledgerEntry.decisionOutcome),
            source: "ledger",
            summary: `Benchmark ledger marked this run as ${ledgerEntry.decisionOutcome.replaceAll("_", " ")}.`,
            benchmarkTaskId: ledgerEntry.taskId,
            traceRefs: ledgerEntry.triggerRefs,
            reason: {
                source: "ledger",
                rule: "benchmark-ledger-decision-outcome",
                fields: ["benchmark-ledger.decisionOutcome", "benchmark-ledger.agentforgeChangedDecision", "benchmark-ledger.triggerRefs"],
                summary: ledgerEntry.decisionImpactReason ?? ledgerEntry.summary ?? `Adjudicated benchmark result: ${ledgerEntry.decisionOutcome.replaceAll("_", " ")}.`
            }
        };
    }
    const evidence = buildEvidenceCompleteness(run.workflow, artifacts);
    const blockerArtifact = artifacts.find((artifact) => {
        const payload = parseArtifactPayload(artifact);
        return artifact.status === "blocked" || payload?.gateStatus === "blocked" || payload?.approvalStatus === "blocked" || payload?.reviewStatus === "blocked" || payload?.readinessStatus === "blocked";
    });
    if (blockerArtifact) {
        const blockedApproval = describeBlockedApproval(run, blockerArtifact);
        return {
            kind: "blocked_approval",
            changedDecision: true,
            source: "inferred",
            summary: `The run emitted a blocked ${blockerArtifact.artifactKind}, which would halt approval or release flow.`,
            traceRefs: blockedApproval.traceRefs,
            reason: {
                source: "inferred",
                rule: "blocked-artifact-status",
                fields: ["artifact.status", "payload.readinessStatus", "payload.reviewStatus", "payload.gateStatus", "payload.approvalStatus"],
                summary: blockedApproval.summary
            }
        };
    }
    if (artifacts.some((artifact) => readStringArray(parseArtifactPayload(artifact)?.blockers).length > 0) || findings.length > 0 || run.status !== "success") {
        return {
            kind: "remediation_before_merge",
            changedDecision: true,
            source: "inferred",
            summary: "The run surfaced blockers or findings that imply remediation before merge or promotion.",
            traceRefs: [
                ...findings.map((finding) => ({
                    runId: run.runId,
                    findingId: finding.id,
                    section: "findings",
                    note: `[${finding.severity}] ${finding.title}`
                })),
                ...artifacts.flatMap((artifact) => readStringArray(parseArtifactPayload(artifact)?.blockers).map((blocker) => ({
                    runId: run.runId,
                    artifactKind: artifact.artifactKind,
                    section: "blockers",
                    note: blocker
                })))
            ],
            reason: {
                source: "inferred",
                rule: "findings-or-blockers-present",
                fields: ["bundle.findings", "artifact.payload.blockers", "bundle.status"],
                summary: "Derived as remediation before merge because findings, blockers, or non-success bundle status were present."
            }
        };
    }
    if (evidence.some((artifact) => artifact.categories.some((category) => category.status !== "present"))) {
        const firstGap = evidence.flatMap((artifact) => artifact.categories).find((category) => category.status !== "present");
        return {
            kind: "added_validation",
            changedDecision: true,
            source: "inferred",
            summary: "The run highlighted missing or partial evidence that should change the validation plan.",
            traceRefs: firstGap?.traceRefs ?? [{ runId: run.runId, section: "evidence-completeness", note: "Missing or partial evidence was detected." }],
            reason: {
                source: "inferred",
                rule: "evidence-completeness-gap",
                fields: firstGap?.reason.fields ?? ["artifact.payload"],
                summary: firstGap
                    ? `Derived as added validation because ${firstGap.label} is ${firstGap.status}: ${firstGap.detail}`
                    : "Derived as added validation because at least one evidence category was not fully present."
            }
        };
    }
    if (run.workflow === "planning-discovery") {
        return {
            kind: "added_confidence",
            changedDecision: false,
            source: "inferred",
            summary: "The planning run improved scope clarity but did not clearly force a different implementation path.",
            traceRefs: [{ runId: run.runId, section: "decision-impact", note: "Planning runs improve confidence unless stronger blockers or evidence gaps exist." }],
            reason: {
                source: "inferred",
                rule: "planning-confidence-default",
                fields: ["workflow"],
                summary: "Derived as added confidence because the workflow is planning-discovery and no stronger outcome rule matched."
            }
        };
    }
    return {
        kind: "no_meaningful_change",
        changedDecision: false,
        source: "inferred",
        summary: "The run completed cleanly without a clear decision delta beyond baseline inspection.",
        traceRefs: [{ runId: run.runId, section: "decision-impact", note: "No blockers, findings, or evidence gaps changed the inferred outcome." }],
        reason: {
            source: "inferred",
            rule: "clean-run-default",
            fields: ["bundle.findings", "artifact.payload.blockers", "evidence-completeness"],
            summary: "Derived as no meaningful change because the run completed cleanly and no decision-changing signals were found."
        }
    };
}
function countOutcome(runs, kind) {
    return runs.filter((run) => run.decisionImpact.kind === kind).length;
}
function aggregateFrequentMissing(categories) {
    const counts = new Map();
    for (const artifact of categories) {
        for (const category of artifact.categories) {
            if (category.status === "missing") {
                counts.set(category.label, (counts.get(category.label) ?? 0) + 1);
            }
        }
    }
    return [...counts.entries()]
        .sort((left, right) => right[1] - left[1] || left[0].localeCompare(right[0]))
        .slice(0, 3)
        .map(([label, count]) => `${label} (${count})`);
}
function aggregateWorkflowChainSummary(runs) {
    return STAGE_ORDER.map((stage) => {
        const stageRuns = runs.filter((run) => run.workflowChain.currentStage === stage);
        return {
            stage,
            label: STAGE_LABELS[stage],
            runCount: stageRuns.length,
            blockedCount: stageRuns.filter((run) => run.decisionImpact.kind === "blocked_approval").length,
            missingUpstreamEvidenceCount: stageRuns.filter((run) => run.workflowChain.stages.some((candidate) => candidate.status === "missing" && STAGE_ORDER.indexOf(candidate.stage) < STAGE_ORDER.indexOf(stage))).length
        };
    }).filter((stage) => stage.runCount > 0);
}
function aggregateConfigurationHotspots(runs) {
    const grouped = new Map();
    const addEntry = (dimension, value, run) => {
        const key = `${dimension}:${value}`;
        const existing = grouped.get(key);
        if (existing) {
            existing.runs += 1;
            existing.changedDecisions += run.decisionImpact.changedDecision ? 1 : 0;
            existing.blockedActions += run.blockedActions;
            if (!existing.workflows.includes(run.workflow)) {
                existing.workflows.push(run.workflow);
                existing.workflows.sort();
            }
            return;
        }
        grouped.set(key, {
            dimension,
            value,
            runs: 1,
            changedDecisions: run.decisionImpact.changedDecision ? 1 : 0,
            blockedActions: run.blockedActions,
            workflows: [run.workflow]
        });
    };
    for (const run of runs) {
        if (!run.configuration) {
            continue;
        }
        addEntry("profile", run.configuration.profile, run);
        addEntry("policyPreset", run.configuration.policyPreset ?? "default", run);
        addEntry("workflowVariant", run.configuration.workflowVariant, run);
        for (const binding of run.configuration.agentBindings) {
            addEntry("agentBinding", binding, run);
        }
    }
    return [...grouped.values()].sort((left, right) => right.changedDecisions - left.changedDecisions
        || right.blockedActions - left.blockedActions
        || right.runs - left.runs
        || left.value.localeCompare(right.value));
}
function buildLeadershipRuns(runs) {
    const runsById = new Map(runs.map((run) => [run.runId, run]));
    const grouped = new Map();
    for (const run of runs) {
        if (!isReleaseChainStage(run.workflowChain.currentStage)) {
            grouped.set(`run:${run.runId}`, run);
            continue;
        }
        const rootRunId = resolveReleaseChainRootRunId(run, runsById);
        const key = `release-chain:${rootRunId}`;
        const current = grouped.get(key);
        if (!current) {
            grouped.set(key, {
                ...run,
                workflowChain: {
                    ...run.workflowChain,
                    chainKey: key
                }
            });
            continue;
        }
        const currentStageIndex = STAGE_ORDER.indexOf(current.workflowChain.currentStage);
        const nextStageIndex = STAGE_ORDER.indexOf(run.workflowChain.currentStage);
        if (nextStageIndex >= currentStageIndex) {
            grouped.set(key, {
                ...run,
                workflowChain: {
                    ...run.workflowChain,
                    chainKey: key
                }
            });
        }
    }
    return [...grouped.values()].sort((left, right) => {
        const leftCompletedAt = parseRunTimestampMs(left.finishedAt) ?? parseRunTimestampMs(left.startedAt) ?? parseRunTimestampMs(left.runId) ?? 0;
        const rightCompletedAt = parseRunTimestampMs(right.finishedAt) ?? parseRunTimestampMs(right.startedAt) ?? parseRunTimestampMs(right.runId) ?? 0;
        if (leftCompletedAt === rightCompletedAt) {
            return right.runId.localeCompare(left.runId);
        }
        return rightCompletedAt - leftCompletedAt;
    });
}
function aggregateFriction(runs, ledger) {
    const agentforgeEntries = ledger.entries.filter((entry) => entry.arm === "agentforge");
    const workflowCounts = new Map();
    const repeatedOverrideReasons = new Map();
    const noisyPatterns = new Map();
    for (const entry of agentforgeEntries) {
        const workflow = entry.workflow ?? "unknown";
        const current = workflowCounts.get(workflow) ?? {
            workflow,
            overrideCount: 0,
            falsePositiveCount: 0,
            manualStepCount: 0,
            requestFrictionCount: 0
        };
        current.overrideCount += entry.friction.override ? 1 : 0;
        current.falsePositiveCount += entry.friction.falsePositivePatterns.length;
        current.manualStepCount += entry.friction.manualSteps.length;
        current.requestFrictionCount += entry.friction.requestFriction.length;
        workflowCounts.set(workflow, current);
        if (entry.friction.overrideReason) {
            repeatedOverrideReasons.set(entry.friction.overrideReason, (repeatedOverrideReasons.get(entry.friction.overrideReason) ?? 0) + 1);
        }
        for (const pattern of entry.friction.falsePositivePatterns) {
            noisyPatterns.set(pattern, (noisyPatterns.get(pattern) ?? 0) + 1);
        }
    }
    return {
        ledgerAvailable: ledger.entries.length > 0 || ledger.errors.length > 0,
        overrideCount: agentforgeEntries.filter((entry) => entry.friction.override).length,
        falsePositiveCount: agentforgeEntries.reduce((total, entry) => total + entry.friction.falsePositivePatterns.length, 0),
        manualStepCount: agentforgeEntries.reduce((total, entry) => total + entry.friction.manualSteps.length, 0),
        requestFrictionCount: agentforgeEntries.reduce((total, entry) => total + entry.friction.requestFriction.length, 0),
        repeatedOverrideReasons: [...repeatedOverrideReasons.entries()]
            .sort((left, right) => right[1] - left[1] || left[0].localeCompare(right[0]))
            .slice(0, 5)
            .map(([reason, count]) => `${reason} (${count})`),
        noisyPatterns: [...noisyPatterns.entries()]
            .sort((left, right) => right[1] - left[1] || left[0].localeCompare(right[0]))
            .slice(0, 5)
            .map(([pattern, count]) => `${pattern} (${count})`),
        workflowHotspots: [...workflowCounts.values()].sort((left, right) => right.overrideCount +
            right.falsePositiveCount +
            right.manualStepCount +
            right.requestFrictionCount -
            (left.overrideCount + left.falsePositiveCount + left.manualStepCount + left.requestFrictionCount)),
    };
}
function deriveReleaseCycleTimeSeconds(entry) {
    if (typeof entry.cycleTimeSeconds === "number") {
        return entry.cycleTimeSeconds;
    }
    if (!entry.startedAt || !entry.finishedAt) {
        return undefined;
    }
    const startedMs = Date.parse(entry.startedAt);
    const finishedMs = Date.parse(entry.finishedAt);
    if (Number.isNaN(startedMs) || Number.isNaN(finishedMs) || finishedMs < startedMs) {
        return undefined;
    }
    return Math.round((finishedMs - startedMs) / 1000);
}
function median(values) {
    if (values.length === 0) {
        return undefined;
    }
    const sorted = [...values].sort((left, right) => left - right);
    const middle = Math.floor(sorted.length / 2);
    if (sorted.length % 2 === 1) {
        return sorted[middle];
    }
    const left = sorted[middle - 1];
    const right = sorted[middle];
    if (left === undefined || right === undefined) {
        return undefined;
    }
    return Math.round((left + right) / 2);
}
function toReleaseBenchmarkSummary(arm, entries) {
    const cycleTimes = entries
        .map((entry) => deriveReleaseCycleTimeSeconds(entry))
        .filter((value) => typeof value === "number");
    const measuredTokenEntries = entries.filter((entry) => typeof entry.tokenUsage?.inputTokens === "number" ||
        typeof entry.tokenUsage?.outputTokens === "number" ||
        typeof entry.tokenUsage?.totalTokens === "number");
    const totalInputTokens = entries.reduce((total, entry) => total + (entry.tokenUsage?.inputTokens ?? 0), 0);
    const totalOutputTokens = entries.reduce((total, entry) => total + (entry.tokenUsage?.outputTokens ?? 0), 0);
    const totalTokens = entries.reduce((total, entry) => total + (entry.tokenUsage?.totalTokens ?? 0), 0);
    const totalRequests = entries.reduce((total, entry) => total + (entry.tokenUsage?.requestCount ?? 0), 0);
    const knownCostEntries = entries.filter((entry) => typeof entry.tokenUsage?.estimatedCostUsd === "number");
    const totalEstimatedCostUsd = knownCostEntries.length > 0
        ? knownCostEntries.reduce((total, entry) => total + (entry.tokenUsage?.estimatedCostUsd ?? 0), 0)
        : undefined;
    const confirmedRiskCount = entries.reduce((total, entry) => total + entry.confirmedRisks.high + entry.confirmedRisks.medium + entry.confirmedRisks.low, 0);
    const blockedReleaseCount = entries.filter((entry) => entry.decisionOutcome === "blocked_approval" || entry.releaseDecision === "no-go").length;
    return {
        arm,
        entryCount: entries.length,
        medianCycleTimeSeconds: median(cycleTimes),
        clearDecisionCount: entries.filter((entry) => entry.decisionClarity === "clear").length,
        ambiguousDecisionCount: entries.filter((entry) => entry.decisionClarity === "ambiguous").length,
        blockedReleaseCount,
        confirmedRiskCount,
        totalInputTokens,
        totalOutputTokens,
        totalTokens,
        totalRequests,
        measuredTokenEntryCount: measuredTokenEntries.length,
        totalEstimatedCostUsd,
        knownCostEntryCount: knownCostEntries.length,
        costPerConfirmedRiskCaught: typeof totalEstimatedCostUsd === "number" && confirmedRiskCount > 0 ? totalEstimatedCostUsd / confirmedRiskCount : undefined,
        costPerBlockedPrematureRelease: typeof totalEstimatedCostUsd === "number" && blockedReleaseCount > 0 ? totalEstimatedCostUsd / blockedReleaseCount : undefined
    };
}
function aggregateReleaseBenchmark(ledger) {
    const entries = ledger.entries.filter((entry) => entry.benchmarkCategory === "release");
    const arms = ["control", "agentforge"];
    const summaries = arms.map((arm) => toReleaseBenchmarkSummary(arm, entries.filter((entry) => entry.arm === arm)));
    const comparablePairs = new Set(entries
        .filter((entry) => entry.arm === "agentforge")
        .map((entry) => entry.taskId)
        .filter((taskId) => entries.some((entry) => entry.arm === "control" && entry.taskId === taskId))).size;
    return {
        available: entries.length > 0,
        comparablePairs,
        totalEntries: entries.length,
        arms: summaries
    };
}
function matchesOutcomeFilters(row, filters) {
    if (filters.workflow && row.workflow !== filters.workflow) {
        return false;
    }
    if (filters.panel === "decision-impact") {
        if (filters.decision === "changed" && !row.id.includes("changed")) {
            return false;
        }
        if (filters.decision && filters.decision !== "changed" && !row.id.includes(filters.decision)) {
            return false;
        }
    }
    if (filters.panel === "risk" && filters.risk && !row.id.includes(filters.risk)) {
        return false;
    }
    if (filters.panel === "release-benchmark") {
        if (filters.arm && !row.id.includes(`:${filters.arm}:`)) {
            return false;
        }
        if (filters.releaseDecision && !row.id.includes(`:${filters.releaseDecision}:`)) {
            return false;
        }
    }
    if (filters.panel === "evidence") {
        if (filters.evidenceCategory && !row.id.includes(filters.evidenceCategory)) {
            return false;
        }
        if (filters.evidenceStatus && !row.id.includes(filters.evidenceStatus)) {
            return false;
        }
    }
    if (filters.panel === "flow" && filters.stage && !row.id.includes(filters.stage)) {
        return false;
    }
    return true;
}
function createOutcomeDetailRows(runs, ledger, filters) {
    const decision = runs
        .map((run) => ({
        id: `decision:${run.decisionImpact.kind}:${run.decisionImpact.changedDecision ? "changed" : "unchanged"}:${run.runId}`,
        workflow: run.workflow,
        runId: run.runId,
        status: run.status,
        title: run.decisionImpact.kind.replaceAll("_", " "),
        summary: run.decisionImpact.reason.summary,
        source: run.decisionImpact.source,
        detailHref: toRunDetailHref(run.runId, "decision-impact"),
        runsHref: toRunsFilterHref({ decisionImpact: run.decisionImpact.kind }),
        traceRefs: run.decisionImpact.traceRefs
    }))
        .filter((row) => matchesOutcomeFilters(row, filters));
    const risk = runs
        .flatMap((run) => run.riskSummary.traceRefs.length > 0
        ? run.riskSummary.traceRefs.map((traceRef, index) => ({
            id: `risk:${run.riskKinds[0] ?? "unresolved"}:${run.runId}:${index}`,
            workflow: run.workflow,
            runId: run.runId,
            status: run.status,
            title: traceRef.note,
            summary: run.riskSummary.reason.summary,
            source: run.riskSummary.reason.source,
            detailHref: toRunDetailHref(run.runId, "risk-summary"),
            runsHref: toRunsFilterHref({ riskKind: run.riskKinds[0] ?? "unresolved" }),
            traceRefs: [traceRef]
        }))
        : [{
                id: `risk:none:${run.runId}`,
                workflow: run.workflow,
                runId: run.runId,
                status: run.status,
                title: "No significant risk signals",
                summary: run.riskSummary.reason.summary,
                source: run.riskSummary.reason.source,
                detailHref: toRunDetailHref(run.runId, "risk-summary"),
                runsHref: toRunsFilterHref({ riskKind: "unresolved" }),
                traceRefs: run.riskSummary.traceRefs
            }])
        .filter((row) => matchesOutcomeFilters(row, filters));
    const releaseBenchmark = ledger.entries
        .filter((entry) => entry.benchmarkCategory === "release")
        .map((entry) => ({
        id: `release:${entry.arm}:${entry.releaseDecision ?? "unclear"}:${entry.taskId}`,
        workflow: entry.workflow ?? "unknown",
        runId: entry.runId ?? "n/a",
        status: entry.workflowStatuses[0]?.status ?? "unknown",
        title: `${entry.arm} ${entry.releaseDecision ?? "unclear"} decision`,
        summary: [
            entry.finalRecommendationSummary ?? entry.summary ?? entry.decisionImpactReason ?? "Release benchmark adjudication entry.",
            typeof entry.tokenUsage?.totalTokens === "number"
                ? `Tokens measured from run metadata: ${entry.tokenUsage.totalTokens}.`
                : "Token usage not recorded.",
            typeof entry.tokenUsage?.estimatedCostUsd === "number"
                ? `Cost estimated from local pricing: $${entry.tokenUsage.estimatedCostUsd.toFixed(6)}.`
                : "Cost unavailable because pricing metadata was missing."
        ].join(" "),
        source: entry.runId ? "ledger+run" : "ledger",
        detailHref: entry.runId ? toRunDetailHref(entry.runId, "decision-impact") : "/outcomes?panel=release-benchmark#release-benchmark",
        runsHref: entry.runId
            ? toRunsFilterHref({
                workflow: entry.workflow,
                decisionImpact: entry.decisionOutcome
            })
            : "/runs",
        traceRefs: [
            ...entry.triggerRefs,
            ...entry.evidenceGapRefs,
            ...entry.confirmedRiskRefs.map((riskRef) => ({
                runId: riskRef.runId,
                artifactKind: riskRef.artifactKind,
                note: `${riskRef.severity} risk: ${riskRef.title}${riskRef.note ? ` (${riskRef.note})` : ""}`
            }))
        ]
    }))
        .filter((row) => matchesOutcomeFilters(row, filters));
    const evidence = runs
        .flatMap((run) => run.evidenceCompleteness.flatMap((artifact) => artifact.categories.map((category) => ({
        id: `evidence:${category.key}:${category.status}:${run.runId}`,
        workflow: run.workflow,
        runId: run.runId,
        status: run.status,
        title: `${category.label} (${category.status})`,
        summary: category.reason.summary,
        source: category.reason.source,
        detailHref: toRunDetailHref(run.runId, "evidence-completeness"),
        runsHref: toRunsFilterHref({ evidenceCategory: category.key, evidenceStatus: category.status }),
        traceRefs: category.traceRefs
    }))))
        .filter((row) => matchesOutcomeFilters(row, filters));
    const friction = ledger.entries
        .filter((entry) => entry.arm === "agentforge")
        .map((entry) => ({
        id: `friction:${entry.workflow ?? "unknown"}:${entry.friction.override ? "override" : "no-override"}:${entry.taskId}`,
        workflow: entry.workflow ?? "unknown",
        runId: entry.runId ?? "n/a",
        status: entry.workflowStatuses[0]?.status ?? "unknown",
        title: entry.summary ?? entry.friction.overrideReason ?? "Benchmark friction entry",
        summary: entry.friction.overrideReason ??
            entry.decisionImpactReason ??
            entry.notes[0] ??
            "Ledger-backed friction/adjudication entry.",
        source: "ledger",
        detailHref: entry.runId ? toRunDetailHref(entry.runId, "decision-impact") : "/outcomes#friction",
        runsHref: toRunsFilterHref({
            workflow: entry.workflow,
            hasOverride: entry.friction.override ? "true" : undefined
        }),
        traceRefs: [
            ...entry.friction.falsePositiveRefs,
            ...entry.triggerRefs,
            ...entry.evidenceGapRefs
        ]
    }))
        .filter((row) => matchesOutcomeFilters(row, filters));
    const workflowChain = runs
        .flatMap((run) => run.workflowChain.stages
        .filter((stage) => stage.status !== "present")
        .map((stage) => ({
        id: `flow:${stage.stage}:${stage.status}:${run.runId}`,
        workflow: run.workflow,
        runId: run.runId,
        status: run.status,
        title: `${stage.label} (${stage.status})`,
        summary: stage.reason.summary,
        source: stage.reason.source,
        detailHref: toRunDetailHref(run.runId, "workflow-chain"),
        runsHref: toRunsFilterHref({ workflowStage: stage.stage }),
        traceRefs: stage.traceRefs
    })))
        .filter((row) => matchesOutcomeFilters(row, filters));
    return { decision, risk, releaseBenchmark, evidence, friction, workflowChain };
}
export function parseOutcomesFilters(searchParams) {
    const panel = searchParams.get("panel");
    return {
        panel: panel === "decision-impact" || panel === "risk" || panel === "release-benchmark" || panel === "evidence" || panel === "friction" || panel === "flow"
            ? panel
            : undefined,
        decision: searchParams.get("decision") ?? undefined,
        risk: searchParams.get("risk") ?? undefined,
        arm: searchParams.get("arm") ?? undefined,
        releaseDecision: searchParams.get("releaseDecision") ?? undefined,
        workflow: searchParams.get("workflow") ?? undefined,
        evidenceCategory: searchParams.get("evidenceCategory") ?? undefined,
        evidenceStatus: searchParams.get("evidenceStatus") ?? undefined,
        stage: searchParams.get("stage") ?? undefined
    };
}
export function loadOutcomesDashboardView(workspaceRoot, runsRoot = resolveRunsRoot(workspaceRoot), benchmarkLedgerPath, filters = {}) {
    const runsIndex = loadRunsIndexView(workspaceRoot, runsRoot, {}, benchmarkLedgerPath);
    const ledger = loadBenchmarkLedgerView(workspaceRoot, benchmarkLedgerPath);
    const runs = runsIndex.runs
        .map((run) => loadRunDetailView(workspaceRoot, run.runId, runsRoot, benchmarkLedgerPath))
        .filter((run) => !!run);
    const comparableTaskIds = new Set(ledger.entries
        .filter((entry) => entry.arm === "agentforge")
        .map((entry) => entry.taskId)
        .filter((taskId) => ledger.entries.some((entry) => entry.arm === "control" && entry.taskId === taskId)));
    const evidence = Object.values(runs.reduce((accumulator, run) => {
        accumulator[run.workflow] ??= [];
        accumulator[run.workflow].push(...run.evidenceCompleteness);
        return accumulator;
    }, {})).flatMap((entries) => {
        if (entries.length === 0) {
            return [];
        }
        const workflow = entries[0]?.workflow ?? "unknown";
        return [
            {
                workflow,
                runs: runs.filter((run) => run.workflow === workflow).length,
                missingCount: entries.reduce((total, artifact) => total + artifact.categories.filter((category) => category.status === "missing").length, 0),
                partialCount: entries.reduce((total, artifact) => total + artifact.categories.filter((category) => category.status === "partial").length, 0),
                frequentMissing: aggregateFrequentMissing(entries)
            }
        ];
    }).sort((left, right) => right.missingCount - left.missingCount || right.partialCount - left.partialCount || left.workflow.localeCompare(right.workflow));
    const friction = aggregateFriction(runs, ledger);
    const workflowChains = aggregateWorkflowChainSummary(runs);
    const configurationHotspots = aggregateConfigurationHotspots(runs);
    const releaseBenchmark = aggregateReleaseBenchmark(ledger);
    const leadershipRuns = buildLeadershipRuns(runs);
    const topEvidenceGap = evidence[0]?.frequentMissing[0];
    const topFrictionWorkflow = friction.workflowHotspots[0];
    const topWorkflowChain = [...workflowChains].sort((left, right) => right.missingUpstreamEvidenceCount - left.missingUpstreamEvidenceCount)[0];
    const details = createOutcomeDetailRows(runs, ledger, filters);
    const controlReleaseSummary = releaseBenchmark.arms.find((summary) => summary.arm === "control");
    const agentforgeReleaseSummary = releaseBenchmark.arms.find((summary) => summary.arm === "agentforge");
    return {
        ledger,
        decisionImpact: {
            changedDecisionCount: leadershipRuns.filter((run) => run.decisionImpact.changedDecision).length,
            scopeReductionCount: countOutcome(leadershipRuns, "scope_reduction"),
            addedValidationCount: countOutcome(leadershipRuns, "added_validation"),
            blockedApprovalCount: countOutcome(leadershipRuns, "blocked_approval"),
            remediationCount: countOutcome(leadershipRuns, "remediation_before_merge"),
            addedConfidenceCount: countOutcome(leadershipRuns, "added_confidence"),
            noMeaningfulChangeCount: countOutcome(leadershipRuns, "no_meaningful_change"),
            comparableBenchmarkPairs: comparableTaskIds.size
        },
        risk: {
            confirmedHighCount: ledger.entries.reduce((total, entry) => total + entry.confirmedRisks.high, 0),
            confirmedMediumCount: ledger.entries.reduce((total, entry) => total + entry.confirmedRisks.medium, 0),
            noisyFindingCount: ledger.entries.reduce((total, entry) => total + entry.confirmedRisks.noisy, 0),
            blockedApprovalPreventedCount: leadershipRuns.filter((run) => run.riskSummary.blockedApprovalPrevented).length,
            unresolvedRiskCount: leadershipRuns.reduce((total, run) => total + run.riskSummary.unresolved, 0)
        },
        releaseBenchmark,
        evidence,
        friction,
        workflowChains,
        configurationHotspots,
        runCount: runs.length,
        filteredPanel: filters.panel,
        filters,
        summaries: {
            decision: [
                {
                    label: "Changed decisions",
                    value: leadershipRuns.filter((run) => run.decisionImpact.changedDecision).length,
                    detail: `${leadershipRuns.length} leadership-scoped run or chain outcome(s) in view`,
                    href: toOutcomesFilterHref({ panel: "decision-impact", decision: "changed" }, "decision-impact"),
                    provenance: ledger.entries.length > 0 ? "mixed" : "inferred"
                },
                {
                    label: "Blocked approval chains",
                    value: countOutcome(leadershipRuns, "blocked_approval"),
                    detail: "Release chains are counted once at their most downstream blocked gate",
                    href: toOutcomesFilterHref({ panel: "decision-impact", decision: "blocked_approval" }, "decision-impact"),
                    provenance: ledger.entries.length > 0 ? "mixed" : "inferred"
                },
                {
                    label: "Added validation",
                    value: countOutcome(leadershipRuns, "added_validation"),
                    detail: "Runs where evidence gaps changed the next step",
                    href: toOutcomesFilterHref({ panel: "decision-impact", decision: "added_validation" }, "decision-impact"),
                    provenance: ledger.entries.length > 0 ? "mixed" : "inferred"
                }
            ],
            risk: [
                {
                    label: "Blocked approval chains prevented",
                    value: leadershipRuns.filter((run) => run.riskSummary.blockedApprovalPrevented).length,
                    detail: "Release chains are counted once at their most downstream blocked gate",
                    href: toOutcomesFilterHref({ panel: "risk", risk: "blocked-approval" }, "risk"),
                    provenance: "mixed"
                },
                {
                    label: "Confirmed medium/high",
                    value: ledger.entries.reduce((total, entry) => total + entry.confirmedRisks.high + entry.confirmedRisks.medium, 0),
                    detail: "Ledger-backed adjudicated risks",
                    href: toOutcomesFilterHref({ panel: "risk", risk: "confirmed-medium" }, "risk"),
                    provenance: "ledger"
                },
                {
                    label: "Unresolved risks",
                    value: leadershipRuns.reduce((total, run) => total + run.riskSummary.unresolved, 0),
                    detail: "Outstanding risk and blocker signals across runs",
                    href: toOutcomesFilterHref({ panel: "risk", risk: "unresolved" }, "risk"),
                    provenance: ledger.entries.length > 0 ? "mixed" : "inferred"
                }
            ],
            releaseBenchmark: [
                {
                    label: "AgentForge median cycle",
                    value: agentforgeReleaseSummary?.medianCycleTimeSeconds ?? 0,
                    detail: agentforgeReleaseSummary?.medianCycleTimeSeconds !== undefined ? "Seconds to final release recommendation" : "No AgentForge release benchmark entries yet",
                    href: toOutcomesFilterHref({ panel: "release-benchmark", arm: "agentforge" }, "release-benchmark"),
                    provenance: "ledger"
                },
                {
                    label: "Control median cycle",
                    value: controlReleaseSummary?.medianCycleTimeSeconds ?? 0,
                    detail: controlReleaseSummary?.medianCycleTimeSeconds !== undefined ? "Seconds to final release recommendation" : "No control release benchmark entries yet",
                    href: toOutcomesFilterHref({ panel: "release-benchmark", arm: "control" }, "release-benchmark"),
                    provenance: "ledger"
                },
                {
                    label: "AgentForge total tokens",
                    value: agentforgeReleaseSummary?.totalTokens ?? 0,
                    detail: agentforgeReleaseSummary?.entryCount
                        ? `${agentforgeReleaseSummary.measuredTokenEntryCount}/${agentforgeReleaseSummary.entryCount} release entries with measured run usage`
                        : "No AgentForge release benchmark entries yet",
                    href: toOutcomesFilterHref({ panel: "release-benchmark", arm: "agentforge" }, "release-benchmark"),
                    provenance: "run-measured"
                },
                {
                    label: "Blocked release decisions",
                    value: (agentforgeReleaseSummary?.blockedReleaseCount ?? 0) + (controlReleaseSummary?.blockedReleaseCount ?? 0),
                    detail: "Across both benchmark arms",
                    href: toOutcomesFilterHref({ panel: "release-benchmark", releaseDecision: "no-go" }, "release-benchmark"),
                    provenance: "ledger"
                }
            ],
            evidence: [
                {
                    label: "Largest evidence gap",
                    value: evidence[0]?.missingCount ?? 0,
                    detail: topEvidenceGap ?? "No recurring gaps found",
                    href: toOutcomesFilterHref({
                        panel: "evidence",
                        evidenceCategory: topEvidenceGap?.startsWith("CI Evidence") ? "ci-evidence" : undefined,
                        evidenceStatus: "missing"
                    }, "evidence"),
                    provenance: "inferred"
                }
            ],
            friction: [
                {
                    label: "Highest friction workflow",
                    value: (topFrictionWorkflow?.overrideCount ?? 0) +
                        (topFrictionWorkflow?.falsePositiveCount ?? 0) +
                        (topFrictionWorkflow?.manualStepCount ?? 0) +
                        (topFrictionWorkflow?.requestFrictionCount ?? 0),
                    detail: topFrictionWorkflow?.workflow ?? "No ledger-backed friction data",
                    href: toOutcomesFilterHref({ panel: "friction", workflow: topFrictionWorkflow?.workflow }, "friction"),
                    provenance: "ledger"
                }
            ],
            workflowChain: [
                {
                    label: "Release chain breaks",
                    value: topWorkflowChain?.missingUpstreamEvidenceCount ?? 0,
                    detail: topWorkflowChain ? `${topWorkflowChain.label} has the most missing upstream evidence` : "No chain breakpoints detected",
                    href: toOutcomesFilterHref({ panel: "flow", stage: topWorkflowChain?.stage }, "workflow-chain"),
                    provenance: "inferred"
                }
            ]
        },
        details
    };
}
export function loadValueDashboardView(workspaceRoot, runsRoot = resolveRunsRoot(workspaceRoot), benchmarkLedgerPath, filters = {}) {
    return loadOutcomesDashboardView(workspaceRoot, runsRoot, benchmarkLedgerPath, filters);
}
export function createOutcomesExportDocument(workspaceRoot, runsRoot = resolveRunsRoot(workspaceRoot), benchmarkLedgerPath, filters = {}) {
    const view = loadOutcomesDashboardView(workspaceRoot, runsRoot, benchmarkLedgerPath, filters);
    return {
        schemaVersion: "1.0.0",
        generatedAt: new Date().toISOString(),
        workspaceRoot,
        runsRoot: toRelativeDisplayPath(workspaceRoot, runsRoot),
        benchmarkLedgerPath: view.ledger.entries.length > 0 ? toRelativeDisplayPath(workspaceRoot, resolveBenchmarkLedgerPath(workspaceRoot, benchmarkLedgerPath)) : undefined,
        runCount: view.runCount,
        ledgerAvailable: view.ledger.entries.length > 0,
        decisionImpact: view.decisionImpact,
        risk: view.risk,
        releaseBenchmark: view.releaseBenchmark,
        evidence: view.evidence,
        friction: view.friction,
        workflowChains: view.workflowChains,
        configurationHotspots: view.configurationHotspots,
        summaries: view.summaries
    };
}
function toBenchmarkSummaryView(run, artifact, localRunIds) {
    return {
        runId: run.runId,
        workflow: run.workflow,
        status: run.status,
        baselineRunId: artifact.payload.baselineRunId,
        baselineWorkflow: artifact.payload.baselineWorkflow,
        baselineSpecId: artifact.payload.baselineSpecId,
        comparableRunCount: artifact.payload.comparedRuns.filter((candidate) => candidate.comparable).length,
        regressionCount: artifact.payload.regressionCount,
        improvementCount: artifact.payload.improvementCount,
        unchangedCount: artifact.payload.unchangedCount,
        nonComparableCount: artifact.payload.nonComparableCount,
        summaryConclusion: artifact.payload.summaryConclusion,
        comparedRuns: artifact.payload.comparedRuns.map((candidate) => ({
            runId: candidate.runId,
            bundlePath: candidate.bundlePath,
            specId: candidate.specId,
            workflow: candidate.workflow,
            comparable: candidate.comparable,
            passed: candidate.passed,
            failureCount: candidate.failureCount,
            deterministicCheckCount: candidate.deterministicCheckCount,
            regressionCount: candidate.regressions.length,
            improvementCount: candidate.improvements.length,
            unchangedCount: candidate.unchangedCount,
            nonComparableCount: candidate.nonComparableFindings.length,
            hasLocalRunLink: localRunIds.has(candidate.runId)
        })),
        hasBaselineRunLink: localRunIds.has(artifact.payload.baselineRunId)
    };
}
export function listAvailableWorkflows(runs) {
    return [...new Set(runs.map((run) => run.workflow))].sort();
}
export function listAvailableStatuses(runs) {
    return [...new Set(runs.map((run) => run.status))].sort();
}
export function listAvailableArtifactKinds(runs) {
    return [...new Set(runs.flatMap((run) => run.artifactKinds))].sort();
}
export function toRelativeDisplayPath(workspaceRoot, absolutePath) {
    const normalizedWorkspaceRoot = resolve(workspaceRoot);
    const normalizedPath = resolve(absolutePath);
    return normalizedPath.startsWith(normalizedWorkspaceRoot) ? normalizedPath.slice(normalizedWorkspaceRoot.length + 1) : basename(absolutePath);
}
//# sourceMappingURL=data.js.map