Repository navigation
Tracking: Studio-authored metadata that's declared but never wired at runtime (the 'silently does nothing' family) #2605
Description
The pattern (tracking / umbrella)
A recurring, high-severity class surfaced by the online-authoring push and the publish-rebind consumer audit: a business user authors metadata in the Studio, publishes it, and it's stored + shown as live — but the runtime never actually WIRES it, so it silently does nothing (often not even after a restart). Code-reading and mocked unit tests pass; only a real-runtime probe reveals it (the #2560 lesson). This is the same silent-failure theme as the just-shipped #4 (inline validation) and #6 (enable/disable) work — "you authorized it, it quietly doesn't take effect."
This issue consolidates the remaining members of that family into one place, now that the first fix has landed and established the pattern.
✅ Fixed — the template
- Authored hooks never executed their body — Authored (Studio) hooks never execute their body — no bodyRunner on the metadata-service bind path #2588, fixed by fix(objectql,runtime,metadata-protocol): execute authored (Studio) hook bodies — default bodyRunner + live rebind (#2588) #2596 (
2d567cbc7): wired the engine's defaultbodyRunner(+functions) into themetadata-servicehook-bind path and rebinds live on publish. This is the reference implementation for the rest below.
🔴 Open — same "declared-but-not-wired" family
-
Authored actions' handler may never run — UNVERIFIED, untracked (the reason for this issue).
executeActionreads a boot-populatedthis.actionsMap filled only from the app manifest viaregisterAction; objectql does not loadactionmetadata at boot. So a Studio-authored action (a script/bodyaction) is likely dead at runtime — the exact parallel of the hooks gap, and not covered by the hooks fix (fix(objectql,runtime,metadata-protocol): execute authored (Studio) hook bodies — default bodyRunner + live rebind (#2588) #2596 touched the hook-bind path only). Next step: runtime-probe it (author a script action into a writable base, publish, invoke viarun_action/ a flow action node, check before/after/restart). If broken, fix by reusing the fix(objectql,runtime,metadata-protocol): execute authored (Studio) hook bodies — default bodyRunner + live rebind (#2588) #2596 bodyRunner wiring for the action path. -
Authored translations never reach the i18n runtime — Authored (Studio) translations never reach the i18n runtime — only static bundles are loaded #2591.
loadTranslationsis called only with static bundles (appbundle.translations, plugintranslations/); nothing readstranslationmetadata items into service-i18n (getMetaItems({type:'translation'})→ 0).translationisallowRuntimeCreate:truebut unwired → a Studio-authored translation is a dead-end on publish AND after restart. -
Sharing rules created at runtime don't bind until restart — Sharing rules created at runtime don't bind their hooks until restart (bindRuleHooks is boot-only) #2592. Enforcement reads
sys_sharing_rulerows live, but the hook binding (bindRuleHooks,sharing-plugin.ts:201) is boot-only. Different trigger from the rest: authoring a rule is a data insert, sometadata:reloadednever fires → needs asys_sharing_ruledata-change rebind.
✅ Proven-fine — do NOT chase (audit, runtime-verified)
validation rules, object definition edits, action schema, report_schedule, and permission-sets / RLS / FLS all go live on publish without restart (read live per-request, not boot-cached). Confirmed by adding a field to an already-cached object and publishing → the new field was accepted without a restart.
Related but distinct (cross-reference — separate work, already tracked)
- Automation run-history observability follow-ups (retention / discoverable Runs surface / durable run detail) — Automation run observability — follow-ups (retention, discoverable Runs surface, durable run detail) #2585.
- Studio rail doesn't list published metadata for writable-base packages —
task_8dd7c8a0(in progress). - App-nav item authoring is off-spec (
kind/pathnot in the discriminated-union spec; runtimeresolveHrefignores it) — objectui chore: version packages #2245.
Reusable runtime-verify harness
The decisive method for any "authored X takes effect at runtime" claim: com.example.ops (an unregistered writable-base package) + a probe object/hook/action + POST /packages/:id/publish-drafts + a before / after / restart check. Code-reading and mocked tests pass while the real runtime is broken — always probe the running server.
Suggested sequencing
- Verify + (if broken) fix authored actions' handler — most likely the biggest remaining hole, and reuses the fix(objectql,runtime,metadata-protocol): execute authored (Studio) hook bodies — default bodyRunner + live rebind (#2588) #2596 bodyRunner wiring.
- i18n translations (Authored (Studio) translations never reach the i18n runtime — only static bundles are loaded #2591).
- Sharing-rule data-change rebind (Sharing rules created at runtime don't bind their hooks until restart (bindRuleHooks is boot-only) #2592).
Each is a real feature-fix with its own design surface (e.g. "should authored actions/hooks run sandboxed JS?" — a security gate), not a generic metadata:reloaded subscription.
Activity
fix(objectql,runtime,core,service-i18n,plugin-sharing): wire Studio-a…
os-zhuang commentedon Jul 5, 2026
All three open items in this family shipped in #2608 (
4f5b791), each runtime-verified against a live showcase server with the before/after/restart harness described above:POST /actions/... → Action not foundfor a publishedactionrow). Fixed by reusing the fix(objectql,runtime,metadata-protocol): execute authored (Studio) hook bodies — default bodyRunner + live rebind (#2588) #2596 template: default QuickJS action runner installed by AppPlugin (engine.setDefaultActionRunner, opt-outOS_DISABLE_AUTHORED_ACTIONS=1) +resyncAuthoredActionsin ObjectQLPlugin (kernel:ready / metadata:reloaded / action+object mutations; artifact-wins filter; covers standaloneactionrows AND actions embedded in authored object rows). Verified live: body executes on publish and after restart.wireAuthoredTranslationSync), with clear-then-reload delete semantics verified live.sys_record_sharerow on the next matching insert, no restart.Residual (not chased here): standalone authored
actionitems execute via the REST route but aren't discoverable via MCPrun_action/list_actions, since those resolve declarations fromobject.actions— same declaration-surfacing gap as the Studio-rail listing work (task_8dd7c8a0). Object-embedded authored actions are covered on both surfaces.Generated by Claude Code
os-zhuang commentedon Jul 16, 2026
Re-evaluation against latest
main(93fd58e, 2026-07-16)Re-audited every item in this umbrella against current code. Verdict: all three family members remain fixed on main, all sub-issues and cross-references are closed, and the one residual gap is now tracked separately (#3010) — this tracking issue can be closed.
🟢 The three fixes are all present on main, each with a dedicated regression test
resyncAuthoredActionsis live in the ObjectQL plugin with all three triggers (kernel:readyplugin.ts:259, metadata:reloaded:377, action/object mutations:389), serialized resyncs, full-replace semantics (removeActionsByPackage('metadata-service')then re-register), artifact-wins filtering, and coverage of both standaloneactionrows and object-embedded ones (resyncAuthoredActionsNow,plugin.ts:1338). The default QuickJS runner is installed by AppPlugin (app-plugin.ts:263) with theOS_DISABLE_AUTHORED_ACTIONS=1opt-out, and a loud warning fires if authored actions exist with no runner installed. Test:packages/objectql/src/plugin-authored-actions.test.ts.wireAuthoredTranslationSynclives in core (packages/core/src/fallbacks/authored-translation-sync.ts) and is wired by both hosts (AppPlugin for the kernel in-memory fallback,I18nServicePluginfor the file adapter,i18n-service-plugin.ts:126), with an ownership marker so double-wiring no-ops. Clear-then-reload delete semantics; triggers on kernel:ready, metadata:reloaded, and theonMetadataMutationstream. Test:packages/services/service-i18n/src/authored-translations.test.ts.sharing-plugin.ts:171binds afterInsert/afterUpdate/afterDelete triggers onsys_sharing_rulethat re-run the full unbind+bindRuleHookscycle, serialized and fail-safe (previous bindings kept on rebind failure). Test:packages/plugins/plugin-sharing/src/rule-rebind.test.ts.The hooks template fix (#2588/#2596) is also still intact:
setDefaultBodyRunneris called atapp-plugin.ts:225.🟢 Cross-references all resolved since the last update
kind/path) — closed 2026-07-05.task_8dd7c8a0) — the Studio surface now reads published ∪ draft for package items (StudioDesignSurface.tsx:1096,:1140), so this appears landed.🟢 The pattern got institutionalized: ADR-0096
The family's failure mode ("a schema collection the AI can author but the runtime ignores is the worst failure mode a metadata platform can ship") is now the explicit motivation of ADR-0096 declarative connector instances (#2977, runtime re-materialization landed in #3001 = the current
mainHEAD): declarativeconnectors:entries were exactly this class (registered as metadata, never reaching the engine registry). The fix goes further than the members here — provider-bound entries that can't materialize are a hard boot error, plus ametadata:reloadedreconcile with signature-based re-materialization. That's the "fail loudly instead of silently doing nothing" upgrade this umbrella was arguing for. Related: #2992 tracks the latent (stub) GraphQL/realtime surfaces in the same declared-vs-wired spirit.🔴 The one residual → now tracked as #3010
The wrap-up comment's residual is still real on
93fd58eand was untracked until now: standalone authoredactionrows execute (REST route, engine registry) but are invisible to MCPlist_actions/run_action, because both bridge resolution paths read declarations only fromobject.actions(http-dispatcher.ts:813,:1174). Split out as #3010 so it doesn't hold this umbrella open — it's a declaration-surfacing gap (same family as the rail-listing work), not an execution gap.Recommendation
With all three members shipped + regression-tested, all cross-refs closed, and the residual split into #3010, nothing remains that this issue tracks. Recommend closing as completed.
Generated by Claude Code