Events
Kraft records what happens to a work item as a list of events, its timeline.
Each event has a seq number, the work_item_id, a type, a JSON payload
and a created_at time. Read them with:
kraft view events ID # the whole timeline
kraft view events ID --type gate_requested
kraft view events ID --after 120 -f # later events, then follow
kraft view events ID --json # the raw events
The tables list each type and the payload fields you are most likely to use. A payload can carry more fields than these. Event types and payloads are not a stable interface: a release can add, rename or reshape them.
Work item
| Type | When | Key fields |
|---|---|---|
work_item_created | The item is filed. | title, repo, chain_template; source, bead_id, priority when auto-intake filed it |
work_item_attachments | The item is filed with a spec or plan. | attachments |
work_item_resumed | A paused item starts or restarts. | steer |
pause_requested | Someone pauses the item. | sessions |
work_item_blocked_by_dependency | An open bead dependency paused the item before its next node. | node_id, blocked_by |
paused_by_broken_base | Another item broke the base branch, so this one paused. | broken_by, follow_up_bead |
work_item_needs_human | The item stopped for a person. See below. | node_id, reason |
work_item_waiting | The item is parked on an external wait. | node_id, retry_at |
work_item_rate_limited | The agent hit a rate limit; the item retries on its own. | node_id, retry_at |
work_item_retried | Someone retried the item. | node_id, steer, escalated (an escalation turn retried it) |
work_item_completed | The item finished, on its own or by hand. | none |
work_item_manually_completed | Someone completed the item by hand. | reason, node_id |
work_item_cancelled | Someone cancelled the item. | reason, node_id |
work_item_abandoned | The item was cancelled or abandoned. | none |
work_item_archived | The item left the Done column. | by: you or auto |
work_item_restored | An archived item came back. | none |
work_item_title_edited | The title changed. | title |
work_item_description_edited | The description changed. | description |
plan_progress | A worker started a plan task. | node_id, task, total, title |
steer_context_set | A steer was stored for the next agent. | steer |
steer_undelivered | A steer was stored but no agent received it. | steer |
work_item_needs_human
This is the event behind stop_reason in kraft view show --json, and behind
"why did it stop" on the board. Its fields:
| Field | Meaning |
|---|---|
node_id | The node the item stopped on. |
reason | What stopped it, in words. A reason that starts needs_context: is a question from the agent. |
session_id | The last session of that node, when it failed or asked a question. kraft view logs reads its log. |
capped | {cycles, attempts} when a fix loop spent its attempts. |
budget | {scope, spent_usd, cap_usd} when a spend cap stopped the item. |
stuck | true when the item stopped in a way an escalation turn can answer. |
bundle | What Kraft gathered about a stuck item when it gave up. |
suggested_action | {action, reason}, the next step Kraft suggests. |
Troubleshooting maps each reason to a fix.
Chain and nodes
| Type | When | Key fields |
|---|---|---|
chain_loaded | The chain was built for the item. | none |
worktree_prepared | The worktree was created and set up. | report |
node_started | A node began. | node_id |
node_completed | A node finished. | node_id |
node_skipped | A node or its gate was skipped. | node_id, gate, note |
scope_skipped | A step or task inside a node was skipped. | path, note |
node_recovery_started | Kraft reran a node's failed tasks. | node_id, failed_tasks |
run_forked | A retry started a new run from part of the old one. | path, scope, reopened |
base_change_restart | The base branch moved, so part of the chain reran. | node_id, restart_from, nodes |
worktree_rebase_verified | A rebase reported by a worker matched the worktree. | reported_head, worktree_head |
read_only_violated | A read-only node or step changed the worktree. | node_id, scope, files |
fix_cycle_started | A fix loop started another attempt. | node_id, cycle, failed_tasks |
fix_cycle_refunded | A fix attempt did not count, because the repair never ran. | node_id, cycle, outcome |
findings_measured | A review or check produced findings. | node_id, cycle, findings |
judge_verdict | A judge ruled on findings. | node_id, cycle, verdict, reasoning |
sweep_failed | Kraft could not commit work an agent task left uncommitted: the commit failed, or the worktree was not on the item's branch, held a git operation Kraft did not start, or held a repository Kraft did not create. The work stays in the worktree. | node_id, task, error |
bead_not_filed | Intake could not file a bead. | reason |
beads_left_open | The item completed but its beads stayed open. | beads, reason |
Gates and review
| Type | When | Key fields |
|---|---|---|
gate_requested | A gate is waiting for a decision. | gate, node_id |
gate_approved | A gate was approved. | gate, by |
gate_rejected | A gate was rejected. | gate, note, node, by |
gate_reopened | An approved gate is open again. | gate, reason |
gate_auto_review_started | A gate's review agent started. | gate, session_id |
gate_auto_review_skipped | A gate's review agent did not run. | gate, reason |
gate_auto_review_discarded | A review agent's verdict was not used. | gate, verdict, reason |
chain_revision_shown | A person was shown a chain revision. | gate, digest |
chain_revision_unchanged | A chain revision proposed no change, so its gate passed. | gate, rationale |
chain_revised | An approved chain revision replaced the chain. | varies |
artifact_refused | Kraft found a gate's document but would not serve it. | path, reason |
review_submitted | A review was sent. | review_id, gate, outcome |
thread_updated | A review thread was resolved or reopened. | thread_id, state |
rewind_requested | A review asked for work to be redone. | review_id, target, note |
rewind_cancelled | That request was withdrawn. | review_id |
reply_agent_wrote | An agent answered review threads. | gate, files |
reply_agent_skipped | No agent answered review threads. | gate, reason |
reply_agent_failed | The reply agent failed. | gate, error |
by is human (the board or the kraft command), assistant (an agent
through the MCP server), agent (a Kraft worker, such as a gate's review
agent) or kraft (Kraft itself).
Agent sessions
| Type | When | Key fields |
|---|---|---|
worker_session_created | A session was recorded, before launch. | session_id, node_id, hook_point, attempt |
worker_session_started | Its process started. | session_id, node_id, hook_point, pid |
worker_session_exited | It ended. | session_id, status, wall_ms, model, cost_usd, token counts |
worker_session_paused | It was stopped by a pause, skip or manual end. | session_id |
agent_session_resumed | A session resumed the agent's earlier conversation. | task, session_id |
session_reattached | After a restart, Kraft found a session still running. | session_id, pid |
session_unknown | After a restart, Kraft could not confirm a session. | session_id, reason |
background_jobs_abandoned | A session left background jobs behind. | reason |
permission_decision | The permission gate answered a tool call. | session_id, node_id, tool, decision, reason, grant |
Caps, budgets and limits
| Type | When | Key fields |
|---|---|---|
time_cap_reached | A time cap ran out. | scope, field, minutes, reason, node_id |
scope_budget_reached | A scope's token or dollar cap refused a launch. | the breach fields, task |
budget_changed | The item's spend cap was set. | budget_usd |
budget_raised | The cap was raised to continue a stopped item. | budget_usd |
spend_unpriced | The first session on the item whose harness reported no cost and whose model prices.json cannot price: the item and daily dollar caps count it as $0. | session_id, harness, model, message |
cap_counters_reset | Loop counters were reset. | by, counters |
rate_limit_hit | An agent CLI refused a launch for a rate limit. | node_id, rate_limit_type, resets_at_iso |
launch_fallback | A launch moved to the next harness or model. | node_id, task, from, to, reason |
launch_fallback_exhausted | Every fallback was limited. | node_id, task, resets_at_iso |
Escalation
| Type | When | Key fields |
|---|---|---|
escalation_message | A message went into the escalation thread. | session_id, message, auto, thread, turn |
stuck_escalation_started | Kraft started an escalation turn on a stuck item. | node_id, task, reason, attempt |
stuck_escalation_finished | That turn ended. | node_id, task, status |
work_item_auto_escalate_skipped | Kraft did not escalate a stop. | reason |
work_item_auto_escalate_capped | Automatic escalations hit their limit. | cap, count |
work_item_self_retry_requested | An escalation turn asked to retry the item. | node_id, path, restart, steer |
work_item_self_retry_dropped | That retry did not happen. | node_id, status |
Forge, CI and waits
| Type | When | Key fields |
|---|---|---|
mr_opened | The merge request was opened. | number, url |
mr_labels_set | A worker labeled the merge request. | labels |
external_wait_started | A wait on CI or a review began. | task, node_id, kind, timeout_s, deadline |
external_wait_observed | Kraft checked the wait. | task, observation, state, result, next_observation_at |
external_wait_rebounded | A policy change moved the wait's limits. | task, timeout_s, deadline |
external_wait_ended | The wait settled, timed out or errored. | task, outcome, result, observations |
ci_infra_exhausted | CI failed for its own reasons, and retries did not help. | node_id, reason |
ci_run_abandoned | A cancelled CI run has no successor. | node_id, reason |
ci_not_configured | The repo sets ci_checks: false, so a CI wait, before or after the merge, passed without checking. | node_id, task, repo |
automated_review_not_configured | The repo names no automated reviewer, so the wait settles without one. | node_id, task, repo |
automated_review_errored | The automated reviewer failed. | node_id, reason |
Sandbox
| Type | When | Key fields |
|---|---|---|
sandbox_egress_refused | The sandbox proxy refused a host. | session_id, host, port, phase, reason |
sandbox_oom_killed | The container was killed under its memory limit. confirmed is false when the runtime never flagged the kill and Kraft counted exit 137 under the limit instead. | session_id, memory, confirmed |
sandbox_branch_not_synced | The sandbox's branch did not reach the worktree. | session_id, branch, reason |
sandbox_kit_resolved | A task was dispatched under a Kit, once per item and Kit. skipped lists the optional capabilities Kraft does not enforce, and ignored those it accepts without applying (agent-sessions@1, a credential's oauth). | kit, manifest, skipped, ignored |
A workspace item records one sandbox_branch_not_synced for each repository whose branch did not move, and its reason names that repository.
Settings changed
| Type | When | Key fields |
|---|---|---|
chain_template_changed | The item moved to another chain before it started. | from, to |
attachments_changed | Its spec or plan changed before it started. | from, to |
agent_overrides_changed | Its model or effort override changed. | overrides |
node_overrides_changed | A node's override changed. | overrides |
policy_override_changed | Its policy override changed. | policy |
notification_failed | The notification webhook failed twice. | event_type, status, host, error |
Notifications
notify.yaml sends an HTTP POST to a webhook when an event you name
happens. Set it on the Settings screen, or edit the file:
enabled: true
url: https://hooks.example.com/abc123 # a secret; Kraft never shows it back
base_url: https://kraft.example.com # where links in the message point
events: [gate_requested, work_item_needs_human]
| Field | Default | Meaning |
|---|---|---|
enabled | false | Nothing is sent while this is off, or while url is empty. |
url | none | The webhook. |
base_url | the address the server listens on | The start of the link in each message. |
events | [gate_requested, work_item_needs_human] | Which event types send a message. Any type on this page works; Kraft does not check the names, so a typo sends nothing. |
The body is JSON:
{
"work_item_id": "<work-item-id>",
"title": "Fix the flaky login test",
"type": "gate_requested",
"gate": "spec_approval",
"url": "https://kraft.example.com/work-items/<work-item-id>"
}
gate is null for an event that is not about a gate.
- Kraft sends at most one message per work item every 10 seconds. A second matching event in that window sends nothing.
- A send that times out after 5 seconds, errors, or gets a status of 400 or
more is tried once more after 2 seconds. If that fails too, Kraft records a
notification_failedevent on the item. - Events that happen while the server is down send nothing when it comes back.
- The Settings screen's Send a test posts
"type": "test"with anullwork_item_idandgate.