/**
 * Active-run tracking + terminal-transition helper for WorkflowRuns.
 *
 * A WorkflowRun spends ~seconds to minutes in `status: "running"` between
 * CREATE (workflow-execute.ts:331) and the terminal SET
 * (workflow-execute.ts:611). If the process dies inside that window —
 * SIGTERM from the scheduler's DISPATCH_TIMEOUT_MS, or an unhandled
 * exception that bypasses the terminal SET — the run is left as a zombie
 *.
 *
 * This module lets both failure paths converge on one terminal-transition
 * Cypher statement, guarded by `WHERE r.status = 'running'` so replay from
 * a second sink after the normal SET is a silent no-op.
 *
 *   CREATE WorkflowRun ──▶ markRunActive(runId)
 *                              │
 *                              ▼
 *                        step loop + terminal SET
 *                              │
 *              ┌───────────────┼───────────────┐
 *              ▼               ▼               ▼
 *     normal completion   exception     SIGTERM fires
 *       (status set       (catch calls   (index.ts handler
 *        by terminal       terminal-      calls reapActiveRuns
 *        SET)              Transition-    which calls terminal-
 *                          Failed)        TransitionFailed for
 *                                         each tracked run)
 *              │               │               │
 *              └───────────────┼───────────────┘
 *                              ▼
 *                    markRunComplete(runId)
 */
import { getSession } from "./neo4j.js";
import { notTrashed } from "../../../../../lib/graph-trash/dist/index.js";
const activeRuns = new Set();
export function markRunActive(runId) {
    activeRuns.add(runId);
}
export function markRunComplete(runId) {
    activeRuns.delete(runId);
}
export function snapshotActiveRuns() {
    return Array.from(activeRuns);
}
const TRANSITION_TIMEOUT_MS = 3_000;
export async function terminalTransitionFailed(runId, errorMsg) {
    const write = async () => {
        const session = getSession();
        try {
            await session.run(`MATCH (r:WorkflowRun {runId: $runId})
         WHERE r.status = 'running' AND ${notTrashed("r")}
         SET r.status = 'failed',
             r.completedAt = $completedAt,
             r.error = $error`, {
                runId,
                completedAt: new Date().toISOString(),
                error: errorMsg,
            });
        }
        finally {
            await session.close();
        }
    };
    const timeout = new Promise((_, reject) => {
        setTimeout(() => reject(new Error(`terminal-transition write exceeded ${TRANSITION_TIMEOUT_MS}ms`)), TRANSITION_TIMEOUT_MS);
    });
    await Promise.race([write(), timeout]);
}
export async function reapActiveRuns(reason) {
    const runIds = snapshotActiveRuns();
    for (const runId of runIds) {
        console.error(`[workflow-execute] SIGTERM received — terminal-transitioning run=${runId} to failed`);
        try {
            await terminalTransitionFailed(runId, `scheduler dispatch timeout (${reason})`);
        }
        catch (err) {
            const msg = err instanceof Error ? err.message : String(err);
            console.error(`[workflow-execute] terminal-transition write FAILED for run=${runId}: ${msg}`);
        }
        activeRuns.delete(runId);
    }
}
//# sourceMappingURL=active-runs.js.map