Skip to content

Commit 18e4ac1

Browse files
docs(spec): endpoints.item describe names the app bundle route it gates (#28996)
<!-- claude-projects-attribution: {"github_login":"os-steve"} --> _Requested by **Steve** · [project thread](https://claude.ai/code/project/chan_01FdL1XeUyvVNxLreh3F4Qi9?thread=cmsg_01FdL1XeUyvVNxLreh3F4Qi9W9eKaPHQSNGAfHX12ERwnj)_ Fixes #28934 Clause-②: no Metadata-protocol: none ## 维护者速读 - 改了什么:`metadata.endpoints.item` 的 describe 补上它实际已经关掉的第 13 条路由 `GET /meta/app/:appId/bundle`,QA 清单条目 `api-backend.rest-metadata-config-contract` 的步骤、验收[2]和变体行同步改为 13 条。 - 为什么改:#27627 让应用元数据包路由走逐项注册器,挂载表测试已钉 13 条,但公开 schema 的说明还写 12 条;18.3.0 候选版 QA 跑 #28929 判为 FAIL。分诊裁定代码是对的、改说明。 - 风险与回滚:只改说明文字和 QA 清单,不动任何路由、默认值或可接受值;回滚即还原本 PR。 - 先看:`packages/spec/src/api/rest-server.zod.ts`(item 的 describe)、`docs/qa/platform-checklist/areas/api-backend.json`(steps[7]、acceptance[2]、变体行、revision 9)。 - 两分钟验证:`pnpm --filter @objectstack/rest exec vitest run src/rest-config-mount-table.pin.test.ts`,预期全绿,且 describe 列出的路由与该测试 `metadata.endpoints.item` 行的 13 条一一对应。 - 需要你做的:无,席位落地。 ## What changes - `packages/spec/src/api/rest-server.zod.ts`: the `metadata.endpoints.item` `.describe()` now names `GET /meta/app/:appId/bundle` after the book tree, with the reason it rides this switch (it registers through the same per-item registrar because every member the bundle serves is the item read's own answer, the pin's own words). The `endpoints` block header counts no routes, so it is unchanged. - `content/docs/references/api/rest-server.mdx`: regenerated (`check:generated --fix`, one row). - `docs/qa/platform-checklist/areas/api-backend.json`, item `api-backend.rest-metadata-config-contract`: steps[7] and acceptance[2].clause enumerate thirteen mounts including the bundle; the variant row reads `switch:endpoints.item (13 mounts)`; revision 8 to 9 with a history entry. `coverage.json` and `FOLLOW-UPS.md` derive nothing from these fields (no hit for the count in either), so nothing is regenerated there. - Bounded in-place fix in the same item: the title said `prefix moves eleven mounts`; the default table has twenty metadata mounts (the `api.enableMetadata` row of the mount-table pin) and acceptance[1] asserts that the whole table re-bases, so the title now says `prefix moves every metadata mount`. Same defect class (a count in this item that drifted from the pin), mechanical, same file and gate family. Declared for the seat to amend the claim's file surface. - `.changeset/28934-endpoints-item-bundle-describe.md`: `@objectstack/spec` patch. Forced test edits: none. No spec or dogfood test pins the twelve-route sentence or count; the dogfood harness and live tests read the item by acceptance index only. ## Tests - `packages/spec`: `pnpm --filter @objectstack/spec test` 789/789 files, 23325 passed, 1 todo; `typecheck` exit 0. - `packages/rest`: `rest-config-mount-table.pin.test.ts` + `rest-sub-config-parse-not-cast.test.ts` 2/2 files, 49/49 tests. - `packages/qa/dogfood`: `test/rest-config-contract-harness.test.ts` 1/1 file, 7/7 tests. - `pnpm check:platform-checklist` exit 0 (16 areas, 352 items). ## Acceptance notes Three comments outside the claimed file surface still state the old radius. They are drift in prose nobody parses, so they are noted here, not filed (carrier: none): - `packages/rest/src/rest-server.ts`, the `registerPerItemRoute` docblock in `registerMetadataEndpointsInner`, lists the radius without the bundle (the claim excludes the mount code). - `packages/rest/src/rest-config-mount-table.pin.test.ts` file header says `metadata.endpoints.item` gates TWELVE (and "eleven of them moving"); its table and the per-case comment already say thirteen. - `packages/spec/liveness/metadata_endpoints.json`, the `endpoints.item` row's note says "twelve mounts". `docs/NORTH-STAR.md` names this checklist item but states no count, so there is no governed follow-up. ## Verification Measured at base 135054e, head 94cfe51. Derived gate list and its reconciliation are in the report comment on #28934. --- _Generated by [Claude Code](https://claude.ai/code/session_01ADHkmRtDoeyt3cTeZseNHV)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent d1f3ac0 commit 18e4ac1

4 files changed

Lines changed: 27 additions & 7 deletions

File tree

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
'@objectstack/spec': patch
3+
---
4+
5+
docs(spec): `metadata.endpoints.item` names the app metadata bundle route it already gates (#28934)
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
The `describe()` of `RestServerConfig.metadata.endpoints.item` claims to enumerate every mount the switch takes, and it listed twelve. The switch takes thirteen: `GET /meta/app/:appId/bundle`, the app metadata bundle, registers through the same per-item registrar, so `endpoints.item: false` already removes it. The description now names it, with the reason it rides this switch: every member the bundle serves is the item read's own answer.
11+
12+
Only the description text changes. No route, default or accepted value moves; the radius was already pinned at thirteen routes in `@objectstack/rest`'s mount-table test.

‎content/docs/references/api/rest-server.mdx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ const result = BatchEndpointsConfigSchema.parse(data);
224224
| :--- | :--- | :--- | :--- |
225225
| **types** | `boolean` | optional (default: `true`) | Mount the metadata type list — `GET /meta` and `GET /meta/types` (one handler, two paths) |
226226
| **items** | `boolean` | optional (default: `true`) | Mount the per-type item list — `GET /meta/:type`, and nothing else |
227-
| **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 (`/history`, `/audit`, `/diff`, `/published`, `/publish`, `/rollback`) and `GET /meta/book/:name/tree` |
227+
| **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 (`/history`, `/audit`, `/diff`, `/published`, `/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 |
228228
| **maintenance** | `boolean` | optional (default: `true`) | Mount the whole-store maintenance operations — `GET /meta/diagnostics`, `GET /meta/_drafts` and the `POST /meta/_migrate-stored` write door |
229229
| **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. |
230230

‎docs/qa/platform-checklist/areas/api-backend.json‎

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2707,10 +2707,10 @@
27072707
},
27082708
{
27092709
"id": "api-backend.rest-metadata-config-contract",
2710-
"title": "RestServerConfig.metadata is the metadata surface's construction contract: prefix moves eleven mounts and their discovery advertisement, four endpoint switches each gate exactly the face its name states (#15542/#15854), maskObjectFields is the ADR-0106 D8 disclosure gate, and both tombstones refuse at construction",
2710+
"title": "RestServerConfig.metadata is the metadata surface's construction contract: prefix moves every metadata mount and their discovery advertisement, four endpoint switches each gate exactly the face its name states (#15542/#15854), maskObjectFields is the ADR-0106 D8 disclosure gate, and both tombstones refuse at construction",
27112711
"since": "v17",
27122712
"status": "active",
2713-
"revision": 8,
2713+
"revision": 9,
27142714
"priority": "P1",
27152715
"surface": "api",
27162716
"personas": [
@@ -2738,7 +2738,7 @@
27382738
"harness: construct with { metadata: { endpoints: { types: false } } } — GET {prefix} and GET {prefix}/types both disappear (one handler, two paths)",
27392739
"harness: construct with { metadata: { endpoints: { items: false } } } and enumerate what leaves: GET {prefix}/:type and NOTHING else — exactly the one mount its describe() names (#15542 moved the whole-store family off this switch; a run that still sees /diagnostics, /_drafts or POST /_migrate-stored leave with it is the old radius returning)",
27402740
"harness: construct with { metadata: { endpoints: { maintenance: false } } } and enumerate: GET {prefix}/diagnostics, GET {prefix}/_drafts and the POST {prefix}/_migrate-stored write door — three whole-store operations, the key #15542 created so that closing a listing read stops disarming a migration door",
2741-
"harness: construct with { metadata: { endpoints: { item: false } } } and enumerate: the WHOLE per-item face — GET, PUT and DELETE {prefix}/:type/:name, /:type/:name/references, /:type/:name/layers, the history family (/history, /audit, /diff, /published, /publish, /rollback) and {prefix}/book/:name/tree; twelve mounts, writes included (#15854)",
2741+
"harness: construct with { metadata: { endpoints: { item: false } } } and enumerate: the WHOLE per-item face — GET, PUT and DELETE {prefix}/:type/:name, /:type/:name/references, /:type/:name/layers, the history family (/history, /audit, /diff, /published, /publish, /rollback), {prefix}/book/:name/tree and the app metadata bundle GET {prefix}/app/:appId/bundle; thirteen mounts, writes included (#15854; the bundle since #27627, which registers it through the same per-item registrar because every bundle member is the item read's own answer)",
27422742
"harness: construct with { metadata: { enableCache: false } } and confirm the item read takes the uncached branch; then with enableCache true against an app-type, a dashboard-type, a draft read, a preview-drafts read, a package-scoped read, a member-addressed read (?member=, #22877) and an audience-gated type — each is a carve-out that bypasses the cached path even when the switch is on",
27432743
"harness: construct with { metadata: { maskObjectFields: false } }; separately construct with the default and metaType != 'object'",
27442744
"harness: construct with { metadata: { cacheTtl: 3600 } }, then 0, then -1, then { metadata: { endpoints: { schema: false } } }; capture each refusal text"
@@ -2757,7 +2757,7 @@
27572757
"evidence": "route table + discovery body at the non-default prefix"
27582758
},
27592759
{
2760-
"clause": "each of the FOUR endpoint switches gates exactly the face its name states, and the run must ENUMERATE what each removes rather than trust the describe(): `types` takes two paths (one handler at {prefix} and {prefix}/types), `items` takes exactly one ({prefix}/:type), `maintenance` takes the three whole-store operations INCLUDING the POST {prefix}/_migrate-stored write door, `item` takes the whole per-item face (twelve mounts, PUT and DELETE and the history family included)",
2760+
"clause": "each of the FOUR endpoint switches gates exactly the face its name states, and the run must ENUMERATE what each removes rather than trust the describe(): `types` takes two paths (one handler at {prefix} and {prefix}/types), `items` takes exactly one ({prefix}/:type), `maintenance` takes the three whole-store operations INCLUDING the POST {prefix}/_migrate-stored write door, `item` takes the whole per-item face (thirteen mounts, PUT and DELETE, the history family and GET {prefix}/app/:appId/bundle included)",
27612761
"oracle": "test",
27622762
"verify": "per-switch route-table diffs against the all-true baseline; the diff sets must be exactly the routes enumerated in the steps, in both directions — a switch that GROWS a route is as much a FAIL as one that loses one. ⛔ The run records the real radius whatever it finds: the describe() strings now claim to be exhaustive (#15542/#15854), so a diff that disagrees with them is a defect in the code or in the declaration, never a reason to relax this clause",
27632763
"evidence": "the four route-table diffs"
@@ -2790,7 +2790,7 @@
27902790
"knob:prefix",
27912791
"switch:endpoints.types (2 mounts)",
27922792
"switch:endpoints.items (1 mount)",
2793-
"switch:endpoints.item (12 mounts)",
2793+
"switch:endpoints.item (13 mounts)",
27942794
"switch:endpoints.maintenance (3 mounts)",
27952795
"switch:enableCache (+6 carve-outs)",
27962796
"switch:maskObjectFields",
@@ -2862,6 +2862,12 @@
28622862
"date": "2026-10-27",
28632863
"change": "acceptance[0], [1], [3], [4] and [5] and negative[0] are pinned at the layer their oracle names (the release-18.2.0 Tier 1 sweep #26468 scored a0 and a4 unpinned, a1 and a5 half-asserted, a3 and n0 below the HTTP layer). The new rest-config-contract-live.dogfood.test.ts fires the default mounts and the client_liaison schema diff on a booted showcase, and rest-config-contract-harness.test.ts reads the re-based table, the cache branch per carve-out and the refusal texts off a real RestServer. automated.kind is dogfood; automated.ref and acceptance[5].verify name what they assert. Measured at base c8583f80. No clause changed",
28642864
"ref": "#26572"
2865+
},
2866+
{
2867+
"revision": 9,
2868+
"date": "2026-11-02",
2869+
"change": "the `item` radius is thirteen mounts, not twelve: #27627 registered GET {prefix}/app/:appId/bundle through the per-item registrar, and packages/rest/src/rest-config-mount-table.pin.test.ts moved to thirteen with it, but steps[7], acceptance[2].clause and the variants row still enumerated twelve, so the 18.3.0 release-candidate run #28929 scored the describe() drift as a FAIL. Triage ruled the radius intended and the declaration wrong (6569524266): steps[7], acceptance[2].clause and the variant now name the bundle, and the title's stale 'prefix moves eleven mounts' (the default table has twenty) says 'every metadata mount', which is what acceptance[1] asserts. Measured at base 135054eb",
2870+
"ref": "#28934"
28652871
}
28662872
]
28672873
},

‎packages/spec/src/api/rest-server.zod.ts‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -531,7 +531,9 @@ export const MetadataEndpointsConfigSchema = lazySchema(() => z.object({
531531
.describe(
532532
'Mount the whole per-item face — `GET`, `PUT` and `DELETE /meta/:type/:name`, its '
533533
+ '`/references` and `/layers` reads, the history family (`/history`, `/audit`, `/diff`, '
534-
+ '`/published`, `/publish`, `/rollback`) and `GET /meta/book/:name/tree`',
534+
+ '`/published`, `/publish`, `/rollback`), `GET /meta/book/:name/tree` and the app metadata '
535+
+ 'bundle `GET /meta/app/:appId/bundle`, which registers through the same per-item registrar '
536+
+ 'because every member it serves is the item read\'s own answer',
535537
),
536538
maintenance: z.boolean().default(true)
537539
.describe(

0 commit comments

Comments
 (0)