Stale Judgment-Surface Input
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.
Stale Judgment-Surface Input
Alert ID: STALE_INPUT
Category: integrity
Cross-detection: no
A metric feeding the drawdown-risk lens or the health score has gone STALE per the platformβs own freshness machinery (routes._compute_freshness β staleness_thresholds.json max-age + the per-metric as-of bindings). The monitored set is DERIVED, never hand-listed: the drawdown lensβs own feature map plus every registry field carrying a health_score_component_name. Fired per-metric by the capture-cycle integrity guard (app/signals/integrity_guard.py), NOT by the macro alert engine. Before this alert existed, staleness was a display badge only β a metric could render a red STALE pill for 46 days and nobody was paged.
Severity
WARNING β conditions deteriorating; worth monitoring
Trigger condition
{
"type": "computed",
"computation": "integrity_stale_input"
}
Marker, not a rule the macro alert engine evaluates. app/signals/alerts.py:_eval_computed does not recognise integrity_stale_input, 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/integrity_guard.py (run_integrity_checks), which writes the alert row into the alerts table directly. It is fired from the capture cycle β run_integrity_checks runs inside capture_signals_after_report, right after the pack-size guard. 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
Data integrity: {metric} is STALE (~{age_hours}h old vs an expected max of ~{max_hours}h) and it feeds the drawdown-risk lens and/or the health score β those surfaces are running on stale input.
Rendered by app/signals/integrity_guard.py via Python str.format over the per-metric 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 β a template typo must never suppress an integrity alert.
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, metric)β that pair is theidempotency_keyprefix, so two frozen/stale metrics page independently while the same metric inside the cooldown stays quiet. The macro engineβs one-active-row-per-alert_idrule does not apply.
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 (warning-tier).- 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/integrity_guard.py(run_integrity_checks) for the firing logic. - Alert reference
- Live alerts feed