diff --git a/.changeset/24289-hook-ctx-title-doors.md b/.changeset/24289-hook-ctx-title-doors.md new file mode 100644 index 00000000000..561aee21325 --- /dev/null +++ b/.changeset/24289-hook-ctx-title-doors.md @@ -0,0 +1,9 @@ +--- +'@objectstack/skills': patch +--- + +docs(skills): `objectstack-data` says which hook doors provide `ctx.title` — a `body` has it, a deprecated `handler` gets it at runtime without `HookContext` declaring it, and `ql.registerHook` and a bare `ObjectQL` `bindHooks` do not provide it (#24289) + +Clause-②: no + +`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. It is not: a sandboxed `body` has it (the recommended form); an inline or named `handler`, which is deprecated, is handed it at runtime when the runtime binds the stack's hooks, but `HookContext` does not declare it, so a handler annotated with that type fails to compile; a hook registered with `ql.registerHook(...)`, or bound through `ObjectQL.bindHooks` with no runtime, gets no `ctx.title` at all. The row now says so. Text only; nothing an author writes changes, and no hook context changes. diff --git a/skills/objectstack-data/references/data-hooks.md b/skills/objectstack-data/references/data-hooks.md index c0625c1b1f5..54a04ce8d58 100644 --- a/skills/objectstack-data/references/data-hooks.md +++ b/skills/objectstack-data/references/data-hooks.md @@ -366,7 +366,7 @@ The sandbox is handed a **JSON snapshot** of these (built by | `ctx.api` | object | Cross-object CRUD. Gated by `api.read` / `api.write` — see below. | | `ctx.log` | `{ debug, info, warn, error }` | Gated by `log`. Call **`ctx.log.info(msg, data?)`** — `ctx.log` is an **object, not** callable as `ctx.log(msg)`. Emission is **best-effort** (see Troubleshooting). | | `ctx.crypto` | `{ randomUUID }` | Gated by `crypto.uuid`. | -| `ctx.title` | `(field?) => Promise` | **Name the record instead of printing its id.** `await ctx.title()` resolves this object's `nameField` — including when it is a **formula**, evaluated server-side, with no extra read. `await ctx.title('account_id')` resolves the related record's title through a lookup column (one `findOne`, gated by `api.read`; the no-argument form needs no capability). `null` when there is no title — it never falls back to the id. | +| `ctx.title` | `(field?) => Promise` | **Name the record, not its id.** `ctx.title()` resolves this object's `nameField`, a **formula** included (no extra read); `ctx.title('account_id')` resolves a lookup's title (needs `api.read`). `null` when there is no title, never the id. **Per door:** `body` has it; a deprecated `handler` gets it at runtime, undeclared on `HookContext` (TS2339 when annotated) — use `body`; `ql.registerHook` and a bare `ObjectQL` `bindHooks` lack it. | (Action bodies additionally receive `ctx.recordId` and `ctx.record`, and their wrap is `(async (input, ctx) => { … })(input, ctx)` — the action params arrive as