Repository navigation
Description
domain:server#2 · os-steve · session_01ADHkmQxwoGb5fSnrFkpxmt
立卡门: ① 产品缺陷,具名落点 + 复现(reach: 公开入口 HTTP GET /api/v1/meta/view?object[]=lead 实测一次错误结果,由 PR #29046 的开发在其 head 上测得,见该 PR 的合约复审记录 ③)
业务理由: 按 qs 风格序列化数组的客户端(SDK、脚本、集成方)只想取某个对象的视图,却拿回全部视图并按它渲染;#28930 已在 /meta/app 的 id 键上修好同一类静默放宽,object 键还开着。不修,调用方会把全部对象的视图当成一个对象的视图来读。
动手的读者: domain:server 席,packages/rest/src/meta-item-read-gate.ts 的 createMetaListAnswer 第 4 步(object 过滤),与 #29046 的修复同一条链,附一条 HTTP pin。
查重: meta/view object[] 4 命中、object[] view list every view 4 命中(含已关),没有括号写法的视图列表卡;最近邻 #28930(id 键,已由 #29046 关闭)、#28548(空 id=,已关)。
Follow-up of: #28930 (landing PR #29046, landing record on that PR)
What happens
GET /api/v1/meta/view?object[]=lead answers 200 with every view the caller can read, not the views of lead. This is the same defect class #28930 fixed for /meta/app?id[]=: a bracketed key reaches the list chain under another key name, so the filter that reads object is never applied.
Reproduction
- Boot the stock showcase as in Metadata API: GET /api/v1/meta/app?id[]=NAME (bracketed array spelling) ignores the filter and answers the caller's full app list #28930 step 1.
- As admin:
GET /api/v1/meta/view?object=lead→ the views of lead (control, by the filter's contract; not re-measured by the seat). GET /api/v1/meta/view?object[]=lead→ every view (the measured reading).?object[0]=leadis unmeasured and belongs in the same pin.- Expected: the views of
lead, or the same 400 that fix(rest)!: GET /meta/app refuses a bracketed id key (?id[]=, ?id[0]=) instead of answering every app #29046 gives a bracketedid. - Actual: the unfiltered list.
Where
createMetaListAnswerstep 4 readsobject. fix(rest)!: GET /meta/app refuses a bracketed id key (?id[]=, ?id[0]=) instead of answering every app #29046'srefuseBracketedAppIdFilterhard-codes theid/appkeys at step 3; a(param, names, lists)twin at step 4 would serveobject[the same way (the contract reviewer's reading on fix(rest)!: GET /meta/app refuses a bracketed id key (?id[]=, ?id[0]=) instead of answering every app #29046).- A third site, unmeasured:
package[.metaItemPackageBinding(req.query?.package)readspackageat the door entry, before the chain, on both transports, so an unread bracketed key may read as "every package". Measure it in the same PR and fix it there if it reds.
Acceptance
?object[]=and?object[0]=on the view list get one answer (the filtered list or a 400 in fix(rest)!: GET /meta/app refuses a bracketed id key (?id[]=, ?id[0]=) instead of answering every app #29046's frame); pinned by an HTTP test that reds on main.?package[]=measured; if it answers every package, the same fix and pin.- This is a narrowing of a
/metaread door:!title, BREAKING banner,adr-0087:marker, dogfood, as fix(rest)!: GET /meta/app refuses a bracketed id key (?id[]=, ?id[0]=) instead of answering every app #29046 did.
Generated by Claude Code
Activity
objectstack-fleet commentedon Nov 2, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: none
Triage: p2 ·
domain:server·area:api·pm:queue· 2026-11-02T09:58Z⛔ Not a claim.
GET /meta/view?object[]=NAME(the bracketed array spelling) answers every view instead of that object's views. It is the same class as Metadata API: GET /api/v1/meta/app?id[]=NAME (bracketed array spelling) ignores the filter and answers the caller's full app list #28930's/meta/app?id[]=: a filter silently skipped, so the result is wider.packages/rest, p2. Read Metadata API: GET /api/v1/meta/app?id[]=NAME (bracketed array spelling) ignores the filter and answers the caller's full app list #28930's fix (PR fix(rest)!: GET /meta/app refuses a bracketed id key (?id[]=, ?id[0]=) instead of answering every app #29046) first, so both doors share one query-key reading.Generated by Claude Code
objectstack-fleet commentedon Nov 2, 2026
domain:server#2 · os-steve · session_01ADHkmQxwoGb5fSnrFkpxmt
Claim: PM loop round 1 · card thread · 2026-11-02T10:08Z
Session:
session_01ADHkmQxwoGb5fSnrFkpxmt(the seat); the card thread issession_01ADHkmPfJsJeWNxeaZkvyEMAccount:
os-steve(the seat's linked user; the card's assignee)Branch:
claude/project-thread-9szbaq(the card thread's harness-pinned branch; it names no issue, so this claim is the identity record)Worktree: the card thread's own container checkout
Domain:
domain:serverSeat:
domain:server#2Ruling-ref: the maintainer, verbatim: 「开 server 2、3、5 号席位, 前任已经下班,接手所有开发,包括跟进之前未完成的开发落地。」 Card: none from the maintainer on this card; filed by this seat as a landing follow-up, graded p2 by triage.
File surface:
packages/rest/src/meta-item-read-gate.ts(createMetaListAnswer; + themetaItemPackageBindinghome only if?package[]=reds on a read); rest tests; one dogfood file; changeset. ⛔ Noui/**, nopackages/spec/**.Container & model:
M,mode:thread,model: opus(TIER_DEFAULT;--tiernames no path mandate: PM's call),effort: highClause-②: no (narrowing; expected: a REST/meta read door refuses a bracketed key, no spec accept set moves)
Metadata-protocol: none (expected)
Responsibility: the code is the
/meta/:typelist chain in@objectstack/rest· the platform path is a qs-style client asking for one object's views and getting every view · reached by any SDK, script or integration that serialises arrays with bracketsThread-read: none
Premise: holds (measured by the dev of the parent PR; the card thread re-measures red first on current
origin/main)Serial constraints cleared: none — no open PR touches
meta-item-read-gate.ts(read this act); #29046 has landedResume-from: none — fresh from
origin/mainState:
pm:queue→pm:dispatched, assigned toos-steve, recorded by this claim.Generated by Claude Code
objectstack-fleet commentedon Nov 2, 2026
domain:server#2 · os-steve · session_01ADHkmPfJsJeWNxeaZkvyEM
os-dev-report
{ "issue": 29199, "status": "done", "branch": "claude/project-thread-9szbaq", "pr": "https://github.com/objectstack-ai/objectstack/pull/29298", "session": "session_01ADHkmPfJsJeWNxeaZkvyEM", "premise_still_valid": true, "summary": "Step 4 of createMetaListAnswer now refuses a bracketed view `object` key (`object[]`, `object[0]`, any `object[`) with #29046's 400 frame; #29046's id-only helper became refuseBracketedListFilter(query, param, names, lists), and the id/empty messages are byte-identical. `package[]` measured red on main on the list (every type) and the book tree, on both transports (answered across every package); every write door, the item read, /layers and diagnostics already refused it as an unknown name, so no write behaviour changes. The package refusal sits in the arity decision (metaBracketedPackageRefusal, asked by metaQueryArityRefusal for the dispatcher and right after refuseRepeatedQueryParams on RestServer's list and tree), because metaItemPackageBinding receives the value and cannot see the key. Producer census: client, console and docs send neither key bracketed.", "tests": "Red first on main 45e9b2f1 (pins commit 919e71ba): rest `Tests 13 failed | 58 passed (71)` (3 object[…] + 10 package cases, 'expected 200 to be 400'); runtime `Tests 6 failed | 1174 passed (1180)`. Green at 1ee7e4f5: rest test `Test Files 367 passed (367) · Tests 6980 passed | 332 skipped (7312)`; rest/runtime/dogfood typecheck exit 0; runtime arity+parity `Tests 1180 passed (1180)`; dogfood file `Tests 19 passed (19)`. Ablation via ablation-replace + rest build + dist-preflight (marker in 2 built files): object arm -> rest 3 failed/35, runtime 2 failed/1180, dogfood 2 failed/19; package arm -> rest 10 failed/36, runtime 5 failed/1180, dogfood 2 failed/19; each restore blob == HEAD 894bc70c, git diff HEAD empty, marker absent from 6 built files, tree clean. Lint: root eslint --no-inline-config over packages/rest, packages/runtime, packages/qa/dogfood exit 0.", "gates": "Local: dispatch-gates --commands at 1ee7e4f5 = 64 commands, all exit 0; --ran: `64 derived famil(ies) accounted for — 64 run, 0 NOT-MEASURED`. First-pass exit 3 prerequisites re-run green: check-plugin-teardown-shape --self-test (fixture commit 621a4876 fetched), check:dual-build-cjs-loads (7 dists outside the dogfood closure built), check:type-check-debt (its closure build queued out behind the dogfood battery; re-run under the lock, 0 raw tsc errors). CI on PR #29298 head 1ee7e4f5: in_progress at report time (opened 11:20Z); not waited on.", "dogfood_battery": "1ee7e4f5 · os-verify-lock VERDICT command-exit 0 · Test Files 389 passed | 3 skipped (392) · Tests 3220 passed | 28 skipped (3248) · no failures", "mcp_calls": "6 — issue_read get #29199, issue_read get_comments #29199 x3, pull_request_read get_comments #29046, pull_request_read get #29298 (all reads)", "api_writes": "2 — relay pr_create (#29298, 14091 bytes sent = stored), this os-dev-report comment via post-stamped.mjs", "files_changed": [ ".changeset/29199-bracketed-object-package-refused.md", "packages/rest/src/meta-item-read-gate.ts", "packages/rest/src/meta-collection-routes.ts", "packages/rest/src/meta-item-read-routes.ts", "packages/rest/src/meta-app-publish-gate.test.ts", "packages/rest/src/meta-query-arity-doors.test.ts", "packages/runtime/src/domains/dispatcher-query-arity.test.ts", "packages/runtime/src/domains/meta-list-projection-parity.test.ts", "packages/qa/dogfood/test/meta-read-doors-live.dogfood.test.ts" ], "line_budget": "+342 / -31 over 9 files; source +117/-31 (meta-item-read-gate.ts +97/-31, two door hand-offs +10 each), tests +206/-0, changeset +19", "deviations": [ "File surface: the claim named rest-server.ts (metaItemPackageBinding's home) for the package arm; the binding receives the value of query.package, which is undefined for a bracketed key, so the refusal sits in the arity decision (meta-item-read-gate.ts) with 10-line hand-offs in meta-collection-routes.ts and meta-item-read-routes.ts. rest-server.ts untouched.", "Tests added in packages/runtime (dispatcher-query-arity, meta-list-projection-parity) beside the expected rest tests, to pin the dispatcher transport; no runtime source changed.", "Dogfood red-first was read off the ablation legs (each arm's code equals main's for that arm), not from a separate main run of the dogfood file.", "origin/main moved 22 commits after the 1ee7e4f5 merge; none touches a file this PR edits, so no second merge before opening." ], "open_questions": [ { "question": "MetaItemQueryRefusal.rule gains 'bracketed-parameter'. The interface is not exported from @objectstack/rest's index, but it is the return type of the exported metaQueryArityRefusal / metaItemQueryRefusal, so the d.ts union is one member wider. Declared Clause-②: no (narrowing). Does the reviewer read that as a published widening?", "options": [ "A keep 'bracketed-parameter' and Clause-②: no (narrowing): no transport writes `rule` to the wire, and the only readers compare it to 'layered-member'", "B keep the member and re-declare Clause-②: yes (narrowing) (widens AND narrows)", "C drop the member and tag the refusal 'unknown-parameter'" ], "recommendation": "A, because the tag is internal routing data with no wire face and both readers are inequality checks; C would mislabel a recognised name as unknown" } ], "out_of_scope_findings": [ "class: a (inferred, not measured) · reach: none measured — GET /meta/_drafts reads ?packageId= and ?type= with no closed set, so packageId[] / type[] very likely answer every package / every type; ?preview[]=draft and ?include[]= on the list read as absent · evidence: META_QUERY_ARITY_DOORS.drafts = ['packageId','type'] with no recognition rule; same adapter pass-through as this card · dedupe words: bracketed _drafts packageId, type[] drafts list, qs array meta door · carrier: none · noted in PR #29298 Acceptance notes, not filed" ] }Stamped 2026-11-02T11:21Z.
Generated by Claude Code
objectstack-fleet commentedon Nov 2, 2026
domain:server#2 · os-steve · session_01ADHkmQxwoGb5fSnrFkpxmt
Claim amendment to
6574465942· 2026-11-02T11:22Z · file surface increment, declaredThe card thread's report
6575874431measured?package[]=red on main on the list door (every type) and on the book tree, over both transports. The order said to refuse it in this PR if a read reds.metaItemPackageBindingreceives the value and cannot see the key, so the refusal sits in the arity decision:metaBracketedPackageRefusal, insidemetaQueryArityRefusalfor the dispatcher, and afterrefuseRepeatedQueryParamson RestServer. The seat accepts the increment:packages/rest/src/meta-collection-routes.tsandpackages/rest/src/meta-item-read-routes.ts(about 10 lines each), plus pins in runtime tests with no runtime source change./layersand diagnostics already refuse a bracketedpackageas an unknown name; their behaviour does not change.Clause-②: no (narrowing)is unchanged. A CONTRACT_REVIEW_TIER review runs on head1ee7e4f5.Generated by Claude Code