## Summary
Gives external integrations (command palettes, plugin launchers,
alternate dashboard shells) a supported way to **discover the host UI**
— instead of hardcoding the dashboard's view ids, labels and settings
search terms and hand-syncing them on every release. This is the
read-only metadata slice of the "constrained by a stable host context
and API client" idea in
`docs/proposals/2026-07-01-dashboard-theme-plugin-system.md`, and the
follow-on to #2415 (theme tokens + overlay layering).
Two additions, both inert unless called:
| Endpoint | Returns |
|---|---|
| `GET /api/views` | Every registered built-in view id, in dashboard
order — `id`, English `label`, plus optional i18n `labelKey`, legacy
`aliases` and `internal` flag. |
| `GET /api/settings/sections` | Selectable Settings sections — `id`,
`label`, `labelKey`, `scope`, `group`, `keywords`, `searchableKeys`,
`advanced`. |
Both are read-only, return static project-independent metadata, take no
project id, and are mounted inside `createApiRoutes` so they sit behind
exactly the same `/api` authentication as every other dashboard route —
no more, no less.
## What actually changed — one source of truth
The endpoints are the small part. The core of the diff is **collapsing
duplicated UI metadata into two shared registries that now drive both
the dashboard UI and the API**:
- `packages/dashboard/src/shared/dashboard-views.ts` — canonical view
ids + English labels + i18n keys + legacy aliases.
- `packages/dashboard/src/shared/settings-sections.ts` — canonical
settings sections + scope/group/search metadata, with `group` and
`advanced` derived from the list's own structure.
`LeftSidebarNav`, `SettingsModal` and `useViewState` were rewritten to
consume those registries instead of carrying their own copies (net
**−230 lines** in `SettingsModal` alone). Edit the registry and the
rendered UI and the API move together.
## Drift protection
Being precise about what each test can and cannot catch, because
"no-drift" claims are easy to overstate:
- `left-sidebar-nav-registry-parity.test.tsx` — the one test that
catches drift the registry does not already determine. It **renders**
the sidebar with a recording `t()` spy and pins each entry's translation
key and English fallback to the registry (the sidebar still hardcodes
its keys). It also asserts the rendered destination count equals the
enrolled id list, so a newly added sidebar view fails until it is
enrolled.
- `ui-metadata-sync.test.ts` — pins the Settings navigation list,
advanced-visibility set, persisted view list, reset-key registry and
both endpoint payloads to the registries. Since those consumers are now
*derived* from the registries, these assertions mainly guard against a
future consumer **re-hardcoding** its own copy. Two of them do stand on
their own: each section's served `group` is pinned to the group header
it actually renders under, and no published `labelKey` may resolve to a
non-leaf i18n node.
- `register-ui-metadata-routes.test.ts` — drives the real Express router
and asserts each endpoint serves the registry payload verbatim, with no
filtering or reshaping.
- Exactly two **existing** tests are updated, both for the same reason:
they asserted that `SettingsModal.tsx`'s *source text* contains a
section literal that now lives in the registry.
`VoiceInputSection.modal-visibility.test.tsx` now asserts Voice Input's
Basic-mode contract against `SETTINGS_SECTION_METADATA`, and
`mcp-documentation.test.ts` reads the registry for the two MCP section
ids. No other existing test in the package changes.
## Design notes / decisions for review
- **`GET /api/views` returns the full registry, not the live menu.** It
includes flag-gated / experimental ids and `internal` (non-navigable)
destinations; reachability depends on flags and plugins this endpoint
does not evaluate. Documented as "known view ids", not "visible nav
entries".
- **`labelKey` is optional and best-effort; `label` is the guarantee.**
A `labelKey` is published only where the dashboard itself renders that
view's title through it. `graph` (labelled from a plugin manifest) and
the internal `task-detail` carry none rather than advertise a key that
resolves to nothing — and `task-detail` in particular must not point at
`taskDetail.title`, which is an occupied i18n *namespace* whose lookup
returns an object rather than falling through to a default. A guard test
now enforces that. Separately, a few published keys (`nav.ideation`,
`nav.importTasks`, `nav.automations`, `pr.view.title`) are the
dashboard's real keys but aren't in the shipped catalogs yet because the
host supplies their English inline; the docs say plainly that consumers
must fall back to `label`.
- **`keywords` / `searchableKeys` are explicitly non-contractual.**
`searchableKeys` exposes the raw i18n translation-key strings backing a
section's searchable copy; values, ordering and presence may change
between releases. Documented as best-effort search hints, never stable
identifiers.
- **Migration is deliberately partial.** The desktop sidebar, Settings
navigation and persisted view list now come from the registries;
`Header.tsx` and the mobile More sheet still hardcode a few of the same
labels. They can still drift from what `GET /api/views` reports;
converting them is left to a follow-up so this diff stays reviewable.
- **No project scoping, deliberately.** The proposal doc rightly pushes
plugin traffic through a project-scoped client — these two endpoints are
the exception that proves the rule: they return static registry metadata
that is identical for every project, so threading a `projectId` would
imply a scoping guarantee that does not exist here. They never touch
`getScopedStore` / `TaskStore`.
- **Two endpoints rather than one `/api/ui-metadata` envelope.** Views
and Settings sections are independent registries with different
consumers, and `/settings/sections` sits naturally beside the existing
`/settings/*` routes. A consumer that only needs navigation doesn't pay
for settings metadata.
- **The registry extraction ships with the endpoints rather than as a
separate PR.** The registries *are* the mechanism that keeps the API
honest — split apart, the first half is a refactor with no observable
effect and the second can't land without it.
- **Placement:** `packages/dashboard/src/shared/` is a new directory,
and these are the first *production* `app/ → src/` imports in the
package (today the only one is in `ProviderIcon.test.tsx`). They sit
under `src/` because `src/`'s tsconfig cannot import `app/`, so a module
both sides consume has nowhere else to go; both registries are
dependency-free data leaves, and `vite build` plus
`check-no-node-only-core-imports-in-dashboard` confirm the client bundle
is unaffected. The considered alternative was `packages/core/src` behind
the `dashboard-browser-safe-core-modules.json` allowlist, where
`mobile-nav-primary-items.ts` keeps a destination→labelKey table — these
stayed out of `core` because they are dashboard-owned UI ids, and
because the two tables describe different surfaces (core mirrors the
mobile nav's `nav.skills`/`nav.settings`; this registry mirrors the
desktop sidebar's `header.skillsView`/`header.settings`).
- Ships a `@runfusion/fusion` **minor** changeset (`category: feature`).
Happy to adjust any of the above — shape, placement, or dropping
`searchableKeys` — if you'd rather it landed differently.
## Verification
- Rebased onto `main@26dcccb7c`. Two conflicts, both resolved by
absorbing upstream's work rather than reverting it:
- `SettingsModal.tsx` — upstream's `voice-input` section (and the
`FNXC:VoiceInput` decision comment explaining it stays out of the
advanced-only set) moved into the registry. The registry's section list
is byte-identical to `main`'s `SETTINGS_SECTIONS` (45/45 entries, all
fields), and the registry-derived `ADVANCED_SETTINGS_SECTION_IDS` is
identical to `main`'s hardcoded set (19/19, same order) — both verified
mechanically, not by eye. Upstream's `RUNTIME_*`
hide-uninstalled-runtimes sets are untouched.
- `routes/README.md` — the `mount-sequence` list regenerated from
`CREATE_API_ROUTES_REGISTRAR_MOUNT_SEQUENCE`, so `registerVoiceRoutes`
and `registerUiMetadataRoutes` are both in place and the contract test
passes.
- `DASHBOARD_VIEWS` covers exactly `main`'s `BuiltInTaskView` union,
aliases included, and `BUILT_IN_TASK_VIEWS` reproduces `main`'s 27-entry
array in order (`devserver` still preceding `dev-server` for the
migration path).
- Every one of the 20 sidebar labels the refactor rewrote was checked to
be byte-identical to `main`'s hardcoded fallback, and every `FNXC:`
decision comment displaced by the move was accounted for — all 75 in
`SettingsModal.tsx` and all 11 in `useViewState.ts` survive, relocated
onto the registry entries they document.
- The full `dashboard-app` + `dashboard-api` suites were run at this
commit (**20,706 passing**) and again on unmodified `main@26dcccb7c`,
and the failing-file sets compared: **every file that fails here also
fails on `main`** — nothing regresses. The overlap is environment-driven
(Postgres-backed `*.pg.test.ts`, tests needing built `dist` artifacts,
and `SettingsModalNodeRouting.test.tsx`'s `No "fetchSystemInfo" export
is defined on the "../../api" mock`), none of it touched by this change.
- `tsc --noEmit` clean for both dashboard projects, `eslint` clean on
every changed file, and `vite build` of the client bundle succeeds (the
two pre-existing `@fusion-plugin-examples/claude-runtime` /
`playwright-core` module-resolution errors reproduce on unmodified
`main`).
- Repo gate scripts pass: `check-changeset-format`,
`check-routes-modular`, `check-no-node-only-core-imports-in-dashboard`,
`check-no-cwd-relative-dashboard-test-reads`, `check-mock-completeness`.
- The three new assertions were mutation-tested rather than assumed
load-bearing: breaking the registry's `group` derivation, dropping an
enrolled sidebar id, and re-pointing `task-detail` at the
`taskDetail.title` namespace each make their test fail.
- Local CodeRabbit review over two passes: 3 minor findings, all
addressed (parity projection missing `group`; route tests asserting
partial instead of exact payloads; the `labelKey` guard not covering the
settings registry).
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Added authenticated, read-only APIs for discovering dashboard views
and selectable Settings sections.
* Added dashboard view metadata, including labels, aliases, internal
status, and translation keys.
* Added Settings metadata with grouping, scope, advanced status, and
search-related information.
* Updated navigation and Settings UI labels to use shared metadata.
* **Documentation**
* Documented the new metadata endpoints and integration guidance.
* **Bug Fixes**
* Added safeguards and automated checks to keep UI navigation and API
metadata synchronized.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Co-authored-by: Claude <noreply@anthropic.com>
Keep Compound Engineering navigation surfaces on the Boxes glyph even when stale plugin metadata is served.
- Route desktop and mobile plugin nav entries through a dashboard-view icon resolver.
- Pin Compound Engineering plugin nav icons to Boxes by plugin id while preserving normal icon fallback behavior for other plugins.
- Align manifest/header icon invariants and add regression coverage for stale Sparkles/Grid3X3 metadata.
- Add a patch changeset for the published Fusion package.
Files changed:
.changeset/FN-7198-compound-engineering-icon-match.md | 7 +++++++
packages/dashboard/app/components/LeftSidebarNav.tsx | 4 ++--
packages/dashboard/app/components/MobileNavBar.tsx | 6 +++---
.../app/components/__tests__/LeftSidebarNav.test.tsx | 6 +++++-
.../app/components/__tests__/MobileNavBar.test.tsx | 10 +++++++---
.../app/components/__tests__/pluginNavIcon.test.ts | 10 +++++++++-
packages/dashboard/app/components/pluginNavIcon.tsx | 12 ++++++++++++
.../src/__tests__/manifest.test.ts | 14 ++++++++++++++
.../src/dashboard/CompoundEngineeringView.tsx | 3 ++-
9 files changed, 61 insertions(+), 11 deletions(-)
Fusion-Task-Id: FN-7198
Fusion-Task-Lineage: e910bb82-55a7-4c81-a4fe-947a07b3e308
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
- LeftSidebarNav: optimistic active highlight — clicking a nav item paints the active color instantly instead of lingering on the hover color until the lazy view mounts.
- List view: the view-options toggle label is now 'View'.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- LeftSidebarNav: Planning + Missions now sit directly after List/Graph and before Agents.
- ConfirmDialog: portal to document.body and claim the top of the shared floating z-stack on open, so the discard-changes confirm appears ABOVE the floating New Task window (and other floating modals) instead of behind it.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Code review (8 reviewers) fixes:
- P1: embedded board task-detail now passes onRequestClose so delete/merge/retry dismiss the panel (no ghost task).
- P1: RightDock + TerminalModal drag handlers add pointercancel + unmount teardown (no leaked document listeners / rAF / stuck userSelect); remove dead onOpenChange prop.
- Planning slot DOM-poll interval now caps (self-cancels on mobile); heartbeat slider persist debounced + mounted-guarded.
- Cleanup: remove dead ViewHeader-migration CSS (Skills/Insights), extract shared GithubIcon, move Activity Log embedded CSS to ActivityLogModal.css, FNXC date-format fixes, AGENTS lazy-view note.
- Update stale RightDock roster tests + LeftSidebarNav 'Compound Eng' test to current behavior.
Live UI tweaks:
- Default load lands on board, never the Command Center Dashboard.
- View Board/View Agents moved to the Command Center Overview tab (under the live-activity strip).
- Heartbeat card full-width with spaced, wrapping controls.
- Dev Server panel scrolls in the dock; ChatView right-pane title takes the full line.
- Files pop-out renders deterministic left-right two-pane.
- Mailbox list pane narrower + narrow by default; Add Goal button height matches the Compound stage button; Memory Working-Memory tab no longer overlaps; Skills view fills full width.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Docked terminal resize handle: larger grab target straddling the panel edge with touch-action:none; resize batches height to one rAF update per frame and writes localStorage once on pointer-up (was a sync clamp + write every move).
- Git Manager dock tabs are icon-only in the narrow dock (label sr-only, title/aria-label tooltip) so many tabs fit horizontally.
- Left sidebar Compound plugin label reads 'Compound Eng'.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Resolve the stash-pop conflicts in command-center CSS/test by keeping the current branch's newer FN-6784 responsive clamps, Team heartbeat docs, and recharts assertions (the stashed snapshot was older).
- Land the stashed workflow-selection refactor (Board/NewTaskModal/WorkflowSwitcher; remove lastSelectedWorkflow util).
- Rename Command Center -> Dashboard (i18n source) and move it to the top of the left sidebar; workflow switcher chevron sized to match the project selector.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Usage (Activity) removed from the right dock and rendered as a non-mobile header button (left of the right-sidebar toggle) that opens the UsageIndicator as a header-anchored modal.
- Left sidebar: Skills and Memory moved to immediately after Mailbox.
- Updated overflowViewRegistry / Header / tablet-header / LeftSidebarNav tests to the new behavior (all green).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Left sidebar reordered to a single explicit list (board, list, graph, command-center, agents, chat, mailbox, planning, missions, artifacts, goals, compound, automation, import, workflows, insights, research, skills, memory, evals, remaining plugins). Command Center before Agents; Artifacts after Missions.
- Dev Server and Secrets moved off the left sidebar into the right dock (inline render; Dev Server gated by the devServerView flag).
- Import Tasks embedded view uses a Command Center-style header (GitHub logo + shared title font + padding).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Right dock: Files first/default; usage/activity-log/git-manager render inline; embedded Git Manager uses a container query (compact horizontal tab strip in the dock, full two-pane in the wide pop-out); Files inline viewer + pop-out.
- Import Tasks layout fits its container (stacked when narrow, two-pane when wide); Import Tasks uses the GitHub mark.
- Planning embeds full-area and works on mobile; planning shows the board WorkflowSwitcher.
- Automations screen uses theme color tokens.
- Workflow selector matches the project selector height/font.
- Left sidebar: uniform spacing across the primary/secondary boundary.
Adds a changeset for @runfusion/fusion (minor).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- New built-in task views: workflows, import-tasks, automations (left-sidebar destinations rendering in the main content area).
- WorkflowNodeEditor, GitHubImportModal, and ScheduledTasksModal gain a presentation=embedded mode (inline, no overlay/close, modal-only behaviors disabled). Automations embedded view uses a Command Center-style header and a responsive two-pane (list + detail) layout when wide enough.
- Left sidebar adds Workflows, Import Tasks, Automations entries; renderMainContent renders the embedded views.
- Remove github-import and automation from the right dock; hide the desktop Header Workflow button when the left sidebar owns Workflows. Mobile overflow keeps the modal entry points unchanged.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add a persistent New Task entry to the left sidebar so task creation is available from project views.
- Wire the left sidebar to the existing New Task dialog trigger from App.
- Render an accessible, token-styled New Task CTA above sidebar navigation with collapsed rail support.
- Cover the new sidebar affordance behavior and document it in the dashboard guide.
Files changed:
docs/dashboard-guide.md | 2 +-
packages/dashboard/app/App.tsx | 1 +
.../dashboard/app/components/LeftSidebarNav.css | 27 +++++++
.../dashboard/app/components/LeftSidebarNav.tsx | 18 +++++
.../components/__tests__/LeftSidebarNav.test.tsx | 83 ++++++++++++++++++++++
5 files changed, 130 insertions(+), 1 deletion(-)
Fusion-Task-Id: FN-6880
Fusion-Task-Lineage: 7d9cffd4-c216-4250-9615-51e7c16438c2
Keep auxiliary Secrets and Todos navigation anchored to the right dock instead of the left sidebar.\n\n- Remove Secrets and Todos entries and unused icons from LeftSidebarNav.\n- Update sidebar tests to assert those shortcuts are omitted while other routes still work.\n- Refresh dashboard navigation docs for right-dock Todos and Secrets placement.\n\nFiles changed:\n docs/dashboard-guide.md | 6 +++---\n packages/dashboard/app/components/LeftSidebarNav.tsx | 11 ++++-------\n .../app/components/__tests__/LeftSidebarNav.test.tsx | 8 +++-----\n 3 files changed, 10 insertions(+), 15 deletions(-)
Fusion-Task-Id: FN-6889
Fusion-Task-Lineage: de135281-a9a8-4583-8251-e78a6b28122e
Allow the dashboard left sidebar to resize down to a compact but still labeled width.
- Lower the resizable left sidebar minimum width from 192px to 160px.
- Keep narrow sidebar labels truncated so badges and toggles stay aligned.
- Cover drag, keyboard, and persisted-width clamping at the new minimum.
Files changed:
.../dashboard/app/components/LeftSidebarNav.css | 4 +++
.../dashboard/app/components/LeftSidebarNav.tsx | 5 ++-
.../components/__tests__/LeftSidebarNav.test.tsx | 37 ++++++++++++++++++++++
3 files changed, 45 insertions(+), 1 deletion(-)
Fusion-Task-Id: FN-6836
Fusion-Task-Lineage: e136647f-3cd8-4b82-8186-b69d630946fc
Move the left sidebar collapse control into the footer so it sits above Settings with row-style behavior.
- Replace the floating border collapse button with a footer row button that keeps accessible expanded/collapsed labels.
- Retokenize collapse toggle CSS around shared sidebar item styling and remove the obsolete floating modifier.
- Extend sidebar tests and dashboard guide coverage for the footer placement and collapsed rail behavior.
Files changed:
docs/dashboard-guide.md | 4 +-
.../dashboard/app/components/LeftSidebarNav.css | 24 ++--------
.../dashboard/app/components/LeftSidebarNav.tsx | 32 ++++++-------
.../components/__tests__/LeftSidebarNav.test.tsx | 54 ++++++++++++++++++++--
4 files changed, 72 insertions(+), 42 deletions(-)
Fusion-Task-Id: FN-6847
Fusion-Task-Lineage: 5d7781a1-afc9-4e5c-bf7b-0648bb874285
Keep the experimental left sidebar Settings control clear of the fixed executor footer while preserving readable project selector fallbacks.
- Share the executor footer visibility state with the left sidebar and project content.
- Reserve executor-footer height on the sidebar when the status footer is visible so Settings stays clickable in expanded and collapsed modes.
- Add project-selector translation fallbacks for incomplete locale fixtures.
- Cover footer-clearance behavior in left sidebar tests and add a patch changeset.
Files changed:
.changeset/fn-6819-sidebar-footer-clearance.md | 5 +++
packages/dashboard/app/App.tsx | 8 +++--
.../dashboard/app/components/LeftSidebarNav.css | 9 ++++++
.../dashboard/app/components/LeftSidebarNav.tsx | 4 ++-
.../dashboard/app/components/ProjectSelector.css | 2 +-
.../dashboard/app/components/ProjectSelector.tsx | 11 +++++--
.../components/__tests__/LeftSidebarNav.test.tsx | 36 ++++++++++++++++++++++
7 files changed, 67 insertions(+), 8 deletions(-)
Fusion-Task-Id: FN-6819
Fusion-Task-Lineage: 950da852-8dcf-4379-a91b-0188b12d1a6c
Replace the sidebar brand-row collapse control with a floating border toggle.\n\n- Remove the empty sidebar brand shell and keep the collapse control reachable in both expanded and rail modes.\n- Style the collapse button as a floating affordance on the sidebar edge.\n- Cover the missing brand shell, accessible toggle labels, pressed state, and persisted collapsed state in sidebar tests.\n- Update dashboard documentation to describe the border-mounted toggle.\n\nFiles changed:\n docs/dashboard-guide.md | 2 +-\n .../dashboard/app/components/LeftSidebarNav.css | 30 +++++++++++-----------\n .../dashboard/app/components/LeftSidebarNav.tsx | 28 ++++++++++----------\n .../components/__tests__/LeftSidebarNav.test.tsx | 27 +++++++++++++++++++\n 4 files changed, 56 insertions(+), 31 deletions(-)
Fusion-Task-Id: FN-6801
Fusion-Task-Lineage: e1384565-c0cc-4564-b556-f5b74a501c41