From 16c482ee607a031fa97b90d719c04c2851d58a41 Mon Sep 17 00:00:00 2001 From: Jack Zhuang <277994282+os-zhuang@users.noreply.github.com> Date: Mon, 27 Jul 2026 22:36:51 +0800 Subject: [PATCH] =?UTF-8?q?feat(showcase):=20nav=20separator=20+=20badge?= =?UTF-8?q?=20specimens;=20correct=20the=20=C2=A73=20item-8=20verdict=20(#?= =?UTF-8?q?1878)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds the two nav-affordance specimens to the showcase Analytics group (`nav_sep_reports` separator + `badge: 'NEW'`/`badgeVariant: 'secondary'` on Hours by Status) and corrects the naming-drift recheck, which twice published a wrong verdict for this item. The keys are LIVE. UnifiedSidebar delegates the whole app-navigation tree to NavigationRenderer (UnifiedSidebar.tsx:437), which implements separator (:906) and badge/badgeVariant (:983-985); processedNavigation only reorders and pins, spreading every other key through. Browser-verified against an UNMODIFIED objectui checkout: the divider draws and the NEW pill renders. No objectui change was needed -- the port this PR was originally going to make would have duplicated existing capability. Both wrong rounds shared one shape: a strong negative claim ("the component was deleted", "nobody imports it") resting on a search whose results were silently truncated (head -3) or filtered (a pathspec glob that never matched the nested path). The doc now carries a methodology note: a grep can only prove presence; for "does authoring this key do anything", author it and look. Showcase suite 58/58 green. Co-Authored-By: Claude Fable 5 --- .../showcase-nav-affordance-specimen.md | 9 ++++ docs/audits/2026-07-naming-drift-recheck.md | 49 ++++++++++++++----- docs/audits/README.md | 2 +- examples/app-showcase/src/ui/apps/index.ts | 7 ++- 4 files changed, 54 insertions(+), 13 deletions(-) create mode 100644 .changeset/showcase-nav-affordance-specimen.md diff --git a/.changeset/showcase-nav-affordance-specimen.md b/.changeset/showcase-nav-affordance-specimen.md new file mode 100644 index 0000000000..4d18917e39 --- /dev/null +++ b/.changeset/showcase-nav-affordance-specimen.md @@ -0,0 +1,9 @@ +--- +--- + +feat(showcase): add nav `separator` + `badge`/`badgeVariant` specimens, and correct the Β§3 recheck's item-8 verdict (#1878) + +The recheck twice claimed these keys were unconsumed, both times from a faulty +search. Settled empirically: `UnifiedSidebar` delegates the app-navigation tree +to `NavigationRenderer`, which implements all three β€” the specimen renders +against an unmodified objectui checkout. Docs/examples only; releases nothing. diff --git a/docs/audits/2026-07-naming-drift-recheck.md b/docs/audits/2026-07-naming-drift-recheck.md index 6e41991415..81577e41a4 100644 --- a/docs/audits/2026-07-naming-drift-recheck.md +++ b/docs/audits/2026-07-naming-drift-recheck.md @@ -31,7 +31,7 @@ stale. Three real problems remain, two of them a coupled pair that produces | 5 | **page `label` β†’ `title`** | **πŸ”΄ FORWARD-DRIFT β€” live** | spec `label` is required (`page.zod.ts:303`); the region/SDUI renderer reads `schema.title` with no fallback (`page.tsx:503-507`). Zero `schema.label` reads in `page.tsx` | | 6 | page `visibility` | **ALIGNED** (item stale) | spec folds `visibility` β†’ `visibleWhen` (`page.zod.ts:116,126`); `react/src/SchemaRenderer.tsx:285` consumes `visibleWhen`, `:294` still honors raw `visibility` | | 7 | dashboard `title` vs `label` | **ALIGNED** (item stale) | renderer dual-reads `title \|\| label` (`plugin-dashboard/.../DashboardRenderer.tsx:782`, `DashboardGridLayout.tsx:278`). Widget-level keys additionally hard-fail via a `.strict()` schema + naming error map (`dashboard.zod.ts:273,98-123`, #1894) | -| 8 | app `accentColor` / `badgeVariant` / `separator` | **SPLIT** β€” `accentColor` aligned; the other two are **surface-conditional** | #1894 declared all three to match objectui's `NavigationRenderer`. `accentColor` is consumed on the live path (`app-shell/src/layout/ConsoleLayout.tsx:171`, `PageHeader.tsx:46`, `apps/console/src/hooks/useBranding.ts:25`). `badge`/`badgeVariant`/`separator` **are fully implemented** β€” but only in `layout/src/NavigationRenderer.tsx` (`:906` separator, `:983-985`/`:1024-1025` badge), which is a *publicly exported, SDUI-registered* component (`navigation-renderer`), **not** the console's own app sidebar. `UnifiedSidebar` β€” the component that actually renders `app.navigation` in the console β€” renders no badge and has no separator branch (`separator` is merely recognized as "no href" by `AppContent.tsx:849` and excluded by `nav-target.ts:17`) | +| 8 | app `accentColor` / `badgeVariant` / `separator` | **ALIGNED** β€” all three live (item stale) | `accentColor`: `app-shell/src/layout/ConsoleLayout.tsx:171`, `PageHeader.tsx:46`, `apps/console/src/hooks/useBranding.ts:25`. `badge`/`badgeVariant`/`separator`: implemented in `layout/src/NavigationRenderer.tsx` (`:906` separator, `:983-985`/`:1024-1025` badge) β€” and `UnifiedSidebar` **delegates the whole app-navigation tree to that component** (`UnifiedSidebar.tsx:437`, passing `processedNavigation`, which only re-orders/pins and spreads every other key through). **Browser-verified** against an unmodified objectui checkout with a showcase specimen: the separator draws and a `badge: 'NEW'` + `badgeVariant: 'secondary'` pill renders | | 9 | action `disabled` β†’ `enabled` | **RESOLVED 2026-07** | fixed across all six rendering surfaces (objectui#2863); showcase specimen `showcase_archive_task` (#3643) | | β€” | flow `http` vs `http_request` | **ALIGNED** (item stale) | spec enum carries `http` marked canonical (`automation/flow.zod.ts:32`); the runtime registers `HTTP_TYPE = 'http'` (`service-automation/src/builtin/http-nodes.ts:48`) | | β€” | skill `requiredPermissions` vs `permissions` | **NOT A DRIFT** β€” mis-filed | `requiredPermissions` **never existed** on `SkillSchema`; the spec key is and was `permissions` (`ai/skill.zod.ts:121`). The original audit described prose/label drift in the docs, not a spec-vs-runtime mismatch | @@ -86,16 +86,15 @@ fix for this whole class; the per-item wiring below is the tactical one. 3. **`os doctor`** pointed `reference_filters` at the removed `referenceFilters` instead of the live `lookupFilters` β€” corrected (framework). +4. **app `badge`/`badgeVariant` + nav `separator` β€” NOT a drift; nothing to do.** + *(This entry twice carried a wrong verdict before it was settled empirically β€” + see "A note on how this item was got wrong", below.)* All three are consumed: + `NavigationRenderer` implements them, and `UnifiedSidebar` delegates the whole + app-navigation tree to it (`UnifiedSidebar.tsx:437`). Proven end to end by a + showcase specimen (`nav_sep_reports` + `badge: 'NEW'` on Hours by Status) + rendering correctly against an **unmodified** objectui checkout. + **Recorded, not fixed (each needs an owner decision):** -4. **app `badge`/`badgeVariant` + nav `separator` β€” surface-conditional, do NOT - prune.** The capability is fully built in `NavigationRenderer` (public export - + SDUI `navigation-renderer`); what is missing is the same three affordances - in `UnifiedSidebar`, the component that actually renders `app.navigation` in - the console. So an author's `badge: '3'` works if the nav is rendered through - the SDUI component and silently vanishes in the console shell β€” a - *surface-dependent* no-op, the hardest kind to diagnose. Disposition: port the - three branches into `UnifiedSidebar` (reference implementations already exist - at `NavigationRenderer.tsx:906,983-985`), not prune the keys. 5. **`skill.permissions` has no gate** β€” mis-filed as naming drift; it is an aspirational-config item (Β§4). The ledger marks it `live` on the evidence of a *preview renderer* while the identically-unenforced `tool.permissions` is @@ -103,4 +102,32 @@ fix for this whole class; the per-item wiring below is the tactical one. where they should get a warning. 6. **`agent.knowledge` is inert at runtime** β€” naming is fixed, but RAG resolves its sources from the LLM tool call, not from the authored block. Wire or mark - experimental. + experimental. *(Both 5 and 6 are now tracked in #3686; the ledger was made + honest about them in #3685.)* + +## A note on how item 8 was got wrong β€” twice + +Worth recording, because the failure mode is generic and this document exists to +stop exactly this class of error. + +**Round 1.** `git grep -rln "NavigationRenderer" | head -3` returned three +CHANGELOG/ROADMAP hits, and that was read as "the component was deleted." The +`head -3` had truncated the real source hit further down the alphabet. +β†’ Published claim: *"NavigationRenderer no longer exists."* **False.** + +**Round 2.** `git grep -n "NavigationRenderer" -- 'packages/*/src' 'apps/*/src'` +returned nothing, and that was read as "the component is an orphan, nobody +imports it." The pathspec glob never matched the nested +`packages/app-shell/src/layout/UnifiedSidebar.tsx`. +β†’ Published claim: *"live nav renders no badge; port the branches."* **Also false.** + +**Round 3 (settled).** Author a specimen, boot the real stack, look at it. The +separator drew and the badge rendered β€” against an *unmodified* renderer. + +Both wrong rounds share one shape: **a strong negative claim ("X does not +exist", "nobody consumes X") resting on a search whose result set was silently +truncated or filtered.** A grep can only ever prove presence; absence needs +either an exhaustive search you have verified is exhaustive, or β€” better, and +decisive here β€” a runtime observation. For liveness work specifically: when the +question is "does authoring this key do anything", the cheapest *sound* answer +is usually to author it and look, not to grep for readers. diff --git a/docs/audits/README.md b/docs/audits/README.md index ffd99cb1fe..3a0b6dbcc7 100644 --- a/docs/audits/README.md +++ b/docs/audits/README.md @@ -47,7 +47,7 @@ Spec moved to `dataset`+`values`/`dimensions`, but: the **chart view variant** + ### 3. 🟠 Naming drift β†’ silent no-ops (spec key β‰  consumed key) field `maxLength`/`minLength`/`referenceFilters`/`maxRating`; page `type`β†’`pageType` & `label`β†’`title` & `visibility`; dashboard `title` vs `label`; app `accentColor`/`badgeVariant`/`separator` (renderer reads, **not in spec**); action `disabled`β†’`enabled`; flow `http` vs `http_request`; skill `requiredPermissions` vs `permissions`; agent `knowledge.{topicsβ†’sources}`; webhook `object`β†’`object_name`, `isActive`β†’`active`. -**βœ… Rechecked 2026-07-27 β€” six of nine were already resolved; the list was stale, not the code.** Closed: `referenceFilters` (β†’ `lookupFilters`, #2377), `maxRating` (β†’ `max`), page `visibility` (β†’ `visibleWhen`), dashboard `title`/`label` (renderer dual-reads; widget keys hard-fail via `.strict()`, #1894), flow `http` (canonical), agent `knowledge.topics` (alias folded, #1891), webhook keys (#3489); action `disabled` fixed across all six rendering surfaces (objectui#2863 + #3643); skill `requiredPermissions` was **never a drift** (mis-filed prose drift). **Still real, fixed in the recheck pass:** the coupled page pair `type`β†’`pageType` + `label`β†’`title` (mis-renders every non-record page today) and two snake-only `max_length` readers. **Left as decisions:** app `badge`/`badgeVariant` + nav `separator` are implemented in `NavigationRenderer` (SDUI) but missing from `UnifiedSidebar`, the console's own app sidebar β€” a surface-dependent no-op, port rather than prune; `skill.permissions` has no gate (belongs in Β§4); `agent.knowledge` is inert at runtime. Full per-item evidence: [`2026-07-naming-drift-recheck.md`](./2026-07-naming-drift-recheck.md). +**βœ… Rechecked 2026-07-27 β€” six of nine were already resolved; the list was stale, not the code.** Closed: `referenceFilters` (β†’ `lookupFilters`, #2377), `maxRating` (β†’ `max`), page `visibility` (β†’ `visibleWhen`), dashboard `title`/`label` (renderer dual-reads; widget keys hard-fail via `.strict()`, #1894), flow `http` (canonical), agent `knowledge.topics` (alias folded, #1891), webhook keys (#3489); action `disabled` fixed across all six rendering surfaces (objectui#2863 + #3643); skill `requiredPermissions` was **never a drift** (mis-filed prose drift). **Still real, fixed in the recheck pass:** the coupled page pair `type`β†’`pageType` + `label`β†’`title` (mis-renders every non-record page today) and two snake-only `max_length` readers. **Also confirmed aligned:** app `badge`/`badgeVariant` + nav `separator` are live β€” `UnifiedSidebar` delegates the app-navigation tree to `NavigationRenderer`, which implements all three (browser-verified with a showcase specimen; an earlier revision of this annotation claimed otherwise from a faulty grep β€” see the recheck doc's methodology note). **Left as decisions:** `skill.permissions` has no gate and `agent.knowledge` is inert at runtime β€” ledger corrected in #3685, enforce-or-prune tracked in #3686. Full per-item evidence: [`2026-07-naming-drift-recheck.md`](./2026-07-naming-drift-recheck.md). ### 4. 🟠 Aspirational config (rich spec, zero runtime) β€” prune or mark `experimental` field enhanced-type configs (barcode/qr/slider/rating/color/location) + governance (encryption/masking/audit/dataQuality); object `enable`/versioning/partitioning/cdc/softDelete/search; agent autonomy (memory/guardrails/structuredOutput/lifecycle); tool `outputSchema`; job `retryPolicy`/`timeout`; theme rtl/density/touchTarget; translation `messageFormat:'icu'` (no ICU engine); **portal (entire)**; webhook non-HMAC auth. diff --git a/examples/app-showcase/src/ui/apps/index.ts b/examples/app-showcase/src/ui/apps/index.ts index 71c1e1f6ce..96edd46fa4 100644 --- a/examples/app-showcase/src/ui/apps/index.ts +++ b/examples/app-showcase/src/ui/apps/index.ts @@ -94,7 +94,12 @@ export const ShowcaseApp = App.create({ { id: 'nav_revenue_pulse', type: 'dashboard', dashboardName: 'showcase_revenue_pulse', label: 'Revenue Pulse (filtered)', icon: 'sliders-horizontal' }, { id: 'nav_charts', type: 'dashboard', dashboardName: 'showcase_chart_gallery', label: 'Chart Gallery', icon: 'layout-dashboard' }, { id: 'nav_report_tabular', type: 'object', objectName: 'showcase_task', viewName: 'tabular', label: 'Task List', icon: 'table' }, - { id: 'nav_report_summary', type: 'report', reportName: 'showcase_hours_by_status', label: 'Hours by Status', icon: 'sigma' }, + // Nav-affordance specimens (framework#1878 Β§3 recheck): `separator` + // draws a divider; `badge` + `badgeVariant` render a pill on the item. + // Both are consumed by NavigationRenderer, which UnifiedSidebar uses to + // render the app navigation tree β€” dogfooded here so the wire stays hot. + { id: 'nav_sep_reports', type: 'separator' }, + { id: 'nav_report_summary', type: 'report', reportName: 'showcase_hours_by_status', label: 'Hours by Status', icon: 'sigma', badge: 'NEW', badgeVariant: 'secondary' }, { id: 'nav_report_chart', type: 'report', reportName: 'showcase_hours_by_status_chart', label: 'Hours by Status (Chart)', icon: 'bar-chart-3' }, { id: 'nav_report_matrix', type: 'report', reportName: 'showcase_status_priority_matrix', label: 'Status Γ— Priority', icon: 'grid-3x3' }, { id: 'nav_report_joined', type: 'report', reportName: 'showcase_task_overview', label: 'Task Overview', icon: 'layers' },