Skip to content

Bind the dispatch engine at onEnable - #44

Merged
os-warren merged 1 commit into
mainfrom
claude/issue-42-bind-dispatch-engine
Sep 1, 2026
Merged

os-warren merged 1 commit into
mainfrom
claude/issue-42-bind-dispatch-engine

Conversation

@os-warren

Copy link
Copy Markdown
Collaborator

Fixes #42

What

src/jobs/dispatch.job.ts (#43) exposed bindDispatchEngine(engine) but nothing called it, so duly_dispatch threw Job 'duly_dispatch' has no data engine … at every run and dispatched nothing — a scheduled job that appears fully configured and silently produces no tasks.

registerDulyActionHandlers in src/actions/register-handlers.ts is the one place an ObjectStack application is handed ctx.ql (via defineStack({ onEnable })), so it now also calls bindDispatchEngine(ql) beside the existing registerCatalogActionHandlers(ql) / registerTaskActionHandlers(ql) calls — one line, unchanged function shape.

HandlerRegistrationContext widened from { registerAction } to extends DispatchEngine (adds find / insert / update) so the type carries what the bind needs, single-sourced from dispatch.job.ts's own DispatchEngine interface rather than restated.

Per the issue, did not: rename registerDulyActionHandlers (the issue's "consider renaming" was optional; the dispatch prompt for this card said not to restructure the function), touch objectstack.config.ts, or change anything under src/jobs/.

Why the test is the deliverable

register-handlers.ts has no author-time gate — an unregistered/unbound handler renders as fully configured, is clickable/schedulable, and fails only at call time; pnpm validate is green either way. Every existing dispatch assertion (test/dispatch.test.ts) calls bindDispatchEngine(data) itself before touching dulyDispatch, which is right for testing the planner and the idempotency index — but it would keep passing even if this issue's one-line fix were reverted.

test/dispatch-wiring.test.ts boots the app the way a real host does instead: it merges objectstack.config.ts's default export with its onEnable named export before constructing AppPlugin — { ...stackConfig, onEnable } — exactly as @objectstack/cli's serve.ts does (measured in its dist/commands/serve.js, comment included in the test). test/task-actions.test.ts deliberately does not do this (passes the default export alone, and registers handlers by hand) — this is the first suite in the repo that does. It then calls dulyDispatch({ jobId: DISPATCH_JOB_NAME }) with no test-side bindDispatchEngine call and asserts a task is actually created against the real engine.

test/catalog-instantiate.test.ts's handler-wiring fake needed a matching update (no-op find/insert/update) to satisfy the widened interface — that suite is about the action-handler registry, not the dispatch engine, so the additions are inert.

Ablation (required by the dispatch prompt)

Removed the bindDispatchEngine(ql); line (plus its comment) from register-handlers.ts, confirmed on disk before measuring:

$ grep -c "bindDispatchEngine(ql);" src/actions/register-handlers.ts
0
$ git diff --stat -- src/actions/register-handlers.ts
 src/actions/register-handlers.ts | 7 +------
 1 file changed, 1 insertion(+), 6 deletions(-)

Then measured:

  • pnpm validate → exit 0, green (no gate exists for this — as the issue predicted)
  • pnpm test -- test/dispatch-wiring.test.ts → red, exactly:
    Error: Job 'duly_dispatch' has no data engine. The platform invokes a job handler with
    { jobId, data, bundle } and no engine handle, so the host must call
    bindDispatchEngine(ctx.ql) from defineStack({ onEnable }). No tasks were dispatched.
    
    (Test Files 1 failed | 8 passed (9), Tests 1 failed | 278 passed (279) — only the new suite fails)

Restored via a trap 'git checkout -- "$F"' EXIT INT TERM from the committed fix (commit made before ablating, per the "never ablate uncommitted work" rule), confirmed clean (git status --short empty, line back in place) before re-running gates.

Gates — all green at ee3640f

$ pnpm validate    # exit 0 — ✓ Validation passed
$ pnpm typecheck   # exit 0 — tsc --noEmit, no errors
$ pnpm test        # exit 0 — 9 test files, 279/279 passed
$ pnpm build       # exit 0 — ✓ Build complete, artifact + runtime bundle written

Known limit (unchanged from the issue, not this PR's to fix)

This closes the objectstack dev/config-boot path only. The objectstack build artifact path still cannot carry onEnable (a JSON artifact holds no functions), so an artifact-served boot never runs it and the binding never happens there — that's the second half of objectstack#14094, upstream, and applies identically to the action handlers already registered in this same function.


Generated by Claude Code

registerDulyActionHandlers is the one place an ObjectStack application is
handed ctx.ql, so it now also calls bindDispatchEngine(ql) beside the
existing action-handler registrations. Widens HandlerRegistrationContext to
extend DispatchEngine (find/insert/update) so the type carries what the bind
needs, with no restructuring of the function itself.

test/dispatch-wiring.test.ts boots the app the way a real host does — merging
objectstack.config.ts's default export with its onEnable named export before
constructing AppPlugin, exactly as @objectstack/cli's serve.ts does — and
calls dulyDispatch() with no test-side bindDispatchEngine call. This is the
only suite that would fail if the wiring were reverted; every other dispatch
test binds the engine itself.

test/catalog-instantiate.test.ts's handler-wiring fake widened to satisfy the
larger interface (no-op find/insert/update; that suite is about the action
registry, not the dispatch engine).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SqkTcrxUFci7nqXdbBSe2p
@os-warren
os-warren marked this pull request as ready for review September 1, 2026 05:20
@os-warren
os-warren merged commit 83c0d3e into main Sep 1, 2026
1 check passed
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.

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

1 participant