// engine/saga/cascade-saga.ts — SPEC-735: Generic saga executor with compensating rollback
/**
 * Execute a sequence of saga steps.
 *
 * - Steps run in declaration order.
 * - If a **critical** step throws, all previously completed compensations run
 *   in **reverse order** and `SagaResult.rolledBack` is set to true.
 * - If a **non-critical** step throws, the error is collected as a warning
 *   and execution continues to the next step.
 * - Compensating actions are expected to be idempotent.
 *
 * The chaos-testing hook is activated by setting `PLANU_SAGA_CHAOS=<step-name>`
 * in the environment, which forces that step to throw during CI chaos tests.
 *
 * @param steps - Ordered list of saga steps.
 * @param ctx - Shared context threaded through every run/compensate call.
 */
export async function runSaga(steps, ctx) {
    const completed = [];
    const warnings = [];
    // SPEC-735 chaos-test mode: force a specific step to fail
    // eslint-disable-next-line @typescript-eslint/dot-notation
    const chaosTarget = process.env['PLANU_SAGA_CHAOS'];
    for (const step of steps) {
        let result;
        try {
            if (chaosTarget && step.name === chaosTarget) {
                throw new Error(`[chaos] forced failure in step "${step.name}"`);
            }
            result = await step.run(ctx);
        }
        catch (err) {
            const error = err instanceof Error ? err : new Error(String(err));
            if (!step.critical) {
                // Non-critical: log warning and continue
                warnings.push({ step: step.name, error: error.message });
                continue;
            }
            // Critical failure — run compensations in reverse order
            for (let i = completed.length - 1; i >= 0; i--) {
                const done = completed[i];
                if (done?.step.compensate) {
                    try {
                        await done.step.compensate(ctx, done.result);
                    }
                    catch {
                        // Compensation errors are swallowed — best-effort rollback
                    }
                }
            }
            return {
                ok: false,
                failedStep: step.name,
                error,
                rolledBack: true,
                warnings,
            };
        }
        completed.push({ step, result });
    }
    return {
        ok: true,
        rolledBack: false,
        warnings,
    };
}
//# sourceMappingURL=cascade-saga.js.map