Skip to content

Commit 6793240

Browse files
committed
Merge remote-tracking branch 'origin/main' into claude/issue-15513-compliance-families-retirement
2 parents 7058258 + aa6ba06 commit 6793240

7 files changed

Lines changed: 464 additions & 13 deletions

File tree

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
"@objectstack/spec": patch
3+
---
4+
5+
Documentation: the analytics `where` contract and the `element:number` D3 entry now name the hop an array filter is lowered at.
6+
7+
Text only — no schema, accept-set, runtime or test behaviour changes. `AnalyticsQuerySchema.where` is still `FilterConditionSchema` and still refuses an array, which is the protocol working as `FilterArray`'s docblock (#5158 ruling C) declares it: a `FilterArray` is input-only authoring sugar, lowered to a `FilterCondition` at the single sink `parseFilterAST` (`@objectstack/spec/data`) the moment it arrives, and only the lowered `FilterCondition` travels any further.
8+
9+
- `AnalyticsQuerySchema.where`'s `.describe()` gains one sentence pointing array authors at that lowering: an authored `FilterArray` is lowered by `parseFilterAST` on the client before the wire, and this field admits only the lowered `FilterCondition`. It lands in the generated `content/docs/references/{api,data}/analytics.mdx` prop tables, which is where an author reads it.
10+
- The `element-number-filter-rule-array` semantic migration entry recorded its runtime prerequisite one hop too late: "authored array → adapter lowering → filter AST → accepted by `lowerAnalyticsWhere`". `lowerAnalyticsWhere` (`service-analytics`) is the in-process door (#5334) for callers reaching `analyticsService.query` directly. The wire's door is the runtime route `POST /analytics/query`, which parses `where` with `AnalyticsQueryRequestSchema` before any service code runs, so an un-lowered array is refused there. The entry's reason clause now names that route hop and the `parseFilterAST` lowering the adapter owes before the wire (#15828; the adapter-side fix is objectui#7752).
11+
12+
The sibling entry `element-record-picker-filter-rule-array` was read for the same claim and does not make it — its measured path is `find()` / `convertQueryParams`, not the analytics wire — so it is unchanged.

content/docs/references/api/analytics.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ const result = AnalyticsEndpoint.parse(data);
8282
| **cube** | `string` || Target cube name |
8383
| **measures** | `string[]` || List of metrics to calculate |
8484
| **dimensions** | `string[]` | optional | List of dimensions to group by |
85-
| **where** | `any` | optional | Filtering criteria (canonical Query DSL FilterCondition) |
85+
| **where** | `any` | optional | Filtering criteria (canonical Query DSL FilterCondition). An authored `FilterArray` is lowered by `parseFilterAST` on the client before the wire; this field admits only the lowered `FilterCondition` (see `FilterArray` in `data/filter.zod.ts`). |
8686
| **timeDimensions** | `{ dimension: string; granularity?: Enum<'second' \| 'minute' \| 'hour' \| 'day' \| 'week' \| 'month' \| 'quarter' \| 'year'>; dateRange?: string \| string[] }[]` | optional | |
8787
| **order** | `Record<string, Enum<'asc' \| 'desc'>>` | optional | |
8888
| **limit** | `number` | optional | |

content/docs/references/data/analytics.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ const result = AggregationMetricType.parse(data);
5555
| **cube** | `string` | optional | Target cube name (optional when provided externally, e.g. in API request wrapper) |
5656
| **measures** | `string[]` || List of metrics to calculate |
5757
| **dimensions** | `string[]` | optional | List of dimensions to group by |
58-
| **where** | `any` | optional | Filtering criteria (canonical Query DSL FilterCondition) |
58+
| **where** | `any` | optional | Filtering criteria (canonical Query DSL FilterCondition). An authored `FilterArray` is lowered by `parseFilterAST` on the client before the wire; this field admits only the lowered `FilterCondition` (see `FilterArray` in `data/filter.zod.ts`). |
5959
| **timeDimensions** | `{ dimension: string; granularity?: Enum<'second' \| 'minute' \| 'hour' \| 'day' \| 'week' \| 'month' \| 'quarter' \| 'year'>; dateRange?: string \| string[] }[]` | optional | |
6060
| **order** | `Record<string, Enum<'asc' \| 'desc'>>` | optional | |
6161
| **limit** | `number` | optional | |

packages/spec/src/data/analytics.zod.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,11 @@ export const AnalyticsQuerySchema = lazySchema(() => strictObject(
296296
* { where: { is_active: true, stage: { $nin: ['lost'] } } }
297297
* ```
298298
*/
299-
where: FilterConditionSchema.optional().describe('Filtering criteria (canonical Query DSL FilterCondition)'),
299+
where: FilterConditionSchema.optional().describe(
300+
'Filtering criteria (canonical Query DSL FilterCondition). An authored `FilterArray` is '
301+
+ 'lowered by `parseFilterAST` on the client before the wire; this field admits only the '
302+
+ 'lowered `FilterCondition` (see `FilterArray` in `data/filter.zod.ts`).'
303+
),
300304

301305
/**
302306
* Time-bucketed dimensions. Strict as of #4001 batch D — and this item is

packages/spec/src/migrations/entries/semantic/18.element-number-filter-rule-array.ts

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,21 @@ export const entry: SemanticMigration = {
2424
+ 'the filter a list view stores and renders was refused by the KPI element beside it, '
2525
+ 'and the objectui parity gate had to carry a reasoned exemption to look away. The '
2626
+ 'convergence was sequenced consumer-first (ruling recorded 2026-08-25, Option A): '
27-
+ 'objectui#6828 made `ObjectStackAdapter.aggregate()` lower a rule array through the '
28-
+ 'same `translateFilterArray` its `find()` path runs before the analytics wire, and the '
29-
+ 'objectui pin carrying it was re-measured before this entry moved — authored array → '
30-
+ 'adapter lowering → filter AST → accepted by `lowerAnalyticsWhere`, which still refuses '
31-
+ 'a RAW rule-object array by design. The ruled migration check ran with the change: the '
27+
+ 'objectui#6828 made `ObjectStackAdapter.aggregate()` run the same `translateFilterArray` '
28+
+ 'its `find()` path runs, and the objectui pin carrying it was re-measured before this '
29+
+ 'entry moved — but that measurement named the wrong hop, and #15828 corrects it here. '
30+
+ '`translateFilterArray` yields AST tuples, which are still a `FilterArray` — input-only '
31+
+ 'sugar — so the real path is: authored array → `translateFilterArray` → lowered by '
32+
+ '`parseFilterAST` (`@objectstack/spec/data`, the single sink the `FilterArray` docblock '
33+
+ 'names, #5158 ruling C) in the adapter, BEFORE the wire → a `FilterCondition` on the '
34+
+ 'body. The hop that decides it is the runtime route `POST /analytics/query`, which '
35+
+ 'parses `where` with `AnalyticsQueryRequestSchema` — a `FilterCondition` and nothing '
36+
+ 'else — so an un-lowered array is refused there before any service code runs. '
37+
+ '`lowerAnalyticsWhere` (`service-analytics`), where that earlier measurement stopped, '
38+
+ 'is the IN-PROCESS door (#5334) for callers reaching `analyticsService.query` '
39+
+ "directly, not the wire's; it too still refuses a RAW rule-object array by design. "
40+
+ 'objectui#7752 lands the adapter-side lowering. '
41+
+ 'The ruled migration check ran with the change: the '
3242
+ 'sweep of first-party corpora (examples/, skills/, create-objectstack, content/docs/, '
3343
+ 'packages/apps/, spec fixtures) found ONE `element:number` author writing a record-form '
3444
+ '`filter` — a spec test fixture, rewritten to the array form in the same change — and '

packages/spec/src/migrations/registry.ts

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6513,11 +6513,21 @@ const step18: MigrationStep = {
65136513
+ 'the filter a list view stores and renders was refused by the KPI element beside it, '
65146514
+ 'and the objectui parity gate had to carry a reasoned exemption to look away. The '
65156515
+ 'convergence was sequenced consumer-first (ruling recorded 2026-08-25, Option A): '
6516-
+ 'objectui#6828 made `ObjectStackAdapter.aggregate()` lower a rule array through the '
6517-
+ 'same `translateFilterArray` its `find()` path runs before the analytics wire, and the '
6518-
+ 'objectui pin carrying it was re-measured before this entry moved — authored array → '
6519-
+ 'adapter lowering → filter AST → accepted by `lowerAnalyticsWhere`, which still refuses '
6520-
+ 'a RAW rule-object array by design. The ruled migration check ran with the change: the '
6516+
+ 'objectui#6828 made `ObjectStackAdapter.aggregate()` run the same `translateFilterArray` '
6517+
+ 'its `find()` path runs, and the objectui pin carrying it was re-measured before this '
6518+
+ 'entry moved — but that measurement named the wrong hop, and #15828 corrects it here. '
6519+
+ '`translateFilterArray` yields AST tuples, which are still a `FilterArray` — input-only '
6520+
+ 'sugar — so the real path is: authored array → `translateFilterArray` → lowered by '
6521+
+ '`parseFilterAST` (`@objectstack/spec/data`, the single sink the `FilterArray` docblock '
6522+
+ 'names, #5158 ruling C) in the adapter, BEFORE the wire → a `FilterCondition` on the '
6523+
+ 'body. The hop that decides it is the runtime route `POST /analytics/query`, which '
6524+
+ 'parses `where` with `AnalyticsQueryRequestSchema` — a `FilterCondition` and nothing '
6525+
+ 'else — so an un-lowered array is refused there before any service code runs. '
6526+
+ '`lowerAnalyticsWhere` (`service-analytics`), where that earlier measurement stopped, '
6527+
+ 'is the IN-PROCESS door (#5334) for callers reaching `analyticsService.query` '
6528+
+ "directly, not the wire's; it too still refuses a RAW rule-object array by design. "
6529+
+ 'objectui#7752 lands the adapter-side lowering. '
6530+
+ 'The ruled migration check ran with the change: the '
65216531
+ 'sweep of first-party corpora (examples/, skills/, create-objectstack, content/docs/, '
65226532
+ 'packages/apps/, spec fixtures) found ONE `element:number` author writing a record-form '
65236533
+ '`filter` — a spec test fixture, rewritten to the array form in the same change — and '

0 commit comments

Comments
 (0)