Repository navigation
docs(spec): a hook's published body description says a hook body cannot send mail - #33110
Conversation
…ot send mail The body key of HookSchema states the rule its parse already holds: a js hook body granted email.send is refused, because a message cannot be taken back if the write that runs the hook rolls back; mail on a record change goes through an action, a job or a flow. A hook body may add a timeline entry, which commits or rolls back with the write. The generated hook reference page follows. The comment over the build's capability patterns no longer calls every over-inferred token harmless: on a hook, an inferred email.send fails the build, which is the rule working. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L9qibwzNRpkoiCFn1kQpUh
|
security#2 · os-tesla · session_01L9qibwMmGJKaQ52CgHqNu2 Contract-review-claim: session_01L9qibwMmGJKaQ52CgHqNu2 · head 2554ae3 · 2026-11-13T13:59Z |
📓 Docs Drift CheckThis PR changes 2 package(s): ⛔ 2 release-owned page(s) name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 181 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin c86f540ebfeb6feb3d23efe90007239758b92e53 && git checkout c86f540ebfeb6feb3d23efe90007239758b92e53
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 2c98485db5d2981d4e90f6d8210ec88d9457aae5 2554ae3437f52017c6c6363a8b6138f1bee3599a && git checkout -B drift-repro 2c98485db5d2981d4e90f6d8210ec88d9457aae5 && git merge --no-ff 2554ae3437f52017c6c6363a8b6138f1bee3599a
node scripts/docs-audit/affected-docs.mjs --json 2c98485db5d2981d4e90f6d8210ec88d9457aae5
|
|
security#2 · os-tesla · session_01L9qibwMmGJKaQ52CgHqNu2 Contract reviewServed-tier: Inputs read: rulings 6843314411 and 6796076408 on #32004, the step-1 contract record 6844228594 on PR #32966 (its nits N9 and N10), PR #33110 (body, the 4-file list, the net diff against Findings are graded M (must-fix), S (should-fix), N (nit). ① Derived judgments
② Semver level
③ Boundary flags
④ Design judgment
The description is the right place for it: a reader of the hook's own page now meets the rule before the refusal, which is what N9 asked.
N1 (nit), N2 (nit), Tried and held: the description against each of the four checks on No must-fix and no should-fix stands on this head. N1 and N2 are nits, and nits alone end the round. Implemented-by: VERDICT: PASS Generated by Claude Code |
|
security#2 · os-tesla · session_01L9qibwMmGJKaQ52CgHqNu2 ACCEPT · 2026-11-13T14:30Z · PR #33110 · head
|
Requested by Tesla · project thread
Refs #32004
Clause-②: no
Metadata-protocol: none
维护者速读
body键在发布的 schema 描述里写明已生效的规则:hook 体不能发邮件(授予email.send的 js hook 体在解析时被拒),记录变更时的邮件走 action、job 或 flow;hook 体可以追加时间线条目(activity.append),它随写入一起提交或回滚。生成的 hook 参考页随之更新。CLI 能力推断的注释改为说明email.send是例外:hook 处理器里只要写出ctx.email.send(字符串里或不会执行的分支里),就会推断出该能力,构建因此失败,这正是规则在起作用。email.send不成立。packages/spec/src/data/hook.zod.ts中body的描述和其上方的 TSDoc,packages/cli/src/utils/extract-hook-body.ts能力模式列表顶部和email.send模式上方的注释。pnpm --filter @objectstack/spec exec vitest run src/data/hook.test.ts src/data/hook-body-mail-grant.test.ts,再跑pnpm --filter @objectstack/spec run check:generated,都应为 0。What changes
packages/spec/src/data/hook.zod.ts: thebodykey's published description now states the rule its parse already holds. Ajshook body grantedemail.sendis refused, because a message cannot be taken back if the write that runs the hook rolls back; mail on a record change goes through an action, a job or a flow. A hook body may add a timeline entry (activity.append), which commits or rolls back with the write. The TSDoc above the key says the same and names the parse check.packages/cli/src/utils/extract-hook-body.ts: the comment over the capability patterns no longer calls every over-inferred grant harmless.email.sendis the exception on a hook: a handler whose source spellsctx.email.sendin a string or in a branch that never runs is inferred the grant, and the hook's parse refuses it, so the build fails. The comment says that failure is the rule working, and that the remedy is to take the spelling out of the hook handler. An action or a job body is granted the token as before.content/docs/references/data/hook.mdx: regenerated from the schema (check:generated --fix); only thebodyrow changes..changeset/hook-body-mail-rule-description.md:@objectstack/specpatch.Why
The rule is enforced at parse, but the published schema face said nothing about it, so an author learned it from the refusal. The inference comment stated the opposite of what happens for
email.sendon a hook.Verification
scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --ran): 108 derived, 108 run, 0 NOT-MEASURED, every one exit 0, at 8042c8c. After mergingmain(2554ae3) the derivation is still 108, all accounted;check:api-surface(after a spec rebuild),check:authorable-surface,check:changeset-gate-self-testsandcheck:generatedwere run again and exit 0.hook.test.ts,hook-body-mail-grant.test.ts,hook-body-doors.test.ts,hook-body.test.ts,hook-refusals.test.ts188/188; cliextract-hook-body.test.ts,hook-body-refusal-kind.test.ts,published-subpath-hook-body.pin.test.ts,hook-body-parameter-rebind.test.ts,hook-body-lowering.test.ts142/142; spectsc --noEmitexit 0; ESLint over both source files exit 0.ctx.email.sendonly in a comment is inferred no grant (the lowered source carries no comment); one that names it in a string, or in a branch that never runs, is inferredemail.send.Acceptance notes
content/docs/references/data/hook.mdxis outside the claimed file surface: it is the generated reference page for the edited description, andcheck:generatedrequires it.Human Minimum
packages/specholds no runtime logic.main, one pull request.packages/spectouched: its generated artifacts are regenerated and committed.content/docs/references/,packages/*/CHANGELOG.mdorcontent/docs/releases/.🤖 Generated with Claude Code
https://claude.ai/code/session_01L9qibwzNRpkoiCFn1kQpUh