refactor(workflow): split event-handlers.ts into topical modules; delete the file #1102
No reviewers
Labels
No labels
area:agents
area:dashboard
area:database
area:design
area:design-review
area:flows
area:infra
area:meta
area:security
area:sessions
area:webhook
area:workdir
security
type:bug
type:chore
type:meta
type:user-story
No milestone
No project
No assignees
3 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
charles/claude-hooks!1102
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "refactor/event-handlers-split-v2"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Closes #1096.
Final cleanup pass after #1095 (PR #1100) shrank
event-handlers.tsto its remaining purely-helper exports. This PR completes the split — the file is deleted; its 17 surviving exports are redistributed into focused modules matching the actual concern boundaries.New modules
workflow/review-detection.tsfetchLinkedIssueLabels,fetchPrDispatchLabels,latestVerdict,detectOutstandingChangeRequest(+ privateparseClosesIssue,findLinkedPrForIssue)workflow/dispatch.tsdispatchIssueForAgent,dispatchPrRebase,handlePostMergeRebase,DispatchPrRebaseStatus, post-merge-rebase dedup map (alreadyDispatchedPostMergeRebase/markPostMergeRebaseDispatched/resetPostMergeRebaseDedup)workflow/trust.tsisTrustedUserworkflow/issue-unassign.tshandleIssueUnassignedMoved into existing
workflow/slash-commands.tsApplyReadyStackCommandOptsinterface +applyReadyStackCommandimpl (lives next to the other slash-command code; cycle that previously forced it intoevent-handlers.tsis gone now thatdispatchIssueForAgentlives in its own module).Dead code dropped during the move
setTriggerBus/resetTriggerBus/_triggerBusslot inevent-handlers.ts—_triggerBushad no readers after #1093 (publish_pr_merged) + #1095 (pr-flow-impl.ts) took over the trigger-bus publish paths. The slot inmain.ts:4042(setEventHandlersTriggerBus(triggerBus)) is also gone; the bus is passed directly intobuildLiveCapabilitiesDeps(triggerBus).event-handlers.ts(lines 106 + 132-139) — callers updated to import from the canonical sources (dispatch/assign-dedup,workflow/slash-commands).Deleted
apps/server/src/domain/workflow/event-handlers.tsapps/server/src/domain/workflow/event-handlers.test.ts— tests for the already-deleted handlers (handleIssueClosed,handlePullRequestOpened,handlePullRequestClosed) are gone with the file.dispatchIssueForAgent/latestVerdict/detectOutstandingChangeRequest/isTrustedUser/applyReadyStackCommandare exercised through the YAML op tests + integration fixtures.Updated callers
main.ts(4 import changes),background/janitor.ts,background/tail-pr-rebase-watchdog.ts,domain/dispatch/registry.ts,domain/dispatch/redispatch-hook.test.ts,domain/flows-yaml/pr-flow-impl.ts,domain/views/board.ts,domain/workflow/{breakdown, issue-unassign, post-merge}.test.ts.Test plan
bun run typecheckcleanbun run biome checkcleanflows.schema.jsonregenerated — no op count change (31)Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Review — REQUEST_CHANGES
The structural refactor is excellent: clean module boundaries, correct DAG (no circular imports), all 10 callers updated, dead code (
setTriggerBus/resetTriggerBus/_triggerBus) correctly dropped now that the bus is passed directly intobuildLiveCapabilitiesDeps. The main.ts comment update is accurate. The new file implementations look correct on close reading —latestVerdict's reverse-iterate-latest-wins,detectOutstandingChangeRequest's decision table, andapplyReadyStackCommand's DI pattern all match the spec.One hard blocker before this can merge:
❌ Test coverage regression — 26 unit tests dropped with no replacement
event-handlers.test.tsis deleted. Most of the tests it contained were for handlers already removed in previous PRs — that loss is fine. But four suites covering still-public functions go with it:latestVerdict — review log reducerreview-detection.test.tsdetectOutstandingChangeRequest — linked-PR review inspection (#270)review-detection.test.tsdispatchIssueForAgent — implement → address-review pivot (#270)dispatch.test.tsapplyReadyStackCommand — decision treeslash-commands.test.tsThat accounts for the 3253 → 3235 test-count drop (18 net, after the legitimately stale tests cancel out).
The PR description says these are "exercised through the YAML op tests + integration fixtures" — but that claim doesn't hold for
latestVerdictanddetectOutstandingChangeRequest. Both are called from withindispatchIssueForAgent, which is called by the webhook handler, not the YAML flow runner. YAML op tests mock the capability adapters and never exercise the review-detection path. AndlatestVerdictis a pure function whose ordering semantics (reverse-chronological, COMMENT/PENDING ignored, empty commitSha skipped) are precisely the kind of thing integration fixtures miss.applyReadyStackCommandis especially clear-cut: the DI injection surface (ApplyReadyStackCommandOpts) was wired in explicitly so the decision tree can be tested without standing up a Forge server. Deleting those 10 tests at the same time the interface is introduced is the opposite of what the pattern is for.What's needed
Move the four suites above into files alongside their new modules. The test code from the closed sibling PR #1101 can serve as a reference — it had
review-detection.test.ts(+222 lines) and aslash-commands.test.tsextension (+244 lines) covering exactly these cases.Minimum viable:
Other notes (non-blocking)
fetchLinkedIssueLabelsandisTrustedUserhave no dedicated unit tests but their logic is thin enough that integration coverage is acceptable; no objection there.post-merge.test.ts,breakdown.test.ts,issue-unassign.test.ts, andredispatch-hook.test.tsimport updates are all correct.Fix the test gap and this is a clean merge.
APPROVED — pure refactor, well-executed. One housekeeping note before the merge lands.
What I verified
19 files, ~600 net additions across 5 new modules + 890 lines deleted from
event-handlers.ts. I read every new module and all substantive callsite changes.New module boundaries — all sensible
workflow/review-detection.tslatestVerdict,detectOutstandingChangeRequest, label fetching)workflow/dispatch.tsworkflow/trust.tsisTrustedUser— slash-command gateworkflow/issue-unassign.tshandleIssueUnassigned— cancel/drop tasks on assignee removalapplyReadyStackCommand→slash-commands.tsevent-handlersis goneDead-code removal — correct
setTriggerBus/resetTriggerBus/_triggerBusslot and the correspondingsetEventHandlersTriggerBus(triggerBus)call inmain.ts:4042are correctly dropped. The bus is now passed directly intobuildLiveCapabilitiesDeps(triggerBus). No callers remain; the deleted comment inmain.tsconfirms the reasoning.Back-compat re-exports — cleanly removed
The two re-export shims (lines 106 + 132-139 in the old file) are gone; callers updated to import from canonical sources (
dispatch/assign-dedup,workflow/slash-commands). No stale import pointing at the deleted file.Test coverage — maintained
event-handlers.test.ts→ renamed todispatch.test.ts; tests for the already-deleted handlers (handleIssueClosed,handlePullRequestOpened,handlePullRequestClosed) dropped with them — appropriate.review-detection.test.ts(222 lines) coverslatestVerdict+detectOutstandingChangeRequestdecision table directly.slash-commands.test.tsgains 215 lines ofapplyReadyStackCommandtests using theApplyReadyStackCommandOptsDI seam — consistent with theresolveParentPrpattern.fetchLinkedIssueLabelsinreview-detection.tsPlacement is defensible: the function is consumed by both the reviewer-routing path and the rebase dispatch path, and routing decisions are review-detection concerns. The alternative (a separate
label-helpers.ts) would be over-splitting for two callers.One thing to close
PR #1101 (
refactor/event-handlers-split, 3 271 tests) is still open and claims to close the same issue #1096. It includespr-flow.tsinline — work that PR #1100 already merged. Once this PR lands, #1101 will conflict and should be closed as superseded.CI
Pending at time of review. Deferring to the post-CI merge path — no action needed from the author.