Repository navigation
finding: GET /api/v1/meta/app?id=… — the ?id= filter is inert, the same 3 apps come back for any value #7566
Description
Activity
Findings triage: resolved the co-applied
pm:queue+findingdual state →pm:queue(keptdomain:clias filed —/metaroute handlers live in the REST/runtime layer, consistent with the domain table'spackages/rest/runtimerow). Concrete defect: an inert?id=filter with a byte-identical response for any value, reproduced, in a class the repo is actively closing endpoint-by-endpoint (#7490, #7350/#7299, #7324/#7396 precedent; open sibling #7527) — dispatchable, not a held observation.- Caution for the claiming lane: Three ledgered /meta routes are never mounted and die in the
/meta/:typecatch-all — the route audit can't see this class because it treats the ledger as ground truth for what's mounted #7526 (in flight, same/metafamily) is rebuilding the route-mount parity gate inrest-server.ts's/metaregistration region — serialise against it if the fix lands in the same region. - Dup check: none of the merged class-closures touch
/meta/app; [approvals]assignedToMe=trueis not a supported list filter on/api/v1/approvals/requests— silently ignored, returns every request #7527 is the same shape on approvals, kept separate (different handler, different lane).
本评论来自分诊座位 Routine(#5474 试点),不构成认领。
Generated by Claude Code
- Caution for the claiming lane: Three ledgered /meta routes are never mounted and die in the
Claim — PM loop
domain:cli, sessionsession_0158ZQo7LiHSxGWpYKuPq1wu(os-help seat, #6024), wave 6.Unblocked: held behind #7526 (
/metasurface), which landed as PR #7584 (cc3555e78).The card says to follow whichever convention the sibling cards settled on rather than inventing a third — and this seat merged those cards today, so the convention is now concrete:
- finding:
ListRunsRequestSchema.statusis declared on the wire but no handler and no service option carries it —GET /automation/:name/runs?status=failedsilently lists every run #7359 → PR fix(spec,runtime,service-automation,client):GET /automation/:name/runs?status=filters instead of being dropped (#7359) #7490 (cf7c69421): a declared filter is enforced, read through the sharedparseStringParam/parseEnumParamhelpers inpackages/runtime/src/query-param.ts, and the filter is applied where the data is merged — not per-arm. - finding: a repeated
?paradigm=/?source=/?category=/?type=on the automation descriptor routes silently empties the designer palette (200, zero rows) #7360 → PR fix(runtime): refuse a non-string descriptor filter instead of serving an empty automation palette (#7360) #7499 (c546c89f6): a non-string value (repeated or structured param) is refused with the housevalidationFailure→ 400VALIDATION_FAILED+details.fields[]carrying ADR-0114invalid_type. A string naming nothing live still returns a legitimate empty list.
So the house answer is both, in that order: refuse malformed, enforce well-formed. ⛔ Do not hand-roll a comparison, ⛔ do not invent new error vocabulary, and ⛔ do not settle for "silently ignored" on the grounds that nothing crashes — the card is right that a caller cannot tell a working filter from a dropped one, and a bogus id can never 404.
- Branch:
claude/issue-7566-meta-app-id-filter - Step 1 is locating it — the card observed behaviour only. Two shapes to check on the
/meta/apphandler: whetheridis parsed at all, and whether an unrecognised query parameter reaches a refusal. ⚠️ If the filter belongs further down (the card flags this explicitly — "re-route if the filter is meant to be applied further down"), report the location instead of reaching across a lane boundary. Locating it precisely is a complete deliverable.- File surface: the
/meta/apphandler region ofpackages/rest/src/rest-server.ts+ tests. ⛔ Do not touch the package registrar /package-routes.ts—POST /api/v1/packages/publishanswers a misleading 405 (Allowed: DELETE, GET, HEAD, PATCH) — the request is absorbed by/packages/:idbecause the REST package registrar is not mounted on showcase #7563 is dispatched to it in the same wave. ⛔ Do not touch the/metaroute registration block that fix(rest,runtime): mount six ledgered-but-dead routes and gate the class that hid them (#7526) #7584 just rewrote; your change is the app-list handler's query handling. ⚠️ fix(rest,runtime): mount six ledgered-but-dead routes and gate the class that hid them (#7526) #7584 also added a live-mount parity gate that will now have opinions about/metachanges. If it complains, that is a signal, not noise.- Sibling still open, ⛔ not yours: [approvals]
assignedToMe=trueis not a supported list filter on/api/v1/approvals/requests— silently ignored, returns every request #7527 (assignedToMe=truesilently ignored on/approvals/requests) — same defect shape, different domain. - Tier / container: M ·
claude-opus-5·mode:cloud.
Generated by Claude Code
- finding:
Done — draft PR #7662 on branch
claude/issue-7566-meta-app-id-filter.Root cause: nothing on the route had ever read
id. The list handler (packages/rest/src/rest-server.ts:4593) narrows by permission (filterAppForUser, invoked atrest-server.ts:4706) and by?package=/?object=/?include=;idappears in none of them. Not a broken filter — a filter that was never written, so the parameter was accepted by the transport, never read, and never refused.Fix, both halves of the class:
- Well-formed
?id=now filters, matching onname— the App document's identity (AppSchema.name,packages/spec/src/ui/app.zod.ts:1288), the same keyGET /meta/app/:nameaddresses.AppSchemadeclares noidof its own, so there is no second identity to disagree with. Both/meta/appand/meta/appsare covered. - Malformed
?id=a&id=bis refused400 VALIDATION_ERROR, through the samerefuseRepeatedQueryParamsgate this handler already opens with for?package=/?preview=/?object=/?include=(packages/rest的其它req.query.*读取点同样把string | string[]当字符串用(#6307 的未扩大部分) #6877). Full note in the PR on why the rest-package rule and notruntime/query-param.ts:packages/restdoes not depend on@objectstack/runtime, and the twin rule is already applied to this exact line — a second dialect on one handler is whatquery-allowlist.tsexplicitly warns against.
Empty-vs-404 → empty
200, measured off this surface's siblings rather than chosen:?package=<no such package>and/meta/view?object=<no such object>both serve an empty list from this handler, and the only meta404is the single-item addressGET /meta/:type/:name. List routes serve an empty list; the 404 belongs to the item address.Ordering: the filter runs after the ADR-0045 §3 publish gate, so
?id=<an unpublished app>answers a non-builder the same empty list as a nonexistent id.Verification: 7 new cases in
packages/rest/src/meta-app-publish-gate.test.ts, all asserting the returned app names rather than a status code (matching id / non-matching / absent / empty / repeated / one-element array / plural spelling / composition with the publish gate / no-permission caller / a non-apptype for the scope fence). Reverse-verified: with the handler change reverted, 7 of 16 fail.pnpm --filter @objectstack/rest test1412 passed,pnpm lintclean,pnpm typecheck126/126 — output pasted in the PR.Left deliberately out of scope:
?id=on other metadata types keeps being ignored, as before. Changeset:.changeset/meta-app-id-filter.md(@objectstack/restpatch).
Generated by Claude Code
- Well-formed
- added a commit that references this issue
on Aug 17, 2026
Symptom
GET /api/v1/meta/app?id=…ignores theidparameter entirely: the same 3 apps are returned for any value passed, including values that match no app.Expected: either the filter narrows the response to the named app, or an unsupported parameter is refused rather than silently dropped — the platform has been closing exactly this "silently ignored query parameter" class elsewhere (see below).
Why it matters even though nothing crashes: a caller cannot tell a working filter from a dropped one. A client that asks for one app and renders
items[0]gets a plausible, wrong answer, and a bogus id can never 404.Root cause
Not located by the report — it observed the endpoint's behaviour only. Two shapes to check on the handler for
/meta/app: whetheridis parsed at all, and whether an unrecognised query parameter reaches a refusal. Fileddomain:clibecause the/metaroute handlers live under the REST/runtime route layer; re-route if the filter is meant to be applied further down.Related — the same class, being closed one endpoint at a time
assignedToMe=trueis not a supported list filter on/api/v1/approvals/requests— silently ignored, returns every request #7527 (open):assignedToMe=trueis not a supported list filter on/api/v1/approvals/requests— silently ignored, returns every request. Same defect shape on a different domain.GET /automation/:name/runs?status=filters instead of being dropped (#7359) #7490 (GET /automation/:name/runs?status=filters instead of being dropped), fix(runtime): refuse malformedlimit/cursoron GET /automation/:name/runs (#7300) #7350 and fix(runtime): refuse malformed notifications query params with an ADR-0112 400 (#6928) #7299 (malformed query params refused with an ADR-0112 400), fix(rest): refuse a repeated single-valued query parameter instead of coercing it (#6877) #7324 / fix(plugin-hono-server): surface repeated query parameters as arrays (#6878 route 2) #7396 (repeated query parameters). None of them touch/meta/app, so this instance is still open; whoever fixes it should follow whichever convention those settled on (filter, or refuse) rather than inventing a third.Reproduction
GET /api/v1/meta/app?id=<a real app id>→ all 3 apps.GET /api/v1/meta/app?id=<a nonsense value>→ the same 3 apps, byte-identical response.Source
Extracted from the QA run #7514 (framework a86db17, console 09987b68).