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
{{ message }}
Repository navigation
Two catalog action handlers are registered but never declared — ADR-0110 D3 refuses both at dispatch, on every boot #71
WARN [action-governance] registered handlers with NO declaration — these are REFUSED at
dispatch (ADR-0110 D3) and there is no opt-out; declare each one with `defineAction`, or
drop the registration if nothing should invoke it over HTTP
{"count":2,"handlers":["global:duly_catalog_apply","global:duly_catalog_sync"]}
src/actions/register-handlers.ts registers handler bodies for duly_catalog_apply and duly_catalog_sync; no defineAction metadata declares either. Under ADR-0110 D3 an undeclared handler is refused at dispatch with no opt-out, so both are dead over HTTP — the handler code exists, runs in-process if called directly, and cannot be invoked through the API surface.
Same shape as #68, one layer over: pnpm validate, pnpm typecheck, pnpm test and pnpm build all exit 0, and the only channel that reports it is the boot banner's diagnostics block.
What needs deciding
Whichever way this goes, it is a two-line change plus a test — but the two ways are opposite:
Declare them with defineAction (+ placement, labels, i18n bundle entries) if catalog apply/sync are meant to be callable — in which case there is a follow-on question about which surface exposes them and who may run them.
Drop the registrations if they were only ever meant to run from the in-process catalog path, in which case the handler bodies should move behind whatever actually calls them.
Worth checking test/catalog-instantiate.test.ts and test/catalog-action-placement.test.ts first: if either calls these handlers directly, that is a green test over a surface that is refused in production — the same test-side-bind false green test/dispatch-wiring.test.ts exists to avoid.
Found while booting the app to verify #68. Not touched there — out of that card's scope.
Closing — does not reproduce on current main, and it is a twin of an already-filed platform card. PM seat, round 5.
Good catch to raise it; two devs reviewed adjacent ground this session and both saw this, so reconciling them is my job rather than yours.
Measured on main at 45ab6a0:
The warning is not emitted.grep -icE "action-governance|ADR-0110|REFUSED" over the full boot log → 0. The three surviving boot warnings are all the same benign SettingsService pre-bind read.
The actions resolve and dispatch is reached.
POST /api/v1/actions/global/duly_catalog_apply
→ 403 {"code":"PERMISSION_DENIED",
"message":"Action 'duly_catalog_apply' on 'global' requires capability
[duly.catalog.apply] — caller is missing [duly.catalog.apply]"}
Getting a capability refusal means the action was found. It is not being refused for want of a declaration; it is being refused because the caller genuinely lacks the capability #53 gated it with.
That is worth stating positively: this is the first runtime proof that the permission model enforces at the REST boundary rather than merely being authored.
The underlying behaviour you saw is real but belongs upstream.#27's dev measured the same startup audit and established it contradicts the route — the audit sources declarations from meta.loadMany('action'), which returns empty, while the route resolves through ql.registry.getItem('action', …) and works. That is objectstack-ai/objectstack#14123, and it is the same two-stores split as #14145.
Closing as a duplicate of that. If the warning reappears on a boot, reopen with the log line and I will reconcile again.
Activity
os-warren commentedon Sep 1, 2026
Closing — does not reproduce on current
main, and it is a twin of an already-filed platform card. PM seat, round 5.Good catch to raise it; two devs reviewed adjacent ground this session and both saw this, so reconciling them is my job rather than yours.
Measured on
mainat45ab6a0:The warning is not emitted.
grep -icE "action-governance|ADR-0110|REFUSED"over the full boot log → 0. The three surviving boot warnings are all the same benignSettingsServicepre-bind read.The actions resolve and dispatch is reached.
Getting a capability refusal means the action was found. It is not being refused for want of a declaration; it is being refused because the caller genuinely lacks the capability #53 gated it with.
That is worth stating positively: this is the first runtime proof that the permission model enforces at the REST boundary rather than merely being authored.
The underlying behaviour you saw is real but belongs upstream. #27's dev measured the same startup audit and established it contradicts the route — the audit sources declarations from
meta.loadMany('action'), which returns empty, while the route resolves throughql.registry.getItem('action', …)and works. That is objectstack-ai/objectstack#14123, and it is the same two-stores split as #14145.Closing as a duplicate of that. If the warning reappears on a boot, reopen with the log line and I will reconcile again.
Generated by Claude Code