Skip to content

feat(workflow): /next-issue and /wrap-up, with priority labels and an issue claim protocol - #508

Merged
MarkMichaelis merged 2 commits into
mainfrom
feat/498-next-issue-dispatcher
Sep 14, 2026
Merged

feat(workflow): /next-issue and /wrap-up, with priority labels and an issue claim protocol#508
MarkMichaelis merged 2 commits into
mainfrom
feat/498-next-issue-dispatcher

Conversation

@MarkMichaelis

@MarkMichaelis MarkMichaelis commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

Summary

This replaces hand-maintained tracking issues with a queue that lives on the issues themselves, and adds the two commands that work it.

  • /next-issue: a stateless dispatcher. It selects open issues that are not held, not claimed and not blocked, ordered by lowest priority-N and then oldest, and claims each one before dispatching it. Each issue goes to a new named session (through the existing Start-IssueAgent.ps1), to a background subagent when the work is small and settled, or to this session (here).
    • Claim: push the branch, add in-progress, and post a marker comment.
    • Races: the earliest claim wins.
    • Stale claims: a claim that has been silent for 3 days is taken over, with a comment saying so.
  • /wrap-up: for each of this session's PRs whose exit criteria all hold, it checks the repository's shared instructions for an explicit merge-without-asking grant.
  • Delivery: both are Claude Code skills under .claude/skills/, which Claude Code turns into slash commands. The sync manifest names exactly those two directories, so the rest of a consumer's .claude/ (settings, hooks, its own skills) is never touched.
  • Instructions:
    • New section: Issue Queue -- Priority Labels and Claims, covering the label contract and the claim, race, stale and release rules.
    • Generic standing rules moved out of trackers and one user's local memory: judge Pester by FailedContainersCount; re-scan stale issue inventories; a hand-applied rebase is new authorship.
    • Repo-specific traps now live in project.instructions.md.

Owner decisions (recorded on #498)

Decision Choice
Command names /next-issue, /wrap-up
Stale-claim timeout 3 days
Default fan-out 1
Delivery .claude/skills/next-issue/ and .claude/skills/wrap-up/, by exact directory
Copilot prompt file not in this PR, filed as #504

Independent review

The review ran on Sonnet 5, a different model from the Opus 5 author, under an explicit model override. It passed selection (verified live, with #117 on top), the claim race, sync delivery, leak hygiene, and the no-new-command-line-options check. It returned changes requested, with two findings, both accepted and fixed test-first in d0cf8a7:

  1. Important. /wrap-up asserted that merging "is pre-authorized by the Merge Step in the shared instructions". That isn't true on main, because the grant lives in the held PR fix(dev-loop): merging a finished PR is pre-authorized -- merge, close, clean up, then report #507. Step 2 now checks for the grant and, without it, lists the PR under Needs you instead of merging. Shipped skill text no longer cites any issue or PR number, and a scoped assertion pins that.
  2. Important, weak oracle. The /next-issue selection tests matched qualifiers anywhere in the body. They now extract the fenced gh issue list block under Step 1 and assert the exact --search argument. The ranking and launcher assertions are scoped to their own sections.

While fixing (2), the first version of the section helpers used break/continue, which Pester 6 reports as an escaped loop label and fails the whole block. The helpers are now regex-based.

Verification

GitHub Actions is billing-blocked, so every result below is a local run with its scope named. All were re-run at d0cf8a7:

Scope Command Result
.github suite Invoke-Pester -Path .\.github Result=Passed, 850 passed, 0 failed, 5 skipped, FailedContainersCount=0, 71 containers
Root sync suite (not run by CI, #309) Invoke-Pester -Path (Resolve-Path .\Pull-SDLC.ai.Tests.ps1).Path Result=Passed, 394 passed, 0 failed, 0 skipped, FailedContainersCount=0
Skill-contract suite .github/agents/tests/issue-queue-skills.Tests.ps1 (inside the 71 above) Result=Passed, 22 passed, FailedContainersCount=0
Leak scan + internal cross-references the validate-instructions.yml patterns and link check, run locally with bash no leaks, 0 broken references
Frontmatter parsed with a real YAML parser both skills: name matches the directory; description present

Falsification. Every ablation ran in a scratch copy, never in the worktree:

  • Widening the manifest entry to a .claude/ prefix fails 9 of 11 sync tests. The 2 that still pass are the "treats as upstream-managed" cases, which a wider prefix also satisfies.
  • Moving -is:blocked out of the Step 1 command and into the surrounding prose fails the exact-search assertion. A qualifier mentioned only in prose no longer satisfies it.
  • Putting back /wrap-up's unconditional "merging is pre-authorized" sentence fails all four of the new /wrap-up assertions.
  • Before the implementation, the skill tests and 4 of 11 sync tests failed.

Functional dry run. I ran the skill's selection query against this repository: 72 candidates. #500, which has open blockers, was correctly excluded.

Assumptions

Closes #498
Closes #500

🤖 Generated with Claude Code

https://claude.ai/code/session_01Fda3nBeB9rJBPdJSmD7sRN

MarkMichaelis and others added 2 commits September 13, 2026 20:25
… issue claim protocol

Work is queued on the issues themselves -- priority-0..3, hold, in-progress,
and GitHub's native blocked-by -- instead of hand-maintained tracking issues
whose mirror of the underlying issues drifts.

- .claude/skills/next-issue: a stateless dispatcher. Selects open, unheld,
  unclaimed, unblocked issues by lowest priority then oldest; claims by
  pushing the branch, labelling in-progress and posting a marker comment;
  resolves races earliest-claim-wins; takes over claims silent for 3 days;
  dispatches to a new session via Start-IssueAgent.ps1, a subagent for
  small settled work, or this session ("here").
- .claude/skills/wrap-up: merges this session's PRs whose exit criteria hold,
  parks the rest with a hand-off comment and a claim release, files loose
  ends as issues, then reports.
- Sync: the two skill directories are upstream-managed by exact name; the
  rest of .claude/ stays the consumer's. Delivery pinned through
  Get-UpstreamOps and Invoke-PullSDLC against fixture repos.
- Instructions: the claim protocol and label contract; generic standing
  rules (Pester FailedContainersCount, stale issue inventories, hand-applied
  rebase is new authorship); repo-specific traps in project.instructions.md.

Closes #498
Closes #500

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fda3nBeB9rJBPdJSmD7sRN
…asserting it; scope the skill tests

The independent review (Sonnet 5) raised two Important findings on this PR, and both were accepted.

- The wrap-up skill claimed that merging "is pre-authorized by the Merge Step
  in the shared instructions". Whether that is true depends on each
  repository's instructions and their version, so the skill cannot assert it.
  Step 2 now checks the instructions for an explicit merge-without-asking
  grant. Without one it does not merge. It lists the PR under Needs you as
  ready to merge, with its evidence and the exact merge command.
- The next-issue selection tests matched qualifiers anywhere in the body, so a
  qualifier that appeared only in prose still passed. The tests now extract
  the fenced gh issue list block under Step 1 and assert the exact --search
  argument. The ranking and launcher assertions are scoped to their own
  sections.
- Shipped skill text cites no issue number; example lines use placeholders.

Each ablation ran in a scratch copy:
- Moving -is:blocked from the command into prose fails the exact-search test.
- Reinstating the unconditional claim fails all four new wrap-up assertions.

Refs #498, #500

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fda3nBeB9rJBPdJSmD7sRN
@MarkMichaelis
MarkMichaelis merged commit 585d844 into main Sep 14, 2026
2 checks passed
@MarkMichaelis
MarkMichaelis deleted the feat/498-next-issue-dispatcher branch September 14, 2026 04:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant