Skip to content

Commit b00fc55

Browse files
feat(spec,lint): a list-view grouping level can order its groups by another field (grouping.fields[].orderBy) (#25828)
Fixes #25786 Clause-②: yes Metadata-protocol: added A list-view grouping level can now order its groups by another field of the same object: `{ field: 'period_label', orderBy: 'period_start', order: 'desc' }`. Before this, a period label grouped as text (`Nov 2026`, `Oct 2026`, `Q1 2027`, `Q4 2025`), so HotCRM's forecast list carried a second, outer grouping level on `period_start` and showed two header rows per forecast. ## What changes - **Spec (`packages/spec/src/ui/view.zod.ts`).** `GroupingFieldSchema` gains `orderBy?: string`. It is optional with no default; absent, or naming the grouping field itself, means the level orders by its own key exactly as before. Its name is refused padded, as `field`'s is (#17360's ruling). The rule is a native `.regex()`, so the published JSON Schema states it as `pattern` instead of adding an `x-dropped-refinements` site. - **Compiler (`packages/spec/src/ui/view-grouping-query.ts`).** `compileListViewGroupQuery` adds one node per ordered level, `min` over `orderBy` for `order: 'asc'` and `max` for `'desc'`, aliased by `columnSummaryAlias` (`min_period_start`). Each group sits where its first row would sit if the rows were sorted by `orderBy`. - The grouping key stays the only `groupBy` column, so there is still one header per label. - A column summary declaring the same `min` is the same node. - An alias landing on a grouped field's column is `alias_collision`. - A blank `orderBy` is `grouping_order_by_blank` (`INVALID_QUERY` / 400). - In a multi-level grouping the outer level folds exactly (the min of mins, the max of maxes). - `depth` limits the nodes to the grouped levels. - A new export, `listViewGroupOrderColumn(grouping, level)`, names the header-row column a level sorts by. - **Lint (`packages/lint/src/validate-list-view-field-refs.ts`).** `grouping.fields[].orderBy` is judged the way the server judges it, on `os validate`, `os build` and the runtime view save door: - an unknown name is `list-view-field-unknown` (error); - a dotted path is `list-view-field-dotted` (error), on a new `aggregate` axis; - a type `min` / `max` cannot order on every backend is the new `list-view-group-order-unorderable` (error). The pair is asked of `isAggregateCompatibleWithFieldType`, as the engine's type door asks it, so the string classes (collation-dependent) and the virtual `formula` are refused, along with every other type outside the numeric, temporal and boolean classes. - The rule has an `os explain` entry. ## What the server needs (measured, as the order asked) No new server code. The group sort stays the consumer's (`EngineAggregateOptions` carries no `orderBy`, and `order` was already applied by the consumer over the header rows). What the header rows lacked was the value to sort by, and the existing aggregate door already serves it: - `POST /api/v1/data/:object/query` → `protocol.findData` → `engine.aggregate` answers `min` / `max` over `date` and `datetime` operands on both tiers. That is driver-sql pushdown, and the in-memory `applyInMemoryAggregation` behind a driver that does not push down. - The values are identical in value and `typeof` per column on both tiers, asc and desc. This is pinned in `packages/rest/src/list-view-grouping-query-door.test.ts` §11, with the tier asserted by the fork counters, not assumed. - The door refuses what the lint refuses, `400 INVALID_FIELD`: - an unknown or dotted operand at `findData`'s ingress (`assertAggregationFieldsExist`); - an unorderable type at `engine.aggregate`'s type door. - §11 pins a `text` operand and a dotted `period_start.year`. - Paged groups: the header query is compiled unpaged, so the consumer sorts the whole header set. A `limit` on the header query is sliced by `findData` in emission order before any sort, so a paged header set honours neither `order` nor `orderBy`. That is the limit already recorded on `GroupingConfigSchema` ("Header cardinality is unbounded"), unchanged here; bounding it is `orderBy` + `limit` on the aggregate verb, an engine-contract card of its own. ## Evidence Final head `21dc9ad7`. `main` was merged twice: once before the review (`6679644a`), and again after the sibling spec card #25755 landed (`abebc4e1`). There were no conflicts, and the whole workspace was rebuilt each time (`pnpm build`, 76/76). - Tests on the final head: - `@objectstack/spec`: full suite, 800 files, 23119 passed / 1 todo. - `@objectstack/lint`: full suite, 163 files, 7331/7331. - `@objectstack/rest`: `list-view-grouping-query-door.test.ts`, 53/53 (9 new in §11). - Ablations, each through `scripts/ablation-replace.mjs`, with the file restored to its HEAD blob and `git diff HEAD` empty afterwards: - lint verdict forced silent (`|| true` on the compatibility check): 8 red; - compiler order nodes dropped (`...orders.slice(0, 0)`): 5 red; - the padded-name regex widened to `/[\s\S]*/`: 6 red. - Gates: `node scripts/pm/dispatch-gates.mjs --commands` on the final diff derived 114 commands. All 114 ran on the final head, each exit code recorded before any pipe: 114 × exit 0. `--ran` reconciles to "114 derived, 114 run, 0 NOT-MEASURED, 0 UNRUN". - Two prerequisites were met first: a full `pnpm build`, and the self-test fixture commit fetched into the shallow clone. - Also run, all exit 0: the five roster gates whose roster sits under a path of this diff (`check-changeset-fixed`, `check:meta-url-spelling`, `check:authz-resolver`, `check:error-code-casing`, `check:filter-alias-parity`), `check:error-code-provenance`, and `typecheck` for spec, lint and rest. - Dogfood battery: n/a. No `!` in the title, and `Clause-②` carries no `(narrowing)` arm. ## Review An independent full review of the whole diff (a fresh reviewer at the contract-review tier) returned CHANGES-REQUIRED with five findings. All five are fixed in `5d64c378`, `6a3bf131` and `d37faf8f`, and the delta re-review's one nit in `5df57e7f`: - a padded `orderBy` was accepted by the schema; - the unknown and dotted refusals were attributed to the wrong door; - the paged-header case was not stated; - the tie direction was unspecified; - the refused-type list was narrower than the table. The delta re-review verdict is posted as a comment on this PR. ## Acceptance notes - **objectui reads nothing yet.** `orderBy` is declared and compiled, but the console grid ignores it until the objectui child lands: render the label from `field`, order the headers by `listViewGroupOrderColumn`, nulls last, ties by key in the same direction. That child card is filed in objectui with `Blocked-by:` this PR. HotCRM's one-line view edit (dropping its outer `period_start` level) comes after it, and is not this PR. - **A dotted `grouping.fields[].field` is not judged by lint** (pinned silent in `validate-list-view-field-refs.test.ts`), while the data door refuses it: `400 INVALID_FIELD`, "Unknown field 'period_start.year' … a related record's column cannot be a grouping target", measured through the §11 fixture. This predates the PR and is out of scope here; it is listed in the report. - **`field` keeps its `superRefine` and its `dropped-refinements.baseline.json` row**, while `orderBy` publishes the same non-padded rule as a JSON Schema `pattern`. Moving `field` to the same `.regex()` would shrink the ledger, but that is a separate change. --- _Generated by [Claude Code](https://claude.ai/code/session_01E1GNboWQRhh5vidwh2hxTY)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent abebc4e commit b00fc55

15 files changed

Lines changed: 972 additions & 20 deletions

File tree

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
---
2+
'@objectstack/spec': minor
3+
'@objectstack/lint': minor
4+
---
5+
6+
feat(spec,lint): a list-view grouping level can order its groups by another field (`grouping.fields[].orderBy`)
7+
8+
Clause-②: yes
9+
Metadata-protocol: added
10+
11+
- **Before.** A grouping level ordered its groups only by its own key. A period label grouped as text (`Nov 2026`, `Oct 2026`, `Q1 2027`, `Q4 2025`), so putting periods in time order took a second, outer grouping level on the period start, which showed two header rows for every group.
12+
- **Now.** `GroupingFieldSchema` gains `orderBy`, the name of a field on the same object whose value orders this level's groups: `{ field: 'period_label', orderBy: 'period_start', order: 'desc' }`. When it is absent, or names the grouping field itself, the groups order by their own key as before. Nothing is defaulted in.
13+
- **What the header query carries.** The consumer still sorts the header rows. `compileListViewGroupQuery` now adds the value to sort by: one `min` node over `orderBy` for `order: 'asc'`, or one `max` node for `'desc'`, under `min_<field>` / `max_<field>`. Each group sits where its first row would sit if the rows were sorted by `orderBy` in `order`. The grouping key stays the only `groupBy` column, so there is one header per label. A column summary that already declares the same `min` is the same node and the same column. In a multi-level grouping the outer level's value folds exactly from the leaf rows (the `min` of mins, the `max` of maxes).
14+
- **`listViewGroupOrderColumn(grouping, level)`** (`@objectstack/spec/ui`) names the header-row column a level sorts by: the grouping field itself, or the order node's alias. A consumer reads this instead of rebuilding the alias. A group whose order value is `null` sorts last in both directions, and equal values fall back to the group key in the same direction.
15+
- **Refused, at compile and at authoring.** An empty `orderBy` is refused by the schema (`too_small`), and a padded one, whitespace-only included, by name, as a padded `field` is, because the name is compiled verbatim. The compiler refuses a blank one in-process (`grouping_order_by_blank`, `INVALID_QUERY` / 400). An order alias that lands on a grouped field's own column is `alias_collision`. `os validate`, `os build` and the runtime save door judge `orderBy` the way the server doors do. An unknown name is `list-view-field-unknown`. A dotted path is `list-view-field-dotted`, because the header query compiles no join. A field whose type `min` / `max` cannot order on every backend is the new `list-view-group-order-unorderable` (`error`). That covers the string classes, whose order depends on the collation, the virtual `formula`, and every other type outside the numeric, temporal and boolean classes. Each of these is refused `400 INVALID_FIELD` on the server, so the grouped view would render no group: an unknown or dotted name by the data door's ingress (`findData`), and an unorderable type by `engine.aggregate`'s type door.
16+
- **Unchanged.** `order` and `collapsed` keep their meaning and defaults. The row page query (`compileListViewGroupRowsQuery`) does not change. `EngineAggregateOptions` gains no `orderBy`, because the group sort stays the consumer's. That sort is right only over the whole header set, which is what the compiler asks for: a `limit` on the header query is sliced by `findData` before any sort, so a paged header set honours neither `order` nor `orderBy`, the limit already recorded on `GroupingConfigSchema`. The new key is purely additive, so a grouping declared today compiles to exactly the same query.

‎content/docs/references/ui/component.mdx‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -887,7 +887,7 @@ Sort field and direction pair
887887

888888
| Property | Type | Required | Description |
889889
| :--- | :--- | :--- | :--- |
890-
| **fields** | `{ field: string; order?: Enum<'asc' \| 'desc'>; collapsed?: boolean }[]` | ✅ | Fields to group by, in nesting order — the first entry is the outermost group and each later entry nests one level deeper (at least one field); the same order as the group header query's `groupBy` |
890+
| **fields** | `{ field: string; order?: Enum<'asc' \| 'desc'>; orderBy?: string; collapsed?: boolean }[]` | ✅ | Fields to group by, in nesting order — the first entry is the outermost group and each later entry nests one level deeper (at least one field); the same order as the group header query's `groupBy` |
891891

892892
### Nested Shape: `ObjectGridProps.aggregations[number]`
893893

@@ -1037,7 +1037,7 @@ View filter rule
10371037

10381038
| Property | Type | Required | Description |
10391039
| :--- | :--- | :--- | :--- |
1040-
| **fields** | `{ field: string; order?: Enum<'asc' \| 'desc'>; collapsed?: boolean }[]` | ✅ | Fields to group by, in nesting order — the first entry is the outermost group and each later entry nests one level deeper (at least one field); the same order as the group header query's `groupBy` |
1040+
| **fields** | `{ field: string; order?: Enum<'asc' \| 'desc'>; orderBy?: string; collapsed?: boolean }[]` | ✅ | Fields to group by, in nesting order — the first entry is the outermost group and each later entry nests one level deeper (at least one field); the same order as the group header query's `groupBy` |
10411041

10421042
### Nested Shape: `ObjectKanbanProps.conditionalFormatting[number]`
10431043

‎content/docs/references/ui/view.mdx‎

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -613,14 +613,15 @@ Record grouping configuration — SERVER-SIDE: the set of groups and every numbe
613613

614614
| Property | Type | Required | Description |
615615
| :--- | :--- | :--- | :--- |
616-
| **fields** | `{ field: string; order?: Enum<'asc' \| 'desc'>; collapsed?: boolean }[]` | ✅ | Fields to group by, in nesting order — the first entry is the outermost group and each later entry nests one level deeper (at least one field); the same order as the group header query's `groupBy` |
616+
| **fields** | `{ field: string; order?: Enum<'asc' \| 'desc'>; orderBy?: string; collapsed?: boolean }[]` | ✅ | Fields to group by, in nesting order — the first entry is the outermost group and each later entry nests one level deeper (at least one field); the same order as the group header query's `groupBy` |
617617

618618
### Nested Shape: `GroupingConfig.fields[number]`
619619

620620
| Property | Type | Required | Description |
621621
| :--- | :--- | :--- | :--- |
622622
| **field** | `string` | ✅ | Field name to group by — one `groupBy` column of the group header query; the header row carries its raw stored value (null for the empty group). NO leading or trailing whitespace: the name is compiled into the aggregate query verbatim, so a padded spelling buckets every row into one empty group instead of grouping them. |
623-
| **order** | `Enum<'asc' \| 'desc'>` | optional (default: `"asc"`) | Group sort order — applied by the consumer over the header rows (the aggregate query carries no orderBy) |
623+
| **order** | `Enum<'asc' \| 'desc'>` | optional (default: `"asc"`) | Group sort order — applied by the consumer over the header rows (the aggregate query itself sorts nothing): by the group key, or by the `orderBy` value each header row carries when `orderBy` is set |
624+
| **orderBy** | `string` | optional | Field whose value orders this level's groups, when that is not the grouping field — a field of the same object; default: the grouping field itself, so the groups order by their own key. The group header query then carries, per group, the smallest value of this field for `order: asc` and the largest for `desc` (each group sits where its first row would if the rows were sorted by this field), and the consumer sorts the header rows by it; a group with no value here sorts last in both directions, and equal values fall back to the group key in the same direction. NO leading or trailing whitespace (compiled verbatim, as `field` is). A numeric, temporal or boolean field: min/max over a string orders by collation, which differs between backends, so the server refuses it |
624625
| **collapsed** | `boolean` | optional (default: `false`) | Collapse groups by default (presentation only) |
625626

626627

@@ -633,7 +634,8 @@ Record grouping configuration — SERVER-SIDE: the set of groups and every numbe
633634
| Property | Type | Required | Description |
634635
| :--- | :--- | :--- | :--- |
635636
| **field** | `string` | ✅ | Field name to group by — one `groupBy` column of the group header query; the header row carries its raw stored value (null for the empty group). NO leading or trailing whitespace: the name is compiled into the aggregate query verbatim, so a padded spelling buckets every row into one empty group instead of grouping them. |
636-
| **order** | `Enum<'asc' \| 'desc'>` | optional (default: `"asc"`) | Group sort order — applied by the consumer over the header rows (the aggregate query carries no orderBy) |
637+
| **order** | `Enum<'asc' \| 'desc'>` | optional (default: `"asc"`) | Group sort order — applied by the consumer over the header rows (the aggregate query itself sorts nothing): by the group key, or by the `orderBy` value each header row carries when `orderBy` is set |
638+
| **orderBy** | `string` | optional | Field whose value orders this level's groups, when that is not the grouping field — a field of the same object; default: the grouping field itself, so the groups order by their own key. The group header query then carries, per group, the smallest value of this field for `order: asc` and the largest for `desc` (each group sits where its first row would if the rows were sorted by this field), and the consumer sorts the header rows by it; a group with no value here sorts last in both directions, and equal values fall back to the group key in the same direction. NO leading or trailing whitespace (compiled verbatim, as `field` is). A numeric, temporal or boolean field: min/max over a string orders by collation, which differs between backends, so the server refuses it |
637639
| **collapsed** | `boolean` | optional (default: `false`) | Collapse groups by default (presentation only) |
638640

639641

@@ -1018,7 +1020,7 @@ View filter rule
10181020

10191021
| Property | Type | Required | Description |
10201022
| :--- | :--- | :--- | :--- |
1021-
| **fields** | `{ field: string; order?: Enum<'asc' \| 'desc'>; collapsed?: boolean }[]` | ✅ | Fields to group by, in nesting order — the first entry is the outermost group and each later entry nests one level deeper (at least one field); the same order as the group header query's `groupBy` |
1023+
| **fields** | `{ field: string; order?: Enum<'asc' \| 'desc'>; orderBy?: string; collapsed?: boolean }[]` | ✅ | Fields to group by, in nesting order — the first entry is the outermost group and each later entry nests one level deeper (at least one field); the same order as the group header query's `groupBy` |
10221024

10231025
### Nested Shape: `ListView.rowColor`
10241026

@@ -1395,7 +1397,7 @@ View filter rule
13951397

13961398
| Property | Type | Required | Description |
13971399
| :--- | :--- | :--- | :--- |
1398-
| **fields** | `{ field: string; order?: Enum<'asc' \| 'desc'>; collapsed?: boolean }[]` | ✅ | Fields to group by, in nesting order — the first entry is the outermost group and each later entry nests one level deeper (at least one field); the same order as the group header query's `groupBy` |
1400+
| **fields** | `{ field: string; order?: Enum<'asc' \| 'desc'>; orderBy?: string; collapsed?: boolean }[]` | ✅ | Fields to group by, in nesting order — the first entry is the outermost group and each later entry nests one level deeper (at least one field); the same order as the group header query's `groupBy` |
13991401

14001402
### Nested Shape: `ObjectListView.rowColor`
14011403

‎packages/lint/src/index.ts‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -521,10 +521,13 @@ export type {
521521
// refused the same way; `wholeCollection` is the statement.
522522
// Its FIFTH: a list view still carrying a key the spec retired, reported by
523523
// name with the tombstone's prescription (`os lint` never parses).
524+
// [#25786] Its SIXTH: a grouping level's `orderBy` naming a field the group
525+
// header query cannot order by (`min` / `max` refused for its type).
524526
export {
525527
validateListViewFieldRefs,
526528
LIST_VIEW_FIELD_UNKNOWN,
527529
LIST_VIEW_FIELD_DOTTED,
530+
LIST_VIEW_GROUP_ORDER_UNORDERABLE,
528531
LIST_VIEW_OBJECT_OUTSIDE_CLOSURE,
529532
LIST_VIEW_OBJECT_UNKNOWN,
530533
LIST_VIEW_KEY_RETIRED,

‎packages/lint/src/rule-explanations.ts‎

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3304,14 +3304,15 @@ const LIST_VIEW_FIELD_UNKNOWN_EXPLANATION: RuleExplanation = {
33043304
// The positions are held to `listViewWalkedPositions()` by the rule's test.
33053305
'Every field a list view names must be a field of its bound object: its `columns` (and a ' +
33063306
'column\'s `summary` and `prefix`), `hiddenFields`, `fieldOrder`, ' +
3307-
'`grouping`, `rowColor` and `userFilters`, the keys of its `filter` and of each ' +
3307+
'`grouping` (and a grouping level\'s `orderBy`), `rowColor` and `userFilters`, the keys of its `filter` and of each ' +
33083308
'`userFilters.tabs[].filter`, and every binding inside a `kanban`, `calendar`, `gantt`, ' +
33093309
'`timeline`, `gallery`, `map` or `tree` block. Nothing resolves the name at render time: the ' +
33103310
'view renders successfully with a blank, empty or wrong result, and nothing else reports the ' +
33113311
'miss. The verdict\'s "Did you mean" names the nearest field the object declares.',
33123312
'An error where the miss changes what the view returns or collapses what it lays out — for ' +
33133313
'example, a filter key, user filter or gantt quick filter matches no record; a column is sent as the ' +
3314-
'projection, where the REST ingress answers `400 INVALID_FIELD`; a `kanban.groupByField` ' +
3314+
'projection, where the REST ingress answers `400 INVALID_FIELD`; a grouping level\'s `orderBy` ' +
3315+
'is an operand of the group header query, which the data door refuses the same way; a `kanban.groupByField` ' +
33153316
'collapses every card into one lane; a required date field of a `gantt`, `calendar` or ' +
33163317
'`timeline` leaves nothing to place; a map with no coordinate field plots no marker; a ' +
33173318
'`tree.parentField` flattens the tree; a `gantt.lockField` unfreezes every row and a ' +
@@ -3349,6 +3350,10 @@ const LIST_VIEW_FIELD_DOTTED_EXPLANATION: RuleExplanation = {
33493350
'(`assertFilterIsMaterializable` and the REST ingress, `400 INVALID_FIELD`) rather than ' +
33503351
'answered with an empty list. A structured or JSON head, an array (`multiple: true`) head, a ' +
33513352
'file head and an unreadable head are served by the door, so they are not reported.',
3353+
'Aggregate: a grouping level\'s `orderBy` is the operand of the `min` / `max` node the group ' +
3354+
'header query orders its groups by. The header query compiles no join, so the data door\'s ' +
3355+
'ingress refuses every dotted operand whatever its head (`assertAggregationFieldsExist`, ' +
3356+
'`400 INVALID_FIELD`), and the grouped view renders no group.',
33523357
'The ways out are the doors\' own. At a projection position, name the whole column and read ' +
33533358
'into its value in the client, or read the related record with `expand`; at a filter position, ' +
33543359
'filter on a column of the bound object itself. At either, denormalise the value onto the bound ' +
@@ -3360,6 +3365,27 @@ const LIST_VIEW_FIELD_DOTTED_EXPLANATION: RuleExplanation = {
33603365
],
33613366
};
33623367

3368+
const LIST_VIEW_GROUP_ORDER_UNORDERABLE_EXPLANATION: RuleExplanation = {
3369+
rule: 'list-view-group-order-unorderable',
3370+
covers: 'which fields a grouping level can order its groups by',
3371+
paragraphs: [
3372+
'A grouping level\'s `orderBy` orders its groups by another field\'s value — a period label in time ' +
3373+
'order by its period start. The group header query carries that value per group: the smallest ' +
3374+
'(`min`) for `order: asc`, the largest (`max`) for `desc`. So the field must be one `min` and `max` ' +
3375+
'order the same way on every backend: the aggregate × field-type table (`AGGREGATE_FIELD_TYPE_COMPATIBILITY` ' +
3376+
'in `@objectstack/spec/data`) accepts the numeric, temporal and boolean classes.',
3377+
'It refuses every other type, among them the string classes — text, option values, references and ' +
3378+
'`autonumber` — because two backends order strings by two collations, the virtual `formula`, which ' +
3379+
'stores no column, and the file, structured and vector types. The engine\'s aggregate door (`engine.aggregate`) asks ' +
3380+
'the same table, so such an `orderBy` gets the grouped view\'s header query refused ' +
3381+
'(`400 INVALID_FIELD`) and no group renders. To order text labels, store each label\'s rank in a ' +
3382+
'number field and order by that.',
3383+
'Not judged: a level whose `orderBy` names its own grouping field (it compiles no node), a field with no ' +
3384+
'declared type, and a declared type outside `FieldType` — the aggregate door judges neither. A name ' +
3385+
'that resolves to nothing is `list-view-field-unknown`; a dotted one is `list-view-field-dotted`.',
3386+
],
3387+
};
3388+
33633389
/** Which keys a list view's binding is read from, and what stays unjudged (both binding ids write it out). */
33643390
const LIST_VIEW_BINDING_SCOPE =
33653391
'One finding per list view, at the key the binding was read from: the view\'s `object`, or a list ' +
@@ -5037,6 +5063,7 @@ export const RULE_EXPLANATIONS: Readonly<Record<string, RuleExplanation>> = Obje
50375063
[FILTER_EMPTY_NODE_EXPLANATION.rule]: FILTER_EMPTY_NODE_EXPLANATION,
50385064
[LIST_VIEW_FIELD_UNKNOWN_EXPLANATION.rule]: LIST_VIEW_FIELD_UNKNOWN_EXPLANATION,
50395065
[LIST_VIEW_FIELD_DOTTED_EXPLANATION.rule]: LIST_VIEW_FIELD_DOTTED_EXPLANATION,
5066+
[LIST_VIEW_GROUP_ORDER_UNORDERABLE_EXPLANATION.rule]: LIST_VIEW_GROUP_ORDER_UNORDERABLE_EXPLANATION,
50405067
[LIST_VIEW_OBJECT_OUTSIDE_CLOSURE_EXPLANATION.rule]: LIST_VIEW_OBJECT_OUTSIDE_CLOSURE_EXPLANATION,
50415068
[LIST_VIEW_OBJECT_UNKNOWN_EXPLANATION.rule]: LIST_VIEW_OBJECT_UNKNOWN_EXPLANATION,
50425069
[TRANSLATION_TARGET_UNKNOWN_EXPLANATION.rule]: TRANSLATION_TARGET_UNKNOWN_EXPLANATION,

0 commit comments

Comments
 (0)