Skip to content

feat(objectql,spec): per-aggregation filter on engine.aggregate — drivers refuse instead of silently dropping (#10413 contract half) - #10771

Merged
os-elon merged 2 commits into
mainfrom
claude/issue-10576-aggregate-per-filter
Aug 21, 2026
Merged

feat(objectql,spec): per-aggregation filter on engine.aggregate — drivers refuse instead of silently dropping (#10413 contract half)#10771
os-elon merged 2 commits into
mainfrom
claude/issue-10576-aggregate-per-filter

Conversation

@os-elon

Copy link
Copy Markdown
Collaborator

Fixes#10576

The contract half of #10413's ruling (maintainer, 2026-08-21, verbatim 「其他接受」 accepting option A: 「给引擎聚合契约加逐聚合过滤,一次修对所有驱动」). #10413 phase 2 (ObjectQLStrategy lowering each measure's filter into this seam, in service-analytics) consumes this seam next and stays on #10413 — that package is deliberately untouched here.

What changed

Contract (@objectstack/spec)

Execution (@objectstack/objectql)

Driver semantics — refuse loudly, no silent drop anywhere
The engine never pushes a filtered aggregation down, so a driver can only meet the key through a direct call — and each native aggregate face now refuses it with NOT_IMPLEMENTED/501 (the #5907 "declared by the spec, not compiled by this face" class, twin wording first-sentence-for-first-sentence) instead of silently aggregating the UNFILTERED rows:

Driver-investment freeze judgment (per the dispatch): the driver-memory / driver-mongodb changes are refusal paths only — they close a silent drop; no capability build-out was made in the frozen family (a $cond-wrapped mongodb accumulator would be one, and was deliberately not built).

Acceptance evidence (the card's non-negotiables)

  • /api/v1/analytics/query still drops per-measure and dataset-level filter on the ObjectQL path — engine.aggregate receives no filter at all #10413 reproduction at the objectql levelengine-aggregate-filter.test.ts: the measurement's three-measure shape (opp_count count(*), won_count count filtered stage: 'closed_won', won_amount sum(amount) with the same filter) reaches engine.aggregate and the numbers CHANGE: [{ opp_count: 6, won_count: 2, won_amount: 700 }] where the pre-fix behaviour answered 6/6/1970. A native-aggregate driver double that drops the filter (the pre-fix behaviour of every real driver) is proven NOT called (nativeCalls() === 0).
  • Refusal tests assert the named error content, never bare throw — every rejection case asserts code + status + message content: engine/in-memory unknown operator → INVALID_FILTER/400 naming aggregations[i].filter; all four driver faces → NOT_IMPLEMENTED/501 naming the aggregation alias and the engine-lowering remedy (driver-sql, turso remote incl. cross-face wording parity, mongodb builder, memory both doors).
  • Positive pins: entries WITHOUT filter behave byte-identically — unfiltered aggregations keep the native pushdown path (nativeCalls() === 1, same results); {} is the vacuous filter (the where/having convention) and does not break pushdown; equivalent controls pinned on each driver face; all 3999 pre-existing objectql tests and full spec/driver suites green.

Verification

Gate union pinned to final HEAD 3d27f1b367: the fast ratchet families (check:query-options-erasure, check:engine-double-contract, check:where-matcher, check:nul-bytes, check:cross-package-test-inputs, check:test-source-alias, changeset-no-major, adr-0087) re-ran green at that sha; the heavy runs (suites + check:type-check-debt re-measure, 33 entries, none above ledger) ran on a working tree byte-identical to it (git status --porcelain empty at commit; no edits between those runs and the commit). Highlights: objectql 226 files / 3999 tests; spec 416 files / 11107 tests + all 14 generated artifacts current + typecheck; driver-sql 104 files, turso 39 (incl. the previously-unbuilt @objectstack/verify parity file), mongodb 26, memory 39; consumers downstream of the widened types — service-analytics 78 files (tests run as consumer; source fenced for #10413 phase 2), rest 133, runtime 178, qa/dogfood analytics-rls.dogfood.test.ts — all green. The report comment on #10576 carries the full command/readout table.

Notes for review


Generated by Claude Code

…e; drivers refuse instead of dropping
Fixes the contract half of the ObjectQL analytics silent-drop: aggregation
entries gain an enforced per-aggregation filter (SQL FILTER (WHERE ...)
semantics), lowered in memory by the engine for every driver; the four
native driver aggregate faces refuse a directly-delivered filter with
NOT_IMPLEMENTED/501; the StrategyContext.executeAggregate bridge carries
the new key for #10413 phase 2.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B4h3medzvhB9rpfoja9jcw
…EST_DEBT ratchet stays at 355)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B4h3medzvhB9rpfoja9jcw
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 6 package(s): @objectstack/driver-memory, @objectstack/driver-mongodb, @objectstack/driver-sql, @objectstack/driver-turso, @objectstack/objectql, @objectstack/spec, touching 15 documentable anchor(s).

10 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/automation/hooks.mdx(via closed_won (literal))
  • content/docs/automation/index.mdx(via closed_won (literal))
  • content/docs/automation/workflows.mdx(via closed_won (literal))
  • content/docs/concepts/architecture.mdx(via closed_won (literal))
  • content/docs/data-modeling/analytics.mdx(via closed_won (literal))
  • content/docs/data-modeling/seed-data.mdx(via closed_won (literal))
  • content/docs/data-modeling/validation.mdx(via closed_won (literal))
  • content/docs/protocol/objectql/state-machine.mdx(via closed_won (literal))
  • content/docs/protocol/objectui/concept.mdx(via closed_won (literal))
  • content/docs/protocol/objectui/layout-dsl.mdx(via closed_won (literal))
What this run could not see
  • 2 changed file(s) yielded no anchor (packages/spec/liveness/query.json, packages/spec/liveness/state-counts.md) — pages documenting those are invisible to this run
  • 3 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 45 of 221 client-bound route-ledger rows — the other 176 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run: node scripts/docs-audit/affected-docs.mjs --bridge-coverage

Coarse fallback — 131 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 01c50322a10843e169d109b789b4f268bdf9122fpackageMentionDocs.

Which tree this was computed on

This run read content/docs from e9e2732d311473c5c02ad84e74d77b056df4854c — the merge of head 3d27f1b3670ecfcd438f5cf585c6fa8b239bba8e into base 01c50322a10843e169d109b789b4f268bdf9122f, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin e9e2732d311473c5c02ad84e74d77b056df4854c && git checkout e9e2732d311473c5c02ad84e74d77b056df4854c
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 01c50322a10843e169d109b789b4f268bdf9122f 3d27f1b3670ecfcd438f5cf585c6fa8b239bba8e && git checkout -B drift-repro 01c50322a10843e169d109b789b4f268bdf9122f && git merge --no-ff 3d27f1b3670ecfcd438f5cf585c6fa8b239bba8e
node scripts/docs-audit/affected-docs.mjs --json 01c50322a10843e169d109b789b4f268bdf9122f

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs 01c50322a10843e169d109b789b4f268bdf9122f → pass the list as
args.docs, on the commit named under Which tree this was computed on.

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

Labels

documentationImprovements or additions to documentationprotocol:datasize/xlteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

engine.aggregate: add per-aggregation filter to the contract — ruled half of #10413 (measure-level filters on the ObjectQL analytics path)

2 participants

@os-elon@claude