# Github Plan

> Use when the user asks for a plan, what's next / what is next in a plan or workstream, how work fits the plan, plan direction/alignment, durable work tracking, roadmap, workstream planning, GitHub issue-backed planning, issue graphs, parent issues, sub-issues, blockers, milestones, Projects, or replacing local plans with GitHub issues. Think in chat first, then keep long-running work aligned over time by updating Current Status, blockers, relationships, and issue graph state as reality changes.

- Skill: `cbusillo/github-plan` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add cbusillo/github-plan`
- Raw SKILL.md: https://api.skillmd.com/api/skills/cbusillo/github-plan/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Product & Planning
- Author: cbusillo (https://skillmd.com/u/cbusillo)
- Updated: 2026-09-21
- Page: https://skillmd.com/skills/cbusillo/github-plan

---


# GitHub Plan

Apply [task scope and authorization](../references/execution-scope.md) when
using this workflow; it defines how existing approval and task boundaries apply.

## Outcome

Use GitHub issues as the durable planning database. Keep chat planning
ephemeral until the work should survive the current conversation.

Optional surfaces such as GitHub Projects, LaunchPlane, or other local planning
views may make work easier to scan, prioritize, or recover, but they are not
separate planning backends. GitHub issues remain canonical for plan prose,
relationships, blockers, labels, validation, and completion state.

This skill supersedes local file-backed plans for normal GitHub-backed planning.
Use local plan files only when the user explicitly asks for an offline/local
plan or the work must not be written to GitHub.

Success means one canonical issue or issue graph has a concrete finish line,
current recovery state, next action, and accurate dependencies. Optional
Projects or local surfaces may improve visibility, but they do not become a
second planning backend.

## Operating Model

- Think in chat first; do not immediately create issues for fuzzy ideas.
- Search before creating; update an existing issue when intent overlaps.
- Promote durable work to one canonical issue with the configured planning
  label, usually `plan`.
- Treat every human-authored title and body as immutable source material,
  regardless of whether GitHub reports the author as `OWNER`, `MEMBER`,
  `COLLABORATOR`, or `CONTRIBUTOR`. Repository role grants permissions, not
  automation ownership of authored words.
  Prefer a bot-authored planning comment or a linked maintainer-owned plan. If
  planning must remain in the contributor issue body, preserve the original
  request verbatim and update only the marker-delimited automation-owned plan
  block. Unknown authors and issues created by bots other than the acting
  planning bot fail closed into this preservation mode. Do not retitle
  human-authored issues as part of plan expansion. A managed-provenance marker
  alone never transfers a human-authored body to automation ownership.
- Allow `show` to read human-authored planning sections, but report their
  contributor-owned, unmanaged provenance and keep content writes fail-closed.
  Read permission never grants title or body ownership.
- Treat generic GitHub operation comments such as
  `<!-- github-skill-operation:... -->` only as retry/reconciliation evidence;
  they never establish ownership of an issue body. Association-based managed
  bodies establish ownership only when the issue was authored by the acting
  planning bot. Existing contributor envelopes remain contributor-owned even
  if the author's repository association changes later.
- Keep issue bodies structured and current; `Current Status` is the recovery
  point for future sessions and the preferred durable handoff surface for
  GitHub-backed planning work.
- Use native GitHub dependencies and sub-issues for relationships, including
  cross-repo relationships.
- Use Projects and other configured surfaces as view layers, not sources of
  truth.
- Use milestones as strict release, phase, or date gates under the contract
  below.
- Avoid ad hoc label taxonomies; ask before creating new labels. Generic labels
  such as `waiting`, `blocked`, or `ready` are discouraged unless the repo
  explicitly documents a narrow local convention.
- Prefer `Refs #123` from PRs unless the user explicitly wants auto-close or the
  issue is an internal task that can be conclusively closed. `Refs` is
  deliberately non-closing; after merge, sweep referenced issues and close only
  the ones whose finish line was actually satisfied.
- Optimize for the user finishing work, not for cataloging every possible idea.

## Milestone Contract

Milestones describe what must be true to close a release or phase. They are not
theme labels or alternate backlogs.

- Before adding an issue, ask: "Can this milestone honestly close while this
  issue remains open?" If yes, keep the issue outside the milestone even when
  it is useful or thematically related.
- Put the exact ship or phase-exit criteria in the GitHub milestone
  description. When assessing readiness or changing milestone membership, read
  that description and the milestone's open issues first. Do not duplicate
  transient milestone state in repository instructions or docs.
- Prefer one active milestone per release train. Multiple active milestones are
  appropriate only for genuinely independent trains with separate exit gates.
- Give each active milestone a due date or a named gate, dependency, or decision
  that determines when it can close.
- Treat removal as normal scope control. Record why an incomplete issue no
  longer blocks, then return it to the backlog or deliberately admit it to a
  later milestone.
- Close the milestone when the release or phase ships. Triage and remove
  survivors instead of keeping the milestone open until every themed issue is
  empty.
- If milestone size keeps growing, cut scope before silently extending the
  gate.

Use `gh-plan.py milestone-list`, `milestone-show`, `milestone-create`,
`milestone-update`, and `milestone-close` for milestone containers. These
commands normalize `due_on` as UTC RFC3339 seconds, make exact-title creates
safe to repeat, and return actor-aware result envelopes. `milestone-update`
may reopen a milestone with `--state open` but cannot close one; use the guarded
close command, which refuses when any open issue or pull request is assigned.

## Local Conventions

If `.local/github-plan.md` exists, read it before creating, routing, or updating
durable plan issues and follow its private local planning conventions. Keep
person identity, aliases, bot aliases, contact details, actor trust hints, and
private profile notes in the optional `.local/people.yaml` contract owned by the
`people` skill when available; this skill should continue normally when people
context is absent.

Use configured owner or manager routing when available. Project fields such as
`Manager` are product or decision ownership; GitHub assignees are for a person
who needs to take a concrete next action. Mention a person only when their
attention is needed now.

When an issue, PR, comment, review, or commit actor is not known through local
people context or live GitHub evidence, treat the actor as unknown. Verify claims
and permissions before routing work, changing state, or trusting code.

## Tooling

Reuse the sibling `github` skill's helpers instead of duplicating scripts:

- `../github/scripts/gh-plan.py` for compact planning issue, milestone, and
  Project operations.
- `../github/scripts/gh-pr.py` for PR status, checks, merge, and rate-limit
  reads when planning work needs PR evidence. The helper is REST-first for
  normal PR orientation and owns quota-aware degraded behavior.
- `../github/scripts/gh-issue` and `../github/scripts/gh-comment` for safe
  multiline writes.
- `../github/references/issue-templates.md` and
  `../github/references/github-projects.md` for issue shape and Project fields.

Run these helpers from the client repository, which is how they find the
repository, and name them through this skill's base directory. `<skill-dir>` is this skill's base directory, the folder that holds this `SKILL.md`; your host shows it when the skill loads.

```bash
uv run <skill-dir>/../github/scripts/gh-plan.py index
```

If the helpers are unavailable, use `gh` directly with body files and compact
JSON reads. Do not fall back to repo docs or local plan files for durable
GitHub-backed planning.

The `policy.command_policies` block in this file's frontmatter owns the mapping
from raw planning lookup, Project, and GraphQL commands to helpers. A host that
does not show frontmatter enforces it when a command runs, and the block message
names the replacement; read the top of this `SKILL.md` for the whole mapping.
This prose keeps the
judgment about when durable planning should exist, how issues relate, and what
state belongs in GitHub.

For completed durable plan issues, use `gh-plan.py close`. Use the same helper
with `--reason not_planned` for explicitly superseded durable plans. The
planning helper owns `plan:done` labels, cleanup of stale `plan:active`,
`plan:blocked`, `plan:waiting`, and `plan:stale` labels, and Project focus
updates. It also owns relationship preflight and close-comment reconciliation:

Before closing a planning issue, run
`uv run ../github-work-rollup/scripts/github_unanswered_comments.py --thread OWNER/REPO#NUMBER`.
Any attention result or degraded coverage requires a response
or explicit handoff; a bot response never proves owner acknowledgement.

```bash
uv run <skill-dir>/../github/scripts/gh-plan.py close <issue> --comment-file <file>
```

The generic `github/scripts/gh-issue close` helper is for non-plan issues, or as
a fallback when `gh-plan.py close` is unavailable. Closing a durable plan with
the generic issue helper can leave planning labels or Project fields stale. It
also bypasses the no-write relationship preflight. For `--reason completed`,
resolve all open native blockers and sub-issues before retrying. Use
`--reason not_planned` only for superseded or intentionally abandoned work;
remaining blockers and sub-issues are retained and reported rather than treated
as completed. For other multiline writes, prefer body files or stdin. Do not
pass escaped `\n` through shell-quoted flags. Follow
`../references/every-code-formatting.md` when writing durable issue bodies,
planning comments, handoffs, or closeout evidence.

## Broad Workstream Rule

Create a parent issue plus sub-issues when a plan has independent tracks. Do not
hide broad work inside one checklist.

Use sub-issues when any two are true:

- touches three or more modules, repos, systems, or ownership areas
- has independent sequencing, blockers, or parallelizable tracks
- includes research, implementation, validation, and policy/design decisions
- has work that can finish or be reviewed independently
- needs roadmap/focus tracking beyond the current session

Parent issues should hold intent, finish line, dependency order, and recovery
state. Child issues should each have one scoped finish line and one next action.

## Plan Direction Checkpoints

Long-running work drifts when the agent keeps following local discoveries
without reconnecting them to the durable plan. Treat the plan and issue graph as
the navigation layer, not as paperwork.

At natural transition points, answer:

```text
What is next?
How does it fit the current plan?
Did the plan or issue graph change?
What blocker, evidence, or decision explains the direction?
```

When durable GitHub plans exist, run `gh-plan.py next` before choosing roadmap
work. Treat its native `blocked-by` evidence as authoritative; milestone and
Project Focus fields explain context and ranking but do not override blockers.
The command is advisory and read-only, so update labels, Focus, or relationships
separately only after the direction is confirmed.

Run this checkpoint:

- after each implementation slice
- after surprising findings
- before starting adjacent work that was not already planned
- before creating, closing, or superseding issues
- before handoff or closeout
- when the user asks "what's next", "where are we", or "how does this fit"

If the next action still matches the plan, answer briefly and continue. A
passing checkpoint does not need a written artifact. Update `Current Status`
only when the durable recovery state materially changed. If reality changed the
plan, update the canonical parent issue, sub-issues, blockers, relationships,
labels, and Project focus before relying on chat memory. If a new thread of work
appears, classify it as current scope, sub-issue, blocker, related issue, or
later. Do not let it become an untracked pivot.

For broad workstreams, prefer issue graph changes over prose-only status:

- create a sub-issue for independently finishable work
- add `blocked-by` / `blocks` for real execution dependencies
- use `related` for context that should not drive sequencing
- mark stale or superseded plans clearly
- keep the parent issue's `Current Status` as the recovery point

When another repo workflow is waiting on CI, deploy, review, or post-merge
health, keep the main checkout available for verification and parallelize safely:
use read-only exploration or isolated work only for independent planning or
implementation prep, then return to the waiting workflow before calling it done.

Use Focus lanes when configured:

- `Now`: one thing the user and Code are actively trying to finish.
- `Next`: ready after Now or after the manager chooses it.
- `Waiting`: blocked or awaiting an external decision/event.
- `Later`: real but intentionally out of focus.

Prefer at most one `Now` item unless the user explicitly chooses parallel work.

Use planning status labels with narrow meanings:

- `plan:active`: actionable now.
- `plan:blocked`: blocked by a real, current dependency, preferably represented
  by a native GitHub `blocked-by` relationship to an open issue.
- `plan:waiting`: intentionally parked on non-issue evidence, a
  user/customer/maintainer decision, or a future real-world event. Use this only
  on durable planning issues labeled `plan`, not on ordinary bugs, PRs pending
  QA, preview review, reporter validation, deploy, or merge readiness.
- `plan:stale`: needs review before it should guide work.
- `plan:done`: completed or superseded.

Do not use `plan:blocked` merely because work is not currently in focus. If an
issue has no open native blocker, prefer `plan:waiting` and make `Current
Status` say `Waiting for:` or `Parked until:` with the concrete condition.
If a non-issue condition truly blocks execution, include `Blocked by: No native
issue blocker; waiting for ...` so future audits do not chase missing edges.

If LaunchPlane or another local context helper is configured and useful for
orientation, call it once before or alongside `index`. Treat unavailable,
unauthorized, invalid, or missing context as normal absence and continue with
GitHub-only planning. Use local surface output only as a hint for source links,
readiness, blockers, and next inspection targets; do not copy private context
payloads into public issues, PRs, or handoffs unless they have been reviewed for
public safety.

## Token Discipline

Prefer the installed `github/scripts/gh-plan.py` helper for planning state. It
returns compact JSON and avoids loading issue bodies unless needed.

`index` uses paged repository-issue REST reads and explicitly excludes pull
requests. `search` uses paged REST issue search with `repo:` and `is:issue`
constraints plus the dedicated search quota bucket. Use `show` or `deps` when
relationship, dependency, or sub-issue details are required; index and search
intentionally preserve their smaller legacy result shape. Label maintenance
uses paged REST label reads and reconciles concurrent creates without retrying
the write blindly.

`create` performs exact-title dedupe through REST issue search, ensures labels
through REST, and delegates the non-idempotent issue write to the shared issue
helper so unknown outcomes carry reconciliation evidence. `close` first pages
native `blocked_by` dependencies and sub-issues through REST and fails before
mutation when a completed plan is incomplete or relationship reads are unsafe.
Configured Project synchronization remains the only GraphQL-backed phase and
runs before issue closure so the item stays discoverable. Confirmed or
read-reconciled issue closure is the commit point for planning labels and the
optional timeline comment. Re-running the same command reconciles partial
metadata and reuses an identical acting-user comment instead of duplicating it.
If closure fails after Project writes, report the helper's split `project_state`
and rerun the same close command; do not add completion labels or comments by
hand while the issue may still be open.

Project v2, native sub-issues, and native dependency operations may require
GraphQL. Before batching those operations, check rate limits when failures look
quota-related. If GraphQL is exhausted but REST/core is available, keep issue
body/status updates moving through REST-backed helpers and record Project or
native relationship updates as waiting rather than retrying until the LLM
workflow stalls.

- Use `index` or `search` before creating.
- Use `show` for selected sections; use `show --full` only when broad prose is
  required. Inspect the returned provenance before attempting a body update;
  `section_updates_allowed: false` means the current body shape must remain
  read-only. A plain contributor request without unmarked planning headings or
  reserved ownership markers may still be wrapped in the preservation envelope
  by `update-section`.
- Treat `ownership` values as `automation_managed`, `contributor_envelope`, or
  `contributor_unmanaged`. If ownership markers are malformed, `show --full`
  still returns the raw body with `ownership: unknown`, while section parsing
  and every body write remain fail-closed.
- Use `update-section` instead of rewriting the whole body.
- Use installed `github/scripts/gh-issue` and `github/scripts/gh-comment` for
  multiline Markdown bodies.

## Issue Shape

Durable planning issues should use the headings in
`../github/references/issue-templates.md`.

Keep `Current Status` short and concrete:

```text
State:
Next action:
Blocked by:
Waiting for:
Last verified:
```

Use `Blocked by:` for issue dependencies and `Waiting for:` or `Parked until:`
for non-issue conditions. Avoid listing completed work as a blocker; move it to
`Relationships` as completed or historical context.

Include decisive validation evidence only when it changes recovery state, such
as the current blocker, last verified environment, or proof needed to resume.
Keep raw logs, large check output, screenshots, and detailed run evidence in the
linked PR, workflow run, artifact, or comment instead of bloating `Current
Status`.

Keep `Finish Line` observable. If the finish line is vague, narrow it before
creating sub-issues or Project fields.

## Relationship Semantics

- `blocked-by`: current issue cannot move until the target changes.
- `blocks`: current issue is holding up the target.
- `subissue`: target is part of the current workstream and can be tracked
  independently.
- `related`: useful context without execution dependency.

Use native relationships first when the helper/API supports them. Body
references are explanatory, not canonical.
Native `blocked-by`, `blocks`, and `subissue` operations do not require
ownership of either issue body because they update GitHub relationships without
rewriting source text. `related` remains a markdown body update and therefore
keeps the normal plan-body ownership checks.

Completed closure requires every native `blocked-by` target and sub-issue to be
closed. Issues that the plan itself blocks do not prevent closure. An unavailable
or malformed relationship read is a no-write failure, not permission to assume
the graph is clear. `not_planned` closure is intentionally different: it may
retain open blockers or sub-issues, reports them in the result, and preserves the
GitHub `not_planned` state reason as the durable supersession signal.

### Missing Cross-Repository Gates

When another repository's maintainer must create or identify a prerequisite
issue before downstream blockers can be linked, include a return-and-link action
in the canonical waiting record. Use an automation-owned `Current Status` or a
bot-authored planning comment under the existing body-ownership rules:

- Name the gate maintainer, downstream coordinator, return thread, and affected
  downstream issues, using the existing [actor routing](#local-conventions).
- Explicitly ask the maintainer to reply/tag the coordinator in that thread with
  the canonical gate links and relevant completion criteria when ready.
- Name who will verify the returned gate's scope/status and add or reconcile the
  native blockers under existing authorization. Relationship updates do not
  require rewriting a protected issue body.

Use `Waiting for:` and, while no native blocker exists, `Blocked by: No native
issue blocker; waiting for ...`. Existing label and Focus rules still apply;
milestone membership or a mention alone does not establish a native dependency.
Do not claim the missing gate or link exists until verified. Once the gate is
known, use normal dependency tracking without an extra return round-trip.
Reuse applicable approval; when posting authority is missing, prepare the draft
and identify the remaining action. This rule does not authorize messages,
mentions, unrelated writes, or recurring notifications/monitoring.

Example draft: "Gate maintainer: create or identify the prerequisite for
`OWNER/CLIENT#28`, then reply to the client coordinator on that issue with its
canonical link and completion criteria. The coordinator will verify it and
reconcile the native blocker under existing authorization. Until then, the
client issue waits for gate identification and linkage."

## Related Issue Sweep

Stale GitHub planning state is a regression source. Before closeout, handoff,
or declaring a workstream done, search for related, duplicate, stale, parent,
sub-issue, blocker, and PR-linked issues that might still describe the old
state.

- Update every related issue whose `Current Status`, labels, blockers,
  relationships, or acceptance criteria changed.
- Close or relabel stale duplicate issues when they no longer represent current
  work.
- Prefer updating the canonical parent and linked sub-issues over leaving
  corrective context only in chat or PR comments.
- If an old issue might mislead a future agent, treat it as unfinished cleanup,
  not optional housekeeping.

After a canonical PR merges, inspect the issues it references with `Refs`,
`Closes`, `Fixes`, or `Resolves`. `Refs` should remain non-closing by default.
For each referenced issue, either close it with evidence when the merge
conclusively satisfies the finish line, or update/comment the remaining state
and leave it open. Use `gh-plan.py close --comment-file` for durable plan issues
so planning labels and Project fields stay synchronized. Use installed
`github/scripts/gh-issue close` with a multiline evidence comment for non-plan
issues.

Local handoff documents are not durable planning records unless the user asked
for offline/private handoff. If a session created `handoff*.md` or similar
scratch files, migrate recovery-critical content into the owning GitHub issue or
PR comment before closeout and then delete or explicitly preserve the file.

When repository cleanup finds valuable local work that needs parking, read the
shared [repository cleanup and preservation](../references/repo-cleanup.md) and
[parking and handoff procedures](../work-closeout/references/parking-and-handoff.md).
Do not create planning state for routine disposable artifacts. For bulk cleanup
or repository retirement, use a GitHub issue only after its capability, canonical
ownership, publication authorization, and durable recovery contents satisfy
those policies.

## Projects And Surfaces

Planning surfaces are optional views over GitHub issue-backed plan data. They
may help people choose work, inspect roadmap shape, or recover context, but they
must not replace the GitHub issue as the durable record.

Add plans to Projects when repo/workspace config defines a default Project or
the user asks for Project tracking. Use only a few human-facing fields: `Focus`,
`Manager`, `Finish Line`, `Roadmap Start`, and `Roadmap Target`.

When issue create/close succeeds but the helper returns a non-blocking Project
warning, mention it briefly and present the helper's human choices. Do not retry
repeatedly or silently switch to active human auth. Project auth/config fixes are
human decisions: grant automation Project access, use Project-capable auth,
disable Project sync, or correct stale Project config.

Treat roadmap dates as planning anchors, not commitments. Keep them useful for
LLM-assisted coding by using honest day, week, or month windows and moving or
clearing stale dates when reality changes.

## Closeout Check

Before saying a plan is captured, verify:

- existing issues were searched
- parent issue exists for a broad workstream
- sub-issues exist when the Broad Workstream Rule applies
- blockers/dependencies are represented
- stale, duplicate, related, and PR-linked issues were swept and reconciled
- `Current Status` and next action are concrete
- the next action says how it fits the current plan
- issue graph changes caused by the session were applied or explicitly parked
- docs are not being used as active plan state

## Workflow

1. Decide whether the work is ephemeral or durable.
2. Resolve the repo and read the active issue's finish line, `Current Status`,
   next action, blockers, and comments; comments may supersede the original
   body.
3. Run `next` before selecting roadmap work, and use `index` or `search` before
   creating anything; then draft or revise the issue shape with the user in chat
   when intent is unclear.
4. Create or update the canonical parent issue and choose one next action.
5. For broad workstreams, create scoped sub-issues and represent blockers,
   dependencies, and related context in the issue graph.
6. When discoveries change direction, run a Plan Direction Checkpoint and
   update durable state before pivoting.
7. Add configured Project fields only as view/tracking layers; keep the user in
   maker mode instead of duplicating management state in prose.
8. Before pausing, keep `Current Status`, acceptance criteria, decisions,
   validation, and the next action current. Do not leave a local handoff file as
   the only recovery source for GitHub-backed work.
9. When work completes, update status and close durable plan issues with
   `gh-plan.py close`; do not leave stale local plan files behind.

