Skip to content

Commit 95fb97e

Browse files
committed
fix(spec): element-* guidance in words; regenerate registry, spec-changes and upgrade guide; widen the pin
Rewrite the element-* ADR-0087 semantic entries in form D, regenerate registry.ts, spec-changes.json and docs/protocol-upgrade-guide.md with their generators, widen the os migrate meta guidance pin to the datasource-, filter-, action-, data- and element- families (REWRITTEN floor 55 -> 88), re-pin the one test that matched a removed tracker number on the sentence, and add the patch changeset. Claude-Session: https://claude.ai/code/session_01ARcDurZ5j34RdqsGgc4jgH Co-authored-by: Claude <noreply@anthropic.com>
1 parent bf336a6 commit 95fb97e

9 files changed

Lines changed: 353 additions & 219 deletions
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
---
2+
'@objectstack/spec': patch
3+
---
4+
5+
fix(spec): `os migrate meta` guidance for the `datasource-*`, `filter-*`, `action-*`, `data-*` and `element-*` migration entries states each lesson in words instead of citing tracker numbers
6+
7+
Clause-②: no
8+
9+
The ADR-0087 semantic entries of the `datasource-*` family (the publish-time credential,
10+
placeholder and URL refusals, and the bound-secret pairs a mongo datasource cannot use),
11+
the `filter-*` family (the retired `$regex`, the `$between` endpoint refusals and the
12+
comparand shapes the save door now refuses), the `action-*` family (the retired
13+
descriptor key, the `resumeAuthority` default flip, the action-session rename, the bulk
14+
dispatch contract and the engine facade's query envelope), the `data-*` family (the
15+
retired driver and engine contract members, the retired field-changed event and two
16+
duration keys renamed with their unit) and the `element-*` family (the filter rule array
17+
at the page binding and the element and block doors) are printed by `os migrate meta` as
18+
the header, `why:` and `verify:` lines of a manual change. Their text sent the reader to
19+
issue-tracker, decision-batch and ruling-record numbers — some of which no longer
20+
resolve, and some in other repositories — for what a ruling, measurement or fix had
21+
decided; it now says what was decided, in the sentence being read. ADR ids are kept.
22+
23+
Text only: no entry id, `surface`, `from` / `to`, conversion or matching logic changes,
24+
and the chain rewrites exactly what it rewrote before. The generated migration registry,
25+
`spec-changes.json` and the protocol upgrade guide carry the same text.

‎docs/protocol-upgrade-guide.md‎

Lines changed: 12 additions & 12 deletions
Large diffs are not rendered by default.

‎packages/cli/test/migrate-meta-engine-guidance.test.ts‎

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
/**
44
* `os migrate meta` — the guidance it prints for the ADR-0087 semantic entries
55
* of the COVERED families (`engine-*`, `ui-*`, `plugin-*`, `driver-*`,
6-
* `kernel-*`, `system-*`) states each lesson in words and carries no tracker
7-
* number.
6+
* `kernel-*`, `system-*`, `datasource-*`, `filter-*`, `action-*`, `data-*`,
7+
* `element-*`) states each lesson in words and carries no tracker number.
88
*
99
* ## What this pins
1010
*
@@ -73,26 +73,62 @@ const TSX = resolve(HERE, '../../../node_modules/.bin/tsx');
7373
const TRACKER_ID = /#\d{4,5}\b/;
7474

7575
/** The families this pin holds, selected by entry-id prefix. */
76-
const COVERED_PREFIXES = ['engine-', 'ui-', 'plugin-', 'driver-', 'kernel-', 'system-'];
76+
const COVERED_PREFIXES = [
77+
'engine-', 'ui-', 'plugin-', 'driver-', 'kernel-', 'system-',
78+
'datasource-', 'filter-', 'action-', 'data-', 'element-',
79+
];
7780

7881
/**
7982
* The entries rewritten when each family was brought to this line — the
8083
* anti-vacuity floor. A covered entry that carried no tracker id to begin with
8184
* is held by its prefix and needs no row here.
8285
*/
8386
const REWRITTEN = [
87+
'action-bulk-dispatch-contract-undeclared',
88+
'action-descriptor-is-async-retired',
89+
'action-descriptor-resume-authority-default-flip',
90+
'action-engine-facade-find-query-envelope',
91+
'action-session-roles-to-positions',
92+
'data-driver-find-stream-retired',
93+
'data-driver-query-omit-object',
94+
'data-engine-batch-retired',
95+
'data-field-changed-event-retired',
96+
'data-file-value-duration-unit-in-key',
97+
'data-nosql-query-options-timeout-unit-in-key',
98+
'datasource-config-inline-credential-refused',
99+
'datasource-config-mongo-options-credential-refused',
100+
'datasource-config-placeholder-refused',
101+
'datasource-config-postgres-url-unparseable-refused',
102+
'datasource-config-url-query-credential-refused',
103+
'datasource-config-url-userinfo-refused',
104+
'datasource-credentialsref-mongo-composed-no-username-refused',
105+
'datasource-credentialsref-mongo-url-no-user-refused',
84106
'driver-aggregate-undeclared-key-aliases-removed',
85107
'driver-capabilities-inert-bits-removed',
86108
'driver-options-timeout-to-timeout-ms',
87109
'driver-sql-distinct-bare-filter-typed',
88110
'driver-sql-unresolvable-where-column-refused',
89111
'driver-sql-upsert-cross-row-identity-merge-refused',
90112
'driver-turso-config-local-path-wasm-retired',
113+
'element-data-source-and-object-block-filter-rule-array',
114+
'element-number-filter-rule-array',
115+
'element-record-picker-filter-rule-array',
91116
'engine-dotted-filter-refused',
92117
'engine-dotted-projection-refused',
93118
'engine-find-formula-filter-refused',
94119
'engine-find-formula-order-by-refused',
95120
'engine-update-upsert-retired',
121+
'filter-between-blank-endpoint-refused',
122+
'filter-between-field-reference-endpoint-refused',
123+
'filter-comparand-types-and-widget-nested-slots-refused-at-save',
124+
'filter-equality-array-comparand-refused',
125+
'filter-equality-array-comparand-refused-at-save',
126+
'filter-icontains-comparand-refused-at-parse',
127+
'filter-ne-array-comparand-refused',
128+
'filter-preset-ordering-comparand-refused',
129+
'filter-query-face-comparands-refused-at-save',
130+
'filter-regex-options-retired',
131+
'filter-text-operator-declared-type-refused',
96132
'kernel-compatibility-matrix-estimated-migration-time-unit-in-key',
97133
'kernel-context-preview-mode-retired',
98134
'kernel-event-bus-retention-unit-in-key',

‎packages/spec/spec-changes.json‎

Lines changed: 22 additions & 22 deletions
Large diffs are not rendered by default.

‎packages/spec/src/migrations/entries/semantic/18.element-data-source-and-object-block-filter-rule-array.ts‎

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -23,30 +23,32 @@ export const entry: SemanticMigration = {
2323
+ 'also took — becomes `[{ field: \'owner_id\', operator: \'equals\', value: '
2424
+ '\'{current_user_id}\' }]`; the value placeholders and date macros are unchanged. Legacy '
2525
+ 'operator shorthands (`eq`, `ne`, `gt`, `notIn`, …) are accepted and normalized on parse. '
26-
+ 'The dashboard widget `filter` (`dashboard.zod.ts`) is a different family and is not moved '
27-
+ 'by this entry (#15829); `object-grid.defaultFilters` is a different key and is not named '
26+
+ 'The dashboard widget `filter` (`dashboard.zod.ts`) is a different family, judged on its '
27+
+ 'own, and is not moved by this entry; `object-grid.defaultFilters` is a different key and is not named '
2828
+ 'by the ruling this entry records.',
2929
reason:
30-
'One filter orthography platform-wide (objectui#6206, maintainer batch adjudication '
31-
+ '2026-08-25, verbatim 「同意」, Option B) reached two more locations the ComponentPropsMap '
32-
+ 'census could not see (#15442 anchor, #15449 member; decision batch #55, 2026-09-06, '
33-
+ 'verbatim 「同意」, option A: converge family-wide, one entry). The binding-level '
30+
'One filter orthography platform-wide (the maintainer\'s 2026-08-25 ruling, option B: a '
31+
+ 'filter door takes the `ViewFilterRule` array rather than keeping a record-shaped '
32+
+ 'exception every author and AI would have to remember) reached two more locations the '
33+
+ 'ComponentPropsMap census could not see (ruled 2026-09-06, option A: converge the binding '
34+
+ 'and the four block doors family-wide, under one entry, rather than record an exception). The binding-level '
3435
+ '`dataSource.filter` alone still said `FilterConditionSchema`: it refused the array the '
3536
+ 'consumer\'s own pins author at that key, and `element:record_picker` carried two '
3637
+ 'orthographies at two keys (`properties.filter` the rule array, `dataSource.filter` the '
3738
+ 'record) resolved through one `??` in the renderer — the shape in which a dropped or '
3839
+ 'misread filter returns the wrong rows without an error. The four `object-*` doors said '
39-
+ '`z.unknown()`: a read-point record derived from the renderers on 2026-08-13 (#7751), '
40+
+ '`z.unknown()`: a read-point record derived from the renderers on 2026-08-13, when the '
41+
+ '`object-*` blocks first got props schemas in the map, '
4042
+ 'twelve days before the ruling, not an exception to it — so an author following the '
4143
+ 'showcase wrote the record and an author following the manifest wrote an array, and each '
4244
+ 'got a silent success receipt while the html tier already declared `array` for the grid '
4345
+ 'and the metric. The record\'s `$and` / `$or` / `$not` keys were misread by every gate '
44-
+ 'block anyway (objectui#6948), so the exception would have preserved a capability the '
46+
+ 'block anyway (the console\'s filter converter had no branch for them), so the exception would have preserved a capability the '
4547
+ 'consumer does not honour. Sequenced measurement-first, as the family had to be: at the '
4648
+ 'objectui pin `a472b07` the `object-metric` aggregate path posted an array `where` that '
47-
+ '`POST /analytics/query` refused with 400 on every array form (#15828), so the converge '
48-
+ 'was parked behind the pin bump #16626; at the pin this repo builds against (`53ded82b`, '
49-
+ 'objectui#7754) the adapter lowers an authored array through `translateFilterArray` and '
49+
+ '`POST /analytics/query` refused with 400 on every array form, so the converge '
50+
+ 'was parked behind a bump of that pin; at the pin this repo builds against (`53ded82b`) '
51+
+ 'the adapter lowers an authored array through `translateFilterArray` and '
5052
+ 'the spec\'s own `parseFilterAST` sink before the wire, `ObjectGrid.tsx` lowers a rule '
5153
+ 'array through `toFilterNode`, `ObjectKanban.tsx` / `ObjectCalendar.tsx` hand it verbatim '
5254
+ 'to `$filter` where `convertQueryParams` lowers it, and the binding\'s composition seam '
@@ -58,7 +60,9 @@ export const entry: SemanticMigration = {
5860
+ 'in the same change, and zero outside those files; this entry carries the prescription '
5961
+ 'for authors outside the repo. '
6062
+ 'Metadata AT REST: the mappable part of the table above is a D2 conversion, '
61-
+ '`page-component-filter-record-to-rule-array` (#17321, ruling B), so '
63+
+ '`page-component-filter-record-to-rule-array` (ruled 2026-09-12, option B: convert what '
64+
+ 'maps losslessly and name what does not, rather than leave every stored row to its next '
65+
+ 'save or flatten combinators), so '
6266
+ '`os migrate meta --stored` (the pass over a deployment\'s `sys_metadata` rows) rewrites '
6367
+ 'a stored page whose `filter` is a flat record, an operator object whose operators the '
6468
+ 'rule vocabulary spells, several such keys, or a single-level AST tuple array, and every '
@@ -95,7 +99,8 @@ export const entry: SemanticMigration = {
9599
+ 'a record-form `filter: { status: \'active\' }` is refused at the `filter` path of all '
96100
+ 'five doors (`invalid_type`, expected array), and an AST tuple array is refused at '
97101
+ '`filter.0` (expected object). No `filter` door in `ComponentPropsMap` accepts the '
98-
+ 'record any more (the twin of the #14406 census pin). At runtime each block and the '
102+
+ 'record any more (the twin of the census pin that asks whether any `filter` door still '
103+
+ 'refuses the array). At runtime each block and the '
99104
+ 'binding select exactly the rows the array selects — the same filter a list view '
100105
+ 'renders — including the `object-metric` aggregate tile, whose analytics `where` is the '
101106
+ 'lowered condition. Downstream (objectui, after a released spec version reaches the '

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

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,27 +17,32 @@ export const entry: SemanticMigration = {
1717
+ 'several rules (they AND). Legacy operator shorthands (`eq`, `gt`, `notIn`, …) are '
1818
+ 'accepted and normalized on parse',
1919
reason:
20-
'One filter orthography platform-wide (objectui#6206, maintainer batch adjudication '
21-
+ "2026-08-25, verbatim 「同意」, Option B). `ComponentPropsMap['element:number'].filter` "
20+
'One filter orthography platform-wide (the maintainer\'s 2026-08-25 ruling, option B: '
21+
+ 'align the element to the `ViewFilterRule` array rather than keep it the record-shaped '
22+
+ "exception). `ComponentPropsMap['element:number'].filter` "
2223
+ 'was the one `filter` input in the map declared as the MongoDB-style record '
2324
+ '(`FilterConditionSchema`) while its siblings declared the `ViewFilterRule` array, so '
2425
+ 'the filter a list view stores and renders was refused by the KPI element beside it, '
2526
+ 'and the objectui parity gate had to carry a reasoned exemption to look away. The '
2627
+ 'convergence was sequenced consumer-first (ruling recorded 2026-08-25, Option A): '
27-
+ 'objectui#6828 made `ObjectStackAdapter.aggregate()` run the same `translateFilterArray` '
28+
+ 'the console adapter was changed so `ObjectStackAdapter.aggregate()` runs the same '
29+
+ '`translateFilterArray` '
2830
+ '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. '
31+
+ 'entry moved — but that measurement named the wrong hop, and the runtime route\'s refusal '
32+
+ 'of the array corrects it here. '
3033
+ '`translateFilterArray` yields AST tuples, which are still a `FilterArray` — input-only '
3134
+ 'sugar — so the real path is: authored array → `translateFilterArray` → lowered by '
3235
+ '`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 '
36+
+ 'names, since the maintainer\'s 2026-08-04 ruling C declared the array input-only sugar '
37+
+ 'with one lowering seam) in the adapter, BEFORE the wire → a `FilterCondition` on the '
3438
+ 'body. The hop that decides it is the runtime route `POST /analytics/query`, which '
3539
+ 'parses `where` with `AnalyticsQueryRequestSchema` — a `FilterCondition` and nothing '
3640
+ 'else — so an un-lowered array is refused there before any service code runs. '
3741
+ '`lowerAnalyticsWhere` (`service-analytics`), where that earlier measurement stopped, '
38-
+ 'is the IN-PROCESS door (#5334) for callers reaching `analyticsService.query` '
42+
+ 'is the IN-PROCESS door (added when an array `where` was found silently dropped on the '
43+
+ 'analytics path) for callers reaching `analyticsService.query` '
3944
+ "directly, not the wire's; it too still refuses a RAW rule-object array by design. "
40-
+ 'objectui#7752 lands the adapter-side lowering. '
45+
+ 'The adapter-side lowering lands in the console\'s own repository. '
4146
+ 'The ruled migration check ran with the change: the '
4247
+ 'sweep of first-party corpora (examples/, skills/, create-objectstack, content/docs/, '
4348
+ 'packages/apps/, spec fixtures) found ONE `element:number` author writing a record-form '
@@ -53,5 +58,5 @@ export const entry: SemanticMigration = {
5358
+ 'filter a list view renders. Downstream (objectui, after a released spec version reaches '
5459
+ "the pin): the `element:number.filter:array` entry in `OFF_SPEC_ARM_EXEMPTIONS` "
5560
+ '(`registry-inputs-spec-parity.test.ts`) becomes deletable, which is what closes '
56-
+ 'objectui#6206.',
61+
+ 'the console-side half of this convergence.',
5762
};

‎packages/spec/src/migrations/entries/semantic/18.element-record-picker-filter-rule-array.ts‎

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,24 +11,26 @@ export const entry: SemanticMigration = {
1111
'`z.array(ViewFilterRuleSchema)` — the rule array `[{ field, operator, value }, ...]` '
1212
+ "the map's array-declared `filter` doors already carry (`record:related_list`, its nested "
1313
+ 'Add-affordance picker, `element:number`; the four `object-*` blocks declare `filter` as '
14-
+ '`z.unknown()`, #15449). A record-form filter '
14+
+ '`z.unknown()`, a gap measured on its own). A record-form filter '
1515
+ "`{ status: 'active' }` becomes `[{ field: 'status', operator: 'equals', value: 'active' }]`; "
1616
+ "an operator object `{ amount: { $gt: 100 } }` becomes "
1717
+ "`[{ field: 'amount', operator: 'greater_than', value: 100 }]`; several keys become "
1818
+ 'several rules (they AND). Legacy operator shorthands (`eq`, `gt`, `notIn`, …) are '
1919
+ 'accepted and normalized on parse. The binding-level `dataSource.filter` on the same node '
2020
+ 'is a different key (`ElementDataSourceSchema`) and is not moved by this entry',
2121
reason:
22-
'One filter orthography platform-wide (objectui#6206, maintainer batch adjudication '
23-
+ "2026-08-25, verbatim 「同意」, Option B). `ComponentPropsMap['element:record_picker'].filter` "
22+
'One filter orthography platform-wide (the maintainer\'s 2026-08-25 ruling, option B: '
23+
+ 'every `filter` door takes the `ViewFilterRule` array rather than keeping record-shaped '
24+
+ "exceptions). `ComponentPropsMap['element:record_picker'].filter` "
2425
+ 'was the LAST `filter` input in the map still declared as the MongoDB-style record '
25-
+ '(`FilterConditionSchema`) after `element:number` converged (#12039 Key 2): the three '
26+
+ '(`FilterConditionSchema`) after `element:number` converged: the three '
2627
+ 'array-declared doors (`record:related_list`, its nested Add-affordance picker, '
2728
+ '`element:number`) carried the `ViewFilterRule` array and the four `object-*` doors '
28-
+ 'declare `z.unknown()` (#15449), so the filter a list view stores and renders was refused '
29+
+ 'declare `z.unknown()`, so the filter a list view stores and renders was refused '
2930
+ 'by the picker beside them, and a lone holdout is the state where the next author copies '
3031
+ 'the wrong form. Sequenced measurement-first, as that convergence had to be (the 2026-08-25 '
31-
+ 'Option-A ordering ruling, #14406): at the objectui pin `00d3f09c` the renderer hands '
32+
+ 'Option-A ordering ruling: measure the consumer\'s read path before the contract moves): '
33+
+ 'at the objectui pin `00d3f09c` the renderer hands '
3234
+ '`filter` to `query.$filter` and calls `adapter.find()` '
3335
+ '(`components/src/renderers/basic/record-picker.tsx`); `ObjectStackAdapter.convertQueryParams` '
3436
+ 'lowers an ARRAY `$filter` through `translateFilterArray` into filter AST tuples '
@@ -50,5 +52,5 @@ export const entry: SemanticMigration = {
5052
+ "a released spec version reaches the pin): the registry's `inputs.filter` entry for "
5153
+ "`element:record_picker` (`type: 'object'`, `record-picker.tsx`) flips to the array arm and "
5254
+ 'the `record-picker-inputs-spec-parity.test.ts` pins that assert the record form follow — '
53-
+ 'objectui#7663, filed from #14406 with a Blocked-by line.',
55+
+ 'a console-side change filed in the objectui repository, blocked on that release.',
5456
};

0 commit comments

Comments
 (0)