Children never commit (their prompts forbid git add/commit, overriding project instructions) so the commit is deterministic. At the phase commit point (00_phase.md final pass) the harness makes ONE atomic commit: the phase's code changes, the todo→complete file move, and the executor reports, together. - Scoped staging: a worktree snapshot taken at the phase's first unit (phase-sessions/dirty-<phase>, always kept) is subtracted, so the owner's pre-existing uncommitted work is left alone; runtime artifacts are never staged. The commit prints exactly what went in. - Verified: the moved phase file (and report, unless gitignored) must be in the index before committing. - Loud on failure: a commit failure prints the git error + hand-fix and stops the run; the phase stays complete, the miss is never swept into a later phase. - PHASE_COMMIT_SUBJECT (default 'phase: <phase>'); the executor's final report becomes the commit body; --no-gpg-sign always passed. - PHASE_COMMIT defaults to 1: completed phases are committed by default. Explicit PHASE_COMMIT=0 opts out with a loud warning that the phase is complete but uncommitted — a completed phase is never left uncommitted silently (that gap let four phases pile up uncommitted in brain_of_reese).
142 lines
7.9 KiB
Markdown
142 lines
7.9 KiB
Markdown
---
|
|
name: phased-execution
|
|
description: Runs the .agents/phases/ phased-execution pipeline (ported from opencode's next-phase/auto-phase commands). Use when the user asks to run the next task, run the next phase, run all phases, run the phase pipeline, or check pipeline status. Each task executes in a separate pi subprocess so this chat's context stays small.
|
|
---
|
|
|
|
# Phased Execution
|
|
|
|
Phase state lives in files, not chat:
|
|
|
|
- `.agents/PLAN.md` — master plan; LOCKED DECISIONS are binding
|
|
- `.agents/phases/todo/NN_name/` — a pending phase: `00_phase.md` (objective, dependencies, task index, testing & quality, completion criteria) plus `NN_task.md` task files (task sort order = execution order)
|
|
- `.agents/phases/todo/NN_name.md` — legacy single-file phase (still executable as one unit)
|
|
- `.agents/phases/complete/` — finished phases; mirrors the `todo/` layout (completed task files and the phase overview move here)
|
|
- `.agents/reports/<phase>/<task>.a<N>.{md,err,validate}` — per-task executor reports, stderr, and validation logs (legacy phases: `.agents/reports/<phase>.a<N>.*`)
|
|
- `.agents/validate.sh` — the pass/fail gate, run after **every task**
|
|
|
|
The unit of execution is the **task**: each task runs in a **separate pi
|
|
process** (fresh context) with bounded fixer retries. A task only moves to
|
|
`complete/` after the child exits 0, the child's stream ends with a clean
|
|
final report, **and** `.agents/validate.sh` passes. When all of a phase's tasks
|
|
are done, `00_phase.md` runs as the phase's **final pass** (remaining inline
|
|
work + completion criteria + phase-level verification); moving it completes
|
|
the phase and is the `PHASE_COMMIT` commit point (one atomic commit: code +
|
|
phase files + reports — children never commit; see Commits below). This chat only dispatches
|
|
and relays results — do not implement task code yourself; that is what the
|
|
subprocesses are for.
|
|
|
|
## Live display
|
|
|
|
While a phase runs, `scripts/progress.mjs` relays the child's JSON stream to
|
|
the terminal: tool calls, assistant text, `◐ thinking…` / `◑ thought for Ns`
|
|
indicators, yellow `⧉ compacting context` lines (these can take minutes —
|
|
not a hang), and provider auto-retry notices. If the child dies mid-flush and
|
|
the stream loses the final message, the report is recovered from the
|
|
child's session file (`.agents/phase-sessions/`), so a completed phase is
|
|
never lost to a truncated stream.
|
|
|
|
## Commands
|
|
|
|
(Resolve `scripts/` against this skill's directory.)
|
|
|
|
Run the next task, or a specific one:
|
|
|
|
```bash
|
|
bash scripts/run-task.sh # first pending task
|
|
bash scripts/run-task.sh 03_api/02_routes.md # specific task (warns if out of order)
|
|
```
|
|
|
|
Run one phase to completion — all of its remaining tasks, then its
|
|
`00_phase.md` final pass:
|
|
|
|
```bash
|
|
bash scripts/run-phase.sh # next phase with pending work
|
|
bash scripts/run-phase.sh 03_api # specific phase (warns if out of order)
|
|
```
|
|
|
|
Run the whole pipeline — every pending task, in order (phase order, then task
|
|
order), stopping at the first task that fails after all retries:
|
|
|
|
```bash
|
|
bash scripts/auto-phase.sh
|
|
```
|
|
|
|
Re-running `auto-phase.sh` after a failure continues where it stopped.
|
|
|
|
## After a run
|
|
|
|
Exit codes: `0` = success (or nothing to run), `1` = task/phase failed after all
|
|
attempts, `130`/`143` = interrupted (Ctrl+C / SIGTERM).
|
|
Failures always print a `✗ ERROR:` line with the last error output — if the
|
|
script's output looks like it ended abruptly, re-run it; the failed executor's
|
|
session is resumed automatically (retries continue the child's own session,
|
|
keeping its work).
|
|
|
|
Relay to the user: the task (or phase) name, its executor report (printed at
|
|
the end of the script output), and the validation outcome. On failure, point
|
|
the user at `.agents/reports/<phase>/<task>.a*.{md,err,validate}` (legacy
|
|
phases: `.agents/reports/<phase>.a*.*`) — the script also prints a ready to run
|
|
`pi --session … -c “…”` command to continue the failed session manually.
|
|
|
|
## Commits
|
|
|
|
Children never commit — their prompts forbid `git add`/`git commit`
|
|
(overriding any project “commit per phase” instruction), which is what keeps
|
|
the commit deterministic instead of intermittent. By default
|
|
(`PHASE_COMMIT=1`) the harness makes **ONE atomic commit per completed
|
|
phase** at its commit point: the phase's code changes, the
|
|
`todo/`→`complete/` file move, and the executor reports, together.
|
|
`PHASE_COMMIT=0` opts out — the phase is then left uncommitted with a loud
|
|
warning, never silently.
|
|
|
|
- **Scoped staging**: a snapshot of the worktree taken at the phase's first
|
|
unit (`.agents/phase-sessions/dirty-<phase>`, gitignored runtime storage)
|
|
is subtracted, so the owner's pre-existing uncommitted work is left alone;
|
|
pipeline runtime artifacts (`phase-sessions/`, `pipeline.log`) are never
|
|
staged. The commit output prints exactly what went in.
|
|
- **Verified**: the moved phase file (and the executor report, unless the
|
|
project gitignores reports) is checked against the index *before*
|
|
committing — the artifacts that used to go missing silently.
|
|
- **Loud on failure**: a commit failure prints a `✗ ERROR:` block with the
|
|
git error and the exact hand-fix command, and **stops the run**. The phase
|
|
stays complete (the work passed validation) — re-running will not
|
|
re-execute it, and a later phase's commit will not sweep the miss in: fix
|
|
the commit first, then re-run.
|
|
- Subject: `PHASE_COMMIT_SUBJECT` (default `phase: {{PHASE}}`); the
|
|
executor's final report becomes the commit body. `--no-gpg-sign` is always
|
|
passed.
|
|
|
|
## Configuration (environment variables)
|
|
|
|
| Var | Default | Meaning |
|
|
|-----|---------|---------|
|
|
| `MAX_FIX_ATTEMPTS` | `3` | Fixer retries per phase |
|
|
| `PHASE_MODEL` | session default | `--model` for child executors (e.g. `anthropic/claude-sonnet-4-5`) |
|
|
| `PHASE_THINKING` | session default | `--thinking` level for child executors |
|
|
| `PHASE_COMMIT` | `1` | `1` (default) = the harness commits each completed **phase** as one atomic commit (code changes + the file move + executor reports) when its `00_phase.md` final pass passes (legacy: when its file moves). `0` = opt out: the phase is left uncommitted with a loud warning. See Commits above |
|
|
| `PHASE_COMMIT_SUBJECT` | `phase: {{PHASE}}` | Commit subject template for `PHASE_COMMIT=1` (`{{PHASE}}` = the phase directory name); the executor's final report becomes the commit body |
|
|
| `PI_TRUST` | `0` | `1` = pass `--approve` (load project `.pi/` settings/skills into children) |
|
|
| `FRESH_FIX` | `0` | `1` = fixer retries start fresh instead of resuming the failed session |
|
|
| `QUIET` | `0` | `1` = suppress live progress display (reports are still written) |
|
|
| `PHASE_NOTIFY` | `1` | `0` = disable ntfy push notifications. When on and `~/.env/pi-ntfy.env` exists, a notification is sent after every unit completes (`task` for each task file, `phase` for a phase's final pass) |
|
|
|
|
## Setup notes
|
|
|
|
- First run creates `.agents/validate.sh` from `assets/validate.sh` if missing.
|
|
It must be adapted to the project's real checks — it is the authoritative
|
|
quality gate.
|
|
- Phase directories are created by the `phase-authoring` skill or the
|
|
`/to-phase`, `/audit-create`, `/new-project`, and `/new-python-*` prompt
|
|
templates.
|
|
- Legacy flat phase files (`todo/NN_name.md`) are still executed as a single
|
|
unit; `phase-authoring`'s `migrate-phases-to-tasks.sh` converts them to the
|
|
directory layout (the phase's final pass then picks up any inline task list).
|
|
- Child executor sessions are kept in `.agents/phase-sessions/` (plus
|
|
`pipeline.log` in `.agents/`); if the project is versioned, git-ignore those
|
|
runtime artifacts only — `.agents/` itself is tracked and committed.
|
|
(The per-phase worktree snapshot `dirty-<phase>` is always kept there too.)
|
|
- If you keep non-skill markdown (e.g. a `README.md`) in a skills directory
|
|
(like `~/.pi/agent/skills/`), pi warns “description is required” for it.
|
|
Add a `.gitignore` in that directory listing the file — pi's skill scanner
|
|
honors `.gitignore`, so the file is skipped.
|