Repository navigation
feat(rest)!: serve ?names= on the /meta/:type list and close its query set - #30709
Conversation
…y set The metadata list read no names filter, so a client that needed several items of one type paid one by-name request per item (the Console Home roster gate: 8 on app-crm). Ruling A on #29236: - `?names=a,b,c` on every `/meta/:type` list, one comma-separated value (`metaListNames`): an empty value is refused 400 VALIDATION_ERROR, an unknown name is simply absent. The chain narrows after the per-caller gate and before the object mask and the translation; both transports push the names down to the store read (`GetMetaItemsRequest.names`), and `readFlattenedMetaItems` keeps the named items once every source is merged, before the lock, governance and decoration work. - The list declares its closed query set (`META_LIST_QUERY_PARAMS`: package, preview, object, include, id, names, and locale, which the list already reads), asked before arity by both transports (`metaListQueryRefusal`), so a misspelt filter is a located 400 instead of the whole list with 200. - `@objectstack/client` `meta.getItems(type, { names })`. - The spec describe of `GetMetaItemsRequestSchema.names` drops its planned marker, with its pin; the api reference is regenerated. Fixes #29236 Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016WV4cyaiN5dg5cw1zSyihP
…ngeset names both halves Refs #29236 Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016WV4cyaiN5dg5cw1zSyihP
📓 Docs Drift CheckThis PR changes 5 package(s): 38 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: ⛔ 10 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails. What this run could not see
Coarse fallback — 181 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin f177a461b7327709169153c9f694cc019e136979 && git checkout f177a461b7327709169153c9f694cc019e136979
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 447226b222467709ed74ca99eaf808c178656b33 621212ae86bbac8cb5f6a5a434b8a9c9e2dae120 && git checkout -B drift-repro 447226b222467709ed74ca99eaf808c178656b33 && git merge --no-ff 621212ae86bbac8cb5f6a5a434b8a9c9e2dae120
node scripts/docs-audit/affected-docs.mjs --json 447226b222467709ed74ca99eaf808c178656b33
|
|
domain:server#3 · os-elon-musk · session_016WV4cytQjG9X1CrCgytUM5 Contract reviewServed-tier: Inputs read: card #29236 body and all 19 comments (triage 6575623583; the measurement 6576480506; the decision 6579872432; the ruling 6580261740, letter A, quoted here as written; seat 5's execution 6580443235 and release 6591626191; the spec seat's inherited items 6594515725 and the blocker landing 6595098031; seat 3's claim 6670429730 with amendments 6670501210, 6671057079 and the re-posted claim 6671541845; os-dev report 6672708990); PR #30709 title, body, 19-file list and net diff vs ① Derived judgments
② Semver levelChangeset
③ Boundary flags
④ Design judgment
Findings (none must-fix):
Implemented-by: VERDICT: PASS |
…by id; retire the stale TSDoc paragraph Contract review 6672872746 should-fix items, per the seat's ruling: the package list refuses ?names= with a located 400 on both transports (pinned, ablated), the GetMetaItemsRequestSchema TSDoc says the member is served, the describe and http-protocol.mdx name the package exception, and the changeset spells Clause-② canonically. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016WV4cyaiN5dg5cw1zSyihP
|
domain:server#3 · os-elon-musk · session_016WV4cytQjG9X1CrCgytUM5 Contract reviewServed-tier: Delta review of record 6672872746 (PASS on ① Derived judgments
② Semver levelChangeset: the five Check-runs on ③ Boundary flags
④ Design judgmentThe refusal lives in the one decision both transports already share, so the twins cannot drift; it refuses at the key's own granularity (the type whose items have no name), the way the list already refuses an empty Findings (none must-fix):
Implemented-by: VERDICT: PASS |
|
domain:server#3 · os-elon-musk · session_016WV4cytQjG9X1CrCgytUM5 ACCEPT · #29236 · 2026-11-06T00:55Z · Head-sha: 席位核对
|
|
domain:server#3 · os-elon-musk · session_016WV4cytQjG9X1CrCgytUM5 Ruled · 2026-11-06T03:49Z · deviation (1) of ACCEPT 6673684157: Maintainer: 「同意,七个键落地」, on the seat's decision card in the seat thread, 2026-11-06T03:49Z. The landing hold is lifted: ready + auto-merge in this stroke on head |
⛔ merge queue 构建失败 — 先分诊,再决定要不要重排队列构建 42159059506 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集), 分类: 失败的 job(日志抽取,best effort):
跨 PR 相同签名(24h,按失败测试文件聚合):
历史信号:
分诊清单:
Generated by Claude Code · merge-queue-triage workflow (#4859) |
⛔ merge queue 构建失败 — 先分诊,再决定要不要重排队列构建 42163420456 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集), 分类: 失败的 job(日志抽取,best effort):
跨 PR 相同签名(24h,按失败测试文件聚合):
历史信号:
分诊清单:
Generated by Claude Code · merge-queue-triage workflow (#4859) |
Requested by Elon · project thread
domain:server#3 · os-elon-musk · session_016WV4cyaiN5dg5cw1zSyihP
Fixes #29236
Clause-②: yes (narrowing)
Metadata-protocol: none
维护者速读
/meta/:type列表接口新增?names=a,b,c,一次请求按名字读多个元数据项(package列表除外,它的条目按 id 而非名字区分,带?names=会返回 400);同时列表只认七个查询参数,拼错或不认识的参数改为返回 400,不再静默返回整张表。packages/rest/src/meta-item-read-gate.ts的META_LIST_QUERY_PARAMS、metaListNames、列表链第 2a 步;packages/runtime/src/domains/meta.ts的分发器对等实现。pnpm --filter @objectstack/rest exec vitest run src/meta-list-names-filter.test.ts src/meta-query-arity-doors.test.ts,预期全部通过。locale加入封闭集合(裁决原文是六个键,见下方偏差 1);其余无,席位验收、契约复核通过、CI 全绿后落地。What changes
@objectstack/rest, the list doorGET /api/v1/meta/:type(and its environment-scoped twin).?names=a,b,cnarrows every type's list to the items of those names, exceptpackage's: apackagelist item is keyed by its id and carries noname, so thepackagelist refuses?names=with a located400 VALIDATION_ERROR(NAMES_ON_PACKAGE_LIST_MESSAGE) on both transports, before any read. One comma-separated value, as the data routes spellfields; each name is trimmed, blanks are dropped and repeats count once (metaListNames). A name that matches no item is simply absent (no404). A value that names no item (?names=,?names=,) is refused400 VALIDATION_ERRORwithparam: 'names', before the store read. A repeated?names=is refused like the list's other single-valued keys (META_QUERY_ARITY_DOORS.listgainsnames).createMetaListAnswer: after the per-caller gate (a named item the caller may not see stays withheld) and before the object field mask and translation, which now run on the named items only.GetMetaItemsRequest.names. The chain step still narrows, so a store that does not filter answers the same list.META_LIST_QUERY_PARAMS=package,preview,object,include,id,names,locale, judged by the sameunknownQueryParamRefusalMessagethatrefuseUnknownQueryParamsuses, before the arity check and before any read (metaListUnknownParamRefusal/metaListQueryRefusal). Bracketed keys that already have a located refusal keep it:package[everywhere,id[onapp,object[onview. Any other bracketed key (?names[]=) is refused as a name the list does not read.META_LIST_QUERY_PARAMS,metaListQueryRefusal,metaListNames.@objectstack/runtime, the dispatcher twin (packages/runtime/src/domains/meta.ts): the same recognition and arity refusal, the same empty-value400, and the same push-down toprotocol.getMetaItems, all asking the rest exports. The parity census (meta-list-projection-parity.test.ts) derives the read keys from both sources and now also pins thatMETA_LIST_QUERY_PARAMSequals what the handlers actually read.@objectstack/metadata-protocol:getMetaItems/readFlattenedMetaItemstakenames?: readonly string[]and keep only those items once every source is merged, before the app nav contributions, locks, governance and serving decorations run.@objectstack/client: bothmeta.getItemsmethods (unscoped and environment-scoped) take{ names }and send it as one?names=a,bvalue.@objectstack/spec, description only:GetMetaItemsRequestSchema.namesdrops its[PLANNED — not yet served]prefix and its trailing "no list door reads it yet" sentence; the marker test is deleted.content/docs/references/api/protocol.mdxis regenerated (gen:docs).Docs: one paragraph in
content/docs/protocol/kernel/http-protocol.mdx› List All Objects on?names=and the closed set.BREAKING accept-set narrowing at the metadata list (
@objectstack/rest,@objectstack/runtime). Before:GET /meta/object?name=account,?ids=a,bor?filter=xanswered200with every object. Now: a name outside the seven is refused400with the nested{ "error": { "code": "VALIDATION_ERROR", "message" } }body naming the parameter and the supported ones, and nothing is read. Who moves: a script that sends an undeclared name; the Console and the client SDK send only declared names. Remedy: drop the parameter, or use?names=a,b,?id=APP,?object=OBJECTor?package=ID. The changeset carries the same section and anadr-0087: not-required (no-migration-prescription)disposition: no schema key moves, and nothing at rest records the route's answer.Why
Ruling A on #29236 (6580261740): one spelling,
?names=a,b,c, on every/meta/:typelist, narrowing after the per-caller gate and before the object mask, pushed down asGetMetaItemsRequest.names, with the list's closed query set declared in the same server PR. The spec half (#29441, PR #29722) putnamesonGetMetaItemsRequestSchemawith the planned marker; this PR serves it and retires the marker. Measured onmainbefore this PR (6576480506):?names=,?name=,?ids=,?filter=on/meta/objecteach answered the full 115-item, 746 KB list with200.The Console follow-up, folding the Home roster gate's by-name reads into one
?names=request, is its own card, #29444 (domain:ui). This PR does not touchui/**.Deviations (named, accepted by the seat)
localejoins the closed set. The ruling's line reads: "The same server PR declares the list door's closed set of query parameters (package, preview, object, include, id, names)". The list door already reads?locale=(extractLocalein the translation step, a frame below the handler), so a six-key set would refuse?locale=zh-CN, which answers200today. Keeping it is keeping behaviour, not widening. Pinned:?locale=zh-CNstill answers200on both transports, and the census deriveslocalefrom the source. The seat puts this to the maintainer at ACCEPT as a veto point.check-changeset-no-majorrequires every entry to bepatchwhen the PR declaresMetadata-protocol: none, which this is (an API key, not authorable metadata).http-protocol.mdxis outside the ruling's file list; the seat added it to the claim.meta-app-publish-gate.test.ts, a bracketedid[]on theviewlist andobject[]on theapplist used to answer200(ignored); they are now recognition refusals (400), as the closed set requires.?names=is refused on thepackagelist (contract review 6672872746, seat ruling):packageitems carrymanifest.id, noname, so a names filter there could only answer200 []. It is a located400instead; matching packages by id is a different behaviour, left for its own card if someone needs it. The describe andhttp-protocol.mdxsay "every type exceptpackage".Verification
Main's behaviour is the card's measurement (6576480506) and the ablations below: with the recognition, the chain step and the push-down off, the new pins go red the way main answers (the whole list,
200, nonamessent to the store).packages/rest/src/meta-list-names-filter.test.ts(new): the reading of?names=; the chain step onobject(the mask and the translation see only the named items), an unknown name absent, an empty value rejected before the mask,appnarrowed after the per-caller gate (a withheld app stays withheld); the door: push-down, a non-filtering store onobjectandview(and with?object=), nonamessent when absent, empty400with no store read, unknown name200with an empty list.packages/rest/src/meta-query-arity-doors.test.ts: the closed set, recognition before arity, every declared key accepted, undeclared and bracketed keys refused,?locale=zh-CN200.packages/metadata-protocol/src/protocol.meta-list-names.test.ts(new, 5): narrowing onobjectandapp, nav contributions only for the named apps, unknown names absent, each named item served as the whole list serves it.packages/runtime/src/domains/dispatcher-query-arity.test.tsandmeta-list-projection-parity.test.ts: the dispatcher's recognition, push-down, empty refusal andlocale, and REST/dispatcher parity on five new probes (namesnarrowing,names=nope, emptynames,names[],?name=crm).packages/client/src/client.test.ts: the?names=URL, unscoped and environment-scoped.packagerefusal:meta-query-arity-doors.test.ts(the sentence, every value spelling, recognition outranking it, controls on four other types, the door answering the nested400with no protocol read) anddispatcher-query-arity.test.ts(the dispatcher's twin). Ablated (the check off): 2 red in rest, 1 red in runtime.localedropped from the set (3), chain step off (5), chain step moved after the mask (3), rest empty refusal off (5), rest push-down off (1); dispatcher recognition off (census red), dispatcher push-down off, dispatcher empty refusal off, protocol filter off (4), clientnamesoff. All green at head.dispatch-gates --commands), first measured atc2b596ebbefore the main merge: 106 derived families plus the extras andpnpm lint, 112 run, 109 exit 0.@objectstack/speccheck:generatedfailed only becausecheck:test-typecheckran out of heap; with an 8 GB heap it is green atb0872d29(all 14 artifacts current).check:skill-examplesandcheck:dual-build-cjs-loadsexited 3 (NOT MEASURED: they need a whole-tree build). The full re-run at the merged head, after a whole-tree build, goes in the os-dev report on the card.Skills-docs: follow-up #30710
Human Minimum
packages/specholds no runtime logic.pnpm lintpasses.main, one pull request.packages/spectouched: its generated artifacts are regenerated and committed.content/docs/references/,packages/*/CHANGELOG.mdorcontent/docs/releases/.🤖 Generated with Claude Code
https://claude.ai/code/session_016WV4cyaiN5dg5cw1zSyihP