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/dashboardis 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-desktopis 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-mcpis 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-adminis 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
| Surface | Primary audience | Entry point | Router model | Backend dependency style |
|---|---|---|---|---|
apps/dashboard | End users and analysts | apps/dashboard/src/main.tsx | React Router v6 | Shared workspace packages plus a dashboard bootstrap payload |
apps/admin-desktop | Internal operators and admins | apps/admin-desktop/src/renderer/main.tsx | React Router v7 | Desktop-owned renderer plus Electron preload bridge and local control-plane runtime |
apps/admin-mcp | Agent-assisted internal operators | apps/admin-mcp/src/index.ts | MCP tools and resources | Stdio MCP server plus @sterling/admin-runtime and local control-plane API calls |
apps/legacy-admin | Legacy reference only | apps/legacy-admin/src/main.tsx | React Router v7 | Old 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:
apps/dashboard/src/main.tsxinitializes app-local observability fromapps/dashboard/src/lib/observability.tsx.- It reads
VITE_PLATFORM_API_URL, normalizes the base URL, and requests/v1/session. - If the session is authenticated, it fetches
/v1/bootstrap/dashboard. - That payload is written to
window.__STERLING_BOOTSTRAP__beforeAppis imported and rendered. - 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:
ThemeProviderQueryClientProviderTooltipProviderSessionProviderSterlingDataProviderListsProviderBrowserRouterAttorneyModalProvider
DashboardLayout in apps/dashboard/src/components/DashboardLayout.tsx is the authenticated frame for all non-login routes. It:
- blocks on
SessionProviderwhile 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:
ObservabilityPageTrackercaptures route-level pageviews and keeps the active route tag in Sentry.DashboardSessionObservabilitywaits for resolved session state, then identifies the user in PostHog and enriches Sentry withorganization_idanduser_role.
The navigation chrome is split between:
apps/dashboard/src/components/AppSidebar.tsxfor primary route groups,apps/dashboard/src/components/TopBar.tsxfor global search, notifications, profile actions, and theme switching,apps/dashboard/src/components/AttorneyProfileModal.tsxfor 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 group | Paths | Purpose |
|---|---|---|
| Auth | protected app routes | Session establishment through direct WorkOS handoff |
| Core intelligence shell | /, /attorneys, /search, /attorneys/:id, /firms, /practice-areas, /cities | Main exploration and entity analysis flows |
| Workflow tools | /lists, /analytics, /newsroom, /ai-assistant | Saved workflows, executive analytics, news feed, and conversational help |
| Account/help | /settings, /help | Preferences, 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_startedlogin_succeededsearch_executedfilters_appliedattorney_viewedfirm_viewedlist_createdexport_requestednews_item_openedai_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 andloginWithDevSessiondepending on the session provider.apps/dashboard/src/pages/AttorneyProfile.tsx: reads attorney and firm state fromSterlingDataContextand combines it with list mutations.apps/dashboard/src/pages/ListsPage.tsx: usesListsContextfor create/remove/select behavior, while rendering attorney details from the seeded/bootstrap dataset.apps/dashboard/src/pages/AiAssistantPage.tsx: keeps chat history inlocalStorageand streams assistant output from the platform API throughstreamAssistantResponse.
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.tsxapps/dashboard/src/pages/AttorneySearch.tsxapps/dashboard/src/pages/SearchPage.tsxapps/dashboard/src/pages/FirmsPage.tsxapps/dashboard/src/pages/PracticeAreasPage.tsxapps/dashboard/src/pages/CitiesPage.tsxapps/dashboard/src/pages/AnalyticsPage.tsxapps/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
8080in local development, - it is a static Vite build,
- path aliasing is local to the app via
@ -> ./src, @sentry/vite-pluginuploads hidden source maps whenSENTRY_AUTH_TOKEN,SENTRY_ORG, and the dashboard project env are provided,vercel.jsonnow 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.jsonretained as a generic static-host compatibility file.
The app-local env contract in apps/dashboard/.env.example now includes:
VITE_APP_ENVandVITE_APP_RELEASEVITE_PLATFORM_API_URLVITE_SENTRY_DSN,VITE_SENTRY_TRACES_SAMPLE_RATE,VITE_SENTRY_REPLAY_SESSION_SAMPLE_RATE, andVITE_SENTRY_REPLAY_ON_ERROR_SAMPLE_RATEVITE_POSTHOG_KEYandVITE_POSTHOG_HOSTVITE_TURNSTILE_SITE_KEYas the reserved frontend placeholder for a future login hardening step- optional
VITE_OPENFREEMAP_COMPACT_STYLE_URLandVITE_OPENFREEMAP_IMMERSIVE_STYLE_URLoverrides 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.sterlingDesktopfor 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 byapps/admin-desktop/vite.renderer.config.tsinto the packagedresources/adminoutput.
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:
| Route | Responsibility |
|---|---|
| Extractions | End-to-end roster extraction command center, run creation, runtime status, and extraction history. |
| Discovery Chat | Chat-first discovery, config synthesis, artifacts, and live event streams. |
| Agent Sessions | Operational run inspection: traces, runtime metadata, execution artifacts, browser sessions, and rerun/promote actions. |
| Scheduled Jobs | Recurring extraction and monitor orchestration from approved outputs. |
| Review & QA | Staged attorney imports, validation outcomes, and human approval before publishing. |
| Press QA | Reviewed press intelligence and monitor result triage. |
| Management | Firm configs, validation reports, schema registry, providers, readiness, and proxy pool diagnostics. |
| Analytics | Queue health, usage, validation drift, compliance summaries, and operational reporting. |
| Settings | Runtime 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
groqsettings 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 choice | Meaning |
|---|---|
| Internal scraper | The platform owns recon, browser control, extraction, validation, and staged review. |
| Browser Use Agent | Browser 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 fallback | The internal scraper stays primary, with Browser Use Agent available as a configured fallback path for eligible failures. |
browser-use-cloud browser | The 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:unsignedThose 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.tsxinitializes app-local Sentry, then mounts the shell insideSentry.ErrorBoundary.apps/legacy-admin/src/components/admin-auth-gate.tsxfetches/v1/admin/sessionfrom 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 theadmin_accessentitlement, - 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
routetag 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:
| Route | Component | Responsibility |
|---|---|---|
/discovery | ChatDiscoveryPage | Conversational discovery, attachments, artifacts, and thread history |
/agent-sessions | AgentSessionsPage | Run/session inspection, runtime graphs, configs, failures, and replay controls |
/scheduled-jobs | ScheduledJobsPage | Recurring orchestration created from approved discovery outputs |
/management | inline ManagementPage inside App.tsx | Skills, validation reports, compliance, readiness, proxy pool, and schema registry review |
/analytics | inline AnalyticsPage inside App.tsx | Aggregated operational counts, queue health, and reporting |
/settings | SettingsPage | Editable 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:
discoveryloads settings, runtime readiness, and providers.agent-sessionsloads browser sessions and grouped agent sessions.scheduled-jobsloads firms, EIPs, and jobs.managementloads skills, validation reports, compliance, readiness, providers, proxy pool, and schema registry.analyticsloads analytics, compliance, readiness, providers, proxy pool, schema registry, and queue metrics.settingsloads 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.tsxsubscribes 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-pluginuploads hidden source maps whenSENTRY_AUTH_TOKEN,SENTRY_ORG, and the admin project env are available,- Vite proxies many backend prefixes to
http://localhost:8000in 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, andVITE_CONTROL_PLANE_URLsynced 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_ENVandVITE_APP_RELEASEVITE_SENTRY_DSNandVITE_SENTRY_TRACES_SAMPLE_RATEVITE_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 atsrc, - keep app-local
components/uilayers rather than sharing a cross-app design-system package, - consume
@sterling/contractsfor shared backend-facing type shapes, - rely on cookie-backed authenticated fetches to Sterling backend services.
Important differences
| Concern | Dashboard | Admin |
|---|---|---|
| Audience | Product users | Internal operators |
| Auth behavior | Product login route and authenticated app shell | Strict local admin gate against the control-plane API |
| Data model | Context providers plus bootstrap payload and seeded fallback data | Route-aware snapshot loader plus live event refresh |
| API strategy | Shared packages like @sterling/data-client | Desktop-owned app-local control-plane.ts and chat.ts wrappers |
| UI composition | Many self-contained route pages | Large shell with operational panels and feature pages |
| Live updates | Mostly user navigation and query refresh | SSE-driven operational refresh |
| Test maturity | Minimal smoke coverage | Fixture-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.