<!-- Sema documentation — Events
     Sema is a new AI-native, neurosymbolic programming language most models
     have not been trained on. Full corpus: https://sema.49.12.246.95.sslip.io/llms-full.txt
     Install language support: https://sema.49.12.246.95.sslip.io/install-skill/ -->

# Events

> Typed domain signals in Sema — event declarations with contract-checked payloads, emit, and subscriber handlers with bounded queues and journaled delivery.

An `event` is a **typed domain signal** whose deliveries are *allowed to make the
program do something*. It is the third member of Sema's telemetry-and-reaction family,
and the one that closes the loop:

- [`collector`](/governance/collectors/) records telemetry that can **never** drive
  control flow.
- [`monitor`](/governance/monitor/) answers "has this stream's distribution shifted?"
- **`event`** is the construct whose deliveries **do work** — the missing counterpart
  that corpus code used to improvise as ambient `alert(...)` calls, watcher tasks with
  manual `cancel`, and approval-record polling.

## Why events are a language construct

Callback and listener APIs are invisible to the type system: they don't appear in
[effect rows](/governance/effects/), they escape [policies](/governance/policy/), and
they hide the delivery graph. Fire-and-forget delivery with unbounded queues loses
signals silently. Sema replaces all of that with a construct where the payload is a
contract, delivery is journaled and bounded, handlers run under their *own* policy,
and the compiler sees the complete emit/subscribe graph at load time.

## Syntax

An `event` declares a nominal payload record; a function `emit`s it; a `subscriber`
handles it:

```sema
event IncidentQuarantined:
    sem "An ingested item was quarantined by a semantic guard"
    incident: Incident
    evidence: SemanticsViolation
    key incident.region                    # optional per-key ordering/partition

def quarantine(i: Incident, v: SemanticsViolation) -> None !{event.emit, fs.write}:
    audit_store(i, v)
    emit IncidentQuarantined(incident=i, evidence=v)

subscriber quarantine_review on IncidentQuarantined:
    sem "Queue quarantined incidents for analyst review"
    where event.incident.severity >= Severity.high     # deterministic, effect-free filter
    queue ring(4096), on_full=block
    handle event !{db.write, event.emit}:
        review_queue.push(event.incident, event.evidence)
```

The verified `crisis-logistics` corpus uses exactly this shape to quarantine a public
briefing that fails a semantic guard, so the block becomes a persisted record for
review instead of a silent drop:

```sema
event IncidentQuarantined:
    sem "A public briefing was blocked by a semantic guard before publication"
    briefing: PublicBriefing sem "The withheld briefing, redacted but unpublished"
    evidence: SemanticsViolation sem "Guard verdict with predicate, judge, and excerpts"

# ... inside publish_public_briefing, under an except SemanticsViolation branch:
        emit IncidentQuarantined(briefing=safe, evidence=violation)

subscriber quarantine_review on IncidentQuarantined:
    sem "Persist quarantined briefings for analyst review"
    queue ring(256), on_full=block
    handle event !{fs.write}:
        write_quarantine_record("state/quarantine/briefings.jsonl", event.briefing, event.evidence)
```

## The payload is a boundary contract at the emit site

An `event` declaration is a nominal payload record: its fields carry `sem`
descriptors, `where` refinements, and `coerce by` normalizers exactly as struct fields
do. The whole payload is a **full boundary contract at the emit site** — a payload
that fails its contract *never enters the stream*. The failure is a typed
`ContractViolation` blamed on the emitter, not on some downstream handler. `emit` adds
`event.emit` to the caller's [effect row](/governance/effects/), and policies confine
it *per event type*:

```sema
forbid event.emit except event.emit(IncidentQuarantined)
```

## Subscribers: static, bounded, policy-confined

A `subscriber` is a **static declaration**, parallel in shape to a `monitor`.
Registration happens at container/module load, so the compiler sees the complete
delivery graph — it **warns on events with no subscriber** (a dead signal) and on
statically detectable **emit cycles**. Three parts matter:

- **`where <filter>`** — an *effect-free* deterministic filter. It cannot perform I/O
  or call a model; it only decides whether this delivery is relevant.
- **`queue ring(n), on_full=<mode>`** — a **bounded** queue. `ring(n)` is required;
  an unbounded queue is a compile error (same rule as collectors). `on_full` is one of
  `block` (default — backpressure to the emitter), `drop_oldest`, or `fail`. Drops are
  journaled as `EventDropped` records — never silent.
- **`handle event !{...}`** — the handler, with its **own** effect row. It runs under
  the *subscriber's* policy envelope, never the emitter's.

The `robotics-cell` corpus brings the cell to a deterministic safe stop when a
[monitor](/governance/monitor/) emits a fault event — the monitor detects drift, the
event carries it, the subscriber reacts:

```sema
subscriber safe_stop on CellFaultDetected:
    sem "Bring the cell to a deterministic safe stop when telemetry drifts"
    queue ring(64), on_full=block
    handle event !{ffi.call}:
        hardware_safe_stop()
        log.info("cell safe-stopped after telemetry drift", order=event.order_id)
```

## Delivery semantics: journaled, ordered, exactly-once

Emission appends an `EventEmitted` record to the **same hash-chained journal** as
model calls and contract verdicts — the event bus is *not* a side channel, and replay
reproduces delivery order and handler effect traces exactly. Delivery is:

- **Asynchronous**, with per-subscriber FIFO order per emitter (per `key` value when
  declared; cross-key deliveries are concurrent).
- **Exactly-once per subscriber within a run.**
- **Structured** — handlers run as structured children of the scope that owns the
  subscriber (the module's container scope by default, or the enclosing
  [`supervise`](/governance/supervise/) when declared inside one). There are no orphan
  handler tasks. A handler failure is a typed `SubscriberFailure` routed to the owning
  supervision scope; **the emitter is never affected.**

Shutdown drains queues under the container deadline and journals undelivered events as
`EventUndelivered`. Handler-emitted events are depth-budgeted (default 16) against
cycles; exceeding it is a typed `EventCycleBudgetExceeded` on the emitting handler.

:::caution[Emitting endorses nothing]
The payload's trust label is the meet of its field labels at emission and **travels
with delivery**. An `untrusted` [`simulate`](/neurosymbolic/simulate/) output emitted
as an event is still `untrusted` in every handler — the event bus is not an
endorsement door ([trust labels](/governance/provenance/)).
:::

## Runtime lifecycle events are ordinary subscribers

The prelude declares runtime lifecycle events on this *same* construct — `Alert` (the
target of the `alert(...)` sugar), `MonitorVerdictChanged`, `HealEvent`,
`ContainerStarted`, `PolicyDenied`, `RepairExhausted`. So operational reactions like
"page someone when a heal escalates" are ordinary subscribers, not special runtime
hooks:

```sema
subscriber page_oncall on RepairExhausted:
    queue ring(64), on_full=block
    handle event !{net.connect}:
        page(event.supervisor, event.outcome)
```

## Failure modes

- **Emit under a policy without `event.emit`** → typed denial.
- **A handler effect row exceeding the subscriber's policy** → compile error.
- **A contract-failing payload** → emitter-blamed `ContractViolation`; nothing is
  delivered.
- **Queue overflow under `on_full=fail`** → typed `EventBackpressure` at the emit site.
- **An event with no subscriber** → dead-signal warning at compile time.
- **A calibrated `semantics()` guard inside a `where` filter** → a decision site like
  any other; monitor coverage applies.

## How it's checked

- `sema check <project>` builds the emit/subscribe graph, warns on dead signals and
  static emit cycles, verifies handler effect rows against subscriber policies, and
  rejects unbounded queues.
- Emission and delivery are journaled (`EventEmitted`, `EventDropped`,
  `EventUndelivered`), so replay reproduces the exact delivery sequence.
- An event stream is a valid [monitor](/governance/monitor/) target
  (`monitor X on IncidentQuarantined:`).

## See also

- [Monitors & Drift](/governance/monitor/) — the statistical channel that emits events
  on drift.
- [Collectors & Taps](/governance/collectors/) — the observation-only channel that
  cannot react.
- [Supervise & Heal](/governance/supervise/) — the scope that owns subscriber tasks
  and receives `SubscriberFailure`.
- [Policies](/governance/policy/) — confining `event.emit` per event type.
