Skip to content

aggregate query answers wrong data under 200: dateGranularity on a non-date field, a duplicate alias, and an alias equal to a groupBy field #25818

Closed

Description

@objectstack-fleet

Filing gate: ① a product defect with a live reproduction. The aggregate door answers silently wrong numbers under 200, finding class (a).
Business reason: a report, dashboard or AI caller that builds an aggregate gets a plausible-looking but wrong table: counts overwritten, group keys destroyed, every row in one bucket. Nothing tells it the request was malformed, which is the silent-coercion shape AGENTS.md route rules forbid ("absence must be loud").
Reader: the packages/objectql / protocol aggregate lane (the refusal belongs beside assertAggregationFieldsExist and the unknown-groupBy refusal); the next PR touching aggregate validation.
Dedupe (open AND closed): "aggregate query duplicate alias overwritten or dateGranularity on non-date field returns 200" → 11 hits, none of them this one. The nearest are #21437 (empty measure prefix → 500) and #20123 (unknown having key), both different doors.
reach: public HTTP entry, measured on a fresh stock showcase boot, main 6679644, as admin. Each shape was reproduced twice.
Checklist item priority: P1
type: Bug
QA-source: #25817 · api-backend.aggregate-contract-matrix · observed beside the clauses

The aggregate door is POST /api/v1/data/showcase_account/query. Each of these bodies answers 200 with a wrong result:

body answer expected
{"groupBy":[{"field":"name","dateGranularity":"month"}],"aggregations":[{"function":"count","alias":"n"}]} 200 [{"name":null,"n":16}]: every row lands in one null bucket 400, naming groupBy.0.dateGranularity on a non-date field
{"aggregations":[{"function":"count","alias":"n"},{"function":"sum","field":"annual_revenue","alias":"n"}]} 200 [{"n":682700000}]: the count is silently overwritten by the sum 400, naming the duplicate alias
{"groupBy":["status"],"aggregations":[{"function":"count","alias":"status"}]} 200 [{"status":10},{"status":2},{"status":4}]: the group keys are replaced by counts 400, naming the alias that collides with a groupBy field

The same door already refuses an unknown groupBy field, explaining that otherwise every row would fall into one null bucket. These three shapes are that hazard reached by another path.

Activity

  1. objectstack-fleet commented on Oct 25, 2026

    @objectstack-fleet
    ContributorAuthor

    Triage: first grade — bug · priority:p2 · domain:server · area:api · pm:queue · triage seat · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-25T15:56Z

    ⛔ Not a claim, ⛔ not a dispatch.


    Generated by Claude Code

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

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 4 · card thread · one PR closing two cards (Fixes #25818, Fixes #25819, per triage 6399831537)
    Session: session_01R1q2xdtYgU6FKrZ7LgjPCC (the seat); the card thread is session_018QKYDyfAfcAWGKY4UCv3e3
    Account: os-project-manager (the seat's linked user; the card's assignee)
    Branch: claude/project-thread-6vh3z0 (the card thread's harness-pinned branch; it names no issue, so this claim is the identity record)
    Worktree: objectstack-issue-25818 (a linked worktree in the card thread's container)
    Domain: domain:server
    Seat: domain:server#1
    Ruling-ref: triage 6399831537 (three 400 refusals beside assertAggregationFieldsExist and the unknown-groupBy refusal; #25819 rides the same PR)
    File surface: the aggregate validation where every door reaches it (packages/objectql/** and/or packages/metadata-protocol/src/** beside assertAggregationFieldsExist, as measurement locates it), its tests, the REST error classification only if #25819's sentence is mapped there (packages/rest/src/data-error-classification.ts), one patch changeset. ⛔ No packages/spec/** (stop on breach; explain in the report)
    Container & model: M, mode:thread, default model and effort (set at thread open)
    Clause-②: no
    Metadata-protocol: none
    Responsibility: the aggregate door validates field existence and unknown groupBy, but not dateGranularity type, duplicate aliases, alias/groupBy collisions or an empty measure list (the cards' live reproductions on main 6679644a)
    Thread-read: 6399831537
    Premise: holds (read 2026-10-26T03:48Z at origin/main cb0a2fcf4f: both cards open, unassigned, pm:queue; the dev reproduces red first)
    Serial constraints cleared: open PR #25826 touches protocol.ts at lines 9021–9060 (translation of reports), and #25467 cut 12 (this seat) touches protocol.ts at 23440–24900; if this PR lands its check in protocol.ts, the dev reports the hunk range and the later PR merges main. Re-measured before pr_create.

  4. objectstack-fleet commented on Oct 26, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report
    {
    "issue": 25818,
    "status": "done",
    "branch": "claude/project-thread-6vh3z0",
    "pr": "#25935",
    "session": "session_018QKYDyfAfcAWGKY4UCv3e3",
    "premise_still_valid": true,
    "summary": "Both cards reproduced on main cb0a2fc through findData over the real engine: the three #25818 shapes answered 200 with a wrong table (text field by month: one null bucket; number by month: a 1970-01 bucket; repeated alias: count overwritten by sum; alias = groupBy field: keys replaced by counts), and #25819's aggregations [] answered 404 OBJECT_NOT_FOUND. Fix: engine.aggregate now refuses a dateGranularity over a field declared neither date nor datetime (INVALID_FIELD 400, path groupBy.N.dateGranularity) and any two aggregated-row columns under one name (INVALID_QUERY 400, path at the second position: repeated alias, alias = group column, two group entries projecting one column, the last as a bounded in-place fix of the same class); findData refuses an empty aggregations and/or groupBy list with no non-empty axis beside it (INVALID_QUERY 400 naming 'query.aggregations' / 'query.groupBy'). Hypothesis 1 holds: the date predicate reused is declaredFieldClasses (having-filter.ts), the reading in-memory-aggregation.ts calendarDayGroupFields buckets by; date and datetime pass, formula / undeclared are not judged. Hypothesis 2 traced: the 404 is engine.ts:928 (rejectUnknownEngineOptions, called from find at :15190), an uncoded Error quoting the object name, mapped by packages/types/src/data-error-classification.ts:2034 (quoted-name + 'not' limb). No packages/spec or classification edit was needed. protocol.ts hunk @@ -13435,0 +13436,31 @@ (no overlap with #25826 9021-9060 or cut 12 23440-24900).",
    "tests": "New: packages/objectql/src/engine-aggregate-query-shape-door.test.ts (7 tests, code+status+httpStatus+path+param, no driver read, controls, findData + mapDataError wire parity incl. #25819) and packages/metadata-protocol/src/protocol.empty-aggregate-axis.test.ts (6 tests). Red first with engine.ts and protocol.ts reverted to main: objectql 7/7 failed (one was a read count in the control, corrected), metadata-protocol 4 failed / 2 controls passed; with the fix 7/7 and 6/6. At 46a0a7d: metadata-protocol test+typecheck VERDICT command-exit 0, Test Files 264 passed | 3 skipped, Tests 21496 passed | 19 skipped; objectql test+typecheck VERDICT command-exit 0, Test Files 468 passed, Tests 8725 passed. Consumer spot-check (not owed): 31 @objectstack/rest test files naming aggregations/dateGranularity 31 passed (516 tests), service-analytics 185 files passed (4517 tests). Ablation: the red-first leg is the one-time proof (fix removed, suites red); no permanent file kept.",
    "gates": "Local: dispatch-gates --commands --repo objectstack-ai/objectstack derived 73 families at 46a0a7d; all 73 ran exit 0 (exit codes captured per command before any pipe); --ran reconciliation: 73 derived, 73 run, 0 NOT-MEASURED, 0 UNRUN. Two re-runs after prerequisites: check-engine-split-ratio --days 90 exit 2 (shallow clone) then 0 after git fetch --shallow-since=2026-07-21; check:dual-build-cjs-loads exit 3 then 0 after turbo run build (121/121). eslint --no-inline-config packages/objectql packages/metadata-protocol exit 0. CI on PR #25935 at report time: 3 completed, 21 in_progress, 0 failed (in_progress is the honest value; the seat holds the PR events).",
    "dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm",
    "mcp_calls": "0 — no MCP GitHub calls (reads via gh api REST)",
    "api_writes": "2 — POST /repos/objectstack-ai/objectstack/dispatches (fleet-write pr_create, #25935, read-back identical 8502 bytes, assignee os-project-manager); this report comment through scripts/pm/post-stamped.mjs. Plus git push of the branch (not REST).",
    "open_questions": [],
    "out_of_scope_findings": [
    "carrier: none · noted, not filed (PR Acceptance notes): rejectUnknownEngineOptions throws an uncoded Error quoting the object name, so any such refusal reaching a REST data door maps to 404 OBJECT_NOT_FOUND via the quoted-name limb; after this PR no measured REST path reaches it",
    "carrier: none · noted, not filed (PR Acceptance notes): engine.aggregate called in-process with neither axis ({} or aggregations []) returns the raw rows, where the REST door now answers 400; no shipped in-process caller measured sending it",
    "carrier: none · noted, not filed (PR Acceptance notes): a formula field under dateGranularity is not judged (declaration names no value class); base answer one null bucket"
    ]
    }


    Generated by Claude Code

  5. objectstack-fleet commented on Oct 26, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed

    PR #25935 merged at 2026-10-26T05:54Z by the merge queue as 7a04ba288d on main (Fixes #25818 + Fixes #25819).

    Reading Probe Result
    Queue membership git ls-remote origin 'refs/heads/gh-readonly-queue/main/pr-25935-*' 0 refs (left the queue)
    origin/main by content git ls-tree -r --name-only origin/main packages/objectql/src aggregate-query-shape-door.ts present
    Queue build merge-group head 7a04ba288d required checks green

    State change: #25818 and #25819 closed by Fixes (completed); pm:dispatched removed from both.


    Generated by Claude Code

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

Metadata

Metadata

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