CloudSRE-Environment / server /constants.py
Harikishanth R
Fix critical ModuleNotFoundError by removing absolute cloud_sre_v2 imports
160d7db
Raw
History Blame Contribute Delete
103 kB
"""
CloudSRE v2 β€” Constants and Scenario Configuration.
Defines all 5 task tiers with deep scenario pools.
Every fault type here works ACROSS PROCESSES via:
- db_lock: RDS/Aurora EXCLUSIVE on shared file
- queue_overflow: File-backed queue on shared directory
- process_crash: subprocess.kill() sends real signal
- db_pool_exhaustion: Exhausts DB connection pool
- queue_pause: File-based pause flag
- misleading_signal: Writes to shared log files
Scenario count: 26 warmup + 4 single_fault + 17 cascade + 4 multi_cascade
+ unlimited adversarial = 51 static + dynamic
"""
import random
from dataclasses import field
from models import ScenarioSpec, CascadeRule
def _adaptive_choice(scenarios: list, **kwargs) -> "ScenarioSpec":
"""Pick a scenario using adaptive weighted sampling (Theme #4).
If a performance_tracker is provided, scenarios the agent fails at
are sampled MORE OFTEN. Otherwise falls back to uniform random.
"""
tracker = kwargs.get("performance_tracker")
if tracker is None or not scenarios:
return random.choice(scenarios)
scenario_ids = [s.scenario_id for s in scenarios]
weights = tracker.get_weights(scenario_ids)
return random.choices(scenarios, weights=weights, k=1)[0]
# ── Scenario Pools ────────────────────────────────────────────────────────
def _warmup_scenarios(orchestrator, **kwargs) -> ScenarioSpec:
"""Tier 1: Single clear failure, no red herrings, no cascade."""
scenarios = [
ScenarioSpec(
scenario_id="warmup_db_lock",
failure_type="db_lock",
target_service="payment",
difficulty=0.15,
alert_message=(
"🚨 INCIDENT β€” 02:47 UTC | Severity: P2\n"
"Payment service returning HTTP 503 to all requests.\n"
"User reports: 'Cannot checkout', 'Payment keeps failing'"
),
root_cause="RDS connection pool exhausted -- all writes blocked β€” all writes to RDS/Aurora are failing",
correct_fix_description="Restart the payment service or release the RDS lock",
expected_diagnostic_path=[
"curl http://payment.us-east-1.internal/healthz",
"cat /var/log/payment/error.log",
"sqlite3 /data/app.db '.tables'",
"systemctl restart payment",
],
task_id="warmup",
),
ScenarioSpec(
scenario_id="warmup_process_crash",
failure_type="process_crash",
target_service="worker",
params={"target": "worker", "reason": "SIGSEGV"},
difficulty=0.15,
alert_message=(
"🚨 INCIDENT β€” 09:15 UTC | Severity: P2\n"
"Worker service not responding. SQS depth increasing rapidly.\n"
"User reports: 'Orders stuck in processing'"
),
root_cause="ECS task terminated (OOMKilled) (SIGSEGV) β€” messages piling up in queue",
correct_fix_description="Restart the worker service",
expected_diagnostic_path=[
"curl http://worker.eu-west-1.internal/healthz",
"ps aux | grep worker",
"systemctl restart worker",
],
task_id="warmup",
),
ScenarioSpec(
scenario_id="warmup_queue_overflow",
failure_type="queue_overflow",
target_service="worker",
params={"fill": 550},
difficulty=0.15,
alert_message=(
"🚨 INCIDENT β€” 06:30 UTC | Severity: P2\n"
"Worker SQS depth critical β€” 550/1000 messages.\n"
"Payment service unable to enqueue new orders.\n"
"User reports: 'Checkout takes forever', 'Orders not going through'"
),
root_cause="SQS dead letter queue approaching limit β€” worker cannot keep up",
correct_fix_description="Drain queue at controlled rate to clear backlog",
expected_diagnostic_path=[
"queue status",
"ls /data/queue/ | wc -l",
"queue drain 10",
],
task_id="warmup",
),
ScenarioSpec(
scenario_id="warmup_auth_crash",
failure_type="process_crash",
target_service="auth",
params={"target": "auth", "reason": "OOM"},
difficulty=0.20,
alert_message=(
"🚨 INCIDENT β€” 00:01 UTC | Severity: P1\n"
"Auth service returning ConnectionRefused (ECS task not running) on all requests.\n"
"User reports: 'Can't log in', 'Auth errors on every page'"
),
root_cause="EC2 instance terminated (OOMKilled) in us-east-1 (OOM) β€” needs restart",
correct_fix_description="Restart the auth service",
expected_diagnostic_path=[
"curl http://auth.us-east-1.internal/healthz",
"ps aux | grep auth",
"systemctl restart auth",
],
task_id="warmup",
),
ScenarioSpec(
scenario_id="warmup_cache_cold",
failure_type="cache_invalidation",
target_service="cache",
difficulty=0.15,
alert_message=(
"🚨 INCIDENT β€” 11:20 UTC | Severity: P2\n"
"ElastiCache hit ratio dropped from 94% to 0%.\n"
"All requests falling through to database.\n"
"User reports: 'Pages loading very slowly'"
),
root_cause="ElastiCache flush -- all entries expired simultaneously β€” all entries expired simultaneously",
correct_fix_description="Warm the cache back up or restart cache service",
expected_diagnostic_path=[
"curl http://cache.ap-south-1.internal/healthz",
"curl http://cache.ap-south-1.internal/cache/stats",
"cat /var/log/cache/error.log",
"systemctl restart cache",
],
task_id="warmup",
),
ScenarioSpec(
scenario_id="warmup_notification_timeout",
failure_type="process_crash",
target_service="notification",
params={"target": "notification", "reason": "OOM"},
difficulty=0.15,
alert_message=(
"🚨 INCIDENT β€” 08:45 UTC | Severity: P2\n"
"Notification service not responding β€” webhooks not delivering.\n"
"User reports: 'Not receiving order confirmation emails'"
),
root_cause="ECS task evicted (OOMKilled) in ap-south-1 (OOM) β€” needs restart",
correct_fix_description="Restart the notification service",
expected_diagnostic_path=[
"curl http://notification.ap-south-1.internal/healthz",
"ps aux | grep notification",
"systemctl restart notification",
],
task_id="warmup",
),
ScenarioSpec(
scenario_id="warmup_search_index_corruption",
failure_type="index_corruption",
target_service="search",
difficulty=0.20,
alert_message=(
"🚨 INCIDENT β€” 10:12 UTC | Severity: P2\n"
"Search service returning empty results for known queries.\n"
"User reports: 'Product search shows nothing'"
),
root_cause="Search index corruption causing query mismatches",
correct_fix_description="Restart search service to restore clean index state",
expected_diagnostic_path=[
"curl http://search.eu-west-1.internal/healthz",
"curl http://search.eu-west-1.internal/search/stats",
"cat /var/log/search/error.log",
"systemctl restart search",
],
task_id="warmup",
),
ScenarioSpec(
scenario_id="warmup_search_index_lag",
failure_type="index_lag",
target_service="search",
difficulty=0.22,
alert_message=(
"🚨 INCIDENT β€” 11:40 UTC | Severity: P2\n"
"Search indexing delay detected β€” results are stale.\n"
"User reports: 'New products not searchable yet'"
),
root_cause="Search index lag is delaying document visibility",
correct_fix_description="Restart search service to clear indexing lag state",
expected_diagnostic_path=[
"curl http://search.eu-west-1.internal/healthz",
"curl http://search.eu-west-1.internal/search/stats",
"grep \"lag\" /var/log/search/error.log",
"systemctl restart search",
],
task_id="warmup",
),
ScenarioSpec(
scenario_id="warmup_gateway_rate_limit_zero",
failure_type="rate_limit_zero",
target_service="gateway",
difficulty=0.20,
alert_message=(
"🚨 INCIDENT β€” 05:25 UTC | Severity: P2\n"
"Gateway rejecting nearly all API traffic.\n"
"User reports: 'Every API call gets rejected'"
),
root_cause="Gateway rate limit was set to 0 RPS",
correct_fix_description="Restart gateway service to restore normal request flow",
expected_diagnostic_path=[
"curl http://gateway.us-east-1.internal/healthz",
"curl http://gateway.us-east-1.internal/gateway/stats",
"cat /var/log/gateway/error.log",
"systemctl restart gateway",
],
task_id="warmup",
),
ScenarioSpec(
scenario_id="warmup_gateway_circuit_breaker_stuck",
failure_type="circuit_breaker_stuck",
target_service="gateway",
difficulty=0.24,
alert_message=(
"🚨 INCIDENT β€” 15:05 UTC | Severity: P2\n"
"Gateway circuit breaker appears stuck open.\n"
"User reports: 'Requests fail immediately'"
),
root_cause="Gateway circuit breaker is stuck in open state",
correct_fix_description="Restart gateway service to reset the circuit breaker",
expected_diagnostic_path=[
"curl http://gateway.us-east-1.internal/healthz",
"curl http://gateway.us-east-1.internal/gateway/stats",
"grep \"circuit\" /var/log/gateway/error.log",
"systemctl restart gateway",
],
task_id="warmup",
),
ScenarioSpec(
scenario_id="warmup_scheduler_stuck",
failure_type="scheduler_stuck",
target_service="scheduler",
difficulty=0.20,
alert_message=(
"🚨 INCIDENT β€” 06:10 UTC | Severity: P2\n"
"Scheduled jobs have stopped executing.\n"
"User reports: 'Nightly jobs didn't run'"
),
root_cause="Scheduler execution loop is frozen",
correct_fix_description="Restart scheduler service to resume job execution",
expected_diagnostic_path=[
"curl http://scheduler.eu-west-1.internal/healthz",
"curl http://scheduler.eu-west-1.internal/scheduler/stats",
"cat /var/log/scheduler/error.log",
"systemctl restart scheduler",
],
task_id="warmup",
),
ScenarioSpec(
scenario_id="warmup_scheduler_duplicate_execution",
failure_type="duplicate_execution",
target_service="scheduler",
difficulty=0.23,
alert_message=(
"🚨 INCIDENT β€” 12:18 UTC | Severity: P2\n"
"Scheduler reports duplicate job runs.\n"
"User reports: 'Invoices generated twice'"
),
root_cause="Scheduler duplicate execution mode is enabled",
correct_fix_description="Restart scheduler service to clear duplicate execution state",
expected_diagnostic_path=[
"curl http://scheduler.eu-west-1.internal/healthz",
"curl http://scheduler.eu-west-1.internal/scheduler/jobs",
"grep \"duplicate\" /var/log/scheduler/error.log",
"systemctl restart scheduler",
],
task_id="warmup",
),
ScenarioSpec(
scenario_id="warmup_storage_disk_full",
failure_type="disk_full",
target_service="storage",
difficulty=0.22,
alert_message=(
"🚨 INCIDENT β€” 02:42 UTC | Severity: P2\n"
"Storage rejects writes with disk-full errors.\n"
"User reports: 'File uploads failing'"
),
root_cause="Storage service reached full capacity",
correct_fix_description="Restart storage service to recover write path",
expected_diagnostic_path=[
"curl http://storage.eu-west-1.internal/healthz",
"curl http://storage.eu-west-1.internal/storage/stats",
"cat /var/log/storage/error.log",
"systemctl restart storage",
],
task_id="warmup",
),
ScenarioSpec(
scenario_id="warmup_storage_data_corruption",
failure_type="data_corruption",
target_service="storage",
difficulty=0.25,
alert_message=(
"🚨 INCIDENT β€” 13:36 UTC | Severity: P2\n"
"Storage checksum mismatches detected.\n"
"User reports: 'Downloaded files are corrupted'"
),
root_cause="Storage data corruption causing checksum failures",
correct_fix_description="Restart storage service to restore clean storage state",
expected_diagnostic_path=[
"curl http://storage.eu-west-1.internal/healthz",
"curl http://storage.eu-west-1.internal/storage/stats",
"grep \"corrupt\" /var/log/storage/error.log",
"systemctl restart storage",
],
task_id="warmup",
),
ScenarioSpec(
scenario_id="warmup_metrics_scrape_failure",
failure_type="scrape_failure",
target_service="metrics_collector",
difficulty=0.20,
alert_message=(
"🚨 INCIDENT β€” 04:20 UTC | Severity: P2\n"
"Metrics collector failing to scrape all targets.\n"
"User reports: 'Dashboards look frozen'"
),
root_cause="Metrics scrape pipeline failed across all targets",
correct_fix_description="Restart metrics_collector service to restore scraping",
expected_diagnostic_path=[
"curl http://metrics_collector.eu-west-1.internal/healthz",
"curl http://metrics_collector.eu-west-1.internal/metrics/stats",
"cat /var/log/metrics_collector/error.log",
"systemctl restart metrics_collector",
],
task_id="warmup",
),
ScenarioSpec(
scenario_id="warmup_metrics_retention_full",
failure_type="retention_full",
target_service="metrics_collector",
difficulty=0.23,
alert_message=(
"🚨 INCIDENT β€” 19:05 UTC | Severity: P2\n"
"Metrics retention storage is full.\n"
"User reports: 'New metrics are missing'"
),
root_cause="Metrics retention is full and dropping data",
correct_fix_description="Restart metrics_collector service to clear retention pressure",
expected_diagnostic_path=[
"curl http://metrics_collector.eu-west-1.internal/healthz",
"curl http://metrics_collector.eu-west-1.internal/metrics/stats",
"grep \"retention\" /var/log/metrics_collector/error.log",
"systemctl restart metrics_collector",
],
task_id="warmup",
),
ScenarioSpec(
scenario_id="warmup_email_smtp_down",
failure_type="smtp_down",
target_service="email",
difficulty=0.21,
alert_message=(
"🚨 INCIDENT β€” 21:14 UTC | Severity: P2\n"
"Email delivery failing with SMTP errors.\n"
"User reports: 'No alert emails received'"
),
root_cause="SMTP upstream is unavailable",
correct_fix_description="Restart email service to re-establish SMTP flow",
expected_diagnostic_path=[
"curl http://email.ap-south-1.internal/healthz",
"curl http://email.ap-south-1.internal/email/stats",
"cat /var/log/email/error.log",
"systemctl restart email",
],
task_id="warmup",
),
ScenarioSpec(
scenario_id="warmup_email_queue_overflow",
failure_type="email_queue_overflow",
target_service="email",
difficulty=0.24,
alert_message=(
"🚨 INCIDENT β€” 17:50 UTC | Severity: P2\n"
"Email queue size exceeded safe limits.\n"
"User reports: 'Notifications are delayed or missing'"
),
root_cause="Email queue overflow causing dropped messages",
correct_fix_description="Restart email service to reset queue pressure",
expected_diagnostic_path=[
"curl http://email.ap-south-1.internal/healthz",
"curl http://email.ap-south-1.internal/email/stats",
"grep \"overflow\" /var/log/email/error.log",
"systemctl restart email",
],
task_id="warmup",
),
ScenarioSpec(
scenario_id="warmup_billing_desync",
failure_type="billing_desync",
target_service="billing",
difficulty=0.21,
alert_message=(
"🚨 INCIDENT β€” 08:05 UTC | Severity: P2\n"
"Billing totals don't match charge events.\n"
"User reports: 'Revenue dashboard looks wrong'"
),
root_cause="Billing ledger desynchronization",
correct_fix_description="Restart billing service to recover accounting consistency",
expected_diagnostic_path=[
"curl http://billing.us-east-1.internal/healthz",
"curl http://billing.us-east-1.internal/billing/stats",
"cat /var/log/billing/error.log",
"systemctl restart billing",
],
task_id="warmup",
),
ScenarioSpec(
scenario_id="warmup_billing_invoice_stuck",
failure_type="invoice_stuck",
target_service="billing",
difficulty=0.24,
alert_message=(
"🚨 INCIDENT β€” 22:18 UTC | Severity: P2\n"
"Invoice generation pipeline is stalled.\n"
"User reports: 'Invoices did not generate'"
),
root_cause="Invoice generator stuck in billing service",
correct_fix_description="Restart billing service to resume invoice generation",
expected_diagnostic_path=[
"curl http://billing.us-east-1.internal/healthz",
"curl http://billing.us-east-1.internal/billing/stats",
"grep \"invoice\" /var/log/billing/error.log",
"systemctl restart billing",
],
task_id="warmup",
),
ScenarioSpec(
scenario_id="warmup_config_poisoned",
failure_type="config_poisoned",
target_service="config",
difficulty=0.22,
alert_message=(
"🚨 INCIDENT β€” 09:27 UTC | Severity: P2\n"
"Services loading invalid config values.\n"
"User reports: 'Traffic suddenly blocked'"
),
root_cause="Poisoned configuration values in config service",
correct_fix_description="Restart config service to reload safe config state",
expected_diagnostic_path=[
"curl http://config.us-east-1.internal/healthz",
"curl http://config.us-east-1.internal/config/list",
"cat /var/log/config/error.log",
"systemctl restart config",
],
task_id="warmup",
),
ScenarioSpec(
scenario_id="warmup_config_locked",
failure_type="config_locked",
target_service="config",
difficulty=0.25,
alert_message=(
"🚨 INCIDENT β€” 18:34 UTC | Severity: P2\n"
"Config reads and updates are blocked.\n"
"User reports: 'Config updates never apply'"
),
root_cause="Config store is locked",
correct_fix_description="Restart config service to clear lock state",
expected_diagnostic_path=[
"curl http://config.us-east-1.internal/healthz",
"curl http://config.us-east-1.internal/config/list",
"grep \"locked\" /var/log/config/error.log",
"systemctl restart config",
],
task_id="warmup",
),
ScenarioSpec(
scenario_id="warmup_dns_resolution_failure",
failure_type="dns_resolution_failure",
target_service="dns",
difficulty=0.22,
alert_message=(
"🚨 INCIDENT β€” 07:44 UTC | Severity: P2\n"
"Internal service discovery failing.\n"
"User reports: 'Services cannot reach each other'"
),
root_cause="DNS resolution failure returning NXDOMAIN",
correct_fix_description="Restart dns service to restore resolution",
expected_diagnostic_path=[
"curl http://dns.ap-south-1.internal/healthz",
"curl http://dns.ap-south-1.internal/dns/registry",
"cat /var/log/dns/error.log",
"systemctl restart dns",
],
task_id="warmup",
),
ScenarioSpec(
scenario_id="warmup_dns_stale_entries",
failure_type="stale_entries",
target_service="dns",
difficulty=0.24,
alert_message=(
"🚨 INCIDENT β€” 01:33 UTC | Severity: P2\n"
"DNS cache serving stale endpoints.\n"
"User reports: 'Intermittent routing failures'"
),
root_cause="DNS stale cache entries routing to dead targets",
correct_fix_description="Restart dns service to refresh service registry",
expected_diagnostic_path=[
"curl http://dns.ap-south-1.internal/healthz",
"curl http://dns.ap-south-1.internal/dns/registry",
"grep \"stale\" /var/log/dns/error.log",
"systemctl restart dns",
],
task_id="warmup",
),
ScenarioSpec(
scenario_id="warmup_loadbalancer_all_backends_removed",
failure_type="all_backends_removed",
target_service="loadbalancer",
difficulty=0.22,
alert_message=(
"🚨 INCIDENT β€” 03:49 UTC | Severity: P2\n"
"Load balancer has no healthy backends.\n"
"User reports: 'Every request returns 503'"
),
root_cause="All backends removed from load balancer pool",
correct_fix_description="Restart loadbalancer service to restore backend pool",
expected_diagnostic_path=[
"curl http://loadbalancer.us-east-1.internal/healthz",
"curl http://loadbalancer.us-east-1.internal/lb/stats",
"cat /var/log/loadbalancer/error.log",
"systemctl restart loadbalancer",
],
task_id="warmup",
),
ScenarioSpec(
scenario_id="warmup_loadbalancer_session_corruption",
failure_type="session_corruption",
target_service="loadbalancer",
difficulty=0.25,
alert_message=(
"🚨 INCIDENT β€” 23:02 UTC | Severity: P2\n"
"Session affinity behavior is inconsistent.\n"
"User reports: 'Users keep getting logged out'"
),
root_cause="Sticky session data is corrupted in load balancer",
correct_fix_description="Restart loadbalancer service to reset session routing state",
expected_diagnostic_path=[
"curl http://loadbalancer.us-east-1.internal/healthz",
"curl http://loadbalancer.us-east-1.internal/lb/stats",
"grep \"session\" /var/log/loadbalancer/error.log",
"systemctl restart loadbalancer",
],
task_id="warmup",
),
]
return _adaptive_choice(scenarios, **kwargs)
def _single_fault_scenarios(orchestrator, **kwargs) -> ScenarioSpec:
"""Tier 2: Single fault with misleading signals (red herrings)."""
scenarios = [
ScenarioSpec(
scenario_id="single_db_lock_redherring",
failure_type="db_lock",
target_service="payment",
difficulty=0.35,
alert_message=(
"🚨 INCIDENT β€” 14:23 UTC | Severity: P1\n"
"Multiple services showing errors:\n"
" - Payment: HTTP 503 on all POST requests\n"
" - Auth: Elevated latency (120ms vs 40ms baseline)\n"
" - Frontend: 502 Bad Gateway on /api/pay\n"
"User reports: 'Nothing works', 'All pages broken'"
),
root_cause="Database lock in payment service β€” auth latency is a red herring",
correct_fix_description="Fix the RDS connection pool lock in payment; auth is fine",
misleading_signals={
"auth": "AuthService: JWT validation slow β€” 2400ms (normally 5ms)",
"frontend": "FrontendProxy: upstream connection pool near capacity (48/50)",
},
expected_diagnostic_path=[
"systemctl status",
"curl http://payment.us-east-1.internal/healthz",
"curl http://auth.us-east-1.internal/healthz",
"cat /var/log/payment/error.log",
"sqlite3 /data/app.db 'SELECT count(*) FROM payments WHERE status=\"pending\"'",
"systemctl restart payment",
],
task_id="single_fault",
),
ScenarioSpec(
scenario_id="single_worker_crash_redherring",
failure_type="process_crash",
target_service="worker",
params={"target": "worker", "reason": "SIGKILL"},
difficulty=0.40,
alert_message=(
"🚨 INCIDENT β€” 03:15 UTC | Severity: P1\n"
"Worker service not responding β€” port 8003 ConnectionRefused (ECS task not running).\n"
"Queue depth climbing rapidly.\n"
" - Payment: response time elevated (180ms vs 20ms baseline)\n"
" - Auth: 'token refresh rate 3x normal'\n"
"User reports: 'Orders stuck in processing', 'Payments slow'"
),
root_cause="Worker process killed (SIGKILL) β€” payment slowness is queue backpressure",
correct_fix_description="Restart the worker; payment will recover automatically",
misleading_signals={
"payment": "PaymentService: response time elevated (180ms vs 20ms baseline)",
"auth": "AuthService: token refresh rate 3x normal (450/min)",
},
expected_diagnostic_path=[
"ps aux",
"curl http://worker.eu-west-1.internal/healthz",
"queue status",
"systemctl restart worker",
],
task_id="single_fault",
),
ScenarioSpec(
scenario_id="single_queue_overflow",
failure_type="queue_overflow",
target_service="worker",
params={"fill": 950},
difficulty=0.35,
alert_message=(
"🚨 INCIDENT β€” 11:05 UTC | Severity: P1\n"
"Payment service returning 503 intermittently.\n"
"Worker SQS depth critical.\n"
" - Frontend: cache hit ratio dropped (94% β†’ 71%)\n"
"User reports: 'Payments failing randomly'"
),
root_cause="SQS dead letter queue overflow β€” worker can't keep up, payment can't publish",
correct_fix_description="Drain queue at controlled rate, then investigate worker lag",
misleading_signals={
"frontend": "FrontendProxy: cache hit ratio dropped from 94% to 71%",
},
expected_diagnostic_path=[
"queue status",
"ls /data/queue/ | wc -l",
"curl http://worker.eu-west-1.internal/healthz",
"queue drain 10",
],
task_id="single_fault",
),
ScenarioSpec(
scenario_id="single_payment_crash_db_redherring",
failure_type="process_crash",
target_service="payment",
params={"target": "payment", "reason": "unhandled exception"},
difficulty=0.45,
alert_message=(
"🚨 INCIDENT β€” 19:42 UTC | Severity: P1\n"
"Payment service completely unreachable β€” port 8001 closed.\n"
"Database showing elevated latency (50ms vs 5ms baseline).\n"
" - Worker: GC pause time elevated (400ms)\n"
"User reports: 'Can't make any payments'"
),
root_cause="Payment crashed β€” DB latency is from connection retry storms",
correct_fix_description="Restart payment; DB latency resolves when retries stop",
misleading_signals={
"worker": "WorkerService: GC pause time elevated (400ms)",
},
expected_diagnostic_path=[
"curl http://payment.us-east-1.internal/healthz",
"ps aux | grep payment",
"cat /var/log/payment/error.log",
"systemctl restart payment",
],
task_id="single_fault",
),
]
return _adaptive_choice(scenarios, **kwargs)
def _cascade_scenarios(orchestrator, **kwargs) -> ScenarioSpec:
"""Tier 3: Single fault that triggers a cascade after fix."""
scenarios = [
ScenarioSpec(
scenario_id="cascade_db_thundering_herd",
failure_type="db_lock",
target_service="payment",
difficulty=0.55,
alert_message=(
"🚨 INCIDENT β€” 18:44 UTC | Severity: P0\n"
"Payment service fully down β€” database connection errors.\n"
"SQS depth increasing rapidly (847 messages backed up).\n"
"User reports: 'Cannot make any payments for 30 minutes'"
),
root_cause="Database lock causing payment failures and queue backup",
correct_fix_description=(
"1. Release RDS lock\n"
"2. IMPORTANT: Drain queue at CONTROLLED rate (not all at once)\n"
"3. If you drain all at once, worker will OOM from thundering herd"
),
cascade_rules=[
CascadeRule(
trigger_condition="RDS lock released while queue depth > 100",
cascade_type="queue_overflow",
affected_service="worker",
description="847 queued requests flood worker simultaneously β†’ OOM",
agent_must="Use 'queue drain 10' instead of 'queue drain all'",
),
],
expected_diagnostic_path=[
"curl http://payment.us-east-1.internal/healthz",
"cat /var/log/payment/error.log",
"queue status",
"systemctl restart payment",
"queue drain 10",
],
task_id="cascade",
),
ScenarioSpec(
scenario_id="cascade_worker_crash_queue_flood",
failure_type="process_crash",
target_service="worker",
params={"target": "worker", "reason": "SIGSEGV"},
difficulty=0.55,
alert_message=(
"🚨 INCIDENT β€” 22:05 UTC | Severity: P0\n"
"Worker crashed β€” all queued messages accumulating.\n"
"Queue depth: 750/1000 and climbing 20/sec.\n"
"Payment service: 'queue publish timeout β€” backing off'\n"
"User reports: 'Payments accepted but never processing'"
),
root_cause="Worker segfault β†’ queue fills β†’ payment can't enqueue new work",
correct_fix_description=(
"1. Restart worker service (it crashed)\n"
"2. After restart, drain queue at CONTROLLED rate\n"
"3. If drain all at once, worker OOMs from burst load"
),
cascade_rules=[
CascadeRule(
trigger_condition="Worker restarted while queue depth > 200",
cascade_type="queue_overflow",
affected_service="worker",
description="Worker restarts β†’ processes all 750 queued messages β†’ OOM",
agent_must="Drain queue at controlled rate before or after restart",
),
],
expected_diagnostic_path=[
"ps aux | grep worker",
"curl http://worker.eu-west-1.internal/healthz",
"queue status",
"systemctl restart worker",
"queue drain 10",
],
task_id="cascade",
),
ScenarioSpec(
scenario_id="cascade_db_lock_to_payment_crash",
failure_type="db_lock",
target_service="payment",
difficulty=0.60,
alert_message=(
"🚨 INCIDENT β€” 07:30 UTC | Severity: P0\n"
"Payment service returning 503 β€” database errors.\n"
"Service has been retrying DB connections for 15 minutes.\n"
"Memory usage climbing due to retry queue buildup.\n"
"User reports: 'All payments failing since 7:15am'"
),
root_cause="RDS lock β†’ payment retries exhaust memory β†’ payment crashes",
correct_fix_description=(
"1. Release RDS lock first\n"
"2. Then restart payment (it may have crashed from retries)\n"
"3. Monitor payment memory after restart"
),
cascade_rules=[
CascadeRule(
trigger_condition="RDS lock released β†’ payment retry storm",
cascade_type="process_crash",
affected_service="payment",
description="Payment retries all failed writes β†’ memory spike β†’ OOM",
agent_must="Restart payment after releasing RDS lock",
),
],
expected_diagnostic_path=[
"curl http://payment.us-east-1.internal/healthz",
"cat /var/log/payment/error.log",
"sqlite3 /data/app.db 'PRAGMA journal_mode'",
"systemctl restart payment",
],
task_id="cascade",
),
ScenarioSpec(
scenario_id="cascade_queue_overflow_worker_crash",
failure_type="queue_overflow",
target_service="worker",
params={"fill": 990},
difficulty=0.60,
alert_message=(
"🚨 INCIDENT β€” 13:00 UTC | Severity: P0\n"
"Queue at 990/1000 β€” nearly full.\n"
"Worker consuming slowly (10/min vs normal 200/min).\n"
"New payments being dropped β€” queue rejecting publishes.\n"
"User reports: 'Payments disappearing', 'No confirmation emails'"
),
root_cause="Queue near-full β†’ if drained too fast, worker crashes from burst",
correct_fix_description=(
"1. Check queue depth: ls /data/queue/ | wc -l\n"
"2. Drain in batches of 10-20, not all at once\n"
"3. If worker crashes, restart and drain more slowly"
),
cascade_rules=[
CascadeRule(
trigger_condition="Queue drained too fast (>50 at once)",
cascade_type="process_crash",
affected_service="worker",
description="Mass drain β†’ 990 messages processed β†’ worker OOM",
agent_must="Use 'queue drain 10' repeatedly, not 'queue drain all'",
),
],
expected_diagnostic_path=[
"queue status",
"ls /data/queue/ | wc -l",
"curl http://worker.eu-west-1.internal/healthz",
"queue drain 10",
],
task_id="cascade",
),
ScenarioSpec(
scenario_id="cascade_auth_crash_payment_timeout",
failure_type="process_crash",
target_service="auth",
params={"target": "auth", "reason": "OOM"},
difficulty=0.55,
alert_message=(
"🚨 INCIDENT β€” 16:20 UTC | Severity: P0\n"
"Auth service crashed β€” port 8002 ConnectionRefused (ECS task not running).\n"
"Payment service reporting auth timeout errors.\n"
"All user sessions invalid β€” cannot verify any tokens.\n"
"User reports: 'Logged out everywhere', 'Can't authenticate'"
),
root_cause="Auth OOM β†’ payment can't authenticate β†’ queue fills with retries",
correct_fix_description=(
"1. Restart auth service\n"
"2. After auth is back, payment retries may flood queue\n"
"3. Monitor queue depth and drain if needed"
),
cascade_rules=[
CascadeRule(
trigger_condition="Auth restarted β†’ payment retries flood queue",
cascade_type="queue_overflow",
affected_service="worker",
description="All deferred auth-dependent requests retry β†’ queue overflow",
agent_must="Monitor queue after auth restart and drain conservatively",
),
],
expected_diagnostic_path=[
"curl http://auth.us-east-1.internal/healthz",
"ps aux | grep auth",
"systemctl restart auth",
"queue status",
"queue drain 10",
],
task_id="cascade",
),
ScenarioSpec(
scenario_id="cascade_db_cache_stampede",
failure_type="db_lock",
target_service="payment",
difficulty=0.60,
alert_message=(
"🚨 INCIDENT β€” 14:55 UTC | Severity: P0\n"
"Payment service returning 503 β€” RDS connection pool locked.\n"
"Cache hit ratio dropping rapidly (94% β†’ 12%).\n"
"Cache entries expiring without refresh.\n"
"User reports: 'Checkout keeps timing out'"
),
root_cause="RDS lock blocks cache refresh β†’ cache expires β†’ thundering herd on DB recovery",
correct_fix_description=(
"1. Release RDS lock (root cause)\n"
"2. IMPORTANT: Warm cache BEFORE traffic resumes\n"
"3. If cache stays cold, thundering herd will crash DB again"
),
cascade_rules=[
CascadeRule(
trigger_condition="RDS lock released while cache is cold",
cascade_type="cache_invalidation",
affected_service="cache",
description="All cached data expired during outage β†’ 100% miss rate β†’ DB thundering herd",
agent_must="Warm cache after fixing DB, before full traffic resumes",
),
],
expected_diagnostic_path=[
"curl http://payment.us-east-1.internal/healthz",
"curl http://cache.ap-south-1.internal/cache/stats",
"cat /var/log/payment/error.log",
"systemctl restart payment",
"curl http://cache.ap-south-1.internal/cache/warmup",
],
task_id="cascade",
),
ScenarioSpec(
scenario_id="cascade_payment_webhook_storm",
failure_type="process_crash",
target_service="payment",
params={"target": "payment", "reason": "SIGSEGV"},
difficulty=0.60,
alert_message=(
"🚨 INCIDENT β€” 20:30 UTC | Severity: P0\n"
"Payment service crashed β€” port 8001 closed.\n"
"Notification service queuing webhooks (can't deliver).\n"
"Webhook queue depth climbing: 250 and rising.\n"
"User reports: 'Payments failing, no confirmation emails'"
),
root_cause="Payment crash β†’ webhooks queue up β†’ when payment recovers, webhook storm overwhelms it",
correct_fix_description=(
"1. Restart payment service\n"
"2. IMPORTANT: Drain webhook queue at controlled rate\n"
"3. If all webhooks fire at once, payment will crash again"
),
cascade_rules=[
CascadeRule(
trigger_condition="Payment restarted β†’ queued webhooks fire simultaneously",
cascade_type="webhook_storm",
affected_service="notification",
description="300 queued webhooks retry at once β†’ payment overwhelmed β†’ crashes again",
agent_must="Drain webhook queue at controlled rate after payment restart",
),
],
expected_diagnostic_path=[
"curl http://payment.us-east-1.internal/healthz",
"curl http://notification.ap-south-1.internal/webhooks/pending",
"ps aux | grep payment",
"systemctl restart payment",
"curl -X POST http://notification.ap-south-1.internal/webhooks/drain?count=10",
],
task_id="cascade",
),
ScenarioSpec(
scenario_id="cascade_search_corruption_to_gateway_block",
failure_type="index_corruption",
target_service="search",
difficulty=0.58,
alert_message=(
"🚨 INCIDENT β€” 09:10 UTC | Severity: P0\n"
"Search index corruption causing empty critical lookups.\n"
"Gateway fallback traffic surging.\n"
"User reports: 'Search broken and checkout now timing out'"
),
root_cause="Search corruption first, then gateway throttling under retry surge",
correct_fix_description=(
"1. Recover search service first\n"
"2. Then handle gateway overload cascade safely"
),
cascade_rules=[
CascadeRule(
trigger_condition="Search restored while retries are still high",
cascade_type="rate_limit_zero",
affected_service="gateway",
description="Recovery surge flips gateway into hard-reject mode",
agent_must="Restart gateway after stabilizing search",
),
],
expected_diagnostic_path=[
"curl http://search.eu-west-1.internal/healthz",
"curl http://gateway.us-east-1.internal/healthz",
"cat /var/log/search/error.log",
"systemctl restart search",
"systemctl restart gateway",
],
task_id="cascade",
),
ScenarioSpec(
scenario_id="cascade_gateway_circuit_to_scheduler_stuck",
failure_type="circuit_breaker_stuck",
target_service="gateway",
difficulty=0.57,
alert_message=(
"🚨 INCIDENT β€” 10:55 UTC | Severity: P0\n"
"Gateway is rejecting all upstream calls.\n"
"Scheduler callbacks now piling up.\n"
"User reports: 'APIs down and jobs are delayed'"
),
root_cause="Gateway circuit stuck open triggers scheduler backlog",
correct_fix_description=(
"1. Restore gateway service\n"
"2. Monitor and recover scheduler after callback surge"
),
cascade_rules=[
CascadeRule(
trigger_condition="Gateway restored with pending callback backlog",
cascade_type="scheduler_stuck",
affected_service="scheduler",
description="Job callback flood stalls scheduler loop",
agent_must="Restart scheduler after gateway comes back",
),
],
expected_diagnostic_path=[
"curl http://gateway.us-east-1.internal/gateway/stats",
"curl http://scheduler.eu-west-1.internal/scheduler/stats",
"systemctl restart gateway",
"systemctl restart scheduler",
],
task_id="cascade",
),
ScenarioSpec(
scenario_id="cascade_scheduler_duplicate_to_billing_desync",
failure_type="duplicate_execution",
target_service="scheduler",
difficulty=0.58,
alert_message=(
"🚨 INCIDENT β€” 12:40 UTC | Severity: P0\n"
"Duplicate scheduler executions detected in billing jobs.\n"
"User reports: 'Charges and invoices are inconsistent'"
),
root_cause="Scheduler duplicate execution creates inconsistent billing writes",
correct_fix_description=(
"1. Recover scheduler first\n"
"2. Then recover billing consistency"
),
cascade_rules=[
CascadeRule(
trigger_condition="Scheduler stabilized after duplicate bursts",
cascade_type="billing_desync",
affected_service="billing",
description="Duplicate writes leave billing ledger desynchronized",
agent_must="Restart billing after stabilizing scheduler",
),
],
expected_diagnostic_path=[
"curl http://scheduler.eu-west-1.internal/scheduler/stats",
"curl http://billing.us-east-1.internal/billing/stats",
"systemctl restart scheduler",
"systemctl restart billing",
],
task_id="cascade",
),
ScenarioSpec(
scenario_id="cascade_storage_disk_full_to_email_overflow",
failure_type="disk_full",
target_service="storage",
difficulty=0.60,
alert_message=(
"🚨 INCIDENT β€” 02:22 UTC | Severity: P0\n"
"Storage writes are failing due to full disk.\n"
"Retry notifications are rapidly accumulating.\n"
"User reports: 'Uploads failing and alerts delayed'"
),
root_cause="Storage write outage creates retry storm in email pipeline",
correct_fix_description=(
"1. Recover storage writes\n"
"2. Then drain email pressure safely"
),
cascade_rules=[
CascadeRule(
trigger_condition="Storage restored while retries are queued",
cascade_type="email_queue_overflow",
affected_service="email",
description="Queued retry notices overflow email delivery queue",
agent_must="Restart email after restoring storage",
),
],
expected_diagnostic_path=[
"curl http://storage.eu-west-1.internal/storage/stats",
"curl http://email.ap-south-1.internal/email/stats",
"systemctl restart storage",
"systemctl restart email",
],
task_id="cascade",
),
ScenarioSpec(
scenario_id="cascade_metrics_scrape_failure_to_dns_stale",
failure_type="scrape_failure",
target_service="metrics_collector",
difficulty=0.56,
alert_message=(
"🚨 INCIDENT β€” 14:08 UTC | Severity: P0\n"
"Metrics collector blind β€” no fresh scrape data.\n"
"Service discovery changes are now delayed.\n"
"User reports: 'Routing feels random and monitoring is dark'"
),
root_cause="Metrics scrape failure hides DNS freshness problems",
correct_fix_description=(
"1. Restore metrics collection first\n"
"2. Then recover DNS freshness"
),
cascade_rules=[
CascadeRule(
trigger_condition="Metrics restored after prolonged blind period",
cascade_type="stale_entries",
affected_service="dns",
description="Stale DNS cache is discovered and starts causing wrong routing",
agent_must="Restart dns after metrics_collector recovery",
),
],
expected_diagnostic_path=[
"curl http://metrics_collector.eu-west-1.internal/metrics/stats",
"curl http://dns.ap-south-1.internal/dns/registry",
"systemctl restart metrics_collector",
"systemctl restart dns",
],
task_id="cascade",
),
ScenarioSpec(
scenario_id="cascade_email_smtp_down_to_notification_storm",
failure_type="smtp_down",
target_service="email",
difficulty=0.57,
alert_message=(
"🚨 INCIDENT β€” 16:41 UTC | Severity: P0\n"
"Email SMTP is down and deliveries are backing up.\n"
"Webhook retries are increasing.\n"
"User reports: 'No emails and delayed order confirmations'"
),
root_cause="SMTP outage causes notification retry storm after recovery",
correct_fix_description=(
"1. Recover email service first\n"
"2. Then handle notification retry surge"
),
cascade_rules=[
CascadeRule(
trigger_condition="Email recovered with large pending delivery backlog",
cascade_type="webhook_storm",
affected_service="notification",
description="Notification webhook retries fire in a burst",
agent_must="Drain retries conservatively after email recovery",
),
],
expected_diagnostic_path=[
"curl http://email.ap-south-1.internal/email/stats",
"curl http://notification.ap-south-1.internal/webhooks/pending",
"systemctl restart email",
"curl -X POST http://notification.ap-south-1.internal/webhooks/drain?count=10",
],
task_id="cascade",
),
ScenarioSpec(
scenario_id="cascade_billing_desync_to_config_poisoned",
failure_type="billing_desync",
target_service="billing",
difficulty=0.58,
alert_message=(
"🚨 INCIDENT β€” 18:12 UTC | Severity: P0\n"
"Billing ledger mismatch detected across services.\n"
"Automated remediation attempted config changes.\n"
"User reports: 'Charges wrong and APIs unstable'"
),
root_cause="Billing desync triggers bad auto-config rollout",
correct_fix_description=(
"1. Stabilize billing first\n"
"2. Then recover poisoned config state"
),
cascade_rules=[
CascadeRule(
trigger_condition="Billing recovered after inconsistent charge stream",
cascade_type="config_poisoned",
affected_service="config",
description="Auto-remediation writes unsafe config values",
agent_must="Restart config after billing stabilization",
),
],
expected_diagnostic_path=[
"curl http://billing.us-east-1.internal/billing/stats",
"curl http://config.us-east-1.internal/config/list",
"systemctl restart billing",
"systemctl restart config",
],
task_id="cascade",
),
ScenarioSpec(
scenario_id="cascade_config_locked_to_gateway_circuit",
failure_type="config_locked",
target_service="config",
difficulty=0.60,
alert_message=(
"🚨 INCIDENT β€” 20:02 UTC | Severity: P0\n"
"Config service locked and updates blocked.\n"
"Gateway policy refresh failing repeatedly.\n"
"User reports: 'Requests rejected after config freeze'"
),
root_cause="Config lock prevents gateway policy updates",
correct_fix_description=(
"1. Unlock config service via restart\n"
"2. Recover gateway from stuck circuit state"
),
cascade_rules=[
CascadeRule(
trigger_condition="Config restored after prolonged gateway refresh failures",
cascade_type="circuit_breaker_stuck",
affected_service="gateway",
description="Gateway remains stuck open after stale policy cycle",
agent_must="Restart gateway after config recovery",
),
],
expected_diagnostic_path=[
"curl http://config.us-east-1.internal/config/list",
"curl http://gateway.us-east-1.internal/gateway/stats",
"systemctl restart config",
"systemctl restart gateway",
],
task_id="cascade",
),
ScenarioSpec(
scenario_id="cascade_dns_failure_to_loadbalancer_empty",
failure_type="dns_resolution_failure",
target_service="dns",
difficulty=0.61,
alert_message=(
"🚨 INCIDENT β€” 00:48 UTC | Severity: P0\n"
"DNS resolution failing for internal services.\n"
"Load balancer health checks cannot resolve backends.\n"
"User reports: 'Entire platform serving 503s'"
),
root_cause="DNS outage causes load balancer backend removal",
correct_fix_description=(
"1. Recover DNS resolution first\n"
"2. Then recover load balancer backend pool"
),
cascade_rules=[
CascadeRule(
trigger_condition="DNS restored after failed LB health checks",
cascade_type="all_backends_removed",
affected_service="loadbalancer",
description="Load balancer has already evicted all backends",
agent_must="Restart loadbalancer after DNS recovery",
),
],
expected_diagnostic_path=[
"curl http://dns.ap-south-1.internal/dns/registry",
"curl http://loadbalancer.us-east-1.internal/lb/stats",
"systemctl restart dns",
"systemctl restart loadbalancer",
],
task_id="cascade",
),
ScenarioSpec(
scenario_id="cascade_loadbalancer_session_to_auth_overflow",
failure_type="session_corruption",
target_service="loadbalancer",
difficulty=0.56,
alert_message=(
"🚨 INCIDENT β€” 05:58 UTC | Severity: P0\n"
"Load balancer session routing is corrupted.\n"
"Auth retries climbing sharply.\n"
"User reports: 'Users constantly logged out'"
),
root_cause="Session corruption at load balancer causes auth retry flood",
correct_fix_description=(
"1. Recover load balancer session routing\n"
"2. Then stabilize auth retry pressure"
),
cascade_rules=[
CascadeRule(
trigger_condition="Load balancer restored while retries remain queued",
cascade_type="queue_overflow",
affected_service="worker",
description="Session retry flood overflows worker queue",
agent_must="Drain queue conservatively after load balancer recovery",
),
],
expected_diagnostic_path=[
"curl http://loadbalancer.us-east-1.internal/lb/stats",
"queue status",
"systemctl restart loadbalancer",
"queue drain 10",
],
task_id="cascade",
),
]
return _adaptive_choice(scenarios, **kwargs)
def _multi_cascade_scenarios(orchestrator, **kwargs) -> ScenarioSpec:
"""Tier 4: Multiple cascading failures β€” the hardest deterministic scenarios."""
scenarios = [
ScenarioSpec(
scenario_id="multi_cascade_full_outage",
failure_type="db_lock",
target_service="payment",
difficulty=0.75,
alert_message=(
"🚨 INCIDENT β€” 03:00 UTC | Severity: P0 β€” FULL OUTAGE\n"
"All services degraded or failing:\n"
" - Payment: 503 on all requests (RDS locked)\n"
" - Worker: Queue depth 940/1000, processing stalled\n"
" - Auth: Elevated error rate\n"
" - Frontend: 502 Bad Gateway on everything\n"
"Duration: 45 minutes and escalating\n"
"User reports: 'Site is completely down'"
),
root_cause="Multi-fault: RDS lock (primary) + queue overflow (secondary)",
correct_fix_description=(
"PRIORITY ORDER:\n"
"1. Fix RDS lock (primary cause of payment failure)\n"
"2. Drain queue at controlled rate (prevent thundering herd)\n"
"3. Verify all services healthy"
),
cascade_rules=[
CascadeRule(
trigger_condition="RDS lock released",
cascade_type="queue_overflow",
affected_service="worker",
description="Queue floods worker after DB release",
agent_must="Drain queue at controlled rate",
),
],
expected_diagnostic_path=[
"systemctl status",
"curl http://payment.us-east-1.internal/healthz",
"cat /var/log/payment/error.log",
"systemctl restart payment",
"queue drain 10",
],
task_id="multi_cascade",
),
ScenarioSpec(
scenario_id="multi_cascade_payment_worker_down",
failure_type="process_crash",
target_service="payment",
params={"target": "payment", "reason": "SIGSEGV"},
difficulty=0.80,
alert_message=(
"🚨 INCIDENT β€” 04:15 UTC | Severity: P0 β€” FULL OUTAGE\n"
"Payment AND Worker both unresponsive:\n"
" - Payment: ConnectionRefused (ECS task not running) on port 8001\n"
" - Worker: ConnectionRefused (ECS task not running) on port 8003\n"
" - Queue: 870 messages backed up\n"
" - Auth: Elevated latency (possible red herring)\n"
"Duration: 20 minutes\n"
"User reports: 'Nothing is working'"
),
root_cause="Payment crashed β†’ queue backed up β†’ worker OOM from pressure",
correct_fix_description=(
"1. Restart payment first (root cause)\n"
"2. Drain queue BEFORE restarting worker\n"
"3. Restart worker\n"
"4. Ignore auth latency β€” red herring"
),
misleading_signals={
"auth": "AuthService: JWT validation slow β€” 1800ms (normally 5ms)",
},
cascade_rules=[
CascadeRule(
trigger_condition="Payment restarted β†’ queue floods worker",
cascade_type="process_crash",
affected_service="worker",
description="Restarted payment publishes burst β†’ worker crashes",
agent_must="Drain queue before restarting worker",
),
],
expected_diagnostic_path=[
"ps aux",
"systemctl restart payment",
"queue drain 10",
"systemctl restart worker",
],
task_id="multi_cascade",
),
ScenarioSpec(
scenario_id="multi_cascade_db_queue_auth",
failure_type="db_lock",
target_service="payment",
difficulty=0.85,
alert_message=(
"🚨 INCIDENT β€” 02:30 UTC | Severity: P0 β€” TRIPLE FAULT\n"
"Three simultaneous failures detected:\n"
" - Payment: 503 β€” 'RDS connection pool exhausted'\n"
" - Worker: Queue at 960/1000, not processing\n"
" - Auth: Process crashed β€” port 8002 ConnectionRefused (ECS task not running)\n"
"User reports: 'Complete site outage for 1 hour'"
),
root_cause="RDS lock (root) β†’ queue overflow β†’ auth crashed from load",
correct_fix_description=(
"CRITICAL ORDER:\n"
"1. Release RDS lock (unblocks payment)\n"
"2. Drain queue slowly (prevents worker OOM)\n"
"3. Restart auth (was collateral damage)\n"
"4. Verify all services via systemctl status"
),
cascade_rules=[
CascadeRule(
trigger_condition="DB released β†’ queue flood β†’ auth overload",
cascade_type="process_crash",
affected_service="auth",
description="Cascading load reaches auth β†’ auth crashes",
agent_must="Fix in order: DB β†’ queue β†’ auth",
),
],
expected_diagnostic_path=[
"systemctl status",
"cat /var/log/payment/error.log",
"queue status",
"ps aux",
"systemctl restart payment",
"queue drain 10",
"systemctl restart auth",
"systemctl status",
],
task_id="multi_cascade",
),
ScenarioSpec(
scenario_id="multi_cascade_rolling_crash",
failure_type="process_crash",
target_service="worker",
params={"target": "worker", "reason": "SIGKILL"},
difficulty=0.90,
alert_message=(
"🚨 INCIDENT β€” 01:00 UTC | Severity: P0 β€” CASCADING OUTAGE\n"
"Worker killed by OOM killer. Queue overflowing.\n"
" - Worker: DEAD (SIGKILL by kernel OOM)\n"
" - Queue: 995/1000 β€” dropping new messages\n"
" - Payment: 'queue publish failed β€” backing off'\n"
" - Frontend: 504 Gateway Timeout\n"
"This is a ROLLING FAILURE β€” fixing one may crash another.\n"
"User reports: 'Has been broken for 2 hours'"
),
root_cause=(
"Worker OOM β†’ queue fills β†’ if worker restarted without "
"draining, it OOMs again from burst"
),
correct_fix_description=(
"CAREFUL β€” this will cascade if you rush:\n"
"1. Do NOT restart worker yet β€” queue will kill it again\n"
"2. Drain queue to <100 messages (queue drain 10, repeat)\n"
"3. THEN restart worker\n"
"4. Monitor: if queue climbs again, worker is overloaded"
),
cascade_rules=[
CascadeRule(
trigger_condition="Worker restarted while queue > 500",
cascade_type="process_crash",
affected_service="worker",
description="Worker restarts β†’ processes 995 msgs β†’ OOM again",
agent_must="Drain queue BEFORE restarting worker",
),
],
expected_diagnostic_path=[
"ps aux | grep worker",
"queue status",
"ls /data/queue/ | wc -l",
"queue drain 10",
"queue drain 10",
"queue drain 10",
"systemctl restart worker",
],
task_id="multi_cascade",
),
]
chosen = _adaptive_choice(scenarios, **kwargs)
# Inject additional faults for multi-cascade complexity
if "full_outage" in chosen.scenario_id:
orchestrator.inject_fault("queue_overflow", {"fill": 900})
if "payment_worker" in chosen.scenario_id:
orchestrator.inject_fault("process_crash", {"target": "worker", "reason": "OOM"})
if "db_queue_auth" in chosen.scenario_id:
orchestrator.inject_fault("queue_overflow", {"fill": 960})
orchestrator.inject_fault("process_crash", {"target": "auth", "reason": "OOM"})
if "rolling_crash" in chosen.scenario_id:
orchestrator.inject_fault("queue_overflow", {"fill": 995})
return chosen
# Death Spiral scenario definitions β€” multi-fault cascading failures
# where fix order matters. Zero-shot 70B models CANNOT solve these.
_DEATH_SPIRALS = [
{
"id": "death_spiral_dns_cascade",
"name": "The Global Outage",
"root_fault": ("dns_resolution_failure", "dns"),
"description": (
"DNS corruption cascades through the entire infrastructure. "
"Gateway can't route, loadbalancer returns 503, payment and billing fail. "
"Fix order: DNS β†’ gateway auto-recovers β†’ restart payment β†’ restart billing."
),
"alert": (
"🚨 INCIDENT β€” {time} UTC | Severity: P0\n"
"CRITICAL: Multiple services degraded across all regions\n"
" - payment.us-east-1.internal: 503 Service Unavailable\n"
" - gateway.us-east-1.internal: 502 Bad Gateway\n"
" - billing.us-east-1.internal: ledger desync detected\n"
" - loadbalancer.us-east-1.internal: all backends removed\n"
" - dns.ap-south-1.internal: NXDOMAIN on all lookups\n"
"Duration: 12 minutes and escalating\n"
"Customer impact: ALL payment processing halted"
),
"fix_order": ["dns", "gateway", "payment", "billing"],
},
{
"id": "death_spiral_config_poison",
"name": "The Config Poison",
"root_fault": ("config_poisoned", "config"),
"description": (
"Config store poisoned with bad values. Search and metrics_collector "
"use config for their operation β€” both degrade. Metrics blindness means "
"the scheduler can't auto-scale, causing worker queue to overflow."
),
"alert": (
"🚨 INCIDENT β€” {time} UTC | Severity: P0\n"
"CRITICAL: Configuration corruption detected\n"
" - config.us-east-1.internal: poisoned values (rate_limit=0)\n"
" - search.eu-west-1.internal: empty results on all queries\n"
" - metrics_collector.eu-west-1.internal: scrape failures\n"
" - worker.eu-west-1.internal: queue depth 950/1000\n"
"Duration: 8 minutes and escalating\n"
"Customer impact: Search broken, monitoring blind"
),
"fix_order": ["config", "search", "metrics_collector"],
"extra_faults": [("queue_overflow", {"fill": 950})],
},
{
"id": "death_spiral_auth_collapse",
"name": "The Auth Cascade",
"root_fault": ("process_crash", "auth"),
"description": (
"Auth service OOMKilled. Everything that depends on auth fails: "
"payment can't verify tokens, billing can't reconcile, frontend "
"shows 401 errors to all users. Gateway circuit breaker trips."
),
"alert": (
"🚨 INCIDENT β€” {time} UTC | Severity: P0\n"
"CRITICAL: Authentication infrastructure failure\n"
" - auth.us-east-1.internal: ECS task OOMKilled\n"
" - payment.us-east-1.internal: 401 Unauthorized on all requests\n"
" - billing.us-east-1.internal: invoice generation stuck\n"
" - frontend.ap-south-1.internal: user sessions expired\n"
" - gateway.us-east-1.internal: circuit breaker OPEN\n"
"Duration: 5 minutes and escalating\n"
"Customer impact: ALL users logged out, payments failing"
),
"fix_order": ["auth", "gateway", "payment"],
"extra_faults": [("circuit_breaker_stuck", {})],
"auth_crash_params": {"target": "auth", "reason": "OOM"},
},
{
"id": "death_spiral_storage_meltdown",
"name": "The Storage Meltdown",
"root_fault": ("disk_full", "storage"),
"description": (
"Storage disk full. Search can't write indexes, email queue "
"overflows because logs can't be written, metrics retention "
"fills up. The cascade looks like 5 independent failures but "
"they all stem from storage."
),
"alert": (
"🚨 INCIDENT β€” {time} UTC | Severity: P1\n"
"CRITICAL: Storage subsystem failure\n"
" - storage.eu-west-1.internal: disk 100% full, writes rejected\n"
" - search.eu-west-1.internal: index lag > 5000 documents\n"
" - email.ap-south-1.internal: queue overflow, dropping messages\n"
" - metrics_collector.eu-west-1.internal: retention full\n"
"Duration: 15 minutes and escalating\n"
"Customer impact: Search stale, emails delayed"
),
"fix_order": ["storage", "search", "email", "metrics_collector"],
"extra_faults": [
("index_lag", {}),
("email_queue_overflow", {}),
("retention_full", {}),
],
},
{
"id": "death_spiral_scheduler_chaos",
"name": "The Scheduler Chaos",
"root_fault": ("scheduler_stuck", "scheduler"),
"description": (
"Scheduler stuck causes worker jobs to pile up. Worker queue "
"overflows, notification service can't deliver because worker "
"is backed up. Meanwhile scheduler duplicate execution mode "
"fires billing jobs twice, causing desync."
),
"alert": (
"🚨 INCIDENT β€” {time} UTC | Severity: P1\n"
"CRITICAL: Job scheduling infrastructure failure\n"
" - scheduler.eu-west-1.internal: main loop frozen\n"
" - worker.eu-west-1.internal: SQS depth 900/1000\n"
" - notification.ap-south-1.internal: delivery backed up\n"
" - billing.us-east-1.internal: duplicate charges detected\n"
"Duration: 20 minutes and escalating\n"
"Customer impact: Delayed processing, duplicate billing"
),
"fix_order": ["scheduler", "worker"],
"extra_faults": [
("queue_overflow", {"fill": 900}),
("billing_desync", {}),
],
},
]
def _adversarial_scenarios(orchestrator, **kwargs) -> ScenarioSpec:
"""Tier 5: Death Spiral scenarios and LLM Adversarial Designer.
Multi-fault cascading failures with strict fix-order requirements,
or dynamically generated LLM scenarios targeting agent weaknesses.
"""
tracker = kwargs.get("performance_tracker")
episode_count = tracker.get_episode_count() if tracker else 0
# Progressive difficulty: start at 2, max 5, increase every 3 episodes
difficulty = min(5, max(2, (episode_count // 3) + 2))
hf_token = os.environ.get("HF_TOKEN")
# 50% chance: Death Spiral (if difficulty >= 4), 50% chance: LLM / dynamic
if random.random() < 0.5 and difficulty >= 4 and _DEATH_SPIRALS:
spiral = random.choice(_DEATH_SPIRALS)
fault_type, target = spiral["root_fault"]
# Inject root fault
if fault_type == "process_crash":
crash_params = spiral.get("auth_crash_params", {"target": target, "reason": "OOM"})
orchestrator.inject_fault("process_crash", crash_params)
else:
orchestrator.inject_fault(fault_type, {})
# Inject extra faults (symptoms that look independent but aren't)
for extra_fault, extra_params in spiral.get("extra_faults", []):
orchestrator.inject_fault(extra_fault, extra_params)
time_str = f"{random.randint(0,23):02d}:{random.randint(0,59):02d}"
alert = spiral["alert"].format(time=time_str)
return ScenarioSpec(
scenario_id=spiral["id"],
failure_type=fault_type,
target_service=target,
params={},
difficulty=0.85,
alert_message=alert,
root_cause=spiral["description"],
correct_fix_description=f"Fix order: {' β†’ '.join(spiral['fix_order'])}",
misleading_signals={},
cascade_rules={},
expected_diagnostic_path=[
"systemctl status",
f"curl http://{target}.*.internal/healthz",
f"cat /var/log/{target}/error.log",
],
task_id="adversarial",
)
# Use LLM Adversarial Designer
if hf_token:
try:
import os
import logging
# Import inline to avoid circular imports if any
try:
from server.adversarial_designer import AdversarialDesigner
except ImportError:
from server.adversarial_designer import AdversarialDesigner
designer = AdversarialDesigner(hf_token=hf_token)
if tracker:
designer.tracker = tracker
raw_scenario = designer.design_scenario(difficulty=difficulty)
fault_type = raw_scenario.get("failure_type", "latency_injection")
target = raw_scenario.get("target_service", "payment")
# Validation to ensure the environment doesn't crash on hallucinated faults
if fault_type not in _FAULT_CATALOG:
fault_type = "process_crash"
valid_targets = ["payment", "auth", "worker", "frontend", "cache", "notification",
"search", "gateway", "scheduler", "storage", "metrics_collector",
"email", "billing", "config", "dns", "loadbalancer"]
if target not in valid_targets:
target = "payment"
catalog_entry = _FAULT_CATALOG[fault_type]
params = catalog_entry["gen_params"]() if "gen_params" in catalog_entry else {}
params["target"] = target
misleading = {}
for hr in raw_scenario.get("red_herrings", []):
hr_svc = hr.get("service")
if hr_svc in valid_targets:
misleading[hr_svc] = hr.get("message", "Misleading signal")
return ScenarioSpec(
scenario_id=f"llm_adversarial_{random.randint(1000, 9999)}",
failure_type=fault_type,
target_service=target,
params=params,
difficulty=difficulty * 0.2,
alert_message=raw_scenario.get("alert_message", f"Alert on {target}"),
root_cause=raw_scenario.get("root_cause", f"{fault_type} on {target}"),
correct_fix_description=raw_scenario.get("correct_fix_description", "Fix service"),
misleading_signals=misleading,
cascade_rules=[],
expected_diagnostic_path=["systemctl status", f"cat /var/log/{target}/error.log"],
task_id="adversarial",
)
except Exception as e:
import logging
logging.getLogger(__name__).warning(f"Adversarial LLM failed: {e}. Falling back to dynamic.")
# Fallback to programmatic dynamic scenario
return _generate_dynamic_scenario(orchestrator, difficulty_range=(0.4 + (difficulty*0.1), 0.5 + (difficulty*0.1)))
# ── Dynamic Scenario Generator ───────────────────────────────────────────
# Fault types that work ACROSS PROCESSES (no in-process methods needed)
_FAULT_CATALOG = {
"db_lock": {
"services": ["payment"],
"gen_params": lambda: {},
"root_cause_tpl": "RDS connection pool exhausted -- all writes blocked β€” all writes to RDS/Aurora are failing",
"fix_tpl": "Release the RDS connection pool lock or restart the payment service",
},
"db_pool_exhaustion": {
"services": ["payment"],
"gen_params": lambda: {},
"root_cause_tpl": "Database connection pool exhausted β€” no connections available",
"fix_tpl": "Restart payment service to reset the connection pool",
},
"queue_overflow": {
"services": ["worker"],
"gen_params": lambda: {"fill": random.randint(700, 980)},
"root_cause_tpl": "SQS dead letter queue overflowed β€” worker cannot keep up",
"fix_tpl": "Drain queue at controlled rate (queue drain 10)",
},
"queue_pause": {
"services": ["worker"],
"gen_params": lambda: {},
"root_cause_tpl": "Queue consumer paused β€” messages accumulating",
"fix_tpl": "Resume queue consumer and check worker health",
},
"process_crash": {
"services": [
"payment", "auth", "worker", "cache", "notification",
"search", "gateway", "scheduler", "storage", "metrics_collector",
"email", "billing", "config", "dns", "loadbalancer",
],
"gen_params": lambda: {
"target": random.choice([
"payment", "auth", "worker", "cache", "notification",
"search", "gateway", "scheduler", "storage", "metrics_collector",
"email", "billing", "config", "dns", "loadbalancer",
]),
"reason": random.choice([
"SIGSEGV", "SIGKILL", "OOM", "SIGABRT",
"unhandled exception", "stack overflow",
]),
},
"root_cause_tpl": "Service instance terminated ({reason}) β€” needs restart",
"fix_tpl": "Restart the crashed service via systemctl restart",
},
"cache_invalidation": {
"services": ["cache"],
"gen_params": lambda: {},
"root_cause_tpl": "Cache invalidated β€” all entries expired, 0% hit ratio",
"fix_tpl": "Warm the cache or restart cache service",
},
"webhook_storm": {
"services": ["notification"],
"gen_params": lambda: {"count": random.randint(100, 400)},
"root_cause_tpl": "Webhook retry storm β€” {count} webhooks firing simultaneously",
"fix_tpl": "Drain webhook queue at controlled rate, pause delivery if needed",
},
"latency_injection": {
"services": [
"payment", "auth", "worker", "cache", "notification",
"search", "gateway", "scheduler", "storage", "metrics_collector",
"email", "billing", "config", "dns", "loadbalancer",
],
"gen_params": lambda: {
"target": random.choice([
"payment", "auth", "worker", "cache", "notification",
"search", "gateway", "scheduler", "storage", "metrics_collector",
"email", "billing", "config", "dns", "loadbalancer",
]),
"latency_ms": random.choice([2000, 3000, 5000, 8000, 15000]),
},
"root_cause_tpl": "Network latency spike β€” p95 latency {latency_ms}ms (baseline: 40ms)",
"fix_tpl": "Restart service to clear network degradation, check upstream dependencies",
},
# ── New service-specific fault types (10 new services) ──
"index_corruption": {
"services": ["search"],
"gen_params": lambda: {},
"root_cause_tpl": "Search index corrupted β€” queries returning empty or incorrect results",
"fix_tpl": "Restart search service to rebuild clean index",
},
"index_lag": {
"services": ["search"],
"gen_params": lambda: {},
"root_cause_tpl": "Search index lagging by >1000 documents β€” stale query results",
"fix_tpl": "Restart search service to clear indexing backlog",
},
"rate_limit_zero": {
"services": ["gateway"],
"gen_params": lambda: {},
"root_cause_tpl": "Gateway rate limit misconfigured to 0 RPS β€” all traffic rejected",
"fix_tpl": "Restart gateway service to restore default rate limits",
},
"circuit_breaker_stuck": {
"services": ["gateway"],
"gen_params": lambda: {},
"root_cause_tpl": "Gateway circuit breaker stuck open β€” all upstream calls rejected",
"fix_tpl": "Restart gateway to reset circuit breaker state",
},
"scheduler_stuck": {
"services": ["scheduler"],
"gen_params": lambda: {},
"root_cause_tpl": "Scheduler loop frozen β€” no jobs executing",
"fix_tpl": "Restart scheduler service to resume job execution",
},
"duplicate_execution": {
"services": ["scheduler"],
"gen_params": lambda: {},
"root_cause_tpl": "Scheduler executing jobs multiple times β€” duplicate processing",
"fix_tpl": "Restart scheduler service to clear duplicate execution state",
},
"disk_full": {
"services": ["storage"],
"gen_params": lambda: {},
"root_cause_tpl": "Storage disk full β€” all write operations rejected",
"fix_tpl": "Restart storage service to reclaim space and clear write queue",
},
"data_corruption": {
"services": ["storage"],
"gen_params": lambda: {},
"root_cause_tpl": "Storage data corruption β€” checksum validation failing",
"fix_tpl": "Restart storage service to trigger integrity check and repair",
},
"scrape_failure": {
"services": ["metrics_collector"],
"gen_params": lambda: {},
"root_cause_tpl": "Metrics scrape failures β€” telemetry pipeline stale",
"fix_tpl": "Restart metrics collector to reconnect scrape targets",
},
"retention_full": {
"services": ["metrics_collector"],
"gen_params": lambda: {},
"root_cause_tpl": "Metrics retention store full β€” new datapoints being dropped",
"fix_tpl": "Restart metrics collector to flush and reclaim retention storage",
},
"smtp_down": {
"services": ["email"],
"gen_params": lambda: {},
"root_cause_tpl": "SMTP upstream unavailable β€” email delivery queue backing up",
"fix_tpl": "Restart email service to reconnect SMTP upstream",
},
"email_queue_overflow": {
"services": ["email"],
"gen_params": lambda: {},
"root_cause_tpl": "Email delivery queue overflow β€” messages being dropped",
"fix_tpl": "Restart email service to drain and reset queue",
},
"billing_desync": {
"services": ["billing"],
"gen_params": lambda: {},
"root_cause_tpl": "Billing ledger desync β€” charges recorded but not reconciled",
"fix_tpl": "Restart billing service to force reconciliation",
},
"invoice_stuck": {
"services": ["billing"],
"gen_params": lambda: {},
"root_cause_tpl": "Invoice generation stuck β€” invoices not being produced",
"fix_tpl": "Restart billing service to resume invoice generation",
},
"config_poisoned": {
"services": ["config"],
"gen_params": lambda: {},
"root_cause_tpl": "Config store poisoned β€” critical keys set to unsafe values",
"fix_tpl": "Restart config service to restore safe defaults",
},
"config_locked": {
"services": ["config"],
"gen_params": lambda: {},
"root_cause_tpl": "Config store locked β€” read/write operations blocked",
"fix_tpl": "Restart config service to release lock",
},
"dns_resolution_failure": {
"services": ["dns"],
"gen_params": lambda: {},
"root_cause_tpl": "DNS resolution failure β€” all lookups returning NXDOMAIN",
"fix_tpl": "Restart DNS service to restore resolution",
},
"stale_entries": {
"services": ["dns"],
"gen_params": lambda: {},
"root_cause_tpl": "DNS stale entries β€” services routed to dead endpoints",
"fix_tpl": "Restart DNS service to flush stale cache",
},
"all_backends_removed": {
"services": ["loadbalancer"],
"gen_params": lambda: {},
"root_cause_tpl": "Load balancer has no healthy backends β€” all traffic returning 503",
"fix_tpl": "Restart loadbalancer service to rediscover backends",
},
"session_corruption": {
"services": ["loadbalancer"],
"gen_params": lambda: {},
"root_cause_tpl": "Sticky-session table corrupted β€” users routed inconsistently",
"fix_tpl": "Restart loadbalancer service to rebuild session table",
},
}
# Red herring generators β€” 22 templates for diverse misdirection (original 12 + 10 new services)
_RED_HERRING_TEMPLATES = [
# ── Original 6 services ──
{"service": "auth", "message": "AuthService: JWT validation slow β€” {latency}ms (normally 5ms)"},
{"service": "frontend", "message": "FrontendProxy: upstream pool near capacity ({used}/50)"},
{"service": "worker", "message": "WorkerService: CPU spike to {cpu}% β€” batch backlog"},
{"service": "payment", "message": "PaymentService: response time {latency}ms vs 20ms baseline"},
{"service": "cache", "message": "CacheService: hit ratio dropped to {ratio}% (normally 94%)"},
{"service": "notification", "message": "NotificationService: webhook delivery delayed {latency}ms"},
{"service": "frontend", "message": "FrontendProxy: cache hit ratio dropped to {ratio}%"},
{"service": "auth", "message": "AuthService: token refresh rate 3x normal ({rate}/min)"},
{"service": "worker", "message": "WorkerService: GC pause elevated ({gc_ms}ms)"},
{"service": "payment", "message": "PaymentService: retry rate {retries}/min"},
{"service": "frontend", "message": "FrontendProxy: TLS handshake failures ({tls_errors}/min)"},
{"service": "worker", "message": "WorkerService: disk I/O latency {io_ms}ms (normally 2ms)"},
# ── 10 new services ──
{"service": "search", "message": "SearchService: index lag {latency}ms behind primary β€” stale results possible"},
{"service": "gateway", "message": "GatewayService: circuit breaker half-open β€” {ratio}% of requests rerouted"},
{"service": "scheduler", "message": "SchedulerService: job execution {latency}ms slower than SLO"},
{"service": "storage", "message": "StorageService: disk utilization at {ratio}% β€” approaching threshold"},
{"service": "metrics_collector", "message": "MetricsCollector: scrape latency elevated to {latency}ms (normally 50ms)"},
{"service": "email", "message": "EmailService: delivery latency {latency}ms β€” SMTP upstream slow"},
{"service": "billing", "message": "BillingService: reconciliation queue depth {used} β€” processing slow"},
{"service": "config", "message": "ConfigService: config reload latency {latency}ms (normally 10ms)"},
{"service": "dns", "message": "DNSService: resolution latency {latency}ms β€” cache miss rate {ratio}%"},
{"service": "loadbalancer", "message": "LoadBalancer: backend health check latency {latency}ms β€” {ratio}% backends slow"},
]
# 17 cascade chain templates from real-world P0 incident patterns (original 7 + 10 new services)
_CASCADE_TEMPLATES = [
# ── Original 7 chains ──
{
"trigger_fault": "db_lock",
"cascade_type": "queue_overflow",
"affected": "worker",
"desc": "Queued requests flood worker after DB release β†’ OOM",
"agent_must": "Use 'queue drain 10' instead of 'queue drain all'",
},
{
"trigger_fault": "process_crash",
"cascade_type": "queue_overflow",
"affected": "worker",
"desc": "Crashed service recovery causes burst of queued requests",
"agent_must": "Drain queue at controlled rate after restart",
},
{
"trigger_fault": "queue_overflow",
"cascade_type": "process_crash",
"affected": "worker",
"desc": "Mass drain causes worker memory spike β†’ OOM crash",
"agent_must": "Use controlled drain rate, not drain all",
},
{
"trigger_fault": "db_lock",
"cascade_type": "process_crash",
"affected": "payment",
"desc": "RDS lock release β†’ payment retry storm β†’ payment OOM",
"agent_must": "Monitor payment after RDS lock release",
},
{
"trigger_fault": "process_crash",
"cascade_type": "db_lock",
"affected": "payment",
"desc": "Service restart hammers DB with connections β†’ RDS locks",
"agent_must": "Check DB status after restarting crashed service",
},
{
"trigger_fault": "queue_pause",
"cascade_type": "queue_overflow",
"affected": "worker",
"desc": "Unpausing consumer with deep queue β†’ immediate overflow",
"agent_must": "Drain some messages before unpausing consumer",
},
{
"trigger_fault": "db_pool_exhaustion",
"cascade_type": "process_crash",
"affected": "payment",
"desc": "DB pool reset causes connection storm β†’ payment crashes",
"agent_must": "Restart payment gracefully after pool reset",
},
# ── 10 new service cascade chains ──
{
"trigger_fault": "index_corruption",
"cascade_type": "rate_limit_zero",
"affected": "gateway",
"desc": "Search recovery surge triggers gateway rate limiter β†’ blocks all traffic",
"agent_must": "Restart gateway after stabilizing search",
},
{
"trigger_fault": "circuit_breaker_stuck",
"cascade_type": "scheduler_stuck",
"affected": "scheduler",
"desc": "Gateway blocks callbacks β†’ scheduler job queue stalls",
"agent_must": "Restart scheduler after gateway comes back",
},
{
"trigger_fault": "duplicate_execution",
"cascade_type": "billing_desync",
"affected": "billing",
"desc": "Duplicate job execution β†’ double billing charges β†’ ledger desync",
"agent_must": "Restart billing after fixing scheduler duplicates",
},
{
"trigger_fault": "disk_full",
"cascade_type": "email_queue_overflow",
"affected": "email",
"desc": "Storage full β†’ email attachments fail β†’ email queue backs up",
"agent_must": "Restart email after clearing storage",
},
{
"trigger_fault": "scrape_failure",
"cascade_type": "stale_entries",
"affected": "dns",
"desc": "Metrics blind β†’ missed DNS TTL refresh β†’ stale routing entries",
"agent_must": "Restart DNS after restoring metrics pipeline",
},
{
"trigger_fault": "smtp_down",
"cascade_type": "webhook_storm",
"affected": "notification",
"desc": "Email SMTP down β†’ notifications fall back to webhooks β†’ webhook storm",
"agent_must": "Rate-limit webhooks after restoring email",
},
{
"trigger_fault": "billing_desync",
"cascade_type": "config_poisoned",
"affected": "config",
"desc": "Billing desync triggers auto-config update with bad values",
"agent_must": "Restart config service after fixing billing ledger",
},
{
"trigger_fault": "config_locked",
"cascade_type": "circuit_breaker_stuck",
"affected": "gateway",
"desc": "Config locked β†’ gateway can't read routing config β†’ circuit opens",
"agent_must": "Restart gateway after releasing config lock",
},
{
"trigger_fault": "dns_resolution_failure",
"cascade_type": "all_backends_removed",
"affected": "loadbalancer",
"desc": "DNS failure β†’ LB can't resolve backends β†’ removes all from pool",
"agent_must": "Restart loadbalancer after fixing DNS",
},
{
"trigger_fault": "session_corruption",
"cascade_type": "process_crash",
"affected": "auth",
"desc": "LB session corruption β†’ auth hammered with re-auth requests β†’ OOM",
"agent_must": "Restart auth after fixing loadbalancer sessions",
},
]
_USER_REPORTS = [
"Cannot checkout", "Payment keeps failing", "Site is completely down",
"Can't log in", "Orders stuck in processing", "Nothing works",
"Getting 502 errors", "Auth errors on every page", "Payments failing randomly",
"All pages broken", "Logged out everywhere", "Extremely slow",
"Payments disappearing", "No confirmation emails", "Cart won't save",
"Stuck on loading screen", "Getting timeout errors", "Been broken for hours",
]
_TIMES = [f"{h:02d}:{m:02d}" for h in range(24) for m in (0, 15, 30, 45)]
_SEVERITIES = ["P0", "P1", "P2"]
_DURATIONS = [5, 10, 15, 20, 30, 45, 60, 90]
def _generate_dynamic_scenario(
orchestrator,
difficulty_range: tuple = (0.3, 0.9),
force_cascade: bool = False,
) -> ScenarioSpec:
"""Generate a unique scenario by combining random elements.
Produces 28 fault types x 16 services x N_params x 22 herrings x 17 cascades
= thousands of unique scenarios. The model CANNOT memorize solutions.
"""
fault_type = random.choice(list(_FAULT_CATALOG.keys()))
catalog = _FAULT_CATALOG[fault_type]
target = random.choice(catalog["services"])
params = catalog["gen_params"]()
params.setdefault("target", target)
format_args = {
k: v for k, v in params.items()
if isinstance(v, (str, int, float)) and k != "target"
}
root_cause = catalog["root_cause_tpl"].format(target=target, **format_args)
difficulty = round(random.uniform(*difficulty_range), 2)
num_herrings = random.randint(0, 1) if difficulty < 0.5 else random.randint(1, 3)
available = [h for h in _RED_HERRING_TEMPLATES if h["service"] != target]
chosen_herrings = random.sample(available, min(num_herrings, len(available)))
misleading_signals = {}
for h in chosen_herrings:
msg = h["message"].format(
latency=random.randint(80, 2500),
used=random.randint(40, 49),
cpu=random.randint(75, 95),
ratio=random.randint(50, 75),
rate=random.randint(100, 500),
gc_ms=random.randint(200, 800),
retries=random.randint(30, 150),
tls_errors=random.randint(10, 50),
io_ms=random.randint(50, 500),
)
misleading_signals[h["service"]] = msg
cascade_rules = []
applicable = [c for c in _CASCADE_TEMPLATES if c["trigger_fault"] == fault_type]
if (force_cascade or difficulty > 0.5) and applicable:
cd = random.choice(applicable)
cascade_rules.append(CascadeRule(
trigger_condition=f"{fault_type} fixed β†’ {cd['cascade_type']}",
cascade_type=cd["cascade_type"],
affected_service=cd["affected"],
description=cd["desc"],
agent_must=cd["agent_must"],
))
primary_symptom = f" - {target.capitalize()}: "
symptom_map = {
# Original faults
"db_lock": "HTTP 503 on all requests (database errors)",
"queue_overflow": f"Queue depth critical ({params.get('fill', 900)}/1000)",
"process_crash": f"Process crashed ({params.get('reason', 'SIGSEGV')})",
"db_pool_exhaustion": "DB connection pool exhausted β€” all queries failing",
"queue_pause": "Queue consumer paused β€” messages accumulating",
"cache_invalidation": "Cache fully invalidated β€” 0% hit ratio",
"webhook_storm": "Webhook retry storm β€” hundreds of retries per second",
"latency_injection": f"P95 latency spike to {params.get('latency_ms', 5000)}ms",
# New service faults
"index_corruption": "Search index corrupted β€” queries returning empty results",
"index_lag": "Search index lagging >1000 docs β€” stale results",
"rate_limit_zero": "Rate limit set to 0 RPS β€” all traffic rejected",
"circuit_breaker_stuck": "Circuit breaker stuck open β€” upstream calls blocked",
"scheduler_stuck": "Scheduler loop frozen β€” jobs not executing",
"duplicate_execution": "Jobs executing multiple times β€” duplicate processing",
"disk_full": "Disk usage 100% β€” write operations rejected",
"data_corruption": "Data corruption detected β€” checksum failures",
"scrape_failure": "Metrics scrape failing β€” telemetry pipeline stale",
"retention_full": "Metrics retention full β€” new datapoints dropped",
"smtp_down": "SMTP upstream unreachable β€” email delivery failing",
"email_queue_overflow": "Email queue overflow β€” messages being dropped",
"billing_desync": "Billing ledger desync β€” charges not reconciled",
"invoice_stuck": "Invoice generation stuck β€” no invoices produced",
"config_poisoned": "Config store poisoned β€” unsafe values in critical keys",
"config_locked": "Config store locked β€” read/write blocked",
"dns_resolution_failure": "DNS resolution failing β€” NXDOMAIN on all lookups",
"stale_entries": "DNS stale entries β€” routing to dead endpoints",
"all_backends_removed": "No healthy backends β€” all traffic returning 503",
"session_corruption": "Sticky-session table corrupted β€” inconsistent routing",
}
primary_symptom += symptom_map.get(fault_type, "Service degraded")
secondary = [f" - {s.capitalize()}: {m}" for s, m in misleading_signals.items()]
secondary_str = "\n".join(secondary) if secondary else " (no other issues)"
sev = "P0" if difficulty > 0.6 else random.choice(["P1", "P2"])
alert = (
f"🚨 INCIDENT β€” {random.choice(_TIMES)} UTC | Severity: {sev}\n"
f"Multiple services showing errors:\n"
f"{primary_symptom}\n"
f"{secondary_str}\n"
f"Duration: {random.choice(_DURATIONS)} minutes and escalating\n"
f"User reports: '{random.choice(_USER_REPORTS)}'"
)
scenario_id = f"dynamic_{fault_type}_{target}_{random.randint(1000, 9999)}"
port_map = {
"payment": 8001, "auth": 8002, "worker": 8003, "frontend": 8004,
"cache": 8005, "notification": 8006, "search": 8007, "gateway": 8008,
"scheduler": 8009, "storage": 8010, "metrics_collector": 8011,
"email": 8012, "billing": 8013, "config": 8014, "dns": 8015,
"loadbalancer": 8016,
}
return ScenarioSpec(
scenario_id=scenario_id,
failure_type=fault_type,
target_service=target,
params=params,
difficulty=difficulty,
alert_message=alert,
root_cause=root_cause,
correct_fix_description=catalog["fix_tpl"],
misleading_signals=misleading_signals,
cascade_rules=cascade_rules,
expected_diagnostic_path=[
"systemctl status",
f"curl http://localhost:{port_map.get(target, 8001)}/healthz",
f"cat /var/log/{target}/error.log",
],
task_id="adversarial",
)
# ── Task Configuration ────────────────────────────────────────────────────
TASK_CONFIGS = {
"warmup": {
"tier": 1,
"max_steps": 10,
"scenario_picker": _warmup_scenarios,
"description": "Single clear failure, no red herrings",
},
"single_fault": {
"tier": 2,
"max_steps": 15,
"scenario_picker": _single_fault_scenarios,
"description": "Single fault with misleading signals",
},
"cascade": {
"tier": 3,
"max_steps": 20,
"scenario_picker": _cascade_scenarios,
"description": "Single fault that triggers cascade after fix",
},
"multi_cascade": {
"tier": 4,
"max_steps": 25,
"scenario_picker": _multi_cascade_scenarios,
"description": "Multiple cascading failures",
},
"adversarial": {
"tier": 5,
"max_steps": 30,
"scenario_picker": _adversarial_scenarios,
"description": "Dynamic adversarial β€” unique every episode",
},
}