| name | plan-management |
| description | Build and maintain the executable plan as a GitHub issue graph — Epics, just-in-time Task decomposition, blocked-by dependencies, the actionable frontier, and replanning after deviations. Use this whenever creating or splitting issues, deciding what can run in parallel, wiring or changing dependencies, reacting to a task outcome that invalidates later tasks, or asking "what should agents work on next". |
Plan Management
The plan is not a document; it is the issue graph itself. Epics hold the
outline, Task issues hold executable work orders, blocked-by edges hold the
ordering, and the graph's edit history is the plan's change log. A schedule
drawn once never survives contact with reality — so this skill optimizes for
cheap, auditable plan changes, not for a perfect initial plan.
Requires GitHub CLI v2.94.0 or later (native --parent, --blocked-by,
--blocking support). Check with gh --version.
Data model
| Concept | GitHub primitive | Conventions |
|---|
| Outline item / phase | Epic issue | label type:epic; template epic.yml; holds outcome, scope, phase outline |
| Work order | Task issue, sub-issue of an Epic | label type:task; template ai-task.yml; one exec:* label |
| Ordering constraint | Issue dependency (blocked by / blocking) | the only mechanism for sequencing — never encode order in prose only |
| Ready gate | label ai:ready | present only when the brief is complete enough for a context-free agent |
| Plan exceptions | labels needs:replan, needs:human | set by executors; consumed by planner/orchestrator |
| Portfolio view | GitHub Project (optional) | group by Epic, surface Blocked markers; keep Project fields derived from issues, not the reverse |
Rolling-wave decomposition
- Create Epics for the whole outline up front — cheap, low detail, gives the
"what comes next" visibility agents need for preparation.
- Decompose an Epic into Task sub-issues only when its phase is about to
start (or when the frontier is nearly empty). Detail decays; write it late.
- Every Task must clear the planner quality bar
(self-contained, traceable to
REQ-###, bounded, partitioned, routed —
see .claude/agents/planner.md). Only then add ai:ready.
- Partition for parallelism: tasks meant to run concurrently must have
disjoint File ownership path sets. If two tasks need the same paths,
add a
blocked-by edge between them — serialization by dependency beats
merge-conflict roulette.
The frontier
Frontier = open Task issues labeled ai:ready whose blocked by issues are
all closed. This is the set an orchestrator may dispatch right now.
- Compute it with
.claude/skills/plan-management/scripts/frontier.sh, or manually per issue:
gh issue view <n> shows Blocked by: rows; each listed issue must be
CLOSED. (Dependency data is also exposed as JSON fields in gh ≥ 2.94 — run
gh issue view <n> --json with no field list to see the exact field names
your version supports.)
- Before dispatching two frontier tasks together, re-check ownership
disjointness — the graph guarantees ordering, not file safety.
The tracking graph (four edges, one rule)
The graph that plans the work also traces it afterwards. Four edge kinds,
four questions answered:
- Vertical — sub-issue parent/child: part of what?
- Horizontal —
blocked-by: waiting on what?
- Diagonal — a plain
#N reference in a body or comment: born from
what? (GitHub records the cross-reference on both timelines.)
- Needle —
Closes #<n> on the PR: became which code? (Merge closes
the issue and stitches code to work order.)
The one rule that keeps the net connected: derived issues cite the origin
as #N in one line. A fix issue born from an investigation, a follow-up
born from a review comment — each carries "found while working #N" in its
body, and the cross-reference does the rest. Diagnosis then enters from
three directions: down from the Epic through sub-issues and
dependencies, up from git blame (commit → PR → Closes issue → plan
comment), or sideways via label and date search. No extra tracking
fields: actual times stamp every event automatically, planned spans live in
Projects Start/Target fields as a view.
Intervening on a running task (three forks)
Plans are visible but ungated — lazy consensus: no objection means proceed.
When intervention is warranted, land only the conclusion, at the address
that caused it:
- (a) The approach is off → post a revised-plan comment on the Task
issue (conclusion only; the discussion itself may happen anywhere and
does not need to land).
- (b) The work order is off → edit the issue body plus the
immediate change comment (Replanning step 3 below).
- (c) An agreement is off → file a fix issue citing the discovering
task as #N, then correct it via an agreements PR — never patch a task
around a wrong agreement.
Exception gate: a task labeled risk:high stops after posting its plan
comment and waits for an approval comment before touching files
(session-orchestration skill, child protocol). The default is pass-through;
gate only the exception — inverting that ordering kills parallelism.
Command cookbook
Body files start from the canonical templates bundled with this skill:
templates/epic-body.md and templates/task-body.md (issue forms in
.github/ISSUE_TEMPLATE/ mirror the same sections but apply only to the
web UI).
gh issue create --title "Epic: <outcome>" --label "type:epic" --body-file epic-body.md
.claude/skills/plan-management/scripts/new-task.sh --dry-run \
-t "<task title>" -b task-body.md -p 12 -e cloud -d 14,15 \
--origin "#12 — first wave of this epic" --ready
gh issue create --title "<task title>" --label "type:task,exec:cloud,ai:ready" \
--body-file task-body.md --parent 12 --blocked-by 14,15
gh issue edit 23 --add-blocked-by 22
gh issue edit 23 --remove-blocked-by 14
gh issue view 23
gh issue list --label "ai:ready" --state open --json number,title,labels
Roadmap scheduling
Optional: visualize Epics and Tasks as spans on a Projects v2 roadmap. The
board stays a view of the issue graph — fields are derived from issues,
never the reverse (see the Portfolio view row in the Data model).
Bootstrap the board once per repository (idempotent: reuses the same-title
project, skips existing fields, re-links safely). init creates the DATE
fields Start date / Target date plus a single-select field Kind
(options Epic, Task) so both issue kinds are distinguishable on the
board:
scripts/setup-project.sh init
Projects v2 boards cannot be repo-owned — they always belong to a user or
org, so the board URL is github.com/orgs/<owner>/projects/<n> (or
/users/...). That is normal. init links the board to the repository,
which makes it appear in the repo's Projects tab
(https://github.com/<owner>/<repo>/projects) — look for it there. Pass
--owner <login> only to place the board under a different user/org.
Set an issue's schedule span when the Task is created during decomposition,
and update it whenever replanning moves the schedule (re-running replaces
both dates on the existing item). The same call sets Kind automatically
from the issue's labels — type:epic → Epic, type:task → Task:
scripts/setup-project.sh dates --project <number> \
--issue <n> --start 2026-07-07 --target 2026-07-11
One-time manual steps: in the project UI, add a Roadmap view, pick
Start date / Target date as its date fields, and set Group by to
Kind so Epics and Tasks render as separate swimlanes. View creation and
configuration (including Group by) are not exposed by the GitHub API, so
init cannot script this part; it reminds you on every run.
Replanning procedure
Trigger: a Task closes with recorded deviations; a needs:replan label
appears; an Epic's assumptions are invalidated; or the frontier is empty while
the Epic is unfinished.
- Read the triggering record (issue comment / PR "Deviations" section). The
trigger must already be written down — if someone asks you to replan from a
verbal report, record it on the issue first (persistence rule,
AGENTS.md).
- Walk the downstream graph: the trigger issue's
Blocking: list, its Epic's
remaining sub-issues, and any task whose File ownership or references
mention the changed area.
- For each affected issue decide: keep / modify / split / add / close.
Apply the decisions with
gh issue edit / create / close — edit the
graph, don't just talk about it. Closed-as-obsolete issues get a one-line
reason; never delete issues (history is the audit trail). Every edit to a
Task issue's body gets an immediate change comment on that issue:
what changed, why, and whether in-flight work must replan. GitHub keeps
body edit history behind the edited dropdown, but it never appears in
the timeline — the change comment is what keeps the diagnosis trail
readable top to bottom.
- Remove
ai:ready from any task whose brief is no longer accurate; restore
it only after the brief is fixed.
- Post one rationale comment on the Epic: what changed, why, and the list of
added/modified/closed issue numbers. This comment stream is the diff log of
the schedule.
- Clear the
needs:replan label from the trigger issue.
Anti-patterns
- Giant tasks ("implement the whole feature") — undispatchable and
unreviewable; split until each task fits one session/PR.
- Hidden ordering in prose ("do this after the API lands") without a
blocked-by edge — invisible to frontier computation, so it will be
violated by a parallel dispatch.
- Silent work-order edits — changing a Task issue's body without the
change comment above. The timeline no longer explains what the executing
agent saw at each point, so deviations stop being diagnosable.
- Plan-in-a-file drift — maintaining the real plan in a
plan.md while
issues rot. Session-local plan files are caches (see
session-orchestration); the graph is the truth.
- Premature detail — fully decomposing phase 4 while phase 1 is running;
you will rewrite it, and stale
ai:ready tasks are dispatch hazards.