osdlc

consolidate-task-prs

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.

osdlc 3 Actualitzat fa 5 d
GitHub

Instal·la

npx skillscat add osdlc/osdlc/claude-plugin-packs-sdlc-skills-consolidate-task-prs

Instal·la des del registre de SkillsCat.

SKILL.md

/sdlc:consolidate-task-prs

Invoke explicitly to clean up an accumulation of one-task-each PRs:

/sdlc:consolidate-task-prs

Project context (don't re-derive every run):

  • A "single-task-file PR" is an open PR whose entire diff is exactly
    one new .md file under docs/planning/tasks/
    — an addition, no
    other files touched. That is the shape /sdlc:spawn-task-pr produces
    on a meta-task/<slug> branch, and the shape a hand-made
    docs/<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-branch removes its
    only copy of the draft task, so the consolidation PR must already
    carry that file first.
  • Draft task files are at state: planning/draft and 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 gh is authenticated (gh auth status); if not, stop and
    tell the user to authenticate. Every PR query and mutation goes
    through gh.
  • Resolve the repo root (git rev-parse --show-toplevel) and the
    <owner>/<name> from git remote get-url origin (strip .git;
    handle SSH and HTTPS shapes). All gh calls 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 200

For 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 (not isDraft, reviewDecision is empty/null,
    mergeable is not CONFLICTING).
  • needs-decision — the diff matches the single-task-file shape
    but a blocking signal is present: isDraft is true, a review
    exists (reviewDecision is APPROVED / CHANGES_REQUESTED /
    REVIEW_REQUIRED), or mergeable is CONFLICTING. 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/*.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/main

For 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>.md

From 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/ \
  --json

A 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 json

Its tasks[] rows carry basename, state, 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's
    docs/planning/tasks/<file>.md.
  • LINKED-EXISTING — the candidate duplicates the present task named in
    link_to. Mark it dropped-as-dup: do not add the file. Edit
    the existing present task — append the candidate's originating
    basenames to its related: array (de-duplicated) and add one
    ## Discovery context line 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 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 and
their branches deleted by /sdlc:consolidate-task-prs.
EOF

Push 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 or
sdlc 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 state shows OPEN.
  • For each closed PR, gh pr view <N> --json state shows CLOSED.
  • 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-work chore/
    namespace, distinct from task/ and meta-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:docs if 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.