Skip to content

Commit 1c8c497

Browse files
committed
docs(spec): regenerate content/docs/references/** with the never-tombstone renderer (#5606)
Generated by `pnpm --filter @objectstack/spec gen:schema && gen:docs`. Do not hand-edit — regenerate instead. 30 reference pages, 117 lines: - 101 per-key tombstone rows: type cell `any` -> `never`, [REMOVED] prescriptions unchanged. - 16 inline summary cells: tombstones dropped before INLINE_KEY_LIMIT, e.g. Theme.typography.fontFamily `{ base?: string; heading?: any; mono?: any }` -> `{ base?: string }`, ObjectSchema.indexes drops `type?: any` and its trailing ellipsis. The three sharded artifact dirs (authorable-surface/, json-schema.manifest/, api-surface/) are byte-identical after gen:schema — this change does not reach them. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014wsZeReNTqiceBfLb5Pyf5
1 parent 2e49383 commit 1c8c497

30 files changed

Lines changed: 117 additions & 117 deletions

‎content/docs/references/ai/agent.mdx‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,8 @@ const result = AIModelConfigSchema.parse(data);
5353
| **lifecycle** | `{ id: string; description?: string; contextSchema?: Record<string, any>; initial: string; … }` | optional | [EXPERIMENTAL — not enforced] State machine defining the agent conversation flow and constraints. Parsed but no runtime consumer yet (liveness #1878/#1893). |
5454
| **surface** | `Enum<'ask' \| 'build'>` | ✅ | Product surface this agent binds ('ask' \| 'build') — ADR-0063 §1 |
5555
| **skills** | `string[]` | optional | Skill names to attach (Agent→Skill→Tool architecture) |
56-
| **tools** | `any` | optional | [REMOVED] `agent.tools` was removed in @objectstack/spec 17 (#3894) — use `skills`. An agent reaches exactly the tools its surface-compatible skills declare (ADR-0064), so move each reference into a skill: a platform tool by its registered name, or `action_<name>` for one of your own AI-exposed Actions. Run `os migrate meta --from 16` to rewrite it automatically. |
57-
| **knowledge** | `any` | optional | [REMOVED] `agent.knowledge` was removed in @objectstack/spec 17.0.0 (#3896 audit close-out) — declaring knowledge sources/indexes on an agent never scoped retrieval: the `search_knowledge` tool takes `sourceIds` from the LLM's tool-call arguments, not from the agent record. Delete the block. Restrict retrieval at the knowledge-service / source level (per-source permissions), and describe intended grounding in `instructions` so the model asks for the right sources. |
56+
| **tools** | `never` | optional | [REMOVED] `agent.tools` was removed in @objectstack/spec 17 (#3894) — use `skills`. An agent reaches exactly the tools its surface-compatible skills declare (ADR-0064), so move each reference into a skill: a platform tool by its registered name, or `action_<name>` for one of your own AI-exposed Actions. Run `os migrate meta --from 16` to rewrite it automatically. |
57+
| **knowledge** | `never` | optional | [REMOVED] `agent.knowledge` was removed in @objectstack/spec 17.0.0 (#3896 audit close-out) — declaring knowledge sources/indexes on an agent never scoped retrieval: the `search_knowledge` tool takes `sourceIds` from the LLM's tool-call arguments, not from the agent record. Delete the block. Restrict retrieval at the knowledge-service / source level (per-source permissions), and describe intended grounding in `instructions` so the model asks for the right sources. |
5858
| **active** | `boolean` | ✅ | |
5959
| **access** | `string[]` | optional | Who can chat with this agent |
6060
| **permissions** | `string[]` | optional | Required permission-set capabilities |

‎content/docs/references/ai/skill.mdx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ const result = SkillSchema.parse(data);
3939
| **surface** | `Enum<'ask' \| 'build' \| 'both'>` | ✅ | Agent surface this skill binds to ('ask' \| 'build' \| 'both') — ADR-0063 §3 |
4040
| **instructions** | `string` | optional | LLM instructions when skill is active |
4141
| **tools** | `string[]` | ✅ | Tool names belonging to this skill (supports trailing wildcard, e.g. `action_*`) |
42-
| **triggerPhrases** | `any` | optional | [REMOVED] `skill.triggerPhrases` was removed in @objectstack/spec 17.0.0 (#3896 audit close-out) — phrases were never matched against the user's message; skill activation is `triggerConditions` (AND of context field/operator/value) intersected with the agent's `skills[]`, plus explicit /skill-name pinning. Delete the key. Put routing intent in `triggerConditions`; describe intent in `description`/`instructions` for the LLM. |
42+
| **triggerPhrases** | `never` | optional | [REMOVED] `skill.triggerPhrases` was removed in @objectstack/spec 17.0.0 (#3896 audit close-out) — phrases were never matched against the user's message; skill activation is `triggerConditions` (AND of context field/operator/value) intersected with the agent's `skills[]`, plus explicit /skill-name pinning. Delete the key. Put routing intent in `triggerConditions`; describe intent in `description`/`instructions` for the LLM. |
4343
| **triggerConditions** | `{ field: string; operator: Enum<'eq' \| 'neq' \| 'in' \| 'not_in' \| 'contains'>; value: string \| string[] }[]` | optional | Programmatic activation conditions |
4444
| **active** | `boolean` | ✅ | Whether the skill is enabled |
4545
| **protection** | `{ lock: Enum<'none' \| 'no-overlay' \| 'no-delete' \| 'full'>; reason: string; docsUrl?: string }` | optional | Package author protection block — lock policy for this skill. |

‎content/docs/references/api/analytics.mdx‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,8 @@ const result = AnalyticsEndpoint.parse(data);
6767
| **limit** | `number` | optional | |
6868
| **offset** | `number` | optional | |
6969
| **timezone** | `string` | optional | |
70-
| **query** | `any` | optional | [REMOVED] `query` was removed from AnalyticsQueryRequest in @objectstack/spec 17.0.0 (#3878). The `{ cube, query: {...} }` envelope was the dialect of the retired degraded analytics shim (#3891) — the real engine never understood it. Move the query.* fields to the body top level: `{ cube, measures, dimensions?, where?, timeDimensions?, order?, limit?, offset?, timezone? }`. |
71-
| **format** | `any` | optional | [REMOVED] `format` was removed from AnalyticsQueryRequest in @objectstack/spec 17.0.0 (#3878). It was never implemented — every response is the JSON envelope. Delete the key; for CSV/XLSX use the export surface instead. |
70+
| **query** | `never` | optional | [REMOVED] `query` was removed from AnalyticsQueryRequest in @objectstack/spec 17.0.0 (#3878). The `{ cube, query: {...} }` envelope was the dialect of the retired degraded analytics shim (#3891) — the real engine never understood it. Move the query.* fields to the body top level: `{ cube, measures, dimensions?, where?, timeDimensions?, order?, limit?, offset?, timezone? }`. |
71+
| **format** | `never` | optional | [REMOVED] `format` was removed from AnalyticsQueryRequest in @objectstack/spec 17.0.0 (#3878). It was never implemented — every response is the JSON envelope. Delete the key; for CSV/XLSX use the export surface instead. |
7272

7373

7474
---

‎content/docs/references/api/automation-api.mdx‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,12 +102,12 @@ const result = AutomationApiErrorCode.parse(data);
102102
| **errorMessage** | `string` | optional | Toast shown when a screen flow fails (defaults to the raw error). |
103103
| **version** | `integer` | optional | Version number |
104104
| **status** | `Enum<'draft' \| 'active' \| 'obsolete' \| 'invalid'>` | optional | Deployment status |
105-
| **template** | `any` | optional | [REMOVED] `flow.template` was removed in @objectstack/spec 17.0.0 (#3896 audit close-out) — no designer or engine path ever read it, so flagging a flow as a template/subflow did nothing. Delete the key. Shared logic is invoked via a subflow NODE referencing the flow by name. |
105+
| **template** | `never` | optional | [REMOVED] `flow.template` was removed in @objectstack/spec 17.0.0 (#3896 audit close-out) — no designer or engine path ever read it, so flagging a flow as a template/subflow did nothing. Delete the key. Shared logic is invoked via a subflow NODE referencing the flow by name. |
106106
| **type** | `Enum<'autolaunched' \| 'record_change' \| 'schedule' \| 'screen' \| 'api'>` | ✅ | Flow type |
107107
| **variables** | `{ name: string; type: string; isInput?: boolean; isOutput?: boolean }[]` | optional | Flow variables |
108108
| **nodes** | `{ id: string; type: string; label: string; config?: Record<string, any>; … }[]` | ✅ | Flow nodes |
109109
| **edges** | `{ id: string; source: string; target: string; condition?: string \| { dialect: Enum<'cel' \| 'cron' \| 'template'>; source?: string; ast?: any; meta?: object }; … }[]` | ✅ | Flow connections |
110-
| **active** | `any` | optional | [REMOVED] `flow.active` was removed in @objectstack/spec 17.0.0 (#3896 audit close-out) — it never had an effect: the engine arms flows from `status`, and `active: false` did NOT stop a flow (worse, the default read as disabled while the engine treated unset as enabled). Delete the key. Use `status: 'obsolete'` (or 'invalid') to unbind and disable a flow, `status: 'active'` to arm it. |
110+
| **active** | `never` | optional | [REMOVED] `flow.active` was removed in @objectstack/spec 17.0.0 (#3896 audit close-out) — it never had an effect: the engine arms flows from `status`, and `active: false` did NOT stop a flow (worse, the default read as disabled while the engine treated unset as enabled). Delete the key. Use `status: 'obsolete'` (or 'invalid') to unbind and disable a flow, `status: 'active'` to arm it. |
111111
| **runAs** | `Enum<'system' \| 'user'>` | optional | Execution identity for the run: system = elevated (bypasses RLS), user = the triggering user (RLS-respecting). A run with no trigger user has no identity to scope to, so under user its data operations are REFUSED — declare system to make the elevation explicit. This covers schedule/time-relative/api triggers AND any record-change flow fired by a write that carried no user. |
112112
| **errorHandling** | `{ strategy?: Enum<'fail' \| 'retry' \| 'continue'>; maxRetries?: integer; backoffMs?: integer; backoffMultiplier?: number; … }` | optional | Flow-level error handling configuration |
113113
| **protection** | `{ lock: Enum<'none' \| 'no-overlay' \| 'no-delete' \| 'full'>; reason: string; docsUrl?: string }` | optional | Package author protection block — lock policy for this flow. |

‎content/docs/references/api/batch.mdx‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ const result = BatchConfigSchema.parse(data);
4747
| :--- | :--- | :--- | :--- |
4848
| **enabled** | `boolean` | ✅ | Enable batch operations |
4949
| **maxRecordsPerBatch** | `integer` | ✅ | Maximum records per batch |
50-
| **defaultOptions** | `{ atomic: boolean; returnRecords: boolean; continueOnError: boolean; validateOnly?: any }` | optional | Default batch options |
50+
| **defaultOptions** | `{ atomic: boolean; returnRecords: boolean; continueOnError: boolean }` | optional | Default batch options |
5151

5252

5353
---
@@ -89,7 +89,7 @@ const result = BatchConfigSchema.parse(data);
8989
| **atomic** | `boolean` | ✅ | Opt-in all-or-nothing. When explicitly true the whole batch runs inside ONE engine transaction: the first failure rolls back every prior write, and the response reports zero successes — each row carries `errors[0].code` ROLLED_BACK (written, then undone), the causal row its own error, and rows never reached NOT_ATTEMPTED. A runtime that cannot roll back REFUSES the request (501 NOT_IMPLEMENTED) rather than silently degrading to best-effort — probe `capabilities.transactionalBatch` on /discovery first. Takes precedence over continueOnError. Default false: sequential best-effort. |
9090
| **returnRecords** | `boolean` | ✅ | If true, return full record data in response |
9191
| **continueOnError** | `boolean` | ✅ | If true (and atomic=false), continue processing remaining records after errors |
92-
| **validateOnly** | `any` | optional | [REMOVED] `options.validateOnly` was removed from BatchOptions in @objectstack/spec (#4052). It was never implemented: the batch surfaces persisted regardless, so a "dry-run" would have silently executed. There is no dry-run today — drop the key. If you need to preview a batch without writing, open an issue so it can be designed (no-commit cascade / constraint semantics) and reintroduced as a flag that actually holds. |
92+
| **validateOnly** | `never` | optional | [REMOVED] `options.validateOnly` was removed from BatchOptions in @objectstack/spec (#4052). It was never implemented: the batch surfaces persisted regardless, so a "dry-run" would have silently executed. There is no dry-run today — drop the key. If you need to preview a batch without writing, open an issue so it can be designed (no-commit cascade / constraint semantics) and reintroduced as a flag that actually holds. |
9393

9494

9595
---
@@ -115,7 +115,7 @@ const result = BatchConfigSchema.parse(data);
115115
| :--- | :--- | :--- | :--- |
116116
| **operation** | `Enum<'create' \| 'update' \| 'upsert' \| 'delete'>` | ✅ | Type of batch operation |
117117
| **records** | `{ id?: string; data?: Record<string, any>; externalId?: string }[]` | ✅ | Array of records to process (server caps the count — see batch.maxBatchSize) |
118-
| **options** | `{ atomic: boolean; returnRecords: boolean; continueOnError: boolean; validateOnly?: any }` | optional | Batch operation options |
118+
| **options** | `{ atomic: boolean; returnRecords: boolean; continueOnError: boolean }` | optional | Batch operation options |
119119

120120

121121
---
@@ -199,7 +199,7 @@ A cross-object batch strip event: dropped fields plus the operation index
199199
| Property | Type | Required | Description |
200200
| :--- | :--- | :--- | :--- |
201201
| **ids** | `string[]` | ✅ | Array of record IDs to delete (server caps the count — see batch.maxBatchSize) |
202-
| **options** | `{ atomic: boolean; returnRecords: boolean; continueOnError: boolean; validateOnly?: any }` | optional | Delete options |
202+
| **options** | `{ atomic: boolean; returnRecords: boolean; continueOnError: boolean }` | optional | Delete options |
203203

204204

205205
---
@@ -223,7 +223,7 @@ A cross-object batch strip event: dropped fields plus the operation index
223223
| Property | Type | Required | Description |
224224
| :--- | :--- | :--- | :--- |
225225
| **records** | `{ id: string; data: Record<string, any> }[]` | ✅ | Array of records to update (server caps the count — see batch.maxBatchSize) |
226-
| **options** | `{ atomic: boolean; returnRecords: boolean; continueOnError: boolean; validateOnly?: any }` | optional | Update options |
226+
| **options** | `{ atomic: boolean; returnRecords: boolean; continueOnError: boolean }` | optional | Update options |
227227

228228

229229
---

‎content/docs/references/api/contract.mdx‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -155,13 +155,13 @@ const result = ApiErrorSchema.parse(data);
155155
| **limit** | `number` | optional | Max records to return (LIMIT) |
156156
| **offset** | `number` | optional | Records to skip (OFFSET) |
157157
| **top** | `number` | optional | Alias for limit (OData compatibility) |
158-
| **cursor** | `any` | optional | [REMOVED] `query.cursor` was removed in @objectstack/spec 17 (#4286, ADR-0049) — no driver ever implemented keyset pagination, so the cursor was accepted and ignored and every page came back identical (a caller looping "until hasMore is false" never terminates). Delete the key; `QueryBuilder.cursor()` was removed with it. Express the keyset as an ordinary `where` predicate on your sort key — `where: { created_at: { $gt: last.created_at } }` with the matching `orderBy` — which every driver executes with canonicalised comparands. A first-class cursor, if ever built, will be a response-minted opaque token, not this caller-built record. |
159-
| **joins** | `any` | optional | [REMOVED] `query.joins` was removed in @objectstack/spec 17 (#4286, ADR-0049) — no engine or driver ever read it: a query carrying `joins` behaved exactly as if the key were absent, while its name squatted on the reserved REST parameter set. Delete the key. Related records are read through `expand` — `expand: { owner: { object: 'user', fields: ['name'] } }` — which the engine resolves via batch $in queries, and a single related column is a dotted `fields` path (`fields: ['owner.name']`). |
158+
| **cursor** | `never` | optional | [REMOVED] `query.cursor` was removed in @objectstack/spec 17 (#4286, ADR-0049) — no driver ever implemented keyset pagination, so the cursor was accepted and ignored and every page came back identical (a caller looping "until hasMore is false" never terminates). Delete the key; `QueryBuilder.cursor()` was removed with it. Express the keyset as an ordinary `where` predicate on your sort key — `where: { created_at: { $gt: last.created_at } }` with the matching `orderBy` — which every driver executes with canonicalised comparands. A first-class cursor, if ever built, will be a response-minted opaque token, not this caller-built record. |
159+
| **joins** | `never` | optional | [REMOVED] `query.joins` was removed in @objectstack/spec 17 (#4286, ADR-0049) — no engine or driver ever read it: a query carrying `joins` behaved exactly as if the key were absent, while its name squatted on the reserved REST parameter set. Delete the key. Related records are read through `expand` — `expand: { owner: { object: 'user', fields: ['name'] } }` — which the engine resolves via batch $in queries, and a single related column is a dotted `fields` path (`fields: ['owner.name']`). |
160160
| **aggregations** | `{ function: Enum<'count' \| 'sum' \| 'avg' \| 'min' \| 'max' \| 'count_distinct' \| 'array_agg' \| 'string_agg'>; field?: string; alias: string; distinct?: boolean; … }[]` | optional | Aggregation functions |
161161
| **groupBy** | `(string \| { field: string; dateGranularity?: Enum<'day' \| 'week' \| 'month' \| 'quarter' \| 'year'>; alias?: string })[]` | optional | GROUP BY targets (strings or `{field, dateGranularity?}` objects for date bucketing) |
162162
| **having** | `any` | optional | HAVING — filter over the AGGREGATED rows (aggregation aliases + groupBy projections); applied engine-side after aggregation |
163-
| **windowFunctions** | `any` | optional | [REMOVED] `query.windowFunctions` was removed in @objectstack/spec 17 (#4286, ADR-0049) — `find()` never applied it: no engine or driver read the key on the query path, so every OVER clause it declared was silently dropped. Delete the key. Window functions are a SQL-driver capability behind `SqlDriver.findWithWindowFunctions(object, query)` (embedder-level; not on the `IDataDriver` contract or the REST surface); request-level analytics are `aggregations` + `groupBy`. |
164-
| **distinct** | `any` | optional | [REMOVED] `query.distinct` was removed in @objectstack/spec 17 (#4286, ADR-0049 / ADR-0078) — no driver ever rendered SELECT DISTINCT; the flag's only observable effect was MIS-WIRED: the REST list path treated a distinct query as not countable and silently degraded `total`/`hasMore` to a page-local estimate while still returning duplicate rows. Delete the key; `QueryBuilder.distinct()` was removed with it, and the count suppression is gone (`total` is truthful again). For unique values of one column use the SQL/memory drivers' `distinct(object, field)` door; for unique combinations, `groupBy`; for a deduplicated count, the `count_distinct` aggregation. |
163+
| **windowFunctions** | `never` | optional | [REMOVED] `query.windowFunctions` was removed in @objectstack/spec 17 (#4286, ADR-0049) — `find()` never applied it: no engine or driver read the key on the query path, so every OVER clause it declared was silently dropped. Delete the key. Window functions are a SQL-driver capability behind `SqlDriver.findWithWindowFunctions(object, query)` (embedder-level; not on the `IDataDriver` contract or the REST surface); request-level analytics are `aggregations` + `groupBy`. |
164+
| **distinct** | `never` | optional | [REMOVED] `query.distinct` was removed in @objectstack/spec 17 (#4286, ADR-0049 / ADR-0078) — no driver ever rendered SELECT DISTINCT; the flag's only observable effect was MIS-WIRED: the REST list path treated a distinct query as not countable and silently degraded `total`/`hasMore` to a page-local estimate while still returning duplicate rows. Delete the key; `QueryBuilder.distinct()` was removed with it, and the count suppression is gone (`total` is truthful again). For unique values of one column use the SQL/memory drivers' `distinct(object, field)` door; for unique combinations, `groupBy`; for a deduplicated count, the `count_distinct` aggregation. |
165165
| **expand** | `Record<string, { object: string; fields?: string[]; where?: any; search?: string \| { query: string; fields?: string[]; fuzzy: boolean; operator: Enum<'and' \| 'or'>; … }; … }>` | optional | Recursive relation loading map. Keys are lookup/master_detail field names; values are nested QueryAST objects that control select (`fields`) and filter (`where`, AND-merged with the batch $in), plus further expansion on the related object. The engine resolves expand via batch $in queries (driver-agnostic) with a default max depth of 3; per-parent `limit`/`offset`/`orderBy` are NOT applied on this path. |
166166

167167

0 commit comments

Comments
 (0)