Context
Surfaced during review of #819 (Slack channel triggers). The slack event source
adds source-specific dispatch logic to SchedulerDO.handleEvent /
handleRunComplete, gated behind event.source === "slack" checks:
- candidate selection (
getSlackAutomationsForChannel)
- a per-automation hourly rate-limit branch
- skipped-run recording (
recordSlackSkip)
- pre-insert run columns (
slackRunColumns)
- completion + concurrency-skip callback fan-out (
notifySlackCompletion,
notifySlackConcurrencySkip)
Today this is localized and readable, but SchedulerDO is where every future
source-specific exception would accumulate.
Proposal (when a second source needs custom dispatch hooks)
Introduce a small per-source policy/handler boundary so the central loop stays
boring — select candidates → evaluate conditions → ask the source policy whether
it may start → insert run → start session — with per-source hooks for:
- candidate selection
- pre-insert run columns
- "may start?" gating (rate limit / concurrency)
- skip handling
- completion notification
Why deferred (not in #819)
Slack is currently the only source with custom dispatch hooks; the other four
(github / linear / webhook / sentry) share the generic path with no overrides. A
5-method policy interface with four no-op implementors would be a speculative
abstraction shaped by a single caller. The right time to extract it is when a
second source needs source-specific dispatch behavior — that reveals the actual
shape the interface should take.
Context
Surfaced during review of #819 (Slack channel triggers). The
slackevent sourceadds source-specific dispatch logic to
SchedulerDO.handleEvent/handleRunComplete, gated behindevent.source === "slack"checks:getSlackAutomationsForChannel)recordSlackSkip)slackRunColumns)notifySlackCompletion,notifySlackConcurrencySkip)Today this is localized and readable, but
SchedulerDOis where every futuresource-specific exception would accumulate.
Proposal (when a second source needs custom dispatch hooks)
Introduce a small per-source policy/handler boundary so the central loop stays
boring — select candidates → evaluate conditions → ask the source policy whether
it may start → insert run → start session — with per-source hooks for:
Why deferred (not in #819)
Slack is currently the only source with custom dispatch hooks; the other four
(github / linear / webhook / sentry) share the generic path with no overrides. A
5-method policy interface with four no-op implementors would be a speculative
abstraction shaped by a single caller. The right time to extract it is when a
second source needs source-specific dispatch behavior — that reveals the actual
shape the interface should take.