Skip to content

Metadata API: GET /api/v1/meta/app?id[]=NAME (bracketed array spelling) ignores the filter and answers the caller's full app list #28930

Closed

Description

@objectstack-fleet

domain:devx#1 · os-warren · session_01JQwxbZpHPpovxXPremPLtv

立卡门: ① 产品缺陷,具名落点 + 复现(reach: 公开入口 HTTP GET /api/v1/meta/app?id[]= 实测一次错误结果)
业务理由: 按 qs 风格序列化数组的客户端(SDK、脚本、集成方)只想取一个应用,却拿回全部应用并按它渲染;#28548 修好了空 id= 这一面,括号写法仍是同一类静默放宽。不修,调用方会把整份列表当成单个应用来读。
动手的读者: domain:server 席,/meta/app 的 ?id= 过滤(packages/rest),与 #28548 的修复同一处,附一条 HTTP pin。
查重: repo is:issue meta/app id filter 179 命中、"meta/app" id array 70(均含已关);最近邻 #28548(空 id,已关已修)、#23183、#26838 都不是括号写法。

QA-source: #28927 · platform-core.builtin-apps-nav-render · observed beside the clauses

What happens

GET /api/v1/meta/app?id[]=setup (and ?id[0]=setup) answers 200 with the caller's unfiltered app list. A client that serialises arrays qs-style silently gets every app back instead of one app or a refusal. This is the sibling of #28548 (empty ?id=), which is fixed at this subject: ?id= now answers 400 "Query parameter 'id' names no app", and a repeated id answers VALIDATION_ERROR.

Not an authorization issue: the member's unfiltered list is still pruned (setup never appears in it).

Measured at framework 7fa9873f (main, 2026-11-02) on a stock showcase boot.

Reproduction

  1. Boot the stock showcase: OS_PORT=PORT pnpm -C examples/app-showcase exec objectstack dev --ui --seed-admin --no-compile -p PORT -d file:/tmp/x/data.db.
  2. As admin: GET /api/v1/meta/app?id[]=setup → 200, items [showcase_app, setup, account].
  3. As the seeded member phone.demo@example.com (password showcase123): the same → 200, [showcase_app, account].
  4. Expected: the one named app (as ?id=setup answers), or a 400 like the empty and repeated spellings get.
  5. Actual: the unfiltered list.

Controls: ?id=setup → that one app; ?id= → 400; a repeated id → VALIDATION_ERROR; ?id=SETUP, ?id=setup%20, ?id=* → 200 items:[] (exact-name match).

Where (a guess, not traced)

RUNNER places the /meta/app ?id= filter in packages/rest/src/rest-server.ts (a.name === id). The #28548 fix refuses an empty and a repeated id; a bracketed key presumably reaches the handler under another key name, so the filter is never applied.


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Path: platform-core.builtin-apps-nav-render

    Triage: priority:p2 · domain:server · pm:queue · 2026-11-02T05:06Z

    ⛔ Not a claim.


    Generated by Claude Code

  2. added
    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
  3. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#2 · os-project-manager · session_0186vfjHHMsRXC9EUT9hTFMo

    Claim: PM loop round 4 · card thread · 2026-11-02T05:20Z
    Session: session_0186vfjHHMsRXC9EUT9hTFMo (the seat); the card thread is session_01FEkQ5o6bowfKzFDhW76JZ3
    Account: os-project-manager (the seat's linked user; the card's assignee)
    Branch: claude/project-thread-vh6ns8 (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, 2026-11-01T03:46Z: 「server 车道现在就是你负责,任务有一大堆啊,都可以派,包括 p3」, with 2026-10-26T10:29Z 「p1 p2 处理完了,就可以处理 p3」. With 2026-11-01T12:11Z 「定级你可以自己决定」 (triage graded this card) and 2026-11-02T05:15Z 「队列进了很多任务,抓紧派发」; seat 1 split the queue and gave this card to seat 2. Seat's route: refuse, with one answer. A bracketed id key gets the same 400 VALIDATION_ERROR as a repeated id (#28548's words), never the unfiltered list.
    File surface: the /meta list ?id= filter in packages/rest/src/rest-server.ts (and any /meta/:type door sharing that seam); its tests; one dogfood pin; changeset
    Container & model: M, mode:thread, model: opus (--tier names no path mandate: PM's call, default), effort: medium
    Clause-②: yes (narrowing) expected: a query answered 200 is now refused; the dev measures and declares; CONTRACT_REVIEW_TIER review
    Metadata-protocol: none (expected; ruling A #25105 ships a non-protocol narrowing at patch with the BREAKING banner; the dev measures and declares)
    Responsibility: the dev extends #28548's refusal to bracketed key spellings, red first with ablation; the seat reviews and lands
    Thread-read: none
    Premise: measured by triage (6569547911) on main
    Serial constraints cleared: none; overlap re-read of open PRs on rest-server.ts before pr_create
    Resume-from: none — fresh from origin/main

    State: → pm:dispatched, assigned to os-project-manager, recorded by this claim.


    Generated by Claude Code

  4. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#2 · os-project-manager · session_01FEkQ5o6bowfKzFDhW76JZ3
    os-dev-report
    {
    "issue": 28930,
    "status": "done",
    "branch": "claude/project-thread-vh6ns8",
    "pr": "#29046",
    "session": "session_01FEkQ5o6bowfKzFDhW76JZ3",
    "premise_still_valid": true,
    "summary": "GET /meta/app (and /meta/apps) now refuses any query key that opens id[ (?id[]=NAME, ?id[0]=NAME, ?id[x]=) with 400 VALIDATION_ERROR naming the key, no list, in the empty ?id= refusal's sentence frame (#28548). Both refusals go through one builder; the empty id/object messages are byte-identical. The step lives in createMetaListAnswer step 3 (packages/rest/src/meta-item-read-gate.ts, not rest-server.ts as the card guessed), after the per-caller gate, so RestServer and the runtime dispatcher answer it identically. Only the app list reads id, so the fix is /meta/app only; ?object[]= on the view list is the same class, measured and reported, not fixed. Declared Clause-② no (narrowing), not the order's expected yes: nothing published is widened (same as #28548's own declaration); Metadata-protocol none, patch, BREAKING banner, FROM ?id[]=NAME TO ?id=NAME. The bracketed refusal does not reuse repeatedQueryParamMessage, whose 'supplied N times' would be false for a key sent once.",
    "tests": "HEAD 2055ce2. rest: pnpm --filter @objectstack/rest test exit 0, Test Files 366 passed (366), Tests 6940 passed | 332 skipped; rest typecheck exit 0. runtime parity (repo-project file, run directly): 991/991 exit 0; runtime typecheck exit 0. dogfood meta-read-doors-live: 14/14. Ablation: call replaced with 'void refuseBracketedAppIdFilter;' via ablation-replace (anchor 1->0, blob 9c8674a8->b9866803), rest rebuilt exit 0, dist-preflight --absent exit 0 (marker 'refuseBracketedAppIdFilter(query);' absent from 6 built files); reds exactly the new cases: rest 3 failed|28 passed (BRACKETED id[]/id[0]/id[name]), runtime parity 1 failed|990 (reference-moves test; row A stays equal since both transports gave the same unfiltered list), dogfood 2 failed|12 (?id[]=setup and ?id[0]=setup got 200 with [showcase_app]). Restore: blob==HEAD, git diff HEAD empty, rebuild exit 0, preflight present in 2 files, tree clean. A first ablation that deleted the line made the unused function fail the rest DTS build (exit 1); discarded. Dogfood stack has no setup app (only showcase_app for both callers), so the control narrows on showcase_app and ?id=setup -> [].",
    "gates": "Local derived set (dispatch-gates --commands, 64 commands) all exit 0 at 2055ce2; --ran: '64 derived famil(ies) accounted for — 64 run, 0 NOT-MEASURED'. First-pass exit 3s were prerequisites, re-run green: check-plugin-teardown-shape --self-test after git fetch --depth=1 origin 621a487; check:dual-build-cjs-loads after building 7 missing dists (studio, client-react, embedder-openai, knowledge-memory, knowledge-ragflow, service-cluster-redis, service-knowledge). Record: /mnt/project-files/pm-orders/server/28930-gates-ran.txt. CI check-runs at report time (PR #29046): all in_progress or queued (Check Changeset, Type Check x4, Lint x9, PR-body guards, Governed Surface Queue Guard, Spec property liveness, Check PR Size, Flag docs affected); skipped: 18.x upgrade harness, Schedule gate, Packed-tarball smoke. Not run locally: test:repo beyond the one edited parity file, pnpm lint (CI-owned).",
    "dogfood_battery": "2055ce23 · os-verify-lock: VERDICT command-exit 0 · Test Files 383 passed | 3 skipped (386) · Tests 3166 passed | 28 skipped (3194) · no failures",
    "mcp_calls": "8 — issue_read x3 (get, get_comments x2), pull_request_read x3 (get_files #28602, get_files #28731, get_check_runs #29046), list_pull_requests x1; no write tools",
    "api_writes": "1 — fleet-write relay pr_create (POST /repos/objectstack-ai/objectstack/pulls, run 41399602096) → #29046; plus this os-dev-report comment (POST /repos//issues/28930/comments via post-stamped)",
    "open_questions": [],
    "out_of_scope_findings": [
    "class: a · reach: RestServer GET /meta/:type route handler, view list with a three-view fixture: ?object[]=lead -> 200 [all_leads, acct_view, loose_view] where ?object=lead -> 200 [all_leads]; the adapters hand the bracketed key through as spelled, as measured for id[] over HTTP on the dogfood stack · evidence: createMetaListAnswer step 4 reads query.object only · same family as #28930 (qs-style array key bypasses a one-value list filter); package[] on the list doors not measured · dedupe words: view object[] bracketed filter, meta view list object array, qs array key meta list",
    "carrier: none · noted in PR #29046 Acceptance notes: open PR #28850 also edits meta-read-doors-live.dogfood.test.ts (header comment and acceptance[2] setup); this PR appends a describe at the end, hunks disjoint"
    ]
    }


    Generated by Claude Code

  5. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#2 · os-project-manager · session_0186vfjHHMsRXC9EUT9hTFMo

    Release: session_0186vfjHHMsRXC9EUT9hTFMo · cause: the maintainer's wrap-up (「进展中的开发先提交代码然后暂停,先确保已有的pr都合并」, 「草稿的也交给下一任,在座位帖写清楚」); seat 2 signs off · next: PR #29046 is ACCEPTed (6570953324) with review PASS (6570942095) and is in the merge queue; Fixes #28930 closes this card on merge. Next: the next seat posts the landing record and files the ?object[]= view-list follow-up card. ⛔ Do not re-dispatch while #29046 is queued.
    Resume-from: claude/project-thread-vh6ns8@2055ce2324552f66a7654791d750177a9859837d

    State: pm:dispatched → pm:queue, assignee cleared, in this same relay stroke. Seat hand-off: mailbox #28284 comment 6571506250; seat post #20966.


    Generated by Claude Code

  6. objectstack-fleet commented on Nov 2, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#2 · os-steve · session_01ADHkmQxwoGb5fSnrFkpxmt

    Claim: PM loop round 1 · seat takeover · 2026-11-02T08:10Z
    Session: session_01ADHkmQxwoGb5fSnrFkpxmt (the seat)
    Account: os-steve (the seat's linked user; the card's assignee)
    Branch: claude/project-thread-vh6ns8 (continued from the released claim; PR #29046)
    Worktree: the card thread's own container checkout
    Domain: domain:server
    Seat: domain:server#2
    Ruling-ref: the maintainer, verbatim: 「开 server 2、3、5 号席位, 前任已经下班,接手所有开发,包括跟进之前未完成的开发落地。」 (os-steve project "objectstack spec"). Takes over the hand-back Release: 6571737801 of seat 2's previous term (session_0186vfjHHMsRXC9EUT9hTFMo).
    File surface: the /meta list ?id= filter in packages/rest/src/rest-server.ts; its tests; one dogfood pin; changeset (unchanged from claim 6569718113)
    Container & model: M, mode:thread, model: opus (as the previous claim), effort: medium
    Clause-②: yes (narrowing) — as declared on PR #29046; CONTRACT_REVIEW_TIER review PASS 6570942095 on record
    Metadata-protocol: none — as declared on PR #29046
    Responsibility: the dev extended #28548's refusal to bracketed key spellings (claim 6569718113); the seat follows PR #29046 to MERGED and files the ?object[]= view-list follow-up
    Thread-read: 6571737801
    Premise: holds (measured by the previous term's dev; re-read on the PR head before ACCEPT)
    Serial constraints cleared: none new; the PR's file list is re-read against live claims before landing
    Resume-from: claude/project-thread-vh6ns8@2055ce2324552f66a7654791d750177a9859837d

    Status: PR #29046 is ACCEPTed (6570953324) and in the merge queue; no new dev work. ⛔ No re-dispatch while it is queued.

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

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