feat: phases + tasks — per-task execution with phase directories
- A phase is now a directory: 00_phase.md (overview + task index) plus small, quick NN_task.md task files; complete/ mirrors todo/ - Task is the unit of execution: run-task.sh (one unit), run-phase.sh (phase to completion incl. 00_phase.md final pass), auto-phase.sh (all units in order); validate.sh gate runs after every task - PHASE_COMMIT commits at phase boundaries (00_phase.md / legacy file moves) - Legacy flat todo/NN_name.md files still execute as a single unit; new migrate-phases-to-tasks.sh converts them to the directory layout - phase-status.sh reports per-task state and the next unit - New task-template.md; phase templates now carry a Tasks index
This commit is contained in:
+32
-24
@@ -10,16 +10,17 @@ adopting an *existing* project into the phased-execution strategy. You audit
|
||||
the codebase, scaffold the `.agent/` planning structure, and write a phase
|
||||
roadmap that takes the project from its current state to the agreed target
|
||||
state. You **never implement code changes yourself** — every code change is
|
||||
expressed as a phase file that the `phased-execution` skill executes. And you
|
||||
expressed as a phase directory (a `00_phase.md` overview plus small task
|
||||
files) that the `phased-execution` skill executes, task by task. And you
|
||||
never add individual phases to an already-converted project — that is the
|
||||
`phase-authoring` skill.
|
||||
|
||||
Phase state lives in files, not chat:
|
||||
|
||||
- `.agent/PLAN.md` — master plan; **LOCKED DECISIONS** are binding
|
||||
- `.agent/phases/todo/NN_name.md` — pending phases (sort order = execution order)
|
||||
- `.agent/phases/complete/` — finished phases (read-only history)
|
||||
- `.agent/validate.sh` — the pass/fail gate for every phase (installed by the `phased-execution` skill on first run)
|
||||
- `.agent/phases/todo/NN_name/` — a pending phase: `00_phase.md` overview + `NN_task.md` task files (task sort order = execution order)
|
||||
- `.agent/phases/complete/` — finished phases, mirroring the todo/ layout (read-only history)
|
||||
- `.agent/validate.sh` — the pass/fail gate, run after every task (installed by the `phased-execution` skill on first run)
|
||||
|
||||
## Choosing the mode
|
||||
|
||||
@@ -86,7 +87,7 @@ Create (or merge into what already exists) with file tools:
|
||||
strategy, public-API principles, or UI/UX guidelines), and the phase roadmap.
|
||||
- **`AGENTS.md`** — the five base rules below, plus the domain additions.
|
||||
- **`.agent/phases/todo/`** — the roadmap from Phase 5.
|
||||
- **`.agent/phases/complete/`** — create the directory, leave it empty.
|
||||
- **`.agent/phases/complete/`** — create the directory, leave it empty (it mirrors `todo/` as phases complete).
|
||||
- **`.gitignore`** — add `.agent/` and `.agent/phase-sessions/` if missing.
|
||||
|
||||
**AGENTS.md base rules:**
|
||||
@@ -122,10 +123,12 @@ project's real checks (the foundation phase does that).
|
||||
|
||||
## Phase 5 — The Phase Roadmap
|
||||
|
||||
Write sequential phase files into `.agent/phases/todo/` using
|
||||
`assets/phase-template.md` (mirrors the `phase-authoring` template; keep the
|
||||
sections identical). Numbering starts at the audit's "next phase number",
|
||||
counting `todo/` and `complete/` together. Never reuse or collide a number.
|
||||
Write sequential phase **directories** into `.agent/phases/todo/` — each
|
||||
`NN_name/` holds a `00_phase.md` overview (from `assets/phase-template.md`)
|
||||
and its task files (from `assets/task-template.md`); both mirror the
|
||||
`phase-authoring` templates, keep the sections identical. Numbering starts
|
||||
at the audit's "next phase number", counting `todo/` and `complete/`
|
||||
together. Never reuse or collide a number.
|
||||
|
||||
Roadmap shape:
|
||||
|
||||
@@ -138,19 +141,23 @@ Roadmap shape:
|
||||
3. **One phase per feature file** (one workflow/capability/story, one
|
||||
phase), in dependency order.
|
||||
|
||||
Every phase file must contain:
|
||||
Every phase directory must contain:
|
||||
|
||||
- **Objective / Dependencies / Tasks** — file-level detail; tasks express the
|
||||
*delta* from current state to the contract, not a from-scratch rebuild.
|
||||
- **Testing & Quality (mandatory)** — unit and integration tests for all
|
||||
new/modified logic; coverage **>90%** on new/modified code; plus the domain
|
||||
block below.
|
||||
- **Completion Criteria** — observable checks (commands to run, endpoints to
|
||||
hit, artifacts to exist).
|
||||
- **Feature linkage** — the header line references the corresponding feature
|
||||
file (`.agent/workflows/…`, `.agent/features/…`, or `.agent/user_stories/…`).
|
||||
- **`00_phase.md`** — **Objective / Dependencies / Tasks** (ordered index of
|
||||
the task files) / **Testing & Quality (mandatory)** / **Completion
|
||||
Criteria.** Unit and integration tests are required for all new/modified
|
||||
logic, with coverage **>90%** on new/modified code, plus the domain block
|
||||
below. Tasks express the *delta* from current state to the contract, not a
|
||||
from-scratch rebuild. The header line references the corresponding feature
|
||||
file (`.agent/workflows/…`, `.agent/features/…`, or
|
||||
`.agent/user_stories/…`).
|
||||
- **Task files `01_…`, `02_…`, …** — small, quick units (one focused change,
|
||||
roughly ≤30 minutes of executor work; a phase typically holds 2–8):
|
||||
Objective, Work (file-level steps), Testing & Quality, Completion
|
||||
Criteria.
|
||||
|
||||
Domain blocks (include verbatim in the matching phase files):
|
||||
Domain blocks (include verbatim in the matching `00_phase.md`; the phase's
|
||||
final pass is where they are verified end-to-end):
|
||||
|
||||
- **CLI:** `## CLI Contract Execution Phase` — instruct the executing agent
|
||||
to run ONLY the specific CliRunner contract test module for that workflow
|
||||
@@ -175,8 +182,9 @@ Domain blocks (include verbatim in the matching phase files):
|
||||
- **Independent Viability:** each phase leaves the project functional and launchable.
|
||||
- **Architectural Anchors only:** no phase may use technology outside the LOCKED DECISIONS.
|
||||
- **No Regressions:** a phase must not alter the behavior of completed phases.
|
||||
- **Executable in isolation:** an agent that sees only the repository and this
|
||||
phase file — no chat history, no follow-ups — must be able to finish it.
|
||||
- **Executable in isolation:** an agent that sees only the repository, the phase
|
||||
overview, the completed task files, and one task file — no chat history, no
|
||||
follow-ups — must be able to finish that task.
|
||||
|
||||
## Phase 6 — Version Control & Hand-Off
|
||||
|
||||
@@ -192,8 +200,8 @@ Domain blocks (include verbatim in the matching phase files):
|
||||
Finish by summarizing the Architectural Anchors, the feature decomposition,
|
||||
and the phase list (number, name, one-line objective). Then hand off:
|
||||
|
||||
- **Execute:** the `phased-execution` skill — `run-phase.sh` for a single
|
||||
phase, `auto-phase.sh` for the full pipeline.
|
||||
- **Execute:** the `phased-execution` skill — `run-task.sh` for a single
|
||||
task, `run-phase.sh` for a phase, `auto-phase.sh` for the full pipeline.
|
||||
- **Extend:** the `phase-authoring` skill for individual additional phases.
|
||||
|
||||
## Strict Operational Rules
|
||||
|
||||
@@ -11,8 +11,8 @@
|
||||
{{or "— (none)"}}
|
||||
|
||||
## Tasks
|
||||
1. `{{path/to/file}}` — {{specific change, file-level detail}}
|
||||
2. `{{path/to/file}}` — {{specific change}}
|
||||
1. `01_{{short_name}}.md` — {{one-line summary}}
|
||||
2. `02_{{short_name}}.md` — {{one-line summary}}
|
||||
|
||||
## Testing & Quality
|
||||
- Unit/integration: {{tests required for all new/modified logic — name the behaviors to cover}}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
# Task {{NN}} — {{Short Title}}
|
||||
|
||||
**Phase:** `{{NN_phase}}` · **Feature:** `{{.agent/workflows/<name>.md | .agent/features/<name>.md | .agent/user_stories/<name>.md | "n/a"}}`
|
||||
|
||||
## Objective
|
||||
{{1–2 sentences: what this task delivers (the delta from current state to the contract)}}
|
||||
|
||||
## Work
|
||||
1. `{{path/to/file}}` — {{specific change, file-level detail}}
|
||||
2. `{{path/to/file}}` — {{specific change}}
|
||||
|
||||
## Testing & Quality
|
||||
- Unit/integration: {{tests required for this task's logic}}
|
||||
- Coverage: **>90%** on this task's new/modified code
|
||||
|
||||
## Completion Criteria
|
||||
- [ ] {{observable check: command to run / endpoint to hit / artifact to exist}}
|
||||
- [ ] full test suite green
|
||||
- [ ] no behavior change in completed work
|
||||
@@ -95,37 +95,41 @@ for d in workflows features user_stories; do
|
||||
done
|
||||
todo_dir="$root/.agent/phases/todo"
|
||||
complete_dir="$root/.agent/phases/complete"
|
||||
if [[ -d "$todo_dir" ]]; then
|
||||
echo " .agent/phases/todo:"
|
||||
any=0
|
||||
for f in "$todo_dir"/*.md; do
|
||||
if [[ -e "$f" ]]; then
|
||||
echo " $(basename "$f")"
|
||||
list_phases() {
|
||||
local dir="$1" entry n t any=0
|
||||
for entry in "$dir"/*; do
|
||||
if [[ ! -e "$entry" ]]; then continue; fi
|
||||
n="$(basename "$entry")"
|
||||
if [[ "$n" =~ ^[0-9] ]]; then
|
||||
if [[ -d "$entry" ]]; then
|
||||
t="$(ls -1 "$entry" 2>/dev/null | grep -cE '\.md$' || true)"
|
||||
echo " $n/ ($t file(s): 00_phase.md + tasks)"
|
||||
else
|
||||
echo " $n (legacy single-file phase)"
|
||||
fi
|
||||
any=1
|
||||
fi
|
||||
done
|
||||
if (( ! any )); then echo " (empty)"; fi
|
||||
}
|
||||
if [[ -d "$todo_dir" ]]; then
|
||||
echo " .agent/phases/todo:"
|
||||
list_phases "$todo_dir"
|
||||
a=1
|
||||
fi
|
||||
if [[ -d "$complete_dir" ]]; then
|
||||
echo " .agent/phases/complete:"
|
||||
any=0
|
||||
for f in "$complete_dir"/*.md; do
|
||||
if [[ -e "$f" ]]; then
|
||||
echo " $(basename "$f")"
|
||||
any=1
|
||||
fi
|
||||
done
|
||||
if (( ! any )); then echo " (empty)"; fi
|
||||
list_phases "$complete_dir"
|
||||
a=1
|
||||
fi
|
||||
if (( ! a )); then echo " (no .agent/ artifacts — full conversion needed)"; fi
|
||||
|
||||
max=0
|
||||
for d in "$todo_dir" "$complete_dir"; do
|
||||
for f in "$d"/*.md; do
|
||||
if [[ ! -e "$f" ]]; then continue; fi
|
||||
n="$(basename "$f" .md)"
|
||||
[[ -d "$d" ]] || continue
|
||||
for entry in "$d"/*; do
|
||||
if [[ ! -e "$entry" ]]; then continue; fi
|
||||
n="$(basename "$entry" .md)"
|
||||
if [[ "$n" =~ ^([0-9]+) ]]; then
|
||||
n=$((10#${BASH_REMATCH[1]}))
|
||||
if (( n > max )); then max=$n; fi
|
||||
|
||||
+55
-34
@@ -1,25 +1,33 @@
|
||||
---
|
||||
name: phase-authoring
|
||||
description: The required entry point for ANY new work on a .agent/phases/ project. Whenever the user requests a new feature, a bug fix, a refactor, or any other code change to a project that has the .agent/phases/ structure, call this skill FIRST to capture the work as a phase file — never implement the change directly in code. Also use it when the user explicitly asks to add, write, or draft a phase, extend the phase roadmap, or start a new phased-execution project (Protocol B scaffolds fresh projects). Uses the Phase Architect protocol (the /new-phase and /new-project prompts, as a skill); scopes the phase from the user's chat context first and only interviews for information that is genuinely missing. This skill writes phase files; the phased-execution skill runs them.
|
||||
description: The required entry point for ANY new work on a .agent/phases/ project. Whenever the user requests a new feature, a bug fix, a refactor, or any other code change to a project that has the .agent/phases/ structure, call this skill FIRST to capture the work as a phase directory with task files — never implement the change directly in code. Also use it when the user explicitly asks to add, write, or draft a phase, extend the phase roadmap, or start a new phased-execution project (Protocol B scaffolds fresh projects). Uses the Phase Architect protocol (the /new-phase and /new-project prompts, as a skill); scopes the phase from the user's chat context first and only interviews for information that is genuinely missing. This skill writes phase directories (a 00_phase.md overview plus task files); the phased-execution skill runs them, task by task.
|
||||
---
|
||||
|
||||
# Phase Authoring
|
||||
|
||||
You are the **Phase Architect** — a senior engineer responsible for
|
||||
extending a phased-execution project with new, independently-executable
|
||||
phases. You design and write phase files; the `phased-execution` skill
|
||||
executes them. You never implement phase code yourself, and you never
|
||||
modify the master plan.
|
||||
phases. You design and write phase directories — a `00_phase.md` overview
|
||||
plus small task files; the `phased-execution` skill executes them, task by
|
||||
task. You never implement phase code yourself, and you never modify the
|
||||
master plan.
|
||||
|
||||
Phase state lives in files, not chat:
|
||||
|
||||
- `.agent/PLAN.md` — master plan; **LOCKED DECISIONS** are binding
|
||||
- `.agent/phases/todo/NN_name.md` — pending phases (sort order = execution order)
|
||||
- `.agent/phases/complete/` — finished phases (read-only history)
|
||||
- `.agent/phases/todo/NN_name/` — a pending phase: `00_phase.md` overview + `NN_task.md` task files (task sort order = execution order)
|
||||
- `.agent/phases/todo/NN_name.md` — legacy single-file phase (still valid; migrate or split it)
|
||||
- `.agent/phases/complete/` — finished phases, mirroring the todo/ layout (read-only history)
|
||||
|
||||
Legacy flat phases (`todo/NN_name.md`) are still executed as a single unit.
|
||||
`bash scripts/migrate-phases-to-tasks.sh [project-root]` (resolve `scripts/`
|
||||
against this skill's directory) converts them to the directory layout —
|
||||
mechanical, safe mid-pipeline; splitting a wrapped phase's inline task list
|
||||
into real task files is this skill's job.
|
||||
|
||||
## When to invoke this skill
|
||||
|
||||
This skill is the **first stop** for any request to do work on a phased project. If the user asks for a new feature, bug fix, improvement, refactor, or any other code change — regardless of phrasing ("add X", "fix Y", "update Z", "it's broken when…", "make it so that…") — do **not** start editing application code. Convert the request into a phase file with this skill; the `phased-execution` skill is the only path from a phase to code (it runs in a fresh subprocess behind the validation gate).
|
||||
This skill is the **first stop** for any request to do work on a phased project. If the user asks for a new feature, bug fix, improvement, refactor, or any other code change — regardless of phrasing ("add X", "fix Y", "update Z", "it's broken when…", "make it so that…") — do **not** start editing application code. Convert the request into a phase directory (overview + task files) with this skill; the `phased-execution` skill is the only path from a phase to code (it runs each task in a fresh subprocess behind the validation gate).
|
||||
|
||||
- **Phased project** (`.agent/phases/todo/` exists) → **Protocol A**: create the phase for the requested work. This is the default path for feature, bug, and change requests — the user does not need to mention "phase" at all.
|
||||
- **Fresh project** (no `.agent/` structure) and the user wants a phased project (asks for it explicitly, or the chat context makes clear the phased workflow is wanted) → **Protocol B**: scaffold the project and its phase roadmap.
|
||||
@@ -53,8 +61,8 @@ Before writing anything, you must:
|
||||
|
||||
1. Read `.agent/PLAN.md` — project goals, architecture, and **LOCKED DECISIONS**.
|
||||
2. Read `AGENTS.md` if present — project rules may add requirements (e.g. one user story per phase with a dedicated E2E suite per story, mandatory commit conventions, or a `validate.sh` gate).
|
||||
3. Run `bash scripts/phase-status.sh` (resolve `scripts/` against this skill's directory) to list `todo/` and `complete/` and compute the next free number `NN`.
|
||||
4. Read every file in `.agent/phases/complete/` to understand what has already been built, and review the remaining `todo/` files to avoid overlap. Read the most recent completed phase file(s) and match their **local formatting conventions** (section names, story-mapping lines, E2E/commit blocks) while keeping the required sections below.
|
||||
3. Run `bash scripts/phase-status.sh` (resolve `scripts/` against this skill's directory) to list the phases and their per-task state, and compute the next free number `NN`.
|
||||
4. Read the `00_phase.md` files in `.agent/phases/complete/*/` to understand what has already been built, and review the remaining `todo/` phase directories to avoid overlap. Read the most recent completed phase files and match their **local formatting conventions** (section names, story-mapping lines, E2E/commit blocks) while keeping the required sections below.
|
||||
|
||||
### Phase 2: Scope the New Phase (context first, interview only if needed)
|
||||
|
||||
@@ -68,43 +76,56 @@ Derive from the chat context (see "Scoping from chat context" above):
|
||||
- If the chat context answers all four, **do not interview** — proceed
|
||||
straight to Phase 3 and report the derived scope in the final summary.
|
||||
- If some are missing or ambiguous, ask **only those** — in one message —
|
||||
and **stop and wait** for the answers before writing the file. Never
|
||||
and **stop and wait** for the answers before writing the phase. Never
|
||||
re-ask what the chat already settled.
|
||||
- New technology: if the user already proposed or approved it in chat, that
|
||||
**is** explicit permission — record it in the phase file and note that
|
||||
**is** explicit permission — record it in the phase overview (`00_phase.md`) and note that
|
||||
`PLAN.md`'s anchor table needs their sign-off (this skill never edits
|
||||
`PLAN.md`). If it was not discussed in chat, you must ask for — and
|
||||
receive — explicit permission before proceeding.
|
||||
|
||||
### Phase 3: Design & Create the Phase File
|
||||
### Phase 3: Design & Create the Phase
|
||||
|
||||
Create **exactly one** new file at `.agent/phases/todo/NN_name.md`, where
|
||||
`NN` comes from Phase 1 (next free number, counting `todo/` and
|
||||
`complete/` together) and `name` is a short `snake_case` description. Use
|
||||
`assets/phase-template.md` as the skeleton. The file must contain:
|
||||
Create **exactly one** new phase directory at `.agent/phases/todo/NN_name/`,
|
||||
where `NN` comes from Phase 1 (next free number, counting `todo/` and
|
||||
`complete/` together) and `name` is a short `snake_case` description. It
|
||||
contains the phase overview and the phase's task files:
|
||||
|
||||
1. **Objective** — a 1–3 sentence statement of what the phase delivers.
|
||||
2. **Dependencies** — the phases (by file name) that must be completed first.
|
||||
3. **Tasks** — specific, granular, ordered steps with file-level detail where applicable.
|
||||
4. **Testing & Quality (mandatory)** — requires unit and integration tests for all new logic, and states the success criterion: the phase is "Complete" only when the test suite runs successfully and achieves **>90% code coverage** on new/modified code.
|
||||
5. **Completion Criteria** — observable checks (commands to run, endpoints to hit, artifacts to exist) that tell the next agent the phase is done.
|
||||
1. **`00_phase.md`** — use `assets/phase-template.md` as the skeleton. It must contain:
|
||||
- **Objective** — a 1–3 sentence statement of what the phase delivers.
|
||||
- **Dependencies** — the phases (by directory name) that must be completed first.
|
||||
- **Tasks** — the ordered index of this phase's task files (one line each).
|
||||
- **Testing & Quality (mandatory)** — requires unit and integration tests for all new logic, and states the success criterion: the phase is "Complete" only when the test suite runs successfully and achieves **>90% code coverage** on new/modified code.
|
||||
- **Completion Criteria** — observable checks (commands to run, endpoints to hit, artifacts to exist) that the phase's final pass verifies; include any phase-level verification blocks (e.g. a dedicated E2E or contract suite).
|
||||
2. **Task files `01_…`, `02_…`, …** — use `assets/task-template.md` as the skeleton. Each must contain:
|
||||
- **Objective** — 1–2 sentences on what the task delivers.
|
||||
- **Work** — specific, ordered steps with file-level detail where applicable.
|
||||
- **Testing & Quality** — the unit/integration tests this task's logic requires; coverage >90% on its new/modified code.
|
||||
- **Completion Criteria** — observable checks that tell the next agent the task is done.
|
||||
|
||||
**Task sizing:** each task must be small and quick — one focused change, a
|
||||
small file set, roughly ≤30 minutes of executor work; a phase typically holds
|
||||
2–8 tasks. Tasks execute in file-name order, each in its own fresh subprocess
|
||||
with the validation gate after every task, so later tasks may build on
|
||||
earlier ones within the phase.
|
||||
|
||||
**Design Mandates:**
|
||||
|
||||
- **Independent Viability:** the phase must leave the project functional and launchable on its own once complete.
|
||||
- **Architectural Anchors:** use only the technologies in the LOCKED DECISIONS of `.agent/PLAN.md`. Never introduce new technology without explicit permission.
|
||||
- **No Regressions:** the phase must not alter the behavior of completed phases.
|
||||
- **Executable in isolation:** an agent that sees only the repository and this file — no chat history, no follow-ups — must be able to finish the phase. No hidden assumptions.
|
||||
- **Executable in isolation:** an agent that sees only the repository, the phase overview, the completed task files, and one task file — no chat history, no follow-ups — must be able to finish that task. No hidden assumptions.
|
||||
|
||||
### Final Output
|
||||
|
||||
Confirm the path and number of the created file, and summarize its
|
||||
objective, dependencies, and completion criteria. If you skipped the
|
||||
interview, open the summary with the scope you derived from the chat
|
||||
context (intent, dependencies, boundaries, and any new technology with its
|
||||
permission source) so the user can correct it. Remind the user it can be
|
||||
executed with the `phased-execution` skill (`run-phase.sh NN_name.md` for a
|
||||
single phase, `auto-phase.sh` for the full pipeline).
|
||||
Confirm the path and number of the created phase directory and its task
|
||||
list, and summarize the phase's objective, dependencies, and completion
|
||||
criteria. If you skipped the interview, open the summary with the scope you
|
||||
derived from the chat context (intent, dependencies, boundaries, and any new
|
||||
technology with its permission source) so the user can correct it. Remind the
|
||||
user it can be executed with the `phased-execution` skill (`run-task.sh
|
||||
NN_name/01_…` for a single task, `run-phase.sh NN_name` for the phase,
|
||||
`auto-phase.sh` for the full pipeline).
|
||||
|
||||
## Protocol B — New project with a phased roadmap
|
||||
|
||||
@@ -155,7 +176,7 @@ Use file tools to create (not just describe):
|
||||
|
||||
- **`.agent/PLAN.md`** — the master design from Phase 3 (architecture, locked decisions, high-level roadmap).
|
||||
- **`AGENTS.md`** — initialized with: always read `.agent/PLAN.md` first; follow the phased protocol in `.agent/phases/`; never modify `.agent/PLAN.md` or anything in `.agent/phases/complete/`; ask the user before editing files in `.agent/phases/todo/`; strictly adhere to the **LOCKED DECISIONS**.
|
||||
- **`.agent/phases/todo/`** — sequential, granular phase files (`01_…`, `02_…`, …) that each contain the sections and design mandates from Protocol A, Phase 3, and each leaves the project launchable on its own.
|
||||
- **`.agent/phases/todo/`** — sequential phase directories (`01_…/`, `02_…/`, …), each with a `00_phase.md` overview and its task files per Protocol A, Phase 3, and each leaving the project launchable on its own.
|
||||
- **`.agent/phases/complete/`** — create the directory, leave it empty.
|
||||
|
||||
Do **not** create `.agent/validate.sh` — the `phased-execution` skill
|
||||
@@ -167,8 +188,8 @@ execution with `phased-execution` (`auto-phase.sh`).
|
||||
|
||||
## Strict Operational Rules
|
||||
|
||||
- **Work requests become phase files.** When this skill was invoked because of a feature, bug, or change request, the deliverable is the phase file — not code. Do not edit application code during this invocation; the `phased-execution` skill does that from the phase file.
|
||||
- **Work requests become phase directories.** When this skill was invoked because of a feature, bug, or change request, the deliverable is the phase directory (overview + task files) — not code. Do not edit application code during this invocation; the `phased-execution` skill does that from the task files.
|
||||
- **Never** modify `.agent/PLAN.md`, `AGENTS.md`, or any file in `.agent/phases/complete/`.
|
||||
- **Never** modify existing files in `.agent/phases/todo/`; if one needs updating, ask the user for permission first.
|
||||
- Create exactly **one** phase file per invocation in Protocol A. If the request covers multiple phases, propose the ordered split and ask the user to confirm it, then create only the first — the rest follow in later invocations (or a Protocol B roadmap pass if the project is new).
|
||||
- Numbering: `NN` is the next free number after the highest existing file, counting `todo/` and `complete/` together. Never reuse or collide a number.
|
||||
- **Never** modify existing phase directories or their files in `.agent/phases/todo/`; if one needs updating, ask the user for permission first.
|
||||
- Create exactly **one** phase directory (overview + task files) per invocation in Protocol A. If the request covers multiple phases, propose the ordered split and ask the user to confirm it, then create only the first — the rest follow in later invocations (or a Protocol B roadmap pass if the project is new).
|
||||
- Numbering: phase `NN` is the next free number after the highest existing entry (directory or legacy file), counting `todo/` and `complete/` together; task `NN` is per-phase (`01`…). Never reuse or collide a number.
|
||||
|
||||
@@ -11,13 +11,13 @@
|
||||
{{or "— (none)"}}
|
||||
|
||||
## Tasks
|
||||
1. `{{path/to/file}}` — {{specific change, file-level detail}}
|
||||
2. `{{path/to/file}}` — {{specific change}}
|
||||
1. `01_{{short_name}}.md` — {{one-line summary}}
|
||||
2. `02_{{short_name}}.md` — {{one-line summary}}
|
||||
|
||||
## Testing & Quality
|
||||
- Unit/integration: {{tests required for all new logic — name the behaviors to cover}}
|
||||
- Coverage: **>90%** on new/modified code
|
||||
{{project additions, e.g. a dedicated Playwright E2E suite run in isolation}}
|
||||
{{project additions, e.g. a dedicated Playwright E2E suite run in isolation by this phase's final pass}}
|
||||
|
||||
## Completion Criteria
|
||||
- [ ] {{observable check: command to run / endpoint to hit / artifact to exist}}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
# Task {{NN}} — {{Short Title}}
|
||||
|
||||
**Phase:** `{{NN_phase}}` · **Story:** `{{.agent/user_stories/<story>.md or "n/a"}}`
|
||||
|
||||
## Objective
|
||||
{{1–2 sentences: what this task delivers}}
|
||||
|
||||
## Work
|
||||
1. `{{path/to/file}}` — {{specific change, file-level detail}}
|
||||
2. `{{path/to/file}}` — {{specific change}}
|
||||
|
||||
## Testing & Quality
|
||||
- Unit/integration: {{tests required for this task's logic}}
|
||||
- Coverage: **>90%** on this task's new/modified code
|
||||
|
||||
## Completion Criteria
|
||||
- [ ] {{observable check: command to run / endpoint to hit / artifact to exist}}
|
||||
- [ ] full test suite green
|
||||
- [ ] no behavior change in completed work
|
||||
@@ -0,0 +1,72 @@
|
||||
#!/usr/bin/env bash
|
||||
# migrate-phases-to-tasks.sh — convert the flat phase-file layout to the
|
||||
# phase-directory + task-file layout.
|
||||
#
|
||||
# Each numeric-prefixed flat phase file in {todo,complete}/ is wrapped into a
|
||||
# directory of the same name with the file renamed to 00_phase.md:
|
||||
#
|
||||
# todo/03_api.md → todo/03_api/00_phase.md
|
||||
# complete/01_init.md → complete/01_init/00_phase.md
|
||||
#
|
||||
# The harness still executes wrapped phases: their inline task lists are
|
||||
# picked up by the phase's 00_phase.md final pass, so this is safe
|
||||
# mid-pipeline. Splitting a wrapped todo phase into real task files is the
|
||||
# phase-authoring skill's job, not this script's. Already-migrated phases are
|
||||
# left alone; a flat file colliding with an existing same-name directory is
|
||||
# reported and skipped.
|
||||
#
|
||||
# Usage: bash migrate-phases-to-tasks.sh [project-root]
|
||||
# project-root defaults to the nearest ancestor containing
|
||||
# .agent/phases/todo or agent/phases/todo (older projects).
|
||||
set -euo pipefail
|
||||
|
||||
root="${1:-}"
|
||||
if [[ -n "$root" ]]; then
|
||||
[[ -d "$root" ]] || { echo "✗ ERROR: $root is not a directory" >&2; exit 1; }
|
||||
else
|
||||
root="$(pwd)"
|
||||
while :; do
|
||||
if [[ -d "$root/.agent/phases/todo" || -d "$root/agent/phases/todo" ]]; then break; fi
|
||||
[[ "$root" == "/" ]] && { echo "✗ ERROR: no phases/todo found at or above $(pwd)" >&2; exit 1; }
|
||||
root="$(dirname "$root")"
|
||||
done
|
||||
fi
|
||||
|
||||
if [[ -d "$root/.agent/phases" ]]; then
|
||||
phases="$root/.agent/phases"
|
||||
elif [[ -d "$root/agent/phases" ]]; then
|
||||
phases="$root/agent/phases"
|
||||
else
|
||||
echo "✗ ERROR: no phases directory under $root" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "project root: $root"
|
||||
echo "phases: $phases"
|
||||
echo
|
||||
|
||||
moved=0 skipped=0
|
||||
for d in todo complete; do
|
||||
[[ -d "$phases/$d" ]] || continue
|
||||
for f in "$phases/$d"/*.md; do
|
||||
[[ -e "$f" ]] || continue
|
||||
base="$(basename "$f" .md)"
|
||||
[[ "$base" =~ ^[0-9] ]] || continue
|
||||
if [[ -d "$phases/$d/$base" ]]; then
|
||||
echo "⚠ skip: $d/$base.md — directory $d/$base/ already exists (conflict)" >&2
|
||||
skipped=$((skipped + 1))
|
||||
continue
|
||||
fi
|
||||
mkdir -p "$phases/$d/$base"
|
||||
mv -f "$f" "$phases/$d/$base/00_phase.md"
|
||||
echo " $d/$base.md → $d/$base/00_phase.md"
|
||||
moved=$((moved + 1))
|
||||
done
|
||||
done
|
||||
|
||||
echo
|
||||
echo "migrated: $moved phase file(s) → 00_phase.md directory layout; skipped: $skipped"
|
||||
if (( moved > 0 )); then
|
||||
echo "wrapped todo phases now run as a single final pass; use the phase-authoring"
|
||||
echo "skill to split their inline task lists into real task files."
|
||||
fi
|
||||
@@ -1,9 +1,13 @@
|
||||
#!/usr/bin/env bash
|
||||
# phase-status.sh — phase pipeline state for the Phase Architect.
|
||||
# phase-status.sh — phase/task pipeline state for the Phase Architect.
|
||||
#
|
||||
# Finds the project root (nearest ancestor with .agent/phases/todo), prints
|
||||
# the todo/ and complete/ phase listings, and computes the next free phase
|
||||
# number NN (counting both directories together, zero-padded to 2 digits).
|
||||
# the todo/ and complete/ phase listings with per-task state, the next free
|
||||
# phase number NN (counting both directories together, zero-padded to 2
|
||||
# digits), and the next pending unit in pipeline order.
|
||||
#
|
||||
# Phase layout: todo/NN_name/ holds 00_phase.md (overview) + NN_task.md task
|
||||
# files; a legacy flat todo/NN_name.md is a single-unit phase.
|
||||
#
|
||||
# Usage: bash phase-status.sh # from anywhere in the project tree
|
||||
|
||||
@@ -16,31 +20,107 @@ while :; do
|
||||
root="$(dirname "$root")"
|
||||
done
|
||||
|
||||
list() {
|
||||
local d="$root/.agent/phases/$1" f found=0
|
||||
for f in "$d"/*.md; do
|
||||
[[ -e "$f" ]] || continue
|
||||
found=1
|
||||
printf ' %s\n' "$(basename "$f")"
|
||||
done
|
||||
if (( ! found )); then printf ' (empty)\n'; fi
|
||||
}
|
||||
phases="$root/.agent/phases"
|
||||
|
||||
# --- todo: per-task state ------------------------------------------------------
|
||||
echo "todo:"
|
||||
found=0
|
||||
for entry in "$phases/todo/"*; do
|
||||
[[ -e "$entry" ]] || continue
|
||||
name="$(basename "$entry")"
|
||||
[[ "$name" =~ ^[0-9] ]] || continue
|
||||
found=1
|
||||
if [[ -d "$entry" ]]; then
|
||||
# union of this phase's files across todo/ and complete/ (completed units
|
||||
# move to complete/, so pending = present in todo/, done = in complete/)
|
||||
comp_dir="$phases/complete/$name"
|
||||
comp_files=""
|
||||
if [[ -d "$comp_dir" ]]; then comp_files="$(ls -1 "$comp_dir" 2>/dev/null || true)"; fi
|
||||
names="$( { ls -1 "$entry" 2>/dev/null || true; printf '%s\n' "$comp_files"; } | grep -E '\.md$' | sort -u || true)"
|
||||
total=0 done_n=0
|
||||
while IFS= read -r t; do
|
||||
[[ -n "$t" ]] || continue
|
||||
total=$((total + 1))
|
||||
[[ -f "$phases/complete/$name/$t" ]] && done_n=$((done_n + 1))
|
||||
done <<< "$names"
|
||||
printf ' %s/ (%s of %s done)\n' "$name" "$done_n" "$total"
|
||||
# tasks first (sorted), then 00_phase.md as the final pass
|
||||
while IFS= read -r t; do
|
||||
[[ -n "$t" ]] || continue
|
||||
[[ "$t" == 00_phase.md ]] && continue
|
||||
if [[ -f "$phases/complete/$name/$t" ]]; then
|
||||
printf ' [x] %s\n' "$t"
|
||||
else
|
||||
printf ' [ ] %s\n' "$t"
|
||||
fi
|
||||
done <<< "$names"
|
||||
if grep -qxF '00_phase.md' <<< "$names"; then
|
||||
if [[ -f "$phases/complete/$name/00_phase.md" ]]; then
|
||||
printf ' [x] 00_phase.md (final pass)\n'
|
||||
else
|
||||
printf ' [ ] 00_phase.md (final pass)\n'
|
||||
fi
|
||||
fi
|
||||
else
|
||||
printf ' %s (legacy single-file phase)\n' "$name"
|
||||
fi
|
||||
done
|
||||
(( found )) || echo " (empty)"
|
||||
|
||||
# --- complete -------------------------------------------------------------------
|
||||
echo "complete:"
|
||||
found=0
|
||||
for entry in "$phases/complete/"*; do
|
||||
[[ -e "$entry" ]] || continue
|
||||
name="$(basename "$entry")"
|
||||
[[ "$name" =~ ^[0-9] ]] || continue
|
||||
found=1
|
||||
if [[ -d "$entry" ]]; then
|
||||
n=0
|
||||
for f in "$entry"/*.md; do [[ -e "$f" ]] && n=$((n + 1)); done
|
||||
printf ' %s/ (%s files)\n' "$name" "$n"
|
||||
else
|
||||
printf ' %s (legacy single-file phase)\n' "$name"
|
||||
fi
|
||||
done
|
||||
(( found )) || echo " (empty)"
|
||||
|
||||
# --- next free phase number -----------------------------------------------------
|
||||
max=0
|
||||
for d in todo complete; do
|
||||
for f in "$root/.agent/phases/$d"/*.md; do
|
||||
[[ -e "$f" ]] || continue
|
||||
n="$(basename "$f" .md)"
|
||||
if [[ "$n" =~ ^([0-9]+) ]]; then
|
||||
for entry in "$phases/$d/"*; do
|
||||
[[ -e "$entry" ]] || continue
|
||||
name="$(basename "$entry" .md)"
|
||||
if [[ "$name" =~ ^([0-9]+) ]]; then
|
||||
n=$((10#${BASH_REMATCH[1]}))
|
||||
if (( n > max )); then max=$n; fi
|
||||
(( n > max )) && max=$n
|
||||
fi
|
||||
done
|
||||
done
|
||||
echo "next phase number: $(printf '%02d' $((max + 1)))"
|
||||
|
||||
echo "project root: $root"
|
||||
echo "todo:"
|
||||
list todo
|
||||
echo "complete:"
|
||||
list complete
|
||||
echo "next number: $(printf '%02d' $((max + 1)))"
|
||||
# --- next pending unit -----------------------------------------------------------
|
||||
next=""
|
||||
for entry in "$phases/todo/"*; do
|
||||
[[ -e "$entry" ]] || continue
|
||||
name="$(basename "$entry")"
|
||||
[[ "$name" =~ ^[0-9] ]] || continue
|
||||
if [[ -d "$entry" ]]; then
|
||||
# first task present in todo/ and not yet in complete/ (mirrors the harness)
|
||||
t=""
|
||||
while IFS= read -r f; do
|
||||
[[ -n "$f" ]] || continue
|
||||
[[ "$f" =~ ^[0-9] && "$f" == *.md ]] || continue
|
||||
[[ "$f" == 00_phase.md ]] && continue
|
||||
[[ -f "$phases/complete/$name/$f" ]] && continue
|
||||
t="$f"; break
|
||||
done < <(ls -1 "$entry" 2>/dev/null || true)
|
||||
if [[ -n "$t" ]]; then next="$name/$t"; break; fi
|
||||
if [[ -f "$entry/00_phase.md" && ! -f "$phases/complete/$name/00_phase.md" ]]; then
|
||||
next="$name/00_phase.md"; break
|
||||
fi
|
||||
elif [[ -f "$entry" ]]; then
|
||||
next="$name"; break
|
||||
fi
|
||||
done
|
||||
echo "next unit: ${next:-(none — pipeline complete)}"
|
||||
|
||||
+42
-24
@@ -1,6 +1,6 @@
|
||||
---
|
||||
name: phased-execution
|
||||
description: Runs the .agent/phases/ phased-execution pipeline (ported from opencode's next-phase/auto-phase commands). Use when the user asks to run the next phase, run all phases, run the phase pipeline, or check pipeline status. Each phase executes in a separate pi subprocess so this chat's context stays small.
|
||||
description: Runs the .agent/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
|
||||
@@ -8,16 +8,21 @@ description: Runs the .agent/phases/ phased-execution pipeline (ported from open
|
||||
Phase state lives in files, not chat:
|
||||
|
||||
- `.agent/PLAN.md` — master plan; LOCKED DECISIONS are binding
|
||||
- `.agent/phases/todo/NN_name.md` — pending phases (alphanumerical sort = execution order)
|
||||
- `.agent/phases/complete/` — finished phases
|
||||
- `.agent/reports/` — per-phase executor reports, stderr, and validation logs
|
||||
- `.agent/validate.sh` — the pass/fail gate for every phase
|
||||
- `.agent/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)
|
||||
- `.agent/phases/todo/NN_name.md` — legacy single-file phase (still executable as one unit)
|
||||
- `.agent/phases/complete/` — finished phases; mirrors the `todo/` layout (completed task files and the phase overview move here)
|
||||
- `.agent/reports/<phase>/<task>.a<N>.{md,err,validate}` — per-task executor reports, stderr, and validation logs (legacy phases: `.agent/reports/<phase>.a<N>.*`)
|
||||
- `.agent/validate.sh` — the pass/fail gate, run after **every task**
|
||||
|
||||
The scripts run each phase in a **separate pi process** (fresh context) with
|
||||
bounded fixer retries. A phase only moves to `complete/` after the child exits
|
||||
0, the child's stream ends with a clean final report, **and**
|
||||
`.agent/validate.sh` passes. This chat only dispatches and relays results —
|
||||
do not implement phase code yourself; that is what the subprocess is for.
|
||||
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** `.agent/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. This chat only dispatches
|
||||
and relays results — do not implement task code yourself; that is what the
|
||||
subprocesses are for.
|
||||
|
||||
## Live display
|
||||
|
||||
@@ -33,15 +38,23 @@ never lost to a truncated stream.
|
||||
|
||||
(Resolve `scripts/` against this skill's directory.)
|
||||
|
||||
Run the next phase, or a specific one:
|
||||
Run the next task, or a specific one:
|
||||
|
||||
```bash
|
||||
bash scripts/run-phase.sh # first pending phase
|
||||
bash scripts/run-phase.sh 03_api.md # specific phase (warns if out of order)
|
||||
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 the whole pipeline — every pending phase, in order, stopping at the first
|
||||
phase that fails after all retries:
|
||||
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
|
||||
@@ -51,17 +64,18 @@ Re-running `auto-phase.sh` after a failure continues where it stopped.
|
||||
|
||||
## After a run
|
||||
|
||||
Exit codes: `0` = success (or nothing to run), `1` = phase failed after all
|
||||
attempts / no pending phases error, `130`/`143` = interrupted (Ctrl+C / SIGTERM).
|
||||
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 phase name, its executor report (printed at the end of
|
||||
the script output), and the validation outcome. On failure, point the user at
|
||||
`.agent/reports/<phase>.a*.{md,err,validate}` — the script also prints a ready
|
||||
to run `pi --session … -c “…”` command to continue the failed session manually.
|
||||
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 `.agent/reports/<phase>/<task>.a*.{md,err,validate}` (legacy
|
||||
phases: `.agent/reports/<phase>.a*.*`) — the script also prints a ready to run
|
||||
`pi --session … -c “…”` command to continue the failed session manually.
|
||||
|
||||
## Configuration (environment variables)
|
||||
|
||||
@@ -70,7 +84,7 @@ to run `pi --session … -c “…”` command to continue the failed session ma
|
||||
| `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` | `0` | `1` = `git commit --no-gpg-sign` after each passing phase |
|
||||
| `PHASE_COMMIT` | `0` | `1` = `git commit --no-gpg-sign` after each completed **phase** (when its `00_phase.md` final pass passes; legacy: when its file moves) |
|
||||
| `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) |
|
||||
@@ -80,8 +94,12 @@ to run `pi --session … -c “…”` command to continue the failed session ma
|
||||
- First run creates `.agent/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 files are created by the `/to-phase`, `/audit-create`, `/new-project`,
|
||||
and `/new-python-*` prompt templates.
|
||||
- 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 `.agent/phase-sessions/`; add it to
|
||||
`.gitignore` if the project is versioned.
|
||||
- If you keep non-skill markdown (e.g. a `README.md`) in a skills directory
|
||||
|
||||
@@ -5,7 +5,7 @@ Target phase file: `.agent/phases/todo/{{PHASE}}`
|
||||
## Steps
|
||||
1. Read `.agent/PLAN.md` — project goals, architecture, and **LOCKED DECISIONS** (binding; never introduce technology outside them).
|
||||
2. Read `AGENTS.md` if present.
|
||||
3. Read every file in `.agent/phases/complete/` so your work stays architecturally consistent with what is already built.
|
||||
3. Read the completed phase files so your work stays architecturally consistent with what is already built: every `00_phase.md` in `.agent/phases/complete/*/`, plus any legacy flat phase files directly in `.agent/phases/complete/`.
|
||||
4. Read the target phase file and complete **every** task in it, in order.
|
||||
5. Write the unit and integration tests required by the phase's Testing & Quality section. Do not omit parts of the code to inflate coverage.
|
||||
6. Run the project's full test suite and linter. If anything fails — including the phase's coverage criterion — fix it and re-run until green.
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
You are the phase-completion executor in a phased build pipeline. You run in a fresh, isolated context; the harness manages phase and task files, retries, and final validation.
|
||||
|
||||
Phase overview: `.agent/phases/todo/{{PHASE}}/00_phase.md`
|
||||
|
||||
## Steps
|
||||
1. Read `.agent/PLAN.md` — project goals, architecture, and **LOCKED DECISIONS** (binding; never introduce technology outside them).
|
||||
2. Read `AGENTS.md` if present.
|
||||
3. Read the phase overview `.agent/phases/todo/{{PHASE}}/00_phase.md`.
|
||||
4. Check the phase's task index against `.agent/phases/complete/{{PHASE}}/`: if any task is missing from there, the overview itself still carries work (a legacy phase with an inline task list) — implement that remaining work now, in order, including its tests.
|
||||
5. Otherwise every task is done: this is the final verification pass. Verify **each** completion criterion in the phase overview (commands to run, endpoints to hit, artifacts to exist), and execute any phase-level verification blocks it defines (e.g. a dedicated E2E or contract test suite).
|
||||
6. Run the project's full test suite and linter. If anything fails — including the phase's coverage criterion or a completion criterion — fix it and re-run until green.
|
||||
7. If you find defects in previously completed work (failing tests, lint errors, bugs), fix those as part of this pass.
|
||||
|
||||
## Rules
|
||||
- Never start work from other files in `todo/`.
|
||||
- Do **not** move, rename, or edit the phase overview, other files in `.agent/phases/todo/`, `.agent/PLAN.md`, or anything in `.agent/phases/complete/`. The harness moves the phase file on success.
|
||||
- Do not assume the code is correct; fix any errors you find while testing.
|
||||
- Leave the repository functional when you finish.
|
||||
|
||||
## Final response
|
||||
When everything is green, reply with a report of **at most 15 lines**:
|
||||
- What was implemented or verified (short bullet list)
|
||||
- Test / lint / coverage results (exact commands and outcomes)
|
||||
- Completion criteria: each one with its outcome
|
||||
- Notable decisions or deviations
|
||||
- The next pending phase, if any
|
||||
@@ -0,0 +1,26 @@
|
||||
You are a task executor in a phased build pipeline. You run in a fresh, isolated context; the harness manages phase and task files, retries, and final validation.
|
||||
|
||||
Target task file: `.agent/phases/todo/{{PHASE}}/{{TASK}}`
|
||||
|
||||
## Steps
|
||||
1. Read `.agent/PLAN.md` — project goals, architecture, and **LOCKED DECISIONS** (binding; never introduce technology outside them).
|
||||
2. Read `AGENTS.md` if present.
|
||||
3. Read the phase overview `.agent/phases/todo/{{PHASE}}/00_phase.md` — objective, dependencies, testing & quality mandate, and completion criteria.
|
||||
4. Read this phase's completed task files (`.agent/phases/complete/{{PHASE}}/*.md`, excluding `00_phase.md`) and the `00_phase.md` overviews of other completed phases (`.agent/phases/complete/*/00_phase.md`) so your work stays architecturally consistent with what is already built.
|
||||
5. Read the target task file and complete it fully, in order.
|
||||
6. Write the unit and integration tests required by the task. Do not omit parts of the code to inflate coverage.
|
||||
7. Run the project's full test suite and linter. If anything fails — including the task's coverage criterion — fix it and re-run until green.
|
||||
8. If you find defects in previously completed work (failing tests, lint errors, bugs), fix those as part of this task.
|
||||
|
||||
## Rules
|
||||
- Work only on the target task; never start the next task or work from other files in `todo/`.
|
||||
- Do **not** move, rename, or edit the task file, `00_phase.md`, other files in `.agent/phases/todo/`, `.agent/PLAN.md`, or anything in `.agent/phases/complete/`. The harness moves the task file on success.
|
||||
- Do not assume the code is correct; fix any errors you find while testing.
|
||||
- Leave the repository functional when you finish.
|
||||
|
||||
## Final response
|
||||
When everything is green, reply with a report of **at most 15 lines**:
|
||||
- What was implemented (short bullet list)
|
||||
- Test / lint / coverage results (exact commands and outcomes)
|
||||
- Notable decisions or deviations
|
||||
- The next pending task, if any
|
||||
@@ -1,12 +1,15 @@
|
||||
#!/usr/bin/env bash
|
||||
# auto-phase.sh — run the full phased pipeline, no LLM in the loop.
|
||||
#
|
||||
# Processes every file in .agent/phases/todo/ in alphanumerical order.
|
||||
# Each phase runs in its own pi process (fresh context); on failure the
|
||||
# executor's session is resumed for up to MAX_FIX_ATTEMPTS fixer rounds.
|
||||
# A phase moves to .agent/phases/complete/ only after the child exits 0
|
||||
# AND .agent/validate.sh passes. Stops at the first phase that cannot be
|
||||
# completed — re-run this script to continue where it stopped.
|
||||
# Processes every pending task in .agent/phases/todo/ in pipeline order
|
||||
# (phase order, then task order within each phase). Each task runs in its
|
||||
# own pi process (fresh context); .agent/validate.sh runs after EVERY task.
|
||||
# On failure the executor's session is resumed for up to MAX_FIX_ATTEMPTS
|
||||
# fixer rounds. A task moves to .agent/phases/complete/ only after the
|
||||
# child exits 0 AND validation passes; a phase completes when its
|
||||
# 00_phase.md final pass succeeds (legacy phases: when their file moves).
|
||||
# Stops at the first task that cannot be completed — re-run this script to
|
||||
# continue where it stopped.
|
||||
#
|
||||
# Env: see SKILL.md (MAX_FIX_ATTEMPTS, PHASE_MODEL, PHASE_THINKING,
|
||||
# PHASE_COMMIT, PI_TRUST, FRESH_FIX).
|
||||
@@ -16,24 +19,24 @@ source "$SCRIPT_DIR/lib.sh"
|
||||
|
||||
# Make interruptions visible: state stays in .agent/phases/todo, and the
|
||||
# failed executor's session is still resumable on the next run.
|
||||
trap 'echo; echo "✗ ERROR: interrupted (SIGINT) — ${phase:-the pipeline} is left in $PHASE_TODO/; re-run to continue where it stopped" >&2; exit 130' INT
|
||||
trap 'echo; echo "✗ ERROR: interrupted (SIGTERM) — ${phase:-the pipeline} is left in $PHASE_TODO/; re-run to continue where it stopped" >&2; exit 143' TERM
|
||||
trap 'echo; echo "✗ ERROR: interrupted (SIGINT) — ${unit:-the pipeline} is left in $PHASE_TODO/; re-run to continue where it stopped" >&2; exit 130' INT
|
||||
trap 'echo; echo "✗ ERROR: interrupted (SIGTERM) — ${unit:-the pipeline} is left in $PHASE_TODO/; re-run to continue where it stopped" >&2; exit 143' TERM
|
||||
|
||||
cd "$(find_root)" || die "no .agent/phases/todo found in this or parent directories (run /to-phase or /audit-create first)"
|
||||
|
||||
build_pi_args
|
||||
delivered=()
|
||||
while phase="$(next_phase)"; do
|
||||
[[ -n "$phase" ]] || break
|
||||
if ! execute_phase "$phase"; then
|
||||
echo "✗ ERROR: pipeline stopped — $phase FAILED after $MAX_FIX_ATTEMPTS attempts" >&2
|
||||
while unit="$(next_unit)"; do
|
||||
[[ -n "$unit" ]] || break
|
||||
if ! execute_unit "$unit"; then
|
||||
echo "✗ ERROR: pipeline stopped — $unit FAILED after $MAX_FIX_ATTEMPTS attempts" >&2
|
||||
echo " fix the issues above, then re-run this script to continue where it stopped" >&2
|
||||
exit 1
|
||||
fi
|
||||
delivered+=("$phase")
|
||||
delivered+=("$unit")
|
||||
done
|
||||
|
||||
echo
|
||||
echo "✓ pipeline done — ${#delivered[@]} phase(s) delivered this run: ${delivered[*]:-none}"
|
||||
remaining="$(ls -1 "$PHASE_TODO" 2>/dev/null | grep -cE '^[0-9]' || true)"
|
||||
echo " remaining in $PHASE_TODO/: $remaining"
|
||||
echo "✓ pipeline done — ${#delivered[@]} task(s) delivered this run: ${delivered[*]:-none}"
|
||||
remaining="$(count_units)"
|
||||
echo " remaining in $PHASE_TODO/: $remaining task(s)"
|
||||
|
||||
+176
-57
@@ -1,19 +1,28 @@
|
||||
#!/usr/bin/env bash
|
||||
# lib.sh — shared logic for the phased-execution skill.
|
||||
# Sourced by run-phase.sh and auto-phase.sh. Not meant to be run directly.
|
||||
# Sourced by run-task.sh, run-phase.sh, and auto-phase.sh. Not meant to be run directly.
|
||||
#
|
||||
# Phase state lives in files, not chat context:
|
||||
# .agent/PLAN.md master plan, LOCKED DECISIONS (binding)
|
||||
# .agent/phases/todo/ pending phases, NN_name.md, sorted = execution order
|
||||
# .agent/phases/complete/ finished phases
|
||||
# .agent/reports/ per-phase executor reports, stderr, validation logs
|
||||
# .agent/phases/todo/NN_name/ pending phase: 00_phase.md (overview) + NN_task.md task files
|
||||
# .agent/phases/todo/NN_name.md legacy single-file phase (still executable)
|
||||
# .agent/phases/complete/ finished phases — mirrors the todo/ layout
|
||||
# .agent/reports/ per-task executor reports, stderr, validation logs
|
||||
# .agent/phase-sessions/ child pi session files (resumable fixers)
|
||||
#
|
||||
# The pass/fail gate is .agent/validate.sh. A phase only moves to complete/
|
||||
# after the child executor exits 0 AND validation passes.
|
||||
# The unit of execution is the TASK: each task file runs in its own pi
|
||||
# subprocess (fresh context) with bounded fixer retries, and
|
||||
# .agent/validate.sh runs after EVERY task. A unit only moves to complete/
|
||||
# after the child exits 0, the child's stream ends with a clean final
|
||||
# report, and validation passes. When all of a phase's tasks are done,
|
||||
# 00_phase.md runs as the phase's final pass (any remaining inline work +
|
||||
# completion criteria + phase-level verification); moving it completes the
|
||||
# phase and is the PHASE_COMMIT commit point.
|
||||
|
||||
SKILL_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
EXECUTOR_PROMPT_FILE="$SKILL_DIR/assets/executor-prompt.md"
|
||||
TASK_EXECUTOR_PROMPT_FILE="$SKILL_DIR/assets/task-executor-prompt.md"
|
||||
PHASE_FINAL_PROMPT_FILE="$SKILL_DIR/assets/phase-final-prompt.md"
|
||||
|
||||
PHASE_TODO=".agent/phases/todo"
|
||||
PHASE_DONE=".agent/phases/complete"
|
||||
@@ -36,10 +45,100 @@ find_root() {
|
||||
done
|
||||
}
|
||||
|
||||
# --- phase selection ----------------------------------------------------------
|
||||
# First pending phase (alphanumerical sort), or empty when none remain.
|
||||
next_phase() {
|
||||
( cd "$PHASE_TODO" 2>/dev/null && ls -1 | grep -E '^[0-9]' | sort | head -n1 ) || true
|
||||
# --- unit selection -------------------------------------------------------------
|
||||
# A unit is the smallest schedulable piece of work, referenced relative to
|
||||
# .agent/phases/todo/:
|
||||
# directory phase → each task file "NN_name/NN_task.md" (sort order), then
|
||||
# the phase overview "NN_name/00_phase.md" as the final pass
|
||||
# legacy flat → the phase file itself, "NN_name.md"
|
||||
|
||||
# Pending phase entries in todo/ (phase directories or legacy .md files), in execution order.
|
||||
phase_entries() {
|
||||
( cd "$PHASE_TODO" 2>/dev/null && ls -1 | grep -E '^[0-9]' | sort ) || true
|
||||
}
|
||||
|
||||
# First pending unit of one phase entry, or empty when the phase is done.
|
||||
phase_next_unit() {
|
||||
local p="$1" t
|
||||
if [[ -d "$PHASE_TODO/$p" ]]; then
|
||||
t="$(ls -1 "$PHASE_TODO/$p" 2>/dev/null | grep -E '^[0-9].*\.md$' | grep -vE '^00_phase\.md$' | sort | head -n1)"
|
||||
if [[ -n "$t" ]]; then printf '%s/%s\n' "$p" "$t"; return 0; fi
|
||||
if [[ -f "$PHASE_TODO/$p/00_phase.md" ]]; then printf '%s/00_phase.md\n' "$p"; return 0; fi
|
||||
elif [[ -f "$PHASE_TODO/$p" ]]; then
|
||||
printf '%s\n' "$p"
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
# Next pending unit in the whole pipeline, or empty when everything is done.
|
||||
next_unit() {
|
||||
local p u
|
||||
for p in $(phase_entries); do
|
||||
u="$(phase_next_unit "$p")"
|
||||
if [[ -n "$u" ]]; then printf '%s\n' "$u"; return 0; fi
|
||||
done
|
||||
return 0
|
||||
}
|
||||
|
||||
# Count of pending units across all phases (task files + 00_phase.md + legacy files).
|
||||
count_units() {
|
||||
local p n=0
|
||||
for p in $(phase_entries); do
|
||||
if [[ -d "$PHASE_TODO/$p" ]]; then
|
||||
n=$(( n + $(ls -1 "$PHASE_TODO/$p" 2>/dev/null | grep -cE '\.md$') ))
|
||||
elif [[ -f "$PHASE_TODO/$p" ]]; then
|
||||
n=$(( n + 1 ))
|
||||
fi
|
||||
done
|
||||
printf '%s\n' "$n"
|
||||
}
|
||||
|
||||
# Name used in reports/sessions: "NN_name__NN_task" (dir phases) or "NN_name" (flat).
|
||||
unit_base() {
|
||||
local u="${1%.md}"
|
||||
printf '%s\n' "${u//\//__}"
|
||||
}
|
||||
|
||||
# Phase name for a unit (both forms).
|
||||
unit_phase() {
|
||||
local u="${1%.md}"
|
||||
printf '%s\n' "${u%%/*}"
|
||||
}
|
||||
|
||||
# Reports directory for a unit: per-phase subdir for dir phases, flat otherwise.
|
||||
unit_report_dir() {
|
||||
local u="$1"
|
||||
if [[ "$u" == */* ]]; then
|
||||
printf '%s/%s\n' "$PHASE_REPORTS" "$(unit_phase "$u")"
|
||||
else
|
||||
printf '%s\n' "$PHASE_REPORTS"
|
||||
fi
|
||||
}
|
||||
|
||||
# Report/log path for a unit attempt: <dir>/<base>.a<attempt>.<ext>.
|
||||
unit_report() {
|
||||
local u="$1" attempt="$2" ext="$3"
|
||||
printf '%s/%s.a%s.%s\n' "$(unit_report_dir "$u")" "$(unit_base "$u")" "$attempt" "$ext"
|
||||
}
|
||||
|
||||
# Phase-end unit (the commit point): 00_phase.md, or the whole file for legacy.
|
||||
is_phase_end() {
|
||||
local u="$1"
|
||||
if [[ "$u" == */* ]]; then [[ "${u##*/}" == "00_phase.md" ]]; else return 0; fi
|
||||
}
|
||||
|
||||
# Move a completed unit from todo/ to complete/ (same relative path).
|
||||
move_unit() {
|
||||
local u="$1" p t
|
||||
if [[ "$u" == */* ]]; then
|
||||
p="${u%%/*}"; t="${u##*/}"
|
||||
mkdir -p "$PHASE_DONE/$p"
|
||||
mv -f "$PHASE_TODO/$u" "$PHASE_DONE/$p/$t"
|
||||
if [[ -z "$(ls -A "$PHASE_TODO/$p" 2>/dev/null)" ]]; then rmdir "$PHASE_TODO/$p"; fi
|
||||
else
|
||||
mkdir -p "$PHASE_DONE"
|
||||
mv -f "$PHASE_TODO/$u" "$PHASE_DONE/$u"
|
||||
fi
|
||||
}
|
||||
|
||||
# --- child pi arguments -------------------------------------------------------
|
||||
@@ -89,38 +188,52 @@ recover_report() {
|
||||
}
|
||||
|
||||
# --- prompts ------------------------------------------------------------------
|
||||
# First-attempt prompt for a unit. Directory tasks render the task executor
|
||||
# prompt; the phase overview renders the phase-final prompt; legacy flat
|
||||
# phase files render the original phase executor prompt.
|
||||
first_prompt() {
|
||||
local phase="$1"
|
||||
local unit="$1" p t
|
||||
if [[ "$unit" == */* ]]; then
|
||||
p="${unit%%/*}"; t="${unit##*/}"
|
||||
if [[ "$t" == "00_phase.md" ]]; then
|
||||
[[ -f "$PHASE_FINAL_PROMPT_FILE" ]] || die "missing $PHASE_FINAL_PROMPT_FILE"
|
||||
sed "s|{{PHASE}}|$p|g" "$PHASE_FINAL_PROMPT_FILE"
|
||||
else
|
||||
[[ -f "$TASK_EXECUTOR_PROMPT_FILE" ]] || die "missing $TASK_EXECUTOR_PROMPT_FILE"
|
||||
sed "s|{{PHASE}}|$p|g; s|{{TASK}}|$t|g" "$TASK_EXECUTOR_PROMPT_FILE"
|
||||
fi
|
||||
else
|
||||
[[ -f "$EXECUTOR_PROMPT_FILE" ]] || die "missing $EXECUTOR_PROMPT_FILE"
|
||||
sed "s|{{PHASE}}|$phase|g" "$EXECUTOR_PROMPT_FILE"
|
||||
sed "s|{{PHASE}}|$unit|g" "$EXECUTOR_PROMPT_FILE"
|
||||
fi
|
||||
}
|
||||
|
||||
fix_prompt() {
|
||||
local errors="$1"
|
||||
{
|
||||
echo "Your previous attempt at this phase was rejected by the harness."
|
||||
echo "The failures from the last attempt are below. Review them, fix the code, and re-run the full test suite and linter until everything is green. Do not start other phases' work."
|
||||
echo "Your previous attempt at this task was rejected by the harness."
|
||||
echo "The failures from the last attempt are below. Review them, fix the code, and re-run the full test suite and linter until everything is green. Do not start other tasks' work."
|
||||
echo
|
||||
echo "Failure output (may be truncated):"
|
||||
echo '```'
|
||||
printf '%s\n' "$errors" | tail -c 6000
|
||||
echo '```'
|
||||
echo
|
||||
echo "When everything is green, reply with the same report as before (at most 15 lines: what was fixed, test/lint/coverage results, notable decisions, next pending phase)."
|
||||
echo "When everything is green, reply with the same report as before (at most 15 lines: what was fixed, test/lint/coverage results, notable decisions, next pending task)."
|
||||
}
|
||||
}
|
||||
|
||||
# --- child executor -----------------------------------------------------------
|
||||
# run_child <phase> <attempt> <prompt> [resume-session]
|
||||
# run_child <unit> <attempt> <prompt> [resume-session]
|
||||
# Attempt 1: fresh session in .agent/phase-sessions/.
|
||||
# Attempt N>1: resumes the given session file — the failed executor's own
|
||||
# session, tracked by execute_phase (so retries keep its work). When no
|
||||
# session, tracked by execute_unit (so retries keep its work). When no
|
||||
# session was captured (or FRESH_FIX=1) a fresh ephemeral session runs with
|
||||
# the failure context instead.
|
||||
# Progress (tool calls, assistant text, thinking, compaction) streams to the
|
||||
# terminal live via scripts/progress.mjs, which also writes the final
|
||||
# assistant message to: .agent/reports/<base>.a<attempt>.md
|
||||
# Child stderr → .agent/reports/<base>.a<attempt>.err
|
||||
# assistant message to: unit_report <unit> <attempt> md
|
||||
# Child stderr → unit_report <unit> <attempt> err
|
||||
# QUIET=1 suppresses the progress display (report file is still written).
|
||||
# Sets CHILD_RC (pi's exit code) and PROGRESS_RC (progress.mjs's exit code;
|
||||
# non-zero means the stream ended without a clean final report).
|
||||
@@ -141,21 +254,23 @@ _run_pi_pipeline() {
|
||||
# trap had fired, so the failure is always visible.
|
||||
if (( CHILD_RC == 130 || PROGRESS_RC == 130 )); then
|
||||
echo
|
||||
echo "✗ ERROR: interrupted (SIGINT) — phase is left in $PHASE_TODO/; re-run to continue" >&2
|
||||
echo "✗ ERROR: interrupted (SIGINT) — unit is left in $PHASE_TODO/; re-run to continue" >&2
|
||||
exit 130
|
||||
fi
|
||||
if (( CHILD_RC == 143 || PROGRESS_RC == 143 )); then
|
||||
echo
|
||||
echo "✗ ERROR: interrupted (SIGTERM) — phase is left in $PHASE_TODO/; re-run to continue" >&2
|
||||
echo "✗ ERROR: interrupted (SIGTERM) — unit is left in $PHASE_TODO/; re-run to continue" >&2
|
||||
exit 143
|
||||
fi
|
||||
}
|
||||
|
||||
run_child() {
|
||||
local phase="$1" attempt="$2" prompt="$3" resume_session="${4:-}"
|
||||
local base="${phase%.md}"
|
||||
local out="$PHASE_REPORTS/$base.a$attempt.md"
|
||||
local errf="$PHASE_REPORTS/$base.a$attempt.err"
|
||||
local unit="$1" attempt="$2" prompt="$3" resume_session="${4:-}"
|
||||
local base out errf
|
||||
base="$(unit_base "$unit")"
|
||||
out="$(unit_report "$unit" "$attempt" md)"
|
||||
errf="$(unit_report "$unit" "$attempt" err)"
|
||||
mkdir -p "$(dirname "$out")"
|
||||
local PROGRESS=(node "$SKILL_DIR/scripts/progress.mjs" "$out")
|
||||
[[ "${QUIET:-0}" == "1" ]] && PROGRESS+=(--quiet)
|
||||
|
||||
@@ -176,7 +291,7 @@ ensure_validate() {
|
||||
cp "$SKILL_DIR/assets/validate.sh" .agent/validate.sh
|
||||
chmod +x .agent/validate.sh
|
||||
warn "no .agent/validate.sh found — created it from the skill template."
|
||||
warn "adapt it to this project's real test/lint/coverage commands; it is the pass/fail gate for every phase."
|
||||
warn "adapt it to this project's real test/lint/coverage commands; it is the pass/fail gate after every task."
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -185,39 +300,39 @@ run_validation() {
|
||||
bash .agent/validate.sh >"$1" 2>&1
|
||||
}
|
||||
|
||||
# --- one phase, with bounded fixer retries ------------------------------------
|
||||
# execute_phase <phase-file>
|
||||
# Returns 0 and moves the phase to complete/ on success; returns 1 after
|
||||
# MAX_FIX_ATTEMPTS failed attempts (phase file is left in todo/).
|
||||
execute_phase() {
|
||||
local phase="$1"
|
||||
local base="${phase%.md}"
|
||||
local attempt=1 errors=""
|
||||
# --- one unit (task, phase final pass, or legacy phase), with retries ---------
|
||||
# execute_unit <unit>
|
||||
# Returns 0 and moves the unit to complete/ on success; returns 1 after
|
||||
# MAX_FIX_ATTEMPTS failed attempts (unit file is left in todo/).
|
||||
execute_unit() {
|
||||
local unit="$1"
|
||||
local base attempt=1 errors=""
|
||||
local last_session="" pre post
|
||||
command -v node >/dev/null 2>&1 || die "node not found on PATH (needed to render phase progress)"
|
||||
mkdir -p "$PHASE_DONE" "$PHASE_REPORTS" "$PHASE_SESSIONS"
|
||||
base="$(unit_base "$unit")"
|
||||
command -v node >/dev/null 2>&1 || die "node not found on PATH (needed to render task progress)"
|
||||
mkdir -p "$PHASE_DONE" "$PHASE_REPORTS" "$PHASE_SESSIONS" "$(unit_report_dir "$unit")"
|
||||
ensure_validate
|
||||
|
||||
while (( attempt <= MAX_FIX_ATTEMPTS )); do
|
||||
echo "━━ $phase — attempt $attempt/$MAX_FIX_ATTEMPTS ━━"
|
||||
echo "━━ $unit — attempt $attempt/$MAX_FIX_ATTEMPTS ━━"
|
||||
pre="$(latest_child_session)"
|
||||
if (( attempt == 1 )); then
|
||||
run_child "$phase" 1 "$(first_prompt "$phase")"
|
||||
run_child "$unit" 1 "$(first_prompt "$unit")"
|
||||
else
|
||||
run_child "$phase" "$attempt" "$(fix_prompt "$errors")" "$last_session"
|
||||
run_child "$unit" "$attempt" "$(fix_prompt "$errors")" "$last_session"
|
||||
fi
|
||||
# Track which session file this attempt used, so the next attempt resumes
|
||||
# exactly this phase's failed session (not just "latest in the directory").
|
||||
# exactly this unit's failed session (not just "latest in the directory").
|
||||
post="$(latest_child_session)"
|
||||
if [[ -n "$post" && "$post" != "$pre" ]]; then
|
||||
last_session="$post"
|
||||
fi
|
||||
|
||||
# The child can be signaled mid-flush: the session file then holds a final
|
||||
# report the stream lost. Recover it so a completed phase is not retried.
|
||||
if (( CHILD_RC == 0 )) && [[ -f "$PHASE_REPORTS/$base.a$attempt.md" ]] \
|
||||
&& grep -q "no final assistant message" "$PHASE_REPORTS/$base.a$attempt.md"; then
|
||||
if [[ -n "$last_session" ]] && recover_report "$PHASE_REPORTS/$base.a$attempt.md" "$last_session"; then
|
||||
# report the stream lost. Recover it so a completed unit is not retried.
|
||||
if (( CHILD_RC == 0 )) && [[ -f "$(unit_report "$unit" "$attempt" md)" ]] \
|
||||
&& grep -q "no final assistant message" "$(unit_report "$unit" "$attempt" md)"; then
|
||||
if [[ -n "$last_session" ]] && recover_report "$(unit_report "$unit" "$attempt" md)" "$last_session"; then
|
||||
warn "stream lost the final message — report recovered from ${last_session##*/}"
|
||||
PROGRESS_RC=0
|
||||
fi
|
||||
@@ -225,40 +340,44 @@ execute_phase() {
|
||||
|
||||
errors=""
|
||||
if (( CHILD_RC != 0 )); then
|
||||
warn "child pi exited with code $CHILD_RC — see $PHASE_REPORTS/$base.a$attempt.err"
|
||||
errors+="[child pi exited with code $CHILD_RC]"$'\n'"$(tail -c 4000 "$PHASE_REPORTS/$base.a$attempt.err" 2>/dev/null)"
|
||||
warn "child pi exited with code $CHILD_RC — see $(unit_report "$unit" "$attempt" err)"
|
||||
errors+="[child pi exited with code $CHILD_RC]"$'\n'"$(tail -c 4000 "$(unit_report "$unit" "$attempt" err)" 2>/dev/null)"
|
||||
fi
|
||||
if (( PROGRESS_RC != 0 )); then
|
||||
warn "child run ended without a clean final report — see $PHASE_REPORTS/$base.a$attempt.md"
|
||||
errors+="[child run ended without a clean final report]"$'\n'"$(tail -c 4000 "$PHASE_REPORTS/$base.a$attempt.err" 2>/dev/null)"
|
||||
warn "child run ended without a clean final report — see $(unit_report "$unit" "$attempt" md)"
|
||||
errors+="[child run ended without a clean final report]"$'\n'"$(tail -c 4000 "$(unit_report "$unit" "$attempt" err)" 2>/dev/null)"
|
||||
fi
|
||||
if ! run_validation "$PHASE_REPORTS/$base.a$attempt.validate"; then
|
||||
warn ".agent/validate.sh FAILED — see $PHASE_REPORTS/$base.a$attempt.validate"
|
||||
errors+="[.agent/validate.sh FAILED]"$'\n'"$(tail -n 120 "$PHASE_REPORTS/$base.a$attempt.validate" 2>/dev/null)"
|
||||
if ! run_validation "$(unit_report "$unit" "$attempt" validate)"; then
|
||||
warn ".agent/validate.sh FAILED — see $(unit_report "$unit" "$attempt" validate)"
|
||||
errors+="[.agent/validate.sh FAILED]"$'\n'"$(tail -n 120 "$(unit_report "$unit" "$attempt" validate)" 2>/dev/null)"
|
||||
fi
|
||||
|
||||
if [[ -z "$errors" ]]; then
|
||||
mv -f "$PHASE_TODO/$phase" "$PHASE_DONE/$phase"
|
||||
echo "✓ $phase → complete"
|
||||
move_unit "$unit"
|
||||
if is_phase_end "$unit"; then
|
||||
echo "✓ $unit → complete (phase $(unit_phase "$unit") done)"
|
||||
if [[ "${PHASE_COMMIT:-0}" == "1" ]]; then
|
||||
if git add -A 2>/dev/null && git commit --no-gpg-sign -m "phase: $phase" >/dev/null 2>&1; then
|
||||
if git add -A 2>/dev/null && git commit --no-gpg-sign -m "phase: $(unit_phase "$unit")" >/dev/null 2>&1; then
|
||||
echo " (committed)"
|
||||
else
|
||||
warn "git commit failed (continuing)"
|
||||
fi
|
||||
fi
|
||||
else
|
||||
echo "✓ $unit → complete"
|
||||
fi
|
||||
echo "── executor report ──"
|
||||
cat "$PHASE_REPORTS/$base.a$attempt.md"
|
||||
cat "$(unit_report "$unit" "$attempt" md)"
|
||||
return 0
|
||||
fi
|
||||
attempt=$(( attempt + 1 ))
|
||||
done
|
||||
|
||||
{
|
||||
echo "✗ $phase FAILED after $MAX_FIX_ATTEMPTS attempts — left in $PHASE_TODO/."
|
||||
echo "✗ $unit FAILED after $MAX_FIX_ATTEMPTS attempts — left in $PHASE_TODO/."
|
||||
echo " last errors:"
|
||||
printf '%s\n' "$errors" | tail -n 40 | sed 's/^/ /'
|
||||
echo " logs: $PHASE_REPORTS/$base.a*.{md,err,validate}"
|
||||
echo " logs: $(unit_report_dir "$unit")/$(unit_base "$unit").a*.{md,err,validate}"
|
||||
if [[ -n "${last_session:-}" && -f "${last_session:-}" ]]; then
|
||||
echo " resume: re-run this script (it auto-resumes the failed session), or manually:"
|
||||
echo " pi --session $last_session -c \"review the failures above, fix them, re-validate\""
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
#!/usr/bin/env bash
|
||||
# run-phase.sh — execute exactly one phase in a fresh pi context.
|
||||
# run-phase.sh — run every remaining task of one phase to completion.
|
||||
#
|
||||
# Each task runs in its own pi process (fresh context) with the
|
||||
# .agent/validate.sh gate after every task; the phase ends with its
|
||||
# 00_phase.md final pass.
|
||||
#
|
||||
# Usage:
|
||||
# run-phase.sh # first pending phase (alphanumerical order)
|
||||
# run-phase.sh 03_api.md # a specific pending phase (warns if out of order)
|
||||
# run-phase.sh # next phase with pending work, to completion
|
||||
# run-phase.sh 03_api # a specific phase (warns if out of order)
|
||||
# run-phase.sh 03_api.md # a legacy single-file phase
|
||||
#
|
||||
# Env: see SKILL.md (MAX_FIX_ATTEMPTS, PHASE_MODEL, PHASE_THINKING,
|
||||
# PHASE_COMMIT, PI_TRUST, FRESH_FIX).
|
||||
@@ -20,22 +25,29 @@ cd "$(find_root)" || die "no .agent/phases/todo found in this or parent director
|
||||
|
||||
phase="${1:-}"
|
||||
if [[ -n "$phase" ]]; then
|
||||
[[ "$phase" == *.md ]] || phase="$phase.md"
|
||||
[[ -f "$PHASE_TODO/$phase" ]] || die "$phase not found in $PHASE_TODO/"
|
||||
first="$(next_phase)"
|
||||
if [[ -n "$first" && "$first" != "$phase" ]]; then
|
||||
phase="${phase%/}"; phase="${phase%.md}"; phase="${phase%%/*}"
|
||||
[[ -d "$PHASE_TODO/$phase" || -f "$PHASE_TODO/$phase.md" ]] || die "$phase not found in $PHASE_TODO/"
|
||||
first="$(next_unit)"
|
||||
if [[ -n "$first" && "${first%%/*}" != "$phase" ]]; then
|
||||
warn "dependency order: $first is pending before $phase — running out of order"
|
||||
fi
|
||||
else
|
||||
phase="$(next_phase)"
|
||||
[[ -n "$phase" ]] || { echo "✓ no pending phases in $PHASE_TODO/ — project complete."; exit 0; }
|
||||
unit="$(next_unit)"
|
||||
[[ -n "$unit" ]] || { echo "✓ no pending tasks in $PHASE_TODO/ — project complete."; exit 0; }
|
||||
phase="$(unit_phase "$unit")"
|
||||
fi
|
||||
|
||||
build_pi_args
|
||||
if execute_phase "$phase"; then
|
||||
exit 0
|
||||
fi
|
||||
echo "✗ ERROR: phase $phase FAILED after $MAX_FIX_ATTEMPTS attempts" >&2
|
||||
echo " reports: $PHASE_REPORTS/${phase%.md}.a*.{md,err,validate}" >&2
|
||||
failed=0
|
||||
while unit="$(phase_next_unit "$phase")"; do
|
||||
[[ -n "$unit" ]] || break
|
||||
execute_unit "$unit" || { failed=1; break; }
|
||||
done
|
||||
if (( failed )); then
|
||||
echo "✗ ERROR: phase $phase FAILED — see error above" >&2
|
||||
echo " reports: $(unit_report_dir "$unit")/$(unit_base "$unit").a*.{md,err,validate}" >&2
|
||||
echo " resume: re-run this script — the failed executor session is resumed automatically" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "✓ phase $phase complete"
|
||||
exit 0
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
#!/usr/bin/env bash
|
||||
# run-task.sh — execute exactly one task in a fresh pi context.
|
||||
#
|
||||
# A task is the smallest execution unit: a task file inside a phase
|
||||
# directory (03_api/02_routes.md), a phase's 00_phase.md final pass, or a
|
||||
# legacy single-file phase.
|
||||
#
|
||||
# Usage:
|
||||
# run-task.sh # next pending task (pipeline order)
|
||||
# run-task.sh 03_api/02_routes.md # a specific task (warns if out of order)
|
||||
# run-task.sh 03_api/02_routes # ".md" is added when missing
|
||||
#
|
||||
# Env: see SKILL.md (MAX_FIX_ATTEMPTS, PHASE_MODEL, PHASE_THINKING,
|
||||
# PHASE_COMMIT, PI_TRUST, FRESH_FIX).
|
||||
set -uo pipefail
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
source "$SCRIPT_DIR/lib.sh"
|
||||
|
||||
# Make interruptions visible: state stays in .agent/phases/todo, and the
|
||||
# failed executor's session is still resumable on the next run.
|
||||
trap 'echo; echo "✗ ERROR: interrupted (SIGINT) — ${unit:-this task} is left in $PHASE_TODO/; re-run to continue" >&2; exit 130' INT
|
||||
trap 'echo; echo "✗ ERROR: interrupted (SIGTERM) — ${unit:-this task} is left in $PHASE_TODO/; re-run to continue" >&2; exit 143' TERM
|
||||
|
||||
cd "$(find_root)" || die "no .agent/phases/todo found in this or parent directories (run /to-phase or /audit-create first)"
|
||||
|
||||
unit="${1:-}"
|
||||
if [[ -n "$unit" ]]; then
|
||||
[[ "$unit" == *.md ]] || unit="$unit.md"
|
||||
[[ -f "$PHASE_TODO/$unit" ]] || die "$unit not found in $PHASE_TODO/"
|
||||
first="$(next_unit)"
|
||||
if [[ -n "$first" && "$first" != "$unit" ]]; then
|
||||
warn "execution order: $first is pending before $unit — running out of order"
|
||||
fi
|
||||
else
|
||||
unit="$(next_unit)"
|
||||
[[ -n "$unit" ]] || { echo "✓ no pending tasks in $PHASE_TODO/ — project complete."; exit 0; }
|
||||
fi
|
||||
|
||||
build_pi_args
|
||||
if execute_unit "$unit"; then
|
||||
exit 0
|
||||
fi
|
||||
echo "✗ ERROR: task $unit FAILED after $MAX_FIX_ATTEMPTS attempts" >&2
|
||||
echo " reports: $(unit_report_dir "$unit")/$(unit_base "$unit").a*.{md,err,validate}" >&2
|
||||
echo " resume: re-run this script — the failed executor session is resumed automatically" >&2
|
||||
exit 1
|
||||
Reference in New Issue
Block a user