// tools/challenge-spec/event-challenge-scenarios.ts — Event-driven challenges (SPEC-031)
// Generates backward-compatibility contract and idempotence challenges
// for specs that produce or consume events.
import { detectBrokers, getBrokerConfig, requiresIdempotence, } from '../../engine/event-detector.js';
/**
 * Returns true when the spec or project has event-driven characteristics.
 */
export function isEventDrivenSpec(spec, specContent, knowledge) {
    const brokers = detectBrokers(knowledge.stack);
    if (brokers.length > 0 && brokers[0] !== 'unknown') {
        return true;
    }
    const lower = `${spec.title} ${spec.tags.join(' ')} ${specContent}`.toLowerCase();
    return (lower.includes('event') ||
        lower.includes('kafka') ||
        lower.includes('rabbitmq') ||
        lower.includes('pubsub') ||
        lower.includes('nats') ||
        lower.includes('eventbridge') ||
        lower.includes('sqs') ||
        lower.includes('sns') ||
        lower.includes('message') ||
        lower.includes('consumer') ||
        lower.includes('producer'));
}
/**
 * Generate event-driven failure scenarios for challenge_spec.
 * Covers:
 * - Backward-compatibility contract (schema evolution)
 * - Idempotence / deduplication for at-least-once consumers
 * - DLQ poisoned message
 */
export function generateEventChallengeScenarios(spec, specContent, knowledge) {
    if (!isEventDrivenSpec(spec, specContent, knowledge)) {
        return [];
    }
    const scenarios = [];
    const brokers = detectBrokers(knowledge.stack);
    const primaryBroker = brokers[0] ?? 'unknown';
    const brokerConfig = getBrokerConfig(primaryBroker);
    const brokerName = brokerConfig?.displayName ?? 'message broker';
    // 1. Backward-compatibility contract challenge
    scenarios.push({
        scenario: `[${spec.id}] Event Contract Backward-Compatibility: this spec modifies an existing event schema. Is the change backward-compatible with all known consumers? List the consumers impacted and whether they can handle the new schema without code changes.`,
        probability: 'high',
        impact: 'critical',
        currentHandling: 'Schema change may not have a formal contract review — consumers may fail silently with unrecognized fields or crash on missing ones.',
        requiredHandling: 'Classify the change: additive (new optional field — safe), breaking (removed/type-changed field — requires dual-version emission), or deprecation (field marked deprecated with a sunset date). ' +
            'For breaking changes: emit both old and new versions during the transition period (recommended: 2-4 weeks). ' +
            'Update the event contract in event-contracts/ before merging.',
        dataConsistency: 'If the change is breaking and dual-version is not implemented, consumers will receive invalid events and may write corrupt state.',
        userExperience: 'Invisible to end users, but breaking event changes cause downstream service failures that eventually surface as user-facing errors.',
    });
    // 2. Idempotence challenge (at-least-once brokers)
    if (requiresIdempotence(primaryBroker)) {
        scenarios.push({
            scenario: `[${spec.id}] Idempotence: ${brokerName} guarantees at-least-once delivery — the same event CAN be delivered twice. How does this consumer verify it has not already processed a given event? Describe the deduplication mechanism (event ID stored in DB, idempotency key, time window).`,
            probability: 'high',
            impact: 'high',
            currentHandling: 'Consumer may lack a deduplication check — processing the same event twice could double-charge, double-ship, or create duplicate records.',
            requiredHandling: 'Store the event ID (or a derived idempotency key) in a durable store (DB, Redis with TTL) before processing. ' +
                'Check at the start of each consumer handler: if ID already processed, acknowledge and skip. ' +
                'Choose the deduplication window based on expected redelivery window (typically 24h–7d).',
            dataConsistency: 'The idempotency record must be written atomically with the side-effect (same DB transaction or two-phase commit) to avoid the "processed but not recorded" gap.',
            userExperience: 'Idempotence failure is invisible until users report duplicates (duplicate orders, double payments, etc.) — prevent, do not detect.',
        });
    }
    // 3. DLQ poisoned message challenge
    if (brokerConfig?.dlqSupport && brokerConfig.dlqSupport !== '') {
        scenarios.push({
            scenario: `[${spec.id}] DLQ Poisoned Message: a consumer receives an event with a malformed payload (missing required field, wrong type, corrupt JSON). Does the consumer retry forever and block the queue? How many retries before the message goes to the DLQ?`,
            probability: 'medium',
            impact: 'high',
            currentHandling: 'Consumer may retry indefinitely on schema validation errors, blocking queue processing for all subsequent events.',
            requiredHandling: 'Implement retry with exponential backoff (e.g., 1s, 2s, 4s, 8s, 16s). After max retries (e.g., 5), route to DLQ with original payload + error context. ' +
                `DLQ support: ${brokerConfig.dlqSupport}. ` +
                'Set an alert when DLQ depth exceeds threshold. Document the re-injection procedure for manual recovery.',
            dataConsistency: 'Messages in the DLQ must be investigated and re-injected only after the root cause is fixed — re-injecting without fix will loop back to DLQ.',
            userExperience: 'DLQ accumulation means events are not processed — monitor and alert before SLA is breached.',
        });
    }
    return scenarios;
}
//# sourceMappingURL=event-challenge-scenarios.js.map