You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Found immediately after #4084 made boot-phase warnings visible. The ADR-0110 D5 [action-governance] inventory fired on the platform's own example app, and it was right: the handlers really are missing.
This is the D5 checklist doing exactly the job it was built for, on its first boot where anyone could see it.
The finding
Booting examples/app-todo through os serve (or os dev) reports all eight of its todo_task script actions as unbound:
WARN [action-governance] declared script actions with NO handler — a button wired to
nothing (ADR-0078); add a `body`, or register a handler under the declared `target`
{"count":8,"actions":["todo_task:clone_task","todo_task:complete_task",
"todo_task:defer_task","todo_task:delete_completed","todo_task:export_csv",
"todo_task:mass_complete","todo_task:set_reminder","todo_task:start_task"]}
The example is correctly authored. task.actions.ts declares complete_task with target: 'completeTask', register-handlers.ts registers engine.registerAction('todo_task', 'completeTask', completeTask), and objectstack.config.ts exports export const onEnable = async (ctx) => registerTaskActionHandlers(ctx.ql). That pairing is exactly what resolveActionHandlerKeys resolves, so a registered handler would reconcile.
It is not a false positive — the action 404s
$ curl -sX POST http://localhost:44340/api/v1/actions/todo_task/complete_task \
-H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' -d '{}'
{"success":false,"error":{"code":"RESOURCE_NOT_FOUND",
"message":"Action 'complete_task' on object 'todo_task' not found","httpStatus":404}}
Every "Mark Complete" / "Start" / "Clone" / "Export CSV" button in the example app is dead over HTTP.
Root cause
AppPlugin runs, reaches the runtime-hook resolution at packages/runtime/src/app-plugin.ts:563, and finds nothing:
$ os serve objectstack.config.ts --dev --log-level debug | grep onEnable
DEBUG No runtime.onEnable function found {"appId":"com.example.todo"}
So the bundle AppPlugin receives has no onEnable, even though the config module exports one. Note that serve.ts carries explicit code for exactly this hazard:
// Preserve module-level named exports (e.g. `onEnable`, `onDisable`// lifecycle hooks) that would otherwise be dropped when we unwrap// `mod.default`. Without this AppPlugin can never invoke runtime hooks// declared as `export const onEnable = ...` alongside the default// `defineStack(...)` export.
…and app-plugin.ts:559-565 carries a matching fallback for the same reason. Both guards exist, and the hook still does not arrive — so the loss is somewhere between them. The [StandaloneStack] artifact read path is the obvious suspect: dist/objectstack.json is pure JSON metadata and cannot carry a function, so if the bundle handed to AppPlugin is derived from the artifact rather than the loaded module, metadata (the 8 declarations) survives and code (the handlers) does not — which is precisely the observed split.
Repro
cd examples/app-todo
os serve objectstack.config.ts --port 44340 --dev
# banner now shows the [action-governance] WARN naming all 8 (needs #4084)# then, with a session token:
curl -sX POST localhost:44340/api/v1/actions/todo_task/complete_task \
-H "Authorization: Bearer $TOKEN" -d '{}'# → 404
Why this matters
declared ≠ enforced at the call site (Prime Directive chore: version packages #10): the app declares eight executable actions and the runtime serves none of them. The showcase demonstrates a capability that does not work.
ADR-0110 D3 refuses undeclared handlers at dispatch with no opt-out. The mirror case — declared, handler dropped by the host — currently fails the same way at the same place, with the operator having done nothing wrong.
Scope check worth doing
I only verified the os serve <config> path on examples/app-todo. Worth confirming whether the compiled-artifact path (os build → os start), the os dev fast path, and app-showcase / app-crm behave the same, since they share AppPlugin.
Found immediately after #4084 made boot-phase warnings visible. The ADR-0110 D5
[action-governance]inventory fired on the platform's own example app, and it was right: the handlers really are missing.This is the D5 checklist doing exactly the job it was built for, on its first boot where anyone could see it.
The finding
Booting
examples/app-todothroughos serve(oros dev) reports all eight of itstodo_taskscript actions as unbound:The example is correctly authored.
task.actions.tsdeclarescomplete_taskwithtarget: 'completeTask',register-handlers.tsregistersengine.registerAction('todo_task', 'completeTask', completeTask), andobjectstack.config.tsexportsexport const onEnable = async (ctx) => registerTaskActionHandlers(ctx.ql). That pairing is exactly whatresolveActionHandlerKeysresolves, so a registered handler would reconcile.It is not a false positive — the action 404s
Every "Mark Complete" / "Start" / "Clone" / "Export CSV" button in the example app is dead over HTTP.
Root cause
AppPluginruns, reaches the runtime-hook resolution atpackages/runtime/src/app-plugin.ts:563, and finds nothing:So the bundle
AppPluginreceives has noonEnable, even though the config module exports one. Note thatserve.tscarries explicit code for exactly this hazard:…and
app-plugin.ts:559-565carries a matching fallback for the same reason. Both guards exist, and the hook still does not arrive — so the loss is somewhere between them. The[StandaloneStack] artifact readpath is the obvious suspect:dist/objectstack.jsonis pure JSON metadata and cannot carry a function, so if the bundle handed toAppPluginis derived from the artifact rather than the loaded module, metadata (the 8 declarations) survives and code (the handlers) does not — which is precisely the observed split.Repro
Why this matters
declared ≠ enforcedat the call site (Prime Directive chore: version packages #10): the app declares eight executable actions and the runtime serves none of them. The showcase demonstrates a capability that does not work.Scope check worth doing
I only verified the
os serve <config>path onexamples/app-todo. Worth confirming whether the compiled-artifact path (os build→os start), theos devfast path, andapp-showcase/app-crmbehave the same, since they shareAppPlugin.