Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions .changeset/govern-report-dashboard-liveness.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
---
"@objectstack/spec": patch
---

chore(spec): enroll `report` and `dashboard` in the liveness GOVERNED set (#3462)

Closes the systemic anti-drift gap for two more authorable UI types (umbrella
#1878). Both were registered/round-trippable but ungoverned, so their property
liveness wasn't CI-checked — the reason drifts like dashboard `title`↔`label`
and stale report `chart` config survived until an audit caught them.

- Added `packages/spec/liveness/report.json` (20 live / 2 dead) and
`dashboard.json` (18 live / 2 dead), each property classified with an
objectui consumer reference.
- Re-verification corrected several stale 2026-06 audit findings against current
code: report `chart` is **live** (DatasetReportChart plots `chart.xAxis`/
`yAxis` via `useDatasetRows`, #1890/#3441); dashboard `globalFilters`/
`dateRange` are **live** (framework#2501); `title`↔`label` fixed (objectui#2806);
the ADR-0021 widget migration shipped (#3251). Only `aria`/`performance` remain
dead on each (perf `authorWarn`'d).
- Added both to `GOVERNED` in `check-liveness.mts`; the gate is green. Future
drift on these types is now a CI failure, not an audit finding.

`webhook` (the third type in #3462) is deferred — it isn't a registered
metadata type; its enrollment rides with the disconnect decision in #3461.

No spec shape/behavior change (ledger + gate config only).
5 changes: 4 additions & 1 deletion packages/spec/liveness/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,10 @@ EOF
| page | 16 | – | – | 1 | fully live + one planned |
| view | 70 | 0 | 5 | – | list/form drilled via `children` (#2998 Track B); dead = list.{responsive,performance} + form.{data,defaultSort,aria}, all but aria authorWarn'd; form.{buttons,defaults} now live — objectui ObjectForm folds them onto its flat props (framework#1894 / #2998); audit-era DEAD lines superseded by re-verification (submitBehavior, sharing.lockedBy, list ViewData providers, and the ADR-0021 chart shape — all live now); level-2 dead residue (userActions.buttons, addRecord.mode/formView, tabs[].order) noted on parents — one drill level only |

| report | 20 | 0 | 2 | – | dataset-bound (ADR-0021); dead = aria + performance (perf authorWarn'd); audit-era `chart` DEAD superseded — DatasetReportChart plots `chart.xAxis`/`yAxis` via useDatasetRows (framework#1890 / #3441), `groupBy` stays experimental (describe marker) |
| dashboard | 18 | 0 | 2 | – | ADR-0021 dataset widgets (WidgetConfigPanel + DashboardRenderer migrated #3251; DashboardWidgetSchema `.strict()`); dead = aria + performance (perf authorWarn'd); audit-era `globalFilters`/`dateRange` DEAD superseded — LIVE via framework#2501; `title`↔`label` fixed (objectui#2806) |

The `dead` set across types is the enforce-or-remove worklist (ADR-0049); every
misleading entry carries `authorWarn` so authors hear about it at compile time.
Not yet governed (rollout): dashboard, app, report, job, datasource,
Not yet governed (rollout): app, job, datasource,
translation, email_template, doc, book, validation, seed.
56 changes: 56 additions & 0 deletions packages/spec/liveness/dashboard.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
{
"type": "dashboard",
"_note": "DashboardSchema (UI, ADR-0021 dataset-bound). Live path: objectui DashboardView → DashboardRenderer → DatasetWidget. Seeded from docs/audits/2026-06-dashboardschema-property-liveness.md and re-verified against objectui HEAD — several audit-era findings are superseded: the ADR-0021 widget migration shipped (Studio WidgetConfigPanel + DashboardRenderer on dataset/dimensions/values, framework#3251; DashboardWidgetSchema is now `.strict()`); `globalFilters`/`dateRange` are LIVE (dashboard-level filters, framework#2501); the `title`↔`label` drift is fixed (renderer falls back to `label`, objectui#2806); the undeclared widget props were reconciled (#1894). objectui paths cited as prose in `note` (not `evidence`). Framework provenance/lock fields auto-classify live (ADR-0010). Widget-level props are classified in the DashboardWidgetSchema subtree, not drilled here.",
"props": {
"name": {
"status": "live",
"note": "dashboard identity — metadata key + objectui DashboardView/DashboardRenderer."
},
"label": {
"status": "live",
"note": "objectui: DashboardRenderer header title (falls back from the legacy `title`, objectui#2806) + spec-bridge dashboard.ts."
},
"description": {
"status": "live",
"note": "objectui: DashboardRenderer header (`:787`), gated by header.showDescription."
},
"header": {
"status": "live",
"note": "objectui: DashboardRenderer.tsx:778-796 — showTitle/showDescription gates + header.actions row."
},
"widgets": {
"status": "live",
"note": "objectui: the widget grid — DashboardRenderer maps each to DatasetWidget/metric/etc. Per-widget props live in the DashboardWidgetSchema subtree (strict, ADR-0021)."
},
"columns": {
"status": "live",
"note": "objectui: DashboardRenderer.tsx:250 — grid column count (inferred when unset)."
},
"gap": {
"status": "live",
"note": "objectui: DashboardRenderer.tsx:262 — grid gap."
},
"refreshInterval": {
"status": "live",
"note": "objectui: DashboardRenderer.tsx:385-386 — sets a setInterval(onRefresh) every N seconds. Caveat: only fires when the host passes an `onRefresh` handler; the renderer is the consumer."
},
"globalFilters": {
"status": "live",
"note": "objectui: DashboardRenderer.tsx:279,988 — dashboard-level filter values hosted and merged into every widget's query (framework#2501). Supersedes the 2026-06 audit's DEAD finding."
},
"dateRange": {
"status": "live",
"note": "objectui: DashboardRenderer.tsx:279,988 — dashboard-level date-range filter (framework#2501). Supersedes the 2026-06 audit's DEAD finding."
},
"aria": {
"status": "dead",
"note": "ARIA attributes — authorable (dashboard.form.ts) but no DashboardRenderer path applies them."
},
"performance": {
"status": "dead",
"authorWarn": true,
"authorHint": "Dashboard performance tuning is not implemented — this config has no runtime consumer.",
"note": "Perf-optimization config — authorable but no renderer/runtime reads it (audit; re-verified — no consumer in plugin-dashboard)."
}
}
}
64 changes: 64 additions & 0 deletions packages/spec/liveness/report.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
{
"type": "report",
"_note": "ReportSchema (analytics report, ADR-0021 single-form). Live path: objectui ReportRenderer → DatasetReportRenderer (dataset-bound). Seeded from docs/audits/2026-06-reportschema-property-liveness.md and re-verified against objectui HEAD — the audit's `chart` = DEAD finding is superseded: DatasetReportRenderer's DatasetReportChart now plots `chart.xAxis`/`yAxis` (dataset dimension/measure) via useDatasetRows (framework#1890/#3441), so `chart` is LIVE (only its `groupBy` sub-key stays experimental, per its own describe marker). objectui paths cited as prose in `note` (not `evidence`, which resolves framework file:line). Framework provenance/lock fields (protection/_lock*) auto-classify live (ADR-0010).",
"props": {
"name": {
"status": "live",
"note": "report identity — metadata key + objectui ReportRenderer."
},
"label": {
"status": "live",
"note": "objectui: DatasetReportRenderer header + Studio ReportDefaultInspector."
},
"description": {
"status": "live",
"note": "display subtitle — objectui report header."
},
"type": {
"status": "live",
"note": "objectui: DatasetReportRenderer routes tabular/summary/matrix (cross-tab + server totals) / joined."
},
"dataset": {
"status": "live",
"note": "objectui: DatasetReportRenderer binds the semantic-layer dataset (ADR-0021); superRefine requires dataset+values for non-joined reports."
},
"rows": {
"status": "live",
"note": "objectui: dataset dimension names grouped down — DatasetReportRenderer."
},
"columns": {
"status": "live",
"note": "objectui: matrix across-dimensions (rows × columns pivot) — DatasetReportRenderer."
},
"values": {
"status": "live",
"note": "objectui: dataset measure names — DatasetReportRenderer; required (superRefine)."
},
"runtimeFilter": {
"status": "live",
"note": "objectui: render-time scope filter ANDed at query time (`?? filter`, mergeFilters) — DatasetReportRenderer."
},
"drilldown": {
"status": "live",
"note": "objectui: DatasetReportRenderer.tsx:697 — click-through to underlying records (default on; set false to disable)."
},
"chart": {
"status": "live",
"note": "objectui: DatasetReportRenderer.tsx:776 + DatasetReportChart plots `chart.xAxis` (dataset dimension) × `chart.yAxis` (measure) via useDatasetRows, honoring `type`/`title` (framework#1890/#3441). Supersedes the 2026-06 audit's DEAD finding. Sub-key `groupBy` stays experimental (its own describe marker) — the dataset renderer plots a single series and never reads it."
},
"blocks": {
"status": "live",
"note": "objectui: joined-report sub-reports, each dataset-bound (dataset/rows/columns/values/runtimeFilter/chart) — DatasetReportRenderer joined branch."
},
"aria": {
"status": "dead",
"note": "ARIA attributes — authorable (report.form.ts) but no report renderer applies them; the dataset renderer ignores `report.aria`."
},
"performance": {
"status": "dead",
"authorWarn": true,
"authorHint": "Report performance tuning is not implemented — this config has no runtime consumer.",
"note": "Perf-optimization config — authorable but no renderer/runtime reads it (audit L; re-verified — no consumer in plugin-report)."
}
}
}
2 changes: 1 addition & 1 deletion packages/spec/scripts/liveness/check-liveness.mts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ const repoRoot = resolve(specRoot, '../..');
const ledgerRoot = join(specRoot, 'liveness');

// Governed metadata types, rolled out highest-frequency / highest-risk first.
const GOVERNED = ['object', 'field', 'flow', 'action', 'hook', 'permission', 'position', 'agent', 'tool', 'skill', 'dataset', 'page', 'view'];
const GOVERNED = ['object', 'field', 'flow', 'action', 'hook', 'permission', 'position', 'agent', 'tool', 'skill', 'dataset', 'page', 'view', 'report', 'dashboard'];

// ADR-0010 provenance/lock overlay fields — system-stamped, on every type; auto-live.
const FRAMEWORK_FIELDS = new Set([
Expand Down