Skip to content

Wire the dispatch job's engine at onEnable — one line, in the file #4 currently owns #42

Description

@os-warren

Split out of #2 rather than bundled into it, because the one line lands in src/actions/register-handlers.ts, which #4 is editing right now.

What is missing

src/jobs/dispatch.job.ts (from #2) is complete and tested: the planner, the insert-and-swallow loop, the backfill window, last_dispatched_period. What it does not have is an engine, because the platform does not give a job handler one.

Measured on @objectstack/* 17.2.0, a defineJob handler is invoked with exactly { jobId, data, bundle } — no engine, no service registry, no logger. Filed upstream as objectstack-ai/objectstack#14094. Until that lands, the only place this application is handed ctx.ql is defineStack({ onEnable }), which is already wired through to registerDulyActionHandlers(ql).

So the dispatcher exposes one named seam and nothing calls it yet:

export function bindDispatchEngine(engine: DispatchEngine): void

Unbound, dulyDispatch refuses loudly — Job 'duly_dispatch' has no data engine … No tasks were dispatched. That is deliberate: a dispatcher that quietly does nothing is the worst failure this product can have, so the run fails and says why rather than reporting a clean night. But it does mean the job does not currently dispatch anything at runtime, and this issue is what closes that.

The change

In src/actions/register-handlers.ts, inside the existing function:

import { bindDispatchEngine } from '../jobs/dispatch.job.js';
// …
export function registerDulyActionHandlers(ql: HandlerRegistrationContext): void {
  registerCatalogActionHandlers(ql);
  registerTaskActionHandlers(ql);   // #4
  bindDispatchEngine(ql);           // this issue
}

HandlerRegistrationContext is declared as { registerAction } — narrowed to actions on purpose. It will need to widen to the find / insert / update slice DispatchEngine names. The value passed is the real ObjectQL engine (AppPlugin builds hostContext = { ...ctx, ql, logger, drivers }), so nothing new has to be threaded; only the type has to stop hiding it.

Consider renaming the function while you are there — it registers more than action handlers now. registerDulyRuntimeHandlers says what it does.

Acceptance

  • dulyDispatch({ jobId: 'duly_dispatch' }) dispatches against the real engine after a boot, with no test-side bindDispatchEngine call
  • the "refuses loudly when no host has bound an engine" test in test/dispatch.test.ts still passes (it unbinds first, so it should be unaffected)
  • pnpm validate && pnpm typecheck && pnpm test && pnpm build

Known limit, and why it is not this issue's to fix

This closes the objectstack dev / config-boot path. It does not close the artifact path: objectstack build emits functions into a runtime module exporting only { functions, meta }, the artifact JSON carries no onEnable, and mergeRuntimeModule merges only functions — so an artifact-served boot never runs onEnable and the binding is never made. That is the second half of objectstack#14094 and it affects action handlers identically; it is not something this application can fix from its own side.

Blocked-by

Blocked-by: #4 — same file, in flight. Land #4 first.

Activity

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

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions