Skip to content

Commit e295f36

Browse files
committed
fix(spec): the view-chart-binding-dataset-required guidance names no tracker id; regenerate its projections
Claude-Session: https://claude.ai/code/session_01KNKBCRDJCu5tGy3TEbvtrF Co-authored-by: Claude <noreply@anthropic.com>
1 parent b83e8aa commit e295f36

4 files changed

Lines changed: 5 additions & 5 deletions

File tree

‎docs/protocol-upgrade-guide.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1537,7 +1537,7 @@ This is a RUNTIME registration API, not stored metadata, so — like `hook-regis
15371537
- Why not automatic: ADR-0049 enforce-or-remove, the enforce arm: the four keys stay declared (they are the selection of every non-joined report), and the one report type that never reads them now refuses them. A `joined` report selects nothing itself, and the refinement already said so for `order` alone — it refused a container `order` with a pointer onto `blocks[]` while the four selection keys beside it parsed green. Measured at this repo's `.objectui-sha` pin `f8a9d0fb0596f4521076628e2bbfe27e6ce67d52`: `DatasetReportRenderer`'s joined branch (`DatasetReportRenderer.tsx:1462`) reads `blocks`, plus the container `runtimeFilter` and `drilldown` resolved above it, and returns before the top-level reads of `columns` / `dataset` / `rows` / `values` begin (line 1529 onward) — so each was accepted by the metadata layer and dropped by the renderer without a word. The alias tables made it reachable: `fields` / `measures` / `metrics` route to `values`, `groupings` / `groupBy` / `dimensions` to `rows`, and `objectName` / `object` / `dataSet` / `source` to `dataset`, on a joined report as on any other. Studio's report inspector hides the top-level binding for a joined report (`ReportDefaultInspector.tsx:328`) but its type picker patches only `type`, so a report bound first and switched to `joined` second carries the keys invisibly. An empty list is NOT refused: it selects nothing, which is what a joined container selects — the container `order` refusal's own threshold. Ships at once, no deprecation window: there is no window in which a key the renderer never reads does anything.
15381538
- Done when: WHICH DOOR: this is the spec schema's refusal, so it lands wherever a report is parsed through `@objectstack/spec` — `defineReport`, `os validate` / `os build`, and the metadata save door (the `report` entry of the metadata type registry) — as one `custom` issue per key at `dataset` / `rows` / `columns` / `values`. A stored `sys_metadata` report row is not rewritten: it carries the same issue in its read-side `_diagnostics` and is refused on its next save. Fix each by moving the key onto the blocks that need it or deleting it, then check the rendered report: it renders exactly as before, because the container value was never read. A joined report that carries only `blocks`, `runtimeFilter`, `drilldown` and the identity / protection keys parses byte-identically to before, and every non-joined report is untouched. Census at the time of the change: zero joined reports carry any of the four at the container — in this repo one example-app report, one docs example and five test fixtures across `packages/lint` and `packages/platform-objects`; in objectui every joined-report fixture and docs example at the pin above (13 occurrences); in the cloud repo none exist.
15391539
- **`view-chart-binding-dataset-required`** — `A list view whose type is chart and whose effective chart binding names no dataset: no chart block and no options.chart bag, or, on a flattened view overlay saved through the metadata write door, an options.chart bag missing its dataset or its values while no chart block replaces it. Judged at every list-view door: views[].list and views[].listViews, objects[].listViews, a view item config, and the flattened list overlay.` → Bind the chart: declare a top-level `chart` block naming the ADR-0021 `dataset` to plot and at least one of its measures in `values` (`dimensions`, the X / group axis, stays optional, and `chartType` defaults to `bar`). A view whose only binding is the legacy `options.chart` bag completes the bag with `dataset` and `values`, or, preferred, moves the binding to the top-level `chart` block, which replaces the bag whole. A view that is not meant to be a chart takes another `type`.
1540-
- Why not automatic: ADR-0021 single form, enforced (ADR-0049 enforce-or-remove, the enforce arm; ADR-0078, a view that renders nothing is refused rather than warned). A chart list view plots only the dataset its effective binding names, and the renderer reads that binding as the `chart` block, else the `options.chart` bag, the block replacing the bag whole (objectui plugin-list `ListView`, `resolveListChartBinding`, at this repo's `.objectui-sha` pin and at objectui main alike). The authoring `chart` block already required `dataset` and `values`, but a view with no block at all, or with only the bag, never met that schema. Measured on `origin/main` at `e148ca98`: the flattened overlay member accepted `type: 'chart'` with no block and an `options.chart` bag holding only `chartType`, and both authoring doors accepted the block-less view. What such a view rendered was a dead screen: at the pin the renderer fabricated a binding nobody wrote (an aggregate over a field named name and a measure named value), and objectui#6152 round 15 retired that floor, after which the chart component refuses on screen. Now refused at the view's own path, `chart`, or at `options.chart.dataset` / `options.chart.values`, with the binding to declare. Ships at once, no grace window and no dual spelling (2026-08-27 maintainer ruling 「短期不考虑渐进」). Not convertible: only the author knows which dataset a chart was meant to show.
1540+
- Why not automatic: ADR-0021 single form, enforced (ADR-0049 enforce-or-remove, the enforce arm; ADR-0078, a view that renders nothing is refused rather than warned). A chart list view plots only the dataset its effective binding names, and the renderer reads that binding as the `chart` block, else the `options.chart` bag, the block replacing the bag whole (objectui plugin-list `ListView`, `resolveListChartBinding`, at this repo's `.objectui-sha` pin and at objectui main alike). The authoring `chart` block already required `dataset` and `values`, but a view with no block at all, or with only the bag, never met that schema. Measured on `origin/main` at `e148ca98`: the flattened overlay member accepted `type: 'chart'` with no block and an `options.chart` bag holding only `chartType`, and both authoring doors accepted the block-less view. What such a view rendered was a dead screen: at the pin the renderer fabricated a binding nobody wrote (an aggregate over a field named name and a measure named value), and objectui has since retired that floor, after which the chart component refuses on screen. Now refused at the view's own path, `chart`, or at `options.chart.dataset` / `options.chart.values`, with the binding to declare. Ships at once, no grace window and no dual spelling (2026-08-27 maintainer ruling 「短期不考虑渐进」). Not convertible: only the author knows which dataset a chart was meant to show.
15411541
- Done when: WHICH DOOR: the spec schema's refusal, so it lands wherever a list view is parsed through `@objectstack/spec` — `defineView`, `defineStack`, `os validate` / `os build`, and the metadata write door (`PUT /api/v1/meta/view/:name`, answering `422 INVALID_METADATA`) — as one `custom` issue at `chart` for a view with no binding at all, or one per missing key at `options.chart.dataset` / `options.chart.values` for an incomplete bag (overlay only; the authoring doors refuse `options` by name). A stored `sys_metadata` view row is neither rewritten nor refused on read: measured, the read door serves it as stored with the same issue in its `_diagnostics`, and it is refused on its next save. Fix each chart view by declaring its binding, then open it: it plots the dataset. A chart view that already declares a complete `chart` block parses byte-identically to before, and every view of another type is untouched, a grid that only offers a chart in `allowedVisualizations` included. Census at the time of the change: the two chart list views in `examples/app-showcase` and the chart list views in the `packages/lint` fixtures all bind a dataset and a measure; the only spec test that parsed a block-less chart view was a type-acceptance pin, re-judged in the same change.
15421542
- **`view-filter-rule-absent-value-refused`** — `ui.ViewFilterRule with NO value on an operator that takes one — the value key omitted, or present and undefined, on equals, not_equals, contains, not_contains, icontains, starts_with, ends_with, greater_than, less_than, greater_than_or_equal, less_than_or_equal, before or after (an alias spelling of any of them included), on every carrier of ViewFilterRuleSchema` → the value the rule compares against — value: "open" on equals, value: "2026-01-01" on after. A rule that meant "the field has no value" becomes one of the four operators that take none — is_empty / is_not_empty / is_null / is_not_null — which read their direction from their name and still parse with or without a value. A rule that was an unfinished row is deleted. The list operators (in / not_in) and the range operator (between) refused an absent value before this change and still do, in their own words
15431543
- Why not automatic: The value key's own published description has declared, since the value was first shaped by its operator, that every operator outside the list, range and unary sets takes a scalar, and that only the unary operators ignore the key; the refinement implementing the coupling returned early on an absent value for every operator, so a rule with no value parsed green on all thirteen scalar operators. The query path refuses the same rule: both lowerings of a stored rule — the console's and the REST lookup-picker route's — emit it as the two-element [field, operator] node, which the filter-AST lowering reads as an undefined comparand and refuses with INVALID_FILTER / 400, measured for all thirteen operators. Nothing between storage and the query drops the rule, so one such rule failed every query that read its view, the view's other rules included. The first-party producer does not write the shape: the console filter builder drops a row whose operator takes a value and whose value is missing before it saves, and the drill-down save-as-view path checks each rule against this schema before persisting it (read at the pinned objectui commit). Metadata AT REST is deliberately NOT rewritten and this entry adds no D2 conversion: there is no value to infer, and writing a value, switching to a unary operator and deleting the rule are three different predicates only the author can choose between. The read path does not re-validate stored rows (the reading the sibling entry view-filter-rule-scalar-operator-array-refused records), so a stored view keeps loading — and keeps failing its queries, as it did before this change; what changes is that RE-SAVING it is refused at the value path, naming the operator and the field. ADR-0049 / ADR-0087 / ADR-0112.

‎packages/spec/spec-changes.json‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3506,7 +3506,7 @@
35063506
"replacement": "Bind the chart: declare a top-level `chart` block naming the ADR-0021 `dataset` to plot and at least one of its measures in `values` (`dimensions`, the X / group axis, stays optional, and `chartType` defaults to `bar`). A view whose only binding is the legacy `options.chart` bag completes the bag with `dataset` and `values`, or, preferred, moves the binding to the top-level `chart` block, which replaces the bag whole. A view that is not meant to be a chart takes another `type`.",
35073507
"migrationId": "view-chart-binding-dataset-required",
35083508
"toMajor": 18,
3509-
"rationale": "ADR-0021 single form, enforced (ADR-0049 enforce-or-remove, the enforce arm; ADR-0078, a view that renders nothing is refused rather than warned). A chart list view plots only the dataset its effective binding names, and the renderer reads that binding as the `chart` block, else the `options.chart` bag, the block replacing the bag whole (objectui plugin-list `ListView`, `resolveListChartBinding`, at this repo's `.objectui-sha` pin and at objectui main alike). The authoring `chart` block already required `dataset` and `values`, but a view with no block at all, or with only the bag, never met that schema. Measured on `origin/main` at `e148ca98`: the flattened overlay member accepted `type: 'chart'` with no block and an `options.chart` bag holding only `chartType`, and both authoring doors accepted the block-less view. What such a view rendered was a dead screen: at the pin the renderer fabricated a binding nobody wrote (an aggregate over a field named name and a measure named value), and objectui#6152 round 15 retired that floor, after which the chart component refuses on screen. Now refused at the view's own path, `chart`, or at `options.chart.dataset` / `options.chart.values`, with the binding to declare. Ships at once, no grace window and no dual spelling (2026-08-27 maintainer ruling 「短期不考虑渐进」). Not convertible: only the author knows which dataset a chart was meant to show."
3509+
"rationale": "ADR-0021 single form, enforced (ADR-0049 enforce-or-remove, the enforce arm; ADR-0078, a view that renders nothing is refused rather than warned). A chart list view plots only the dataset its effective binding names, and the renderer reads that binding as the `chart` block, else the `options.chart` bag, the block replacing the bag whole (objectui plugin-list `ListView`, `resolveListChartBinding`, at this repo's `.objectui-sha` pin and at objectui main alike). The authoring `chart` block already required `dataset` and `values`, but a view with no block at all, or with only the bag, never met that schema. Measured on `origin/main` at `e148ca98`: the flattened overlay member accepted `type: 'chart'` with no block and an `options.chart` bag holding only `chartType`, and both authoring doors accepted the block-less view. What such a view rendered was a dead screen: at the pin the renderer fabricated a binding nobody wrote (an aggregate over a field named name and a measure named value), and objectui has since retired that floor, after which the chart component refuses on screen. Now refused at the view's own path, `chart`, or at `options.chart.dataset` / `options.chart.values`, with the binding to declare. Ships at once, no grace window and no dual spelling (2026-08-27 maintainer ruling 「短期不考虑渐进」). Not convertible: only the author knows which dataset a chart was meant to show."
35103510
},
35113511
{
35123512
"surface": "ui.ViewFilterRule with NO value on an operator that takes one — the value key omitted, or present and undefined, on equals, not_equals, contains, not_contains, icontains, starts_with, ends_with, greater_than, less_than, greater_than_or_equal, less_than_or_equal, before or after (an alias spelling of any of them included), on every carrier of ViewFilterRuleSchema",
@@ -7109,7 +7109,7 @@
71097109
"replacement": "Bind the chart: declare a top-level `chart` block naming the ADR-0021 `dataset` to plot and at least one of its measures in `values` (`dimensions`, the X / group axis, stays optional, and `chartType` defaults to `bar`). A view whose only binding is the legacy `options.chart` bag completes the bag with `dataset` and `values`, or, preferred, moves the binding to the top-level `chart` block, which replaces the bag whole. A view that is not meant to be a chart takes another `type`.",
71107110
"migrationId": "view-chart-binding-dataset-required",
71117111
"toMajor": 18,
7112-
"rationale": "ADR-0021 single form, enforced (ADR-0049 enforce-or-remove, the enforce arm; ADR-0078, a view that renders nothing is refused rather than warned). A chart list view plots only the dataset its effective binding names, and the renderer reads that binding as the `chart` block, else the `options.chart` bag, the block replacing the bag whole (objectui plugin-list `ListView`, `resolveListChartBinding`, at this repo's `.objectui-sha` pin and at objectui main alike). The authoring `chart` block already required `dataset` and `values`, but a view with no block at all, or with only the bag, never met that schema. Measured on `origin/main` at `e148ca98`: the flattened overlay member accepted `type: 'chart'` with no block and an `options.chart` bag holding only `chartType`, and both authoring doors accepted the block-less view. What such a view rendered was a dead screen: at the pin the renderer fabricated a binding nobody wrote (an aggregate over a field named name and a measure named value), and objectui#6152 round 15 retired that floor, after which the chart component refuses on screen. Now refused at the view's own path, `chart`, or at `options.chart.dataset` / `options.chart.values`, with the binding to declare. Ships at once, no grace window and no dual spelling (2026-08-27 maintainer ruling 「短期不考虑渐进」). Not convertible: only the author knows which dataset a chart was meant to show."
7112+
"rationale": "ADR-0021 single form, enforced (ADR-0049 enforce-or-remove, the enforce arm; ADR-0078, a view that renders nothing is refused rather than warned). A chart list view plots only the dataset its effective binding names, and the renderer reads that binding as the `chart` block, else the `options.chart` bag, the block replacing the bag whole (objectui plugin-list `ListView`, `resolveListChartBinding`, at this repo's `.objectui-sha` pin and at objectui main alike). The authoring `chart` block already required `dataset` and `values`, but a view with no block at all, or with only the bag, never met that schema. Measured on `origin/main` at `e148ca98`: the flattened overlay member accepted `type: 'chart'` with no block and an `options.chart` bag holding only `chartType`, and both authoring doors accepted the block-less view. What such a view rendered was a dead screen: at the pin the renderer fabricated a binding nobody wrote (an aggregate over a field named name and a measure named value), and objectui has since retired that floor, after which the chart component refuses on screen. Now refused at the view's own path, `chart`, or at `options.chart.dataset` / `options.chart.values`, with the binding to declare. Ships at once, no grace window and no dual spelling (2026-08-27 maintainer ruling 「短期不考虑渐进」). Not convertible: only the author knows which dataset a chart was meant to show."
71137113
},
71147114
{
71157115
"surface": "ui.ViewFilterRule with NO value on an operator that takes one — the value key omitted, or present and undefined, on equals, not_equals, contains, not_contains, icontains, starts_with, ends_with, greater_than, less_than, greater_than_or_equal, less_than_or_equal, before or after (an alias spelling of any of them included), on every carrier of ViewFilterRuleSchema",

‎packages/spec/src/migrations/entries/semantic/18.view-chart-binding-dataset-required.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ export const entry: SemanticMigration = {
3535
+ 'overlay member accepted `type: \'chart\'` with no block and an `options.chart` bag holding only '
3636
+ '`chartType`, and both authoring doors accepted the block-less view. What such a view rendered was a '
3737
+ 'dead screen: at the pin the renderer fabricated a binding nobody wrote (an aggregate over a field '
38-
+ 'named name and a measure named value), and objectui#6152 round 15 retired that floor, after which '
38+
+ 'named name and a measure named value), and objectui has since retired that floor, after which '
3939
+ 'the chart component refuses on screen. Now refused at the view\'s own path, `chart`, or at '
4040
+ '`options.chart.dataset` / `options.chart.values`, with the binding to declare. Ships at once, no '
4141
+ 'grace window and no dual spelling (2026-08-27 maintainer ruling 「短期不考虑渐进」). Not convertible: '

‎packages/spec/src/migrations/registry.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22977,7 +22977,7 @@ const step18: MigrationStep = {
2297722977
+ 'overlay member accepted `type: \'chart\'` with no block and an `options.chart` bag holding only '
2297822978
+ '`chartType`, and both authoring doors accepted the block-less view. What such a view rendered was a '
2297922979
+ 'dead screen: at the pin the renderer fabricated a binding nobody wrote (an aggregate over a field '
22980-
+ 'named name and a measure named value), and objectui#6152 round 15 retired that floor, after which '
22980+
+ 'named name and a measure named value), and objectui has since retired that floor, after which '
2298122981
+ 'the chart component refuses on screen. Now refused at the view\'s own path, `chart`, or at '
2298222982
+ '`options.chart.dataset` / `options.chart.values`, with the binding to declare. Ships at once, no '
2298322983
+ 'grace window and no dual spelling (2026-08-27 maintainer ruling 「短期不考虑渐进」). Not convertible: '

0 commit comments

Comments
 (0)