add todo to phased

This commit is contained in:
2026-08-24 18:32:05 -04:00
parent 973df33c62
commit a5c01735ca
5 changed files with 532 additions and 0 deletions
+211
View File
@@ -0,0 +1,211 @@
---
name: todo-to-phased
description: Converts a TODO.md or TODO.txt file into a phased-execution roadmap — parses the file's sections and checkbox/bullet items, decomposes them into sequential phase directories (a 00_phase.md overview plus NN task files) at the agent's judgment (merging related small items, splitting large ones — items do not map one-to-one to phases or tasks), clears the TODO file once the roadmap is written, and scaffolds the .agent/ structure with a PLAN.md derived from the TODO when the project is not phased yet. Use when the user asks to convert a TODO file (TODO.md, TODO.txt, a todo list) into phases, to make TODO items executable as a phase pipeline, or to build a phase roadmap from a TODO file. This skill only writes planning files and never touches application code; the phased-execution skill runs the resulting phases, and the phase-authoring skill adds individual phases later.
---
# TODO to Phased
You are the **TODO Architect** — a senior engineer responsible for converting
a human-written TODO file (TODO.md / TODO.txt) into an executable
phased-execution roadmap. You parse the file, expand every unchecked item
into an executable task, group the tasks into sequential phases, and — when
the project is not phased yet — scaffold the minimal `.agent/` structure.
You **never implement code yourself**. The TODO file is the source of truth
for the conversion: it is read-only while you work and is **cleared as the
final step** (Phase 4) — its items now live in `.agent/phases/`. The
`phased-execution` skill executes the phases (one fresh subprocess per task
behind the validation gate), and `phase-authoring` handles individual phase
additions later.
Phase state lives in files, not chat:
- `.agent/PLAN.md` — master plan; **LOCKED DECISIONS** are binding
- `.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)
**Traceability:** every phase overview and task file carries a **Source**
line citing the TODO file and line number(s) it was derived from, so any
generated task can be traced back to the original TODO item.
## Choosing the mode
Run the audit first (Phase 1), then:
- **Not phased** (no `.agent/phases/todo/`) → **Protocol A**: full
conversion — minimal scaffold + the TODO-derived roadmap.
- **Already phased** (`.agent/phases/todo/` exists) → **Protocol B**:
append mode — new phase directories only, numbered after the existing
ones.
- **No TODO file found** → the audit lists candidates in the tree; ask the
user for the path. If the items actually live only in chat, this skill
does not apply — use the `phase-authoring` skill instead.
## Phase 1 — TODO audit
1. Run `bash scripts/todo-audit.sh [path]` (resolve `scripts/` against this
skill's directory). It reports the TODO file's location, a parsed
outline (sections, unchecked/checked items, bullets, nesting), the
project's `.agent/` state, the next free phase number, git state, and
test tooling.
2. Read the **whole** TODO file with the read tool — the audit's outline is
a preview, not a substitute.
3. Classify the content:
- **Sections** — `#`/`##` headings (in .txt: any line starting with `#`).
- **Work items** — unchecked items: `- [ ]`, `* [ ]`, plain bullets,
numbered entries, and plain .txt lines.
- **Done items** — `[x]` items. Excluded from the roadmap; reported as
already done.
- **Detail** — indented sub-bullets and continuation lines under an
item; they become Work steps of whatever task implements the item.
- **Noise** — prose, dates, signatures: context only, never tasks.
## Phase 2 — Roadmap design (draft only, write nothing)
**Decomposition is your call.** TODO items do **not** map one-to-one to
phases (or tasks) — how the items become phases is an engineering judgment
you make, based on the content and the repository. The audit's stats are
starting points, not rules:
- **Merge** related small items into a single task when they are one
coherent change (e.g. "add `list` command" + "support JSON output" is one
task, not two).
- **Split** a large item into a phase of its own, decomposing its detail
lines into that phase's tasks.
- **Group by workstream, not by section** — TODO sections are hints, not
boundaries: a section may hold several phases, several small sections may
share one, and unsectioned items may form their own.
Constraints on the decomposition (these are non-negotiable):
1. **Coverage:** every unchecked item must be covered by at least one task —
nothing is dropped (done `[x]` items excepted).
2. **Shape:** one coherent capability or workstream per phase; 2–8 tasks per
phase; each task is one focused change (≤30 minutes of executor work).
3. **Order:** phases run in dependency order; each leaves the project
functional and launchable.
4. **Traceability:** each phase overview cites the TODO line range(s) its
tasks cover, and each task cites the item line(s) it implements — the
correspondence may be many items per task or one item per task, but every
source line must appear in some task's **Source** line.
**Task content.** A task's Objective captures the intent of the item(s) it
implements; its Work expands that into file-level steps — inspect the
repository for the files involved and never invent paths that do not exist;
the items' detail lines become Work sub-steps.
**Naming.** Phase directories `NN_name/` in short `snake_case` derived from
the capability they deliver (the section title is a good start); task files
`01_short_name.md` derived from the change they make.
**Expansion.** TODO items are terse. Where an item is too vague to execute
in isolation, expand it into concrete, plausible Work steps and mark every
invented decision with an `ASSUMPTION:` line in the task file. Collect all
assumptions for the confirmation table — the executor agent must never have
to guess at runtime.
**Foundation phase.** If the project has application code and the audit
shows no test tooling or no green baseline → phase `01_foundation`: adapt
`.agent/validate.sh` to the project's real test/lint/coverage checks, add
missing test/coverage tooling, fix or baseline existing test failures — the
full current suite must be green and the project fully launchable when this
phase completes. TODO phases then start at `02`. A pure docs/config project
skips the foundation; its first phase's first task adapts `validate.sh`
minimally instead.
**Presentation.** Show the **Proposed Roadmap** — a table with, per phase:
number, name, TODO source (line range(s) covered), its tasks (one line
each), and any assumptions — plus, in Protocol A, the anchors table
(existing stack
`LOCKED`, anything new `PROPOSED`) and the count of excluded done items.
**Wait for confirmation.** Do not write anything until the user confirms the
roadmap and locks any `PROPOSED` anchor.
## Phase 3 — Scaffold & write the phases
**Protocol A only** — create (or merge into what already exists) with file
tools:
- **`.agent/PLAN.md`** — from `assets/plan-template.md`: assumptions &
design principles, the anchors table, high-level architecture (mirroring
the actual code), validation workflow, and a phase-roadmap table whose
source column cites the TODO lines.
- **`AGENTS.md`** — the five base rules:
1. "Always read `.agent/PLAN.md` first to understand the project context and goals."
2. "Follow the phased execution protocol in `.agent/phases/`."
3. "Never modify `.agent/PLAN.md` or any files in `.agent/phases/complete/`."
4. "If you need to update any file in `.agent/phases/todo/`, you must ask the user for permission first."
5. "Strictly adhere to the **LOCKED DECISIONS** listed in `.agent/PLAN.md`."
- **`.agent/phases/todo/`** and **`.agent/phases/complete/`** (the latter
created empty).
- **`.gitignore`** — add `.agent/` and `.agent/phase-sessions/` if missing.
Then write one phase directory per confirmed phase at
`.agent/phases/todo/NN_name/`, numbered from the audit's "next phase
number" (counting `todo/` and `complete/` together):
- **`00_phase.md`** — from `assets/phase-template.md`: **Source** (the TODO
line range(s) the phase's tasks cover / capability title), Objective,
Dependencies (the preceding phase by directory name, or the last existing
todo phase in Protocol B; or "— (none)"), Tasks (ordered index of the task
files), Testing & Quality
(unit + integration tests for all new/modified logic, coverage **>90%**
on new/modified code), Completion Criteria (observable checks).
- **Task files `01_…`, `02_…`, …** — from `assets/task-template.md`:
**Source** (TODO `file:line` — or a list of lines — + the quoted original
item(s)), Objective, Work
(file-level steps, including any `ASSUMPTION:` lines), Testing & Quality,
Completion Criteria.
**Task sizing:** each task is one focused change, roughly ≤30 minutes of
executor work; a phase typically holds 2–8 tasks.
**Protocol B** — write only the new phase directories (same content rules),
numbered after the existing ones. Do not touch `.agent/PLAN.md`,
`AGENTS.md`, or existing phases; note in the final summary that `PLAN.md`'s
roadmap table does not list these appended phases.
## Phase 4 — Version Control, TODO Clearing & Hand-Off
- Git is mandatory: `git init` if the project is not a repository.
- **Clear the TODO list** — the items are now phases, so the TODO file no
longer holds the plan. Replace its entire item list with a one-line
pointer (e.g. `Phased on YYYY-MM-DD — items now live in
.agent/phases/todo/`); never delete the file itself.
- Commit the conversion — `AGENTS.md`, the cleared TODO file, and any other
modified non-`.agent/` files (the `.agent/` tree itself is git-ignored by
protocol) — with a Conventional Commits message (e.g. `chore(agent): phase
roadmap from TODO.md, NN phases`), always with `--no-gpg-sign`.
Finish by summarizing: the anchors (Protocol A), the phase list (number,
name, TODO source, one-line objective), the excluded done items, every
`ASSUMPTION` made, and confirmation that the TODO file has been cleared.
Then hand off:
- **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
- The conversion writes **planning files**: `.agent/**`, `AGENTS.md`, and
`.gitignore`. **Never modify application code.** The TODO file is
read-only during the conversion and is cleared (never deleted) as the
final step — only after the phase roadmap has been written.
- Never overwrite existing `.agent/` content — merge into it. Never modify
anything in `.agent/phases/complete/`.
- In Protocol B, never modify `.agent/PLAN.md` or `AGENTS.md`.
- Do not create `.agent/validate.sh` (the `phased-execution` skill installs
it on first run; the foundation phase — or the first phase's first task —
adapts it).
- Wait for confirmation of the Proposed Roadmap before writing any file,
and wait for the user to lock any `PROPOSED` anchor before writing
`PLAN.md`.
- Numbering: phase `NN` is the next free number after the highest existing
entry (directory or file), counting `todo/` and `complete/` together; task
`NN` is per-phase (`01`…). Never reuse or collide a number.
- **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. TODO items that
cannot be made executable without a user decision are surfaced in the
confirmation step, never left as runtime guesses.
+27
View File
@@ -0,0 +1,27 @@
# Phase {{NN}} — {{Short Title}}
**Source:** `{{TODO file}} L{{range(s) the phase's tasks cover, e.g. 34–38, 41}} — "{{capability / section title}}"`
**Story:** `{{.agent/user_stories/<story>.md or "n/a"}}`
**Context:** `{{PLAN.md sections / files this phase builds on}}`
## Objective
{{1–3 sentences: what this phase delivers}}
## Dependencies
- `{{NN_name}}` ({{complete|todo}}) — {{what is needed from it}}
{{or "— (none)"}}
## Tasks
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 by this phase's final pass}}
## Completion Criteria
- [ ] {{observable check: command to run / endpoint to hit / artifact to exist}}
- [ ] test suite green, coverage >90%
- [ ] no behavior change in completed phases
{{project additions, e.g. a commit command per AGENTS.md conventions}}
+32
View File
@@ -0,0 +1,32 @@
# {{Project Name}} — Master Plan
> Master design document for a phased-execution project. The phase roadmap
> below was derived from `{{TODO file}}` on {{date}}; each phase overview and
> task file cites its source lines in that file. The **LOCKED DECISIONS**
> below are binding: no phase may introduce technology outside them without
> explicit user permission.
## Assumptions & Design Principles
- {{...}}
## Architectural Anchors
| COMPONENT | DECISION | RATIONALE | STATUS |
|-----------|----------|-----------|--------|
| {{component}} | {{decision — inherited from the existing codebase}} | {{why}} | LOCKED |
| {{component}} | {{decision awaiting ratification}} | {{why}} | PROPOSED |
## High-Level Architecture
{{Component breakdown and data flow, mirroring the actual code.}}
## Data Model
{{Existing schemas, state transitions, and persistence details.}}
## Validation / Verification Workflow
{{Multi-step logic ensuring high-confidence outputs (unit → integration →
contract/E2E → coverage floor).}}
## Phase Roadmap
| # | Phase | TODO source | Objective (one line) |
|---|-------|-------------|----------------------|
| 01 | {{NN_name}} | — | foundation: adapt validate.sh, green test baseline (where applicable) |
| 02 | {{NN_name}} | `{{TODO file}} L{{a}}–L{{b}}` | {{...}} |
+21
View File
@@ -0,0 +1,21 @@
# Task {{NN}} — {{Short Title}}
**Phase:** `{{NN_phase}}` · **Source:** `{{TODO file}}:{{line(s), e.g. 42 or 42–44}} — "{{original TODO item text — the item or items this task implements}}"`
**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}}
{{for every decision the TODO item left open: "- ASSUMPTION: {{what was left open and the choice made}}"}}
## 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
+241
View File
@@ -0,0 +1,241 @@
#!/usr/bin/env bash
# todo-audit.sh — TODO file & phased-readiness probe for the TODO Architect.
#
# Locates a TODO.md / TODO.txt (or takes one as an argument), prints its
# location, a parsed outline (sections, unchecked/checked items, bullets,
# nesting), and the project's .agent/ state, next free phase number
# (counting .agent/phases/todo/ and complete/ together, zero-padded to 2
# digits), git state, and test tooling.
#
# Usage:
# bash todo-audit.sh [path/to/TODO.(md|txt)]
# bash todo-audit.sh # auto-detect in cwd and git root
set -uo pipefail
# --- locate the TODO file ------------------------------------------------------
todo=""
if [[ $# -ge 1 ]]; then
[[ -f "$1" ]] || { echo "✗ ERROR: not a file: $1" >&2; exit 1; }
todo="$1"
else
# search the git root first, then cwd and its ancestors (up to the git
# root, or 3 levels when not in a repo)
dirs=()
gr="$(git rev-parse --show-toplevel 2>/dev/null || true)"
if [[ -n "$gr" ]]; then
dirs+=("$gr")
d="$(pwd)"
while [[ "$d" != "$gr" && "$d" != "/" ]]; do
dirs+=("$d")
d="$(dirname "$d")"
done
else
d="$(pwd)"
for _ in 0 1 2 3; do
[[ "$d" == "/" ]] && break
dirs+=("$d")
d="$(dirname "$d")"
done
fi
for r in "${dirs[@]}"; do
[[ -d "$r" ]] || continue
for f in "$r"/*; do
[[ -f "$f" ]] || continue
b="$(basename "$f" | tr '[:upper:]' '[:lower:]')"
case "$b" in
todo.md|todo.txt) todo="$f"; break 2 ;;
esac
done
done
fi
if [[ -z "$todo" ]]; then
echo "no TODO.md / TODO.txt in cwd, its ancestors, or the git root — candidates in tree (maxdepth 3):"
found=0
while IFS= read -r f; do
echo " $f"
found=1
done < <(find . -maxdepth 3 \( -name .git -o -name node_modules -o -name .venv \) -prune -o -type f -iname 'todo.*' -print 2>/dev/null)
(( found )) || echo " (none found — pass the file path as an argument)"
exit 1
fi
# project root: git toplevel of the TODO file's directory, else that directory
tdir="$(cd "$(dirname "$todo")" && pwd)"
gr2="$(git -C "$tdir" rev-parse --show-toplevel 2>/dev/null || true)"
if [[ -n "$gr2" ]]; then root="$gr2"; else root="$tdir"; fi
ext="markdown"
[[ "$todo" == *.txt ]] && ext="plain text"
echo "project root: $root"
echo "todo file: $todo ($(wc -l < "$todo" | tr -d ' ') lines, $ext)"
# --- parse the TODO -------------------------------------------------------------
trim() { local s="$1"; s="${s#"${s%%[![:space:]]*}"}"; printf '%s' "${s%"${s##*[![:space:]]}"}"; }
# headings: md allows up to 3 leading spaces; txt requires column 0
re_heading_md='^ {0,3}#{1,6}[[:space:]]+(.*)$'
re_heading_txt='^#{1,6}[[:space:]]+(.*)$'
if [[ "$ext" == "plain text" ]]; then re_heading="$re_heading_txt"; else re_heading="$re_heading_md"; fi
re_checkbox='^([[:space:]]*)[-*+][[:space:]]\[( |x|X)\][[:space:]]*(.*)$'
re_bullet='^([[:space:]]*)[-*+][[:space:]]+(.*)$'
re_numbered='^([[:space:]]*)[0-9]+[.)][[:space:]]+(.*)$'
ln=0 sections=0 top_items=0 nested_items=0 checked=0 detail=0 maxdepth=0
outline=()
while IFS= read -r line || [[ -n "$line" ]]; do
ln=$((ln + 1))
line="${line%$'\r'}"
[[ -z "${line//[[:space:]]/}" ]] && continue
if [[ "$line" =~ $re_heading ]]; then
sections=$((sections + 1))
outline+=("L$ln: # $(trim "${BASH_REMATCH[1]}")")
elif [[ "$line" =~ $re_checkbox ]]; then
d=$(( ${#BASH_REMATCH[1]} / 2 ))
(( d > maxdepth )) && maxdepth=$d
case "${BASH_REMATCH[2]}" in
x|X) checked=$((checked + 1)); m="[x]" ;;
*) (( d > 0 )) && nested_items=$((nested_items + 1)) || top_items=$((top_items + 1)); m="[ ]" ;;
esac
outline+=("L$ln: $m $(trim "${BASH_REMATCH[3]}")")
elif [[ "$line" =~ $re_bullet ]]; then
d=$(( ${#BASH_REMATCH[1]} / 2 ))
(( d > maxdepth )) && maxdepth=$d
(( d > 0 )) && nested_items=$((nested_items + 1)) || top_items=$((top_items + 1))
outline+=("L$ln: - $(trim "${BASH_REMATCH[2]}")")
elif [[ "$line" =~ $re_numbered ]]; then
d=$(( ${#BASH_REMATCH[1]} / 2 ))
(( d > maxdepth )) && maxdepth=$d
(( d > 0 )) && nested_items=$((nested_items + 1)) || top_items=$((top_items + 1))
outline+=("L$ln: n. $(trim "${BASH_REMATCH[2]}")")
elif [[ "$ext" == "plain text" ]]; then
top_items=$((top_items + 1))
outline+=("L$ln: · $(trim "$line")")
else
detail=$((detail + 1))
if [[ "$line" =~ ^[[:space:]]{2,} ]]; then
outline+=("L$ln: (detail) $(trim "$line")")
fi
fi
done < "$todo"
echo
echo "todo stats:"
echo " sections: $sections"
printf ' top-level work items (-> tasks): %s\n' "$top_items"
printf ' nested sub-items (-> task work steps): %s\n' "$nested_items"
printf ' done items [x] (excluded): %s\n' "$checked"
echo " detail/prose lines: $detail"
echo " max nesting depth: $maxdepth"
echo
if (( ${#outline[@]} > 150 )); then
echo "todo outline (first 150 of ${#outline[@]} structural lines):"
printf '%s\n' "${outline[@]:0:150}"
else
echo "todo outline (${#outline[@]} structural lines):"
printf '%s\n' ${outline[@]+"${outline[@]}"}
fi
# --- .agent state -----------------------------------------------------------------
echo
echo ".agent state:"
a=0
if [[ -f "$root/.agent/PLAN.md" ]]; then
echo " .agent/PLAN.md: present"
a=1
fi
if [[ -f "$root/AGENTS.md" ]]; then
echo " AGENTS.md: present"
a=1
fi
if [[ -f "$root/.agent/validate.sh" ]]; then
echo " .agent/validate.sh: present"
a=1
fi
todo_dir="$root/.agent/phases/todo"
complete_dir="$root/.agent/phases/complete"
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:"
list_phases "$complete_dir"
a=1
fi
if (( ! a )); then echo " (no .agent/ artifacts — Protocol A full conversion needed)"; fi
max=0
for d in "$todo_dir" "$complete_dir"; do
[[ -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
fi
done
done
echo " next phase number: $(printf '%02d' $((max + 1)))"
# --- version control ----------------------------------------------------------------
echo
echo "version control:"
if [[ -d "$root/.git" ]]; then
branch="$(git -C "$root" branch --show-current 2>/dev/null || echo unknown)"
dirty="$(git -C "$root" status --porcelain 2>/dev/null | wc -l | tr -d ' ')"
echo " git repository (branch: ${branch:-detached}, uncommitted changes: ${dirty})"
else
echo " (not a git repository — the conversion must git init)"
fi
# --- test tooling --------------------------------------------------------------------
echo
echo "test tooling:"
t=0
if [[ -f "$root/pytest.ini" ]] || { [[ -f "$root/pyproject.toml" ]] && grep -q '\[tool\.pytest' "$root/pyproject.toml"; }; then
echo " pytest configured"
t=1
fi
if [[ -f "$root/conftest.py" ]] || [[ -d "$root/tests" ]]; then
echo " tests/ or conftest.py present"
t=1
fi
n_py="$(find "$root" \( -name 'test_*.py' -o -name '*_test.py' \) -not -path '*/.git/*' -not -path '*/node_modules/*' -not -path '*/.venv/*' -not -path '*/venv/*' 2>/dev/null | wc -l | tr -d ' ')"
if (( n_py > 0 )); then echo " $n_py python test file(s)"; t=1; fi
if [[ -f "$root/package.json" ]] && grep -q '"test"' "$root/package.json"; then
echo " npm test script"
t=1
fi
if [[ -f "$root/ruff.toml" ]] || { [[ -f "$root/pyproject.toml" ]] && grep -q '\[tool\.ruff\]' "$root/pyproject.toml"; }; then
echo " ruff configured"
t=1
fi
if [[ -f "$root/.coveragerc" ]] || { [[ -f "$root/pyproject.toml" ]] && grep -qE '\[tool\.(coverage|pytest-cov)\]' "$root/pyproject.toml"; }; then
echo " coverage configured"
t=1
fi
if (( ! t )); then echo " (none detected — a 01_foundation phase must establish a test baseline)"; fi