Subprocess tasks
A kind: subprocess task runs one command in the item's worktree. This page is
the contract between that command and Kraft. For the task's keys, see
Chain nodes.
The command
tasks:
lint:
kind: subprocess
command: sh -c 'npm run lint && npx tsc --noEmit'
Kraft does not run command through a shell. It splits the string into
arguments the way a shell would (Python's shlex.split) and runs the first one
directly. So &&, ||, pipes, redirects and $VAR do nothing on their own:
npm run lint && npx tsc runs npm with && as an argument. Wrap anything
that needs a shell in sh -c '...'. A command Kraft cannot split, such as one
with an unclosed quote, stops the item before it runs.
Where it runs
- Working directory. The item's worktree. A
scope: each_repositorytask on a workspace item runs once per selected repository, in that repository's checkout and with itsrepos.yamlentry, and stops at the first run that does not enddone. - Input. stdin is
/dev/null. - Output. stdout and stderr go to the session log. Read it with
kraft view logs ID --session SESSION. - Processes. The command gets a process group of its own. When it exits,
Kraft sends
SIGTERMto whatever it left running in that group, thenSIGKILL10 seconds later. - Sandbox. In a sandboxed repo, the command runs inside the sandbox like every other task, and its result file is mounted into the container.
Environment
The command gets the worker allowlist and the repo's env and
env_passthrough (see
Environment variables), plus:
| Variable | Value |
|---|---|
KRAFT_RESULT_PATH | The session's result file. Writing it is optional. |
PYTHONDONTWRITEBYTECODE | 1, so a fix loop's re-run never imports bytecode left over from the code before the fix. |
GIT_AUTHOR_NAME, GIT_AUTHOR_EMAIL, GIT_COMMITTER_NAME, GIT_COMMITTER_EMAIL | The server's own values when it has them, otherwise user.name and user.email from the repo's git config. The repo's env wins over them. |
Kraft does not set KRAFT_WORK_ITEM_ID or KRAFT_SESSION_ID for a subprocess
task.
How the status is decided
A result file, when there is one, wins over the exit code.
| What happened | Status |
|---|---|
The command wrote a result file with a known status | That status. See Result file. |
The result file has no status, an unknown one, or is not valid JSON | failed |
| No result file, or an empty one, and exit code 0 | done |
| No result file, or an empty one, and any other exit code | failed |
| The command or the working directory does not exist | config_error: the item stops for you, and no fix loop runs |
| The command cannot be split | config_error |
| A time cap ran out | capped_out: the process group is killed and the item stops for you |
| You paused the item | paused |
done and done_with_concerns let the node go on. failed sends the node to
its recovery and fix loop. needs_context
stops the item with the result file's question.
What a fix loop sees
A failing subprocess task that reports no findings becomes one critical
finding. It holds:
- the task's path and the command that ran;
- up to five lines from the end of its output: the lines marked
FAILED,Error:,Tracebackor✘if there are any, otherwise the last five; Confirm the fix with:and the command;- the
kraft view logs ID --session SESSIONcommand for the full log.
A command that writes findings into its result file reports those instead,
in the same shape a review agent uses. See
Result file.