Warning, /swf-monitor/docs/ALARM_QUEUE.md is written in an unsupported language. File is not indexed.
0001 # Alarm Queue
0002
0003 The alarm system is the operations queue for ePIC production: the set of
0004 raised conditions that need a response, each with a lifecycle from raise to
0005 clear, a per-subscriber feed with read state for the people and agents who
0006 follow it, and an automated responder that acts on selected alarm classes.
0007 This document specifies those three extensions of the alarm system described
0008 in [alarms.md](alarms.md). They build on the action stream
0009 ([ACTION_STREAM.md](ACTION_STREAM.md)) and notice routing
0010 ([NOTICE_ROUTING.md](NOTICE_ROUTING.md)).
0011
0012 ## Roles of the three systems
0013
0014 - **The alarm system** holds the queue. A raised condition is an alarm event;
0015 the event carries the condition's lifecycle and its response record. The
0016 dashboard at `/alarms/` is the shared view of every raised condition.
0017 - **The action stream** is evidence and record. Detect modules read it (with
0018 PanDA and Rucio) to decide whether a condition is raised, and every
0019 lifecycle transition and every response action is logged on it as an
0020 incident, so Mattermost channels, external feeds, and reports receive alarm
0021 incidents by subscription.
0022 - **Notice routing** delivers. The responder is a push plugin; announcements
0023 reach channels through subscriptions.
0024
0025 Admission to the queue is a detect module with a threshold, in code, in git.
0026 A condition enters the queue only by being detected. The responder never
0027 creates queue items.
0028
0029 ## Lifecycle
0030
0031 An alarm event is one condition on one subject (`dedupe_key`). Its lifecycle
0032 is a sequence of transitions, recorded in an `AlarmTransition` table (event,
0033 kind, time, actor, detail). The engine originates the detection transitions;
0034 the web tier, REST actions, and the responder originate the rest. Every
0035 transition is recorded through one endpoint (Data), which also logs it as an
0036 incident, so a transition and its incident cannot diverge.
0037
0038 | Transition | Written by | Meaning |
0039 |---|---|---|
0040 | `raised` | engine | condition detected; the event is created |
0041 | `renotified` | engine | condition persists past the renotify interval |
0042 | `acknowledged` | person or agent | someone has taken the item |
0043 | `assigned` | person | the item is handed to a named subscriber |
0044 | `response_proposed` | responder | a diagnosis and proposed action are attached |
0045 | `response_taken` | ops agent, via the responder or a person | the action was executed |
0046 | `verified` | responder | the action's effect was checked |
0047 | `escalated` | engine | raised and unacknowledged past the escalation interval |
0048 | `cleared` | engine | condition no longer detected |
0049
0050 The event row keeps its current state as a summary of the latest transitions
0051 (`acknowledged_by`, `assigned_to`, `escalated_at`, `clear_time`), and the
0052 transitions are its history. A `renotified` transition is written when the
0053 renotify interval has elapsed since the later of `raised` and the previous
0054 `renotified`, independently of whether email is enabled or sent; the email
0055 bundle keeps its own `last_notified` stamp. Acknowledgement and assignment are
0056 actions on the dashboard and through REST; the response transitions carry a
0057 response record: the diagnosis, the proposed action, who approved it, what was
0058 done, and what the verification found.
0059
0060 Every transition is also logged on the action stream as an incident
0061 (`alarm_raised`, `alarm_acknowledged`, `alarm_response_taken`, `alarm_cleared`,
0062 and so on) with the alarm name, the subject, the severity, and the dashboard
0063 URL as attributes. The engine does not boot Django; it posts its transitions
0064 to the endpoint, which records them and logs the incidents. Email
0065 notification is unchanged.
0066
0067 ## Feeds
0068
0069 A feed is a subscriber's view of the queue with read state. A subscriber is a
0070 person or an agent, identified by account. Everyone sees every raised
0071 condition by default; a subscription filter is available and is used for
0072 agents, where it is an authorization boundary (a responder subscribes to the
0073 alarm classes it may act on and sees nothing else).
0074
0075 Read state is one row per (subscriber, event) holding the id of the last
0076 transition that subscriber has read. The event is unread for a subscriber
0077 when its latest transition is newer than that pointer, so every transition
0078 (a renotify, a response, a clear) makes the item unread again and moves it
0079 to the top of the feed, ordered by latest transition. The event's history is
0080 the transition list, expandable in place. The unread count is a count over
0081 the join.
0082
0083 - **People** use a feed page in swf-monitor: reverse-time by latest
0084 transition, unread rendering, mark-read and mark-unread per item, mark-all,
0085 unread-only and severity filters carried in the URL, and the same
0086 keyboard navigation as the Logs page. Marking an item read updates only
0087 that subscriber's pointer.
0088 - **Agents** use two MCP tools on their own service account: list unread
0089 (never marks anything) and mark read. For an agent, unread is its work
0090 queue: it lists, handles, and marks read when handled; an item it did not
0091 mark stays unread and is handled again on the next pass.
0092
0093 Read state is private to the subscriber. The dashboard shows the shared state
0094 of the queue and no read state.
0095
0096 ## Response
0097
0098 The responder is an LLM operation dispatched for the alarm classes it
0099 subscribes to: on `raised`, and on `renotified` when the event carries no
0100 response transition yet, so a dispatch lost to an outage is retried at the
0101 next renotify. It runs on the wrangler agent
0102 ([WRANGLER_AGENT.md](WRANGLER_AGENT.md)): the notice-routing push plugin
0103 `alarm-responder` enqueues one worker per matching incident, keyed on the
0104 event, and the agent's `alarm_respond` handler runs the doer with the
0105 swf-monitor MCP tools. The responder never holds a production credential; a
0106 privileged action is a request to the production ops agent through the
0107 task-operation queue, which executes it, verifies the result, and records the
0108 verified outcome as it does for any operator request.
0109
0110 The responder has three modes, set per alarm class in SysConfig and visible
0111 on the dashboard:
0112
0113 | Mode | Behaviour |
0114 |---|---|
0115 | `listen` | the responder is not dispatched |
0116 | `advise` | the responder diagnoses and attaches `response_proposed` (diagnosis, proposed action, reasoning); a person approves on the dashboard or through the bot, and the approval executes the action |
0117 | `act` | for reversible actions only: the responder requests the action, waits for the ops agent's verified outcome, attaches `response_taken` and `verified`, and the transitions notify subscribers after the fact |
0118
0119 Reversible means the action has an inverse and a false positive costs delay
0120 rather than data: pausing tasks (resume is the inverse) is the first such
0121 action. Kill, retry, and anything touching data are never taken in `act`
0122 mode.
0123
0124 Guardrails apply in every mode: the tool surface exposed to the responder is
0125 the authorization boundary (pause and resume, nothing else, in the first
0126 version); a per-hour cap on responder-initiated actions per alarm class; every
0127 proposal, approval, action, and verification is a transition and an
0128 action-stream incident; and the dispatch is skipped when the event already
0129 carries a response transition, so a renotify does not restart the analysis.
0130
0131 ## Escalation
0132
0133 An event raised and unacknowledged past the alarm's escalation interval gets
0134 an `escalated` transition, which re-flags every subscriber and is an incident
0135 subscribable by a Mattermost channel or an email digest. The interval is a
0136 per-alarm parameter alongside the existing renotify interval.
0137
0138 ## Detect modules
0139
0140 The queue is only as useful as its detect modules. The first three:
0141
0142 - **Black hole** — a site or queue on which jobs fail at a high rate with
0143 short wall time over a window; the subject is the site, and the response is
0144 to pause the tasks routed to it.
0145 - **Catalog-sync freshness** — the nightly `catalog_sync` summary record
0146 missing or old on the action stream.
0147 - **Payload-fetch rate** — `fetch_payload_log` error rate over a window on
0148 the action stream.
0149
0150 Each is one `detect(client, params)` module and one alarm entry, per
0151 [alarms.md](alarms.md).
0152
0153 ## Data
0154
0155 - `AlarmTransition` — event, kind, time, actor, detail (JSON). Written only
0156 by the web tier: a token-authenticated endpoint, `/api/alarms/transitions/`,
0157 records a transition and logs its action-stream incident in one request.
0158 The engine posts its detection transitions to it; the dashboard, REST
0159 actions, and the responder write through the same path.
0160 - `AlarmFeedState` — subscriber, event, last read transition. Unique per
0161 (subscriber, event).
0162 - Alarm entry parameters gain `escalation_interval` and `responder_mode`.
0163 - SysConfig gains the responder's per-class caps.
0164
0165 ## Delivery sequence
0166
0167 1. Transitions table and detection transitions from the engine; every
0168 transition logged as an action-stream incident; acknowledgement and
0169 assignment on the dashboard. Existing alarms and email unchanged.
0170 2. Feed state, the feed page, and the two MCP tools.
0171 3. The black-hole detect module.
0172 4. The responder in `advise` mode on the black-hole class, with the
0173 dashboard approval path executing through the ops agent.
0174 5. `act` mode for pause, after the advise record shows the proposals are
0175 what an operator would have done.
0176
0177 ## Status
0178
0179 Design. Nothing in this document is implemented.