feat(janitor): unmergeable_pr_rebase rule — auto-rebase when no event fires #783

Merged
charles merged 1 commit from code-lead/781 into main 2026-05-02 22:36:11 +00:00
Collaborator

Closes #781.

Summary

Adds janitor rule unmergeable_pr_rebase that closes the no-event gap on Forgejo: when a PR's mergeable flag flips to false outside of a pull_request closed && merged=true (push to base outside a merge, force-push, dependency landing elsewhere), nothing currently dispatches a rebase. The rule sweeps open PRs each janitor pass and dispatches via the same path as handlePostMergeRebase.

Design

  • New rule registered in _ALL_RULES and reconcileOnce.
  • Skips mergeable !== false, non-agent authors, and any PR that declares parents (those defer to handlePostMergeRebase's parent-merge cascade — avoids racing the stacked-rebase serialisation).
  • Reuses the existing _postMergeRebaseDispatched map. Two paths (event-driven + janitor sweep) cannot double-dispatch within the 10-minute window.
  • Dispatch logic factored out of handlePostMergeRebase into dispatchPrRebase so both call sites share the skill loader, B10 watchdog tagging, A3 label resolution, and dedup.
  • Exposed via the impl seam so tests swap the dispatcher / dependency-graph reader / agent resolver without mock.module (which is process-global in Bun).

Test plan

  • Unit: rule dispatches when mergeable=false + agent author + no parents
  • Unit: rule skips when mergeable=true
  • Unit: rule skips when author isn't a configured agent
  • Unit: rule skips when PR declares any parent (B13 / #429 routing)
  • Unit: dedup status from dispatchPrRebase is a silent skip (no JanitorAction emitted)
  • Unit: dry-run mode flags without calling the dispatcher
  • Existing post-merge tests (post-merge.test.ts, event-handlers.test.ts) still pass through the refactored helper — 100/100
  • just qa clean (3 pre-existing session JSONL pruning failures on main are unrelated)
Closes #781. ## Summary Adds janitor rule `unmergeable_pr_rebase` that closes the no-event gap on Forgejo: when a PR's `mergeable` flag flips to `false` outside of a `pull_request closed && merged=true` (push to base outside a merge, force-push, dependency landing elsewhere), nothing currently dispatches a rebase. The rule sweeps open PRs each janitor pass and dispatches via the same path as `handlePostMergeRebase`. ## Design - New rule registered in `_ALL_RULES` and `reconcileOnce`. - Skips `mergeable !== false`, non-agent authors, and any PR that declares parents (those defer to `handlePostMergeRebase`'s parent-merge cascade — avoids racing the stacked-rebase serialisation). - Reuses the existing `_postMergeRebaseDispatched` map. Two paths (event-driven + janitor sweep) cannot double-dispatch within the 10-minute window. - Dispatch logic factored out of `handlePostMergeRebase` into `dispatchPrRebase` so both call sites share the skill loader, B10 watchdog tagging, A3 label resolution, and dedup. - Exposed via the `impl` seam so tests swap the dispatcher / dependency-graph reader / agent resolver without `mock.module` (which is process-global in Bun). ## Test plan - [x] Unit: rule dispatches when `mergeable=false` + agent author + no parents - [x] Unit: rule skips when `mergeable=true` - [x] Unit: rule skips when author isn't a configured agent - [x] Unit: rule skips when PR declares any parent (B13 / #429 routing) - [x] Unit: dedup status from `dispatchPrRebase` is a silent skip (no `JanitorAction` emitted) - [x] Unit: dry-run mode flags without calling the dispatcher - [x] Existing post-merge tests (`post-merge.test.ts`, `event-handlers.test.ts`) still pass through the refactored helper — 100/100 - [x] `just qa` clean (3 pre-existing `session JSONL pruning` failures on `main` are unrelated)
feat(janitor): unmergeable_pr_rebase rule (closes #781)
Some checks failed
qa / dockerfile (pull_request) Successful in 7s
qa / qa (pull_request) Failing after 22m39s
9cda43f575
Forgejo fires no event when a PR's mergeable flag flips after main moves
on under it (push outside a PR-merge, force-push, dependency landing
elsewhere). handlePostMergeRebase only covers the explicit merge case;
pr-changes-requested-graph only covers the review case. Stale unmergeable
PRs sat indefinitely.

Adds the eighth janitor rule. Each pass walks open PRs, picks
mergeable=false ones whose author is a configured agent and whose
declared parents are all merged, and dispatches a rebase via the shared
post-merge path — same dedup map keyed on repo#pr@sha so a janitor sweep
and a webhook-driven cascade can't double-dispatch within the 10-minute
window.

The dispatch helper is extracted from handlePostMergeRebase into
dispatchPrRebase so both call sites use the same skill loader, watchdog
tagging, label resolution, and dedup. Exposed via the impl seam so tests
can swap it without process-global mock.module.

Closes #781
charles deleted branch code-lead/781 2026-05-02 22:36:12 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
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!783
No description provided.