fix(objectql,runtime): the ADR-0110 D5 inventory is engine-owned — AppPlugin never ran it under os dev - #4011
Merged
Conversation
…pPlugin never ran it under os dev A positive-control dogfood (inject an undeclared handler, expect it named at boot) proved the inventory's kernel:ready hook never fired on the platform's own dev loop: AppPlugin is registered CONDITIONALLY — serve.ts skips it when the host wraps itself, and the dev fast path loads apps without it — so the checklist that justifies D3's no-opt-out refusal was never printed where an upgrade most needs it. The addressing vocabulary (GLOBAL_ACTION_OBJECT_KEY, actionHandlerObjectKeys, isObjectLessActionKey, resolveActionHandlerKeys) and the reconciliation move into @objectstack/objectql: the engine owns the map they describe, and the dependency direction (runtime -> objectql, never the reverse) permits no other home for code its plugin must call. Runtime re-exports them unchanged (reconcileActionRegistrations keeps its deps-taking signature as a thin wrapper), so dispatch, the MCP bridge and every existing importer keep reading ONE implementation — the property that stops the inventory from ever disagreeing with the router. ObjectQLPlugin runs the audit in its existing kernel:ready handler, AFTER resyncAuthoredActions so the registry is final, and re-runs it on metadata:reloaded with fingerprint suppression — a Studio edit that orphans or binds a handler updates the report live, and a reload that changed nothing logs nothing. It also stops multiplying: the AppPlugin version ran once per app instance over the same global registry. Verified with a programmatic kernel: the injected orphan is named, a clean registry is silent. os dev / os serve still swallow ALL plugin boot logs (pre-existing console-sink defect, filed separately); on those surfaces the inventory surfaces once that sink is fixed. Also corrects app-todo's register-handlers comment, which still described defer/reminder as 'modal-type actions' with modal-page targets — the shape #3974 removed. objectql 6 new tests; runtime action-reconciliation 9 via the wrapper; objectql/runtime/metadata suites green by exit code (except 3 pre-existing analytics failures reproduced on clean main, filed separately). Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DTTKgYriDB6RVrkYjxxnG1
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
Contributor
📓 Docs Drift CheckThis PR changes 2 package(s): 26 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
This was referenced Jul 30, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Found by dogfooding the inventory itself with a positive control — inject an undeclared handler into
app-todo, boot, expect it named. It never was.The defect
D5's inventory hung off a
kernel:readyhook in AppPlugin, and AppPlugin is registered conditionally:serve.tsskips it when the host already wraps itself, and theos devfast path loads apps without it entirely. A programmatic kernel (which does register AppPlugin) printed the inventory correctly; the platform's own dev loop never did. That matters beyond aesthetics: #3987 removed the migration valve on the argument that the boot inventory names every offender — a claim that was true nowhere a developer actually looks.Silence was unfalsifiable by design — the inventory logs only when orphans exist — which is why this needed a positive control rather than a "boot it and grep" check.
The fix
Ownership follows the data. The map being audited is
ObjectQL's ownactionsregistry, so the audit moves to the engine plugin, which is unconditionally present wherever actions can execute:GLOBAL_ACTION_OBJECT_KEY,actionHandlerObjectKeys,isObjectLessActionKey,resolveActionHandlerKeys) andreconcileActionRegistrationsmove into@objectstack/objectql. The dependency direction (runtime → objectql, never the reverse) permits no other home for code the engine plugin must call. Runtime re-exports them unchanged — dispatch, the MCP bridge and every existing importer keep reading ONE implementation, the property that stops the inventory from ever disagreeing with the router. (reconcileActionRegistrationskeeps its old deps-taking signature in runtime as a thin wrapper.)ObjectQLPluginruns the audit inside its existingkernel:readyhandler — afterresyncAuthoredActions, so the registry it reads is final for the boot — and re-runs it onmetadata:reloadedwith fingerprint suppression: a Studio edit that orphans or binds a handler updates the report live; a reload that changed nothing action-related logs nothing. The AppPlugin version was boot-only (stale after the first Studio edit) and ran once per app instance over the same global registry (N apps → N identical reports).Verification
ghostProbeis named ([action-governance] … handlers: ['todo_task:ghostProbe']); reverting restores silence — so silence is now evidence, not absence of it.action-governance.test.ts(orphan naming, unbound-declaration naming, clean silence, standalone-item folding, fingerprint suppression across reloads, throw-proofing via a poisoned declarations source).plugin.step2.test.ts/protocol-discovery.test.ts(analytics-slot expectations) that reproduce on cleanmain, filed separately.Known remaining gap, filed separately
os devandos serveswallow all plugin boot-phase logs — a debug-level boot produces thousands of data-hook lines but zerobootstrap/kernel:readylines and zero plugin WARNs, on both entrypoints. So this inventory (like every other boot diagnostic, including the flow/hook failures ADR-0032 promises to surface at the defaultwarnlevel) is still invisible on the CLI surfaces until that sink is fixed. That is a pre-existing console-sink defect, not a hook-placement one — this PR fixes the half that was ours, and the issue tracks the half that isn't.Also corrects
app-todo'sregister-handlers.tscomment, which still described defer/reminder as "modal-type actions" with modal-page targets — the shape #3974 removed.🤖 Generated with Claude Code
https://claude.ai/code/session_01DTTKgYriDB6RVrkYjxxnG1
Generated by Claude Code