Skip to content

Commit 20e7d52

Browse files
feat(spec)!: a type: 'chart' list view whose effective binding names no dataset is refused at every list-view door (#22528)
Fixes #22491 Clause-②: yes (narrowing) Claim `6083229542` (session `session_01KNKBCRDJCu5tGy3TEbvtrF`, branch `claude/issue-22491-chart-view-needs-dataset`), triage direction `6082420385`. Every list-view door now refuses a `type: 'chart'` list view whose **effective** chart binding names no dataset, with the binding to declare. No default binding is fabricated. ## What changes - **The rule** (`packages/spec/src/ui/view.zod.ts`): `checkListViewChartBinding`, exported, attached by identifier at the same three list-view doors as `checkListViewCalendarVisualization`: - `ListViewSchema`, which also covers `views[].list` / `listViews`, a view item `config` and `defineView`; - `ObjectListViewSchema` (`objects[].listViews`); - the flattened list overlay member of the view write door (`PUT /api/v1/meta/view/:name`). One rule judges every door. There is no door-specific second contract. - **The effective binding** is the renderer's: the `chart` block, else the legacy `options.chart` bag, with the block replacing the bag whole. objectui `packages/plugin-list/src/ListView.tsx` `resolveListChartBinding`, `schema.chart || schema.options?.chart || {}`: `:207` at this repo's `.objectui-sha` pin `f0268ad7`, `:260` at objectui `2a48bd40`. Read only; nothing in objectui was touched. - no block and no bag: one `custom` issue at `chart`. Its first sentence is "This list view is `type: 'chart'` but declares no `chart` block, so it binds no dataset and there is nothing to plot." The remedy names `chart.dataset` and `chart.values`. - no block, and a bag missing `dataset` or `values`: one `custom` issue per missing key, at `options.chart.dataset` / `options.chart.values`. Only the overlay carries the bag; the two authoring doors refuse `options` by name. - a declared `chart` block: left to its own strict schema, which already requires both keys at `chart.dataset` / `chart.values`, so nothing is reported twice. - The overlay reads `type` on the input side, as `checkListOverlayTypeNeedsColumns` does. A patch that names no `type` (the console's toolbar save) is not judged; the view it shadows decides. - The `chart` slot gains a `.describe()` saying a chart view must bind one. The reference pages regenerate with it. - **Ledger:** the D3 semantic entry `view-chart-binding-dataset-required` (step 18). It has no rationale fragment, so the hand-written region of `registry.ts` is untouched and only the generated region moved. There is no D2 conversion, because only the author knows the dataset. The changeset is `major` on `@objectstack/spec`, because pre mode `next` takes a breaking change at `major`. It carries the registered ADR-0087 marker. ## Premise, measured on `origin/main` @ `e148ca98` (before) | body | overlay member | `ViewMetadataSchema` union | `ListViewSchema` | `ObjectListViewSchema` | |---|---|---|---|---| | `type: 'chart'`, no block | ACCEPT | ACCEPT | ACCEPT | ACCEPT | | `options.chart: { chartType }` only | ACCEPT | ACCEPT | refused (`options` unknown key) | refused (same) | | `chart: { chartType }` | refused at `chart.dataset` / `chart.values` | refused | refused | refused | After the change, the first two rows are refused at `chart` and at `options.chart.dataset` + `options.chart.values`. The container member (`list`) and the view item member (`config`) refuse them too, at `list.chart` / `config.chart`. ## Stored rows (Zone 2 item 4), measured `applyConversionsToStoredItem` is the one rehydration primitive. It replays only the conversion registry, and no entry touches a list-view chart block. The read door does not re-validate: it decorates. `meta-view-chart-binding.test.ts` measures this through the real `PUT` / `GET /api/v1/meta/view/:name` over SQLite. A row stored with no binding, planted directly in `sys_metadata`, is served `200` as stored, with `_diagnostics.valid: false` and the same issue at `chart`. Re-saving the body that was read answers `422 INVALID_METADATA` at `chart`. So no stored view becomes unreadable, and each one is refused on its next save. The ADR-0087 disposition is `registered view-chart-binding-dataset-required`; the changeset states it, along with this read-side behaviour. ## Pins - `packages/spec/src/ui/view-chart-binding.test.ts`, across all three doors: - `type: 'chart'` with no block is refused at `chart`. The test checks the `code`, the path and the message's first sentence, and that the remedy names both keys. - Control: a complete block saves and is kept. - A declared incomplete block gets no second issue. - Grid views are not judged, including one that lists `chart` in `allowedVisualizations`. - Overlay only: - a bag holding only `chartType` is refused at both keys; - a bag with `dataset` but no `values` is refused at `values`; - a complete bag is accepted and kept; - an incomplete bag under a complete block is accepted, because the block replaces the bag whole; - a patch that names no `type` is not judged; - a drift pin derives `ListChartConfigSchema`'s required keys and asks the bag for exactly those. - `packages/spec/src/ui/object-refinement-check-exports.test.ts` catalogues the export: - parity and bijection over its fixture matrix; - three attachments by identifier, chained after the calendar check; - barrel identity. - `packages/rest/src/meta-view-chart-binding.test.ts` exercises the real door (`RestServer` route, then `saveMetaItem`, then ObjectQL with SQLite): - the ADR-0112 envelope `422` + `INVALID_METADATA`, the issue path, and an empty store for both refusals; - control `200`, with the stored row carrying its binding; - the stored-row read and re-save described above. ## Fixture triage (consumer radius, not just `packages/spec`) I grepped `type: 'chart'` across `examples/**`, `packages/**` and `skills/**`: - The two chart list views in `examples/app-showcase` (`project.view.ts`, `task.view.ts`) bind a dataset and measures. Importing the showcase's `src/ui/views` against the built spec runs `defineView` at import and parsed 26 list views, 2 of them charts. - The lint fixtures (`showcase-shape.fixtures.ts`, `validate-chart-bindings.test.ts`) bind a dataset and a measure. - The conversion-registry and `additional-types` hits are dashboard widgets and a metadata type name, not list views. Two spec pins parsed a block-less chart view as a stand-in for "every type". Each was re-judged as missing a declaration, and now carries the binding: - `view.test.ts`, "keeps every surviving view type accepting exactly as before"; - `view-form-pagination.test.ts`, "is backed by the door: type 'chart' parses a pagination block". Studio's view form does have a `chart` section (`view.form.ts`), so a Studio author can declare the binding. ## Reverse verification I ran this once in a second worktree at `7836b326`, after committing the fix. The mutation went through `scripts/ablation-replace.mjs`: the check's guard line became an unconditional `return`, the anchor count went 1 to 0, and the blob went `0915637f` to `d75f30ac`. The two spec pin files then ran **10 failed / 204 passed**: - every refusal pin went red, at all three doors and in the bag cases; - the export parity and bijection legs went red; - every control stayed green. The direction was the expected one: the pins turned red. The restore was proven: the blob equals HEAD and `git diff HEAD` is empty. These spec tests resolve `./view.zod` from source, so no `dist` rebuild was involved. The REST pin reads the built spec and was not ablated. ## Verification (local; first round at `7836b326`, patch round at `e295f361`; CI owns the full farm) Build: `pnpm --filter @objectstack/spec build`, then `check:generated` found 3 of 15 artifacts stale (`api-surface`, `export-origins`, `docs`). `--fix` regenerated only those three, and a re-check reported all 15 up to date. After that I built the dependency closure of `rest`, `lint` and `metadata*` with turbo: 24/24 tasks. | run | result | |---|---| | `@objectstack/spec` tests, 4 shards (`--project local`) | 158/158 files (5261 tests), 157/158 then re-run, 158/158 files (4012 tests), 156 + 1 skipped (5032 tests). The one shard-2 red was the pagination pin re-judged above; it was fixed in `7836b326`, and the four touched spec test files re-run at `7836b326` gave 4/4 files, 732 tests. | | `@objectstack/spec` typecheck (`tsc` + scripts + test layer) | exit 0 | | `@objectstack/lint` tests | 130/130 files, 5943 tests | | `@objectstack/rest` tests (`--project local`, full) | 269/269 files, 4979 passed / 326 skipped, including `meta-view-chart-binding.test.ts` | | `@objectstack/rest` typecheck (`tsc` + test layer) | exit 0 | | `@objectstack/metadata-core` / `metadata-fs` tests | 17/17 files (387 tests), 10/10 files (70 tests) | | `@objectstack/metadata` / `metadata-protocol` (narrowed, see below) | 4/4 files (51 tests), 31/31 files (1247 tests) | | `examples/app-showcase` views, imported against the built spec | 26 list views (2 charts) parse | | eslint on the 8 changed `.ts` files, `--format json` | 8 files, 0 errors, 0 warnings | | `dispatch-gates --commands` (114 derived) + the 49 artifact-roster commands | 156 at exit 0. `check:skill-examples` re-ran at 0 after its `client-react` prerequisite was built. `check:dual-build-cjs-loads` is NOT MEASURED: it exits 3 with PREREQUISITE NOT MET because it needs every package built. Three roster guards (`closing-target-claim`, `partof-closing-keyword`, `single-claim-paths`) exit 2 NOT WIRED without a PR; `partof-closing-keyword` was re-run on this body and passed (exit 0). `--ran` reports "114 derived famil(ies) accounted for — 113 run, 1 NOT-MEASURED". | **Declared narrowing (coordinator-approved): `metadata` and `metadata-protocol` ran their view-door test files only; the full suites are CI's.** - Why the narrowing cannot hide a regression: the new check fires only on `type === 'chart'`. `git grep -nE "['\"]chart['\"]"` over `packages/metadata`, `packages/metadata-protocol`, `packages/metadata-core`, `packages/metadata-fs`, `packages/rest` and `packages/objectql`, leaving out my own new test, finds two hits. Neither is a list view: one is a report container in `protocol.invalid-metadata-422-face-inventory.test.ts`, the other a zod union member in `zod-union-fields.test.ts`. - A search for iteration of the list-view `type` enum over the same packages (`shape\.type|overlayTypeValues|'gantt', ?'map'`) finds only `page` type reads. The control term in the same corpus, `viewKind|ListViewSchema|ViewMetadataSchema`, hits 4 test files in `metadata` and 29 in `metadata-protocol`. - Files run instead: those 33, plus `protocol.stored-conversions`, `protocol.invalid-metadata-422-face-inventory` and `protocol.read-decorations` (31 in `metadata-protocol` once de-duplicated). `metadata`: `metadata-manager-views-by-object-container`, `plugin-artifact-view-container-object`, `view-container-name`, `view-expand`. - The lint narrowing is a measurement. The population comes from `eslint.config.mjs`'s own `files: ['**/*.{ts,…}']` objects; the file count comes from `--format json`. That config never enables type-aware linting (no `parserOptions.project`; it says so at `:327`), so the diff cannot move the verdict on any file it does not touch. **Patch round, at `e295f361`.** - **Sync.** Merged `origin/main` at `ee8751d4` with `scripts/pm/os-regen-merge.sh` (no rebase) into merge commit `be6f3e23`. That brought in protocol 18 from `4e9fe9ff`. - The script's step-3 hand-off commit `d3900905` regenerated `content/docs/references/data/object.mdx` from the merged tree, after a spec build. That build ran after the merge commit, never in the MERGE state. - `742960d6` contains only `gen:spec-changes` + `gen:upgrade-guide` output. - Every step-18 entry id on `origin/main` is still present after the merge; the only id added is `view-chart-binding-dataset-required`. - **Changeset** (`b83e8aaf`): `Clause-②: yes (narrowing)`, and one sentence naming the new export `checkListViewChartBinding`. `check-changeset-no-major`, `check-adr-0087-registration` ("registered view-chart-binding-dataset-required (new here)"), `check-empty-changeset` and `check:changeset-gate-self-tests` each exit 0. - **Test Core fix** (`e295f361`): the D3 entry's `reason` named an objectui tracker id, which the `migrate meta` guidance must not print. That wording is gone, and the projections were regenerated with `gen:migration-registry` / `gen:spec-changes` / `gen:upgrade-guide`. No other `#` plus 4–5 digit id is left in this diff's printed text (refusal messages, `.describe()`, changeset, entry strings). - **Re-run, one lock call per suite:** - the four touched spec test files: 4/4 files, 732 tests (at `b83e8aaf`); - spec typecheck: exit 0 (at `b83e8aaf`); - `rest` `meta-view-chart-binding.test.ts` after a turbo build of `@objectstack/rest^...` (24/24): 1/1 file, 4 tests (at `b83e8aaf`); - `packages/cli` `test/migrate-meta-engine-guidance.test.ts` (`--project integration`), after a turbo build of `@objectstack/cli...` (59/59): 1/1 file, 3 tests (at `e295f361`). - `e295f361` changes only the entry's prose and the three projections, so the `b83e8aaf` runs read identical inputs. - **Gates** at `e295f361`: all 161 union commands (`dispatch-gates --commands`, 114 derived, plus the 49 artifact-roster commands) were run, each exit code captured before any pipe. - 158 exit 0, including `check:spec-changes`, `check:upgrade-guide`, `check:migration-registry`, `check:generated`, `check:error-code-provenance`, `check:skill-examples` and `check:dual-build-cjs-loads`. - The three PR-context guards (`check-closing-target-claim`, `check-partof-closing-keyword`, `check-single-claim-paths`) exit 2 without a PR. Re-run against PR #22528, each exits 0. - `--ran`: "✓ dispatch-gates --ran: 114 derived famil(ies) accounted for — 114 run, 0 NOT-MEASURED (a DERIVED zero — all 114 recorded an exit code and none of them is 3)." - **Declared to CI:** the full `spec`, `lint`, `rest`, `metadata*` and `cli` suites at the merged head. ## Acceptance notes - **ADR-0078 §4** says there is "no hard Zod `.refine()` that rejects existing metadata at registration". This rule sits at the parse doors (authoring, `defineStack`, the write door). That is the same placement as the calendar binding check and the joined-report `blocks[i].dataset` refusal. The runtime registration seam is untouched: views do not register through it, and stored rows keep loading, with `_diagnostics`. - **Scope:** only `type: 'chart'` is judged. A grid that only offers a chart in `appearance.allowedVisualizations` is a degrade, not a dead screen: objectui's `availableViews` gate asks the same resolver and never offers an unbound chart. This is recorded and pinned as a deliberate non-rule. - `checkViewCompleteness` stays silent on `chart`. The schema doors now refuse the block-less view, so a completeness finding would double-report it. - **Observation, not filed (read-only, unreproduced):** objectui `packages/app-shell/src/views/ObjectView.tsx` `:3162` reads only `viewDef.chart` for a `type === 'chart'` view, not `options.chart`. A view whose only binding is a complete bag therefore renders the unbound refusal on that route, but plots on `ListView`'s. That makes two precedences in one renderer repo. It belongs with objectui's `domain:spec` seat, which files the `ListViewSchema` mirror after this lands. - `#22491` names only this card. objectui's mirror is not part of this PR. --- _Generated by [Claude Code](https://claude.ai/code/session_01KNKBCRDJCu5tGy3TEbvtrF)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 6a3f82e commit 20e7d52

16 files changed

Lines changed: 671 additions & 28 deletions
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
---
2+
'@objectstack/spec': major
3+
---
4+
5+
A `type: 'chart'` list view must bind a dataset: a view whose effective chart binding names no `dataset` is refused at every list-view door, at `chart` (no binding at all) or at `options.chart.dataset` / `options.chart.values` (an incomplete legacy bag), with the binding to declare.
6+
7+
Clause-②: yes (narrowing)
8+
9+
<!-- adr-0087: registered view-chart-binding-dataset-required -->
10+
11+
**BREAKING**: an accept-set narrowing on a published authoring surface and on the view write door, graded `major` on `@objectstack/spec`: Changesets is in pre mode on `main` (tag `next`), where the launch-window `major` guard stands aside for the line's breaking changes.
12+
13+
**Why.** A chart list view plots only the ADR-0021 `dataset` its binding names. The renderer reads that binding as the top-level `chart` block, else the legacy `options.chart` bag, the block replacing the bag whole. The `chart` block already required `dataset` and `values`, but a view with no block at all, or with only the bag, never met that schema: the view write door (`PUT /api/v1/meta/view/:name`) saved `type: 'chart'` with no `chart` block, and an `options.chart` bag holding only `chartType`, and `defineStack` / `os validate` accepted the block-less view. Such a view renders a dead screen: the renderer either guessed a binding nobody wrote or, since objectui retired that guess, refuses on screen.
14+
15+
**What is refused.** A list view whose `type` is `chart` and that:
16+
17+
- declares no `chart` block and no `options.chart` bag: one `custom` issue at `chart`, whose message begins *This list view is `type: 'chart'` but declares no `chart` block, so it binds no dataset and there is nothing to plot.*;
18+
- declares no `chart` block and an `options.chart` bag with no `dataset` or no `values` (the bag is legal on the flattened overlay only): one `custom` issue per missing key, at `options.chart.dataset` / `options.chart.values`.
19+
20+
It is a check on the list-view schema itself, so it reaches every door that parses a list view: `defineView`, `defineStack`, `os validate` / `os build`, a view item's `config`, and the metadata write door, which answers `422 INVALID_METADATA`. **New export:** `checkListViewChartBinding`, published from `@objectstack/spec/ui`, is this refinement check itself, a `(view, ctx) => void` function; objectui's `ListViewSchema` mirror, which is built from `ListViewSchema.shape` and so drops the schema's object-level checks, attaches it with `.superRefine(checkListViewChartBinding)`.
21+
22+
**What stays accepted, byte for byte.** A chart view whose `chart` block names a `dataset` and at least one measure in `values`; a chart overlay whose `options.chart` bag carries both and no `chart` block replaces it; an incomplete bag under a complete `chart` block, which replaces it whole; a flattened overlay patch that names no `type`; and every view of another type, including a grid that only offers a chart in `appearance.allowedVisualizations`.
23+
24+
**What to do.** Bind the chart: declare a top-level `chart` block naming the dataset to plot and at least one of its measures, for example `chart: { dataset: 'lead_metrics', values: ['amount_sum'] }`, with `dimensions` (the X / group axis) optional and `chartType` defaulting to `bar`. A view that carries its binding in the legacy `options.chart` bag either completes the bag or, preferred, moves it to the top-level `chart` block. A view that is not meant to be a chart takes another `type`. No conversion can do this for you: only the author knows which dataset a chart plots.
25+
26+
**Stored views.** A stored `view` row is neither rewritten nor refused on read: it is served as stored, carries the same issue in its read-side `_diagnostics`, and is refused on its next save.
27+
28+
**Who is affected, measured.** No chart list view without a binding exists in this repository: the two chart list views in `examples/app-showcase` and the chart list views in the `@objectstack/lint` fixtures all bind a dataset and a measure. Deployed metadata was not measured.
29+
30+
### The kit
31+
32+
- **The refusal.** `checkListViewChartBinding` in `ui/view.zod.ts`, attached beside the calendar binding check at the three list-view doors: `ListViewSchema`, `ObjectListViewSchema` and the flattened list overlay member of the view write door. The `chart` slot's description now says a chart view must bind one, and the generated reference page carries it.
33+
- **The ledger.** The D3 semantic entry `view-chart-binding-dataset-required` (protocol 18). No key is removed, so there is no tombstone, and there is no D2 conversion.

‎content/docs/references/api/protocol.mdx‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1688,7 +1688,7 @@ The published metadata item body, opaque by ruling (1C). Shape is the item's own
16881688
| **gantt** | `{ startDateField: string; endDateField: string; titleField: string; progressField?: string; … }` | optional | Gantt-timeline configuration — applies when the view renders as a gantt layout |
16891689
| **gallery** | `{ coverField?: string; coverFit?: Enum<'cover' \| 'contain'>; cardSize?: Enum<'small' \| 'medium' \| 'large'>; titleField?: string; … }` | optional | Gallery/card view configuration |
16901690
| **timeline** | `{ startDateField: string; endDateField?: string; titleField: string; groupByField?: string; … }` | optional | Timeline view configuration |
1691-
| **chart** | `{ chartType?: Enum<'bar' \| 'line' \| 'pie' \| 'area' \| 'scatter'>; dataset: string; dimensions?: string[]; values: string[] }` | optional | List chart view configuration |
1691+
| **chart** | `{ chartType?: Enum<'bar' \| 'line' \| 'pie' \| 'area' \| 'scatter'>; dataset: string; dimensions?: string[]; values: string[] }` | optional | Chart binding — applies when the view renders as a chart. A `type: 'chart'` view must bind one: it names the ADR-0021 `dataset` and the measures (`values`) the chart plots, and there is no default binding |
16921692
| **map** | `{ latitudeField?: string; longitudeField?: string; locationField?: string; titleField?: string; … }` | optional | Map configuration — applies when the view renders as a map layout |
16931693
| **tree** | `{ parentField?: string; labelField?: string; fields?: string[]; defaultExpandedDepth?: integer }` | optional | Tree/hierarchy configuration — applies when the view renders as a tree layout |
16941694
| **pageName** | `never` | optional | [REMOVED] `view.pageName` was removed in @objectstack/spec 17.5.0 (ADR-0049 enforce-or-remove) — it named the page a `type: 'page'` view was to mount, and that mount was never built: no renderer read the key, so the named page was never reached and the view drew an empty grid. Delete the key; to put a published page in front of users, give the app a navigation item — `{ type: 'page', pageName: '<page_name>' }` under the app's `navigation` — which is a different key on a different surface and is the page mount that has always rendered. Run `os migrate meta --from 17` to list the mechanical edits for existing sources; `--write` applies the ones it can prove, and you apply the rest by hand. |
@@ -1773,7 +1773,7 @@ The published metadata item body, opaque by ruling (1C). Shape is the item's own
17731773
| **gantt** | `{ startDateField: string; endDateField: string; titleField: string; progressField?: string; … }` | optional | Gantt-timeline configuration — applies when the view renders as a gantt layout |
17741774
| **gallery** | `{ coverField?: string; coverFit?: Enum<'cover' \| 'contain'>; cardSize?: Enum<'small' \| 'medium' \| 'large'>; titleField?: string; … }` | optional | Gallery/card view configuration |
17751775
| **timeline** | `{ startDateField: string; endDateField?: string; titleField: string; groupByField?: string; … }` | optional | Timeline view configuration |
1776-
| **chart** | `{ chartType?: Enum<'bar' \| 'line' \| 'pie' \| 'area' \| 'scatter'>; dataset: string; dimensions?: string[]; values: string[] }` | optional | List chart view configuration |
1776+
| **chart** | `{ chartType?: Enum<'bar' \| 'line' \| 'pie' \| 'area' \| 'scatter'>; dataset: string; dimensions?: string[]; values: string[] }` | optional | Chart binding — applies when the view renders as a chart. A `type: 'chart'` view must bind one: it names the ADR-0021 `dataset` and the measures (`values`) the chart plots, and there is no default binding |
17771777
| **map** | `{ latitudeField?: string; longitudeField?: string; locationField?: string; titleField?: string; … }` | optional | Map configuration — applies when the view renders as a map layout |
17781778
| **tree** | `{ parentField?: string; labelField?: string; fields?: string[]; defaultExpandedDepth?: integer }` | optional | Tree/hierarchy configuration — applies when the view renders as a tree layout |
17791779
| **pageName** | `never` | optional | [REMOVED] `view.pageName` was removed in @objectstack/spec 17.5.0 (ADR-0049 enforce-or-remove) — it named the page a `type: 'page'` view was to mount, and that mount was never built: no renderer read the key, so the named page was never reached and the view drew an empty grid. Delete the key; to put a published page in front of users, give the app a navigation item — `{ type: 'page', pageName: '<page_name>' }` under the app's `navigation` — which is a different key on a different surface and is the page mount that has always rendered. Run `os migrate meta --from 17` to list the mechanical edits for existing sources; `--write` applies the ones it can prove, and you apply the rest by hand. |

‎content/docs/references/data/object.mdx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -385,7 +385,7 @@ const result = ApiMethod.parse(data);
385385
| **gantt** | `{ startDateField: string; endDateField: string; titleField: string; progressField?: string; … }` | optional | Gantt-timeline configuration — applies when the view renders as a gantt layout |
386386
| **gallery** | `{ coverField?: string; coverFit?: Enum<'cover' \| 'contain'>; cardSize?: Enum<'small' \| 'medium' \| 'large'>; titleField?: string; … }` | optional | Gallery/card view configuration |
387387
| **timeline** | `{ startDateField: string; endDateField?: string; titleField: string; groupByField?: string; … }` | optional | Timeline view configuration |
388-
| **chart** | `{ chartType?: Enum<'bar' \| 'line' \| 'pie' \| 'area' \| 'scatter'>; dataset: string; dimensions?: string[]; values: string[] }` | optional | List chart view configuration |
388+
| **chart** | `{ chartType?: Enum<'bar' \| 'line' \| 'pie' \| 'area' \| 'scatter'>; dataset: string; dimensions?: string[]; values: string[] }` | optional | Chart binding — applies when the view renders as a chart. A `type: 'chart'` view must bind one: it names the ADR-0021 `dataset` and the measures (`values`) the chart plots, and there is no default binding |
389389
| **map** | `{ latitudeField?: string; longitudeField?: string; locationField?: string; titleField?: string; … }` | optional | Map configuration — applies when the view renders as a map layout |
390390
| **tree** | `{ parentField?: string; labelField?: string; fields?: string[]; defaultExpandedDepth?: integer }` | optional | Tree/hierarchy configuration — applies when the view renders as a tree layout |
391391
| **pageName** | `never` | optional | [REMOVED] `view.pageName` was removed in @objectstack/spec 17.5.0 (ADR-0049 enforce-or-remove) — it named the page a `type: 'page'` view was to mount, and that mount was never built: no renderer read the key, so the named page was never reached and the view drew an empty grid. Delete the key; to put a published page in front of users, give the app a navigation item — `{ type: 'page', pageName: '<page_name>' }` under the app's `navigation` — which is a different key on a different surface and is the page mount that has always rendered. Run `os migrate meta --from 17` to list the mechanical edits for existing sources; `--write` applies the ones it can prove, and you apply the rest by hand. |

0 commit comments

Comments
 (0)