docs(spec): document the declarative flat-record surface on the HookContext.input contract table (#7254) - #7393
Conversation
…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
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
📓 Docs Drift CheckThis PR changes 1 package(s): 106 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
⛔ 7 release-owned page(s) also reference the affected code. These are read-only:
|
⛔ merge queue 构建失败 — 先分诊,再决定要不要重排队列构建 31375339749 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集), 失败的 job(日志抽取,best effort):
历史信号:
分诊清单:
Generated by Claude Code · merge-queue-triage workflow (#4859) |
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 theengine.registerHookview 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
input.fieldinput.data.fieldengine.registerHookhandlerhandlerdatathrough)bodydatakey at allSo 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):
installFlatInputathook-wrappers.ts:446/ helper:502;buildSandboxContextatbody-runner.ts:314;unwrapProxyToPlainat:387.Premise re-checked before implementing:
grep -i "declarative|bindHooks|installFlatInput|flat|sandbox|proxy"over the file returns nothing in theinputblock — #7101's revision did not add these rows. Premise valid.Measured with a throwaway probe (real
ObjectQLkernel +bindHooksToEngine, deleted before commit; the probe appliedObject.fromEntries(Object.entries(input)), i.e. exactly whatunwrapProxyToPlaindoes to build the body snapshot):That adds three facts beyond the card's table, all now documented: a body sees no
id/options/asteither; onfindanddeletethe whole snapshot is{}; and a body that needs the row readsctx.previous(pre-image,idincluded). Theinput.dataTypeError itself is already pinned on main by #7258 atexamples/app-showcase/test/hook-body-persisted-writes.test.ts:174—KEYS[email,message,name] hasData=undefined— which this PR cites rather than re-pinning.Render-input check: TSDoc, not
.describe()— no reference-page regenThe table lives in the TSDoc block above the property; the
.describe()is only'Mutable input parameters'. Confirmed empirically rather than assumed:content/docs/references/data/hook.mdxandpackages/spec/json-schema/**carry only the describe string, so no consumer surface is reached ⇒ no changeset,skip-changesetapplied.Composition with the same-file churn
#7101(per-row vs record dispatch) and#7235(the@exampleline) both landed on this file. The new rows are additive and orthogonal; threerows aboveback-references in the following paragraphs were narrowed toenvelope rows aboveso #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 thaninput.data.fielddid.Gates
contracts/scoped-context.test.ts— which extracts theUsage in hooksexample 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