Events

The job_events table is an append-only ledger of every state transition. Subscribers are readers of the SQLite file; there is no daemon, no broker, and no push mechanism.

One-shot query

python3 -m spoolctl events --db ./queue.db --json

Returns an envelope with data.events (array of event objects) and meta.pagination.cursor (the resume point for the next query).

Flags:

Flag Default Description
--since-id N 0 Return events after this event ID. Also aliased as --since-cursor.
--limit N 1000 Maximum events to return. 0 means unlimited.
--job N all Filter to events for a specific job.

Long-poll (--wait)

python3 -m spoolctl events --db ./queue.db --json --wait

If no events are available, the command blocks until at least one event arrives or the wait timeout expires.

Flag Default Description
--wait off Enable long-polling.
--wait-timeout N 30.0 Maximum seconds to wait for events.

The response envelope includes meta.wait.reason: either records_available (events arrived) or timeout (the wait expired without new events).

Follow mode (--follow --json)

python3 -m spoolctl events --db ./queue.db --follow --json

Streams events as NDJSON (one JSON object per line). The command runs until interrupted, a limit is reached, or an error occurs.

Each line is either a data frame (an event object) or a control frame.

Control frames

Control frames have the shape {"control": {"type": "<type>", "reason": "<reason>", ...}}:

Type Reason Meaning
end idle_timeout No events arrived within --idle-timeout seconds.
end max_events The --max-events limit was reached.
error sqlite_error A database error occurred. Includes message and exit_code.

Follow-mode flags

Flag Default Description
--follow off Enable streaming mode.
--poll-interval N 0.5 Seconds between polls.
--max-events N unlimited Stop after this many events.
--idle-timeout N unlimited Stop after this many seconds with no new events.
--since-id N current high-water Start from this event ID.

--follow and --wait are mutually exclusive. --limit is not valid with --follow (use --max-events instead).

Consuming events programmatically

Cursor-based pagination

# First page
python3 -m spoolctl events --db ./queue.db --json --limit 100

# Next page (use cursor from meta.pagination.cursor)
python3 -m spoolctl events --db ./queue.db --json --limit 100 --since-id 42

The cursor is the event ID to resume from. It is stable across database changes.

Follow mode for agents

For agents that need real-time event processing:

python3 -m spoolctl events --db ./queue.db --follow --json --idle-timeout 60

Parse each line as JSON. Data frames have event fields (id, job_id, event, at, worker_id, detail). Control frames have control.type and control.reason. An end control frame means the stream terminated normally; an error control frame means it terminated due to a problem.

Human output

Without --json, events are printed in a human-readable table format. --follow without --json prints one line per event as it arrives.