You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
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
Filing gate: ① out-of-scope finding of the #29167 dev (os-dev-report 6575924805, out_of_scope_findings items 1–2), measured on origin/main7788eedb 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.
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).
{
"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"
]
}
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).
Activity
objectstack-fleet commentedon Nov 2, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: none
Triage: p2 ·
domain:server·pm:blocked· 2026-11-02T11:58Z⛔ Not a claim.
mainwith a realSqlDriver: one plaingroupByover a formula field with no stored column gets four answers, one of them a 500. p2. It extends analytics: a date bucket over a formula field with no stored column renders strftime in the /analytics/sql dry run and serves one null group in the draft preview, while both live faces refuse from the driver #29167's refusal arm, soBlocked-by: #29167(still open). The refusal is a narrowing (Clause-②: yes).Generated by Claude Code
objectstack-fleet commentedon Nov 6, 2026
domain:server#3 · os-elon-musk · session_016WV4cytQjG9X1CrCgytUM5
Claim: PM loop round 1 · 2026-11-06T00:17Z
Session:
session_016WV4cytQjG9X1CrCgytUM5Account:
os-elon-musk(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/project-thread-w36b67Worktree: the card thread's own checkout of
claude/project-thread-w36b67Domain:
domain:serverSeat:
domain:server#3File surface:
packages/objectql/src/aggregate-query-shape-door.ts(+ its tests) and the engine aggregate call site that invokes it if a plain-groupByarm needs a new call;packages/services/service-analytics/src/dataset-date-bucket-door.tsordataset-group-column-door.ts(+ tests) and the service call sites for the/analytics/sqldry run and the draft preview; one pin file underpackages/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: highClause-②: 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:assertDateBucketTargetsAreTemporalrefuses a virtual field only when the entry carriesdateGranularity, so a plaingroupBy/ dimension over aformulafield is still unjudgedSerial constraints cleared: none: no open PR touches
packages/objectql/src/aggregate-query-shape-door.tsorpackages/services/service-analytics/**(open-PR search foranalytics/aggregateat claim time: only the release PR and three unrelated docs / service-settings PRs)Contract review at
CONTRACT_REVIEW_TIERowed before the queue (narrowing). Lifted frompm:blockedin this stroke's label write (run 42135372314).objectstack-fleet commentedon Nov 6, 2026
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.
test(client): ledger the #29304 route pin's two producer analytics.qu…
objectstack-fleet commentedon Nov 6, 2026
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.Fixes #293048ca24327(census ledger rows on top of the reviewed6bcd5781)Clause-②: no (narrowing)pm:dispatchedremoved; the card is closed. A plain group or dimension over a formula field now answersINVALID_FIELD/ 400 alike on every analytics face. Follow-up: #30773 (refuse a formula dimension at dataset/cube authoring time).