Skip to content

docs(spec): document the declarative flat-record surface on the HookContext.input contract table (#7254) - #7393

Merged
os-help merged 1 commit into
mainfrom
claude/issue-7254-hook-input-contract-table
Aug 10, 2026
Merged

docs(spec): document the declarative flat-record surface on the HookContext.input contract table (#7254)#7393
os-help merged 1 commit into
mainfrom
claude/issue-7254-hook-input-contract-table

Conversation

@os-help

@os-help os-help commented Aug 10, 2026

Copy link
Copy Markdown
Collaborator

Fixes #7254

What

The contract table on HookContextSchema.input (packages/spec/src/data/hook.zod.ts) documented exactly one shape — the raw envelope the engine builds — while addressing readers who only ever meet the other surface. This adds the declarative surface to the same table, as its own rows, and reframes the existing rows as the engine.registerHook view rather than the default.

Prose only. No behaviour change, no proxy change, no convergence proposal. The legal metadata set is byte-identical before and after (domain:spec-surface). packages/objectql/src/hook-input-shape-contract.test.ts — which pins the RAW envelope — is untouched, as required.

The three facts the table now states

surface input.field input.data.field
raw engine.registerHook handler not the record the record
declarative code handler the record also works (proxy passes data through)
declarative sandboxed body the record TypeError — no data key at all

So there is no single spelling that works everywhere, and the table now says which is correct where.

Verified against origin/main

Mechanism line positions re-verified (all unmoved): installFlatInput at hook-wrappers.ts:446 / helper :502; buildSandboxContext at body-runner.ts:314; unwrapProxyToPlain at :387.

Premise re-checked before implementing: grep -i "declarative|bindHooks|installFlatInput|flat|sandbox|proxy" over the file returns nothing in the input block — #7101's revision did not add these rows. Premise valid.

Measured with a throwaway probe (real ObjectQL kernel + bindHooksToEngine, deleted before commit; the probe applied Object.fromEntries(Object.entries(input)), i.e. exactly what unwrapProxyToPlain does to build the body snapshot):

beforeInsert  snapshotKeys=[status,title]  snapshot.data=undefined  proxy.data=object     proxy.title="hello"
beforeUpdate  snapshotKeys=[status]        snapshot.data=undefined  proxy.data=object     proxy.id=string
beforeFind    snapshotKeys=[]              snapshot.data=undefined  proxy.data=undefined  proxy.options=object
beforeDelete  snapshotKeys=[]              snapshot.data=undefined  proxy.data=undefined  proxy.id=string
previous:     beforeUpdate/beforeDelete -> [id,status,title]; absent on insert/find

That adds three facts beyond the card's table, all now documented: a body sees no id / options / ast either; on find and delete the whole snapshot is {}; and a body that needs the row reads ctx.previous (pre-image, id included). The input.data TypeError itself is already pinned on main by #7258 at examples/app-showcase/test/hook-body-persisted-writes.test.ts:174KEYS[email,message,name] hasData=undefined — which this PR cites rather than re-pinning.

Render-input check: TSDoc, not .describe() — no reference-page regen

The table lives in the TSDoc block above the property; the .describe() is only 'Mutable input parameters'. Confirmed empirically rather than assumed:

$ pnpm --filter @objectstack/spec gen:docs
✅ Generated 231 files
$ git status --short
 M packages/spec/src/data/hook.zod.ts        # zero artifact drift

content/docs/references/data/hook.mdx and packages/spec/json-schema/** carry only the describe string, so no consumer surface is reached ⇒ no changeset, skip-changeset applied.

Composition with the same-file churn

#7101 (per-row vs record dispatch) and #7235 (the @example line) both landed on this file. The new rows are additive and orthogonal; three rows above back-references in the following paragraphs were narrowed to envelope rows above so #5997's and #7101's statements still resolve unambiguously past the inserted block. One clause explicitly composes with #7101's D3: on a bulk write the flat spelling writes the same batch-scoped payload, so it scopes a rewrite no better than input.data.field did.

Gates

pnpm --filter @objectstack/spec test        -> 362 files / 9476 tests passed
pnpm --filter @objectstack/spec typecheck   -> OK (incl. check:test-typecheck)
pnpm --filter @objectstack/spec check:docs  -> ✅ 231 generated files in sync
pnpm --filter @objectstack/spec check:liveness -> ✓ green
node scripts/check-nul-bytes.mjs            -> OK (6757 files, no raw control bytes)

contracts/scoped-context.test.ts — which extracts the Usage in hooks example from this file's own JSDoc — stays green, so the E14/E23 pin sweep is honest: no test pins the table's literal text, and the one source-reading pin is unaffected.


Generated by Claude Code

…ontext.input contract table (#7254)

The contract table on `HookContextSchema.input` documented exactly one shape —
the raw envelope the engine builds for `engine.registerHook` callers — while
addressing readers who only ever meet the other surface.

Every declarative hook (a metadata `Hook`, i.e. everything from
`defineStack({ hooks })`) is wrapped by `wrapDeclarativeHook` →
`installFlatInput`, which swaps `ctx.input` for a Proxy presenting a flat
record view. A sandboxed `body` goes one step further: the runner hands the
script `unwrapProxyToPlain(engineCtx.input)`, which materialises only what the
proxy's `ownKeys` trap exposes — so `input` IS the record, there is no `data`
key, and the documented `input.data.<field>` spelling is a TypeError that
aborts the caller's write under the default `onError: 'abort'`.

Prose only: no behaviour change, no proxy change, and the legal metadata set is
byte-identical before and after. Whether the two surfaces should converge is
deliberately left undecided.

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

vercel Bot commented Aug 10, 2026

Copy link
Copy Markdown

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

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Aug 10, 2026 9:09am

Request Review

@os-help os-help added skip-changeset PR has no user-facing published change; bypasses the changeset gate and removed size/s labels Aug 10, 2026 — with Claude
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

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

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

  • content/docs/ai/agents.mdx (via @objectstack/spec)
  • content/docs/ai/skills-reference.mdx (via @objectstack/spec)
  • content/docs/ai/skills.mdx (via @objectstack/spec)
  • content/docs/api/client-sdk.mdx (via @objectstack/spec)
  • content/docs/api/environment-routing.mdx (via @objectstack/spec)
  • content/docs/api/error-catalog.mdx (via @objectstack/spec)
  • content/docs/api/error-handling-client.mdx (via @objectstack/spec)
  • content/docs/api/error-handling-server.mdx (via @objectstack/spec)
  • content/docs/api/index.mdx (via @objectstack/spec)
  • content/docs/automation/approvals.mdx (via @objectstack/spec)
  • content/docs/automation/connectors.mdx (via @objectstack/spec)
  • content/docs/automation/flows.mdx (via @objectstack/spec)
  • content/docs/automation/hook-bodies.mdx (via packages/spec)
  • content/docs/automation/hooks.mdx (via @objectstack/spec)
  • content/docs/automation/index.mdx (via @objectstack/spec)
  • content/docs/automation/webhooks.mdx (via @objectstack/spec)
  • content/docs/automation/workflows.mdx (via @objectstack/spec)
  • content/docs/concepts/architecture.mdx (via @objectstack/spec)
  • content/docs/concepts/design-principles.mdx (via packages/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 @objectstack/spec)
  • content/docs/data-modeling/analytics.mdx (via @objectstack/spec)
  • content/docs/data-modeling/drivers.mdx (via @objectstack/spec)
  • content/docs/data-modeling/external-datasources.mdx (via @objectstack/spec)
  • content/docs/data-modeling/field-types.mdx (via @objectstack/spec)
  • content/docs/data-modeling/fields.mdx (via @objectstack/spec)
  • content/docs/data-modeling/formulas.mdx (via @objectstack/spec)
  • content/docs/data-modeling/index.mdx (via @objectstack/spec)
  • content/docs/data-modeling/objects.mdx (via @objectstack/spec)
  • content/docs/data-modeling/queries.mdx (via @objectstack/spec)
  • content/docs/data-modeling/schema-design.mdx (via @objectstack/spec)
  • content/docs/data-modeling/seed-data.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation-rules.mdx (via @objectstack/spec)
  • content/docs/data-modeling/validation.mdx (via @objectstack/spec)
  • content/docs/deployment/cli.mdx (via @objectstack/spec)
  • content/docs/deployment/tenancy-modes.mdx (via @objectstack/spec)
  • content/docs/deployment/troubleshooting.mdx (via @objectstack/spec)
  • content/docs/deployment/validating-metadata.mdx (via @objectstack/spec)
  • content/docs/getting-started/build-with-claude-code.mdx (via @objectstack/spec)
  • content/docs/getting-started/common-patterns.mdx (via @objectstack/spec)
  • content/docs/getting-started/examples.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-reference.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-start.mdx (via @objectstack/spec)
  • content/docs/getting-started/your-first-project.mdx (via @objectstack/spec)
  • content/docs/kernel/cluster.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/auth-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/cache-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/data-engine.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/index.mdx (via @objectstack/spec)
  • content/docs/kernel/contracts/metadata-service.mdx (via packages/spec)
  • content/docs/kernel/contracts/storage-service.mdx (via @objectstack/spec)
  • content/docs/kernel/index.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/data-service.mdx (via @objectstack/spec)
  • content/docs/kernel/runtime-services/email-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/examples.mdx (via @objectstack/spec)
  • content/docs/kernel/runtime-services/index.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/queue-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/sharing-service.mdx (via @objectstack/spec)
  • content/docs/kernel/runtime-services/sms-service.mdx (via packages/spec)
  • content/docs/kernel/runtime-services/storage-service.mdx (via @objectstack/spec)
  • content/docs/kernel/services-checklist.mdx (via @objectstack/spec)
  • content/docs/kernel/services.mdx (via @objectstack/spec)
  • content/docs/permissions/authorization.mdx (via @objectstack/spec)
  • content/docs/permissions/permission-sets.mdx (via @objectstack/spec)
  • content/docs/permissions/permissions-matrix.mdx (via @objectstack/spec)
  • content/docs/permissions/positions.mdx (via @objectstack/spec)
  • content/docs/permissions/rls.mdx (via @objectstack/spec)
  • content/docs/permissions/sharing-rules.mdx (via @objectstack/spec)
  • content/docs/permissions/system-context.mdx (via packages/spec)
  • content/docs/plugins/adding-a-metadata-type.mdx (via @objectstack/spec)
  • content/docs/plugins/development.mdx (via @objectstack/spec)
  • content/docs/plugins/index.mdx (via @objectstack/spec)
  • content/docs/plugins/packages.mdx (via @objectstack/spec)
  • content/docs/protocol/backward-compatibility.mdx (via @objectstack/spec)
  • content/docs/protocol/diagram.mdx (via packages/spec)
  • content/docs/protocol/kernel/config-resolution.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/http-protocol.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/i18n-standard.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/index.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/lifecycle.mdx (via @objectstack/spec)
  • content/docs/protocol/kernel/plugin-spec.mdx (via @objectstack/spec)
  • content/docs/protocol/knowledge.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/index.mdx (via @objectstack/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 @objectstack/spec)
  • content/docs/protocol/objectui/record-alert.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/widget-contract.mdx (via @objectstack/spec)
  • content/docs/ui/actions.mdx (via @objectstack/spec)
  • content/docs/ui/apps.mdx (via @objectstack/spec)
  • content/docs/ui/create-vs-edit-form.mdx (via @objectstack/spec)
  • content/docs/ui/dashboards.mdx (via @objectstack/spec)
  • content/docs/ui/field-grouping-and-order.mdx (via @objectstack/spec)
  • content/docs/ui/forms.mdx (via @objectstack/spec)
  • content/docs/ui/index.mdx (via @objectstack/spec)
  • content/docs/ui/public-data-collection.mdx (via @objectstack/spec)
  • content/docs/ui/setup-app.mdx (via @objectstack/spec)
  • content/docs/ui/translations.mdx (via @objectstack/spec)
  • content/docs/ui/views.mdx (via @objectstack/spec)

7 release-owned page(s) also reference the affected code. These are read-only:

  • content/docs/releases/implementation-status.mdx (via @objectstack/spec)
  • content/docs/releases/index.mdx (via @objectstack/spec)
  • content/docs/releases/v12.mdx (via @objectstack/spec)
  • content/docs/releases/v13.mdx (via @objectstack/spec)
  • content/docs/releases/v16.mdx (via @objectstack/spec)
  • content/docs/releases/v17.mdx (via @objectstack/spec)
  • content/docs/releases/v9.mdx (via @objectstack/spec)

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

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.

@github-actions

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 31375339749 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

失败的 job(日志抽取,best effort):

  • Test Core (2/3) — 失败步骤: Install dependencies(日志不可读,点进 job 看)
  • Dogfood Verify CLI — 失败步骤: Install dependencies(日志不可读,点进 job 看)
  • Test Core (3/3) — 失败步骤: Install dependencies(日志不可读,点进 job 看)
  • Build Core — 失败步骤: Install dependencies(日志不可读,点进 job 看)

历史信号:

  • 本 PR 过去 24h 无队列失败记录(首次)。
  • 过去 24h 队列共有 7 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 在其他 PR 的同类评论里搜同名测试;出现过 ⇒ flaky 实锤,开 issue 修/隔离那条测试。修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

Merged via the queue into main with commit 182037b Aug 10, 2026
33 checks passed
@os-help
os-help deleted the claude/issue-7254-hook-input-contract-table branch August 10, 2026 10:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

protocol:data skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

2 participants