docs(kernel): runtime-services 的 hook 示例改教真实通道 —— 去掉不存在的 ctx.services (#5720) - #5938
Merged
Merged
Conversation
…s` (#5720) hook 上下文由引擎逐键构造(object/event/input/session/provenance/user/api/ transaction/ql),从来没有 `services` 键;`services.*` 是本页记录的**契约签名面** (见 runtime-services 索引页的 binding note),托管运行时才注入。照抄旧示例的 `beforeUpdate` 因可选链短路成 `undefined`,`if (!ok) throw` 会无条件拒掉每一次 写入,失败方向还伪装成"正常拒绝"。 - examples.mdx 第 1 节:实测 flow `script` 函数按契约是纯函数 (`handlerContract: 'pure'`),运行时只交 input/variables/automation/logger, **没有任何数据句柄**——既无 `services` 也无 `ctx.api`。故改写为真实通道: 声明式 `get_record` 读行、`script` 节点把变量映射进函数 `inputs`、函数返回值 由后续声明式节点落库。整块过 `defineFlow` + `defineStack` 真实解析。 - examples.mdx 第 2 节:改为 hook 的真实数据通道 `ctx.api`,示例语义(写前校验 + 拒绝路径)保留,但换成引擎无法代劳的**业务**规则;共享强制由 plugin-sharing 的 引擎中间件按动词自动执行(update → canEdit,delete → canDelete,拒绝抛 FORBIDDEN),hook 手查是冗余教学,故删除。 - 两块示例入参从 `ctx: any` 改 `(ctx: HookContext)`:`any` 让 `{/* os:check */}` 变成空门(块内每次属性访问都不被检查)。反向验证:把旧函数体按 HookContext 如实标注后,两处 `ctx.services` 均报 TS2339 —— 键确实不存在,而门此前是绿的。 - sharing-service.mdx:裸 `services.sharing` 的 Example 实测教的是 hook 语境 (注释自称 "The hook exposes …"、读 ctx.input/ctx.session),不是 action 面。 改为"由持有该服务的代码调用"(契约类型 ISharingService),并新增一节写明强制 自动执行、hook 不得复查;该块补 os:check 标记,首次真正把本页签名钉在契约上。 门禁:check:skill-examples 206 → 207 块全绿(runtime-services 三块从 "标记了但零覆盖"变为真检查);check:doc-authoring 362 文件干净; check:nul-bytes、check:docs-audit-scope 均绿。 Closes #5720 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GX3sL71LFq8m2usg6VqTSE
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
This was referenced Aug 6, 2026
Contributor
Author
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #5720
按维护者 2026-08-06 批复裁「改教
ctx.api」执行:docs-only,⛔ 不新增「hook ctx 注入 services」公共契约 ——packages/spec/**、packages/objectql/**零改动。为什么是缺陷
hook 上下文由引擎逐键构造(
object/event/input/session/provenance/user/api/transaction/ql,engine.ts五处构造点同构),沙箱buildHookSandboxContext同样不产 —— 从来没有services键。照抄旧示例写beforeUpdate授权检查,ctx.services?.sharing?.canEdit(…)因可选链短路成undefined,if (!ok) throw new Error('PERMISSION_DENIED')于是无条件抛出:该对象上所有更新一律 403,而失败方向伪装成"正常拒绝",极难归因到文档。services.*本身不是幻觉,是这几页记录的契约签名面(索引页 binding note 已写明:开放框架不往 hook ctx 注入services,托管运行时才提供该绑定)。缺陷在于把它当成 hook 能拿到的东西来教。改了什么
content/docs/kernel/runtime-services/examples.mdxctx.api;flowscript函数 → 无;plugin → 插件上下文),并点名ctx.services的短路后果。ctx.services.data)改写为真实通道:声明式get_record节点读行 →script节点把变量映射进注册函数的inputs→ 函数返回值由后续声明式节点落库。ctx.services?.sharing?.canEdit)改为 hook 的真实数据通道ctx.api.object(…);示例语义(写前校验 + 拒绝路径)保留,但换成引擎无法代劳的业务规则(合同金额 vs 客户信用额度)。共享强制由plugin-sharing的引擎中间件按动词自动执行(update 走canEdit、delete 走canDelete,拒绝抛FORBIDDEN),hook 手查是冗余教学,故删。ctx: any→(ctx: HookContext)([spec] HookContext.session 少声明了positions/preserveAudit—— 引擎在生产、消费方在读、文档在教,契约里没有(#5050 的镜像方向) #5605 落地后session.positions已可正常标类型)。content/docs/kernel/runtime-services/sharing-service.mdxservices键 + hook 自己的通道是ctx.api。ISharingService,来自@objectstack/spec/contracts),并首次给该块补os:check标记 —— 本页自称 canonical source 是那个契约文件,现在真的被钉住了。与 issue / 裁定子断言不符的两处(按实测办)
ctx.api」。实测它是 flowscript节点调用的注册函数:按契约是纯函数(handlerContract: 'pure',Ascriptnode's function is contractually pure, but nothing enforces it — a function that writes makes its run under-report #4396),运行时只交input/variables/automation/logger,没有任何数据句柄 —— 既没有services,也没有ctx.api(skills/objectstack-automation/SKILL.md原话:hooks getctx.api; flow functions don't)。所以第 1 节不能改成ctx.api,只能改成"读在声明式节点、算在纯函数"。旧示例是双重失真:键不存在,且它教的行为本身违反纯函数契约。organizationId",读的是ctx.input.id/ctx.session(action ctx 是params/record/recordId)。故按 hook 处置,不是"仅标注语境"。验证
反向验证(方向先定后跑:预期红) —— 把两处旧函数体的
any换成如实的HookContext后编译,两处都报同一条:即:键确实不存在,而
check:skill-examples此前是绿的 ——ctx: any让块内每一次属性访问都不被检查,os:check标记成了空门(issue 正文第 2 点)。新示例不止过类型,还真解析/真跑:
defineFlow实际 parse 通过(flow parsed ok: sales_order_roll_up_totals 5 nodes);defineHook实际 parse 通过,并用手搭 ctx 跑了两条路径:超限 →rejected as expected: VALIDATION_FAILED: contract amount exceeds the account credit limit;未超限 →within limit: passed (expected)。旧示例是"恒抛",新示例只在业务规则被违反时抛。门禁(全绿)
check:skill-examples✅ 207 prose examples type-check—— 206 → 207(sharing-service 新增 1 块);runtime-services 三块从"标记了但零覆盖"变为真检查check:doc-authoring✓ 362 files cleancheck:nul-bytesOK (scanned 5723 tracked text file(s))check:docs-audit-scope✓ scope in sync (178 hand-written docs),releases 只读未动docs-only,不发布任何包 ⇒ 无 changeset,已加
skip-changeset标签。分开交付(不在本 PR)
check:skill-examples加「os:check 块内禁止把入参标any」约束 —— 落packages/spec/scripts/,属 spec 座位面,已拆子单。packages/spec/src/data/hook.zod.ts:416的 JSDoc 同病 —— 已在HookEvent的枚举注释仍在说批量写把行级谓词「carried ininput」—— 与 #5273 同一句假话,在同文件 40 行之外 #5899 留证据评论归拢(该文件属 spec 座位面,不自改)。🤖 Generated with Claude Code
https://claude.ai/code/session_01GX3sL71LFq8m2usg6VqTSE
Generated by Claude Code