# Occurrences

> The durable timeline of every occurrence edge, observed by a run's judge or by the outside-run Always watcher, with the frozen condition behind each one.

The Occurrences tab of [Events](events) (`/events?view=occurrences`) is the durable timeline of what
the things under test did: one row per **occurrence edge**, whether a run's judge observed it or the
outside-run **Always** watcher did. Seeing it takes **View events**; exporting takes
**Export/import histories**. Occurrences are defined per model under
[Process > Setup > Occurrences](process-occurrences); this tab reads their history and changes
nothing.

It follows the shared conduct of the three timelines on
[Events](events#how-the-three-timelines-behave): 500 rows a page with a pager under them, a date
range, a debounced search, live refresh, one detail panel, CSV and JSON export of the whole period.

## What one row is {#what-one-row-is}

An occurrence is a Boolean condition over a model's channels with an on-delay and an off-delay. A
row is one of four transitions of one **observation session**:

| Transition | Meaning |
| --- | --- |
| Activated | The condition rose and held for the on-delay. |
| Cleared | The condition fell and held for the off-delay. |
| Unavailable | One or more channels the condition references stopped delivering a value. The judge holds its last confirmed condition and never fabricates a clear. |
| Restored | Every referenced channel delivers again; the condition is evaluated afresh and its delay restarts. |

Each row also says in which **scope** it was observed and why its session began:

| Field | Values | Meaning |
| --- | --- | --- |
| Scope | **Run**, **Outside run** | Run: a run's own judge, armed by the procedure; the edge is also written into that run's file. Outside run: the Always watcher that reads the unit's bindings between runs. |
| Reason | **Run started**, **Run resumed**, **Watcher started**, **Outside-run observation resumed**, **Definition changed** | Why a fresh observation session began: a run started; a run was resumed after the app restarted; the Always watcher started for this occurrence and unit; the unit returned to outside-run observation after a run; or the condition, delays, referenced channels, enabled state or armed mode were edited. |

A new session starts from nothing: no condition, availability or debounce state is carried across an
unobserved gap, so an already-true condition can produce another **Activated** when a run starts or
the app comes back, and there is no synthetic "session started" row in this timeline. The run's own
file records the session boundary even when no transition follows; Histories and reports use it to
classify an older open activation as Interrupted. That marker stays out of this timeline and raises
no notification. Display-only edits (name, message, severity) keep the session and only change
later snapshots.

## Search and filter {#search-and-filter}

| Control | Values | What it does |
| --- | --- | --- |
| Search | Placeholder "Search name, message, model, unit or procedure" | Contains-match on the occurrence's name, its message, the model name, the unit name and the procedure name, as snapshotted at the time. |
| Severity | **All severities** (default), **Critical**, **Warning**, **Info** | Keeps only transitions of occurrences that carried that severity when they happened. |
| Refresh | | Re-reads the journal; tooltip "Refresh occurrences". |
| Export | **Export as CSV…**, **Export as JSON…** | Writes the rows currently listed. Drawn only with **Export/import histories**; greyed while the list is empty. |

## The rows {#the-rows}

| Part of the row | Content |
| --- | --- |
| Time | Date and time the judge observed the edge (for a run, the judged sample's own timestamp). |
| Dot | The severity's tone: critical for Critical, caution for Warning, neutral for Info. |
| Name | The occurrence's name at the time. |
| Summary | "Transition · obs N (Reason) · Model / Unit · context", where N is the edge's sequence within its session and the context is "Run · " followed by the procedure's name (or just "Run" when no procedure name was recorded) or "Outside run". Example: "Activated · obs 1 (Run started) · Furnace / Line 2 · Run · Bake 180". |
| Level | The severity word. |

The message a row carries is the occurrence's own message for **Activated** and **Cleared** (empty
when the definition has none, shown as "No message captured." in the panel), and for the
availability edges "Signal unavailable:" or "Signal restored:" followed by the names of the affected
channels, separated by commas.

## The detail panel {#the-detail-panel}

Selecting a row opens the panel with the severity badge and the name, the full timestamp, the
summary line, the message, and four cards:

| Card | Field | Content |
| --- | --- | --- |
| EVENT | Level | The severity. |
| EVENT | Event ID | The journal's identity of this edge. For a run observation it is the same id the run file holds. |
| OCCURRENCE SNAPSHOT | Transition | Activated, Cleared, Unavailable or Restored. |
| OCCURRENCE SNAPSHOT | Model | The model's name at the time. |
| OCCURRENCE SNAPSHOT | Unit | The unit's name at the time. |
| OCCURRENCE SNAPSHOT | Scope | Run or Outside run. |
| OCCURRENCE SNAPSHOT | Procedure | Only for the Run scope: the procedure's name, or a dash when none was recorded. |
| OCCURRENCE SNAPSHOT | Run ID | Only for the Run scope: the run's id, the same one Histories shows. |
| OCCURRENCE SNAPSHOT | Model ID, Unit ID, Occurrence ID | The stable ids behind the names, unchanged by a rename. |
| OBSERVATION SESSION | Journal sequence | The row's insertion number in the journal, which orders rows that share a timestamp. |
| OBSERVATION SESSION | Observation ID | The identity of the session that observed the edge; every edge of one session shares it. |
| OBSERVATION SESSION | Transition sequence | The edge's one-based order within the session. A gap means an edge the journal could not persist. |
| OBSERVATION SESSION | Started | When the session began, as a full timestamp. |
| OBSERVATION SESSION | Reason | Run started, Run resumed, Watcher started, Outside-run observation resumed or Definition changed. |
| FROZEN CONFIGURATION | Condition | The JavaScript condition the session actually judged. |
| FROZEN CONFIGURATION | On / off delay | "x ms / y ms", the debounce the session used. |
| FROZEN CONFIGURATION | Referenced channel IDs | The ids of the channels that gated the condition, separated by semicolons. |

**Copy event** copies one line: "timestamp | Occurrence | severity | name | summary | message |
EventId=… | OccurrenceId=… | ObservationId=… | ObservationSequence=… | ObservationStartedAt=… |
ObservationReason=… | Condition=… | OnDelayMs=… | OffDelayMs=… | ReferencedChannelIds=…".

## Export columns {#export-columns}

CSV and JSON carry the same fields, in this order: `Timestamp` (the CSV header adds the time
reference, "Timestamp (UTC-03:00)"), `JournalSequence`, `Severity`, `Name`, `Transition`, `Scope`,
`Model`, `ModelId`, `Unit`, `UnitId`, `Procedure`, `RunId`, `Message`, `EventId`, `OccurrenceId`,
`ObservationId`, `ObservationSequence`, `ObservationStartedAt`, `ObservationReason`, `Condition`,
`OnDelayMs`, `OffDelayMs` and `ReferencedChannelIds` (semicolon-separated in the CSV, an array in
the JSON). The JSON is an envelope with `TimeZone` and `Events`. File names are
`occurrence-history-<yyyyMMdd-HHmmss>.csv` and `.json`. The CSV follows the station's
decimal-separator preference like every other export: with point decimals the list separator is
`,`, with comma decimals it is `;`. A cell beginning with `=`, `+`, `-` or `@` is prefixed with an
apostrophe so a spreadsheet reads it as text.

## Where else an occurrence is recorded {#where-else-an-occurrence-is-recorded}

A run observation is written to the run's own file first, then copied here with the same event id
and the run context; if the copy fails, the run file still holds the edge and Console carries an
Error line saying so. An outside-run observation exists only here. Every edge also leaves a Console
line under the Process area: an activation at the level of its severity (Critical as an error,
Warning as a warning, Info as information), a signal loss at that same level, a clear and a
restoration as information. An activation additionally raises an in-window notification titled
"Occurrence · " followed by the unit's name and, for a Warning or Critical activation observed
during a run, an OS
notification while the window is hidden or minimized.

## Empty states and the status line {#empty-states-and-the-status-line}

- "No occurrences yet" with "Occurrence transitions from runs and outside-run Always monitoring
  will appear here."
- "No occurrences match these filters" with "Adjust the severity or search terms to widen this
  history."
- The status line reads "N shown · page 1", then " · filtered" when a search, a severity or a date
  range is in force; on the right, "1 selected" and "Updated hh:mm:ss".

## What the Occurrences tab does not do {#what-the-occurrences-tab-does-not-do}

- It does not define, arm or edit an occurrence
  ([Process > Setup > Occurrences](process-occurrences)).
- It does not pair edges into episodes with durations: that derived view, with its Interrupted and
  Incomplete classifications, is in [Histories](histories) for a run and on the unit's own panels.
- It has no unit, run, scope or date filter; search by the unit, model or procedure name, or open
  the run in Histories.
- It shows no live state: whether an occurrence is active right now is read on the unit's panel,
  not here.
