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.
Instal·la
npx skillscat add osdlc/osdlc/claude-plugin-packs-sdlc-skills-consolidate-task-prs Instal·la des del registre de SkillsCat.
/sdlc:consolidate-task-prs
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
state: planning/draftand are never under
a task-lifecycle lease (a lease is only taken when/sdlc:task-work
starts implementation). This skill therefore does no lease handling.
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
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 matchingdocs/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 adocs/planning/tasks/*.md
addition. 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
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 --quiet
git -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> --quiet
git -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):
${CLAUDE_PLUGIN_ROOT}/cli/sdlc task dedup-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, state, headline and the fulltext 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
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
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
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 throughsdlc 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 and
their 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>
${CLAUDE_PLUGIN_ROOT}/cli/sdlc pr create --project-root <worktree> --base main \
--head chore/consolidate-task-prs-<today-UTC> \
--title "docs(tasks): consolidate <N> single-task PRs" \
--body-file <enumerated-body-file>Capture the consolidation PR number and URL. If the push orsdlc 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
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
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.
Notes
- 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 a shared op. This skill reuses
${CLAUDE_PLUGIN_ROOT}/cli/sdlc task dedup-search— the same
keyword-scoring dedup the spawn-from-post-mortem procedure uses — so a
duplicate is dropped-and-linked the same way on both the spawn and the
consolidate side. - 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 throughsdlc 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.