Skip to content

fix(events): fan-out resolves against an empty sink registry - #490

Open
theRealBithive wants to merge 1 commit into
QuackbackIO:mainfrom
theRealBithive:fix/event-dispatch-resolver-registry
Open

fix(events): fan-out resolves against an empty sink registry#490
theRealBithive wants to merge 1 commit into
QuackbackIO:mainfrom
theRealBithive:fix/event-dispatch-resolver-registry

Conversation

@theRealBithive

Copy link
Copy Markdown

Every event published since the WO-18 cutover fanned out to nothing.

resolveTargets reads a module-level array that only registerAllResolvers() fills. Its sole caller was getHookTargets() in targets.ts, whose last production call site disappeared in that cutover — process.ts records the removal ("the legacy direct getHookTargets + bulk-add path is deleted"), but nothing took over the registration. The registry therefore stayed empty for the whole process lifetime and resolveTargets returned [] for every event: the row was stamped published_at, no hook job was enqueued, and no sink fired at all — integrations, webhooks, notifications, AI, summary, workflows, app webhooks and remote status push. Nothing logged, nothing errored.

Observed on a self-hosted instance: 14 event-dispatch jobs succeeded, zero rows ever appeared in the events queue, every post.created row carried published_at, and kv_store held no hooks:integration-mappings entry — proving the integration resolver had never once run.

runEventDispatch now registers before it resolves. The import is static rather than deferred: jobs/__tests__/handler-imports.test.ts forbids a call-time import() in a handler module because it would load that graph inside a per-pass workspace scope, and nothing in the resolver graph imports back into this module, so the cycle targets.ts works around is its own.

resolveTargets also logs an error when the registry is empty. The defining property of this outage was silence; an empty registry is never a valid state in a running tier, and finding this took a day of reading events, job_queue, kv_store and hook_deliveries by hand because no log pointed anywhere.

Every event published since the WO-18 cutover fanned out to nothing.

`resolveTargets` reads a module-level array that only `registerAllResolvers()`
fills. Its sole caller was `getHookTargets()` in targets.ts, whose last
production call site disappeared in that cutover — `process.ts` records the
removal ("the legacy direct getHookTargets + bulk-add path is deleted"), but
nothing took over the registration. The registry therefore stayed empty for the
whole process lifetime and `resolveTargets` returned `[]` for every event: the
row was stamped `published_at`, no hook job was enqueued, and no sink fired at
all — integrations, webhooks, notifications, AI, summary, workflows, app
webhooks and remote status push. Nothing logged, nothing errored.

Observed on a self-hosted instance: 14 `event-dispatch` jobs succeeded, zero
rows ever appeared in the `events` queue, every `post.created` row carried
`published_at`, and `kv_store` held no `hooks:integration-mappings` entry —
proving the integration resolver had never once run.

`runEventDispatch` now registers before it resolves. The import is static
rather than deferred: `jobs/__tests__/handler-imports.test.ts` forbids a
call-time `import()` in a handler module because it would load that graph
inside a per-pass workspace scope, and nothing in the resolver graph imports
back into this module, so the cycle targets.ts works around is its own.

`resolveTargets` also logs an error when the registry is empty. The defining
property of this outage was silence; an empty registry is never a valid state
in a running tier, and finding this took a day of reading `events`,
`job_queue`, `kv_store` and `hook_deliveries` by hand because no log pointed
anywhere.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@CLAassistant

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

@theRealBithive

theRealBithive commented Sep 4, 2026

Copy link
Copy Markdown
Author

The license signing thing is also broken:

image

Ah its fixed! thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants