Skip to content

fix(sdd-dispatch): unwedge cascade on task-close and at sdd:review#139

Merged
norrietaylor merged 3 commits into
mainfrom
fix/dispatch-cascade-wedge
May 22, 2026
Merged

fix(sdd-dispatch): unwedge cascade on task-close and at sdd:review#139
norrietaylor merged 3 commits into
mainfrom
fix/dispatch-cascade-wedge

Conversation

@norrietaylor
Copy link
Copy Markdown
Owner

Root cause

#133 — re-dispatch-on-close walks 0 hops. The route job seeds the
task → Unit → tracker walk with cursor = payload.issue, then reads
cursor.parent_issue_url. The issues.closed webhook payload does not
populate parent_issue_url on the closed issue (GitHub omits the
child → parent pointer from the event body), so the walk breaks on the
first hop, walked stays 0, the route logs "walked 0 hops; ignoring",
and compute/dispatch are skipped. Closing a task PR never re-fires the
cascade.

#134 — tracker advances to sdd:review while tasks remain. sdd-execute
moves the feature to sdd:review once the first layer of impl PRs opens,
before later-layer task sub-issues are dispatched. The compute
precondition then refused /dispatch at sdd:review (refuse_late), and
close-triggered re-dispatch had nothing to resume. compute() can derive
readiness from the graph; only the lifecycle-state gate blocked fan-out.

Fix

#133 — re-fetch the closed issue via github.rest.issues.get before
walking, which populates parent_issue_url, then seed cursor from that.
The upward hops already re-fetch each parent, so only the seed needed the
explicit fetch. Lowest-cost reliable approach (one extra GET on the closed
task), matching the existing sdd-validate wrapper pattern.

#134 — accept sdd:review as a valid /dispatch precondition
(option b): arm sdd:dispatched and fan out the remaining ready tasks,
leaving the lifecycle label to sdd-execute. Only sdd:done now refuses
as late. The cascade path already skipped the precondition check, so this
unblocks both /dispatch and close-triggered re-dispatch from sdd:review.

Edited the wrapper wrappers/sdd-dispatch.yml (the runtime source of truth
for this deterministic agent) and the sdd-dispatch.md contract doc, then
ran gh aw compile to regenerate sdd-dispatch.lock.yml.

Acceptance

  • Merging a task PR fires compute+dispatch and arms every newly
    unblocked task within one cycle; a multi-Unit feature drains to
    sdd:done with no manual /dispatch.
  • A multi-layer feature does not wedge at sdd:review: /dispatch on a
    tracker with remaining ready tasks fans out regardless of sdd:review.

References

Closes #133
Closes #134

🤖 Generated with Claude Code

Two companion blockers that stall the dispatch cascade:

1. Re-dispatch-on-close walked 0 hops. The route job seeded the
   task->Unit->tracker walk from `payload.issue.parent_issue_url`,
   which the `issues.closed` webhook leaves empty even for native
   sub-issues. The walk broke on the first hop, logged "walked 0
   hops; ignoring", and the cascade never re-fired on a task PR
   merge. Re-fetch the closed issue via REST (which populates
   parent_issue_url) to seed the walk; the upward hops already
   re-fetch each parent. (#133)

2. The tracker advances to sdd:review while open, unstarted task
   sub-issues remain (sdd-execute moves it there once the first
   layer of impl PRs opens). The compute precondition then refused
   /dispatch at sdd:review with refuse_late, and close-triggered
   re-dispatch had nothing to resume. Accept sdd:review as a valid
   /dispatch precondition: arm sdd:dispatched and fan out the
   remaining ready tasks, leaving the lifecycle label to sdd-execute.
   Only sdd:done now refuses as late. (#134)

Contract doc and lock regenerated to match (gh aw compile).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 22, 2026

Review Change Stack

Warning

Rate limit exceeded

@norrietaylor has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 9 minutes and 20 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 178d0160-65ae-4b5d-bc1b-604b11159561

📥 Commits

Reviewing files that changed from the base of the PR and between 2b04e81 and b533b0e.

📒 Files selected for processing (3)
  • .github/workflows/sdd-dispatch.lock.yml
  • .github/workflows/sdd-dispatch.md
  • wrappers/sdd-dispatch.yml
📝 Walkthrough

Walkthrough

The PR refines the sdd-dispatch cascade to handle webhook payload gaps and expands /dispatch preconditions. The route job now re-fetches closed issues via REST before parent-walk routing. The compute job accepts sdd:review as an armed lifecycle state for dispatch commands. Documentation and generated prompts are updated accordingly.

Changes

sdd-dispatch cascade and precondition fixes

Layer / File(s) Summary
Issues.closed cascade re-fetch in route job
wrappers/sdd-dispatch.yml
The route job's handler for issues.closed events now performs an explicit REST API re-fetch of the closed issue to populate parent_issue_url before starting the parent-walk to the tracking issue, fixing cases where webhook payloads omit the child→parent link.
Dispatch precondition expansion in compute job
wrappers/sdd-dispatch.yml
The compute job's /dispatch precondition check for command triggers was updated to treat sdd:review as an allowed "ok" lifecycle state alongside sdd:ready and sdd:in-progress. The refusal branch for sdd:spec/sdd:triage was adjusted to reflect the expanded set of valid lifecycle labels in its generated reason text.
Contract documentation updates
.github/workflows/sdd-dispatch.md
Documentation clarifies the cascade-path handling by explaining the explicit REST re-fetch step for closed issues, expands /dispatch validity rules to accept sdd:review, and refines lifecycle-label invariants to explicitly include the sdd:review case where the label is left unchanged.
Generated prompt and metadata updates
.github/workflows/sdd-dispatch.lock.yml
The lock file updates the gh-aw metadata hash, adjusts internal heredoc delimiter identifiers in the prompt assembly and config sections, and embeds the updated sdd-dispatch contract text reflecting both the re-fetch behavior and the expanded /dispatch preconditions.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Possibly related issues

Possibly related PRs

  • norrietaylor/spectacles#101: Updates /dispatch handling logic and sdd-dispatch contract at the same dispatch decision points; the main PR expands preconditions to accept sdd:review while the retrieved PR introduces a fast-path-specific noop branch.
  • norrietaylor/spectacles#97: Introduces foundational sdd-dispatch contract and wrapper logic; the main PR refines cascade-path handling and dispatch preconditions within the same workflow components.

Poem

🐰 A cascade now hops with REST in its stride,
When issues close, we fetch the child's guide,
And dispatch commands accept one state more—
Review is armed! Our workflow's at war.
With docs and prompts all singing the same,
The sdd machine now runs true to its name.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The pull request title directly reflects the main objectives: fixing the cascade wedge issue on task-close (#133) and at sdd:review (#134).
Description check ✅ Passed The pull request description comprehensively explains the root causes, fixes, and acceptance criteria, directly relating to all changes across the three modified files.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/dispatch-cascade-wedge

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/sdd-dispatch.md:
- Around line 116-129: The paragraph describing preconditions is
self-contradictory: it allows the `/dispatch` command at sdd:review but still
states that sdd:dispatched is only applied alongside sdd:in-progress; update the
text to reflect that sdd:dispatched can also be set from sdd:review (when the
dispatcher arms sdd:dispatched and fans out tasks) or rephrase to say that
sdd:dispatched is normally applied with sdd:in-progress except on the cascade
path from sdd:review; adjust the sentence mentioning sdd:dispatched and
sdd:in-progress to reference the cascade exception and/or sdd:review and
sdd:execute so the invariant is no longer contradictory.

In `@wrappers/sdd-dispatch.yml`:
- Around line 346-350: Update the stale precondition comment for /dispatch in
wrappers/sdd-dispatch.yml so it matches the implemented behavior: change the
note that "sdd:dispatched is only applied alongside sdd:in-progress" to state
that sdd:dispatched may be applied alongside sdd:in-progress or sdd:review
(because this PR can arm dispatched from review), and clarify that a cascade
fire skips the tracking-issue label check for those cases; apply the same
wording fix to the other occurrence around lines 359-361.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a02e236e-5cd6-4c9f-a708-ddfb7d8402bf

📥 Commits

Reviewing files that changed from the base of the PR and between 9fc0598 and 2b04e81.

📒 Files selected for processing (3)
  • .github/workflows/sdd-dispatch.lock.yml
  • .github/workflows/sdd-dispatch.md
  • wrappers/sdd-dispatch.yml

Comment thread .github/workflows/sdd-dispatch.md Outdated
Comment thread wrappers/sdd-dispatch.yml Outdated
@norrietaylor norrietaylor merged commit 1c13898 into main May 22, 2026
10 checks passed
@norrietaylor norrietaylor deleted the fix/dispatch-cascade-wedge branch May 22, 2026 15:19
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