Tasks, ownership and worktrees

Work in order with st task and --deps, what each worker may change with --owns, a git worktree per worker with --worktree, and the same task given to several workers with --fan.

Several workers at once need two things settled: what goes first, and who changes which files. st keeps both, so the coordinator does not have to hold them in its head. Usually you just say it ("schema first, then the API, then the UI; keep them out of each other's folders") and the coordinator runs these commands.

Work in order (st task)

Some work can start only once other work is done. Write that order down in st and st keeps it. st 0.3.82 or later.

st task create schema --task schema.md --agent codex  --owns "db/**"
st task create api    --task api.md    --agent claude --deps schema --owns "internal/api/**"
st task create ui     --task ui.md     --agent claude --deps api,schema --owns "web/**"
st task list               # each task and where it stands
st task start schema       # refused while a --deps task is not done
st task wait               # returns when the next task can start
  • st task create <name> --task <file|-> takes --deps a,b, --agent claude|codex|opencode|pi, --owns and --worktree; the worker that takes the task gets them.
  • --deps names only tasks that exist already, so tasks can never wait for each other in a loop.
  • st task start <name> hands the task to a new worker. It takes --agent (when the task has none), --split and --cwd like st worker start.
  • st task list --ready shows only the tasks that can start.

A task is in one of five states:

StateMeans
waitinga task it depends on is not done
readyit can start
runninga worker has it
doneits worker reported done
blockedits worker reported blocked:; tasks that depend on it keep waiting

st task wait exits:

CodeWhen
0a task is ready, or all are done
3a worker is asking
4nothing can move because of a blocked task
2timeout (--timeout, 15 minutes by default)

The coordinator repeats st task wait and st task start until all are done.

Paths a worker may change (--owns)

Workers share one folder by default. Two editing the same file at once overwrite each other. Give each worker the paths it may change and st stops edits outside them before they happen. No git worktree needed, and it works in projects that do not use git. st 0.3.82 or later.

st worker start --agent claude --task auth.md --owns "src/auth/**,docs/auth.md"
st worker own %15 "web/**"          # a worker already running, adopted ones too
st worker own %15 --clear           # no limit again
  • Paths are comma separated, relative to the worker's folder or absolute. * stays within a directory, ** goes into subdirectories, ? is one character, and a path without wildcards is that file or everything under that directory.
  • When a Claude worker tries to Edit or Write a file outside them, its hook refuses before the change and says why. The worker does not work around it: it says in its answer what it needed to change and asks the coordinator.
  • Files outside the folder (scratch files and the like) are not held back.
  • Not checked yet: files written by shell commands, and Codex workers. Put the ownership in their task file too.

A worktree per worker (--worktree)

Each worker can have its own worktree and branch, so its files, builds and tests never mix with anyone else's. st 0.3.83 or later; in a folder without git from st 0.3.107 (see below).

st worker start --agent claude --task api.md --worktree
st task create api --task api.md --deps schema --worktree
st worker diff %15 [--stat]        # what it changed, committed or not
st worker merge %15 [--commit]     # its branch into this checkout
st worker clean %15                # remove the worktree and branch once merged
  • The branch is st/<run>/<name>, from the commit the coordinator's checkout is on. The worktree goes outside the repository, in ~/.sophonz/worktrees/, and the worker works in the same subfolder the coordinator was in.
  • Files git does not keep but a worktree needs (.env.local and the like) are copied in when .worktreeinclude at the top of the repository names them, one pattern a line.
  • Changes not committed are not merged: ask the worker to commit, or add --commit to commit them as they are. --squash stages the branch as one change for you to commit.
  • A conflict merges nothing. st undoes the merge and names the files. Send the worker something like "merge main into your branch, resolve the conflicts, commit", then merge again.
  • st worker clean refuses while there are changes not committed or not merged; --force removes them anyway, and --keep-branch keeps the branch.
  • st worker trees lists every worktree on the machine by its id (<run>/<worker>), including those whose worker is done or gone. diff, merge and clean take that id too.
  • st worker ask <id|tree> ["<question>"] opens a reading agent in its own window in the worktree, with the changes in its first prompt, to ask about them. It is never the worker itself.
  • A folder without git still gets worktrees. st snapshots the folder into a repository of its own in ~/.sophonz/shadow/ and makes the worktree from that snapshot; nothing is written into the folder. What a .gitignore there leaves out is left out, and so are dependencies and build output (node_modules/, target/, dist/ and the like). Past 20,000 files or 2 GB st refuses with the numbers; add --any-size to go ahead. The snapshot is removed with the last worktree made from it.

Several tries, pick one (--fan)

When the right answer is not obvious (a design direction, a hard bug), give the same task to several workers at once, compare, and pick one. st 0.3.84 or later.

st worker start --fan 3 --agent claude,codex --task fix.md   # each in its own worktree
st worker fan                    # side by side: what each changed, done or not
st worker pick %15               # merge that one; the others' windows and worktrees go
  • --fan takes 2 to 8 workers. Each starts from the same commit in a worktree of its own. A list in --agent is used in turn.
  • st worker fan [<id>] shows each fan's workers with how much each changed, whether it left changes not committed, and its summary.
  • st worker pick <id> takes --commit (commit what it left first) and --keep (merge, but leave the others). On a conflict nothing is merged and nothing removed.
  • You can pick on screen too. In the desktop app pick the worker in the agent graph and click Changes; in the web terminal click the Δ on the worker's window in the sidebar. The diff and the fan's other workers show side by side; Use this one picks.
  • A single worker with a worktree gets the same view, with Merge.
  • With --machine <name>, every worker of the fan starts on that machine. st worker pick and diff take workers on this machine only, for now.

The full flags are in the st command reference.