Skip to content

Commit aa5e2e3

Browse files
os-zhuangclaude
andcommitted
docs(audit): App/Page/Dashboard/Report property liveness
Recurring themes: ADR-0021 migration debt (dashboard renderer+Studio still on legacy object/valueField shape the spec says was removed; report chart dead), orphaned spec-bridge (page/dashboard bridges have no src caller -> their exclusive props dead), spec<->renderer naming drift (page type->pageType & label->title break layouts/headers; dashboard title vs label; app reads accentColor/badgeVariant/separator not in spec), and aspirational config (app mobileNavigation/sharing/embed; page recordReview/blankLayout required by superRefine yet unrendered; report chart/aria/performance). Evidence file:line in each doc. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent c981c6e commit aa5e2e3

4 files changed

Lines changed: 84 additions & 0 deletions
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Audit: AppSchema property liveness & necessity
2+
3+
**Date**: 2026-06-15 · **Scope**: `packages/spec/src/ui/app.zod.ts`. **Renderers**: `objectui` `layout/NavigationRenderer.tsx`, `app-shell` `{AppSidebar,ConsoleLayout,AppContent}`. **Method**: consumer cross-reference.
4+
5+
## 🔴 Spec↔renderer drift — renderer reads fields the spec doesn't declare (would be stripped by `AppSchema.parse()`)
6+
- `branding.accentColor` — consumed `AppShell.tsx:160-165`, **absent from `AppBrandingSchema`**
7+
- nav-item `badgeVariant` — consumed `NavigationRenderer.tsx:850/890`, absent from `BaseNavItemSchema`
8+
- nav-item `type:'separator'` — rendered `NavigationRenderer.tsx:574/774`, **not a union member** in app.zod.ts
9+
10+
These are the highest-priority fixes (authoring them per spec fails; they only work because parse is bypassed somewhere).
11+
12+
## DEAD — aspirational, no consumer in either repo
13+
`version`, `aria`, `objects[]`, `apis[]`, `App.sharing`, `App.embed`, `mobileNavigation`/`bottomNavItems` (fully unimplemented — even `packages/mobile` ignores it), `branding.logo` (passed at `ConsoleLayout.tsx:117` but never read in `AppShell`).
14+
- **Misleading**: `App.sharing`/`App.embed` attach Sharing/Embed config to apps, but the only live sharing/embed path is `FormView.sharing` (`framework/.../rest-server.ts:3282`) — no public-app or iframe route reads the app-level versions.
15+
- The spec itself labels `objects[]`/`apis[]` "config convenience"; the chatbot's object list comes from nav items (`AppHeader.tsx:500 collectNavObjects`), not `App.objects`.
16+
17+
## LIVE & necessary (the sidebar core — all camelCase, no snake drift)
18+
`name`, `label`, `description`, `icon`, `active`, `isDefault`, `hidden`, `navigation` (whole tree), `areas` (precedence over navigation), `contextSelectors` (all sub-fields), `homePageId`, `defaultAgent` (dual consumer: framework `agent-runtime.ts:341` + objectui chatbot), `branding.{primaryColor,favicon}`, `protection`. Nav-item union fully live: `id/label/icon/order/badge/visible(CEL)/requiredPermissions/requiresObject/requiresService` + per-type payloads (`objectName/viewName/recordId/recordMode/dashboardName/pageName/url/target/reportName/componentRef/params/children/expanded`). NavigationContribution (ADR-0029) live via `objectql/engine.ts:912`.
19+
- PARTIAL: `App.requiredPermissions` (app-entry gate not observed; only nav-item perms enforced), nav `type:'action'` `actionDef` (fires `onAction(item)`; `actionDef.{actionName,params}` shape read loosely in the action runtime, 0 direct grep in shell).
20+
21+
## Recommendation
22+
Add the 3 drift fields to the spec (`accentColor`, `badgeVariant`, `separator`) **or** stop the renderer reading them. Prune the aspirational block; `App.sharing`/`App.embed`/`apiEnabled`-style props create a false security/feature impression.
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Audit: DashboardSchema / DashboardWidgetSchema property liveness & necessity
2+
3+
**Date**: 2026-06-15 · **Scope**: `packages/spec/src/ui/dashboard.zod.ts`. **Live path**: `objectui` `DashboardView``DashboardRenderer``DatasetWidget` (ADR-0021) / legacy inline `getComponentSchema()`. The `spec-bridge/bridges/dashboard.ts` path is **orphaned** (no `src` caller; its node types have no renderer).
4+
5+
## 🔴 The ADR-0021 cutover is half-done and the spec contradicts the code
6+
`dashboard.zod.ts:153-156` declares `dataset`+`values` **required** and says the legacy `object/categoryField/valueField/aggregate` query "was removed." But `DashboardRenderer.getComponentSchema()` (`:423-723`) is built almost entirely on that **legacy inline shape**, and Studio's `WidgetConfigPanel.tsx:161-285` still authors `object/categoryField/valueField/aggregate` with **no dataset/dimensions/values controls**. → Studio emits widgets the current spec **rejects**; the dataset-required rule cannot hold. (Same debt that left the HotCRM dashboard seeds invalid.)
7+
8+
## DEAD — only the orphaned spec-bridge references them
9+
Dashboard: `globalFilters`, `dateRange`, `aria`, `performance`. Widget: `responsive`, `aria`, `actionUrl`/`actionType`/`actionIcon`, `requiresService` (nav uses it; widgets don't). `chartConfig` is read by **CLI lint only** (`validate-widget-bindings.ts:239`), no runtime renderer. They appear in `dashboard.form.ts` so Studio shows editable fields that render nothing.
10+
11+
## Drift
12+
- **`title` vs `label`**: renderer reads `schema.title` (`DashboardRenderer.tsx:856`, `DashboardView.tsx:358`), but `DashboardSchema` has only `label`. `header.showTitle/showDescription` gate on a non-spec field.
13+
- **`refreshInterval` effectively dead**: interval logic exists (`:332`) but only fires if `onRefresh` is passed; `DashboardView` never passes it.
14+
- **Renderer depends on undeclared props**: `component`, `data`, `rowField`, `columnField`, `searchable`, `pagination`, `categoryGranularity` are read but **not in `DashboardWidgetSchema`** (inverse of dead-spec-prop).
15+
16+
## LIVE & well-wired
17+
`name`, `label`, `description`, `widgets`, `columns`, `gap`, `header.actions`, widget `id/title/description/type/colorVariant/filter→runtimeFilter/compareTo/layout.{w,h}/options/requiresObject`, and the canonical `dataset`/`dimensions`/`values` (DatasetWidget + framework `build-probes.ts` runtime probe + CLI lint). Plus the legacy `object/valueField/categoryField/aggregate` (LIVE-drift; spec says removed but renderer+Studio still depend on them).
18+
19+
## Recommendation
20+
Finish ADR-0021: migrate `DashboardRenderer` + `WidgetConfigPanel` to dataset/dimensions/values, then remove the legacy inline shape (or un-deprecate it in the spec). Fix `title``label`. Prune the orphaned-bridge-only props. Declare the undeclared-but-read props.
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Audit: PageSchema property liveness & necessity
2+
3+
**Date**: 2026-06-15 · **Scope**: `packages/spec/src/ui/page.zod.ts` (+ PageComponent/InterfaceConfig/PageVariable). **Renderers**: `objectui` `PageView``SchemaRenderer``components/.../layout/page.tsx`, `plugin-detail` (record path), `InterfaceListPage`.
4+
5+
## 🔴 `bridgePage` (spec-bridge) is entirely dead
6+
`PageView.tsx` spreads the **raw** spec into `SchemaRenderer`; nothing calls `SpecBridge.toPage`. So every prop whose only reader is the bridge is DEAD: component `events`, `style`, `visibility`, element `dataSource`, `responsive`, page `icon`.
7+
8+
## 🔴 `type``pageType` naming drift breaks page-type layouts
9+
The custom-page renderer switches on `schema.pageType` (`page.tsx:349`) but the spec emits `type`; only `usePageAssignment` normalizes `pageType ?? type`. So `app`/`home`/`utility` pages rendered via `PageView` **silently collapse to the `record` layout**. Only `template`-named pages route correctly. Same drift: renderer reads `title` (`page.tsx:429`), spec emits `label` → the page header never shows the spec label. Non-spec `priority` is also read for page selection (`usePageAssignment.ts:139`).
10+
11+
## 🔴 Component-level `visibility` (CEL) silently does nothing
12+
`SchemaRenderer` gates on `visible`/`hidden`/`visibleOn`/`hiddenOn` (`:251-262`), **never the spec's `visibility`** — an author-written visibility predicate on a page block is ignored (correctness footgun).
13+
14+
## DEAD — whole config subtrees with zero renderers
15+
`recordReview` (`record_review`), `blankLayout` (`blank`) — and the zod `superRefine` **requires** these for types that have no renderer (validation enforces config for dead features). InterfaceConfig `levels` + `allowPrinting`. `PageVariable.source`. `assignedProfiles`, `isDefault`, `icon`.
16+
17+
## LIVE & well-wired
18+
`name`, `description`, `variables{name,type,defaultValue}`, `object`, `template` (default/full-width/header-sidebar-main/three-column/dashboard), `regions`, `kind`+`slots` (slotted composition via `usePageAssignment`+`buildDefaultPageSchema`), `interfaceConfig` (10/12 sub-props: source/sourceView/appearance/userFilters/userActions/addRecord/filterBy/showRecordCount). PageComponent `type/id/label/properties/className/aria`. Component types: record:* (details/related_list/highlights/activity/chatter/path/quick_actions/history/reference_rail/alert), page:* containers, element:text/number/image/divider/button/repeater all wired. **Aspirational (render "Unknown component type")**: `element:filter`, `element:form`, `element:record_picker`, `ai:chat_window`.
19+
20+
## Recommendation
21+
Fix `type``pageType` and `label``title` at one boundary; route component `visibility` into the live gate (or rename). Either delete `bridgePage` (dead) or wire it. Prune `recordReview`/`blankLayout`/`levels`/`allowPrinting` and stop `superRefine` requiring config for unrendered types.
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Audit: ReportSchema property liveness & necessity
2+
3+
**Date**: 2026-06-15 · **Scope**: `packages/spec/src/ui/report.zod.ts` (ADR-0021 single-form). **Live path**: `objectui` `ReportRenderer``DatasetReportRenderer` (dataset-bound). Pre-9.0 object/columns-query renderers retired; old JSON limps through the lossy `specReportToPresentation` bridge.
4+
5+
## LIVE & well-wired (dataset shape — the canonical path)
6+
`name`, `label`, `description`, `type` (summary/tabular/matrix/joined), `dataset`, `rows`, `columns` (matrix-across only, matches spec), `values`, `runtimeFilter` (`?? filter`, ANDed via `mergeFilters`), `drilldown` (default-on; cells clickable), `blocks` (joined, per-block dataset/rows/columns/values/runtimeFilter). Evidence: `DatasetReportRenderer.tsx:486-575`, `:493-543`. Wired types: tabular/summary/matrix (true cross-tab + server totals + drill)/joined.
7+
8+
## 🔴 DEAD on the live path — aspirational
9+
- **`chart`** (top-level and per-block) — in the spec + `report.form.ts:62` (editable in Studio) but **no renderer reads `report.chart`**. The dataset renderer ignores it entirely; legacy `ReportViewer` only handles a different `section.chart`/`xAxisField` shape via the bridge, which never populates from spec `report.chart`. **Editing chart config produces no output.**
10+
- **`aria`**, **`performance`**`report.form.ts:71-72` only; no renderer.
11+
12+
## 🔴 Obsolete sub-schemas (type-only re-exports, fully superseded by ADR-0021)
13+
- `ReportColumnSchema` (`field/label/aggregate/responsive`) — replaced by plain `string[]` `values`; per-column aggregate/label now live in the **dataset** definition.
14+
- `ReportGroupingSchema` (`field/sortOrder/dateGranularity`) — replaced by `string[]` `rows`; sort/granularity now in the **dataset**.
15+
- `ReportChartSchema` (`xAxis/yAxis/groupBy`) — **naming drift**: the only chart code (`ReportViewer.tsx:329`) reads legacy `xAxisField/yAxisFields`, never the spec's `xAxis/yAxis` → spec chart field names are dead.
16+
17+
## Studio gap (PARTIAL)
18+
`ReportPreview.tsx:34` branches on top-level `draft.dataset` only — a **`joined`** report (legitimately has no top-level `dataset`; data on `blocks`) falls to the "bind a dataset" empty state instead of previewing, though the runtime renders it. Joined previews are effectively unwired in Studio.
19+
20+
## Recommendation
21+
Remove `chart`/`aria`/`performance` from ReportSchema (or wire `chart` for dataset reports). Delete the obsolete `ReportColumn/ReportGrouping/ReportChart` re-exports. Fix `ReportPreview` to preview joined reports (branch on `dataset || blocks?.length`).

0 commit comments

Comments
 (0)