AI Capital Cycle β composite entered HIGH
Last verified
Auto-generated. This article is rebuilt from
app/signals/config/alert_thresholds.jsonbyscripts/build_alerts_kb.py. Edit the alert config and re-run the script β do not edit this file directly.
AI Capital Cycle β composite entered HIGH
Alert ID: AI_CYCLE_STATE_HIGH
Category: capital_cycle
Cross-detection: no
NOTIFICATION-ONLY. The 25-wire AI capital-cycle trigger composite (app/signals/ai_capital_cycle.py) crossed INTO the high state β i.e. a FIRED wire sits at position D3-D8 in the converged demand sequence, the watch docβs βevent modeβ. Edge-triggered day-over-day against the most recent strictly-prior daily_signals.ai_cycle_state, so a steady high emits ONE alert; a drop back out of high emits a single lower-severity recovery message via recovery_message_template / recovery_severity. Wired 2026-08-10 on operator ask, invoking improvements/ai-capital-cycle.md Architecture Decision 5βs own revisit clause; the forward-grade prereg (research/planning/prereg_ai_cycle_forward_grade_2026-07-18.md) still gates every PREDICTIVE-INPUT role β the composite stays data_only, never scored, never a lens, never a sizing or conviction input. Fired per-transition by app/signals/ai_cycle_alerts.py from the capture cycle, NOT by the macro alert engine: evaluate_alerts runs at capture step 2 while the composite is computed hundreds of lines later, and ai_cycle_state never reaches source_raw, so the condition below is declared computed with a computation name _eval_computed does not recognise (it returns False every cycle and can never double-fire). The 1440-minute cooldown is load-bearing rather than decorative β the prior-row baseline does not move until tomorrow, so it is what collapses a day of re-observed edges into one alert and absorbs repeated recoveries on a flapping day. enabled and cooldown_minutes here are the SEED: both resolve at runtime from the alerts_config row, so PATCH /api/v1/admin/alerts/{config_id} kills or retunes this alert with no deploy. The message templates and severities stay JSON-only (alerts_config has one column of each and this alert ships two). When the recovery fires it resolves the still-open entry row so /alerts/active never shows both as active.
Severity
CRITICAL β immediate attention; extreme stress or crash setup
Trigger condition
{
"type": "computed",
"computation": "ai_cycle_state_high"
}
Marker, not a rule the macro alert engine evaluates. app/signals/alerts.py:_eval_computed does not recognise ai_cycle_state_high, so the per-cycle evaluate_alerts loop returns False for it every cycle and this alert can never fire from the macro path.
The real trigger lives in app/signals/ai_cycle_alerts.py (emit_ai_cycle_alerts), which writes the alert row into the alerts table directly. It is fired from the capture cycle β emit_ai_cycle_alerts runs inside capture_signals_after_report once the trigger composite has been merged into the signals dict, and BEFORE _persist_daily_row writes todayβs row (the transition is read against the most recent strictly-prior row). The definition stays in alert_thresholds.json so severity, message template, cooldown, the alerts_config admin surface, and this catalog all read from one place.
Message template
AI capital cycle: the trigger composite entered HIGH (was {prev_state}) β {score} trigger(s) fired, {amber_count} amber. Event-mode trigger: {event_wire}. Fired: {fired_summary}. State descriptor from the mechanical trigger composite β NOT a validated predictor. Operator risk-policy review is the intended response.
Rendered by app/signals/ai_cycle_alerts.py via Python str.format over the per-transition context it builds for the fire, NOT the macro signals dict. A placeholder that fails to resolve logs a warning and falls back to a plainly-built message that carries the descriptor-not-predictor framing verbatim β a template typo must never suppress the notification, nor strip the honesty line the operatorβs override was granted under.
Recovery message
This alert ships a PAIRED message: the fire above when the state it reports begins, and one lower-severity message when that state goes away.
Recovery severity: WARNING β conditions deteriorating; worth monitoring
AI capital cycle: the trigger composite left HIGH β now {state} ({score} trigger(s) fired, {amber_count} amber). Fired: {fired_summary}. State descriptor from the mechanical trigger composite β NOT a validated predictor. Operator risk-policy review is the intended response.
Cooldown / dedup
- Cooldown: 1440 minutes (~24.0 h) per dedup key, measured from the last fire. A repeat inside the window is suppressed entirely β no row is inserted and no webhook is dispatched.
- Dedup: per
(alert_id, transition)β entry and recovery carry INDEPENDENT cooldown scopes (ENTRY:/RECOVERY:idempotency_keyprefixes), so a genuine drop out of the state is never suppressed by the entry that preceded it. The cooldown is load-bearing rather than decorative: the day-over-day baseline does not move until the next session, so every capture cycle of the entry day re-observes the SAME edge and the window is what collapses them into one alert.
Notifies
- Active alert feed (
/api/v1/alerts/active,/alerts/summary). - SSE stream (
/api/v1/agents/stream) for real-time consumers. alert_firedwebhook on every genuinely-new insert (critical-tier).alert_escalatedwebhook alongside it, because the severity is critical β operator routing rules keyed on that event see this alert.- The recovery message dispatches its own
alert_firedat its own warning severity. It also marks the still-open fire rowresolvedin thealertstable so/alerts/activenever shows the pair as two live, self-contradicting rows. Since the macro engine ignores this alert entirely, that UPDATE is the ONLY thing that ever resolves the fire row β a table write owned by the module, not analert_resolvedwebhook. - A duplicate suppressed by the cooldown dispatches nothing.
- The macro alert engine ignores this marker computation entirely β it neither fires, resolves, nor dispatches webhooks for this alert. The full lifecycle (fire, resolve, webhook) belongs to the owning module.
See also
- No simple field references β see
app/signals/ai_cycle_alerts.py(emit_ai_cycle_alerts) for the firing logic. - Alert reference
- Live alerts feed