From gh-projects
Schedule issues into the gh-projects board's current Iteration + Milestone, set Start/Target dates, show working-day capacity vs assigned load, and reorder the Ready queue to the deterministic recommendation. Use when the user asks to "plan the sprint", "assign these to the current iteration", "schedule the sprint", or "reorder the ready queue". Dry-by-default — previews every assignment, date set, and reorder; writes only on explicit --force. NO AI/model call (pure date math + field writes). Does NOT route/project new issues or self-assign the actor (route-issue), and does NOT open/merge PRs or move Status (promote-pr).
How this skill is triggered — by the user, by Claude, or both
Slash command
/gh-projects:plan-sprint --owner <org> --number <project#> + the issue set + iteration/milestone (add --force after the dry run)--owner <org> --number <project#> + the issue set + iteration/milestone (add --force after the dry run)claude-opus-4-8This skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
Schedule a set of already-routed issues into the **current Iteration**, assign a
Schedule a set of already-routed issues into the current Iteration, assign a
Milestone, set Start/Target dates, show working-day capacity vs load,
and reorder the Ready queue to the deterministic recommendation. This is a
thin orchestrator over the deterministic engine — all load-bearing logic lives
in ${CLAUDE_PLUGIN_ROOT}/lib/gh.py + ${CLAUDE_PLUGIN_ROOT}/lib/sprint.py,
ridden behind the dry-by-default / --force rail that
${CLAUDE_PLUGIN_ROOT}/lib/engine.sh enforces. Leave no decision logic in this
prose.
Let ENGINE=${CLAUDE_PLUGIN_ROOT}/lib/engine.sh and
SPRINT=${CLAUDE_PLUGIN_ROOT}/lib/sprint.py. Project writes need a GitHub App
installation token already in the environment (GH_APP_TOKEN, or
APP_ID+APP_PRIVATE_KEY) — never GITHUB_TOKEN (it cannot write Projects
v2 fields). The engine never prints the token: bash $ENGINE token only
confirms availability (returns a REDACTED ok:true); do not try to capture it
(AC-28).
Field-home split. plan-sprint OWNS the scheduling fields — Sprint
(Iteration), Milestone, Start, Target — and the Ready order. It
does not set the intake-time fields (Type/Size/Tier/PM-ID/Spec/Priority — that
is route-issue) and does not move Status beyond what scheduling implies
(lifecycle Status is promote-pr). plan-sprint does not deploy or merge, so it
does not wire the guard.
| Step | Mechanism | AC |
|---|---|---|
| Pick the active Iteration | computed OFFLINE from the field config (see rule below) | AC-12 |
| Assign each issue to that Iteration (Sprint field) | engine.sh resolve ids → gh.py field write | AC-12 |
| Assign the Milestone | gh.py set-milestone (repo-scoped number, idempotent) | AC-12 |
| Set Start/Target dates | gh.py field write per item | AC-12 |
| Show capacity vs load, warn on over-allocation | sprint.py capacity vs assigned count | AC-13 |
| Reorder the Ready queue | sprint.py ready-order → gh.py reorder-item | AC-14 |
The "current" Iteration is not asked for and not guessed — it is computed
from the resolved Iteration field's configuration (the iterations list of
{title, startDate, duration} that engine.sh resolve already returns), against
today:
[startDate, startDate + duration)
(the same half-open convention as sprint.working_day_capacity).iterations list is
considered, never completedIterations.startDate strictly after today) is used.startDate ascending; this is a pure, stable function
of the config + today, so a re-run picks the identical iteration.This active-iteration selection is the one new piece of logic plan-sprint needs. It is a pure computation over the resolved field config — there is no model call. (Implementation note for the orchestrator: if a shared lib helper is wanted, it belongs in
lib/sprint.pyas anactive_iteration(iterations, today)pure function; this leaf does not editlib/. Meanwhile the rule above is fully specified and is covered offline bytest_plan_sprint.py.)
Resolve the field config (read-only, safe in dry mode):
bash "$ENGINE" resolve --owner <org> --number <project#>
Without --force the engine mutates nothing — it prints the resolved
command(s) it would run and exits (AC-15). Build and preview the full plan:
reorder-item calls.Compute capacity and the recommended order with the read-only sprint.py (no
token, no network, no AI):
python3 "$SPRINT" capacity --start <iter-startDate> --duration <iter-duration>
python3 "$SPRINT" ready-order --items '<json array of {id,priority,target}>'
If the load exceeds capacity, emit an over-allocation WARNING and still show the full plan — capacity is advisory, it does not hard-block (AC-13). Surface the warning to the user; do not silently drop issues.
Use AskUserQuestion to confirm (this mutates the board). The App token must
already be in the environment — GH_APP_TOKEN, or APP_ID+APP_PRIVATE_KEY
(the engine never prints the token; bash "$ENGINE" token returns a REDACTED
confirmation only, so do not try to capture it). Confirm availability first
(ok:true); if it is unset the write verbs refuse (exit 2) — that refusal is
intentional (AC-28):
bash "$ENGINE" token # confirms availability only — prints {"app_token":"[REDACTED]","ok":true}
Then run each write verb without --force first (dry preview), and re-run the
identical command with --force appended to execute. Per issue in the set:
(a) Assign the active Iteration (write-field on the Sprint field —
--value is the iteration TITLE; the engine resolves it to the iteration id and
reads it back identical):
bash "$ENGINE" write-field --owner <org> --number <project#> --repo owner/name --issue <n> --field Sprint --value "<iteration title>" --force
(b) Set the Start / Target dates (write-field on the date fields — --value
is an ISO YYYY-MM-DD date):
bash "$ENGINE" write-field --owner <org> --number <project#> --repo owner/name --issue <n> --field Start --value <YYYY-MM-DD> --force
bash "$ENGINE" write-field --owner <org> --number <project#> --repo owner/name --issue <n> --field Target --value <YYYY-MM-DD> --force
(c) Assign the Milestone (set-milestone — repo-scoped number, idempotent):
bash "$ENGINE" set-milestone --repo owner/name --number <n> --milestone <m> --force
(d) Reorder the Ready queue (reorder-item) in the recommended order
returned by ready-order: move the first recommended item to the top (omit
--after), then each subsequent item --after the previous one (AC-14):
bash "$ENGINE" reorder-item --project-id <projectId> --item <firstItemId> --force
bash "$ENGINE" reorder-item --project-id <projectId> --item <nextItemId> --after <prevItemId> --force
An already-assigned Iteration/date/Milestone, or an item already at its recommended position, is detected and skipped — never a 409/422 (AC-33).
State: the active Iteration chosen (and why — in-window vs next-upcoming vs boundary), the Milestone, the Start/Target dates set, the capacity vs load line and whether an over-allocation warning fired, and the new Ready order. If you re-ran on an already-scheduled sprint, confirm it was a no-op (every assignment / milestone / position already in place → no further write, exit 0).
--force only after the user confirms (AC-15).GH_APP_TOKEN), never
GITHUB_TOKEN (AC-28). The engine refuses to write without it and scrubs
secrets from all output.route-issue), and does not open/merge PRs or
advance lifecycle Status (promote-pr).0 ok · 2 usage / no App token · 3 project/field not found ·
1 unexpected.Guides creation, editing, and verification of skills for AI coding agents using test-driven development with subagent scenarios. Use when authoring or debugging skills.
npx claudepluginhub zakattack9/agentic-coding --plugin gh-projects