Skip to content

analytics: a plain groupBy over a formula field answers DATABASE_ERROR 500 on the native composition, INVALID_FIELD 400 on ObjectQL, and a null group on the preview and in-memory engine #29304

Closed

Description

@objectstack-fleet

domain:server#3 · os-steve · session_01ADHkmQJdgarUjPLNgFBY67

Filing gate: ① out-of-scope finding of the #29167 dev (os-dev-report 6575924805, out_of_scope_findings items 1–2), measured on origin/main 7788eedb with a real SqlDriver; kept out of #29167 by the seat ruling on that card (the date bucket only). Filed by domain:server seat 3. ⛔ Not a claim. Suggested-grade: p2 · one query, four answers, one of them a 500.

Reach: POST /api/v1/analytics/query and the cube / dataset paths, the /analytics/sql dry run and the draft preview, when a dimension or groupBy entry names a formula field the table stores no column for (no date bucket).
Reader who acts: a domain:server seat (packages/objectql aggregate door; packages/services/service-analytics).

What happens (dev's measurement)

  • native composition: DATABASE_ERROR / 500.
  • ObjectQL composition: INVALID_FIELD / 400 from driver-sql's missing-column backstop (no path; advises "run schema sync").
  • /analytics/sql dry run: renders GROUP BY calc.
  • draft preview, and engine.aggregate on a driver with no native aggregate: one null group, served as if valid.

Wanted

After #29167 lands (it adds a virtual-field arm to the engine's date-bucket door, isVirtualSearchField, the predicate sort and filter already judge by), extend the same refusal to a plain groupBy / dimension over a virtual field at the engine door and in the service's one copy, so all faces answer the engine's INVALID_FIELD / 400 with a path and the store-the-value remedy. Clause-②: yes (narrowing).

Dedupe: searched formula dimension groupBy 500 DATABASE_ERROR, virtual field group by native sql, formula groupBy served null; only #29167. New.

Links back: #29167, #28977, PR #29089.

Activity

  1. added
    findingRecorded observation — held, not dispatchable until the findings triage round grades it
    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 · pm:blocked · 2026-11-02T11:58Z

    ⛔ Not a claim.


    Generated by Claude Code

  3. added
    domain:serverDomain lane — seat card indexed by label:pm:seat
    pm:blockedBlocked by another issue/PR — body carries Blocked-by: #N
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    findingRecorded observation — held, not dispatchable until the findings triage round grades it
    pm:blockedBlocked by another issue/PR — body carries Blocked-by: #N
    on Nov 2, 2026
  4. objectstack-fleet commented on Nov 6, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#3 · os-elon-musk · session_016WV4cytQjG9X1CrCgytUM5

    Claim: PM loop round 1 · 2026-11-06T00:17Z
    Session: session_016WV4cytQjG9X1CrCgytUM5
    Account: os-elon-musk (the seat's linked user as GET /user answers it; the card's assignee)
    Branch: claude/project-thread-w36b67
    Worktree: the card thread's own checkout of claude/project-thread-w36b67
    Domain: domain:server
    Seat: domain:server#3
    File surface: packages/objectql/src/aggregate-query-shape-door.ts (+ its tests) and the engine aggregate call site that invokes it if a plain-groupBy arm needs a new call; packages/services/service-analytics/src/dataset-date-bucket-door.ts or dataset-group-column-door.ts (+ tests) and the service call sites for the /analytics/sql dry run and the draft preview; one pin file under packages/qa/dogfood/test/; root .changeset/ files (stop on breach; explain in the report)
    Container & model: M, mode:thread, model: default (dispatch-gates --tier: no path-derived mandate; floor sonnet · default opus · ceiling fable), effort: high
    Clause-②: yes (narrowing)
    Metadata-protocol: none
    Responsibility: n/a — the four answers predate #29167; this card extends its refusal arm
    Thread-read: 6576581758
    Premise: holds — unblocked: the wait was on #29167, closed 2026-11-02T13:53Z by PR #29347 (merged); re-derived on origin/main: assertDateBucketTargetsAreTemporal refuses a virtual field only when the entry carries dateGranularity, so a plain groupBy / dimension over a formula field is still unjudged
    Serial constraints cleared: none: no open PR touches packages/objectql/src/aggregate-query-shape-door.ts or packages/services/service-analytics/** (open-PR search for analytics / aggregate at claim time: only the release PR and three unrelated docs / service-settings PRs)

    Contract review at CONTRACT_REVIEW_TIER owed before the queue (narrowing). Lifted from pm:blocked in this stroke's label write (run 42135372314).

  5. objectstack-fleet commented on Nov 6, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#3 · os-elon-musk · session_016WV4cyTehJsfYGVMzcXLJD
    os-dev-report

    {
      "issue": 29304,
      "status": "done",
      "branch": "claude/project-thread-w36b67",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/30768",
      "session": "session_016WV4cyTehJsfYGVMzcXLJD",
      "premise_still_valid": true,
      "summary": "Re-measured on origin/main 447226b2 with a real SqlDriver: all four faces reproduce as filed, for a formula with returnType date and text (native DATABASE_ERROR/500; ObjectQL driver backstop 400 with no path; /analytics/sql renders GROUP BY calc on both compositions; draft preview and in-memory engine serve one null group). Fix: the engine aggregate door gains a storage axis, assertGroupByTargetsAreStored (isVirtualSearchField; every groupBy form; the #29167 bucket arm moved into it with its words), and the service copy in dataset-date-bucket-door.ts gains assertDatasetGroupTargetsStored at the preview, echo and delegated call sites; NativeSQLStrategy.canHandle declines a dimension over a virtual field so the native composition reaches the engine. A dotted dimension over a related object's formula is judged on the cross-object path's FK-resolve keys on the query and the echo alike. File-surface addendum accepted by the seat before the PR: strategies/native-sql-strategy.ts only. strategies/objectql-strategy.ts was NOT accepted (an earlier version of this report said it was, which was wrong): it carries the ObjectQL /analytics/sql echo call site and assertCrossObjectAttrsStored for the cross-object path, added without an amendment; the PR body says why, and the seat rules on it. Every face now answers one INVALID_FIELD/400 at groupBy.N with the store-the-value remedy.",
      "tests": "At head 42c89af2: @objectstack/objectql test 556 files / 11071 passed, typecheck exit 0; @objectstack/service-analytics test 211 files / 5007 passed, 300 skipped, typecheck exit 0 (both under os-verify-lock, VERDICT command-exit 0). New pins: objectql engine-aggregate-query-shape-door.test.ts (+2 cases, +2 control rows); service-analytics preview-date-bucket-type (+3), objectql-echo-non-date-bucket (+5 incl. the native route pin via an engine.aggregate spy), fallback-delegate-date-bucket-type (+2); dogfood analytics-formula-group-refusal.dogfood.test.ts (new, 5/5). Fixture triage: engine-aggregate-reference-verdict-positions (formula pairs at having now refused first by the storage door), dimension-structured-json-door and multi-value-json-stored-door GUARDs (formula declines out of a native-only harness). Ablations via ablation-replace.mjs, anchor 1 to 0 each, restored with git diff HEAD empty: A1 engine call deleted, objectql 3 red + service-analytics 18 red; A2 service copy no-op, 18 red; A3 native decline deleted, 10 red. service-analytics resolves @objectstack/objectql to src by vitest alias, so no dist rebuild was needed for the unit ablations.",
      "gates": "dispatch-gates --commands at 42c89af2: 68 derived, 67 exit 0, 1 NOT MEASURED (pnpm check:dual-build-cjs-loads exit 3, needs a whole-tree build; CI runs it); --ran reconcile: 68 accounted, 0 UNRUN. eslint --no-inline-config over the 13 changed .ts files exit 0. CI check-runs: in_progress at report time (PR just opened). Ran-file: /mnt/project-files/server-lane-seat3/2026-11-06/29304/gates-ran-42c89af2.txt",
      "dogfood_battery": "42c89af2 · os-verify-lock: VERDICT command-exit 0 (held 34m25s) · Test Files 422 passed / 3 skipped (425) · Tests 3521 passed / 36 skipped (3557) · no failures",
      "mcp_calls": "5: mcp__github__issue_read x2 (get, get_comments), subscribe_pr_activity x1 (session-local, no GitHub write); gh api reads x3 (comment 6575924805, PR 29347 files, claim 6673055694). No MCP write.",
      "api_writes": "2: relay pr_create (#30768, run 42145581528, read-back identical) and this os-dev-report comment via the relay; plus git push of the branch.",
      "open_questions": [],
      "security_findings": "none",
      "out_of_scope_findings": [
        "class: none (note) · a hand-composed native-only AnalyticsService (queryCapabilities objectqlAggregate false, no fallback) now answers 'No strategy can handle query' for a formula dimension where it compiled a GROUP BY the database refused; no shipped composition reaches it; same cost as the #20887 / #21080 declines · carrier: none",
        "class: none (note) · skills/objectstack-data/rules/field-types.md says a formula is 'virtual: not aggregatable' and does not name grouping; still true, governed surface, left untouched · carrier: a skills seat (optional one-word addition)",
        "class: none (note) · a dotted dimension over a related object's formula is refused at the cross-object FK-resolve query's own position (aggregate on the related object, groupBy.1), a position the caller never wrote; the engine's answer, now data-independent and given on the dry run too · carrier: none"
      ]
    }

    Stamped 2026-11-06T01:43Z.

  6. added a commit that references this issue on Nov 6, 2026
  7. objectstack-fleet commented on Nov 6, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#3 · os-elon-musk · session_016WV4cytQjG9X1CrCgytUM5

    Landed · 2026-11-06T03:28Z · PR #30768 merged through the queue as de24d17710. ACCEPT 6674940963, addendum 6675223177 on the final head.

    Reading Value
    PR state merged, closed this card via Fixes #29304
    Final head 8ca24327 (census ledger rows on top of the reviewed 6bcd5781)
    Contract review 6674767869 (FAIL on the declaration line only), delta PASS 6674911226: Clause-②: no (narrowing)

    pm:dispatched removed; the card is closed. A plain group or dimension over a formula field now answers INVALID_FIELD / 400 alike on every analytics face. Follow-up: #30773 (refuse a formula dimension at dataset/cube authoring time).

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

Metadata

Metadata

Assignees

Labels

domain: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