Observability for agent runs — jobs, rounds, and per-step telemetry
Every task Kiwi runs is inspectable end-to-end: which rounds ran, what each one did, why a task has not started yet, and where the PR landed. Observability is built on a few primitives plus the dashboard.
Jobs
A job is one submitted task. It carries the task summary, accumulated cost, and a status that rolls up from its underlying queued task — plus, if a PR review comment resumed it, from any continuation tasks that followed. The dashboard renders a job's rounds in the job detail view.
The run timeline
The daemon runs the Architect/Implementer session in its own process, so nothing else observes it. It therefore emits an event per phase — round_start, implementer, tool, review, verify, session_end — each with its outcome, duration, and the tokens and cost it consumed. Those events are what the dashboard's task drawer renders, phase by phase: what the Implementer did, whether the Architect's review approved or asked for a revision, whether the test passed.
The Architect's review rationale is in the timeline — that is the only place a run explains itself in words. Raw test output stays out of it, as it always has, because it can carry secrets. The same events are the evidence a verified execution record is assembled from, which is why they are stored as append-only rows that outlive the queue row they describe.
Why a task has not started
A QUEUED task on its own says only "nobody has leased this yet" — the same display whether work is about to begin or can never begin at all. So each queued task also reports a blocked reason, a stable code plus a sentence:
| Reason | Meaning |
|---|---|
awaiting_runner | the healthy case — a live daemon serves this fleet and has not picked it up yet |
provisioning | a Free-tier daemon is being cold-started for the org |
provision_failed | the last cold-start failed outright, with the launcher's reason attached |
no_runner | no daemon is registered that could ever lease it |
runner_offline | daemons are registered but none has heartbeated recently |
concurrency_cap | the org is at its concurrent-job limit |
compute_cap | the org is at its monthly agent-minute ceiling |
The diagnosis reads the same code paths as the lease rules it describes, including fleet routing, so an explanation cannot drift from the enforcement. It is a pure read that never affects scheduling, and it is best-effort: an explanation of a status must never be the reason the status request fails. Two of these were previously silent by construction — an org at a cap and an org with no work at all both produced "nothing leased".
In the dashboard the reasons split by severity: waiting reasons keep the spinner, while problems get a static warning, because animating a spinner over a job nobody can run is the UI asserting progress that is not happening.
Job control
A job can be cancelled, retried, or deleted from the dashboard, and the API reports how many tasks each action actually affected — "cancel" on a job whose tasks all finished a second earlier is a success that changed nothing, and the caller should be able to tell. Cancellation reaches a running task: the Control Plane cannot call into a daemon, so it revokes the lease, and the daemon's renewal failure aborts the run in flight.
Live topology
The Next.js dashboard renders a live topology of the running system. Crucially, it hangs each running job off the executor daemon that leased it, so you can see the whole swarm at once: which daemons are alive, which fleet they belong to, and what each is working on right now.
The dashboard also surfaces jobs, fleets, models, integrations, spend, onboarding and settings — a single pane over the whole platform. Job lists can be filtered, sorted and grouped by date, with the view persisted in the URL so a filtered list is a shareable link.
Spend
Spend is computed from each call's token usage at the price of the model that actually ran, using the same model-to-provider routing the daemon used — so a job's cost cannot be attributed to a provider it did not run on. Admission itself makes no model call and costs nothing to meter: the Architect and Implementer are the only spend on a job, tracked separately so a cheap Implementer cannot be used to hide an expensive Architect.
Surfaces for automation
Beyond the dashboard, the same job state is reachable programmatically:
- The
kiwiCLI — submit, resume (kiwi submit -resume -task-id <id>), and inspect. - Node and Python SDKs — programmatic submission for CI/CD and auto-triage (e.g. Sentry), published as
@runkiwi/sdkon npm andkiwi-sdkon PyPI. - The Linear webhook — issues labeled
kiwi(or moved to In Progress) become planner jobs, closing the loop from issue tracker to PR. - The GitHub webhook — a merged PR appends a signed merge link to the job's execution record, capturing who approved it.