// @crash-shield-ignore-file — config/cache reader for Planu-controlled JSON; writer is this codebase, shape guaranteed by build/seed.
// engine/next-spec-resolver/session-writer.ts — SPEC-686: Best-effort session.json writer
import { readFile, writeFile, mkdir } from 'node:fs/promises';
import { join, dirname } from 'node:path';
// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------
export const WRITE_DEDUP_WINDOW_MS = 5_000;
export const MAX_REASON_LENGTH = 200;
// Dedup tracking: last write per projectPath
const lastWriteAt = new Map();
// ---------------------------------------------------------------------------
// Public API
// ---------------------------------------------------------------------------
/**
 * SPEC-686 Scenarios 6, 7: Write nextSpec to planu/session.json.
 * Best-effort — never throws. Uses 5s dedup window for idempotence.
 */
export async function writeNextSpec(projectPath, next) {
    const key = `nextSpec:${projectPath}`;
    const now = Date.now();
    const last = lastWriteAt.get(key) ?? 0;
    if (now - last < WRITE_DEDUP_WINDOW_MS) {
        // Within dedup window — no-op (Scenario 7)
        return;
    }
    lastWriteAt.set(key, now);
    try {
        const patch = {
            nextSpec: next
                ? {
                    id: next.id,
                    title: next.title,
                    reason: next.reason.slice(0, MAX_REASON_LENGTH),
                }
                : null,
            nextSpecReason: next
                ? next.reason.slice(0, MAX_REASON_LENGTH)
                : 'All approved specs are blocked or conflicting. Review blockers in roadmap.md',
        };
        await patchSessionJson(projectPath, patch);
    }
    catch {
        /* best-effort — never throw */
    }
}
/**
 * SPEC-686 Scenarios 12, 15: Write orchestrationPlan to planu/session.json.
 * Pass null to clear the plan (backward compat, Scenario 15).
 * Best-effort — never throws.
 */
export async function writeOrchestrationPlan(projectPath, plan) {
    try {
        const patch = {
            orchestrationPlan: plan,
        };
        await patchSessionJson(projectPath, patch);
    }
    catch {
        /* best-effort — never throw */
    }
}
/**
 * SPEC-686: Patch planu/session.json with the given partial fields.
 * Reads current state, merges, and writes back — preserves all other fields.
 * Best-effort — never throws. Exposed for use by other modules.
 */
export async function patchSessionJson(projectPath, patch) {
    try {
        const filePath = join(projectPath, 'planu', 'session.json');
        await mkdir(dirname(filePath), { recursive: true });
        let current = { activeSpecs: [], updatedAt: '' };
        try {
            const raw = await readFile(filePath, 'utf-8');
            current = JSON.parse(raw);
        }
        catch {
            /* start fresh */
        }
        const updated = {
            ...current,
            ...patch,
            updatedAt: new Date().toISOString(),
        };
        await writeFile(filePath, JSON.stringify(updated, null, 2), 'utf-8');
    }
    catch {
        /* best-effort — never throw */
    }
}
//# sourceMappingURL=session-writer.js.map