task · git:20261019.2f3b8a7 · 2026-10-19 · sha256 5f13dd174a324e74
task git:20261019.2f3b8a7A
Immutable. This exact content never changes and is served at /api/v1/blob/5f13dd174a324e74.
---
name: task
description: This skill should be used when the user wants to "create a task", "start a new task", "split a task", "make this an epic", "mark a task in progress", "mark a task done", "complete a task", "run this task autonomously", "set a budget on this task", "raise the budget", "save what we decided", "bring the site up" for a task's worktree, or "prune the worktrees" of complete tasks. It makes a new task, moves an old one into the project's tasks folder, changes a task's state, splits one task into a parent with children, sets a task's run mode, sets the ceiling on its build, saves a mid-stage decision as a note, brings the worktree's own site up and down, or removes the worktrees of complete tasks.
argument-hint: "[create <name> | repair <old-task-folder> | start <task-id> | complete <task-id> | split <parent-task-id> | set-run-mode <task-id> <autonomous|light|interactive> [--stage <stage>]... | set-budget <task-id> [--dispatches <n>] [--minutes <n>] | save <task-id> | environment <task-id> <show|up|down|not-applicable> | prune [<task-id>]...]"
arguments: [action, target]
allowed-tools: Bash(${CLAUDE_PLUGIN_ROOT}/skills/task/scripts/task-actions.sh *), Bash(${CLAUDE_PLUGIN_ROOT}/skills/project/scripts/project-actions.sh read-worktree-base *), Bash(${CLAUDE_PLUGIN_ROOT}/skills/project/scripts/project-actions.sh set-worktree-base *), Bash(${CLAUDE_PLUGIN_ROOT}/skills/project/scripts/project-actions.sh --run-mode * set-worktree-base *), Bash(${CLAUDE_PLUGIN_ROOT}/skills/research/scripts/research-actions.sh distill *), Bash(${CLAUDE_PLUGIN_ROOT}/skills/design/scripts/design-actions.sh distill *), Agent, EnterWorktree
---
# Task
A task is one unit of work inside a project: a folder holding `task.json` (every field a script
reads) and `task.md` (the goal, in prose, that nothing parses). This skill makes one, moves an old
one into place, changes its state, splits it into a parent with children, or sets its run mode. It
does not run any of the six stages, and it does not pick which task is active: that is
`/aida:next`.
Every action below needs the active project's own folder (the one holding `project.json`, never
the code folder). Resolve that first, with the project skill, before using anything here.
`task-actions.sh` takes a text value from a file. Write the text with the Write tool in the
session's scratchpad folder. With no scratchpad, write it in the task folder, and delete it after
the call. Then pass `@/<absolute path>` as the value. Use this for a text that names a command, such
as git. Claude Code refuses a command whose quoted text names git when the session works in a
worktree.
## Determine the run mode
Look for a stated run mode on the task active in this conversation, when one is already active.
Found, and it says `autonomous` or `light`: act autonomously through this whole invocation, passing
`--run-mode autonomous` on every call to the script below. Anything else, including no active
task, such as the moment `create` itself runs: act interactively, the safe default. Decide this
once, at the start. A mode that names stages in brackets covers this call only when it names the
stage this call runs inside.
`task-actions.sh` never asks a question on its own. Every fact below that this skill would
otherwise ask for must be decided before the script runs; the script only writes what it is given
and reports what happened. Every action prints summary lines: `task-file:` with the path, `id:`,
`state:`, `parent:`, `children:` and `runMode:`, with the stages the mode covers in brackets when
it covers fewer than all. It never prints the record. Read the file at the
printed path when another field is needed.
## `create <name>`
Makes the task and nothing else: no contract, no interview, no stage. It takes a name and a goal.
Run it only when the person asked for this task in this conversation, by name or by a yes to an
offer. Another skill's hand-off carries that yes. Nothing here invents one.
**1. Name.** Ask what to call it, unless already said. Check it against `^[a-z0-9][a-z0-9-]*$`:
lowercase letters, digits and hyphens, starting with a letter or digit. The worktree folder takes
this name and becomes a hostname label, and DDEV lowercases and rewrites the rest. Existing
tasks keep their ids. On a mismatch, say so and ask again; the script refuses it too, so this
check only saves a round trip.
Autonomous with no name given: **halt.** A task cannot be filed without one.
**2. Goal.** Ask what this task is for, in the spirit of a user story: what someone wants to
accomplish and why, not a ticket. Write it back in one or two sentences and confirm with a plain
yes or no before writing anything. Autonomous with no goal given or implied by the conversation:
**halt.** A task with no stated goal is not a record of anything.
**3. Write it.** First the worktree base. A project made before this setting may record none. Run:
```
"${CLAUDE_PLUGIN_ROOT}"/skills/project/scripts/project-actions.sh read-worktree-base "<projectPath>"
```
Exit 0: a base is recorded. Use it silently; this is never asked a second time. Exit 1: it
printed the default. Interactive: ask once, "Where should task worktrees go? Default:
`<printed folder>`." An empty answer passes no folder, which records the default. A typed
absolute path passes that path. Record it:
```
"${CLAUDE_PLUGIN_ROOT}"/skills/project/scripts/project-actions.sh --run-mode <interactive|autonomous> \
set-worktree-base "<projectPath>" ["<folder>"]
```
Exit 3 refuses the folder, for the reason it prints: ask again. Autonomous: do not ask, and
record nothing. The tree goes in the default folder. Say so; the next attended create asks. This
is asked here
because the first tree is about to be made, and a tree once made stays where it is.
Then run the create, with the run mode set as decided above. Add `--after <task-id>` when the
person said this task builds on another task of this project:
```
"${CLAUDE_PLUGIN_ROOT}"/skills/task/scripts/task-actions.sh --run-mode <interactive|autonomous> \
create --project "<projectPath>" --name "<name>" [--after <task-id> [--in-tree]] -- <goal...>
```
It writes the folder, `task.json` with `state: "new"`, and `task.md` with the goal under `## Goal`.
It then makes the task's own git worktree in one folder per repository beside the code path, at
`<parent of codePath>/<slug of the code folder>.worktrees/<slug of the code folder>-<name>`.
The worktree base recorded above takes the place of `<parent of codePath>`.
The tree is on the branch `feature/<name>`. The script records both in `task.json` and commits. The folder name is the
slug of the code folder plus the task name, so a site name is predictable and unique. The tree is
outside the code path for one reason. A nested worktree is invisible to a tool that registers
projects by folder, and DDEV hands it to the parent project. A tree that `task.json` already
records on disk stays where it is.
The tree is cut from HEAD of the folder this call runs in, when that folder is in the code
repository. So a task made from inside another task's tree starts from that task's work. From
anywhere else the tree is cut from HEAD of the code path. With `--after`, the tree is cut from
that task's branch, and `task.json` records `after`. Use it for a chain of tasks made up front,
because a tree cut now from trunk holds none of the earlier builds. While that task's build is
unfinished, no tree is made: `worktree:` reads `none` and `after-build:` reads `unfinished`. Skip
steps 4 and 5 then. `start` makes the tree once that build is finished.
Add `--in-tree` with `--after` when the person runs the chain one task after another. The task
then takes over that task's tree, on the new branch `feature/<name>` from its tip. So the chain
pays one checkout and one dependency sync. That task's review runs in its tree, so the take-over
waits until that review has closed. While it is open, `create` makes no tree and prints `in-tree:`.
Skip steps 4 and 5 then. `start` takes the tree over, or cuts a new tree if the review is still
open, and says which. That tree must hold no uncommitted change. Both tasks
then record one tree, and the branch checked out there says which task builds in it now. A stage
action of the other task exits 3, and its message names the `git switch` that gives it the tree.
When no tree holds that task's branch, a new tree is cut as with `--after` alone.
Show the whole output. Exit code 3 has five causes. The name collided with an existing task,
or it failed the name rule. `--after` named a task this project does not hold, or `--in-tree`
came without `--after`. Or the worktree could not be made. In the last case the folder is removed. Say what it printed. For a name, ask for a different one. For the worktree, name the
repair the message gives and stop.
**4. Enter the tree.** Every stage action of this task runs inside that worktree, and refuses
from anywhere else. The `worktree:` line names it. Call the `EnterWorktree` tool with that path,
so scoping in this same window is not refused. The tool asks for approval, because the path is
outside `.claude/worktrees/`; that is expected. From a window outside the code repository the
tool refuses on first entry, and from a session already inside a worktree it refuses too. A
person may also decline the prompt, which is not a refusal. After either outcome, do not call the
tool again. Take the other route, and read `/aida:next` for the whole rule. The scripts stay
reachable without entry: start every Bash
call with `cd <path> &&`. Every call needs it, because the shell's directory resets between
calls. The `cd` part asks for approval, because the tree sits outside this window's directory.
The exit 79 message names that form too. The way in is `/cd <path>`, typed by the person: it
moves this session into the tree and keeps the conversation (Claude Code 2.1.169 or later).
Print the path and say so. Go on to step 5 either way.
**5. Offer the site.** Runs here after step 4, and again at `start` whenever the task record
still has no `environment`, whoever called `start`. A worktree has the branch's files and no
site, so a review or a baseline taken there would capture the served checkout instead. Dispatch
`catalog-identifier` once per point, as the surfaces skill does. The role reads any word that is
not on a `point:` line as a framework. The first dispatch has the line
`point: worktree-environment`, then every framework the project records and the project folder.
When the project record has `surfaces.e2e.enabled`, dispatch the role again with
`point: e2e-setup` and the same other lines. When it has `surfaces.visualRegression.enabled`,
dispatch it again with `point: visual-regression`. **Light:** send all these points in one
dispatch, one `point:` line each. `up` then installs that harness in the tree.
Pass the worktree-environment answer as `--recipe <framework>=<path>` or
`--lookup-failed <framework>=<word>`, one flag per framework. Pass each setup recipe as
`--setup-recipe <kind>=<path>`, where the kind is `e2e` or `visual-regression`. A setup lookup
that returns no path gives no flag, and `up` says that harness is not installed. Then run
`environment <name> show`.
The word is `no-recipe`, `listing-unreachable` or `fetch-failed`; the script refuses any other.
`not-applicable` from `show` means no framework has a recipe: record it with
`environment <name> not-applicable -- <that reason>`, and say once that this worktree has files
and no site. `show` runs the precondition checks and exits 3 when one fails. A failing check
means the site cannot come up, so make no offer. Show the check's output and the remedy it names.
When the remedy says to commit, give the `environment:` lines that name the branches. Record
nothing, so the offer comes again at `start`. Otherwise, interactive: show the commands and the
prose, and ask once whether to
bring the site up now. A yes runs `environment <name> up` with the same flags. A no is recorded
too, with the person's reason, through the same `not-applicable` call, so nothing offers again.
Say `up` with the same flags still brings it up later. Autonomous: never bring it up, record
nothing, and say once that the offer waits for a person.
## `environment <task-id> <show|up|down|not-applicable>`
The worktree's own running site, from the framework's `worktree-environment` recipe. The recipe
holds the commands; this plugin holds none. `show` and `up` take the recipe flags step 5 names,
and the `--setup-recipe` flags. `down` takes none: it reads the recipe path the record holds.
`not-applicable` takes the person's reason after `--`.
```
"${CLAUDE_PLUGIN_ROOT}"/skills/task/scripts/task-actions.sh --run-mode <interactive|autonomous> \
environment --project "<projectPath>" "<task-id>" <show|up|down> <recipe flags>
"${CLAUDE_PLUGIN_ROOT}"/skills/task/scripts/task-actions.sh --run-mode <interactive|autonomous> \
environment --project "<projectPath>" "<task-id>" not-applicable -- <reason...>
```
`not-applicable` writes `environment` as the reason alone, and commits. It is a person's answer,
so it refuses unattended at 70. `up` replaces it; `down` with it recorded says nothing was up.
`show` prints the recipe path, the preconditions prose and the build-in-place prose. It prints
the token, bring-up, address and tear-down commands with `{codePath}` filled, and runs none of
them. It prints the paths of the `## Files` blocks, the files `up` writes, and one
`precondition:` line per `## Preconditions` command `up` runs.
`show` runs each `## Preconditions` line the way `up` does, with the same code. It removes the
files it wrote for the check and puts back the ones it replaced. So it commits nothing and leaves
the tree as it found it. A failing
check exits 3 and prints what `up` prints. A worktree not on disk prints
`precondition check: not run`, because only `up` makes a tree again.
A recipe with no bring-up block, or no address block, exits 3 from `show` too, so its exit code
says what `up` would do.
When the recipe has a `## Status` block and the worktree is on disk, `show` runs its one line
there. Exit 0 prints `status: up`. Any other exit prints `status: down` and the line's first
output. The line may run a script from `## Files`. The script is written where absent, and taken
out after the line. The implementation step runs the same line before an order's verify lines.
`up` is a person's yes, so it refuses unattended at 70. It runs in the task's worktree, with each
command as it ran and that command's own output in `records/environment-up.txt`, in this order,
and one line in that record marking the address command, so `down` can find that command's output
later.
A site the worktree already runs is adopted. This applies when `task.json` records no address and
no marker, and the recipe's `## Status` line says the site is up. Then `up` writes and commits the
`## Files` blocks, runs the address command with its `root:` check, and records the address with
`adopted: true`. The root check runs before the commit, so a site in another tree leaves nothing.
It runs no precondition, token or bring-up line, because the seed would replace that site's database.
It records no named file in `worktree.recipeChanges`, and prints `environment: adopted` in place of
`environment: coming-up`. It also says that no bring-up took a seed. A configuration gate then
restores the seed the worktree already holds, whose age is unknown. Say so to the person.
`show` then prints `precondition check: not run`.
First it writes each `## Files` block absent from the worktree. A file that holds the block of an
earlier version of the recipe is replaced. Any other file with other content refuses at 3. The
earlier versions are the ones the navigator store holds, so nothing is fetched. Then it runs each
`## Preconditions` line, after the files because the check is a script the recipe ships. A
failing line stops at 3, prints its output, removes the files this run wrote, puts back the ones
it replaced, and commits nothing. When a line ran and failed, it also names the task branch and the branch the worktree
was cut from. An older task names the branch the code path is on now, and says so. A failed
commit, or an interrupt before the commit, also removes the written files, their index entries
and the output file, and says so. Then it commits the written and replaced files alone, so other changed or staged work is
never taken in. Then it records in `worktree.recipeChanges` each `## Files` path, and each file the
`## Preconditions` prose names that the branch changed, with the content each holds. It skips a
file git tracks neither at HEAD nor where the branch started. No order owns
them, so the owned-files checks in the build and in review set them aside until an edit changes
that content. A site brought up before this record existed has none. Run `up` again to write it:
files already committed are not committed again. Then
each `## Tokens` command, whose first output line is the token's value. A token command that
prints nothing or fails refuses at 4 by the token's name. Then it writes the marker into
`environment`: `state: coming-up`, the recipe, and the time. The record names the site before the
site exists, so a failure during the bring-up leaves a site `down` can still find. The marker
keeps every other field the record held, so a second `up` over a site that is up does not drop
that site's address while the bring-up runs again. Then the
bring-up lines before the
`## Address` heading. Then one line marking the address command in the record, so `down` can find
that command's output later. Then the address command, whose output is `key: value` lines.
`address:` is required, and every other key is a token for the later lines and for the
tear-down. A `root:`
line that is not the worktree stops at 3 before the later lines: the environment resolved to
another tree, and the marker stays for `down`. Then it completes the record, in place of the
marker: the address, the recipe, when, and the other address keys. Then the bring-up lines after
the heading. Then, for each surfaces kind the
project has on, the `## Install` lines of the setup recipe given as `--setup-recipe`. It
commits nothing after that; what the install left uncommitted is named and stays for the task's
own commit. With no path for a kind it says so and goes on, and the
harness is the person's next step. A line still holding an unfilled `{token}` stops at 3 and
names it. A failing line stops at 4 with a `first:` line. Show that line; do not bring the site
up by hand. Any refusal after the marker leaves the marker, so run `down` before `up` again.
It prints `environment: coming-up`, then `address:`. Running it twice is safe: the recipe
promises every step runs again cleanly.
`down` runs the tear-down lines, each one and its output to `records/environment-down.txt`, and removes
`environment` from `task.json`. It runs unattended too: tearing a copy down loses nothing. An
adopted site is no copy, so `down` runs no tear-down line for it. It keeps `adopted` and the filled
tear-down lines, commits, and says the site stays up in the worktree. Prune refuses a tree whose
record says `adopted`. A tree another task holds stays, and that record stays with it. A second
`down` runs the recipe's `## Status` line in the worktree. A site that is down loses the mark, so
prune can remove the tree. A site still up is refused at 3 with the tear-down lines. With no
`## Status` line, only a person can say the site is gone, so it refuses unattended at 70. With
nothing up it says so and exits 0. It reads the recipe from the marker as it reads it from a
finished record, so a site that was coming up is torn down the same way. A marker with no address
key holds none of the other address keys either. `up` marks the address command in
`records/environment-up.txt` before it runs it. `down` fills a tear-down token from the output
under that mark, and from nothing else in that file. No mark means the address command never ran,
so `down` reads nothing there. A token nothing fills stops it at 3 and names that token.
Run it before the worktree is removed, or the framework keeps
an orphaned registry entry; the completion body names it when a site is up or coming up. Review
and `baseline` read `environment.address` before asking for a base URL.
The files in `worktree.recipeChanges` are for the worktree's own site, and trunk must not
receive them. Completion takes the site down. Then it commits each file back to its content
where the branch started. A DDEV worktree then holds the main checkout's `name:` line again. So a
site command there can reach the main checkout's site. The content decides, not who made the
commit or its subject. The test reads the changed files that existed where the branch started.
When HEAD or the working tree holds one of them at that content again, `up` and `down` refuse at
3. They name the latest commit on those files. `show` then prints `status: not run`. A `## Files`
script the branch dropped does not count, because it names no site. To work on the site again,
put the change back first. The test can refuse wrongly: a file the preconditions prose only names,
changed before `up` and then changed back, counts too. It also misses a recipe that names the
site in a file absent where the branch started.
## `prune [<task-id>]...`
The worktrees of complete tasks. A worktree kept after completion costs disk and a DDEV project
each, and a tree removed too early loses uncommitted work. So this lists first, asks per tree,
and removes only what a person named. Run it from the main checkout, never from inside a tree
it may remove.
```
"${CLAUDE_PLUGIN_ROOT}"/skills/task/scripts/task-actions.sh --run-mode <interactive|autonomous> \
prune --project "<projectPath>" [--all] [<task-id>]...
```
**1. List.** With no id the script prints one `id:` line per complete task that records a
worktree. The line holds the path, the branch, and whether the branch is merged into the code
path's current branch. It also says whether the tree is on disk and whether a site is up,
which reads `coming-up` for a tree whose bring-up did not finish.
`prune: none` means nothing to remove. Show the lines. Autonomous: this is the whole action.
Say once that a tree goes only on a person's yes, and stop. The script refuses an id unattended
at 70.
**2. Ask per tree.** For each listed tree ask a plain yes or no, one at a time, with the line.
Never ask once for all of them. An unmerged branch is a reason to say so before asking: the
tree goes, the branch stays.
**3. Remove.** Run the action once with every id that got a yes, in the order given, or with
`--all` when every tree got one. For each tree the script tears the site down when one is up,
then removes the tree. It removes the group folder when it is empty, never the worktree base
itself. It deletes the branch when it is merged. It clears `worktree` and
`environment` from `task.json` and commits. It prints one `pruned:` line per tree naming what
happened to the branch. A tree that another task also records stays, and its line says `kept`:
a later task made with `--in-tree` builds in it. Show them. Exit 3 names the tree it stopped at and why. The task is not
complete, git refused a tree with uncommitted changes, or the tear-down failed. Nothing after
that tree was touched, and nothing is ever forced. Say what it printed and stop.
## `repair <old-task-folder>`
Moves an old task into the project's own `tasks/` folder, the first time it is opened. This is the
only move in the whole skill: everything else here changes a field, never a location.
Given the path to a task folder still sitting under `implementation_process/in_progress/` or
`implementation_process/completed/` inside the project folder, or under a repaired parent's own
`in_progress/` or `completed/` in `tasks/`, run:
```
"${CLAUDE_PLUGIN_ROOT}"/skills/task/scripts/task-actions.sh repair --project "<projectPath>" "<old-task-folder>"
```
No confirmation is needed: nothing here is destructive. The script refuses outright when the
destination already exists. It reads the goal under `## Goal`, or under `## Problem` when there
is no `## Goal`, the heading some version 5 records used. With neither it refuses, naming both,
rather than moving something it cannot verify. It prints `goal-heading:` with the one it read.
It reads the goal, and the parent and children when an old header carries them, back from the
new location before it reports success. Show the whole output either way.
A version 5 parent task holds its children under `in_progress/` and `completed/` inside its own
folder. The same call moves each one to `tasks/<child id>/`, sets the child's `parent` to that
task and adds it to the task's `children`. It prints one `MOVED:` line per child. A child it
cannot move, because its goal heading is missing or its destination exists, stays where it sits
with a `LEFT:` line naming why. The parent's own repair is never refused for a child. Fix the
child there, then run this action on that path. It moves to `tasks/<child id>/`, takes the
parent from the folder it sat in, and joins that parent's `children`.
Inside the new folder it renames `alignment.md`, `research.md`, `architecture.md` and `research/`
to `alignment.v5.md`, `research.v5.md`, `architecture.v5.md` and `research.v5/`, each when
present. It prints one `KEPT:` line per rename. Version 6 writes under those names, and the old
files are the input the first run of each stage reads. It refuses, before moving anything, when
a `.v5` name already exists in the old folder.
Which old tasks still need this is `/aida:next`'s job: it lists them as `kind: legacy` and runs
this action on the one it loads.
## `start <task-id>`
A task becomes in progress the moment a stage first writes an artifact into it. Each stage's own
script calls this once, before its first write: scope `init`, research `start`, design `start`,
implement `start` and review `checks`. It skips the call when the task is already in progress. So
it is not usually a person typing a command. By hand, run:
```
"${CLAUDE_PLUGIN_ROOT}"/skills/task/scripts/task-actions.sh --run-mode <interactive|autonomous> \
start --project "<projectPath>" "<task-id>" -- <why...>
```
Already `in_progress`: prints `UNCHANGED` and does nothing further. Already `complete`: refused,
since a completed task is not reopened here. Otherwise it writes the new state, commits, and runs
the task check. Show the whole output. The check reports and never repairs, so a finding here is
the one thing to repair now, before the stage writes anything.
A task made with `--after` starts only when the task it builds on has finished its build. That
task then holds `implementation/finished.json`, or reads complete. Otherwise `start` prints
`REFUSED` and exits 1, and the stage that called it writes nothing. Say which task to build first.
When it starts, `start` cuts the tree from that task's branch, and `worktree:` names the tree.
That task may be complete with its branch merged and pruned. The tree is then cut by the base
rule above, because that work is on trunk. A tree that cannot be cut exits 3, and no state is
written.
Enter it as `create` step 4 says.
When the output holds `environment: none`, run `create`'s step 5 now, whoever called `start`: a
person by hand, or a stage's script through scope's `init`. A stage's script passes that line
through. Unattended, the line says the offer waits for a person, and nothing is recorded.
When the output holds `environment: coming-up`, a bring-up did not finish and a site may be
running. Run `environment <task-id> down` before any stage runs. Then offer the bring-up again.
## `complete <task-id>`
Writes `state: complete`. This action is the one writer of that state, and the completion skill
calls it last. A person runs the completion skill, not this action: completion reads the review
verdict, records on what grounds the task closed, and writes the pull request body. This action
records none of that, so a task closed here has a state and no grounds.
The completion skill passes the summary the person gave, after `--`. The script refuses an empty
one outright. By hand, the call is:
```
"${CLAUDE_PLUGIN_ROOT}"/skills/task/scripts/task-actions.sh --run-mode <interactive|autonomous> \
complete --project "<projectPath>" "<task-id>" -- <summary...>
```
It sets the state to `complete`, appends a dated `## Completed` section holding the summary to
`task.md`, and commits this task's folder, `tasks/<id>`, alone. Every task action commits that
way, never `tasks/` whole, so another task's uncommitted file is never taken. That commit carries
the completion record and the body when completion called it. Already complete: prints `UNCHANGED`. Show the
whole output.
## `split <parent-task-id>`
Turns one task into a parent with two or more children. Nothing moves, so there is no temporary
build, no atomic swap and no rollback copy: version 5 needed all of that because splitting moved
folders; here nothing does.
Every fact this needs must already be decided before calling it: which children, each child's own
goal, and which criteria hand down to it. This skill never derives them. The research skill's
split advisor recommends the children and their criteria after research closes. That skill's
`split-read` action checks that every criterion is claimed once, before this action runs. This only performs
the mechanical split.
Each child takes the parent's `after`, so it waits for the same build, and gets its tree as
`create` gives one. The two-level limit stays: a task that already has a parent cannot be split again, and a child
always lives in the same project as its parent, never another one. Run:
```
"${CLAUDE_PLUGIN_ROOT}"/skills/task/scripts/task-actions.sh --run-mode <interactive|autonomous> \
split --project "<projectPath>" "<parent-task-id>" \
--child "<child-id-1>" --goal "<goal-1>" [--criterion "<text>"]... \
--child "<child-id-2>" --goal "<goal-2>" [--criterion "<text>"]...
```
Repeat `--child ... --goal ... [--criterion ...]` once per child, at least twice. It creates each
child's folder and `task.json` with `parent` set to the split task's own id, writes each child's
goal and any handed-down criteria into its `task.md`, makes each child's own worktree the way
`create` does, adds every new id to the parent's own
`children` list, reads all of that back, and commits everything together. Exit code 1 means it
stopped before writing anything: `NOT FOUND` says the named task does not exist, `REFUSED` says
the two-level limit stopped it. Say which and stop. Show the whole output otherwise.
## `set-run-mode <task-id> <autonomous|light|interactive> [--stage <stage>]...`
Run mode is written only when a person explicitly asks for an autonomous or a light run on this task.
Nothing above asks about it on its own, and nothing here proposes it either. Only call this when
asked. Run:
```
"${CLAUDE_PLUGIN_ROOT}"/skills/task/scripts/task-actions.sh --run-mode <interactive|autonomous> \
set-run-mode --project "<projectPath>" "<task-id>" <autonomous|light|interactive> [--stage <stage>]...
```
`autonomous` writes the field. `--stage`, repeatable, limits it to the stages named, one of
`scope`, `research`, `design`, `implement`, `review`, `completion`. A person who asks for the build
alone unattended passes `--stage implement`. No `--stage` covers every stage. `interactive`
removes the field and the stages: there is no `"interactive"` value to write, since the field's
absence already means that. Show the whole output.
`light` is an autonomous run over every stage that skips named steps, and it takes no `--stage`.
Each skip is logged in `COMPROMISES.md` in the task folder. The `path-script:` line says
whether end to end is on. Light keeps one script that walks the demo path, and review
fails the task without it. When end to end is off, say that a person sets it up with
`/aida:surfaces e2e` before the run. When the project declined end to end, light keeps no
such script.
A light or autonomous run still stops for a person, and a dispatched agent cannot answer for one.
So when one session coordinates agents that run the tasks, that session holds every person-only
call. An agent never runs `set-run-mode` and never writes a person's answer. It reports the stop
to the coordinator once and stops. The coordinator puts the stop to the person. Then it runs the
call itself. For an implement call, review's `close`, or `environment` `up`, `not-applicable` or
`down`, it can instead write the answer as a record. The exit 70 refusal prints that record.
The implement skill's `references/finish.md` says how.
When the mode covers review, two lines can follow, on light or autonomous. The `sign-off:` line
names the criteria a person answers, so an unattended review writes no verdict. The
`review-tools:` line names the review tools that preconditions found absent. Before
preconditions has run, it says that preconditions will check them. Read both to the person.
## `set-budget <task-id> [--dispatches <n>] [--minutes <n>]`
A budget is the ceiling on one implementation run, and a person sets it. Nothing above asks about
it, and nothing here proposes one. Only call this when asked. Run:
```
"${CLAUDE_PLUGIN_ROOT}"/skills/task/scripts/task-actions.sh --run-mode <interactive|autonomous> \
set-budget --project "<projectPath>" "<task-id>" [--dispatches <n>] [--minutes <n>]
```
`--dispatches` counts the roles implementation dispatches over the whole run. `--minutes` counts
wall-clock time from the ledger's own start. Either one alone is a ceiling, and both may be given.
Neither is refused. Each takes a whole number of 1 or more, and the action refuses anything else.
The task check does not read the schema's minimum, so a zero would pass it and halt the first
dispatch. A number this call does not name keeps the value it had. There is no action that removes
a budget.
The build halts the order it was about to dispatch when either number is reached, with `budget
spent`. To answer that halt a person raises the number here first. Then they run `grant-attempt` in
the implementation skill, which clears the halt segment. The order is the whole point. The spend is
recomputed from the ledger at every dispatch, so a grant before the raise brings the halt straight
back. Show the whole output.
## `save <task-id>`
A person stops mid-stage, and a decision this conversation made is in no record yet. This writes
it down for the next window. Only a person invokes it; nothing dispatches it.
The current stage is the `stage` the next skill's report prints for this task, the first whose
close record is absent.
That is scope without `records/scope-distill.json`. A light task is at scope only when
`alignment.json` also lacks `pluginVersion`, which scope's close stamps. Research is the stage without
`records/research-check.json` at `exitCode` 0, and design without `design-closed.json`. A task in state `new`, or whose stage has no
record on disk yet, skips the distiller: nothing exists to distill. The stage's first record is
`alignment.json` for scope, `research/*.json` for research, `design/*.json` for design. Say which
stage it would have been and that none exists, then go on to the list. Otherwise, when the
stage's sidecar `records/<stage>-distill.json` exists, run the stage's own `distill` action from
the task's worktree, the call started with `cd <worktree> &&`:
```
"${CLAUDE_PLUGIN_ROOT}"/skills/research/scripts/research-actions.sh distill "<task_folder>"
"${CLAUDE_PLUGIN_ROOT}"/skills/design/scripts/design-actions.sh distill "<task_folder>"
```
Scope needs no call, because its sidecar is its close record. The action prints `standsAlone:
stale` when the sidecar is older than the last change to the stage's records. A stale sidecar
counts as absent. So does one out of shape: the action sets it aside and exits 4. When none
exists, dispatch the `distiller` role
with the run mode, the task folder, the stage, and the stage's record paths, one per line. Then
read the sidecar it writes. A record path absent mid-stage is normal; the distiller names it as
a gap.
Name what this conversation decided that neither the sidecar's `decisions` nor the stage's own
files hold. Each is one sentence: what was decided and what it applies to. Show the list and ask
for a plain yes or no. Nothing is written before yes. Nothing to save is said in one line, and
`save` still runs with no text. That records the time, which clears the compaction hook's refusal.
Yes: run once per sentence, or once with all of them:
```
"${CLAUDE_PLUGIN_ROOT}"/skills/task/scripts/task-actions.sh --run-mode <interactive|autonomous> \
save --project "<projectPath>" "<task-id>" -- <text...>
```
It appends the text to `<task_folder>/notes/<date>.md` under a `## <UTC time>` heading. It
records `savedAt` in `task.json`, commits, and prints `savedAt:` and `note:` with the path. With no text
it writes no note and prints `savedAt:` only. When the stage had no record, it prints `distill:
none` naming the stage. Show the lines.
A note is never a stage record: the stage action that later records the same decision makes it
stale, and the record wins. The session-start hook names the newest note after `Stage:`, and
`/aida:next` lists its date, so the next window reads it before its first turn.