/sdlc:consolidate-task-prs
Generated from solutions/ontological/skills/consolidate-task-prs/SKILL.md.
Description
Section titled “Description”Collapse every open single-task-file PR — each carrying one new draft task file under docs/planning/tasks/ — into a single consolidation PR, deduping the folded tasks against the present task list, then close the superseded PRs and delete their branches. Explicit cleanup only, never auto-invoked: run it to fold an accumulation of one-task-each PRs back into one reviewable PR.
Allowed tools
Section titled “Allowed tools”ReadBashEditAskUserQuestion
Source
Section titled “Source”Invoke explicitly to clean up an accumulation of one-task-each PRs:
/sdlc:consolidate-task-prsProject context (don’t re-derive every run):
- A “single-task-file PR” is an open PR whose entire diff is exactly
one new
.mdfile underdocs/planning/tasks/— an addition, no other files touched. That is the shape/sdlc:spawn-task-prproduces on ameta-task/<slug>branch, and the shape a hand-madedocs/<basename>task-spec PR usually has. See${CLAUDE_PLUGIN_ROOT}conventions/branch-naming.md. - This skill is the inverse of
/sdlc:spawn-task-pr: that skill fans one PR out per post-mortem friction bullet; this skill folds the accumulation back into one PR. - The consolidation PR is opened and confirmed before any original
PR is closed. Closing a folded PR with
--delete-branchremoves its only copy of the draft task, so the consolidation PR must already carry that file first. - Draft task files are at
status: planning/draftand are never under a task-lifecycle lease (a lease is only taken when/sdlc:task-workstarts implementation). This skill therefore does no lease handling.
1. Check preconditions
Section titled “1. Check preconditions”- Confirm
ghis authenticated (gh auth status); if not, stop and tell the user to authenticate. Every PR query and mutation goes throughgh. - Resolve the repo root (
git rev-parse --show-toplevel) and the<owner>/<name>fromgit remote get-url origin(strip.git; handle SSH and HTTPS shapes). Allghcalls run against this repo.
Operate from the repo root, not whatever worktree you were invoked in —
the consolidation worktree and every git worktree op target the
primary checkout. The base branch is main.
2. Discover candidate PRs
Section titled “2. Discover candidate PRs”List open PRs against main, then classify each by its file diff.
gh pr list --state open --base main \ --json number,title,headRefName,url,isDraft,reviewDecision,mergeable,author \ --limit 200For each PR, fetch its changed files and classify it:
gh pr view <N> --json files -q '.files'- fold — the diff is exactly one file, that file is a new
addition matching
docs/planning/tasks/*.md, and the PR has no blocking signal (notisDraft,reviewDecisionis empty/null,mergeableis notCONFLICTING). - needs-decision — the diff matches the single-task-file shape
but a blocking signal is present:
isDraftis true, a review exists (reviewDecisionisAPPROVED/CHANGES_REQUESTED/REVIEW_REQUIRED), ormergeableisCONFLICTING. List these for the user but leave them alone — do not auto-fold them. - skip — the diff touches more than one file, modifies an existing
file, or the single file is not a
docs/planning/tasks/*.mdaddition. Not a single-task-file PR.
Never classify the skill’s own consolidation branch
(chore/consolidate-task-prs-*) as a candidate.
If there are zero fold PRs, print Nothing to consolidate. and stop.
3. Dedup each candidate against the present task list
Section titled “3. Dedup each candidate against the present task list”Create the consolidation worktree off origin/main so the present task
list is clean, then dedup before materializing anything.
git -C <repo-root> fetch origin main --quietgit -C <repo-root> worktree add <repo-root>/.sdlc/worktrees/consolidate-task-prs-<today-UTC> \ -B chore/consolidate-task-prs-<today-UTC> origin/mainFor each fold candidate, read the draft task it carries from the PR head without checking it out:
git -C <repo-root> fetch origin <headRefName> --quietgit -C <repo-root> show origin/<headRefName>:docs/planning/tasks/<file>.mdFrom that content take the headline — the body’s first # heading,
NOT a headline: frontmatter key; the task schema has no such key, and
grepping for one yields an empty bullet that scores against nothing —
plus the ## Goal body text, and score the pair against the present
task list (the worktree’s clean docs/planning/tasks/, which does
not yet contain the candidate):
bun run ${CLAUDE_PLUGIN_ROOT}skills/task-work/dedup_search.ts search \ --bullet "<headline> — <goal text>" \ --tasks-dir <worktree>/docs/planning/tasks/ \ --jsonA cheap way to get both fields without hand-parsing is the companion op, which already extracts them from every open PR head:
${CLAUDE_PLUGIN_ROOT}cli/sdlc task list-unmerged --output jsonIts tasks[] rows carry basename, status, headline and the full
text for exactly these candidates.
Branch on the script’s decision (see its module docstring for the
scoring rule and threshold defaults):
SPAWNED— no present task is a near-duplicate. Mark the candidate fold: write its task file into the worktree’sdocs/planning/tasks/<file>.md.LINKED-EXISTING— the candidate duplicates the present task named inlink_to. Mark it dropped-as-dup: do not add the file. Edit the existing present task — append the candidate’s originating basenames to itsrelated:array (de-duplicated) and add one## Discovery contextline recording the dropped PR (e.g.Duplicate draft proposed in #<N>; folded by /sdlc:consolidate-task-prs.).
Guard against candidate-vs-candidate collisions: if two fold
candidates resolve to the same target filename, keep the first and
re-mark the second needs-decision with reason “same filename as
#
4. Build the plan
Section titled “4. Build the plan”Render one markdown plan, grouped by action so review is one read:
# Consolidation plan
## Will fold into the consolidation PR (<N>)- #<pr> <title> → docs/planning/tasks/<file>.md
## Dropped as duplicate (<M>)- #<pr> <title> → duplicate of [[<existing-basename>]]; will link, not add
## Needs your decision — left alone (<K>)- #<pr> <title> — <reason: draft / has review / conflicts / same filename as #X>
## Will close + delete branch on approval (<N+M>)- #<pr> (head <headRefName>) — folded | dropped-as-dup
## Leaving alone (skipped)- #<pr> <title> — <why it is not a single-task-file PR>State plainly that close + branch deletion happens only after the
consolidation PR is confirmed open, and that --delete-branch removes
the remote head branch (and the local branch if present).
5. Confirm before mutating
Section titled “5. Confirm before mutating”Closing PRs and deleting branches is destructive and outward-facing, so get one confirmation. Ask one AskUserQuestion with the plan above shown:
- Proceed (Recommended) — open the consolidation PR (Step 6), then close + delete every folded and dropped-as-dup PR (Step 7).
- Cancel — change nothing — remove the consolidation worktree and branch and exit without mutating any PR.
6. Open the consolidation PR
Section titled “6. Open the consolidation PR”Land every folded task file plus the dropped-as-dup link edits on the consolidation branch as one commit, then open one PR.
Validate every file you wrote or edited (no pipe — the exit code must propagate so a bad file blocks the commit):
${CLAUDE_PLUGIN_ROOT}cli/sdlc entities validate <path>Stage only the task files you touched, then commit through
sdlc commit create --message - so the parens-bearing docs(tasks):
subject reaches git through a tempfile (see
${CLAUDE_PLUGIN_ROOT}conventions/commit-messages.md):
git -C <worktree> add docs/planning/tasks/${CLAUDE_PLUGIN_ROOT}cli/sdlc commit create \ --project-root <worktree> --message - <<'EOF'docs(tasks): consolidate <N> single-task PRs into one
Folds the draft task files from #<a>, #<b>, ... onto one PR and drops<M> as duplicates of existing tasks. Superseded PRs are closed andtheir branches deleted by /sdlc:consolidate-task-prs.EOFPush the branch and open exactly one PR against main, its body
enumerating every folded PR (with links) and every dropped-as-dup
decision:
git -C <worktree> push -u origin chore/consolidate-task-prs-<today-UTC>gh pr create --repo <owner/name> --base main \ --head chore/consolidate-task-prs-<today-UTC> \ --title "docs(tasks): consolidate <N> single-task PRs" \ --body "<enumerated body>"Capture the consolidation PR number and URL. If the push or
gh pr create fails, stop and surface the error — do not proceed to
Step 7. No original PR has been closed at this point, so the repo is
recoverable.
7. Close superseded PRs and delete branches
Section titled “7. Close superseded PRs and delete branches”Run this step only after the consolidation PR is confirmed open (Step 6 returned a PR number).
For each folded and dropped-as-dup PR, close it with a comment pointing at the consolidation PR and delete its branch in one call:
gh pr close <N> --repo <owner/name> --delete-branch \ --comment "Superseded by #<consolidation> — its draft task was <folded into|dropped as a duplicate of [[<existing>]] in> the consolidation PR."--delete-branch deletes the remote head branch and the local branch
if it exists. If a close fails (protected branch, race, auth), do not
silently continue: print the failure and the remaining queue, then ask
the user via AskUserQuestion whether to continue with the rest, retry,
or stop (default: stop).
8. Verify and report
Section titled “8. Verify and report”Confirm the end state and print a summary.
gh pr view <consolidation> --json stateshowsOPEN.- For each closed PR,
gh pr view <N> --json stateshowsCLOSED. - For each deleted branch,
git -C <repo-root> ls-remote --heads origin <headRefName>is empty.
Print one summary: the consolidation PR URL, the count folded, the count dropped-as-dup (with link targets), the count of PRs closed + branches deleted, and any needs-decision / skipped PRs left untouched. If any verification check failed, surface it rather than reporting clean.
- Read-only until Step 6. Discovery (Step 2) and dedup (Step 3) only read PR/branch state and write into a throwaway worktree. No PR is mutated and no branch is deleted until the user confirms in Step 5.
- Open before close. The ordering in Steps 6→7 is load-bearing: a folded PR’s branch holds the only copy of its draft task until the consolidation PR carries it. Never reorder these.
- Dedup is borrowed from task-work. This skill reuses
${CLAUDE_PLUGIN_ROOT}skills/task-work/dedup_search.ts— the same keyword-scoring dedup/sdlc:spawn-from-post-mortemuses — so a duplicate is dropped-and-linked the same way on both the spawn and the consolidate side. If a third caller needs it, promote it to ansdlcverb (${CLAUDE_PLUGIN_ROOT}conventions/typescript-runtime.md, “co-locate first, promote when shared”). - Branch naming. The consolidation branch is
chore/consolidate-task-prs-<today-UTC>— the bulk-workchore/namespace, distinct fromtask/andmeta-task/. See${CLAUDE_PLUGIN_ROOT}conventions/branch-naming.md. - Committing model-generated messages. The consolidation commit body
and the PR body are model-authored; route the commit through
sdlc commit create --message -per${CLAUDE_PLUGIN_ROOT}conventions/commit-messages.md. - What this skill does NOT do. It does not promote folded drafts off
planning/draft, does not regenerate entity rosters (draft task additions don’t change them; run/sdlc:docsif a later promotion needs it), and does not implement any task. It only collapses single-task-file PRs into one and cleans up the originals.