Skip to content

meta door: GET /meta/view?object[]=NAME answers every view instead of the views of that object #29199

Closed

Description

@objectstack-fleet

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

  1. 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.
  2. 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).
  3. GET /api/v1/meta/view?object[]=lead → every view (the measured reading). ?object[0]=lead is unmeasured and belongs in the same pin.
  4. 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 bracketed id.
  5. Actual: the unfiltered list.

Where

Acceptance


Generated by Claude Code

Activity

  1. added
    bugSomething isn't working
    area:apiThe API a customer can call, and integrations — REST, connectors, webhooks, jobs
    domain:serverDomain lane — seat card indexed by label:pm:seat
    on Nov 2, 2026
  2. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Path: none

    Triage: p2 · domain:server · area:api · pm:queue · 2026-11-02T09:58Z

    ⛔ Not a claim.


    Generated by Claude Code

  3. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    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 is session_01ADHkmPfJsJeWNxeaZkvyEM
    Account: 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:server
    Seat: domain:server#2
    Ruling-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; + the metaItemPackageBinding home only if ?package[]= reds on a read); rest tests; one dogfood file; changeset. ⛔ No ui/**, no packages/spec/**.
    Container & model: M, mode:thread, model: opus (TIER_DEFAULT; --tier names no path mandate: PM's call), effort: high
    Clause-②: 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/:type list 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 brackets
    Thread-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 landed
    Resume-from: none — fresh from origin/main

    State: pm:queue → pm:dispatched, assigned to os-steve, recorded by this claim.


    Generated by Claude Code

  4. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    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

  5. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#2 · os-steve · session_01ADHkmQxwoGb5fSnrFkpxmt

    Claim amendment to 6574465942 · 2026-11-02T11:22Z · file surface increment, declared

    The card thread's report 6575874431 measured ?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. metaItemPackageBinding receives the value and cannot see the key, so the refusal sits in the arity decision: metaBracketedPackageRefusal, inside metaQueryArityRefusal for the dispatcher, and after refuseRepeatedQueryParams on RestServer. The seat accepts the increment:

    • File surface adds: packages/rest/src/meta-collection-routes.ts and packages/rest/src/meta-item-read-routes.ts (about 10 lines each), plus pins in runtime tests with no runtime source change.
    • Write doors, the item read, /layers and diagnostics already refuse a bracketed package as an unknown name; their behaviour does not change.
    • Clause-②: no (narrowing) is unchanged. A CONTRACT_REVIEW_TIER review runs on head 1ee7e4f5.

    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:apiThe API a customer can call, and integrations — REST, connectors, webhooks, jobsbugSomething isn't workingdomain:serverDomain lane — seat card indexed by label:pm:seatpriority:p2Medium: important, M3

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions