Skip to content

feat(spec): reject script actions with no executable binding + showcase execution tests - #2171

Merged
xuyushun441-sys merged 1 commit into
mainfrom
feat/action-script-executable-guard
Jun 22, 2026
Merged

feat(spec): reject script actions with no executable binding + showcase execution tests#2171
xuyushun441-sys merged 1 commit into
mainfrom
feat/action-script-executable-guard

Conversation

@xuyushun441-sys

Copy link
Copy Markdown
Contributor

Follow-up hardening for #2169 (the "Mark Done" runtime error). That bug shipped because a type: 'script' action with neither body nor target is schema-valid and passes the coverage test, yet registers no runtime handler — so it only failed when a user clicked the button (Action '<name>' on object '*' not found).

This adds the two layers that would have caught it, per the post-mortem discussion.

① Author/compile-time guard (root cause)

ActionSchema now enforces body || target when type === 'script' via a superRefine, mirroring the existing "non-script types require target" rule. os build / defineAction reject the broken shape immediately, for every bundle.

  • body-bound actions are auto-registered by the runtime (AppPluginactionBodyRunnerFactory).
  • target-bound actions name a function wired imperatively (e.g. via onEnable, as app-todo does) — the compiler can't see that wiring, so target is accepted as the author's assertion that a handler exists.
  • Only body-less andtarget-less script actions are rejected — those are unambiguously dead.

Verified against the full monorepo build (turbo build, 76 tasks): every shipped bundle still compiles, so this rejects only configurations that were already non-functional at runtime. Spec fixtures that leaned on the looser rule were updated.

② Execution-path test (catch-net)

examples/app-showcase/test/actions.test.ts drives the realactionBodyRunnerFactory + QuickJS sandbox (the exact bridge AppPlugin uses) against the shipped actions:

  • every declared script action is executable (body or target);
  • the runtime produces a handler for Mark Done (regression: fix(showcase): register Mark Done action handler via inline body #2169);
  • Mark Done writes { done: true, progress: 100 } and leaves status untouched;
  • a body-less script action yields no handler (documents the failure mode the schema now blocks).

The prior coverage.test.ts only asserted each ActionTypeappeared in the bundle — that blind spot is what let #2169 ship.

Verification

  • @objectstack/spec full suite: 6603 tests pass (incl. updated action/stack fixtures).
  • turbo build (whole monorepo): 76 tasks pass — no real bundle violates the new rule.
  • turbo test for runtime / objectql / downstream-contract / cli / service-ai / service-automation / example-crm / example-todo: all pass.
  • app-showcase pnpm verify: typecheck + 26 tests (incl. the 4 new execution tests).

🤖 Generated with Claude Code

…se execution tests
Prevents the class of bug fixed in #2169, where `showcase_mark_done` declared
`type: 'script'` but carried neither a `body` nor a `target`. AppPlugin only
registers an engine handler for actions with a runnable binding, so the action
fell through to the `'*'` wildcard lookup and failed at invocation with
`Action '<name>' on object '*' not found` — a soft failure that build- and
shape-level tests never caught.
Two complementary layers:
1. Author/compile-time guard (root cause): `ActionSchema` now requires
`body || target` when `type === 'script'` (a `superRefine`, mirroring the
existing "non-script types require `target`" rule). `os build` / `defineAction`
now reject the broken shape immediately, for every bundle. Verified against
the full monorepo build — every shipped bundle still compiles, so this only
rejects configurations that were already non-functional at runtime. Existing
spec fixtures that relied on the looser rule were updated.
2. Execution-path test (catch-net): examples/app-showcase/test/actions.test.ts
drives the real `actionBodyRunnerFactory` + QuickJS sandbox against the
shipped actions — asserting Mark Done produces a handler and writes
`{ done: true, progress: 100 }`. The prior coverage test only checked that
each ActionType *appeared* in the bundle, which is what let #2169 ship.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@vercel

vercelBot commented Jun 22, 2026

Copy link
Copy Markdown

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

ProjectDeploymentActionsUpdated (UTC)
specBuildingBuildingPreview, CommentJun 22, 2026 4:15am

Request Review

@github-actionsgithub-actionsBot added size/m documentation Improvements or additions to documentation tests protocol:ui tooling labels Jun 22, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/spec.

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

  • content/docs/concepts/architecture.mdx(via @objectstack/spec)
  • content/docs/concepts/cloud-artifact-api.mdx(via packages/spec)
  • content/docs/concepts/cluster-semantics.mdx(via @objectstack/spec)
  • content/docs/concepts/design-principles.mdx(via packages/spec)
  • content/docs/concepts/implementation-status.mdx(via @objectstack/spec)
  • content/docs/concepts/index.mdx(via @objectstack/spec)
  • content/docs/concepts/metadata-driven.mdx(via @objectstack/spec)
  • content/docs/concepts/metadata-lifecycle.mdx(via packages/spec)
  • content/docs/concepts/north-star.mdx(via packages/spec)
  • content/docs/concepts/packages.mdx(via @objectstack/spec)
  • content/docs/concepts/setup-app.mdx(via @objectstack/spec)
  • content/docs/concepts/skills.mdx(via @objectstack/spec)
  • content/docs/concepts/webhook-delivery.mdx(via @objectstack/spec)
  • content/docs/getting-started/architecture.mdx(via @objectstack/spec)
  • content/docs/getting-started/cli.mdx(via @objectstack/spec)
  • content/docs/getting-started/core-concepts.mdx(via @objectstack/spec)
  • content/docs/getting-started/examples.mdx(via @objectstack/spec)
  • content/docs/getting-started/quick-start.mdx(via @objectstack/spec)
  • content/docs/guides/adding-a-metadata-type.mdx(via @objectstack/spec)
  • content/docs/guides/ai-capabilities.mdx(via @objectstack/spec)
  • content/docs/guides/airtable-dashboard-analysis.mdx(via @objectstack/spec)
  • content/docs/guides/analytics-datasets.mdx(via @objectstack/spec)
  • content/docs/guides/api-reference.mdx(via @objectstack/spec)
  • content/docs/guides/business-logic.mdx(via @objectstack/spec)
  • content/docs/guides/cheatsheets/backward-compatibility.mdx(via @objectstack/spec)
  • content/docs/guides/cheatsheets/error-catalog.mdx(via @objectstack/spec)
  • content/docs/guides/cheatsheets/field-type-gallery.mdx(via @objectstack/spec)
  • content/docs/guides/cheatsheets/field-validation-rules.mdx(via @objectstack/spec)
  • content/docs/guides/cheatsheets/permissions-matrix.mdx(via @objectstack/spec)
  • content/docs/guides/cheatsheets/protocol-diagram.mdx(via packages/spec)
  • content/docs/guides/cheatsheets/query-cheat-sheet.mdx(via @objectstack/spec)
  • content/docs/guides/cheatsheets/quick-reference.mdx(via @objectstack/spec)
  • content/docs/guides/client-sdk.mdx(via @objectstack/spec)
  • content/docs/guides/common-patterns.mdx(via @objectstack/spec)
  • content/docs/guides/contracts/auth-service.mdx(via packages/spec)
  • content/docs/guides/contracts/cache-service.mdx(via packages/spec)
  • content/docs/guides/contracts/data-engine.mdx(via @objectstack/spec)
  • content/docs/guides/contracts/index.mdx(via @objectstack/spec)
  • content/docs/guides/contracts/metadata-service.mdx(via packages/spec)
  • content/docs/guides/contracts/storage-service.mdx(via packages/spec)
  • content/docs/guides/data-modeling.mdx(via @objectstack/spec)
  • content/docs/guides/deployment-vercel.mdx(via @objectstack/spec)
  • content/docs/guides/driver-configuration.mdx(via @objectstack/spec)
  • content/docs/guides/error-handling-client.mdx(via @objectstack/spec)
  • content/docs/guides/error-handling-server.mdx(via @objectstack/spec)
  • content/docs/guides/formula.mdx(via @objectstack/spec)
  • content/docs/guides/hook-bodies.mdx(via packages/spec)
  • content/docs/guides/kernel-services.mdx(via @objectstack/spec)
  • content/docs/guides/metadata/dashboard.mdx(via @objectstack/spec)
  • content/docs/guides/metadata/field.mdx(via @objectstack/spec)
  • content/docs/guides/metadata/flow.mdx(via @objectstack/spec)
  • content/docs/guides/metadata/index.mdx(via @objectstack/spec)
  • content/docs/guides/metadata/object.mdx(via @objectstack/spec)
  • content/docs/guides/metadata/validation.mdx(via @objectstack/spec)
  • content/docs/guides/metadata/workflow.mdx(via @objectstack/spec)
  • content/docs/guides/packages.mdx(via @objectstack/spec)
  • content/docs/guides/plugin-development.mdx(via @objectstack/spec)
  • content/docs/guides/plugins.mdx(via @objectstack/spec)
  • content/docs/guides/project-scoping.mdx(via @objectstack/spec)
  • content/docs/guides/public-forms.mdx(via @objectstack/spec)
  • content/docs/guides/runtime-services/email-service.mdx(via packages/spec)
  • content/docs/guides/runtime-services/index.mdx(via packages/spec)
  • content/docs/guides/runtime-services/queue-service.mdx(via packages/spec)
  • content/docs/guides/runtime-services/sharing-service.mdx(via packages/spec)
  • content/docs/guides/runtime-services/storage-service.mdx(via packages/spec)
  • content/docs/guides/security.mdx(via @objectstack/spec)
  • content/docs/guides/seed-data.mdx(via @objectstack/spec)
  • content/docs/guides/skills.mdx(via @objectstack/spec)
  • content/docs/guides/standards.mdx(via @objectstack/spec)
  • content/docs/guides/troubleshooting.mdx(via @objectstack/spec)
  • content/docs/protocol/knowledge.mdx(via @objectstack/spec)
  • content/docs/protocol/objectos/config-resolution.mdx(via @objectstack/spec)
  • content/docs/protocol/objectos/i18n-standard.mdx(via @objectstack/spec)
  • content/docs/protocol/objectos/lifecycle.mdx(via @objectstack/spec)
  • content/docs/protocol/objectos/plugin-spec.mdx(via @objectstack/spec)
  • content/docs/protocol/objectos/runtime-capabilities.mdx(via @objectstack/spec)
  • content/docs/protocol/objectql/index.mdx(via packages/spec)
  • content/docs/protocol/objectql/query-syntax.mdx(via @objectstack/spec)
  • content/docs/protocol/objectql/schema.mdx(via @objectstack/spec)
  • content/docs/protocol/objectql/security.mdx(via packages/spec)
  • content/docs/protocol/objectql/state-machine.mdx(via @objectstack/spec)
  • content/docs/protocol/objectui/actions.mdx(via @objectstack/spec)
  • content/docs/protocol/objectui/concept.mdx(via @objectstack/spec)
  • content/docs/protocol/objectui/index.mdx(via @objectstack/spec)
  • content/docs/protocol/objectui/layout-dsl.mdx(via packages/spec)
  • content/docs/protocol/objectui/record-alert.mdx(via @objectstack/spec)
  • content/docs/protocol/objectui/widget-contract.mdx(via @objectstack/spec)
  • content/docs/releases/index.mdx(via @objectstack/spec)
  • content/docs/releases/v9.mdx(via @objectstack/spec)

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.

@xuyushun441-sys
xuyushun441-sys merged commit d7ff626 into mainJun 22, 2026
16 of 17 checks passed
@xuyushun441-sys
xuyushun441-sys deleted the feat/action-script-executable-guard branch June 22, 2026 04:25
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationprotocol:uisize/mteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@xuyushun441-sys@os-zhuang