// engine/living-spec/auto-reconciler.ts — Auto-reconciliation engine for safe spec changes
// Classifies changes as safe/unsafe and applies only safe changes automatically.
// Rate-limited: max 1 auto-reconcile per spec per 5 minutes.
/** Rate limit: 5 minutes between auto-reconciles per spec. */
const RATE_LIMIT_MS = 5 * 60 * 1000;
/** In-memory rate limit tracker (spec -> last reconcile timestamp). */
const rateLimitMap = new Map();
/**
 * Check if a spec is rate-limited for auto-reconciliation.
 */
export function isRateLimited(specId) {
    const lastTime = rateLimitMap.get(specId);
    if (lastTime === undefined) {
        return false;
    }
    return Date.now() - lastTime < RATE_LIMIT_MS;
}
/**
 * Record that a spec was auto-reconciled (updates rate limit tracker).
 */
export function recordReconcile(specId) {
    rateLimitMap.set(specId, Date.now());
}
/**
 * Clear rate limit for a spec (for testing).
 */
export function clearRateLimit(specId) {
    rateLimitMap.delete(specId);
}
/**
 * Classify a change as safe or unsafe.
 * Safe: completion % update, met/pending status flip, hash snapshot update, timestamp update.
 * Unsafe: status downgrade, criterion removal, scope change.
 */
export function classifyChange(changeType, description) {
    // Check description for unsafe keywords FIRST (overrides type-based classification)
    const lower = description.toLowerCase();
    const unsafeKeywords = ['downgrade', 'remove', 'scope change', 'delete criterion'];
    for (const keyword of unsafeKeywords) {
        if (lower.includes(keyword)) {
            return 'unsafe';
        }
    }
    const safeTypes = new Set([
        'completion-update',
        'status-flip',
        'hash-update',
        'timestamp-update',
    ]);
    if (safeTypes.has(changeType)) {
        return 'safe';
    }
    return 'unsafe';
}
/**
 * Create a reconcile log entry.
 */
export function createLogEntry(specId, changeType, description, previousValue, newValue) {
    return {
        timestamp: new Date().toISOString(),
        changeType,
        specId,
        description,
        previousValue,
        newValue,
    };
}
/**
 * Build auto-reconcile result from changes, filtering by safety classification.
 */
export function buildAutoReconcileResult(specId, changes) {
    if (isRateLimited(specId)) {
        return {
            specId,
            appliedChanges: [],
            skippedUnsafe: [],
            rateLimited: true,
            reconciledAt: new Date().toISOString(),
        };
    }
    const applied = [];
    const skippedUnsafe = [];
    for (const change of changes) {
        const classification = classifyChange(change.changeType, change.description);
        if (classification === 'safe') {
            applied.push(change);
        }
        else {
            skippedUnsafe.push(`${change.changeType}: ${change.description}`);
        }
    }
    if (applied.length > 0) {
        recordReconcile(specId);
    }
    return {
        specId,
        appliedChanges: applied,
        skippedUnsafe,
        rateLimited: false,
        reconciledAt: new Date().toISOString(),
    };
}
//# sourceMappingURL=auto-reconciler.js.map