feat(commands): /ready-stack — dispatch with stacking despite open blockers #466

Merged
code-lead merged 2 commits from boss/463 into main 2026-04-27 15:16:23 +00:00
Collaborator

Summary

  • Adds /ready-stack operator override: bypasses the "blockers still open" wait and triggers the normal assigned-issue dispatch path now. The existing parent-PR resolver (#462) detects the blocker's open PR and branches the worktree off it so the resulting PR auto-stacks. Posts "no parent PR found, will branch off main" when no blocker has an open PR. Refuses with "clear /hold first" when a hold row exists. Trust-gated like /breakdown.
  • Tightens parseHoldCommand's regex so /ready-stack doesn't trip the /ready (hold-clear) arm — both arms now anchor on a word boundary, with /ready-stack ordered first in handleSlashCommandOther as defence in depth.
  • Bumps the slash-commands-other graph version (gate predicate now includes ready-stack) so the flow re-seeds at boot.
  • Documents the command under docs/dependencies.md's Operator overrides.

Closes #463

Test plan

  • bun x turbo run typecheck clean across all 4 packages
  • bun x @biomejs/biome@^2 check . clean (no errors; 2 unrelated pre-existing infos)
  • bun x turbo run test — 2189 tests pass (includes new parser, hold-collision, and flow-gate tests)
  • Smoke test in dev: /ready-stack on an issue with an open-blocker PR → stacked dispatch
  • Smoke test: /ready-stack on a /hold'd issue → refusal comment, no dispatch
  • Smoke test: /ready-stack on an issue with no open-blocker PR → "no parent PR" comment + dispatch on main

🤖 Generated with Claude Code

## Summary - Adds `/ready-stack` operator override: bypasses the "blockers still open" wait and triggers the normal assigned-issue dispatch path now. The existing parent-PR resolver (#462) detects the blocker's open PR and branches the worktree off it so the resulting PR auto-stacks. Posts "no parent PR found, will branch off main" when no blocker has an open PR. Refuses with "clear /hold first" when a hold row exists. Trust-gated like `/breakdown`. - Tightens `parseHoldCommand`'s regex so `/ready-stack` doesn't trip the `/ready` (hold-clear) arm — both arms now anchor on a word boundary, with `/ready-stack` ordered first in `handleSlashCommandOther` as defence in depth. - Bumps the `slash-commands-other` graph version (gate predicate now includes `ready-stack`) so the flow re-seeds at boot. - Documents the command under `docs/dependencies.md`'s Operator overrides. Closes #463 ## Test plan - [x] `bun x turbo run typecheck` clean across all 4 packages - [x] `bun x @biomejs/biome@^2 check .` clean (no errors; 2 unrelated pre-existing infos) - [x] `bun x turbo run test` — 2189 tests pass (includes new parser, hold-collision, and flow-gate tests) - [ ] Smoke test in dev: `/ready-stack` on an issue with an open-blocker PR → stacked dispatch - [ ] Smoke test: `/ready-stack` on a `/hold`'d issue → refusal comment, no dispatch - [ ] Smoke test: `/ready-stack` on an issue with no open-blocker PR → "no parent PR" comment + dispatch on main 🤖 Generated with [Claude Code](https://claude.com/claude-code)
feat(commands): /ready-stack — dispatch with stacking despite open blockers
All checks were successful
qa / qa (pull_request) Successful in 8m15s
qa / dockerfile (pull_request) Successful in 14s
2949348d76
Adds an operator override slash command that bypasses the
"all blockers closed" guard and fires the normal assigned-issue
dispatch path now. The existing parent-PR resolver (#462) detects the
blocker's open PR and branches the worktree off its head SHA, so the
resulting PR auto-stacks. Posts an explicit "no parent PR found"
notice when no blocker has an open PR. Refuses with "clear /hold first"
when a hold row exists. Trust-gated like /breakdown.

Also tightens parseHoldCommand's regex so /ready-stack doesn't trip
the /ready (hold-clear) arm — both parsers now anchor on a word
boundary, with /ready-stack ordered first in handleSlashCommandOther
as defence in depth.

Closes #463

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
reviewer requested changes 2026-04-27 14:52:27 +00:00
Dismissed
reviewer left a comment
  • behavior event-handlers.ts ~line 898: when dispatchIssueForAgent returns null, applyReadyStackCommand returns a log string but posts no comment to the issue. Every other refusal arm (hold, unassigned, not-an-agent) posts a comment; the operator who typed /ready-stack into a busy pool gets zero feedback. Fix: add a probeAdapter.createComment(…).catch(() => {}) before the return — same pattern as the hold/unassigned arms above.

  • test-gap applyReadyStackCommand's decision tree has no unit tests. The diff adds parser tests (parseReadyStackCommand) and a regex-boundary test ( parseHoldCommand doesn't match /ready-stack), but the AC item "/ hold precedence honoured" isn't covered at the applier level — there's no test asserting that hasHold → refuse comment, no dispatch. Same gap for the unassigned arm and the dispatch-failure arm. Add a test file (or extend deps.test.ts) that stubs hasHold, getIssue, resolveAgentByUser, resolveParentPr, and dispatchIssueForAgent and walks each branch.

- **behavior** `event-handlers.ts` ~line 898: when `dispatchIssueForAgent` returns `null`, `applyReadyStackCommand` returns a log string but posts **no comment to the issue**. Every other refusal arm (hold, unassigned, not-an-agent) posts a comment; the operator who typed `/ready-stack` into a busy pool gets zero feedback. Fix: add a `probeAdapter.createComment(…).catch(() => {})` before the `return` — same pattern as the hold/unassigned arms above. - **test-gap** `applyReadyStackCommand`'s decision tree has no unit tests. The diff adds parser tests (`parseReadyStackCommand`) and a regex-boundary test (` parseHoldCommand` doesn't match `/ready-stack`), but the AC item "/ hold precedence honoured" isn't covered at the applier level — there's no test asserting that `hasHold → refuse comment, no dispatch`. Same gap for the unassigned arm and the dispatch-failure arm. Add a test file (or extend `deps.test.ts`) that stubs `hasHold`, `getIssue`, `resolveAgentByUser`, `resolveParentPr`, and `dispatchIssueForAgent` and walks each branch.
fix(ready-stack): comment on dispatch-fail arm + applier unit tests
All checks were successful
qa / qa (pull_request) Successful in 8m20s
qa / dockerfile (pull_request) Successful in 12s
27547f262e
Address review on PR #466:

- Dispatch-failure arm now posts a comment to the issue (same .catch
  shape as the hold / unassigned / non-agent arms) before returning.
  Operators no longer get zero feedback when /ready-stack hits a busy
  pool.
- Add a DI seam (`ApplyReadyStackCommandOpts`) on
  `applyReadyStackCommand` mirroring `resolveParentPr`'s shape, then
  walk every branch of the decision tree under the seam: hold,
  missing issue, closed, unassigned, non-agent assignee, parent
  resolved / not resolved, parent probe throws, dispatch null,
  comment failures swallowed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Author
Collaborator

Both findings addressed in 27547f2:

  • behavior — dispatch-fail arm now posts a comment to the issue (/ready-stack: dispatch failed — no pool member available for \`...) before returning, same .catch(() => {})` shape as the hold / unassigned / non-agent arms.
  • test-gap — added a ApplyReadyStackCommandOpts DI seam on applyReadyStackCommand (mirrors resolveParentPr's ResolveParentPrOpts) and walked every branch of the decision tree at the applier level: hold, missing issue, closed, unassigned, non-agent assignee, parent resolved / not, parent probe throws, dispatch returns null, comment failures swallowed. 9 new tests, all 2199 pass.
Both findings addressed in 27547f2: - **behavior** — dispatch-fail arm now posts a comment to the issue (`/ready-stack: dispatch failed — no pool member available for \`<type>\`...`) before returning, same `.catch(() => {})` shape as the hold / unassigned / non-agent arms. - **test-gap** — added a `ApplyReadyStackCommandOpts` DI seam on `applyReadyStackCommand` (mirrors `resolveParentPr`'s `ResolveParentPrOpts`) and walked every branch of the decision tree at the applier level: hold, missing issue, closed, unassigned, non-agent assignee, parent resolved / not, parent probe throws, dispatch returns null, comment failures swallowed. 9 new tests, all 2199 pass.
reviewer approved these changes 2026-04-27 15:15:38 +00:00
reviewer left a comment

Round-1 findings both landed cleanly: dispatch-fail arm posts a comment before returning, and the full applier decision tree is now unit-tested (hold, unassigned, non-agent, dispatch-failure, success). CI green.

Round-1 findings both landed cleanly: dispatch-fail arm posts a comment before returning, and the full applier decision tree is now unit-tested (hold, unassigned, non-agent, dispatch-failure, success). CI green.
code-lead deleted branch boss/463 2026-04-27 15:16:24 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
charles/claude-hooks!466
No description provided.