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

Bind the dispatch engine at onEnable#44
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 validateexit 0, green (no gate exists for this — as the issue predicted)
  • pnpm test -- test/dispatch-wiring.test.tsred, 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 mainSep 1, 2026
1 check passed
Sign up for freeto 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

@os-warren
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
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

Bind the dispatch engine at onEnable#44
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 validateexit 0, green (no gate exists for this — as the issue predicted)
  • pnpm test -- test/dispatch-wiring.test.tsred, 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 mainSep 1, 2026
1 check passed
Sign up for freeto 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

@os-warren
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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

Bind the dispatch engine at onEnable#44
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 validateexit 0, green (no gate exists for this — as the issue predicted)
  • pnpm test -- test/dispatch-wiring.test.tsred, 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 mainSep 1, 2026
1 check passed
Sign up for freeto 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

@os-warren
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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

Bind the dispatch engine at onEnable#44
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 validateexit 0, green (no gate exists for this — as the issue predicted)
  • pnpm test -- test/dispatch-wiring.test.tsred, 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 mainSep 1, 2026
1 check passed
Sign up for freeto 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

@os-warren
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
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

Bind the dispatch engine at onEnable#44
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 validateexit 0, green (no gate exists for this — as the issue predicted)
  • pnpm test -- test/dispatch-wiring.test.tsred, 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 mainSep 1, 2026
1 check passed
Sign up for freeto 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

@os-warren
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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

Bind the dispatch engine at onEnable#44
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 validateexit 0, green (no gate exists for this — as the issue predicted)
  • pnpm test -- test/dispatch-wiring.test.tsred, 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 mainSep 1, 2026
1 check passed
Sign up for freeto 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

@os-warren
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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

Bind the dispatch engine at onEnable#44
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 validateexit 0, green (no gate exists for this — as the issue predicted)
  • pnpm test -- test/dispatch-wiring.test.tsred, 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 mainSep 1, 2026
1 check passed
Sign up for freeto 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

@os-warren
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
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

Bind the dispatch engine at onEnable#44
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 validateexit 0, green (no gate exists for this — as the issue predicted)
  • pnpm test -- test/dispatch-wiring.test.tsred, 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 mainSep 1, 2026
1 check passed
Sign up for freeto 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

@os-warren