SterlingIndex Docs

Dashboard and Admin

Architecture deep dive for the customer-facing dashboard and the internal admin control plane.

apps/dashboard and apps/admin-desktop are the active React frontends. apps/legacy-admin remains in the repo as the old hosted admin reference app.

  • apps/dashboard is the customer-facing legal intelligence product. It mixes a real platform bootstrap and session flow with locally seeded demo-style page datasets, then layers a polished analytics and exploration UI on top.
  • apps/admin-desktop is the active internal control plane. It owns the admin renderer, Electron bridge, local API supervision, scrape-local worker controls, discovery chat, agent sessions, scheduled jobs, validation, analytics, and runtime settings.
  • apps/admin-mcp is the non-visual operator adapter for Codex/Claude-style MCP clients. It exposes runtime, discovery, review, press, jobs, artifacts, export, and publish controls over stdio while reusing the same backend runtime.
  • apps/legacy-admin is the retired hosted admin surface kept only as a reference.

This page documents the active product/admin surfaces separately, then calls out the shared contracts and the places where their implementation patterns intentionally diverge.

Frontend Boundary Map

SurfacePrimary audienceEntry pointRouter modelBackend dependency style
apps/dashboardEnd users and analystsapps/dashboard/src/main.tsxReact Router v6Shared workspace packages plus a dashboard bootstrap payload
apps/admin-desktopInternal operators and adminsapps/admin-desktop/src/renderer/main.tsxReact Router v7Desktop-owned renderer plus Electron preload bridge and local control-plane runtime
apps/admin-mcpAgent-assisted internal operatorsapps/admin-mcp/src/index.tsMCP tools and resourcesStdio MCP server plus @sterling/admin-runtime and local control-plane API calls
apps/legacy-adminLegacy reference onlyapps/legacy-admin/src/main.tsxReact Router v7Old hosted control-plane client wrappers

Dashboard Frontend (apps/dashboard)

Product responsibility

The dashboard is the main SterlingIndex product surface for exploring attorney, firm, geography, practice-area, list, analytics, newsroom, and assistant workflows. Its top-level routes are defined in apps/dashboard/src/App.tsx and rendered inside a single authenticated shell.

Runtime boot sequence

The dashboard does a small amount of work before React mounts:

  1. apps/dashboard/src/main.tsx initializes app-local observability from apps/dashboard/src/lib/observability.tsx.
  2. It reads VITE_PLATFORM_API_URL, normalizes the base URL, and requests /v1/session.
  3. If the session is authenticated, it fetches /v1/bootstrap/dashboard.
  4. That payload is written to window.__STERLING_BOOTSTRAP__ before App is imported and rendered.
  5. React is mounted inside Sentry.ErrorBoundary, using the dashboard-specific fallback UI from the same observability module.

That preloading step matters because many dashboard screens still read from apps/dashboard/src/data/mockData.ts, which is designed to prefer bootstrap data when available and fall back to seeded demo data otherwise. In other words, the dashboard currently has a hybrid architecture:

  • Authentication and bootstrap are real backend-facing flows.
  • Lists and assistant streaming use shared client packages against the platform API.
  • Many page-level analytics views still render from local seeded datasets.

Provider stack and shell composition

The provider order in apps/dashboard/src/App.tsx defines the dashboard's runtime model:

  1. ThemeProvider
  2. QueryClientProvider
  3. TooltipProvider
  4. SessionProvider
  5. SterlingDataProvider
  6. ListsProvider
  7. BrowserRouter
  8. AttorneyModalProvider

DashboardLayout in apps/dashboard/src/components/DashboardLayout.tsx is the authenticated frame for all non-login routes. It:

  • blocks on SessionProvider while session state loads,
  • redirects unauthenticated users into the hosted sign-in flow with the current route as returnTo,
  • mounts the collapsible sidebar and top bar,
  • shows a lightweight loading banner while the bootstrap-backed data query is refreshing.

App.tsx also mounts the route/session observability bindings that sit alongside the provider tree:

  • ObservabilityPageTracker captures route-level pageviews and keeps the active route tag in Sentry.
  • DashboardSessionObservability waits for resolved session state, then identifies the user in PostHog and enriches Sentry with organization_id and user_role.

The navigation chrome is split between:

  • apps/dashboard/src/components/AppSidebar.tsx for primary route groups,
  • apps/dashboard/src/components/TopBar.tsx for global search, notifications, profile actions, and theme switching,
  • apps/dashboard/src/components/AttorneyProfileModal.tsx for modal-driven attorney drill-downs that can be opened from multiple pages.

Routing model

The route table in apps/dashboard/src/App.tsx groups naturally into four buckets:

Route groupPathsPurpose
Authprotected app routesSession establishment through direct WorkOS handoff
Core intelligence shell/, /attorneys, /search, /attorneys/:id, /firms, /practice-areas, /citiesMain exploration and entity analysis flows
Workflow tools/lists, /analytics, /newsroom, /ai-assistantSaved workflows, executive analytics, news feed, and conversational help
Account/help/settings, /helpPreferences, profile, support, and documentation-style help content

NotFound handles unmatched routes, while ScrollToTop restores more traditional page navigation behavior between route changes.

State and data access

The dashboard has three main data layers:

1. Session state

apps/dashboard/src/context/SessionContext.tsx uses @tanstack/react-query with getSession from @sterling/data-client and the shared SessionResponse contract from @sterling/contracts.

This context is the source of truth for:

  • whether the user is authenticated,
  • login redirect behavior,
  • user/profile details shown in the top bar,
  • refresh() after sign-in.

2. Bootstrap-backed reference data

apps/dashboard/src/context/SterlingDataContext.tsx queries getDashboardBootstrap(PLATFORM_API_URL) only after session auth succeeds. The response shape is the shared DashboardBootstrapResponse contract.

This powers:

  • attorney and firm collections,
  • practice area and city collections,
  • newsroom items,
  • default lists,
  • trend datasets such as movement, growth, and practice-area history.

3. List mutations

apps/dashboard/src/context/ListsContext.tsx wraps createList, addListItems, and removeListItem from @sterling/data-client, but it also includes optimistic fallback behavior when mutations fail. That means the lists UX is resilient for demos and partial-backend availability, not purely strict-server-driven.

4. Observability and product analytics

apps/dashboard/src/lib/observability.tsx is intentionally app-local rather than a shared package. It owns:

  • Sentry initialization with browser tracing, low replay sampling, release/environment tags, and masked replays
  • PostHog initialization with product analytics, identified-user profiles, and organization grouping
  • route pageviews and global event helpers
  • session-aware cleanup so logout resets PostHog identity and clears Sentry user context

The checked-in event taxonomy is intentionally compact and product-oriented:

  • login_started
  • login_succeeded
  • search_executed
  • filters_applied
  • attorney_viewed
  • firm_viewed
  • list_created
  • export_requested
  • news_item_opened
  • ai_prompt_submitted

This is also where the current free-plan optimization lives: Sentry remains the error system of record, while PostHog owns product analytics and dashboard-only session recordings.

Major page groups

The dashboard pages fall into two implementation styles.

Entity and workflow pages backed by shared context

  • apps/dashboard/src/pages/LoginPage.tsx: switches between WorkOS-style redirect login and loginWithDevSession depending on the session provider.
  • apps/dashboard/src/pages/AttorneyProfile.tsx: reads attorney and firm state from SterlingDataContext and combines it with list mutations.
  • apps/dashboard/src/pages/ListsPage.tsx: uses ListsContext for create/remove/select behavior, while rendering attorney details from the seeded/bootstrap dataset.
  • apps/dashboard/src/pages/AiAssistantPage.tsx: keeps chat history in localStorage and streams assistant output from the platform API through streamAssistantResponse.

Analytics-heavy pages still driven by local seeded data

The following screens import directly from apps/dashboard/src/data/mockData.ts:

  • apps/dashboard/src/pages/Dashboard.tsx
  • apps/dashboard/src/pages/AttorneySearch.tsx
  • apps/dashboard/src/pages/SearchPage.tsx
  • apps/dashboard/src/pages/FirmsPage.tsx
  • apps/dashboard/src/pages/PracticeAreasPage.tsx
  • apps/dashboard/src/pages/CitiesPage.tsx
  • apps/dashboard/src/pages/AnalyticsPage.tsx
  • apps/dashboard/src/pages/NewsroomPage.tsx

This is an important architectural nuance: the shell, auth, and bootstrap contracts are already wired for production-style data, but much of the product storytelling and analytics visualization layer still assumes a curated local dataset shape.

Build and deployment assumptions

  • Package metadata: apps/dashboard/package.json
  • Vite config: apps/dashboard/vite.config.ts
  • Static-host routing: apps/dashboard/vercel.json, apps/dashboard/public/_redirects

Operationally:

  • the app runs on port 8080 in local development,
  • it is a static Vite build,
  • path aliasing is local to the app via @ -> ./src,
  • @sentry/vite-plugin uploads hidden source maps when SENTRY_AUTH_TOKEN, SENTRY_ORG, and the dashboard project env are provided,
  • vercel.json now carries cache/security headers that complement the planned Cloudflare edge proxy,
  • client-side routing relies on a static-host rewrite strategy such as Cloudflare Pages SPA redirects, with vercel.json retained as a generic static-host compatibility file.

The app-local env contract in apps/dashboard/.env.example now includes:

  • VITE_APP_ENV and VITE_APP_RELEASE
  • VITE_PLATFORM_API_URL
  • VITE_SENTRY_DSN, VITE_SENTRY_TRACES_SAMPLE_RATE, VITE_SENTRY_REPLAY_SESSION_SAMPLE_RATE, and VITE_SENTRY_REPLAY_ON_ERROR_SAMPLE_RATE
  • VITE_POSTHOG_KEY and VITE_POSTHOG_HOST
  • VITE_TURNSTILE_SITE_KEY as the reserved frontend placeholder for a future login hardening step
  • optional VITE_OPENFREEMAP_COMPACT_STYLE_URL and VITE_OPENFREEMAP_IMMERSIVE_STYLE_URL overrides for the city intelligence maps; otherwise the dashboard uses the public free OpenFreeMap Positron style

The repo-level deployment guidance now treats these values as Cloudflare Pages env vars that should be managed from Infisical Secret Syncs rather than hand-edited per project.

Test surface

The dashboard test surface is currently minimal:

  • test setup: apps/dashboard/src/test/setup.ts
  • example smoke test: apps/dashboard/src/test/example.test.ts

That means architecture confidence currently comes more from route/code inspection and build/test execution than from deep automated behavior coverage.

Active Admin Frontend (apps/admin-desktop)

The active admin app is the Electron desktop package. Its renderer is copied from the former hosted admin into apps/admin-desktop/src/renderer, while the main process, preload bridge, packaging, local API supervisor, and scrape-local worker runtime stay under apps/admin-desktop/src/main and apps/admin-desktop/src/preload.

The important ownership change is that apps/admin-desktop is now the source of truth for admin UI work. New admin features, settings, route behavior, and renderer tests should land under apps/admin-desktop/src/renderer, not in the old hosted app.

Desktop boot and packaging

The desktop package has three layers:

  • Electron main process: window, tray, updater, local API supervision, packaged backend resources, and worker lifecycle.
  • Preload bridge: window.sterlingDesktop for env import, runtime status, local API start/stop/restart, and scrape-local worker controls.
  • Renderer: the React admin shell under apps/admin-desktop/src/renderer, built by apps/admin-desktop/vite.renderer.config.ts into the packaged resources/admin output.

The renderer defaults VITE_CONTROL_PLANE_URL to http://127.0.0.1:13000, because the desktop app can supervise its own local control-plane API. Packaged builds no longer call pnpm --dir ../admin build; pnpm --dir apps/admin-desktop build owns the Electron main, preload, and renderer outputs directly.

Active route surfaces

The desktop renderer keeps the operational route model that operators already use:

RouteResponsibility
ExtractionsEnd-to-end roster extraction command center, run creation, runtime status, and extraction history.
Discovery ChatChat-first discovery, config synthesis, artifacts, and live event streams.
Agent SessionsOperational run inspection: traces, runtime metadata, execution artifacts, browser sessions, and rerun/promote actions.
Scheduled JobsRecurring extraction and monitor orchestration from approved outputs.
Review & QAStaged attorney imports, validation outcomes, and human approval before publishing.
Press QAReviewed press intelligence and monitor result triage.
ManagementFirm configs, validation reports, schema registry, providers, readiness, and proxy pool diagnostics.
AnalyticsQueue health, usage, validation drift, compliance summaries, and operational reporting.
SettingsRuntime configuration for model routing, gateways, proxies, browser transport, managed execution, env import, and schedules.

Discovery Chat, Extractions, Agent Sessions, and Review & QA are related but not interchangeable: Discovery creates and refines plans, Extractions runs roster workflows, Agent Sessions inspects the execution trace, and Review & QA gates staged data before it becomes product-facing state.

Runtime settings model

The active settings UI is designed around explicit runtime choices instead of one large provider list.

  • Model routing stays limited to chat-completion-compatible routes: Ollama, LM Studio, OpenRouter, and bring-your-own OpenAI-compatible endpoints.
  • OpenAI-compatible routing includes provider label, base URL, API key, static extra headers, connection checks, and model discovery from the endpoint.
  • Groq is intentionally removed from the supported provider set; old groq settings normalize back to a supported route.
  • Browser Use is not an LLM provider. It appears under Agent Runtime as managed execution and under Browser as a remote CDP browser engine.
  • Selecting Browser Use Agent or Browser Use fallback parks unrelated internal LLM controls so operators can see which settings actually affect the current execution path.
  • Proxy management, browser transport, agent runtime limits, and env import are first-class settings because they affect run placement and extraction behavior, not just UI preferences.

The active runtime choices are:

Runtime choiceMeaning
Internal scraperThe platform owns recon, browser control, extraction, validation, and staged review.
Browser Use AgentBrowser Use receives a structured task, streams live messages back, returns typed output, and the platform ingests that output into the same staged review pipeline.
Internal with Browser Use fallbackThe internal scraper stays primary, with Browser Use Agent available as a configured fallback path for eligible failures.
browser-use-cloud browserThe platform still controls extraction, but the browser is a remote Browser Use CDP session with profile, proxy-country, timeout, recording, and diagnostics settings.

Roster/profile enrichment remains one combined roster task. There is no separate profile-enrichment scrape type in the UI or backend contract.

Verification surface

The active desktop checks are:

pnpm --dir apps/admin-desktop test
pnpm --dir apps/admin-desktop build
pnpm --dir apps/admin-desktop run pack:unsigned

Those commands cover renderer tests, Electron main/preload tests, renderer build ownership, packaged-resource copying, and unsigned package creation. Use legacy-admin checks only when you are deliberately validating the retained reference app.

Non-Visual Admin Adapter (apps/admin-mcp)

apps/admin-mcp is not a frontend, but it belongs in the admin architecture because it is now a full operator surface. It lets MCP clients call Sterling tools directly over stdio instead of clicking through the Electron renderer.

The MCP adapter is built around the same operator lifecycle as the desktop app:

  • runtime status, env import, start, stop, repair, logs, and VPS status,
  • roster discovery chat, firm management, conversation reads, preview saves, and CSV export,
  • staged review, row updates, session publish, and VPS snapshot publish,
  • press source discovery, monitor creation, run-now, article review, and article decisions,
  • session, extraction, source-report, scheduled-job, and run-now artifact flows.

The important behavior default is that MCP connection alone is inert. Connecting Codex or Claude to the server lists tools and resources only. The local API and worker start only through sterling_runtime_start, sterling_runtime_repair, or a workflow tool called with ensure_runtime: true.

The implementation depends on @sterling/admin-runtime for env storage, process supervision, control-plane calls, and snapshot package construction. The backend still owns discovery, extraction, validation, press monitoring, and publish semantics.

See Platform API -> Admin MCP Server for the full tool/resource inventory, Codex config, safety rules, and verification runbook.

Legacy Admin Frontend (apps/legacy-admin)

Product responsibility

The legacy admin app is not the active admin surface. It is the old hosted operator console retained as a reference for route behavior and prior deployment assumptions:

  • discovery chat,
  • agent session inspection,
  • scheduled job management,
  • skill/EIP review,
  • runtime and provider visibility,
  • compliance and analytics review,
  • operational settings.

The app is mounted in apps/legacy-admin/src/App.tsx behind an auth and role gate. New admin development should target the matching desktop renderer files under apps/admin-desktop/src/renderer.

Runtime entry and access control

The legacy admin bootstrap is simpler than the dashboard bootstrap but stricter about access:

  • apps/legacy-admin/src/main.tsx initializes app-local Sentry, then mounts the shell inside Sentry.ErrorBoundary.
  • apps/legacy-admin/src/components/admin-auth-gate.tsx fetches /v1/admin/session from the control plane, then:
    • renders the local admin username/password form for unauthenticated operators,
    • blocks non-admin users even if they are authenticated,
    • allows access only for users with role === "admin" or the admin_access entitlement,
    • clears or enriches the active Sentry scope once session state resolves.

This gate keeps admin access separate from WorkOS and the dashboard while still enforcing authorization inside the admin app itself.

Admin observability

apps/legacy-admin/src/lib/observability.tsx keeps the control-plane monitoring rules app-local:

  • Sentry initializes with release/environment tags and browser tracing.
  • route changes update the active Sentry route tag from the shell.
  • resolved admin sessions attach organization_id, user_role, and user identity to the current scope.
  • the runtime intentionally avoids session replay so the operations console keeps a tighter privacy boundary than the customer dashboard.

Shell architecture

App.tsx is the primary shell, not just a route registry. It owns:

  • shell-level search and command palette state,
  • sidenav collapse and dialog state,
  • top-level feedback banners,
  • job/session modal orchestration,
  • route-aware data loading,
  • live event subscription and refresh behavior.

The admin shell is closer to an operations workstation than a classic nested-route frontend. Most of the behavior lives in a single composition root, while feature pages receive already-fetched slices of a shared snapshot object.

Route structure

The main routes are defined in apps/legacy-admin/src/App.tsx and mirrored in apps/legacy-admin/src/lib/dashboard-data.ts:

RouteComponentResponsibility
/discoveryChatDiscoveryPageConversational discovery, attachments, artifacts, and thread history
/agent-sessionsAgentSessionsPageRun/session inspection, runtime graphs, configs, failures, and replay controls
/scheduled-jobsScheduledJobsPageRecurring orchestration created from approved discovery outputs
/managementinline ManagementPage inside App.tsxSkills, validation reports, compliance, readiness, proxy pool, and schema registry review
/analyticsinline AnalyticsPage inside App.tsxAggregated operational counts, queue health, and reporting
/settingsSettingsPageEditable control-plane settings and provider/runtime diagnostics

/ redirects to /discovery, /orchestration redirects to /scheduled-jobs, and unknown routes fall back to /discovery.

Snapshot loading model

The key architectural primitive is useDashboardSnapshot() in apps/legacy-admin/src/App.tsx. Instead of each route owning its own data client layer, the shell chooses a fetch bundle based on currentRoute.

Examples:

  • discovery loads settings, runtime readiness, and providers.
  • agent-sessions loads browser sessions and grouped agent sessions.
  • scheduled-jobs loads firms, EIPs, and jobs.
  • management loads skills, validation reports, compliance, readiness, providers, proxy pool, and schema registry.
  • analytics loads analytics, compliance, readiness, providers, proxy pool, schema registry, and queue metrics.
  • settings loads editable settings plus runtime/provider/proxy diagnostics.

This keeps page components focused on rendering and interaction flows, while the shell owns refresh semantics and cross-route operational context.

Live refresh and event streams

The admin shell subscribes to server-sent events using buildControlPlaneEventsUrl(...) from apps/legacy-admin/src/lib/control-plane.ts. It uses those events to trigger debounced snapshot refreshes instead of relying solely on manual reloads.

That same live-update pattern appears inside discovery chat:

  • apps/legacy-admin/src/components/chat-discovery.tsx subscribes to conversation and run events,
  • re-fetches thread state and conversation lists after relevant updates,
  • preserves the current UI if a refresh fails.

This is a major difference from the dashboard, which is mostly route-local and query-based. The admin console is event-reactive because it is supervising long-running backend work.

The current shell also surfaces hybrid-runtime state directly in the chrome:

  • the readiness banner includes local scrape-node online/offline state derived from /runtime/readiness
  • session detail panels expose the resolved execution target for each run
  • ad hoc discovery and approved-run actions default to execution_target: "auto" so the control plane can prefer a healthy macOS scrape node without making the UI choose infrastructure manually

Control-plane client layer

Unlike the dashboard, the admin app does not rely on a shared workspace package for most of its domain calls. Instead it keeps an app-local client layer:

  • low-level fetch helpers: apps/legacy-admin/src/lib/api.ts
  • operational endpoint wrappers: apps/legacy-admin/src/lib/control-plane.ts
  • discovery chat client: apps/legacy-admin/src/lib/chat.ts

Representative endpoint groups exposed through control-plane.ts include:

  • /firms
  • /sessions
  • /agent-sessions
  • /skills
  • /eips
  • /scheduled-jobs
  • /validation-reports
  • /analytics/overview
  • /analytics/queues
  • /reports/compliance
  • /runtime/readiness
  • /providers
  • /proxy-pool
  • /schema-registry
  • /settings
  • /orchestration/*

This is a strong signal that the admin app is the orchestration/control-plane frontend for the broader backend runtime, not just a thin admin skin over the same product APIs used by the dashboard.

Major feature surfaces

Discovery chat

apps/legacy-admin/src/components/chat-discovery.tsx is the primary operator workflow. It combines:

  • conversation history and thread switching,
  • artifact and attachment handling,
  • assistant-ui transport rendering,
  • SSE-backed refresh,
  • links between discovery threads and downstream sessions/jobs/settings.

This makes discovery the intake layer for future automation, not just a chat widget.

Agent sessions

apps/legacy-admin/src/components/agent-sessions-page.tsx is the deepest operational inspection view. It pulls:

  • grouped conversation/run/session summaries,
  • browser session metadata,
  • runtime environment snapshots,
  • graph revisions,
  • config revisions,
  • failure traces,
  • job links and rerun/promote actions.

Architecturally, this is where the app exposes the backend's runtime introspection model most directly.

Scheduled jobs

apps/legacy-admin/src/components/scheduled-jobs-page.tsx enforces an important business rule in the UI: recurring orchestration starts from approved discovery outputs, not arbitrary ad hoc forms.

The page loads approved EIPs, ties jobs back to firms and conversations, and supports run-now, enable/disable, update, and delete flows.

Recurring jobs intentionally stay more conservative than ad hoc runs:

  • the creation/update contract now carries execution_target
  • the current UI defaults scheduled jobs to cloud
  • run-now requests preserve that execution target so a scheduled job does not unexpectedly depend on the Mac being online

Settings

apps/legacy-admin/src/components/settings-page.tsx exposes editable operational configuration rather than simple user preferences. The form is built around a mutable ControlPlaneSettingsEditable structure and includes source badges for editable, locked, secret, and unavailable values.

Build and local dev assumptions

  • Package metadata: apps/legacy-admin/package.json
  • Vite config: apps/legacy-admin/vite.config.ts

Operationally:

  • the app runs on port 3001,
  • it uses React 19 and React Router v7,
  • Tailwind is integrated through @tailwindcss/vite,
  • @sentry/vite-plugin uploads hidden source maps when SENTRY_AUTH_TOKEN, SENTRY_ORG, and the admin project env are available,
  • Vite proxies many backend prefixes to http://localhost:8000 in local development,
  • the app can also be pointed at an explicit VITE_CONTROL_PLANE_URL,
  • hosted deployment is optional/reference-only on Cloudflare Pages, with VITE_APP_ENV, VITE_APP_RELEASE, VITE_SENTRY_DSN, VITE_SENTRY_TRACES_SAMPLE_RATE, and VITE_CONTROL_PLANE_URL synced from Infisical if that old web surface is deliberately retained.

The app-local env contract in apps/legacy-admin/.env.example now includes:

  • VITE_APP_ENV and VITE_APP_RELEASE
  • VITE_SENTRY_DSN and VITE_SENTRY_TRACES_SAMPLE_RATE
  • VITE_CONTROL_PLANE_URL

Test surface

  • test setup: apps/legacy-admin/src/setupTests.ts
  • app-level fixture-heavy test: apps/legacy-admin/src/App.test.tsx

Compared with the dashboard, the admin app already has a more representative test surface around its shell and fixture-driven operational state.

Shared Patterns and Important Differences

Shared patterns

Both apps:

  • are Vite-based React frontends inside the monorepo,
  • use local @ aliases to point at src,
  • keep app-local components/ui layers rather than sharing a cross-app design-system package,
  • consume @sterling/contracts for shared backend-facing type shapes,
  • rely on cookie-backed authenticated fetches to Sterling backend services.

Important differences

ConcernDashboardAdmin
AudienceProduct usersInternal operators
Auth behaviorProduct login route and authenticated app shellStrict local admin gate against the control-plane API
Data modelContext providers plus bootstrap payload and seeded fallback dataRoute-aware snapshot loader plus live event refresh
API strategyShared packages like @sterling/data-clientDesktop-owned app-local control-plane.ts and chat.ts wrappers
UI compositionMany self-contained route pagesLarge shell with operational panels and feature pages
Live updatesMostly user navigation and query refreshSSE-driven operational refresh
Test maturityMinimal smoke coverageFixture-backed shell coverage

The most important architectural takeaway

The two apps are related by identity and contracts, not by frontend composition.

  • The dashboard is a product experience that is still transitioning from seeded demo-style datasets toward fully backend-driven intelligence views.
  • The admin app is already shaped around real operational workflows, live backend state, and orchestration controls.

That separation is healthy: the dashboard optimizes for analyst exploration and polish, while the admin app optimizes for supervision, inspection, and control-plane safety.

On this page