exlab_wizard.api.events#

WebSocket frame envelope types. Backend Spec §4.6.2.

These msgspec.Struct types are the typed shape of every frame emitted on the two WebSocket channels:

  • WS /api/v1/sessions/{id}/events – per-session pipeline events (PhaseEvent, ProgressEvent, InputRequiredEvent, WarningEvent, DoneEvent, FailedEvent).

  • WS /api/v1/problems/events – audit pub-sub channel (SnapshotEvent, DeltaEvent).

Each Struct uses a string tag that drops onto the wire as the kind discriminator (§4.6.2 example payloads). Encode via msgspec.json.encode to keep the same hot-path serializer the cache files use (§4.4.5 rationale).

The structs are intentionally permissive: fields/added/etc. carry list[dict] rather than typed sub-structs because the field specs and finding shapes are defined elsewhere (PluginInputRequired fields are spec’d by the plugin; findings by §11.8). We round-trip the dict shape so the outbound frame is the literal example payload.

Functions

encode_event(event)

Encode a typed WebSocket frame to JSON bytes via msgspec.json.

event_from_dict(payload)

Convert a plain dict (e.g. from the controller's event queue) into the matching typed Struct.

Classes

DeltaEvent(*, added, removed, changed, audit_at)

Delta frame for the Problems pub-sub channel.

DoneEvent(*, result)

Terminal success frame.

FailedEvent(*, phase, error)

Terminal failure frame.

InputRequiredEvent(*, fields, reason, plugin)

Plugin escalation frame.

PhaseEvent(*, phase, at)

Per-state phase frame.

ProgressEvent(*, phase, current, total)

Progress frame for the long-running plugin pass.

SnapshotEvent(*, findings, audit_at)

Full audit snapshot frame for the Problems pub-sub channel.

WarningEvent(*, phase, message)

Non-fatal warning frame.

class exlab_wizard.api.events.DeltaEvent(*, added: list[dict[str, Any]], removed: list[dict[str, Any]], changed: list[dict[str, Any]], audit_at: str)[source]#

Bases: Struct

Delta frame for the Problems pub-sub channel. Backend Spec §4.6.2.

Sent on every audit pass after the first. The three lists carry the rule + offending_path pairs that were added, removed, or changed compared to the previous snapshot.

added: list[dict[str, Any]]#
audit_at: str#
changed: list[dict[str, Any]]#
removed: list[dict[str, Any]]#
class exlab_wizard.api.events.DoneEvent(*, result: dict[str, Any])[source]#

Bases: Struct

Terminal success frame. Backend Spec §4.6.2.

result carries the final state envelope: path (the new directory), sync_status, blocked, and any other fields the pipeline chooses to surface.

result: dict[str, Any]#
class exlab_wizard.api.events.FailedEvent(*, phase: str, error: dict[str, Any])[source]#

Bases: Struct

Terminal failure frame. Backend Spec §4.6.2.

error follows the §4.6.3 error envelope shape (code, message minimum) so client code can reuse the same dispatch table it uses for HTTP-level errors.

error: dict[str, Any]#
phase: str#
class exlab_wizard.api.events.InputRequiredEvent(*, fields: list[dict[str, Any]], reason: str, plugin: str)[source]#

Bases: Struct

Plugin escalation frame. Backend Spec §4.6.2 / §6.4.

fields is the plugin-supplied list of field-spec dicts (id/label/type/required/default/options/hint) that the wizard’s escalation dialog renders. reason is the plugin’s prompt string. plugin is the plugin name so the UI can attribute the prompt.

fields: list[dict[str, Any]]#
plugin: str#
reason: str#
class exlab_wizard.api.events.PhaseEvent(*, phase: str, at: str)[source]#

Bases: Struct

Per-state phase frame. Backend Spec §4.6.2.

Emitted on every state transition that has a corresponding phase enum value (see §4.7.1 mapping table). at is a UTC ISO-8601 timestamp stamped at transition time.

at: str#
phase: str#
class exlab_wizard.api.events.ProgressEvent(*, phase: str, current: int, total: int)[source]#

Bases: Struct

Progress frame for the long-running plugin pass. Backend Spec §4.6.2.

current: int#
phase: str#
total: int#
class exlab_wizard.api.events.SnapshotEvent(*, findings: list[dict[str, Any]], audit_at: str)[source]#

Bases: Struct

Full audit snapshot frame for the Problems pub-sub channel.

Sent immediately after the WebSocket connects so the client can paint the initial Problems-tab state without a separate REST call. Backend Spec §4.6.2 example.

audit_at: str#
findings: list[dict[str, Any]]#
class exlab_wizard.api.events.WarningEvent(*, phase: str, message: str)[source]#

Bases: Struct

Non-fatal warning frame. Backend Spec §4.6.2.

Emitted by the controller when a phase produces a warning that should be surfaced in the UI without aborting the session (e.g. post-validate hard-tier finding gating sync).

message: str#
phase: str#
exlab_wizard.api.events.encode_event(event)[source]#

Encode a typed WebSocket frame to JSON bytes via msgspec.json.

Single dispatch site so callers don’t repeat the encoder choice; keeps the §4.6.2 wire format consistent with the cache hot path.

Parameters:

event (Struct)

Return type:

bytes