Queen Event Hub
Unified read-only consumer for task events from three sources:
DAG dispatcher (events.jsonl), Kanban (task_events SQLite), and natural
agent tasks (delegate_task / terminal background).
When to use
- Need a single deduped view of "did run X complete, did any task block?"
- Aggregate N task completions into one notification; surface blocked/critical immediately.
- Cross-run timeline for a run_id regardless of source.
Schema (authoritative: ADR-003)
Unified event: {event_id, source, run_id, task_id, ts, type, severity, payload}.
event_id = sha1(source:source_key:ts)[:16]
type in {started, completed, failed, blocked, run_started, run_ended}
severity in {info, warning, critical}
- Mapping table lives in
~/hermes-wiki/decisions/ADR-003-queen-event-schema.md.
Change the ADR first, then the code.
Commands
HUB=~/.hermes/skills/queen-dispatch/event-hub/scripts/hub.py
# ingest a dispatcher run's events.jsonl
python3 $HUB ingest --source dispatcher --path <run_dir>/events.jsonl --run-id <id>
# ingest kanban task_events (SQLite, read-only)
python3 $HUB ingest --source kanban --path <kanban.db> --run-id <id>
# ingest agent events (JSONL: {delegation_id, ts, ok, summary})
python3 $HUB ingest --source agent --path <agent.jsonl> --run-id <id>
# notify: default|burst|quiet
python3 $HUB notify --policy default
# timeline for a run
python3 $HUB show --run-id <id>
Behavior
- Dedup: event_id persisted to
<artifact_root>/_event-log.jsonl; duplicate event_id is skipped.
- Default
--artifact-root is ~/.hermes/artifacts/queen (same parent as dispatcher run_dir).
Log lands at ~/.hermes/artifacts/queen/_event-log.jsonl — NOT nested under queen/queen/.
- Notify default: blocked/critical standalone alert + one aggregated summary per run.
- Notify quiet: only critical/warning, one line each.
- Never edits source artifacts. Read-only consumer.
Source mapping (read-only consumers)
| Source |
Input path |
Notes |
| dispatcher |
<run_dir>/events.jsonl |
Auto-written by dispatcher at run end; no extra config needed. |
| kanban |
<kanban.db> (SQLite, task_events) |
Read via mode=ro; fallback/query surface only. |
| agent |
<agent.jsonl> (one JSON per line) |
{delegation_id, ts, ok, summary} |
status.json (dispatcher) and kanban status tables are NOT ingested into the
event stream — they exist for status queries and as fallback surfaces. The
event stream is events.jsonl only. Pointing ingest --path at status.json
would be silently empty / wrong.
Pitfalls
- Do not duplicate the mapping table in code beyond the minimal projection.
--artifact-root defaults to ~/.hermes/artifacts/queen (matches dispatcher run_dir parent).
- Kanban db opened read-only (
mode=ro); never write to it.
- After a dispatcher run finishes,
<run_dir>/events.jsonl is already on disk —
just point ingest --source dispatcher --path <run_dir>/events.jsonl.
Do NOT point ingest at status.json.
1---2name: event-hub3description: Consume DAG/kanban/agent task events; dedupe and aggregate for Queen.4---56# Queen Event Hub78Unified read-only consumer for task events from three sources:9DAG dispatcher (`events.jsonl`), Kanban (`task_events` SQLite), and natural10agent tasks (`delegate_task` / `terminal background`).1112## When to use13- Need a single deduped view of "did run X complete, did any task block?"14- Aggregate N task completions into one notification; surface blocked/critical immediately.15- Cross-run timeline for a run_id regardless of source.1617## Schema (authoritative: ADR-003)18Unified event: `{event_id, source, run_id, task_id, ts, type, severity, payload}`.19- `event_id` = sha1(source:source_key:ts)[:16]20- `type` in {started, completed, failed, blocked, run_started, run_ended}21- `severity` in {info, warning, critical}22- Mapping table lives in `~/hermes-wiki/decisions/ADR-003-queen-event-schema.md`.23 Change the ADR first, then the code.2425## Commands26```bash27HUB=~/.hermes/skills/queen-dispatch/event-hub/scripts/hub.py28# ingest a dispatcher run's events.jsonl29python3 $HUB ingest --source dispatcher --path <run_dir>/events.jsonl --run-id <id>30# ingest kanban task_events (SQLite, read-only)31python3 $HUB ingest --source kanban --path <kanban.db> --run-id <id>32# ingest agent events (JSONL: {delegation_id, ts, ok, summary})33python3 $HUB ingest --source agent --path <agent.jsonl> --run-id <id>34# notify: default|burst|quiet35python3 $HUB notify --policy default36# timeline for a run37python3 $HUB show --run-id <id>38```3940## Behavior41- Dedup: event_id persisted to `<artifact_root>/_event-log.jsonl`; duplicate event_id is skipped.42- Default `--artifact-root` is `~/.hermes/artifacts/queen` (same parent as dispatcher run_dir).43 Log lands at `~/.hermes/artifacts/queen/_event-log.jsonl` — NOT nested under `queen/queen/`.44- Notify default: blocked/critical standalone alert + one aggregated summary per run.45- Notify quiet: only critical/warning, one line each.46- Never edits source artifacts. Read-only consumer.4748## Source mapping (read-only consumers)49| Source | Input path | Notes |50|-------------|------------------------------------------|----------------------------------------------------|51| dispatcher | `<run_dir>/events.jsonl` | Auto-written by dispatcher at run end; no extra config needed. |52| kanban | `<kanban.db>` (SQLite, `task_events`) | Read via `mode=ro`; fallback/query surface only. |53| agent | `<agent.jsonl>` (one JSON per line) | `{delegation_id, ts, ok, summary}` |5455`status.json` (dispatcher) and kanban status tables are NOT ingested into the56event stream — they exist for status queries and as fallback surfaces. The57event stream is `events.jsonl` only. Pointing `ingest --path` at `status.json`58would be silently empty / wrong.5960## Pitfalls61- Do not duplicate the mapping table in code beyond the minimal projection.62- `--artifact-root` defaults to `~/.hermes/artifacts/queen` (matches dispatcher run_dir parent).63- Kanban db opened read-only (`mode=ro`); never write to it.64- After a dispatcher run finishes, `<run_dir>/events.jsonl` is already on disk —65 just point `ingest --source dispatcher --path <run_dir>/events.jsonl`.66 Do NOT point ingest at `status.json`.