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 file keys.
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 and waits 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 and waits for you |
| You paused the item | paused |
What each status does to the node:
| Status | What the node does |
|---|---|
done, done_with_concerns | The node goes on. |
failed | The node goes to its recovery, then its fix loop. |
needs_context | The item stops and waits for you, 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 last 20 KB of its output: the first five lines
marked
FAILED,Error:,Tracebackor✘if there are any, otherwise the last five; timings, timestamps and URLs are removed and the whole is cut at 400 characters; 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.
Related
- Result file: the JSON a command may write, field by field.
- Fix loop and judge: what runs after a task fails.
- Chain file keys: the keys of every task kind.