Skip to content

fix: wire Studio-authored actions, translations, and runtime sharing rules (#2605, #2591, #2592)#2608

Merged
os-zhuang merged 1 commit into
mainfrom
claude/studio-metadata-wiring-6rk4xw
Jul 5, 2026
Merged

fix: wire Studio-authored actions, translations, and runtime sharing rules (#2605, #2591, #2592)#2608
os-zhuang merged 1 commit into
mainfrom
claude/studio-metadata-wiring-6rk4xw

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Closes the three open members of the "declared but never wired" family tracked in #2605, each following the #2596 authored-hooks template. Fixes #2591, fixes #2592.

1. Authored actions now execute (#2605 item 1 — verified broken, then fixed)

engine.executeAction reads a map populated only from the app bundle at boot; nothing ever registered handlers for action metadata. Runtime probe on the live showcase confirmed the gap: POST /api/v1/actions/showcase_task/probe_authored_actionAction not found even though the row was stored + listed.

  • runtime/AppPlugin installs a QuickJS-sandboxed default action runner at boot (engine.setDefaultActionRunner) — the action-path twin of the fix(objectql,runtime,metadata-protocol): execute authored (Studio) hook bodies — default bodyRunner + live rebind (#2588) #2596 hook body runner. Opt out with OS_DISABLE_AUTHORED_ACTIONS=1.
  • objectql/ObjectQLPlugin gains resyncAuthoredActions: reads active action rows and actions embedded in authored object rows' actions[], unions them with loadMany('action') (fresher DB row wins), filters package-artifact actions (AppPlugin owns those — registerAction replaces by key, so re-registering would clobber), and registers the rest under packageId: 'metadata-service'. Runs at kernel:ready, on metadata:reloaded, and on action/object protocol mutations; serialized; full-replace so edits re-register and deletes tear down; a failed read never tears down live registrations.

2. Authored translations reach the i18n runtime (#2591)

Only static bundles (app bundle.translations, plugin translations/) were ever loaded — a published translation item was a dead-end on publish AND after restart.

  • Both adapters now carry a separate authored layer: FileI18nAdapter (service-i18n) and the kernel's in-memory fallback createMemoryI18n (core) — the fallback is what dev/standalone stacks actually run, which the runtime probe surfaced (the first cut only patched service-i18n and did nothing on the showcase).
  • The shared sync lives in core (wireAuthoredTranslationSync / readAuthoredTranslationLayer) and is wired by both AppPlugin and I18nServicePlugin with single-owner semantics (first wirer per i18n service instance wins). Locale resolution: _meta.locale → top-level locale → BCP-47-shaped item name; the name pattern is deliberately narrow so snake_case item names don't classify as locales.
  • Clear-then-reload: each sync replaces the authored layer wholesale, so deleted items/keys stop resolving instead of lingering in the deep-merged static map. Authored values win over static bundle values on read.

3. Sharing rules created at runtime bind without a restart (#2592)

bindRuleHooks was boot-only, and rule authoring is a data insertmetadata:reloaded never fires — so the first runtime rule for an object with no boot-time rule silently never evaluated.

  • plugin-sharing binds afterInsert/afterUpdate/afterDelete triggers on sys_sharing_rule (own package id plugin-sharing:rule-rebind, so the rebind's own unbindAllRuleHooks can never tear its triggers down) that re-run the boot bind from a fresh listRules(). Serialized so overlapping writes can't leave a stale snapshot bound; awaited so the rule is enforceable when the authoring call returns; fail-safe so a rebind failure never fails the rule write and keeps previous bindings.

Runtime verification (live showcase server, objectstack dev --seed-admin)

The #2560 lesson — mocked tests pass while the real runtime is broken — so every claim was probed end-to-end:

Probe Before After (no restart) After restart
PUT /meta/action/... (script body) → POST /actions/showcase_task/... Action not found body executed: {ok:true, doubled:42} still executes
PUT /meta/translation/zh-CNGET /i18n/translations/zh-CN key absent probe_hello = 你好,探针 still served
PUT + DELETE /meta/translation/fr live on save, gone after delete (clear-then-reload)
Create sys_sharing_rule for showcase_contact (0 rules at boot) → insert matching record (hook never bound) sys_record_share row written, reason: rule:probe_share_contacts

Tests

Residual (called out, not in scope)

  • MCP run_action / list_actions resolve declarations from object schema (obj.actions), so a standalone authored action executes via the REST route but isn't discoverable via MCP until standalone action items are merged into object reads — same declaration-surfacing gap as the Studio-rail listing task (task_8dd7c8a0). Object-embedded authored actions are fully covered on both surfaces.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Ag7HEv9FABsCQnVNNcPweH


Generated by Claude Code

…uthored actions, translations, and runtime sharing rules (#2605, #2591, #2592)

Three more members of the "declared but never wired" family (#2605), each
following the #2596 authored-hooks template and runtime-verified against a
live showcase server (author → invoke/read → delete → restart).

Authored actions (#2605 item 1): engine.executeAction's map was only ever
populated from the app bundle at boot, so a published `action` row was
stored and listed but never executable. AppPlugin now installs a QuickJS
default action runner (engine.setDefaultActionRunner, opt-out via
OS_DISABLE_AUTHORED_ACTIONS=1) and ObjectQLPlugin re-registers authored
actions (standalone rows AND object-embedded actions[]) under
packageId 'metadata-service' at kernel:ready, on metadata:reloaded, and on
action/object protocol mutations. Package-artifact actions are excluded so
AppPlugin's handlers are never clobbered.

Authored translations (#2591): only static bundles ever reached the i18n
runtime. Both adapters (FileI18nAdapter and the kernel's in-memory fallback)
now carry a separate authored layer replaced wholesale on each sync
(clear-then-reload), overlaying static bundles. The shared sync lives in
@objectstack/core (wireAuthoredTranslationSync) and is wired by AppPlugin
and I18nServicePlugin with single-owner semantics.

Sharing rules (#2592): bindRuleHooks was boot-only and rule authoring is a
data insert, so the first runtime rule for an object never evaluated until
restart. The sharing plugin now binds afterInsert/afterUpdate/afterDelete
triggers on sys_sharing_rule that unbind + re-bind the rule-hook package
from a fresh listRules(), serialized and fail-safe.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ag7HEv9FABsCQnVNNcPweH
@vercel

vercel Bot commented Jul 5, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
spec Ready Ready Preview, Comment Jul 5, 2026 2:39am

Request Review

@github-actions github-actions Bot added documentation Improvements or additions to documentation tests tooling size/xl labels Jul 5, 2026
@github-actions

github-actions Bot commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 5 package(s): @objectstack/core, @objectstack/objectql, @objectstack/plugin-sharing, @objectstack/runtime, packages/services.

40 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/ai/chatbot-integration.mdx (via @objectstack/runtime)
  • content/docs/ai/knowledge-rag.mdx (via @objectstack/core)
  • content/docs/api/index.mdx (via @objectstack/runtime)
  • content/docs/automation/hook-bodies.mdx (via @objectstack/runtime)
  • content/docs/automation/webhooks.mdx (via @objectstack/core)
  • content/docs/concepts/metadata-lifecycle.mdx (via @objectstack/objectql)
  • content/docs/concepts/north-star.mdx (via packages/core, packages/runtime)
  • content/docs/data-modeling/drivers.mdx (via @objectstack/runtime)
  • content/docs/data-modeling/formulas.mdx (via packages/objectql)
  • content/docs/deployment/cloud-artifact-api.mdx (via packages/runtime)
  • content/docs/deployment/index.mdx (via @objectstack/runtime)
  • content/docs/deployment/migration-from-objectql.mdx (via @objectstack/core, @objectstack/objectql)
  • content/docs/deployment/production-readiness.mdx (via @objectstack/runtime)
  • content/docs/deployment/single-project-mode.mdx (via @objectstack/runtime)
  • content/docs/deployment/vercel.mdx (via @objectstack/objectql, @objectstack/runtime)
  • content/docs/kernel/contracts/index.mdx (via @objectstack/core)
  • content/docs/kernel/runtime-services/audit-service.mdx (via packages/services)
  • content/docs/kernel/runtime-services/examples.mdx (via @objectstack/core)
  • content/docs/kernel/runtime-services/index.mdx (via packages/services)
  • content/docs/kernel/runtime-services/settings-service.mdx (via packages/services)
  • content/docs/kernel/services-checklist.mdx (via @objectstack/core, @objectstack/objectql)
  • content/docs/kernel/services.mdx (via @objectstack/core, @objectstack/objectql)
  • content/docs/permissions/authentication.mdx (via @objectstack/core, @objectstack/objectql, @objectstack/runtime)
  • content/docs/permissions/authorization.mdx (via packages/plugins/plugin-sharing)
  • content/docs/permissions/permissions-matrix.mdx (via packages/plugins/plugin-sharing)
  • content/docs/permissions/sharing-rules.mdx (via @objectstack/plugin-sharing)
  • content/docs/plugins/anatomy.mdx (via @objectstack/core)
  • content/docs/plugins/development.mdx (via @objectstack/core)
  • content/docs/plugins/index.mdx (via @objectstack/core, @objectstack/objectql)
  • content/docs/plugins/packages.mdx (via @objectstack/core, @objectstack/objectql, @objectstack/plugin-sharing, @objectstack/runtime, packages/services)
  • content/docs/protocol/objectos/config-resolution.mdx (via @objectstack/core)
  • content/docs/protocol/objectos/http-protocol.mdx (via @objectstack/runtime)
  • content/docs/protocol/objectos/i18n-standard.mdx (via packages/services)
  • content/docs/protocol/objectos/index.mdx (via @objectstack/core, @objectstack/objectql, @objectstack/runtime)
  • content/docs/protocol/objectos/lifecycle.mdx (via @objectstack/core, @objectstack/runtime)
  • content/docs/protocol/objectos/plugin-spec.mdx (via @objectstack/core)
  • content/docs/protocol/objectql/security.mdx (via packages/plugins/plugin-sharing)
  • content/docs/protocol/objectql/state-machine.mdx (via @objectstack/objectql)
  • content/docs/releases/implementation-status.mdx (via @objectstack/core, @objectstack/objectql, @objectstack/runtime)
  • content/docs/releases/v9.mdx (via @objectstack/objectql)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

@os-zhuang os-zhuang marked this pull request as ready for review July 5, 2026 02:53
@os-zhuang os-zhuang merged commit 4f5b791 into main Jul 5, 2026
16 checks passed
@os-zhuang os-zhuang deleted the claude/studio-metadata-wiring-6rk4xw branch July 5, 2026 02:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/xl tests tooling

Projects

None yet

2 participants