diff --git a/.changeset/listview-map-liveness-live.md b/.changeset/listview-map-liveness-live.md new file mode 100644 index 0000000000..bbadea09ea --- /dev/null +++ b/.changeset/listview-map-liveness-live.md @@ -0,0 +1,9 @@ +--- +"@objectstack/spec": patch +--- + +**Liveness-ledger verdict:** `view.list.map` (`ListViewSchema`'s view-level `map` block) moves `planned` → `live` (#11442). + +objectui#5908 landed the missing half of the forward: `packages/plugin-list/src/ListView.tsx`'s `resolveListMapConfig` now merges the view-level `map` block over the legacy `options.map` bag before `case 'map'` builds the `object-map` schema, and the same merged config also feeds the visualization-switcher's capability gate — so a view that binds its coordinates only in the spec's `map` block both renders on the map surface and is no longer filtered out of `allowedVisualizations`. Nothing about the schema shape or the authoring-time validation changed; only the runtime consequence of authoring the key does. + +Pinned by objectui `packages/plugin-list/src/__tests__/ListView.mapViewLevelConfig.test.tsx` (the forward, against a spy) and `packages/plugin-map/src/ObjectMap.listViewMapConfigReach.test.tsx` (the end-to-end read through a real `ObjectMap` — markers, titles, camera). Re-measured against objectui `origin/main@08ca73f8` (squash commit `e2e8e68` for #5908 confirmed an ancestor). diff --git a/packages/spec/liveness/README.md b/packages/spec/liveness/README.md index 2e54c64bac..cda2781cbf 100644 --- a/packages/spec/liveness/README.md +++ b/packages/spec/liveness/README.md @@ -798,7 +798,7 @@ marker where the Notes cell goes, never a guess at what belongs there. | skill | `permissions` REMOVED 2026-07 (#3704); `triggerPhrases` REMOVED 2026-07-30 (#3896 close-out sweep — phrases were never matched; activation is `triggerConditions` + the agent's `skills[]` + /skill-name pinning) | | dataset | `measures.certified` (declared-but-unenforced governance flag) REMOVED in 16.0 (#2377) | | page | fully live + one planned | -| view | list/form drilled via `children` (#2998 Track B); list.{responsive,performance} + form.{defaultSort,aria} REMOVED 2026-07-30 (#3896 close-out sweep — list aria/data stay live); **form.data was that sweep's one CORRECTION** — the removal attempt broke the build (`defineForm` writes `data.provider='schema'` onto every metadata form, `metadata-protocol` serves it), so it stands `live` with re-verified evidence; form.{buttons,defaults} live (framework#1894 / #2998); audit-era DEAD lines superseded by re-verification. **The dead set is six, not the four removals above**: #4534 (the last #4001 batch, batch 6e) declared three CONTAINER-level keys this row had never classified — `name` and `label`, both `dead`, and `object`, `live`. All three are properties of the `views: [...]` *container*, not of a view: `name` is dead as a BODY key because the live one is the `sys_metadata` row column the door supplies, and `label` is container display metadata with no reader. Neither is `authorWarn`'d and both are deliberately KEPT — the platform's own writers send `name` (artifact-shipped containers, the metadata-validation sweep), so tombstoning it would reject shapes we write ourselves. `object` is the container's object binding, and it was *stripped on every parse* until #4534 declared it. Separately, the level-2 dead residue (userActions.buttons, addRecord.mode/formView, tabs[].order) is noted on parents and is **not** in the counts — one drill level only **#9340**: `list.map` declared — the eighth visualization block (`ListMapConfigSchema`), keys mirroring objectui plugin-map's documented read set. `planned`, deliberately not `live`: the component-level reader exists (ObjectMap.tsx getMapConfig reads `schema.map`) but objectui ListView.tsx `case 'map'` still forwards only `options.map`, never the view-level block; flips live when the pre-registered objectui consumer card (Blocked-by #9340) lands the forwarding | +| view | list/form drilled via `children` (#2998 Track B); list.{responsive,performance} + form.{defaultSort,aria} REMOVED 2026-07-30 (#3896 close-out sweep — list aria/data stay live); **form.data was that sweep's one CORRECTION** — the removal attempt broke the build (`defineForm` writes `data.provider='schema'` onto every metadata form, `metadata-protocol` serves it), so it stands `live` with re-verified evidence; form.{buttons,defaults} live (framework#1894 / #2998); audit-era DEAD lines superseded by re-verification. **The dead set is six, not the four removals above**: #4534 (the last #4001 batch, batch 6e) declared three CONTAINER-level keys this row had never classified — `name` and `label`, both `dead`, and `object`, `live`. All three are properties of the `views: [...]` *container*, not of a view: `name` is dead as a BODY key because the live one is the `sys_metadata` row column the door supplies, and `label` is container display metadata with no reader. Neither is `authorWarn`'d and both are deliberately KEPT — the platform's own writers send `name` (artifact-shipped containers, the metadata-validation sweep), so tombstoning it would reject shapes we write ourselves. `object` is the container's object binding, and it was *stripped on every parse* until #4534 declared it. Separately, the level-2 dead residue (userActions.buttons, addRecord.mode/formView, tabs[].order) is noted on parents and is **not** in the counts — one drill level only **#9340**: `list.map` declared — the eighth visualization block (`ListMapConfigSchema`), keys mirroring objectui plugin-map's documented read set. FLIPPED `planned` → `live` 2026-08-24 (#11442): objectui#5908 landed `resolveListMapConfig`, which merges the view-level `map` block over the legacy `options.map` bag before `ListView.tsx`'s `case 'map'` forwards it into `ObjectMap`, with the same merged config also feeding the visualization-switcher's capability gate so a view binding coordinates only in the spec block is no longer filtered out of `allowedVisualizations` either (objectui#5042) | | report | dataset-bound (ADR-0021); the aria/performance LEDGER entries were stale — the keys left the schema in the report-liveness close-out; deleted 2026-07-30 as hygiene. Audit-era `chart` DEAD superseded (framework#1890 / #3441) | | dashboard | ADR-0021 dataset widgets (#3251; DashboardWidgetSchema `.strict()`); `aria`/`performance` (and widget `performance` + PerformanceConfigSchema) REMOVED 2026-07-30 (#3896 close-out sweep — no renderer applied any of them); audit-era `globalFilters`/`dateRange` DEAD superseded (framework#2501) **#4956**: `widgets` DRILLED — the row jumps 20 → 41 classified because all 22 widget-level keys enter the count at once. They had never been classified at all: the entry carried one blanket `live` plus a `note` asserting they were classified "in the DashboardWidgetSchema subtree", and no such subtree existed in any of the 28 ledger files. That gap, not any evidence, is what carried `widgets[].responsive` through the #3896 sweep that removed both its sibling `widgets[].performance` and its literal namesake `view.responsive` — `view` is drilled, so `list.responsive` got asked and went out. New dead 6 = `responsive` (retired #4876/#4995, tombstone keeps the row) + `colorVariant` + `actionUrl`/`actionType`/`actionIcon` + `aria`. The action trio is the sharpest: no renderer draws a per-widget action button at all (every `actionUrl` read in DashboardRenderer is scoped to `header.actions[]`), yet `validate-dashboard-action-refs.ts` enforces reference integrity on it and its docblock calls it "the per-widget button" — a lint guarding an affordance that does not exist. `requiresService` is the counter-example worth remembering: dead by every objectui measurement, and LIVE server-side (`filterDashboardForUser`, ADR-0057 D10) — judging a widget key from the renderer repo alone would have retired an enforced gate. `compareTo` is `live` on ONE path only (inline object-provider charts); on the ADR-0021 dataset path the string arms are dropped and `{ offset }` throws in the executor. **#6774** moves the row 33/8 → 34/7: `colorVariant` CORRECTED dead → live 2026-08-09, the enforce leg of #5010 ruling B landing from the renderer side (objectui#3359 / PR objectui#3799, absorbed by pin `09987b68`). Worth reading beside `requiresService` above, because it is the same lesson from the other end — that row warns against judging a widget key from the renderer repo alone, and this one is a `dead` verdict that was correct in this repo AND correct in the renderer repo on the day it was measured, and stopped being either when a cross-repo decision was implemented. A ledger row is a claim with a timestamp; `verifiedAt` is what makes the claim re-askable. It also empties the dashboard warn set, so the author-side lint now says nothing about any widget key — `dashboard` stays in the lint's TYPE_COLLECTIONS all the same (the `webhook`/`email_template` resolved state) | | query | **not a metadata type** — the REQUEST surface (`QuerySchema`: client SDK QueryBuilder output; the `POST /data/:object/query` body), governed via `SPEC_ONLY_SCHEMAS` (#4286). The gate's one-level walk resolves 1 experimental; the 7 marker-experimental search affordances sit one level deeper, below the walk — resolved from `[EXPERIMENTAL — not enforced]` describe markers, not ledger entries (search `fuzzy`/`operator`/`boost`/`minScore`/`language`/`highlight` + `aggregations[].filter` — declared engine affordances no executor receives). The #4286 sweep closed out same-release: `having` ENFORCED 2026-07-31 (engine-side post-aggregation filter, both paths; was finding 1); dead 4 = the tombstoned removals `joins`/`windowFunctions`/`cursor`/`distinct` — REMOVED 2026-07-31 (retiredKey keeps each in the walked shape so the rows stay; protocol-17 semantic migrations; the JoinNode + WindowFunctionNode clusters and the `QueryBuilder.cursor()`/`.distinct()` producers deleted with their keys; `distinct`'s mis-wired REST count suppression deleted too — finding 2). **#6815** adds the 5th dead: `aggregations[].distinct` REMOVED 2026-08-09 (live → dead, `-1` live). It is the one member of this ledger the #4286 sweep could not have caught with the question it asked — that sweep looked for keys NO executor reads, and this one had a reader: the objectql in-memory fallback deduplicated before applying the function while all five other faces (driver-sql, driver-turso, driver-mongodb, driver-memory, service-analytics' `AGGREGATE_SQL`) ignored it, so one query answered two plausible NUMBERS depending on which backend served it. The lesson for the next audit is the question, not the key: a per-key `live` verdict is only as good as the count of faces it was measured across, and this row's 2026-07-31 evidence (`in-memory-aggregation.ts:167,204-206`) was TRUE and still the wrong verdict. `count_distinct` is the surviving spelling (enforce leg, #6409) | diff --git a/packages/spec/liveness/state-counts.md b/packages/spec/liveness/state-counts.md index a8bd0c475f..deba75d157 100644 --- a/packages/spec/liveness/state-counts.md +++ b/packages/spec/liveness/state-counts.md @@ -39,7 +39,7 @@ for both corollaries. | `skill` | 16 | 0 | 1 | 0 | 17 | | `dataset` | 27 | 0 | 0 | 0 | 27 | | `page` | 23 | 0 | 0 | 1 | 24 | -| `view` | 77 | 0 | 9 | 1 | 87 | +| `view` | 78 | 0 | 9 | 0 | 87 | | `report` | 21 | 0 | 0 | 0 | 21 | | `dashboard` | 34 | 0 | 7 | 0 | 41 | | `webhook` | 19 | 0 | 0 | 0 | 19 | @@ -58,4 +58,4 @@ for both corollaries. | `capability` | 12 | 0 | 0 | 0 | 12 | | `qa` | 4 | 0 | 5 | 0 | 9 | | `manifest` | 22 | 0 | 21 | 0 | 43 | -| **total** | **821** | **5** | **76** | **11** | **913** | +| **total** | **822** | **5** | **76** | **10** | **913** | diff --git a/packages/spec/liveness/view.json b/packages/spec/liveness/view.json index 6a84d0e3da..968f42cbd3 100644 --- a/packages/spec/liveness/view.json +++ b/packages/spec/liveness/view.json @@ -118,10 +118,11 @@ "note": "objectui: @object-ui/plugin-tree ObjectTree.tsx (spec CHANGELOG: 'Renderer ships in objectui @object-ui/plugin-tree')." }, "map": { - "status": "planned", - "verifiedAt": "2026-08-17", + "status": "live", + "verifiedAt": "2026-08-24", "evidenceScope": "cross-repo", - "note": "PLANNED, deliberately not `live`, and the gap is one spread. Declared spec-first by the #9340 cross-repo ruling (transfer of objectui#5001). The renderer's read of this exact key set exists today — objectui plugin-map ObjectMap.tsx getMapConfig validates `schema.map` against a local zod schema with the same seven keys (latitudeField/longitudeField/locationField/titleField/descriptionField/zoom/center) — but objectui ListView.tsx `case 'map'` forwards only `schema.options?.map` and never the view-level `map` block, so authoring this key changes nothing at runtime until the pre-registered objectui consumer card (Blocked-by #9340) lands the view→renderer forwarding. Flip to `live` citing that ListView.tsx read when it lands — measured objectui at the checkout current on 2026-08-17." + "evidence": "objectui: packages/plugin-list/src/ListView.tsx:113 resolveListMapConfig merges the view-level `map` block over the legacy `options.map` bag (view-level wins); :1849-1858 the visualization-switcher capability gate asks the SAME merged config, not `options.map` alone, before offering 'map' in allowedVisualizations; :2181-2205 `case 'map'` forwards the merged config into the object-map schema. objectui: packages/plugin-list/src/__tests__/ListView.mapViewLevelConfig.test.tsx pins the forward against a spy; objectui: packages/plugin-map/src/ObjectMap.listViewMapConfigReach.test.tsx pins the end-to-end read through a real ObjectMap (markers/titles/camera) — measured objectui @08ca73f8", + "note": "FLIPPED planned → live 2026-08-24: objectui#5908 (merged 2026-08-23T19:52:08Z; squash commit `e2e8e68` on objectui main, confirmed an ancestor of the objectui @08ca73f8 checkout this row cites) landed the view→renderer forwarding the prior PLANNED note's flip condition named. `resolveListMapConfig` now merges the view-level `map` block this key declares over the legacy `options.map` bag, `case 'map'` forwards the merge into `ObjectMap`, and the same merged config also feeds the visualization-switcher's capability gate — closing a second hole the prior note didn't need to describe (a view binding coordinates only in the spec `map` block no longer gets silently filtered out of `allowedVisualizations`, objectui#5042). The ADR-0047 `InterfaceListPage` path (packages/app-shell/src/views/InterfaceListPage.tsx:406-409) forwards the authored `map` block verbatim onto the schema it hands to `ListView`, so it reaches the same resolver rather than dead-ending. Pins: `ListView.mapViewLevelConfig.test.tsx` (the forward, against a spy) and `ObjectMap.listViewMapConfigReach.test.tsx` (the end-to-end read). Prior PLANNED note (verifiedAt 2026-08-17, #9340) is superseded; its flip instruction is discharged." }, "description": { "status": "live",