From 3118d15c7bdba5f38310cc25599889541dfc5cef Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 6 Sep 2026 06:15:59 +0000 Subject: [PATCH 1/2] docs(services-checklist): align the slot capability-flag bullet with the closed capabilities vocabulary The bullet named the retired top-level `features` map and the retired `workflow` slot, and read `there is no features.auth`. Rewrite it against `WellKnownCapabilitiesSchema` (`packages/spec/src/api/discovery.zod.ts`) and the dispatcher that fills it: name the entries that really follow slot presence, keep the measured `websockets` clause, and record the vocabulary keys that are answered from something other than a slot. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01Vbw3RPgdtqesx4azk9SbW8 --- content/docs/kernel/services-checklist.mdx | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/content/docs/kernel/services-checklist.mdx b/content/docs/kernel/services-checklist.mdx index ffd6e3ed36..a27656bdc1 100644 --- a/content/docs/kernel/services-checklist.mdx +++ b/content/docs/kernel/services-checklist.mdx @@ -607,12 +607,24 @@ When a plugin registers a service, the discovery endpoint automatically updates: `route` → `"/api/v1/auth"` (unless the instance self-declares `stub`/`degraded` via `__serviceInfo`, which is reported verbatim instead) - `routes.auth` → `"/api/v1/auth"` appears in routes -- `features` flags follow for the slots that have one — `search`, `files`, - `analytics`, `ai`, `workflow`, `notifications`, `i18n` (`websockets` does **not** - follow slot presence: it is `isSubscribableChannel(services.realtime)` — `handlerReady: +- the `capabilities` entries derived from that slot flip — `files` (and + `chunkedUpload`, which rides the same storage surface), `analytics`, `ai`, + `notifications`, `i18n`, `automation`, `cron` (the `job` slot) and `export` + (`automation` **or** `queue`). `capabilities` is the canonical spelling: the top-level + `features` map was renamed to it in + [#4828](https://github.com/objectstack-ai/objectstack/issues/4828), and the `workflow` + slot was retired in v17 ([#4451](https://github.com/objectstack-ai/objectstack/issues/4451)), + so neither name is on the wire. The rest of the vocabulary does **not** follow slot + presence: `websockets` is `isSubscribableChannel(services.realtime)` — `handlerReady: true` **and** a connectable route — so registering a realtime service does not flip it, - and it reads `false` on every host the open framework ships, #14646; there is no - `features.auth`) + and it reads `false` on every host the open framework ships, #14646; `search` is stated + `false` because this dispatcher mounts no `/search` route, whatever fills the slot + ([#7602](https://github.com/objectstack-ai/objectstack/issues/7602)); `comments` is + measured from the object registry; and `transactionalBatch` is `false` on this face, + which mounts no `/batch`. There is no `capabilities.auth`: the vocabulary is closed by + `WellKnownCapabilitiesSchema` (`packages/spec/src/api/discovery.zod.ts`), every producer + answers every key, and a capability the host does not deliver reads `enabled: false` + rather than a missing key --- From be6cf258ba14ce7d98e8c75c2d93771010e29ef7 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 6 Sep 2026 06:16:24 +0000 Subject: [PATCH 2/2] docs(data-api): state what the analytics query result's fields[] really declares The callout said each entry carries exactly `name` and `type` "and nothing else", and that reading `label` / `format` off a query result yields `undefined`. `AnalyticsResultResponseSchema` declares `label` / `format` / `currency` / `percentScale` / `builtinAggregate` as optional members and the `AnalyticsResult` contract mirrors them, so both sentences are false on this tree. Rewrite the callout in the conditional form the schema supports and keep the cube-metadata pointer as the declaration surface it is. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01Vbw3RPgdtqesx4azk9SbW8 --- content/docs/api/data-api.mdx | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/content/docs/api/data-api.mdx b/content/docs/api/data-api.mdx index 33fb566a00..d11a70e700 100644 --- a/content/docs/api/data-api.mdx +++ b/content/docs/api/data-api.mdx @@ -420,16 +420,23 @@ Filtering uses the canonical Query DSL `where` object (the same MongoDB-style `F ``` -**`fields[]` describes columns, not presentation.** Each entry carries exactly `name` and -`type` — that is the whole descriptor `AnalyticsResultResponseSchema` declares, and every -strategy answering this endpoint emits those two keys and nothing else. - -Display name and number format live one layer up, in the **cube's metric/dimension -definition** (`MetricSchema.label` / `MetricSchema.format`, `DimensionSchema.label`), and -are read from cube metadata — `GET /analytics/meta` below reports each measure's and -dimension's declared label as `title`. Reading `data.fields[i].label` or -`data.fields[i].format` off a query result yields `undefined`; a client that renders table -headers or formats amounts reads them from the cube metadata instead. +**`fields[]` is the resolved presentation surface — read it first.** Each entry carries +`name` and `type` and, when the producer declares them, `label`, `format`, `currency`, +`percentScale` and `builtinAggregate`: the optional members +`AnalyticsResultResponseSchema` declares (`packages/spec/src/api/analytics.zod.ts`), +mirrored member for member by `IAnalyticsService.query`'s `AnalyticsResult` and bound to +it at compile time. Optional means a given column may omit them — read them defensively, +never as guaranteed present. + +The **cube's metric/dimension definition** (`MetricSchema.label` / `MetricSchema.format`, +`DimensionSchema.label`) is the *declaration* surface, not a substitute for this one. +`GET /analytics/meta` below publishes a deliberately narrow projection of it — `name`, +`type` and `title` (the definition's `label`) only, with `format` dropped +([#6442](https://github.com/objectstack-ai/objectstack/issues/6442)). So a client that +renders table headers or formats amounts takes `label` / `format` / `currency` / +`percentScale` off the query result's `fields[]`: those are resolved server-side (the +ADR-0053 currency chain, the percent-scale chain), and `format` is not reachable through +the metadata endpoint at all. ### `GET /analytics/meta`