Skip to content

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

Merged
objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/project-thread-bgvfrd
Nov 14, 2026
Merged

objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/project-thread-bgvfrd

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Leehom · project thread

Fixes #33085

Clause-②: no
Metadata-protocol: none

维护者速读

  • 改了什么:RestServerConfig.metadata.endpoints.item 的 describe 补上它已经在关的第 14 条路由 GET /meta/:type/:ns/:name(ADR-0150 D5 的按包限定读取);liveness 台账同一行的说明把「十三条」改成「十四条」;参考文档那一行重新生成。
  • 为什么改:这个开关的 describe 承诺列出关掉它会移除的每一条路由,@objectstack/rest 的挂载表测试实测是 14 条,文案只列了 13 条,运维读文档会以为限定读取不受它控制。
  • 风险与回滚:只改说明文字,不动路由、默认值或可接受的值;spec patch changeset。回滚即 revert。
  • 先看:packages/spec/src/api/rest-server.zod.ts 里 item 的 describe;其次 packages/spec/liveness/metadata_endpoints.json 的 endpoints.item 行。
  • 两分钟验证:pnpm --filter @objectstack/rest exec vitest run src/rest-config-mount-table.pin.test.ts,预期 24/24 通过,其中 metadata.endpoints.item 行列出的 14 条与新 describe 一一对应。
  • 需要你做的:无,席位落地。

What changes

Why

The JSDoc above endpoints says each switch's describe() enumerates every mount it takes. The pin packages/rest/src/rest-config-mount-table.pin.test.ts on origin/main fd7b378c lists fourteen routes under metadata.endpoints.item, and the describe listed thirteen. No other route in that row moved since the card was filed (/references and /layers were already named). The describe follows the pin; packages/rest/** is untouched.

Tests

  • No behaviour changes, so no test is added and nothing is ablated.
  • pnpm --filter @objectstack/spec typecheck exit 0; pnpm --filter @objectstack/spec exec vitest run --maxWorkers=2 src/api/rest-server: Test Files 1 passed (1), Tests 53 passed (53).
  • pnpm --filter @objectstack/rest exec vitest run --maxWorkers=2 src/rest-config-mount-table.pin.test.ts (read-only, as the measurement): Test Files 1 passed (1), Tests 24 passed (24).

Acceptance notes

The same count drift sits in files outside this card's surface, so they are listed here and in the report for the seat to file:

  • packages/rest/src/rest-server.ts (the registerPerItemRoute docblock in registerMetadataEndpointsInner) says the per-item face is "thirteen mounts" and leaves out the qualified read.
  • packages/rest/src/rest-server.ts (the endpoints member docblock of the normalized config) lists maintenance as /diagnostics, /_drafts, POST /_migrate-stored and leaves out POST /_migrate-record-currency-codes, which the pin lists as the fourth.
  • docs/qa/platform-checklist/areas/api-backend.json (api-backend.rest-metadata-config-contract) says item takes thirteen mounts in three places and does not name the qualified read.

Verification

On cc5adeb4: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 101 commands. 100 ran with exit 0. pnpm check:dual-build-cjs-loads exited 3 (PREREQUISITE NOT MET: it needs every package's dist, and only the spec, lint, objectql and client closures were built), so it is NOT MEASURED here and left to CI. --ran reconciliation: "101 derived famil(ies) accounted for — 100 run, 1 NOT-MEASURED". node scripts/check-nul-bytes.mjs exit 0.

🤖 Generated with Claude Code

https://claude.ai/code/session_01FEkQ5oCewffujRRbsX1TaR

…me read it already gates

The item switch's describe() enumerates its mount radius; the mount-table
pin in @objectstack/rest removes fourteen routes, the describe named
thirteen. Add GET /meta/:type/:ns/:name (ADR-0150 D5), fix the liveness
row note's count, and regenerate the reference docs row.

Claude-Session: https://claude.ai/code/session_01FEkQ5oCewffujRRbsX1TaR
Co-Authored-By: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added documentation Improvements or additions to documentation tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface labels Nov 14, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/spec, touching 7 documentable anchor(s). ⚠️ 1 changed file(s) yielded no anchor (packages/spec/liveness/metadata_endpoints.json), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

8 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/api/client-sdk.mdx (via getBookTree (sdk, the bare tail of client method meta.getBookTree, bound to GET /api/v1/meta/book/:name/tree), getItem (sdk, the bare tail of client method meta.getItem, bound to GET /api/v1/meta/:type/:ns/:name; the bare tail of client method meta.getItem, bound to GET /meta/:type/:ns/:name), meta.getBookTree (sdk, the route ledger binds it to GET /api/v1/meta/book/:name/tree, selected by route anchor /meta/book/:name/tree), meta.getItem (sdk, the route ledger binds it to GET /api/v1/meta/:type/:ns/:name, selected by route anchor /meta/:type/:ns/:name; the route ledger binds it to GET /meta/:type/:ns/:name), /meta/:type/:ns/:name (route, a path literal in MetadataEndpointsConfigSchema))
  • content/docs/api/error-catalog.mdx (via /meta/:type/:ns/:name (route, a path literal in MetadataEndpointsConfigSchema))
  • content/docs/data-modeling/analytics.mdx (via getItem (sdk, the bare tail of client method meta.getItem, bound to GET /api/v1/meta/:type/:ns/:name; the bare tail of client method meta.getItem, bound to GET /meta/:type/:ns/:name), meta.getItem (sdk, the route ledger binds it to GET /api/v1/meta/:type/:ns/:name, selected by route anchor /meta/:type/:ns/:name; the route ledger binds it to GET /meta/:type/:ns/:name))
  • content/docs/frontend/data-objectstack.mdx (via getItem (sdk, the bare tail of client method meta.getItem, bound to GET /api/v1/meta/:type/:ns/:name; the bare tail of client method meta.getItem, bound to GET /meta/:type/:ns/:name), meta.getItem (sdk, the route ledger binds it to GET /api/v1/meta/:type/:ns/:name, selected by route anchor /meta/:type/:ns/:name; the route ledger binds it to GET /meta/:type/:ns/:name))
  • content/docs/frontend/troubleshooting.mdx (via getItem (sdk, the bare tail of client method meta.getItem, bound to GET /api/v1/meta/:type/:ns/:name; the bare tail of client method meta.getItem, bound to GET /meta/:type/:ns/:name))
  • content/docs/ui/apps.mdx (via /meta/book/:name/tree (route, a path literal in MetadataEndpointsConfigSchema))
  • content/docs/ui/doc-pages.mdx (via getItem (sdk, the bare tail of client method meta.getItem, bound to GET /api/v1/meta/:type/:ns/:name; the bare tail of client method meta.getItem, bound to GET /meta/:type/:ns/:name))
  • content/docs/ui/translations.mdx (via /meta/book/:name/tree (route, a path literal in MetadataEndpointsConfigSchema))

⛔ 4 release-owned page(s) also name something this change touched. These are read-only:

  • content/docs/releases/v14.mdx (via /meta/book/:name/tree (route, a path literal in MetadataEndpointsConfigSchema))
  • content/docs/releases/v17/17-3.mdx (via /meta/:type/:ns/:name (route, a path literal in MetadataEndpointsConfigSchema))
  • content/docs/releases/v17/17-5.mdx (via /meta/book/:name/tree (route, a path literal in MetadataEndpointsConfigSchema))
  • content/docs/releases/v18/18-4.mdx (via getItem (sdk, the bare tail of client method meta.getItem, bound to GET /api/v1/meta/:type/:ns/:name; the bare tail of client method meta.getItem, bound to GET /meta/:type/:ns/:name), meta.getItem (sdk, the route ledger binds it to GET /api/v1/meta/:type/:ns/:name, selected by route anchor /meta/:type/:ns/:name; the route ledger binds it to GET /meta/:type/:ns/:name), /meta/:type/:ns/:name (route, a path literal in MetadataEndpointsConfigSchema))

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

What this run could not see
  • 1 changed file(s) yielded no anchor (packages/spec/liveness/metadata_endpoints.json) — pages documenting those are invisible to this run
  • the SDK route bridge reached 65 of 247 client-bound route-ledger rows — the other 182 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 182: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 59 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 123 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 172 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 6a28a271e86a556b8757c031c959bf6e992a0c83 → packageMentionDocs.

Which tree this was computed on

This run read content/docs from dffbaadf5978bf1b09bf71b73aebad62b03042cc — the merge of head cc5adeb43b99478ca8c015e0d8d6456573e1c9bb into base 6a28a271e86a556b8757c031c959bf6e992a0c83, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin dffbaadf5978bf1b09bf71b73aebad62b03042cc && git checkout dffbaadf5978bf1b09bf71b73aebad62b03042cc
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 6a28a271e86a556b8757c031c959bf6e992a0c83 cc5adeb43b99478ca8c015e0d8d6456573e1c9bb && git checkout -B drift-repro 6a28a271e86a556b8757c031c959bf6e992a0c83 && git merge --no-ff cc5adeb43b99478ca8c015e0d8d6456573e1c9bb

node scripts/docs-audit/affected-docs.mjs --json 6a28a271e86a556b8757c031c959bf6e992a0c83

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs 6a28a271e86a556b8757c031c959bf6e992a0c83 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:spec#1 · os-project-manager · session_01FEkQ5mswnWm181XgYcCUag

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: cc5adeb43b99478ca8c015e0d8d6456573e1c9bb
Local-runs: none
Scope: packages/spec/src/api/rest-server.zod.ts + packages/spec/liveness/metadata_endpoints.json

PR #33312 for card #33085. Inputs read: the card body and its four comments (6850771234, 6880054551, 6880069048, 6880624366), the PR body and file list, the net diff against main (merge-base fd7b378c, 4 files, +17 / -3), and the check-runs on the head. Code outside the diff was read at the head sha, not reviewed: packages/rest/src/rest-config-mount-table.pin.test.ts, packages/rest/src/meta-qualified-item-route.ts, packages/rest/src/meta-item-read-routes.ts, packages/rest/src/rest-server.ts. main (24f2f401) has not touched any of the four diff files or the pin since the merge-base.

① Derived judgments

  • Accept set: unchanged — right. The only hunk in rest-server.zod.ts is inside the string literal passed to .describe() on metadata.endpoints.item. The key stays z.boolean().default(true); no key, type, default, refinement or export moves. Nothing an author could write before is refused now, and nothing new is accepted.
  • Published surface: description text only — right. The describe string ships in the spec dist and in the generated reference row; liveness/metadata_endpoints.json ships through the spec files[]. Both change wording only.
  • Generated output agrees with its source — right. The item row in content/docs/references/api/rest-server.mdx is the concatenated describe string, character for character. No other generated artefact carries the string in this diff, and the check-runs that would say otherwise (Lint · docs & code guards, Build Docs) have not concluded (see the gate note below).
  • packages/rest/** untouched — right. The claim made it read-only; the file list holds no rest path.

Gate verdicts on the head, read at 2026-11-14T16:28Z: 45 check-runs, 14 success, 9 skipped, 22 in_progress, 0 failed. Concluded green and relevant here: Spec property liveness, Check Changeset, Governed Surface Queue Guard, PR-body guards, Lint · PM dispatch-gates, Check Documentation Links, Dogfood Regression Gate. Still running, so NOT yet verdicts: Type Check (workspace, source gates, debt ledger, consumer gates), Test Core 1/8 to 8/8, Lint (ESLint, repo and tooling gates, docs and code guards, CI/release/engine gates, both scoped jobs), Build Core, Build Docs, Build UI Deps, Live E2E UI. This record does not stand in for them: the landing check (every PR check green) is still owed when they conclude, and the dev's own NOT-MEASURED family check:dual-build-cjs-loads is among what CI has yet to answer.

② Semver level

.changeset/33085-item-describe-qualified-read.md declares @objectstack/spec patch. That matches what the diff publishes: description text in one shipped package, no accept-set or type change. A skip-changeset would have been wrong, since both edited spec files ship. No other package is touched, so no other bump is owed.

Clause-②: no — carried on both the PR body and the changeset, and correct: the diff neither widens nor narrows what the schema accepts, and it is not a (narrowing) declaration. Metadata-protocol: none is also correct; no protocol member is read or changed.

③ Boundary flags

  • open_questions in the os-dev-report (6880624366): empty. Nothing to answer.
  • deviations: none against the order. The one surface amendment (the liveness row note) was requested before the commit and granted by the seat in 6880069048; the diff stays inside the amended surface.
  • Branch is claude/project-thread-bgvfrd rather than claude/issue-33085-*: declared in the claim (6880054551) as a platform constraint of mode:thread. Accepted as declared.
  • out_of_scope_findings: three class-b entries, all the same count drift outside this card's surface (packages/rest/src/rest-server.ts registerPerItemRoute docblock "thirteen mounts"; the same file's normalized-config maintenance docblock missing POST /_migrate-record-currency-codes; docs/qa/platform-checklist/areas/api-backend.json saying thirteen in three places). I read the first at the head and it holds. Not reviewed here; the seat still owes filing them. The pin's own row comment ([#27627] Thirteen: ..., above the metadata.endpoints.item row) has the same stale count while its file header says FOURTEEN — outside the diff, worth adding to the same follow-up.
  • Author: PR user.login is objectstack-fleet[bot], so no separate author note.
  • PR is a draft with no auto-merge armed; nothing to disarm.

④ Design judgment

packages/spec/src/api/rest-server.zod.ts (hits). The contract this string answers to is the JSDoc directly above endpoints: each switch's describe() enumerates every mount it takes, and the radius is pinned route by route in the mount-table test. I counted both sides at the head.

  • The pin row metadata.endpoints.item removes fourteen: DELETE, GET, PUT /:type/:name; GET /audit, /diff, /history, /layers, /published, /references; GET /:type/:ns/:name; GET /app/:appId/bundle; GET /book/:name/tree; POST /publish, /rollback.
  • The new describe names fourteen: the three verbs on /meta/:type/:name (3), /references and /layers (5), the six history routes (11), the qualified read (12), the book tree (13), the bundle (14). The two sets are equal; nothing is named that the pin does not list.
  • The mechanism clause, "which answers through the item read's own handler", is true of the code and is the actual reason the switch takes the route. registerMetaItemReadRoutes assigns readItem only inside if (metadata.endpoints.item !== false) and hands it back; registerMetaQualifiedItemRoute opens with if (!readItem) return. So the qualified route is not gated by registerPerItemRoute (unlike the bundle) but by the handler being absent, and the describe says exactly that rather than borrowing the bundle's "same per-item registrar" wording. I tried to break this by looking for a second mount of the path or an arm that survives the switch: the route ledger and rest-server.ts show one registration site, called once per base with the same scope, and the JSDoc above endpoints already covers the environment-scoped second copy for every route.
  • Same-file prose: no other count of the per-item face exists in this file (no "thirteen"/"twelve" left), so the claim's "same-file prose with the same drift" has nothing further to fix.

Findings:

  1. nit — the describe calls the route "the package-qualified by-name read". The one route pattern GET /meta/:type/:ns/:name also serves the _env arm (GET /meta/:type/_env/:name, the environment's own item), which is environment-qualified, not package-qualified; meta-qualified-item-route.ts calls the route "the qualified by-name read" for that reason. The route and the radius are named correctly, so an operator learns the right thing about what the switch removes; only the adjective is narrower than the route. Accepted deviation; "the qualified by-name read" would be exact if the string is next touched.

packages/spec/liveness/metadata_endpoints.json (in the diff; the ledger row for the same key, enforced by the Spec property liveness check, concluded success). Only the row's note changes; status, evidence, producer and the verification fields are untouched, which is right, since who reads the key did not change. "fourteen mounts" agrees with the pin, and both attributions hold: #27627 for the bundle (the pin's own comment), ADR-0150 D5 / #31691 for the qualified read (the pin's row comment and the route file header).

  1. nit — the rewrite left the trailing clause "which is what the switch name has always promised" attached to the qualified read rather than to "the whole per-item face" it used to qualify. Read literally it says the switch name always promised the qualified read. Wording only; the count and the routes are right. Accepted deviation or fold into the follow-up that fixes the rest docblocks.

No must-fix and no should-fix. Nits alone end the round; neither is on a prose face, so fixing either here would move the head past this record.

The rest of the diff (the changeset prose and the regenerated mdx row) is the lane seat's ACCEPT at TIER_DEFAULT.

Implemented-by: claude/project-thread-bgvfrd
Reviewed-by: session_01FEkQ5mswnWm181XgYcCUag

VERDICT: PASS

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:spec#1 · os-project-manager · session_01FEkQ5mswnWm181XgYcCUag

ACCEPT · PR #33312 (#33085) · head cc5adeb43b99478ca8c015e0d8d6456573e1c9bb · 2026-11-14T16:54Z

席位核对

  • 形态: 4 files, +17/−3 (read with get_files, not the report): the metadata.endpoints.item describe in packages/spec/src/api/rest-server.zod.ts, the endpoints.item row note in packages/spec/liveness/metadata_endpoints.json (surface amendment 6880069048), the regenerated row in content/docs/references/api/rest-server.mdx, one @objectstack/spec patch changeset. No governed path, under 3000 lines: Tier S, seat lands.
  • 前提实测: seat read the diff: the describe now names the 14 routes the mount-table pin lists for item (adds GET /meta/:type/:ns/:name); packages/rest/** untouched.
  • 门禁 check-runs: on this head 68 runs: 58 success, 10 skipped, 0 failed. Dev's NOT-MEASURED check:dual-build-cjs-loads is answered by CI.
  • 契约复审: CONTRACT_REVIEW_TIER PASS on this head, record 6880682421 (isolated reviewer; Ruling-ref 「你有 fable ,车道契约复审可以自己做」 2026-11-07). Two nits accepted as deviations (the route also serves the _env arm, so "package-qualified" is narrower than exact; the ledger note's trailing clause attaches to the new route); neither changes the contract.
  • 接受的偏差: the two nits above. Clause-②: no / Metadata-protocol: none on PR body and changeset.
  • 范围外去向: packages/rest/src/rest-server.ts docblock counts (per-item "thirteen mounts"; normalized maintenance list missing the currency backfill) — dropped — internal code comments, no published surface, no reader who acts (filing gate). docs/qa/platform-checklist/areas/api-backend.json — carried by open PR qa(checklist): re-point eight stale checklist items across five cards to the product at e540baf8 #33273, which already rewrites those lines to fourteen/four. The pin's own row comment "[Seam: one request opens a record — a record-page payload (record + related counts and first rows + field access + lock) and an app-scoped metadata bundle, so a record open is 2–3 requests instead of 23–45 serialized in three stages #27627] Thirteen" (reviewer) — dropped, same reason. Skills-docs: not owed (Metadata-protocol: none).
  • 落地条款: ready + auto-merge now under the maintainer's standing order 「按照 pm-dispatch 规则复核过的都授权」 (2026-10-25T11:35Z) and 「你应该使用 pm-dispatch 技能审核和落地pr」 (2026-11-07T16:59Z). K = 4 commits on main past the run's affected-set base 6a28a271, below 15: no merge of main owed.

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review November 14, 2026 16:55
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Nov 14, 2026
Merged via the queue into main with commit 1b878e7 Nov 14, 2026
69 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-bgvfrd branch November 14, 2026 17:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/s tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface

Projects

None yet

Development

Successfully merging this pull request may close these issues.

metadata.endpoints.item's describe() omits the qualified read GET /meta/:type/:ns/:name that the switch also removes (low)

1 participant