Skip to content

Commit a7fb23c

Browse files
docs(skills): objectstack-data factual sweep (3/3) — hooks reference, datasources, and the remaining rule files (#13698)
Four measured-false behavioral claims corrected against the implementation: - data-hooks.md: ctx.log is documented as { info, warn, error } in two places. It has FOUR levels — quickjs-runner.ts wires ['debug','info','warn','error'] and its own comment reads '[#7661] FOUR levels, not three'. - data-hooks.md: the sandbox ctx.api.object(n) method table omits three real methods — aggregate (api.read), updateMany and deleteMany (api.write). - datasources.md: 'field.columnName on managed objects is unaffected' — the key was removed in the 16.x line and is a parse error on ANY object. - datasources.md: 'external settings are ... forbidden otherwise' — a managed datasource carrying an external block parses fine. The required half holds. The other six files in this PR's scope (validation.md, hooks.md, lifecycle.md, naming.md, evals/README.md, references/_index.md) were inventoried and verified with no falsehood found, so they carry no edit. Token ratchet: both edited files shrink (-27 / -8). Fixes #13675 Claude-Session: https://claude.ai/code/session_01EXxTW8mvPBhoHxmyPZ63de Co-authored-by: Claude <noreply@anthropic.com>
1 parent 476ec0a commit a7fb23c

2 files changed

Lines changed: 13 additions & 15 deletions

File tree

skills/objectstack-data/references/data-hooks.md

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -369,7 +369,7 @@ The sandbox is handed a **JSON snapshot** of these (built by
369369
| `ctx.event` | string | e.g. `'afterUpdate'` — dispatch on it when one hook subscribes to several events. |
370370
| `ctx.object` | string | The target object name. |
371371
| `ctx.api` | object | Cross-object CRUD. Gated by `api.read` / `api.write` — see below. |
372-
| `ctx.log` | `{ 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). |
372+
| `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). |
373373
| `ctx.crypto` | `{ randomUUID }` | Gated by `crypto.uuid`. |
374374
| `ctx.title` | `(field?) => Promise<string \| null>` | **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. |
375375

@@ -395,6 +395,7 @@ org / user / transaction. Methods:
395395
| `update(data, opts?)` | `api.write` | **`update({ id, ...fields })`** — put the id **inside** `data` |
396396
| `upsert(data, opts?)` | `api.write` | `upsert({ … })` |
397397
| `delete(opts)` | `api.write` | `delete({ where: { id } })` |
398+
| `aggregate` · `updateMany` · `deleteMany` | read · write · write | also installed; same `where` shape |
398399

399400
**Query shape — the key is `where`.** It takes an object with `$`-operators, the
400401
same DSL as the [objectstack-query](../../objectstack-query/SKILL.md) skill:
@@ -443,11 +444,11 @@ set of legal tokens (`HookBodyCapability`) is exactly five:
443444

444445
| Token | Unlocks |
445446
|:--|:--|
446-
| `api.read` | `ctx.api.object(n).find` / `findOne` / `count`; also `ctx.title('<lookup field>')`, which reads that related record. Plain `ctx.title()` reads nothing and needs no token. |
447-
| `api.write` | `ctx.api.object(n).insert` / `update` / `delete` / `upsert` |
447+
| `api.read` | `ctx.api.object(n).find` / `findOne` / `count` / `aggregate`; also `ctx.title('<lookup field>')`, which reads that related record. Plain `ctx.title()` reads nothing and needs no token. |
448+
| `api.write` | `ctx.api.object(n).insert` / `update` / `delete` / `upsert` / `updateMany` / `deleteMany` |
448449
| `api.transaction` | `ctx.api.transaction(async () => { … })` — runs the callback's `ctx.api` ops in **one driver transaction** (commit on return, rollback on throw). Pair it with `api.write`. |
449450
| `crypto.uuid` | `ctx.crypto.randomUUID()` |
450-
| `log` | `ctx.log.info` / `warn` / `error(msg, data?)` |
451+
| `log` | `ctx.log.debug` / `info` / `warn` / `error(msg, data?)` |
451452

452453
There is **no `http.fetch` capability** by design — outbound calls go through
453454
Connector recipes so they stay auditable and replayable.
@@ -553,8 +554,7 @@ Register it like any hook — add it to `defineStack({ hooks: [fillPositionOnHir
553554
capability it emits only when the runtime wired a logger into the hook context —
554555
otherwise it is a **silent no-op**. Treat `ctx.log` as best-effort diagnostics,
555556
not a reliable side-channel or proof a hook ran; to observe an effect, assert on
556-
the data it writes. (And call `ctx.log.info(msg)``ctx.log` is an object, not
557-
a function, so `ctx.log(msg)` is not callable.)
557+
the data it writes.
558558

559559
---
560560

@@ -589,10 +589,8 @@ interface HookContext {
589589
organizationId?: string; // Active org — the single blessed name. Matches the
590590
// `organization_id` column + `current_user.organizationId` (RLS).
591591
// The former `tenantId` alias was removed in v16.
592-
// There is no `roles` here: `session.roles` was declared but
593-
// never produced, and was retired in 17.0.0. Privilege
594-
// is judged by the security service (permissions / positions /
595-
// posture), never by a role-name string in a hook.
592+
// No `roles` here (retired in 17.0.0) — privilege is judged
593+
// by the security service, never by a role name in a hook.
596594
accessToken?: string;
597595
isSystem?: boolean; // Elevated system context (engine self-writes).
598596
};

skills/objectstack-data/rules/datasources.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ Full field reference: `node_modules/@objectstack/spec/src/data/datasource.zod.ts
1515
| `external` | A mature external DB ObjectStack does **not** own; DDL forbidden; boot mismatch **fails**. |
1616
| `validate-only` | Like `external`, but a mismatch **warns** instead of failing boot. |
1717

18-
`external` settings are required iff `schemaMode !== 'managed'` (and forbidden otherwise).
18+
`external` settings are **required** when `schemaMode !== 'managed'`.
1919

2020
## Federated (external) objects
2121

@@ -38,10 +38,10 @@ ObjectSchema.create({
3838
### ✅ / ❌ Column mapping (ADR-0062 D7)
3939

4040
- ✅ Map remote columns with **`external.columnMap`** (`remoteColumn → localField`).
41-
-**Never set `field.columnName` on an external object.** The driver's query
42-
pipeline ignores it for federated objects, so it is a silent dual-source trap.
43-
`os build` / `os validate` **rejects** it with a clear error. (`field.columnName`
44-
on **managed** objects is unaffected.)
41+
-**`field.columnName` does not exist — on ANY object.** It was removed in the
42+
16.x line (the SQL driver hardcodes the physical column to the field key, so a
43+
custom name was ignored), and authoring it is a parse error everywhere, not
44+
only on a federated object.
4545

4646
## Auto-connect (no `onEnable`)
4747

0 commit comments

Comments
 (0)