Repository navigation
Commit f4755a6
Fixes #24289
Clause-②: no
**Tier H — the maintainer merges this PR.** The diff touches
`skills/**`, a governed surface (AGENTS.md Prime Directive #14). No AI
seat readies, queues, arms auto-merge on, or approves it.
## What this does
This carries out ruling letter E on #24289 (comment 6261581954), item 2:
the hook context type stays as it is, and the docs say which hook doors
provide `ctx.title`.
`skills/objectstack-data/references/data-hooks.md` listed
`ctx.title(field?)` in the sandbox `ctx` table with no qualifier, so it
read as a member of every hook context. One row changes, the `ctx.title`
row. It keeps what it taught (the no-argument form, a formula
`nameField`, the lookup form under `api.read`, `null` and never the id)
and adds a per-door note:
- `body`: has it (the recommended form).
- a deprecated `handler`: gets it at runtime, but `HookContext` does not
declare it, so an annotated handler fails with TS2339. Use `body`.
- `ql.registerHook` and a bare `ObjectQL` `bindHooks`: do not provide
it.
Under ruling item 1 nothing else changes: `HookContextSchema` gets no
`title`, there is no new context type, and there is no engine work.
## Each door, checked against `main` (`ce2e08ee3`, and again after
merging `959ee317b`)
| Door | Reading | Where |
|:--|:--|:--|
| `body` (sandbox) | `buildSandboxContext(...,
buildTitleSurface(engineCtx, opts.ql, ...))` installs the accessor on
every hook body | `packages/runtime/src/sandbox/body-runner.ts`
(`boundBodyHandler`) |
| `handler`, inline or named, when the runtime binds the stack's hooks |
`bindAppArtifactHandlers` passes `wrapHandler: (handler, hook) =>
withInProcessHookTitle(handler, ql, ...)` to `ql.bindHooks`. The binder
applies it after a string `handler` resolves, so inline and named
handlers take the same wrap (PRs #24356 and #24423) |
`packages/runtime/src/app-artifact-handlers.ts`;
`packages/objectql/src/hook-binder.ts` (`wrapHandler`) |
| `HookContext` type | `HookContextSchema` declares no `title` (zero
hits for `title` in the file) | `packages/spec/src/data/hook.zod.ts` |
| `ql.registerHook` | the engine never adds `title` to a hook context.
The only producers of the accessor are `buildTitleSurface` and
`withInProcessHookTitle` in `packages/runtime`, and `registerHook` goes
through neither | `packages/objectql/src/engine.ts` |
| bare `ObjectQL.bindHooks` | without `wrapHandler`, `resolveHandler`
returns the resolved handler as it is (`if (!fn \|\| typeof
opts.wrapHandler !== 'function') return fn;`), so nothing installs
`title` | `packages/objectql/src/hook-binder.ts` |
This is a code reading. No new runtime probe was run. The PR #24423 dev
measured the last two doors with a bare-`ObjectQL` probe
(`registerHook`, `bindInline` and `bindNamed` each read `typeof
ctx.title === 'undefined'`). That reading is in the card's report
comment 6257914461.
## Token and line budget (`skills/**`)
- `skills/objectstack-data/references/data-hooks.md`: **998 → 998
lines**. Tokens (`check-skills-token-ratchet.mjs` `pricedTokens`)
**10059 → 10064**, ceiling 10066 (headroom 2). The ceiling is unchanged.
- Whole package, the sum of all `skills/**/SKILL.md`: **4394 → 4394
lines**. No `SKILL.md` is touched.
- The new text is paid for by deleting text from the same row, not by
re-wrapping it. Removed: the row's copy of the capability table's rule
that the no-argument form needs no capability (the `api.read` row in
"Capabilities — the complete list" already says so), and wordier
phrasing ("including when it is", "evaluated server-side", "one
`findOne`", two `await`s, "it never falls back to").
## Changeset
`.changeset/24289-hook-ctx-title-doors.md`: `'@objectstack/skills':
patch`. That package publishes `skills/**` as its `dist/skills`
(`packages/skills/scripts/sync-catalog.mjs`), so a text change here
ships. The earlier skills docs PRs #24174 and #24262 shipped the same
patch. No `skip-changeset`.
## Gates
`node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` (no paths) derived 42 commands at
`953159abe`, and the same 42 after the merge. I also ran `node
scripts/check-changeset-fixed.mjs`, a roster family whose roster sits
under `.changeset`.
- `--ran`: **42 derived, 42 run, 0 NOT-MEASURED, 0 UNRUN**. Every exit
code was recorded before any pipe.
- First pass at `dcd4a8368` / `eee9ae8de`: `check-skills-token-ratchet`
exited 1 (`data-hooks.md` 10127 / 10066, over by 61). The row was
trimmed in `953159abe`. Five gates exited 3 on prerequisites (no
`dist`). After a full `turbo run build --filter='!@objectstack/docs'`,
all 43 exited 0.
- At the final head `260ca2ea6`, the merge of `origin/main` `959ee317b`,
I rebuilt (`turbo run build --filter='!@objectstack/docs'`, exit 0) and
re-ran all 43: each exited 0, and `--ran` gave 42 / 42 / 0 / 0.
`check-skills-token-ratchet` read `data-hooks.md` at 10064 / 10066.
- Not run locally: `pnpm lint`, `test:repo` and CI's type-check lanes.
CI owns those.
## Acceptance notes
- **Wording difference from the ruling's letter:** ruling item 2 says
`handler` is "available at runtime since PRs #24356 and #24423". The
skill text says "gets it at runtime" and does not cite the PRs.
Published skill text is customer-facing and carries no tracker numbers
(AGENTS.md: the lesson goes into the text). The PRs are cited here
instead.
- **Edit scope:** the row is the only skill text changed. The "Hook
Context API" blockquote above `interface HookContext` still calls a
`body`'s `ctx` a "capability-gated subset" of the in-process context.
`ctx.title` is one member a `body` has that `HookContext` does not
declare. This PR leaves that blockquote alone, because ruling E scopes
the edit to the context table row.
- **Out of this PR:** ruling item 3, the pointer on
objectstack-ai/hotcrm#2086, is not done here. That card is outside this
dispatch's write budget.
---
## 维护者速读(草稿)
**改了什么:** 只改技能文档 `data-hooks.md` 里 `ctx.title` 那一行,写明各个入口能不能用它:`body`
能用(推荐写法);已弃用的 `handler` 运行时能用,但 `HookContext` 类型里没有声明,写了类型注解会编译报错,建议改用
`body`;`ql.registerHook` 和不经 runtime 直接用 `ObjectQL` 绑定的钩子拿不到它。另加一个
`@objectstack/skills` 的 patch changeset。
**为什么改:** 按裁决 E(#24289 评论 6261581954)执行。原文没有任何限定,读起来像每个钩子上下文都有
`ctx.title`,实际并非如此。现在文档写的就是运行时真实提供的。
**风险与代价(含回滚):** 纯文档,不改类型、不改引擎,运行时行为不变。这一行的 token 由删掉同一行里的重复内容来抵,文件 token
仍在上限内(10064 / 10066),行数不变。回滚就是 revert 这个 PR。
**席位意见:**
**你要做的:** 审阅这一行的措辞,同意就合并(Tier H,只能由你合并)。
---
_Generated by [Claude
Code](https://claude.ai/code/session_01D6F9axfoPzR7t8G1J7Ytzz)_
---------
Co-authored-by: Claude <noreply@anthropic.com>
1 parent c1f4093 commit f4755a6
2 files changed
Lines changed: 10 additions & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
366 | 366 | | |
367 | 367 | | |
368 | 368 | | |
369 | | - | |
| 369 | + | |
370 | 370 | | |
371 | 371 | | |
372 | 372 | | |
| |||
0 commit comments