Skip to content

docs(skills): document the sandboxed hook body ctx/capability contract#3314

Merged
os-zhuang merged 1 commit into
mainfrom
docs/skill-hook-sandbox-body
Jul 19, 2026
Merged

docs(skills): document the sandboxed hook body ctx/capability contract#3314
os-zhuang merged 1 commit into
mainfrom
docs/skill-hook-sandbox-body

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

What & why

The objectstack-data hooks docs explained lifecycle events and registration, but never the sandboxed body form — the metadata-native shape ({ language, source, capabilities }) that a metadata-only runtime actually executes and that ships inside every build artifact. A third-party evaluator on 15.1.x got hooks working only by reverse-engineering @objectstack/runtime's dist (memory project_thirdparty_eval_15_1_findings). This fills that gap.

Adds a "Sandboxed Hook Bodies (body)" section to references/data-hooks.md

Verified against runtime source and a live create-objectstack@15.1.1 repro:

  • body vs the deprecated inline handler — where each runs (QuickJS VM vs full Node), how each ships, that body wins when both are present.
  • The sandbox ctxinput / previous (undefined on insert → !ctx.previous = create) / result / user / session / event / object / api / log / crypto, marshalled as a JSON snapshot.
  • ctx.api.object(n) repofind / findOne / count / insert / update({ id, ...fields }) / upsert / delete. Query key is where (object + $-operators) — not filter: [['id','=',x]] (that array-of-triples value matches nothing).
  • The six legal HookBodyCapability tokens (api.read, api.write, api.transaction, crypto.uuid, crypto.hash, log) + call-time gating/throw behaviour.
  • Sandbox globals — verified available (Date/Math/JSON/Promise/Map/…) vs absent (console/fetch/setTimeout/URL/…), and build-rejected identifiers (import/process/eval/free identifiers).
  • Gotcha 1 — afterUpdate ctx.result is a partial patch → re-query for a lookup FK it doesn't carry.
  • Gotcha 2 — cross-object writes obey the target's sharing model (public_readFORBIDDEN: insufficient privileges, admin not exempt).
  • A copy-paste afterUpdate example (gated by the <!-- os:check --> tsc gate) + [BodyRunner] troubleshooting (incl. ctx.log emission is best-effort).

Plus: a quick-ref body block in rules/hooks.md, a SKILL.md pointer, a stale "14 events" → 8 fix, and the handler-form Cross-Object API example switched to canonical where.

Correction vs the source brief

The brief specified findOne({ filter: [['id','=',id]] }). Source + the #1867 real-engine/real-sandbox integration test + the objectstack-query skill all show the canonical key is where with an object value; filter is only a tolerated object-valued alias, and the triple-array value isn't supported. The docs teach where accordingly.

Verification

  • Live repro: scaffolded npx create-objectstack@latest (15.1.1), added candidate/position objects + the doc's hook verbatim, os dev, drove it over REST → hook fired, re-query + cross-object update landed (position.status = filled), zero [BodyRunner] threw. A beforeInsert probe confirmed the globals table.
  • Gates green: check:skill-docs, check:skill-refs, check:skill-examples (new example type-checks against built @objectstack/spec), check:doc-authoring.

🤖 Generated with Claude Code

The objectstack-data hooks docs covered events + registration but never the
sandbox `body` form — the metadata-native shape a metadata-only runtime
actually executes. A third-party evaluator on 15.1.x got hooks working only by
reverse-engineering `@objectstack/runtime`'s dist (memory:
project_thirdparty_eval_15_1_findings).

Add a "Sandboxed Hook Bodies (`body`)" section to references/data-hooks.md,
verified against runtime source AND a live `create-objectstack@15.1.1` repro:

- `body` vs the deprecated inline `handler`; the `{ language, source,
  capabilities }` shape; `source` is the function body wrapped as
  `(async (ctx) => { … })(ctx)`.
- the sandbox `ctx` (input/previous/result/user/session/event/object/api/log/
  crypto); `!ctx.previous` distinguishes create.
- `ctx.api.object(n)` repo — find/findOne/count/insert/update({id,...})/upsert/
  delete; query key is `where` (object + $-operators), NOT `filter:[['id','=',x]]`.
- the six legal `HookBodyCapability` tokens + call-time gating/throw behaviour.
- sandbox globals (verified available vs absent) and build-rejected identifiers.
- gotcha: afterUpdate `ctx.result` is a PARTIAL patch → re-query for lookup FKs.
- gotcha: cross-object writes obey the TARGET's sharing model (public_read →
  `FORBIDDEN`, admin not exempt).
- a copy-paste afterUpdate example (os:check-gated) + `[BodyRunner]` troubleshooting,
  including that `ctx.log` emission is best-effort.

Also: a quick-ref `body` block in rules/hooks.md, a SKILL.md pointer, fix the
stale "14 events" → 8, and switch the handler-form Cross-Object API example to
the canonical `where`.

Verified end-to-end: scaffolded `create-objectstack@15.1.1`, wrote the doc's
hook verbatim, drove it over REST — hook fired, the cross-object update landed
(`position.status = filled`), zero `[BodyRunner] threw`. Skill gates
(skill-docs / skill-refs / skill-examples / doc-authoring) all green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@os-zhuang os-zhuang added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Jul 19, 2026
@vercel

vercel Bot commented Jul 19, 2026

Copy link
Copy Markdown

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

Project Deployment Actions Updated (UTC)
spec Building Building Preview, Comment Jul 19, 2026 5:38pm

Request Review

@github-actions github-actions Bot added documentation Improvements or additions to documentation size/m and removed documentation Improvements or additions to documentation labels Jul 19, 2026
@os-zhuang
os-zhuang merged commit cbf795b into main Jul 19, 2026
17 of 18 checks passed
@os-zhuang
os-zhuang deleted the docs/skill-hook-sandbox-body branch July 19, 2026 18:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/m skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant