Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .changeset/33085-item-describe-qualified-read.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
'@objectstack/spec': patch
---

docs(spec): `metadata.endpoints.item` names the package-qualified by-name read it already gates

Clause-②: no
Metadata-protocol: none

The `describe()` of `RestServerConfig.metadata.endpoints.item` claims to enumerate every mount the switch takes, and it listed thirteen. The switch takes fourteen: `GET /meta/:type/:ns/:name`, the package-qualified by-name read of ADR-0150 D5, answers through the item read's own handler, so `endpoints.item: false` already removes it. The description now names it, and the `endpoints.item` row note in `liveness/metadata_endpoints.json` counts fourteen mounts instead of thirteen.

Only description text changes. No route, default or accepted value moves; the radius was already pinned at fourteen routes in `@objectstack/rest`'s mount-table test.
2 changes: 1 addition & 1 deletion content/docs/references/api/rest-server.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ const result = BatchEndpointsConfigSchema.parse(data);
| :--- | :--- | :--- | :--- |
| **types** | `boolean` | optional (default: `true`) | Mount the metadata type list — `GET /meta` and `GET /meta/types` (one handler, two paths) |
| **items** | `boolean` | optional (default: `true`) | Mount the per-type item list — `GET /meta/:type`, and nothing else |
| **item** | `boolean` | optional (default: `true`) | Mount the whole per-item face — `GET`, `PUT` and `DELETE /meta/:type/:name`, its `/references` and `/layers` reads, the history family (`GET` `/history`, `/audit`, `/diff`, `/published`; `POST` `/publish`, `/rollback`), `GET /meta/book/:name/tree` and the app metadata bundle `GET /meta/app/:appId/bundle`, which registers through the same per-item registrar because every member it serves is the item read's own answer |
| **item** | `boolean` | optional (default: `true`) | Mount the whole per-item face — `GET`, `PUT` and `DELETE /meta/:type/:name`, its `/references` and `/layers` reads, the history family (`GET` `/history`, `/audit`, `/diff`, `/published`; `POST` `/publish`, `/rollback`), the package-qualified by-name read `GET /meta/:type/:ns/:name` (ADR-0150 D5), which answers through the item read's own handler, `GET /meta/book/:name/tree` and the app metadata bundle `GET /meta/app/:appId/bundle`, which registers through the same per-item registrar because every member it serves is the item read's own answer |
| **maintenance** | `boolean` | optional (default: `true`) | Mount the whole-store maintenance operations — `GET /meta/diagnostics`, `GET /meta/_drafts`, the `POST /meta/_migrate-stored` write door and the `POST /meta/_migrate-record-currency-codes` record-currency code backfill |
| **schema** | `never` | optional | [REMOVED] `metadata.endpoints.schema` was removed in @objectstack/spec 17 (ADR-0049 enforce-or-remove) — it gated a route that does not exist: the REST server mounts no `GET /meta/:type/:name/schema`, so `false` removed nothing and `true` added nothing. Delete the key; `endpoints.types` / `items` / `item` / `maintenance` are the switches that gate real mounts. |

Expand Down
2 changes: 1 addition & 1 deletion packages/spec/liveness/metadata_endpoints.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
"evidenceScope": "in-repo",
"evidence": "packages/rest/src/meta-item-read-routes.ts#registerMetaItemReadRoutes (`if (metadata.endpoints.item !== false)` gates the per-item read block); also packages/rest/src/rest-server.ts#registerMetadataEndpointsInner (`registerPerItemRoute` \u2014 the same switch, spelled as a call \u2014 gates the per-item PUT/DELETE and the history family)",
"producer": "packages/rest/src/rest-server.ts#normalizeConfig (threads the authored value into `this.config`, which is the object every consumer below reads; the parsed sub-config's own `.default()`s supply the value when the author omits the key)",
"note": "Same `!== false` shape as `types`. RADIUS WIDENED 2026-09-06 (#15854, ruled with #15542): it used to gate four READS (`/:type/:name`, `/references`, `/layers`, `/book/:name/tree`) and leave the per-item WRITES (`PUT` / `DELETE /:type/:name`) and the history family (`history`, `audit`, `diff`, `published`, `publish`, `rollback`) answering to `api.enableMetadata` alone \u2014 the converse of the `items` mismatch: an operator who closed the per-item surface left its writes mounted. It now gates the whole per-item face, thirteen mounts since #27627 added the app metadata bundle `GET /meta/app/:appId/bundle`, which is what the switch name has always promised. `GET /meta/object/:name/state/:field` is deliberately NOT part of that face (object FSM read, addressed by object name) and stays under `api.enableMetadata` alone. REACHABILITY (#15543, measured 2026-09-08 on origin/main 8ccf7a1df): embedder-only. Written only by a host that constructs a RestServerConfig; no shipped boot path authors it, so a CLI-started deployment always gets the schema default. See this file's _note REACHABILITY paragraph. Liveness and verifiedAt are unaffected: `live` answers who READS this key, this row answers who can SET it."
"note": "Same `!== false` shape as `types`. RADIUS WIDENED 2026-09-06 (#15854, ruled with #15542): it used to gate four READS (`/:type/:name`, `/references`, `/layers`, `/book/:name/tree`) and leave the per-item WRITES (`PUT` / `DELETE /:type/:name`) and the history family (`history`, `audit`, `diff`, `published`, `publish`, `rollback`) answering to `api.enableMetadata` alone \u2014 the converse of the `items` mismatch: an operator who closed the per-item surface left its writes mounted. It now gates the whole per-item face, fourteen mounts: #27627 added the app metadata bundle `GET /meta/app/:appId/bundle` and ADR-0150 D5 (#31691) the package-qualified by-name read `GET /meta/:type/:ns/:name`, which is what the switch name has always promised. `GET /meta/object/:name/state/:field` is deliberately NOT part of that face (object FSM read, addressed by object name) and stays under `api.enableMetadata` alone. REACHABILITY (#15543, measured 2026-09-08 on origin/main 8ccf7a1df): embedder-only. Written only by a host that constructs a RestServerConfig; no shipped boot path authors it, so a CLI-started deployment always gets the schema default. See this file's _note REACHABILITY paragraph. Liveness and verifiedAt are unaffected: `live` answers who READS this key, this row answers who can SET it."
},
"maintenance": {
"status": "live",
Expand Down
4 changes: 3 additions & 1 deletion packages/spec/src/api/rest-server.zod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -536,7 +536,9 @@ export const MetadataEndpointsConfigSchema = lazySchema(() => z.object({
.describe(
'Mount the whole per-item face — `GET`, `PUT` and `DELETE /meta/:type/:name`, its '
+ '`/references` and `/layers` reads, the history family (`GET` `/history`, `/audit`, '
+ '`/diff`, `/published`; `POST` `/publish`, `/rollback`), `GET /meta/book/:name/tree` and the app metadata '
+ '`/diff`, `/published`; `POST` `/publish`, `/rollback`), the package-qualified by-name read '
+ '`GET /meta/:type/:ns/:name` (ADR-0150 D5), which answers through the item read\'s own handler, '
+ '`GET /meta/book/:name/tree` and the app metadata '
+ 'bundle `GET /meta/app/:appId/bundle`, which registers through the same per-item registrar '
+ 'because every member it serves is the item read\'s own answer',
),
Expand Down
Loading