/**
 * RCASD-IVTR pipeline lifecycle - stage transitions and gate enforcement.
 *
 * CANONICAL EXPORTS: `stages.ts` is the single source of truth for stage
 * definitions, ordering, prerequisites, and transition rules. This file
 * re-exports canonical types and provides SQLite-native lifecycle operations
 * backed by `lifecycle_pipelines`, `lifecycle_stages`, and
 * `lifecycle_gate_results` tables.
 *
 * @task T4467
 * @task T4800 - Unified lifecycle barrel export
 * @task T4798 - RCASD rename (Phase 1: code-only, no disk paths)
 * @epic T4454
 */
import { readFile } from 'node:fs/promises';
import { join } from 'node:path';
import { ExitCode } from '@cleocode/contracts';
import { linkPipelineAdr } from '../adrs/link-pipeline.js';
import { syncAdrsToDb } from '../adrs/sync.js';
import { CleoError } from '../errors.js';
import { getCleoDirAbsolute, getProjectRoot } from '../paths.js';
import * as schema from '../store/tasks-schema.js';
import { LIFECYCLE_STAGE_STATUSES } from '../store/tasks-schema.js';
import { linkProvenance } from './evidence.js';
import { ensureStageArtifact } from './stage-artifacts.js';
// =============================================================================
// CANONICAL RE-EXPORTS from stages.ts (single source of truth)
// =============================================================================
export { CONTRIBUTION_STAGE, checkTransition, DECISION_STAGES, DELIVERY_STAGES, EXECUTION_STAGES, FIRST_STAGE, getDependents, getNextStage, getPrerequisites, getPreviousStage, getSkippableStages, getStageOrder, getStagesBetween, getStagesByCategory, isPrerequisite, isStageAfter, isStageBefore, isValidStage, isValidStageStatus, LAST_STAGE, PIPELINE_STAGES, PLANNING_STAGES, STAGE_COUNT, STAGE_DEFINITIONS as CANONICAL_STAGE_DEFINITIONS, STAGE_ORDER, STAGE_PREREQUISITES as CANONICAL_PREREQUISITES, VALIDATION_STAGES, validateStage, } from './stages.js';
import { isValidStage, PIPELINE_STAGES, STAGE_DEFINITIONS, STAGE_ORDER, STAGE_PREREQUISITES, } from './stages.js';
// =============================================================================
// LIFECYCLE DATA DISCOVERY
// =============================================================================
/**
 * Get the current lifecycle state for an epic.
 * @task T4467
 */
export async function getLifecycleState(epicId, cwd) {
    const status = await getLifecycleStatus(epicId, cwd);
    const gates = await getLifecycleGates(epicId, cwd);
    const stages = {};
    for (const stage of status.stages) {
        stages[stage.stage] = {
            status: stage.status,
            completedAt: stage.completedAt,
            notes: stage.notes,
            gates: gates[stage.stage],
        };
    }
    return { epicId, title: status.title, stages };
}
/**
 * Start a lifecycle stage.
 * @task T4467
 */
export async function startStage(epicId, stage, cwd) {
    // Gate check
    const gateResult = await checkGate(epicId, stage, cwd);
    if (!gateResult.allowed) {
        throw new CleoError(ExitCode.LIFECYCLE_GATE_FAILED, gateResult.message);
    }
    if (!PIPELINE_STAGES.includes(stage)) {
        throw new CleoError(ExitCode.INVALID_INPUT, `Unknown stage: ${stage}`);
    }
    const current = await getLifecycleStatus(epicId, cwd);
    const previousStatus = current.stages.find((s) => s.stage === stage)?.status ?? 'not_started';
    if (previousStatus === 'completed') {
        throw new CleoError(ExitCode.LIFECYCLE_TRANSITION_INVALID, `Stage '${stage}' is already completed`);
    }
    const result = await recordStageProgress(epicId, stage, 'completed', undefined, cwd);
    return {
        epicId,
        stage,
        previousStatus,
        newStatus: 'completed',
        timestamp: result.timestamp,
    };
}
/**
 * Complete a lifecycle stage.
 * @task T4467
 */
export async function completeStage(epicId, stage, artifacts, cwd) {
    if (!PIPELINE_STAGES.includes(stage)) {
        throw new CleoError(ExitCode.INVALID_INPUT, `Unknown stage: ${stage}`);
    }
    const current = await getLifecycleStatus(epicId, cwd);
    const previousStatus = current.stages.find((s) => s.stage === stage)?.status ?? 'not_started';
    if (previousStatus === 'completed') {
        throw new CleoError(ExitCode.LIFECYCLE_TRANSITION_INVALID, `Cannot complete stage '${stage}' from status '${previousStatus}'`);
    }
    const notes = artifacts?.length ? `Artifacts: ${artifacts.join(', ')}` : undefined;
    const result = await recordStageProgress(epicId, stage, 'completed', notes, cwd);
    return {
        epicId,
        stage,
        previousStatus,
        newStatus: 'completed',
        timestamp: result.timestamp,
    };
}
/**
 * Skip a lifecycle stage.
 * @task T4467
 */
export async function skipStage(epicId, stage, reason, cwd) {
    if (!PIPELINE_STAGES.includes(stage)) {
        throw new CleoError(ExitCode.INVALID_INPUT, `Unknown stage: ${stage}`);
    }
    const current = await getLifecycleStatus(epicId, cwd);
    const previousStatus = current.stages.find((s) => s.stage === stage)?.status ?? 'not_started';
    if (previousStatus === 'completed') {
        throw new CleoError(ExitCode.LIFECYCLE_TRANSITION_INVALID, `Cannot skip stage '${stage}' - already completed`);
    }
    const result = await recordStageProgress(epicId, stage, 'skipped', reason, cwd);
    return {
        epicId,
        stage,
        previousStatus,
        newStatus: 'skipped',
        timestamp: result.timestamp,
    };
}
/**
 * Check lifecycle gate before starting a stage.
 * @task T4467
 */
export async function checkGate(epicId, targetStage, cwd) {
    // Get enforcement mode from config
    const mode = await getEnforcementMode(cwd);
    if (mode === 'off') {
        return {
            allowed: true,
            mode,
            missingPrerequisites: [],
            currentStage: targetStage,
            message: 'Gate check disabled',
        };
    }
    if (!PIPELINE_STAGES.includes(targetStage)) {
        throw new CleoError(ExitCode.INVALID_INPUT, `Unknown stage: ${targetStage}`);
    }
    const prereqResult = await checkStagePrerequisites(epicId, targetStage, cwd);
    const missing = prereqResult.missingPrerequisites;
    const allowed = mode === 'advisory' || missing.length === 0;
    const message = missing.length > 0
        ? `SPAWN BLOCKED: Lifecycle prerequisites not met. Missing: ${missing.join(', ')}`
        : 'All prerequisites met';
    return {
        allowed,
        mode,
        missingPrerequisites: missing,
        currentStage: targetStage,
        message,
    };
}
/**
 * Get lifecycle enforcement mode from config.
 * @task T4467
 */
async function getEnforcementMode(cwd) {
    // Check environment variable first
    const envMode = process.env['LIFECYCLE_ENFORCEMENT_MODE'];
    if (envMode && ['strict', 'advisory', 'off'].includes(envMode)) {
        return envMode;
    }
    // Read from config
    try {
        const configPath = join(getCleoDirAbsolute(cwd), 'config.json');
        const raw = await readFile(configPath, 'utf-8');
        const config = JSON.parse(raw);
        const mode = config?.lifecycle?.mode;
        if (mode && ['strict', 'advisory', 'off'].includes(mode)) {
            return mode;
        }
    }
    catch {
        // Config may not exist
    }
    return 'strict';
}
/**
 * Get lifecycle status for an epic from SQLite.
 * Returns stage progress, current/next stage, and blockers.
 * @task T4801 - SQLite-native implementation
 */
export async function getLifecycleStatus(epicId, cwd) {
    const { getDb } = await import('../store/sqlite.js');
    const { eq } = await import('drizzle-orm');
    const db = await getDb(cwd);
    // Query pipeline and task for this epic
    const pipelineResult = await db
        .select({
        pipeline: schema.lifecyclePipelines,
        task: schema.tasks,
    })
        .from(schema.lifecyclePipelines)
        .innerJoin(schema.tasks, eq(schema.lifecyclePipelines.taskId, schema.tasks.id))
        .where(eq(schema.lifecyclePipelines.taskId, epicId))
        .limit(1);
    // If no pipeline exists, return uninitialized status with default stages
    if (pipelineResult.length === 0) {
        return {
            epicId,
            currentStage: null,
            stages: PIPELINE_STAGES.map((s) => ({ stage: s, status: 'not_started' })),
            nextStage: 'research',
            blockedOn: [],
            initialized: false,
        };
    }
    const task = pipelineResult[0].task;
    // Query all stages for this pipeline
    const pipelineId = `pipeline-${epicId}`;
    const stageRows = await db
        .select()
        .from(schema.lifecycleStages)
        .where(eq(schema.lifecycleStages.pipelineId, pipelineId))
        .orderBy(schema.lifecycleStages.sequence);
    // Build a lookup map of stage data from DB
    const stageDataMap = new Map();
    for (const row of stageRows) {
        let parsedChain;
        if (row.provenanceChainJson) {
            try {
                parsedChain = JSON.parse(row.provenanceChainJson);
            }
            catch {
                parsedChain = undefined;
            }
        }
        stageDataMap.set(row.stageName, {
            status: row.status,
            completedAt: row.completedAt ?? undefined,
            notes: row.notesJson ? JSON.parse(row.notesJson)[0] : undefined,
            outputFile: row.outputFile ?? undefined,
            provenanceChain: parsedChain,
        });
    }
    // Build stages array in PIPELINE_STAGES order
    const stages = PIPELINE_STAGES.map((s) => {
        const data = stageDataMap.get(s);
        return {
            stage: s,
            status: data?.status || 'not_started',
            completedAt: data?.completedAt,
            notes: data?.notes,
            outputFile: data?.outputFile,
            provenanceChain: data?.provenanceChain,
        };
    });
    // Calculate currentStage and nextStage
    let currentStage = null;
    let nextStage = null;
    for (let i = PIPELINE_STAGES.length - 1; i >= 0; i--) {
        const s = PIPELINE_STAGES[i];
        const data = stageDataMap.get(s);
        if (data?.status === 'completed' || data?.status === 'skipped') {
            currentStage = s;
            if (i < PIPELINE_STAGES.length - 1) {
                nextStage = PIPELINE_STAGES[i + 1];
            }
            break;
        }
    }
    if (!currentStage) {
        nextStage = 'research';
    }
    // Calculate blockedOn based on prerequisites
    const blockedOn = [];
    if (nextStage) {
        const prereqs = STAGE_PREREQUISITES[nextStage] || [];
        for (const prereq of prereqs) {
            const prereqData = stageDataMap.get(prereq);
            const prereqStatus = prereqData?.status;
            if (prereqStatus !== 'completed' && prereqStatus !== 'skipped') {
                blockedOn.push(prereq);
            }
        }
    }
    return {
        epicId,
        title: task.title,
        currentStage,
        stages,
        nextStage,
        blockedOn,
        initialized: true,
    };
}
/**
 * Get lifecycle history for an epic.
 * Returns stage transitions and gate events sorted by timestamp.
 * SQLite-native implementation - queries lifecycle_stages and lifecycle_gate_results tables.
 * @task T4785
 * @task T4801
 */
export async function getLifecycleHistory(epicId, cwd) {
    const { getDb } = await import('../store/sqlite.js');
    const { eq } = await import('drizzle-orm');
    const db = await getDb(cwd);
    const pipelineId = `pipeline-${epicId}`;
    // Query stages for this pipeline
    const stages = await db
        .select()
        .from(schema.lifecycleStages)
        .where(eq(schema.lifecycleStages.pipelineId, pipelineId));
    if (stages.length === 0) {
        return { epicId, history: [] };
    }
    const history = [];
    // Build a map of stageId -> stageName for gate lookups
    const stageIdToName = new Map();
    for (const stage of stages) {
        stageIdToName.set(stage.id, stage.stageName);
    }
    // Add stage completion and skip events
    for (const stage of stages) {
        if (stage.status === 'completed' && stage.completedAt) {
            const notes = stage.notesJson ? JSON.parse(stage.notesJson).join(', ') : undefined;
            history.push({
                stage: stage.stageName,
                action: 'completed',
                timestamp: stage.completedAt,
                notes,
            });
        }
        if (stage.status === 'skipped' && stage.skippedAt) {
            history.push({
                stage: stage.stageName,
                action: 'skipped',
                timestamp: stage.skippedAt,
                notes: stage.skipReason ?? undefined,
            });
        }
    }
    // Query gate results for all stages in this pipeline
    const stageIds = stages.map((s) => s.id);
    if (stageIds.length > 0) {
        const gateResults = await db
            .select()
            .from(schema.lifecycleGateResults)
            .where(eq(schema.lifecycleGateResults.stageId, stageIds[0]));
        // Add gate events
        for (const gate of gateResults) {
            const stageName = stageIdToName.get(gate.stageId);
            if (stageName) {
                history.push({
                    stage: stageName,
                    action: `gate.${gate.result}: ${gate.gateName}`,
                    timestamp: gate.checkedAt,
                    notes: (gate.details || gate.reason) ?? undefined,
                });
            }
        }
        // Query remaining stages
        for (let i = 1; i < stageIds.length; i++) {
            const additionalGates = await db
                .select()
                .from(schema.lifecycleGateResults)
                .where(eq(schema.lifecycleGateResults.stageId, stageIds[i]));
            for (const gate of additionalGates) {
                const stageName = stageIdToName.get(gate.stageId);
                if (stageName) {
                    history.push({
                        stage: stageName,
                        action: `gate.${gate.result}: ${gate.gateName}`,
                        timestamp: gate.checkedAt,
                        notes: (gate.details || gate.reason) ?? undefined,
                    });
                }
            }
        }
    }
    // Sort by timestamp
    history.sort((a, b) => a.timestamp.localeCompare(b.timestamp));
    return { epicId, history };
}
/**
 * Get all gate statuses for an epic.
 * @task T4785
 */
export async function getLifecycleGates(epicId, cwd) {
    const { getDb } = await import('../store/sqlite.js');
    const { eq } = await import('drizzle-orm');
    const db = await getDb(cwd);
    const pipelineId = `pipeline-${epicId}`;
    const stages = await db
        .select()
        .from(schema.lifecycleStages)
        .where(eq(schema.lifecycleStages.pipelineId, pipelineId));
    if (stages.length === 0) {
        return {};
    }
    const gates = {};
    for (const stage of stages) {
        const gateRows = await db
            .select()
            .from(schema.lifecycleGateResults)
            .where(eq(schema.lifecycleGateResults.stageId, stage.id));
        if (gateRows.length > 0) {
            gates[stage.stageName] = {};
            for (const gateRow of gateRows) {
                const status = gateRow.result === 'pass' ? 'passed' : gateRow.result === 'fail' ? 'failed' : 'pending';
                gates[stage.stageName][gateRow.gateName] = {
                    status,
                    agent: gateRow.checkedBy,
                    notes: gateRow.details ?? undefined,
                    reason: gateRow.reason ?? undefined,
                    timestamp: gateRow.checkedAt,
                };
            }
        }
    }
    return gates;
}
/**
 * Get prerequisites for a target stage.
 * Pure data function, no I/O.
 * @task T4785
 */
export function getStagePrerequisites(targetStage) {
    if (!PIPELINE_STAGES.includes(targetStage)) {
        throw new CleoError(ExitCode.INVALID_INPUT, `Invalid stage: ${targetStage}. Valid stages: ${PIPELINE_STAGES.join(', ')}`);
    }
    const def = STAGE_DEFINITIONS[targetStage];
    return {
        prerequisites: STAGE_PREREQUISITES[targetStage] || [],
        stageInfo: def
            ? { stage: def.stage, name: def.name, description: def.description, order: def.order }
            : undefined,
    };
}
/**
 * Check if a stage's prerequisites are met for an epic.
 * @task T4785
 */
export async function checkStagePrerequisites(epicId, targetStage, cwd) {
    if (!PIPELINE_STAGES.includes(targetStage)) {
        throw new CleoError(ExitCode.INVALID_INPUT, `Invalid stage: ${targetStage}. Valid stages: ${PIPELINE_STAGES.join(', ')}`);
    }
    const lifecycleStatus = await getLifecycleStatus(epicId, cwd);
    const prereqs = STAGE_PREREQUISITES[targetStage] || [];
    const stageStatusMap = new Map(lifecycleStatus.stages.map((s) => [s.stage, s.status]));
    const missingPrerequisites = [];
    const issues = [];
    for (const prereq of prereqs) {
        const prereqStatus = stageStatusMap.get(prereq);
        if (prereqStatus !== 'completed' && prereqStatus !== 'skipped') {
            missingPrerequisites.push(prereq);
            issues.push({
                stage: prereq,
                severity: 'error',
                message: `Stage '${prereq}' must be completed or skipped before '${targetStage}'`,
            });
        }
    }
    return {
        epicId,
        targetStage,
        valid: missingPrerequisites.length === 0,
        canProgress: missingPrerequisites.length === 0,
        missingPrerequisites,
        issues,
    };
}
async function ensureLifecycleContext(epicId, stageName, cwd, options) {
    const { getDb } = await import('../store/sqlite.js');
    const { eq } = await import('drizzle-orm');
    const db = await getDb(cwd);
    const pipelineId = `pipeline-${epicId}`;
    const stageId = `stage-${epicId}-${stageName}`;
    const { getNativeDb } = await import('../store/sqlite.js');
    getNativeDb()
        .prepare(`INSERT OR IGNORE INTO tasks (id, title, status, priority, created_at) VALUES (?, ?, 'pending', 'medium', datetime('now'))`)
        .run(epicId, `Task ${epicId}`);
    const existingPipeline = await db
        .select()
        .from(schema.lifecyclePipelines)
        .where(eq(schema.lifecyclePipelines.id, pipelineId))
        .limit(1)
        .all();
    if (existingPipeline.length === 0) {
        await db
            .insert(schema.lifecyclePipelines)
            .values({
            id: pipelineId,
            taskId: epicId,
            status: 'active',
            currentStageId: null,
            startedAt: options.now,
        })
            .run();
    }
    const existingStage = await db
        .select()
        .from(schema.lifecycleStages)
        .where(eq(schema.lifecycleStages.id, stageId))
        .limit(1)
        .all();
    if (existingStage.length === 0) {
        const sequence = isValidStage(stageName) ? STAGE_ORDER[stageName] : 0;
        await db
            .insert(schema.lifecycleStages)
            .values({
            id: stageId,
            pipelineId,
            stageName: stageName,
            status: options.stageStatusOnCreate,
            sequence,
            startedAt: options.now,
        })
            .run();
    }
    if (options.updateCurrentStage) {
        await db
            .update(schema.lifecyclePipelines)
            .set({ currentStageId: stageId })
            .where(eq(schema.lifecyclePipelines.id, pipelineId))
            .run();
    }
    return { db, pipelineId, stageId };
}
/**
 * Record a stage status transition (progress/record).
 * SQLite-native implementation - T4801
 * @task T4785
 * @task T4801
 */
export async function recordStageProgress(epicId, stage, status, notes, cwd) {
    if (!PIPELINE_STAGES.includes(stage)) {
        throw new CleoError(ExitCode.INVALID_INPUT, `Invalid stage: ${stage}`);
    }
    const validStatuses = [...LIFECYCLE_STAGE_STATUSES];
    if (!validStatuses.includes(status)) {
        throw new CleoError(ExitCode.INVALID_INPUT, `Invalid status: ${status}. Valid: ${validStatuses.join(', ')}`);
    }
    const { eq } = await import('drizzle-orm');
    const now = new Date().toISOString();
    const stageName = stage;
    const { db, stageId, pipelineId } = await ensureLifecycleContext(epicId, stage, cwd, {
        now,
        stageStatusOnCreate: status,
        updateCurrentStage: true,
    });
    const artifact = await ensureStageArtifact(epicId, stageName, cwd);
    const provenanceChain = {
        recordedAt: now,
        source: 'pipeline.stage.record',
        stage,
        status,
        related: artifact.related,
    };
    // Upsert stage record
    const existingStage = await db
        .select()
        .from(schema.lifecycleStages)
        .where(eq(schema.lifecycleStages.id, stageId))
        .limit(1)
        .all();
    const sequence = STAGE_ORDER[stage];
    const stageValues = {
        status: status,
        completedAt: status === 'completed' ? now : null,
        skippedAt: status === 'skipped' ? now : null,
        skipReason: status === 'skipped' ? (notes ?? null) : null,
        notesJson: notes ? JSON.stringify([notes]) : '[]',
        outputFile: artifact.outputFile,
        provenanceChainJson: JSON.stringify(provenanceChain),
    };
    if (existingStage.length === 0) {
        // Insert new stage
        await db
            .insert(schema.lifecycleStages)
            .values({
            id: stageId,
            pipelineId,
            stageName: stage,
            status: status,
            sequence,
            startedAt: now,
            completedAt: status === 'completed' ? now : null,
            skippedAt: status === 'skipped' ? now : null,
            skipReason: status === 'skipped' ? (notes ?? null) : null,
            notesJson: notes ? JSON.stringify([notes]) : '[]',
            outputFile: artifact.outputFile,
            provenanceChainJson: JSON.stringify(provenanceChain),
        })
            .run();
    }
    else {
        // Update existing stage
        await db
            .update(schema.lifecycleStages)
            .set(stageValues)
            .where(eq(schema.lifecycleStages.id, stageId))
            .run();
    }
    if (status === 'completed') {
        await linkProvenance(epicId, stageName, artifact.absolutePath, cwd);
        if (stageName === 'architecture_decision') {
            const projectRoot = getProjectRoot(cwd);
            await syncAdrsToDb(projectRoot);
            await linkPipelineAdr(projectRoot, epicId);
        }
    }
    return { epicId, stage, status, timestamp: now };
}
/**
 * Skip a stage with a reason (engine-compatible).
 * @task T4785
 */
export async function skipStageWithReason(epicId, stage, reason, cwd) {
    const result = await recordStageProgress(epicId, stage, 'skipped', reason, cwd);
    return { epicId, stage, reason, timestamp: result.timestamp };
}
/**
 * Reset a stage to pending (emergency).
 * @task T4785
 */
export async function resetStage(epicId, stage, reason, cwd) {
    if (!PIPELINE_STAGES.includes(stage)) {
        throw new CleoError(ExitCode.INVALID_INPUT, `Invalid stage: ${stage}`);
    }
    const { eq } = await import('drizzle-orm');
    const now = new Date().toISOString();
    const { db, stageId } = await ensureLifecycleContext(epicId, stage, cwd, {
        now,
        stageStatusOnCreate: 'not_started',
        updateCurrentStage: false,
    });
    await db
        .update(schema.lifecycleStages)
        .set({
        status: 'not_started',
        completedAt: null,
        skippedAt: null,
        skipReason: null,
        notesJson: JSON.stringify([`Reset: ${reason}`]),
    })
        .where(eq(schema.lifecycleStages.id, stageId))
        .run();
    return { epicId, stage, reason };
}
/**
 * Mark a gate as passed.
 * SQLite-native implementation - T4801
 * @task T4785
 * @task T4801
 */
export async function passGate(epicId, gateName, agent, notes, cwd) {
    const { eq } = await import('drizzle-orm');
    const now = new Date().toISOString();
    const stageName = gateName.split('-')[0];
    const gateId = `gate-${epicId}-${stageName}-${gateName}`;
    const { db, stageId } = await ensureLifecycleContext(epicId, stageName, cwd, {
        now,
        stageStatusOnCreate: 'in_progress',
        updateCurrentStage: true,
    });
    // Upsert gate result
    const existingGate = await db
        .select()
        .from(schema.lifecycleGateResults)
        .where(eq(schema.lifecycleGateResults.id, gateId))
        .limit(1)
        .all();
    const gateValues = {
        id: gateId,
        stageId,
        gateName,
        result: 'pass',
        checkedAt: now,
        checkedBy: agent ?? 'system',
        details: notes ?? null,
        reason: null,
    };
    if (existingGate.length > 0) {
        await db
            .update(schema.lifecycleGateResults)
            .set(gateValues)
            .where(eq(schema.lifecycleGateResults.id, gateId))
            .run();
    }
    else {
        await db.insert(schema.lifecycleGateResults).values(gateValues).run();
    }
    return { epicId, gateName, timestamp: now };
}
/**
 * Mark a gate as failed.
 * SQLite-native implementation - T4801
 * @task T4785
 * @task T4801
 */
export async function failGate(epicId, gateName, reason, cwd) {
    const { eq } = await import('drizzle-orm');
    const now = new Date().toISOString();
    const stageName = gateName.split('-')[0];
    const gateId = `gate-${epicId}-${stageName}-${gateName}`;
    const { db, stageId } = await ensureLifecycleContext(epicId, stageName, cwd, {
        now,
        stageStatusOnCreate: 'in_progress',
        updateCurrentStage: true,
    });
    // Upsert gate result
    const existingGate = await db
        .select()
        .from(schema.lifecycleGateResults)
        .where(eq(schema.lifecycleGateResults.id, gateId))
        .limit(1)
        .all();
    const gateValues = {
        id: gateId,
        stageId,
        gateName,
        result: 'fail',
        checkedAt: now,
        checkedBy: 'system',
        details: null,
        reason: reason ?? null,
    };
    if (existingGate.length > 0) {
        await db
            .update(schema.lifecycleGateResults)
            .set(gateValues)
            .where(eq(schema.lifecycleGateResults.id, gateId))
            .run();
    }
    else {
        await db.insert(schema.lifecycleGateResults).values(gateValues).run();
    }
    return { epicId, gateName, reason, timestamp: now };
}
/**
 * List all epic IDs that have lifecycle data.
 * @task T4785
 */
export async function listEpicsWithLifecycle(cwd) {
    try {
        const { getDb } = await import('../store/sqlite.js');
        const db = await getDb(cwd);
        const rows = await db
            .select({ taskId: schema.lifecyclePipelines.taskId })
            .from(schema.lifecyclePipelines)
            .all();
        return rows.map((r) => r.taskId).sort();
    }
    catch {
        return [];
    }
}
//# sourceMappingURL=index.js.map