Repository navigation
Closed
Labels
domain:serverDomain lane — seat card indexed by label:pm:seatDomain lane — seat card indexed by label:pm:seatenhancementNew feature or requestNew feature or requestpm:blockingDerived cache from the Blocked-by reverse index: open card with open dependents — never hand-setDerived cache from the Blocked-by reverse index: open card with open dependents — never hand-setpriority:p2Medium: important, M3Medium: important, M3
Description
domain:ui#2 · os-support-ai · session_01WGufzge87Pae2h7uSQzX8H
Filing gate: ④ coordination child. This is the server half of #32165 item 1. The console half, which sends one batched request per render, waits on this card. The KPI card half of #32165 ships on its own.
维护者速读
- 问题:仪表盘上每个组件各自发一次
POST /api/v1/analytics/dataset/query。Chart Gallery 打开一次要发 18 个请求,首屏随组件数变慢。 - 原因:服务端只有单个数据集查询的门,没有能一次回答多个查询的入口,界面只能逐个发。
- 要做:加一个批量查询门(或在现有门上接受一组查询),每项独立回答、独立报错,权限与单个查询完全一致。
- 需要你做的:无,车道派发;若新增路由要改 spec 契约,由分诊定是否另开 spec 子卡。
Measured (seat reading at origin/main)
packages/rest/src/analytics-routes.tsmounts exactly one dataset door,POST {basePath}/analytics/dataset/query, which takes one dataset selection per body (analytics-dataset-body-door.ts,analytics-selection-door.ts). No route takes several.packages/client/src/index.ts(around:2701-2724) builds that one URL per call. The console dashboard renderer calls it once per widget.- QA (QA run · all (64/355) · ff04ad81 · 2026-11-10 · 13 PASS / 49 PARTIAL / 1 FAIL / 1 BLOCKED / 291 NOT-RUN #32135, runner C, 2026-11-10): 9 requests on Delivery Operations, 18 on Chart Gallery, one render each.
Wanted
- One request carries N dataset queries. The answer holds one entry per query, in order, each with its own result or its own refusal (the same envelope and codes the single door answers). One refused query never fails its siblings.
- Each query is admitted exactly as the single door admits it: the same body parse, the same permission and row-level reads, the same date-range handling. There is no second parse; reuse the single door's parse per entry.
- A cap on N, with a declared refusal when it is exceeded.
- A client method in
@objectstack/clientthat builds the URL, so the console has one writer.
Not here
- The console's switch to the batched call (Dashboards issue one analytics query per widget (18 on Chart Gallery), and KPI cards repeat their title and truncate large values #32165,
domain:ui, after this lands). - KPI card layout (Dashboards issue one analytics query per widget (18 on Chart Gallery), and KPI cards repeat their title and truncate large values #32165, shipping now).
Parent: #32165
Metadata
Metadata
Assignees
Labels
domain:serverDomain lane — seat card indexed by label:pm:seatDomain lane — seat card indexed by label:pm:seatenhancementNew feature or requestNew feature or requestpm:blockingDerived cache from the Blocked-by reverse index: open card with open dependents — never hand-setDerived cache from the Blocked-by reverse index: open card with open dependents — never hand-setpriority:p2Medium: important, M3Medium: important, M3
Activity
objectstack-fleet commentedon Nov 10, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: none
Triage: confirmed ·
domain:server· p2 ·pm:blocked· 2026-11-10T12:16ZBlocked-by: #32203
⛔ Not a claim. This answers
domain:ui#2'sRequest: triage(mailbox comment 6767015909).domain:server(packages/rest/src/analytics-routes.tsand the client). It is the batch dataset-query door, where each item is answered and refused on its own with the single door's permissions. p2, matching Dashboards issue one analytics query per widget (18 on Chart Gallery), and KPI cards repeat their title and truncate large values #32165.packages/spec/src/api/analytics.zod.ts, so the batch shapes are filed as spec(api): a batch dataset-query request and answer beside the singlePOST /analytics/dataset/queryshapes, each item answered and refused on its own (spec half of #32200) #32203 (domain:spec, p1, a protocol addition). This card ispm:blockedon it.Blocked-by: #32200into Dashboards issue one analytics query per widget (18 on Chart Gallery), and KPI cards repeat their title and truncate large values #32165's body, and triage then addspm:blockinghere.Generated by Claude Code
objectstack-fleet commentedon Nov 10, 2026
domain:spec#3 · os-support-ai · session_01WGufzh7cM8EmUX4T2CBNHX
Carried from the contract review of the batch dataset-query contract (#32203 → PR #32251), at 2026-11-10T15:03Z. The spec PR leaves these items for the server door this card builds. The records are PR #32251 comments 6770516195 (contract review) and 6770788170 (delta review).
declaredServerFaultAnswer/sandboxFaultAnswerinpackages/rest) are{ error, code? }. They carry nomessage, and nocodeat all when the producer declared only a status. The batch entry'serrorrequires bothcodeandmessage(ApiErrorSchema). So the door must fill a missingcodewith the member thatresolveThrownHttpErrorderives from the status, and fillmessagefrom the withheld outward text. Pin both cases.userMessageand singulardetail. The single door answersuserMessageon three arms (viamarkExtra). The batch entry's mapping does not name it, and the singulardetailkey is stripped. Decide whether the batch entry carries them, and say so in the door.data.results. The schema does not bound it. The door must answer exactly one entry per query, in request order. Pin that.DatasetQueryBatchRequest['queries']is typed as records ofunknown. The client method (Dashboards issue one analytics query per widget (18 on Chart Gallery), and KPI cards repeat their title and truncate large values #32165) types its own entries.?preview=draft. The single door reads it from the query string, and the batch contract says nothing about it. Decide whether it applies to the whole batch.{ code, message }.objectstack-fleet commentedon Nov 10, 2026
domain:server#2 · os-support-ai · session_01WGufziCTpjnYbdh76SAWwb
Claim: PM loop round 4 · #32200 · 2026-11-10T16:55Z · enhancement (p2; server half of #32165 item 1; ADR-0150 epic #31527 child)
Session:
session_01WGufziCTpjnYbdh76SAWwb(card thread, dev) · seatsession_01WGufziCTpjnYbdh76SAWwbAccount:
os-support-aiBranch:
claude/project-thread-bk2oei(the card thread's harness-pinned branch; platform exception toclaude/issue-N-slug)Worktree:
objectstack-issue-32200Domain:
domain:serverSeat:
domain:server#2File surface:
packages/rest/src/analytics-routes.ts(+ a batch route file if split),analytics-dataset-body-door.ts/analytics-selection-door.tsonly for the one shared per-body function, their tests,packages/client/src/index.ts+ client test,content/docs/data-modeling/analytics.mdx, route ledger/OpenAPI rows the gates name, root.changeset/; ⛔ nopackages/spec, noui/**Container & model: M,
mode:thread,model: default tier(no path-derived mandate),effort: high (p2)Clause-②: yes (widening): a new door
POST /analytics/dataset/query/batch; no existing door's answer changesMetadata-protocol: none
Responsibility: rest analytics doors (objectstack): a dashboard render sends one POST per widget (18 on Chart Gallery) · no batch door exists · reached by the console dashboard (#32165) and every SDK caller
Thread-read: none
Premise: holds at
origin/main7993aa76:packages/rest/src/analytics-routes.tsmounts only the single dataset door; contract merged in PR #32251 (de6d8935); routed to this seat by the epic seat with its constraint (each query resolvesdatasetNameexactly as the single door after #32098, by package, D3 409 naming holders, admission as the single door judges it; one query's refusal never picks or serves another), leftovers 6770822856 ruled in the orderSerial constraints cleared: touches
analytics-routes.tsbeside #32183 (seat 3) and #32292; merge main before the PR, keep main's resolve/admission order through the shared functionScope: one PR,
Fixes #32200. Order:/mnt/project-files/server-orders/seat2/standard.md+/mnt/project-files/server-orders/seat2/32200.md. Summons: the maintainer's 「所有server车道是恢复开工,不需要停」 (2026-11-10T11:25Z).fix(plugin-dashboard): KPI tiles drop a title-equal subtitle, fit lon…
feat(spec): a batch dataset-query request and answer beside the singl…
objectstack-fleet commentedon Nov 10, 2026
domain:server#2 · os-support-ai · session_01WGufzhCTYgGzvEntGnceRh
os-dev-report
{ "issue": 32200, "status": "done", "branch": "claude/project-thread-bk2oei", "pr": "https://github.com/objectstack-ai/objectstack/pull/32322", "session": "session_01WGufzhCTYgGzvEntGnceRh", "premise_still_valid": true, "summary": "POST {basePath}/analytics/dataset/query/batch mounted beside the single door. The single door's per-body judgment (body door, selection door, ?package=/?preview= reads, per-caller draft admission, by-package resolution with the D3 409, dataset parse, queryDataset, error classification) moved unchanged into answerDatasetQuery, which both doors call; the single door's answers are byte-identical (21 arms snapshotted on origin/main, then run unchanged on the moved door). The batch answers one entry per query in request order, bounded concurrency 6, refusals mapped into ApiErrorSchema (missing 5xx code/message filled, userMessage carried, singular detail left out as free text), whole-request refusals in the house envelope. client.analytics.queryDatasetBatch(queries: DatasetQueryBody[]) resolves to { results } and throws on a whole-batch refusal.", "tests": "rest analytics-dataset-batch-door.test.ts 32/32, analytics-dataset-single-door-bytes.test.ts 21/21 (before leg on main's analytics-routes.ts: 21 written / 21 passed; after leg 21 passed); client analytics-dataset-batch.test.ts 4/4 (real client on the real route). Full: @objectstack/rest test 402 files / 7742 passed, test:repo 5 / 300 passed; @objectstack/client test 58 / 804 passed. Ablations via scripts/ablation-replace.mjs, each restored to HEAD: (a) batch-local copy skipping the resolver/admission -> 12 red incl. 409 byte-equal and sibling pins; (b) drafts gate removed -> negative drafts pin red, CONTROL green; (c) bound removed -> 51-refused pin red + client throws-on-51 red; (d) batch identity floor removed -> 401 pin red; (e) batch repeated-param refusal removed -> 'expected 200 to be 400'; (f) envelope translation removed -> 401/501/503 whole-request pins red.", "gates": "dispatch-gates --commands: 117 derived, each run with exit recorded, 117/117 exit 0; --ran: 117 derived, 117 run, 0 NOT-MEASURED, 0 UNRUN. check-changeset-no-major --base origin/main --event <body> exit 0. eslint --no-inline-config on touched .ts exit 0. tsc --noEmit rest/client exit 0; check:test-typecheck rest/client OK. Merged origin/main (e267b96b) cleanly after the run; affected rest suites (34 files, 1121 passed) and client suite (804 passed) re-run after the merge. CI families: read from PR #32322 check-runs (pending at report time).", "line_budget": "+1783 / -531 across 12 files (git diff 7993aa76..pre-merge tip); analytics-routes.ts is mostly re-indent: git diff -w +349 / -37; under the 3000 human-merge threshold", "files_changed": [ ".changeset/32200-batch-dataset-query-door.md", "content/docs/data-modeling/analytics.mdx", "packages/client/src/analytics-dataset-batch.test.ts", "packages/client/src/index.ts", "packages/rest/src/analytics-dataset-batch-door.test.ts", "packages/rest/src/analytics-dataset-single-door-bytes.test.ts", "packages/rest/src/analytics-routes.ts", "packages/rest/src/meta-draft-read-door-census.test.ts", "packages/rest/src/rest-route-ledger.ts", "packages/rest/src/rest-write-response-internal-fields.tripwire.test.ts", "packages/rest/src/rest-write-route-hook-refusal-sentence.ledger.test.ts", "scripts/check-route-envelope.mjs" ], "deviations": [ "Files outside the order's surface, each demanded by a gate or census the move reaches: meta-draft-read-door-census.test.ts (its two analytics keys name the enclosing function now: function answerDatasetQuery; still two switches), the two write-route ledgers (batch row: no-record-echo; hook-refusal sentence driven from the entry), scripts/check-route-envelope.mjs (analytics-routes.ts siblingCode 1 -> 0, annotated as NOT progress: the { code, error } 500 still ships byte-identical, it is now returned as data and written through a variable the walk does not read).", "The batch handler writes through houseEnvelopeResponse(wire) so it asks enforceAuth/refuseRepeatedQueryParams verbatim (execctx-consumer-census requires the literal guard line); refusals still leave in the house envelope.", "@objectstack/client exports a new type DatasetQueryBody (the single door's body, formerly an inline parameter type of queryDataset), so the batch entries are typed without a second copy.", "Commits were reworded from the session's model-named co-author trailer to the repo's model-free Co-Authored-By: Claude before push (pre-push trailer rule)." ], "dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm", "mcp_calls": "1 — subscribe_pr_activity (claude-code-remote, not a GitHub write); 0 GitHub MCP calls", "api_writes": "2 relay writes — fleet-write pr_create (#32322, draft, no assignee, no reviewer, 13617 bytes read back identical) and this os-dev-report comment via post-stamped.mjs; 1 git push", "open_questions": [], "security_findings": "none", "out_of_scope_findings": [] }Generated by Claude Code
objectstack-fleet commentedon Nov 10, 2026
domain:server#2 · os-support-ai · session_01WGufziCTpjnYbdh76SAWwb
Landed · 2026-11-10T19:42Z
PR #32322 merged through the merge queue as
eefda1ad.POST /analytics/dataset/query/batchnow answers up to 50 dataset queries in one request, one entry per query in request order, each judged by the single door's ownanswerDatasetQuery.client.analytics.queryDatasetBatchbuilds the URL. The console half, #32165, can now switch to it. Removingpm:dispatchedand the seat's assignment.pm:blockingstays for triage to clear on #32165's edge.Generated by Claude Code