An alarm is a state a device is asserting. An incident is a unit of work a person owns. They are linked, and they must never be coupled — because machine state flaps, and human work must not.
This is the whole design in one picture. Each column is its own state machine with its own owner. The arrows between them are the only couplings that exist — and three of the four deliberately do less than you might expect.
Four gates, in this order. Any one of them can end the journey — and when it does, the alarm carries on exactly as it always did. Nothing about alarm handling depends on this layer succeeding.
alarm_def_id + input_id, else a station, else a camera. If none of
those are present the event is malformed, and an event you cannot name the source of is not
an incident. This gate was added after a test run produced twenty-seven incidents titled
"ALARM" with nothing to correlate against.These are the situations where an alarm exists and no new incident does. Five are by design; one is a safety net.
| Case | What happens | Why it is right |
|---|---|---|
| Below every priority floor by policy |
No policy matches, so no incident opens. The alarm still queues, still sounds, still acknowledges. | A door-held-open or a line fault is a maintenance signal, not a unit of investigative work. Give it a queue row and a report, not an owner and an SLA. |
| Repeat while one is open deduplicated |
Attaches to the open incident as "Repeat activation" and marks the source active again. | One event in the world is one incident. A detector cycling twelve times is still one thing happening. |
| No identifiable source malformed |
No correlation key, so nothing is created at all. | An incident with no source cannot be correlated, shown on a map, or acted on. It is noise wearing a work item's clothes. |
| Policy disabled by policy |
Skipped during matching. | Lets a site run alarm-only during commissioning, then switch incidents on when the procedures are agreed. |
| Clear or acknowledge with no open incident no-op |
Returns nothing; no incident is retro-created. | An acknowledgement is not a reason to start tracking work that was never tracked. |
| Incident layer failed safety net |
Exception is swallowed and logged; alarm handling is untouched. | The layer is beside the alarm path, never in front of it. It degrades to nothing. |
The correlation key supports three source kinds, so an incident can open from a
call (call:<station>) or a camera detection
(camera:<id>) as readily as from an alarm point. The policy table keys on
event type, so the same machinery covers all three without special cases.
These are the live policy rows on your server right now, in evaluation order.
| Order | Policy | Priority floor | Severity | State |
|---|---|---|---|---|
10 | Life-safety only | ≥ 50 | Critical | Enabled |
20 | Security and life-safety | ≥ 42 | High | Enabled |
30 | Named alarm types (explicit) | — | High | Disabled |
30 | All other alarms | none | Medium | Enabled |
Three questions come up immediately, and they have different answers: one is yes and configured, one is yes but has no screen yet, and one is genuinely not built.
Yes — on your system today, every alarm with an identifiable source becomes an incident. Not because the design says so, but because the catch-all policy All other alarms is enabled with no priority floor, so it matches everything the two narrower policies did not. Repeats still attach rather than duplicate, and malformed events still create nothing — but no alarm type is currently excluded on purpose.
Yes. That is exactly what incident_policy is for, and it takes
two independent kinds of rule which can be combined:
| Control | Field | What it expresses |
|---|---|---|
| Name the alarm types | match_filter |
A JSON filter on the event's own fields, e.g.
{"alarm_def_id": [11, 13]} — only these alarm types open incidents.
A list is OR within the key; several keys are AND across them. |
| Set an importance floor | min_priority |
"Anything at least this important", without naming types — so a new alarm type inherits the right behaviour the day it is created, instead of being forgotten. |
| Order the rules | sort_order |
First match wins, so a narrow life-safety policy can sit in front of a broad one without ambiguity. |
| Turn one off | enabled |
Disabled policies are skipped. Disabling the catch-all is the single switch that turns "everything is an incident" into "only what I named". |
| Set the severity | severity |
The matched policy supplies the incident's opening severity, so importance is configuration rather than something derived in code — and an operator who has now looked at it can raise or lower it, which the timeline records by name. |
An alarm that matches no policy is still a fully working alarm. It queues, sounds, shows on the wall, acknowledges at the exchange, fires rules and lands in Event History. It simply does not become a unit of owned work.
GET/POST /api/admin/incident-policies — and the
table is read on every event, so changes apply with no restart. But there is no page under
Settings that edits them, so severity tuning is still done through the API or the database.
Two properties make it safe to offer. It deduplicates against automatic creation: the incident is built with the same correlation key the engine would have used, so if a policy has already opened one for that point the operator is handed that incident rather than a second one — one event in the world stays one incident however it came to exist. And the timeline records that a person chose it, by name, because an incident that exists because someone overrode the configuration is the most interesting kind, and reading it back later must not look identical to one a policy opened.
Permission is Incident/create, granted to exactly the roles that can already take
ownership of an incident: if you may own one, you may raise one. That mirroring is deliberate —
it stays true for custom roles a site invents later, which a hardcoded role list would miss.
The container is only worth having if the work happens inside it. Everything below is one screen: where it is, what the camera saw, what the system did, and what has been done about it — so nobody has to hold an incident together across four tabs.
| On the screen | Why it is there |
|---|---|
| A progress strip across the six states | The status pill only ever showed the current one, so "how far along is this" meant knowing the order by heart. Done, current and still-to-come, at a glance. |
| The plan, filling its pane, centred on the alarm | Letterboxing a site plan into a small pane spent most of it on background and left the marker a few pixels across, on the one panel whose job is to say where. |
| The camera bound to that point, already open | Nobody should have to find it in a tree while an alarm is standing. |
| Thirty seconds before the alarm | The useful question is what happened in the run-up, and the live view cannot answer it. One button replays the recorded video with the wall-clock time on the picture and a marker at the exact moment the alarm fired. |
| The alarm type, in words | A point id says which detector. "Fire Alarm" says what you are responding to. |
| Severity as a control, not a label | The policy's severity is a default set before anybody looked. The operator who has now looked can change it, and the timeline records who and from what. |
| A face against every operator action | A timeline is read to find out who did what. Attribution as a grey name at the end of a sentence is the last thing the eye reaches. |
| What FusionX did, truthfully | Requested, sent, confirmed, failed, timed out — as separate words, so an announcement that was sent but never confirmed says exactly that. |
The alarm/incident split is not a FusionX invention. It is the settled pattern across PSIM platforms, alarm receiving centres and IT service management alike, and it exists because every one of those fields learned the same lesson: the volume of things that happen is far larger than the volume of things a person can own.
| Field | Where the line sits | What FusionX shares with it |
|---|---|---|
| PSIM Genetec, Milestone, SureView, Advancis, CNL |
Alarm from a device; an operator "action plan" or procedure is the work item. Plans are per-situation, not per-device, with ordered steps and per-step audit. | The event → filter → ordered steps → audit structure, and the deliberate decision not to copy branching or operator-ticked checklists. |
| Alarm receiving centres EN 50518 and similar |
Signal arrives; the centre logs a handling record with response times and a documented outcome. The signal and the handling are separate records by regulation. | Response-time measurement from the ring, an append-only handling record, and attribution on every state change. |
| Process alarm management ISA-18.2, EEMUA 191 |
The famous rule: if it does not require operator action, it is not an alarm. Rationalisation demotes the rest to events, targeting roughly one alarm per ten minutes per operator. | Exactly the argument for putting a priority floor on the catch-all policy — the discipline is the same one level up. |
| IT service management ITIL, ServiceNow, PagerDuty |
Event management is separate from incident management. Monitoring raises alerts; only some become incidents, which have an owner, a state machine, an SLA and a post-incident review. | The state machine with attribution, deduplication on a correlation key, and the rule that automated recovery does not close a human's ticket. |
| Mechanism | Problem it solves | In FusionX |
|---|---|---|
| Correlation / dedupe key | One real-world event producing many signals | Yes — unique index on the open key, enforced under race |
| Chatter suppression | A flapping detector flooding the work queue | Yes — repeats attach as timeline entries |
| Policy-driven severity | Hardcoded importance that no site can tune | Yes — ordered policy table, filters and priority floors |
| Acknowledge ≠ resolve | "Seen" being mistaken for "handled" | Yes — separate states, separately attributed |
| No auto-close on clear | Machine recovery silently closing human work | Yes — explicit, with a timeline note when it happens unattended |
| Many alarms → one incident | A fire panel producing forty points for one fire | Partial — same source correlates; grouping different sources into one incident is manual |
| Escalation timers / SLA | Work sitting unowned overnight | Partial — unacknowledged counters and an unwatched-queue trigger, but no per-incident SLA clock |
| Procedure attached to the work | Operators improvising under pressure | Adjacent — rules automate the steps; there is no operator-ticked checklist by design |
| Post-incident review | Learning nothing from last month | Yes — append-only timeline plus the truthful action ladder |