Result file
Every agent and subprocess task gets KRAFT_RESULT_PATH in its environment:
the path of a JSON file it writes before it exits. Kraft reads the task's
status, findings, verdict, and usage back from it. The file is
run/results/<session id>.json under $KRAFT_HOME, and it stays after the
session ends.
Who must write it
- An agent task must. Kraft's instructions to every agent say what to
write. An agent that exits cleanly without a result file ends
failed. - An agent's turn must not leave a background job running. For a harness that
declares a log format, a turn that ends with one ends the task
failed, even when the file saysdone. Aneeds_contextstop keeps its status. The last line of the session log and abackground_jobs_abandonedevent name each job. - A subprocess task may. Without one, its exit code decides. See Subprocess tasks.
Examples
A review that found one problem:
{
"status": "done",
"session_summary_ref": ".engineering/sessions/2c1f.md",
"findings": [
{
"severity": "important",
"message": "The retry loop never backs off, so a down server gets hammered.",
"file": "src/client.py",
"line": 42,
"source_plugin": "code-review"
}
]
}
A fix-loop judge's ruling, with its reasoning in concerns:
{
"status": "done",
"verdict": "stop_needs_human",
"concerns": "The same two findings came back after three repairs."
}
A task that cannot go on without an answer:
{
"status": "needs_context",
"question": "Which of the two config loaders should the new key go through?"
}
Fields
The file is one JSON object. Kraft ignores keys it does not know.
| Field | Type | Read from | Meaning |
|---|---|---|---|
status | string | every task | How the task went. See status. |
concerns | string | every task | With done_with_concerns, why. From a judge or a gate review, the reasoning. |
question | string | every task | With needs_context, what the task needs to know. The item stops with it. |
session_summary_ref | string | every task | The session summary's path, relative to the repo root. |
findings | array | measuring tasks | What a review or check found. See findings. |
verdict | string | a fix-loop judge, a gate's auto_review | See verdict. |
suggested_action | object | the last task a node ran before it stopped | What a person should do next. See suggested_action. |
usage | object | every task | Token counts. See usage. |
cost_usd | number | every task | What the session cost. See usage. |
model | string | every task | The model that did the work. |
status
| Value | Set by | Meaning |
|---|---|---|
done | The task | The task finished. |
done_with_concerns | The task | The task finished but doubts the result; concerns says why. The node goes on. A repair in an on_failure recovery that ends this way stops the item and waits for you. |
failed | The task | The task could not finish. |
needs_context | The task | The task is missing information; question says what. The item stops and waits for you, with the question. |
config_error | Kraft | The task could not run as configured, for example a command that does not exist. |
capped_out | Kraft | A cap or a wait ran out. |
paused | Kraft | You paused the item. |
A task never writes the three Kraft statuses. A file that is not valid JSON, is
not UTF-8, has no status, or has a status that is not one of the four task
values ends the task failed, and that includes a file that names a Kraft
status. An empty file counts as no file. See
how the status is decided.
findings
Each entry is an object:
| Key | Required | Meaning |
|---|---|---|
severity | yes | critical, important or minor. Which ones open a fix loop is findings.loop_severities in policy.yaml. |
message | yes | What is wrong and why. |
source_plugin | yes | Who found it, such as code-review. |
file | no | The path, relative to the repo root. |
line | no | An integer. Not part of the finding's identity, so an approximate line is fine. |
same_as | no | The 16-character tag of a finding from an earlier round that this one restates. Only a tag the task was shown counts; any other is ignored. |
An entry missing a required key, or with an unknown severity, is dropped. The
findings_measured event counts dropped entries under dropped, per task.
A finding's identity across rounds is its same_as, or else a hash of
source_plugin, file and message. The fix loop's stuck check and the
judge's history read it. See Fix loop and judge.
verdict
| Written by | Values | Anything else |
|---|---|---|
A fix loop's judge | continue, stop_needs_human, stop_downgrade. See The judge. | continue |
A gate's auto_review | approve, reject, fixed (it repaired the worktree and the node runs again), undecided. reject and fixed need concerns, which becomes the steer for the re-run. | undecided: the gate waits for you |
A verdict from a task that ended failed or needs_context does not count,
whatever it says: Kraft reads it as the "Anything else" value in the table.
suggested_action
A recovery that concludes no repair can help ends failed and writes a
suggested_action:
{"status": "failed", "suggested_action": {"action": "retry", "reason": "the registry was down"}}
action is skip, retry or abandon, and reason is one sentence. Any other
action is ignored. When the item stops, kraft view show prints the reason and
the one command that takes the action, in this form:
retry: <reason>
run: kraft item retry <ID>
The command is kraft item skip ID, kraft item retry ID, or
kraft item abandon --yes ID. With no reason, the first line is the action
alone.
A stop caused by infrastructure always suggests retry, with no recovery involved.
Infrastructure here means a CI pipeline that failed for the forge's own reasons,
an automated reviewer that errored, or a cancelled run with no successor coming.
usage
Kraft reads a session's tokens and cost from the result file first, and from the agent's log after that when the harness declares a log format. A harness that reports usage only through the result file is asked to write it there.
| Key | Meaning |
|---|---|
usage.input_tokens | Input tokens used. |
usage.output_tokens | Output tokens used. |
usage.cache_creation_input_tokens | Cache-write tokens, counted as billed input. |
usage.cache_read_input_tokens | Cache-read tokens, counted as billed input. |
cost_usd | What the session cost, at the top level of the file, not inside usage. Also read as total_cost_usd. Leave it out when you do not know it, rather than guess. |
model | The model that did the work, at the top level of the file. |
Related
- Subprocess tasks: how a command's exit code and result file become a status.
- Fix loop and judge: how findings and a judge's verdict drive the loop.
- Events:
findings_measuredand the other events a result file produces.