Frontend Dashboard

The dashboard is a Next.js App Router project in frontend/.

Run

Default dashboard access uses Docker Compose and does not require local Node.js:

docker compose --profile dashboard up --build api dashboard

Open:

http://127.0.0.1:3000/dashboard

The roboci dev entry point can launch that same path:

roboci dev --docker

For local dashboard development with Node.js:

roboci dev

Open the dashboard URL printed by the command.

To choose ports explicitly:

roboci dev --api-port 8010 --frontend-port 3010

For scripted launches, roboci dev also reads NEXT_PUBLIC_ROBOCI_PORT or ROBOCI_FRONTEND_PORT when --frontend-port is omitted:

NEXT_PUBLIC_ROBOCI_PORT=3010 roboci dev

If the API is protected with ROBOCI_API_TOKEN, pass the same shared token to dashboard fetches with NEXT_PUBLIC_ROBOCI_API_TOKEN. roboci dev does this automatically when it starts both servers and ROBOCI_API_TOKEN is set.

To run only the frontend locally:

cd frontend
npm install
npm run dev

Open:

http://127.0.0.1:3000/dashboard

The dashboard expects the API at:

http://127.0.0.1:8000

Override with:

NEXT_PUBLIC_ROBOCI_API=http://127.0.0.1:8000 npm run dev

If server-rendered dashboard requests need a different API address than the browser, set ROBOCI_SERVER_API too:

ROBOCI_SERVER_API=http://api:8000 NEXT_PUBLIC_ROBOCI_API=http://127.0.0.1:8000 npm run dev

If that API requires a shared token:

NEXT_PUBLIC_ROBOCI_API=http://127.0.0.1:8000 NEXT_PUBLIC_ROBOCI_API_TOKEN=secret-token npm run dev

Override the standalone frontend port with Next.js' --port flag:

NEXT_PUBLIC_ROBOCI_API=http://127.0.0.1:8000 npm run dev -- --port 3010

For production-style local testing, set the same variables when starting Next so server-rendered pages and client-side dashboard actions use the right API:

NEXT_PUBLIC_ROBOCI_API=http://127.0.0.1:8000 npm exec next start
NEXT_PUBLIC_ROBOCI_API=http://127.0.0.1:8000 npm exec next start -- --port 3010

Production builds use Webpack via npm run build. This keeps dynamic App Router pages such as /failures/[failure_id] startable under next start.

Build

npm run build

Structure

frontend/
  app/
    dashboard/
    run-lab/
    runs/
    failures/
    scenarios/
    regressions/
    settings/
    tags/
  components/
  lib/

Theme and UI System

The dashboard uses a lightweight Material Design-inspired theme without adding a component framework. Theme tokens live in:

frontend/app/globals.css
frontend/tailwind.config.ts

Use semantic Tailwind colors such as bg-surface, bg-surfaceContainer, border-outlineVariant, text-onSurface, text-onSurfaceVariant, bg-primaryContainer, and status roles such as bg-successContainer, bg-errorContainer, bg-warningContainer, and bg-infoContainer. Avoid hard-coded one-off colors in page components unless the value is part of a documented token.

The selectable product themes are controlled by ThemeSwitcher in:

frontend/components/AppShell.tsx

The selector is rendered from /settings only. The selected theme is stored in localStorage under roboci-theme and applied to document.documentElement.dataset.theme without a page reload. app/layout.tsx sets the server default data-theme="blue" and uses suppressHydrationWarning on <html> because the boot script may apply a persisted theme before React hydrates.

Status colors are semantic and should remain independent from the selected product theme:

  • passed/success: green
  • failed/error: red
  • warning: amber
  • running/info: blue
  • skipped/unknown: neutral

Data Fetching

All backend calls go through:

frontend/lib/api.ts

Important functions:

  • getRuns()
  • clearRuns()
  • getRun(runId)
  • getFailures()
  • getLaunchOptions()
  • getScenarios()
  • getReplay(runId, scenarioId)
  • getTrends()

The API client returns fallbacks on fetch errors. This keeps the UI from crashing, but it can hide backend connection problems. If the dashboard looks empty, check:

http://127.0.0.1:8000/runs

Pages

/dashboard

File:

frontend/app/dashboard/page.tsx

Shows:

  • Closed-loop pass rate.
  • Critical failures.
  • Regression delta count.
  • Assertion failures from the latest closed-loop run.
  • Run trends.
  • Latest run card.
  • Latest closed-loop run card if the newest run is open-loop.
  • Confirmable reset action for clearing existing run artifacts.
  • Collapsible local artifact tree for the latest run.
  • In-dashboard previews for safe root artifacts: summary.json, metrics.json, regression.json, failures.json, config.yaml, and provenance.json.
  • Local-only actions for indexed artifacts that are not previewed through the API, including episode logs, replay files, open-loop files, and reports. The open button asks the local API to launch only indexed run artifacts with the OS default app.

The dashboard distinguishes run types:

  • closed_loop
  • openloop

This matters because open-loop runs do not have closed-loop metrics such as success_rate, safety_score, or scenario assertion results.

/run-lab

File:

frontend/app/run-lab/page.tsx

Shows:

  • Self-serve closed-loop run presets.
  • Collision failure demo selection.
  • Local scenario and scenario pack selection.
  • Search over local scenario ids, names, descriptions, and tags.
  • Agent cards with behavior labels.
  • Simulator cards with runtime labels.

Scenario tag chips in the local example list are informational. Selecting a pack still launches the full pack.

/runs

File:

frontend/app/runs/page.tsx

Shows all runs as cards.

/runs/[run_id]

File:

frontend/app/runs/[run_id]/page.tsx

Shows:

  • Closed-loop summary stats and scenario table for closed-loop runs.
  • Scenario tags plus ?tag=<tag>, ?domain=<domain>, and ?pack=<pack-id> filtering for closed-loop scenario rows.
  • Open-loop sample/task/quality/latency stats for open-loop runs.
  • Open-loop metrics for open-loop runs.
  • Provenance, including Git state, source file hashes, and dataset hashes when present.
  • Regression diff.
  • Open-by-default local artifact tree.
  • Pretty-printed JSON previews for safe root artifacts.
  • Local open actions for indexed artifacts that are not previewed through the API.

Open-loop detail pages intentionally omit closed-loop-only cards such as success rate, safety score, collisions, and the scenario table. This prevents a passing open-loop run from looking like a failed or incomplete closed-loop run.

/runs/[run_id]/scenarios/[scenario_id]

File:

frontend/app/runs/[run_id]/scenarios/[scenario_id]/page.tsx

Shows:

  • Replay viewer.
  • Replay scene with ego path, actor positions, event markers, and frame context.
  • Metric breakdown.
  • Event timeline.
  • Scenario tag chips that link back to the filtered run detail page.

/scenarios

File:

frontend/app/scenarios/page.tsx

Shows:

  • Marketplace packs and local example scenarios.
  • Tag chips for marketplace scenario previews and local examples.
  • ?tag=<tag> filtering across marketplace packs and local examples. For example, /scenarios?tag=highway shows the matching highway pack scenarios and hides unrelated packs.
  • ?domain=<domain> marketplace filtering for ground_robotics, manipulators, and drones.
  • ?q=<text> marketplace/local search across pack ids, pack names, tasks, metrics, requirements, tags, descriptions, and scenario names.
  • Pack install/remove buttons backed by the /scenario-marketplace/{pack_id}/install API endpoints.
  • Counts scoped to the active filter.

/regressions

File:

frontend/app/regressions/page.tsx

Reads actual regression.json through GET /runs/{run_id} and displays the Regression Explorer plus the compare workbench.

The explorer separates closed-loop and open-loop regression signals. Each mode has sections for:

  • New regressions.
  • Resolved regressions.
  • Recurring regressions.
  • Top degraded scenarios.
  • Top improved scenarios.
  • Critical safety regressions.

Cards are generated only from regression.json data. They show metric/status delta, previous value, current value, severity, affected scenario or sample context, and links to the run, replay, or failure when the artifact exists. Scenario diffs where the current scenario is missing are shown as status transitions such as success -> missing; they do not link to replay pages or unrelated failures.

The page also includes:

  • Compare Runs workbench for baseline diffing.
  • Regression diff table/charts for the selected comparison.
  • Table of runs with regression artifact status, delta count, and failure count.

If it shows no deltas, run:

roboci baseline create smoke --from <known-good-run-dir>
roboci compare --current runs/latest --baseline-name smoke

/failures

File:

frontend/app/failures/page.tsx

Shows failure table with severity, lifecycle status, owner, tag, and search filters. The owner filter includes Unassigned when any failure has no owner.

/failures/[failure_id]

File:

frontend/app/failures/[failure_id]/page.tsx

Shows failure detail, tag editor, lifecycle status editor, owner editor, optional fixed/regressed run fields, status history, notes editor, and replay link.

Components

RunSummaryCard.tsx

Displays either closed-loop or open-loop metrics depending on run_type.

Closed-loop fields:

  • pass rate
  • safety
  • collisions
  • runtime

Open-loop fields:

  • samples
  • ADE
  • FDE
  • latency

ResetDashboardButton.tsx

Client-side dashboard reset control. It asks for confirmation, calls DELETE /runs through clearRuns(), shows how many runs were removed, and refreshes the server-rendered dashboard data.

MetricsChart.tsx

Uses Recharts to show run trends.

RegressionDiff.tsx

Displays the deltas object from regression.json.

ScenarioRunLauncher.tsx

Client-side run launcher used by /run-lab. It builds the launch request from selected scenario packs, local scenarios, agent, simulator, and regression gate settings.

ReplayViewer.tsx

Client-side replay controls:

  • play/pause button state
  • scrubber
  • frame stepping
  • event markers
  • failure deep-link target from ?t=<timestamp>&frame=<frame_index>
  • jump-to-failure button when a target frame can be resolved

EventTimeline.tsx

Readable scenario event list shown beside the replay viewer. It renders normalized timeline events from the replay API, including scenario start, goal reached, collision, near miss, offroad, hard brake, timeout, and assertion failure events. Clicking an event seeks the replay to the closest frame for the event timestamp.

ScenarioReplayPanel.tsx

Client wrapper for replay controls and timeline events. It can be used with controlled frame state so page-level replay surfaces stay in sync.

ScenarioReplayWorkspace.tsx

Coordinates the scenario detail replay area. It keeps ReplayViewer, EventTimeline, and TrajectoryMap on the same selected frame so scrubber movement, playback, event clicks, and the scene overlay stay in sync.

TrajectoryMap.tsx

Renders the replay scene from timeline frames, GeoJSON tracks, and normalized timeline events. The scene shows the driven route, remaining route, current ego pose, visible actors, event markers, route progress, clearance, and active-frame events.

FailureEditor.tsx

Client-side editor surface for tags, notes, lifecycle status, owner, fixed-in run, and regressed-in run. Saves use a single failure update request so related edits persist together. The returned failure payload refreshes the visible status history after a successful save. Replay links are built from replay_url when present, or from run_id, scenario_id, timestamp, and frame_index as a fallback.

Common Issues

Dashboard shows zeros

Usually the newest run is open-loop. Open-loop runs do not have closed-loop metrics, so the UI must display open-loop metrics instead.

This has been handled by run_type.

Dashboard shows no runs

Check:

http://127.0.0.1:8000/runs

If it returns [], restart the backend from the project root or set:

ROBOCI_RUNS_DIR=/absolute/path/to/RoboCI/runs roboci serve

If you just used Reset on the dashboard, [] is expected until you launch or create another run.

Regression diff does not update

The dashboard reads regression.json. Normal roboci run writes an empty gate result. Run roboci compare to populate metric deltas.