Add webhook event push (session.completed / session.failed) #19

Merged
ClaudiuSchuster merged 2 commits from oss-oo/webhook-push into main 2026-09-24 12:36:00 +00:00
Owner

What

The observatory gains true push instead of poll: whenever a session that was observed running lands on a terminal completed/failed status in fetch_sessions(), every registered webhook subscribed to that event receives an HTTP POST — the first step towards a real event bus.

API

  • POST /api/webhooks/register — body {"url": "http://…", "events": ["session.completed", "session.failed"]} (events optional = all); validates absolute http(s) URL and known event names (400 otherwise), returns 201 with the stored record incl. its wh-… id
  • GET /api/webhooks — list registered webhooks
  • DELETE /api/webhooks/<id> — remove one (404 when unknown)

Event payload

{"event": "session.completed", "session_id": "sess_…", "status": "completed", "timestamp": "2026-09-24T12:24:20Z", "title": "…"}

Behavior

  • Registry: ~/.config/zcode-dispatch/webhooks.json (override: ZDO_WEBHOOKS_FILE)
  • Delivery in daemon threads — a dead callback never blocks the dashboard or the sessions API
  • Retry: 3 attempts, 1 s apart, 5 s timeout each, then give up (logged to the observatory console)
  • First observation of a session is baseline only and never fires; hidden-from-view sessions still fire (view state ≠ lifecycle)
  • ZCode DB stays strictly read-only; no secrets in code; do_POST register handling extracted into _register_webhook() to stay within pylint's branch budget

Verification

  • End-to-end against a fixture DB (pinned in the transcript): running→completed and running→failed delivered correct payloads to a local receiver; a dead endpoint was retried exactly 3 times then given up; register/list/delete/404 and all validation-400 paths verified via curl
  • Super-Linter slim v8.7.0 (pinned CI image, 4ce20838…): all validators green — note this required fixing pre-existing findings in .github/generate-icon.py (added in #18 whose checks never ran; the regenerated icon.png is byte-identical). The repo's .python-pylint/.python-ruff files are dead config for this image version (it expects .python-lint/.ruff.toml) — left untouched here
  • README: new Webhooks section with curl examples and a 2-minute manual verification recipe

Closes the Pinky task: push statt Poll.

## What The observatory gains true push instead of poll: whenever a session that was observed **running** lands on a terminal `completed`/`failed` status in `fetch_sessions()`, every registered webhook subscribed to that event receives an HTTP POST — the first step towards a real event bus. ## API - `POST /api/webhooks/register` — body `{"url": "http://…", "events": ["session.completed", "session.failed"]}` (events optional = all); validates absolute `http(s)` URL and known event names (400 otherwise), returns **201** with the stored record incl. its `wh-…` id - `GET /api/webhooks` — list registered webhooks - `DELETE /api/webhooks/<id>` — remove one (404 when unknown) ## Event payload ```json {"event": "session.completed", "session_id": "sess_…", "status": "completed", "timestamp": "2026-09-24T12:24:20Z", "title": "…"} ``` ## Behavior - Registry: `~/.config/zcode-dispatch/webhooks.json` (override: `ZDO_WEBHOOKS_FILE`) - Delivery in daemon threads — a dead callback never blocks the dashboard or the sessions API - Retry: 3 attempts, 1 s apart, 5 s timeout each, then give up (logged to the observatory console) - First observation of a session is baseline only and never fires; hidden-from-view sessions still fire (view state ≠ lifecycle) - ZCode DB stays strictly read-only; no secrets in code; `do_POST` register handling extracted into `_register_webhook()` to stay within pylint's branch budget ## Verification - End-to-end against a fixture DB (pinned in the transcript): running→completed and running→failed delivered correct payloads to a local receiver; a dead endpoint was retried exactly 3 times then given up; register/list/delete/404 and all validation-400 paths verified via curl - Super-Linter slim **v8.7.0** (pinned CI image, `4ce20838…`): all validators green — note this required fixing pre-existing findings in `.github/generate-icon.py` (added in #18 whose checks never ran; the regenerated `icon.png` is byte-identical). The repo's `.python-pylint`/`.python-ruff` files are dead config for this image version (it expects `.python-lint`/`.ruff.toml`) — left untouched here - README: new Webhooks section with curl examples and a 2-minute manual verification recipe Closes the Pinky task: push statt Poll.
The observatory now pushes session status changes instead of only
being polled: when a session that was observed running lands on a
terminal completed/failed status in fetch_sessions(), every registered
webhook subscribed to that event receives an HTTP POST (event,
session_id, status, timestamp, title).

- POST /api/webhooks/register (url + events, validated), GET
  /api/webhooks, DELETE /api/webhooks/<id>
- Registry persisted to ~/.config/zcode-dispatch/webhooks.json
  (override: ZDO_WEBHOOKS_FILE)
- Delivery in daemon threads: 3 attempts, 1 s apart, 5 s timeout,
  then give up (logged); a dead callback never blocks the dashboard
- First observation of a session is baseline and never fires
- ZCode database stays strictly read-only

Verified end to end against a fixture DB: completed and failed
transitions delivered correct payloads, dead endpoints retried 3
times then given up, delete/404 and validation paths covered.
Fix pre-existing lint findings in generate-icon.py
Some checks failed
Checks / Functional checks (pull_request) Has been cancelled
Checks / Super-Linter (pull_request) Has been cancelled
9277d0075e
The pinned Super-Linter (slim v8.7.0) lints the whole codebase and its
run on main fails on generate-icon.py (added with checks never having
run): unused math import, two imports on one line, missing docstrings,
two statements on one line, shadowed name, long docstring line. All
fixes are mechanical; the regenerated icon.png is byte-identical.
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
OSS-Singularity/zcode-dispatch-observatory!19
No description provided.