2026-09-27·by Sijie Wang#standmeet#architecture#design#events

tasks-panel

The Tasks panel

Parent: events

Status: released in v0.1.76 (2026-09-27) — design and as-built record in docs/design/event-bus-outbox-webhooks.md in the StandMeet repo.

The queue, the periodic jobs, the event stream and the delivery log are visible and actionable in admin. The panel follows River UI's information layout, but River UI itself is not adopted: it is a separate service with its own auth. The panel is built on dispatcher ops, so admin and MCP both get it. An owner can also ask their AI "are any tasks stuck?".

Panel structure

  • ?job=<id> opens a job directly. The webhook delivery log links there.
  • Webhooks is its own admin section under Integrations (webhooks), not a tab of this panel.
  • Alerts on the overview: events_backlog (more than 1,000 unfanned events, or the oldest older than 5 min), events_poisoned (any poisoned event), jobs_discarded (any discarded job). See storage-bounds.

Replaced the in-memory registry

The periodic-job list in the monitor panel was backed by the in-memory JobRegistry, lost on restart. The registry and the in-process scheduler are deleted. Periodic jobs run on River, and instance.jobs (System panel) now reads River's durable records.

Ops (declared once, projected to admin and MCP)

The ops live in the stats domain (internal/stats/ops/tasks.go, tasks_events.go). Admin reaches them under /api/admin/tasks* and /api/admin/events*.

  • Reads: tasks.overview, tasks.list, tasks.get, tasks.periodic, events.list, events.get.
  • Actions: tasks.retry, tasks.cancel, tasks.run_periodic, events.requeue.
  • The plan's "discard" on a job is cancel: River stops a job for good only by cancelling it, so a second verb would do the same thing.
  • webhooks.deliveries is a filtered view of webhook.deliver jobs. See webhooks.
  • tasks.get shows a job's state; a result shape that belongs to a domain has its own op (jobs.fetch_result, completion-hooks).

The ops sit behind the Inspector port, so the panel does not depend on River directly (queue-behind-ports).

Permission: owner plane only

These ops are owner reads and owner actions only; they are not on the API-key face. Job args and event payloads are thin, so the panel never shows corpus bodies (event-model).

Manual retry of a stuck delivery

Retry policy and failure classes: retry-has-one-owner.

Acceptance

The e2e specs tasks-panel and tasks-panel-more go through the real UI; every assertion is on what the page shows:

  1. Overview numbers match reality: make 3 retryable and 1 discarded; the overview shows 3 and 1 and the age of the oldest pending job.
  2. The job list filters by state and kind; results contain only the selection. The kind filter lists every declared kind, even one that has never run.
  3. Job detail shows every attempt's error and the next retry time.
  4. "Retry now" → the state becomes completed, and the side effect really happens.
  5. "Cancel" → the job no longer runs and its side effect does not happen (a sentinel job proves the queue is running).
  6. The periodic list shows last and next runs; "Run now" updates the last-run time.
  7. After a backend restart, the panel still shows periodic runs from before the restart.
  8. Event stream: edit a note → the event appears; its detail lists the subscribers it fanned out to and each one's state.
  9. The webhook delivery log links to job detail; "Re-deliver all" delivers every discarded job of that endpoint.
  10. Alert: the panel shows an alert while the backlog is over the threshold, and clears it once drained.
  11. Permission: calling tasks.* with an API key (not the owner) is refused.
  12. Completion hooks: after approving a request, its row goes from "sending" to "sent"; with the mail mock failing first it shows "failed", then "sent" after the retry; jobs.fetch_new past its wait limit returns a receipt, and jobs.fetch_result returns the result in the same shape (completion-hooks · async-response-contract).

Built along the way: Tasks detail acted on a stale job after switching rows; fixed.

about this entry

One of sijie's wiki entries. The AI on this site is grounded in the same corpus and answers in sijie's voice, with citations back to entries like this one — answering costs sijie money, so it waits behind a code: enter an access code →