Dashboard Tour
Status: Phase 1 (v0.1). One section per route. Boot the app with
docker compose up gateway dashboardthen openhttp://localhost:5173. Layout sketches are ASCII. Last verified againstdashboard/src/App.tsxon 2026-09-24.
Look and feel
The dashboard is the MetaForge engineering workspace: light by default,
with a dark theme. The theme control in the topbar offers Light,
Dark and System; the choice is stored in the browser under
metaforge.theme and applied before first paint, so there is no flash
of the wrong theme.
Every colour resolves through a CSS variable in
dashboard/src/styles/console.css: the dark palette is defined on
:root and remapped for light mode under [data-theme=light]. The
accent is #ff5a0a in both themes. Type is DM Sans, with Roboto Mono
for identifiers and code. The 3D and robot viewers keep literal
colours, because WebGL can't read CSS variables.
Layout shell
Every page except the full-width twin renders inside the same shell:
┌─────────── ───────────────────────────────────────────────────────┐
│ [≡] Section › Details project ▾ ☀ Sample 👤 ● Gateway │
├────┬─────────────────────────────────────────────────────────────┤
│ ◆ │ │
│ │ │
│ ▣ │ WORKSPACE: Projects · Agent sessions · Runs · Approvals │
│ ▣ │ │
│ ▣ │ ENGINEERING: Digital twin · Bill of materials · │
│ ▣ │ Files & artifacts · Knowledge · Compliance │
│ │ │
│ ⚙ │ Page content │
└────┴─────────────────────────────────────────────────────────────┘
- Nav rail. A floating rail with icon tooltips, in two groups (Workspace and Engineering), with Settings & connection and a Documentation link at the foot. The topbar toggle expands it to show labels and the full logo; the choice is remembered. Below 760px it becomes a drawer with a backdrop.
- Topbar. Breadcrumb (section, plus Details on a drill-in page),
the active-project switcher, the theme control, the Sample link
(opens the offline sample workspace; reads Exit sample while it
is open), an account link to
/settings, and the gateway chip, which reads Connecting, Connected or Unavailable fromGET /health. - A Skip to main content link is the first focusable element.
Sidebar entries map 1-to-1 to the routes below. The default landing
page is /projects; unknown paths show a Page not found screen.
/projects: project workspace
The home of the dashboard.
┌──────────────────────────────────────────────────────────────────┐
│ YOUR ENGINEERING WORKSPACE │
│ Projects. [+ New project]│
├───────────────┬───────────────┬───────────────┬──────────────────┤
│ Active │ Runs in │ Awaiting │ Work products │
│ projects 03 │ progress 01 │ review 02 │ 14 │
├───────────────┴───────────────┴───────────┬───┴──────────────────┤
│ Project library [▦ grid | ☰ list] │ Review queue │
│ [search…] [Recently updated] │ Recent artifacts │
│ All · Active · Draft · Archived │ The engineering loop │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
│ │ Drone FC │ │ Sensor │ │ … │ │ │
│ └──────────┘ └──────────┘ └──────────┘ │ │
└───────────────────────────────────────────┴──────────────────────┘
- Backed by:
GET /v1/projects,POST /v1/projects,GET /v1/runs,GET /health. - Metric cards link through to the library,
/runs,/approvalsand/files. Review queue lists runs waiting at an approval gate. - When the gateway can't be reached, a Connect your engineering
gateway notice offers Retry and Set up connection (
/settings) and the cards show a dash instead of zero.
/projects/:id: project detail
Drill-in for a single project: metrics, an action row (Start design run, Open twin & agent, Bill of materials, Baselines) and the project's artifact updates, newest first.
Current items (FORGE-526). By default the page lists one row per item
at its current revision, grouped by type (Parts, Assemblies, Requirements,
Intent, Needs, BOM, Components): its KEY@n, the gate and run that produced
it, its evidence state (current, out of date, none) and its validation
status. Older revisions are hidden; the panel header says how many. The
counts and the readiness percentage cover current items only, never every
node a run ever wrote. Decisions, simulation results and evidence stay
listed as records, and a result whose analysed revision is no longer current
moves to an Out of date group. Work products that are not items (a
pinmap, the PRD) are listed under Other work products. If the current
view cannot load, the page falls back to the flat work product list.
Everything else is opt-in:
- Working shows open runs' draft revisions, labelled DRAFT, under their item (and draft-only items), until a gate approves them.
- N revisions on a row opens the item's history: a timeline of revisions with status, run, gate, reason and the baselines that pin each. Tick two revisions to compare them: a 3D overlay with the older revision ghosted (parts and assemblies), volume, mass and bounding box deltas, the parameter table, requirement value changes, and the records still pinned to the older revision.
- Baselines lists the project's baselines (one per gate approval) and diffs any two, or one against the current items, item by item.
Each work product row has a Preview toggle (the eye icon). It opens a compact preview under the row, on demand: the same engine the twin inspector and the full-screen preview use (see Work product previews), loaded only when first opened.
- Backed by:
GET /v1/projects/{id},GET /v1/twin/current-view, plusGET /v1/twin/items/{key}/revisionsand/items/{key}/difffor history and compare,GET /v1/twin/baselinesand/baselines/diff, andGET /v1/twin/nodes/{id}and the twin file routes when a row preview is opened. - Use it to: find a
work_productUUID for the CLI's--work_productflag, rename or delete the project, or set it as the active project.
/sessions: agent sessions
Each row is one captured agent session: status, owning agent, project, last activity.
- Backed by:
GET /v1/sessions?project_id=…. - Use it to: find the session you want to dig into. Same data as
python -m cli.forge_cli status <id>but in a clickable list. - Internal workflow runs carry no project, so they drop out once one is selected. The page says "N internal workflow runs not shown: no project recorded" rather than just getting shorter — otherwise an empty page means both "nothing ran" and "everything that ran was internal", with no way to tell which.
- The agent roster is derived, not declared. It lists the agents that
have actually run a session in this project —
chat-harness,mcp,claude-codeand so on — one row each, showing the most recent session's status and how many that agent has run. It used to be a hardcoded array of four discipline agents with invented statuses, including a pulsing "running spec" that nothing had measured. Those four do not exist as running things:domain_agents/holds the disciplines as code invoked during a run, and nothing keeps a per-discipline process with a status to report. An empty roster says so rather than showing the four. - The pending-approval card is scoped too. It used to read proposals
unscoped while
/approvalsread the same hook scoped, so reading one project's sessions could surface — and let you approve — a proposal belonging to another.
/sessions/:id: session detail
Per-session timeline of thoughts, actions and decisions (see Session capture).
- Backed by:
GET /v1/sessions/{id}.
/runs: design runs
Every design-flow run for the active project, with search and a status filter, and a New design run action.
- Backed by:
GET /v1/runs?project_id=…. - Scoped since the page listed every project's runs in one table —
seven projects' work at once on the dev gateway — and a run's project
existed only inside its untyped
requestblob, so nothing on a row told them apart. Each run now carriesproject_idas a real field. - Runs with no project are reported, not hidden. Five of seventeen on the dev gateway carry none; with a project selected the page says "N runs not shown: no project recorded" rather than just getting shorter. Clear the active project to see them.
/runs/new: start a design run
A two-step wizard: Define the intent, then Review & launch.
┌───────────────────────────────────────────┬──────────────────────┐
│ What are you building? │ PLANNED LIFECYCLE │
│ Project [Choose a project ▾] │ Hardware & robotics │
│ Engineering intent │ 01 Intent │
│ [Design a compact actuator to lift …] │ 02 Stakeholder needs │
│ Engineering workflow │ 03 Requirements │
│ (•) Hardware & robotics │ … │
│ ( ) Mechanical design │ each: Human review │
│ [Cancel] [Review run →] │ gate │
└───────────────────────────────────────────┴──────────────────────┘
- Backed by:
GET /v1/projects, and on launchPOST /v1/runswith adesign_flowrequest (hardware_v1ormech_v1) andstart: true. On success it opens/runs/:id. - The lifecycle panel lists the selected flow's real phases from
orchestrator/design_flow/spec.py; every phase has a human review gate./runs/new?project=<id>preselects a project.
/runs/:id: run detail
One run's phases, gate decisions and outputs. A run that can't be loaded shows a Run could not be loaded state rather than not found. This run changed lists the item revisions the run produced, with what each gate did (approved into a baseline, waiting for the gate, or closed).
- Backed by:
GET /v1/runs/{id},GET /v1/twin/runs/{id}/changes.
/approvals: human review
One queue for everything that needs a human, rendered by a single card component: design-flow gates, flow proposals and versions, held tool calls, human-authority requests, design changes, design loops, sketches and drawings.
- Backed by:
GET /v1/approvals?status=pending|decided&project_id=…&kind=…andPOST /v1/approvals/{id}/decision. Every request carriesX-MetaForge-Surface: dashboardso the audit trail records where a decision came from. - Each card shows: title, kind, project, age and deadline, why the item was held, structured findings (errors, warnings and info are styled apart), and the kind's own detail: tool and arguments, the flow proposal's changes, the design change diff, or the gate's run, phase, attempt and retries left.
- Decisions are exactly what the gateway allows. The buttons are the
item's
allowed_decisions(approve, reject, retry, rework). A reason box appears for the decisions inreason_required_for, and rework asks for a phase from the item'srework_targets. When the item is not decidable the controls are disabled and show the gateway's reason. The dashboard no longer sends a hard-coded reviewer name; the gateway identifies the approver. - Audit tab: decided items with who decided, from which surface, on whose behalf, whether the approver was verified, when, and the outcome.
- Scoped to the active project. Items with no project are counted and reported, because an approval that quietly disappears is the one nobody answers.
- The run page uses the same card. The gate dialog on
/runs/{id}and the Approve and Reject buttons on the flow graph open thegate:<run id>approval in this card. - Use it to: same outcome as
python -m cli.forge_cli approve <id> --reason ….
/bom: bill of materials
Per-row sourcing data with part images, purchase and datasheet links,
and prices in their own currency. Exports to CSV. Each component that is an
item revision shows its @n (marked old when it is not current); clicking
it opens the item's history. The Requirements page's matrix does the same
for each requirement, with its constraint set's revision.
- Backed by:
GET /v1/bom,GET /v1/twin/revision-index. - Use it to: sanity-check supply-chain coverage before a fab release.
/twin: digital twin
The full-width twin workspace. A toolbar switches between Graph (work products and their relationships), Model (the React Three Fiber viewer for STEP, GLB, STL and 3MF geometry), Sim and Assembly. A status strip counts nodes needing attention and nodes without relationships, with a Start design run shortcut. Selecting a node opens the inspector (Overview, Constraints, History) and a conversation drawer for asking an agent about that node.
A node that is a revision of an item (FORGE-526) shows a revision
picker (KEY @n) in the inspector Overview: picking another revision
selects that revision's node, and the Model tab reloads it. The History
tab adds the item's revision timeline and the compare view described under
project detail.
An assembly cad_model (one with metadata.parts, FORGE-511) shows an
Assembly parts tree in the inspector Overview and on the Assembly tab:
each part with its material and dimensions (from its position box).
Selecting a part highlights it in the tree and in the 3D viewer. The
Explorer rail pins a Latest assembly shortcut for the project above the
node list. The node's assemblyParts field in GET /v1/twin/nodes carries
the list.
Selecting a simulation_result node adds an FEA result panel to the
inspector: max von Mises and max displacement with their units, the
load case that produced them, the mesh statistics and, when the run
stored its field (FORGE-532), the 3D contour viewer described under
/sim below. A result recorded before fields were persisted shows a
Field not stored note instead of a contour. The panel links to /sim,
which is where two results compare side by side.
The panel is not the toolbar's Sim button, which is an unrelated
robotics-physics preview (gravity, joint constraints, a Run/Stop toggle
gated on a robot_description node) and shares nothing with an FEA
result but the word.
- Backed by:
GET /v1/twin/nodes,GET /v1/twin/relationships,GET /v1/twin/nodes/{id}/model,GET /v1/twin/nodes/{id}/file,GET /v1/twin/nodes/{id}/versions, and the chat routes under/v1/chat. The FEA panel reads the node's ownmeshStatsplus the scalarmax_von_mises_mpa/max_displacement_mm/load_caseproperties, and its contour comes fromGET /v1/simulation/results/{id}/field;GET /v1/simulation/resultsbacks the/simlisting. - Sample workspace.
/twin?demo=1&node=sample-pcb(the topbar's Sample link) opens an illustrative drone flight-controller workspace that runs entirely in the browser with no gateway. It is labelled Sample data · resets on refresh and its assistant is scripted.
Work product previews
One registry, previewEngineFor in
dashboard/src/components/preview/registry.ts (FORGE-531), picks how a
work product is previewed. The twin inspector, the full-screen
Preview modal and the project page rows all render its answer through
the same PreviewHost, so the three surfaces always agree.
The work product type wins where it carries the meaning; otherwise the
file format decides (the format property, or the file_path
extension); otherwise a per-type default applies.
| Engine | Picked for | Renders |
|---|---|---|
| 3D CAD | .step/.stp/.iges/.brep/.fcstd, a cad_model with no mesh format, any assembly (metadata.parts) | The STEP to GLB converter route (GET /v1/twin/nodes/{id}/model) in the 3D viewer. In the modal an assembly also shows its part tree. |
| 3D mesh | .stl, .3mf, .glb, .gltf | The stored file loaded directly with three.js loaders, in the main viewer and the modal. STL carries no colour, so it renders in a uniform neutral. |
| Markdown | .md, and prd, documentation, test_plan and the other document types | Formatted Markdown (headings, lists, tables, code). Raw HTML in the source shows as text. |
| PRD | a prd revision (metadata.item_key set, FORGE-528) | The derived prd from GET /v1/twin/items/{KEY@n}/prd: the prose plus the project's current intent, needs and a live requirement table read from the current constraint set, with the prose and constraint set revisions shown above it. Falls back to the stored prose, with a note, if the view cannot be fetched. A legacy prd with no item renders as Markdown. |
| Requirements table | constraint_set | One row per constraint: severity, domain, acceptance criteria, verification, binding. Falls back to Markdown if the document is not in the recorder's shape. |
| BOM table | bom (.csv) | Line items and total quantity over the CSV table. |
| Decision card | design_decision | Title, rationale, alternatives with why each was rejected, status. |
| CSV table | .csv, .tsv | A table, capped with a "Show all" for long files. |
| JSON tree | .json | A collapsible tree. Invalid JSON is shown as text with a warning. |
| Code | .c, .h, .cpp, .py and other sources, firmware_source, cad_source_script | Highlighted source with line numbers. |
| KiCad viewer | .kicad_sch, .kicad_pcb, schematic, pcb_layout | Not available yet: no embeddable KiCad viewer ships as an npm package. Says so and offers the download. |
| Gerber renderer | .gbr, .gtl, .gbl and the other layer extensions, .drl | The layer drawn by tracespace. A zipped Gerber set is not unpacked. |
| DXF viewer | .dxf | Lines, polylines, arcs, circles, ellipses, splines and text drawn from dxf-parser. |
| FEA summary | simulation_result | The FEA result card described above, with the 3D field viewer (FORGE-532) in the inspector and the modal; project rows show the numbers only. |
| Image, PDF, HTML sketch | .png/.jpg/.svg/.webp, .pdf, .html | As before. |
| Robot viewer | robot_description | Unchanged: open it in the main 3D viewer. |
Anything else gets No preview engine for .ext files with a download link. The preview never shows a binary as text: only the text engines fetch the file body. The inspector panel leaves 3D, robot, PDF and HTML to the 3D viewer and the full-screen modal. The heavy engines (the 3D scene, the Gerber and DXF renderers, the Markdown and table renderers) load on first use, so the page bundles barely grow.
/sim: simulation
Load cases (reusable boundary conditions, with a 3D face picker for the fixed and loaded faces) and the project's FEA results.
Click a result's name to open it in 3D; the newest opens by default. The viewer colours the solved mesh by von Mises stress, displacement magnitude or temperature (pick one in the toolbar), with a legend showing the full-field minimum, maximum and peak. The Deform slider scales the displacement so the deflected shape is visible; it starts at a scale that makes the largest deflection about 5% of the part size. Fixtures show as blue boxes and loads as orange arrows (both also listed in the legend in words), the peak as a white dot, and hovering the mesh reads out the value under the cursor. A Simplified from N triangles note appears when the stored surface was decimated to fit its size cap.
Tick two results to compare them: the numeric deltas, then the two fields side by side with one camera (orbit either and both follow), one quantity and one colour scale spanning both, so equal colours mean equal values. A result that recorded a mesh-convergence sweep also shows a chart of peak stress against element count with the Converged or Not converged verdict. A result recorded without a field keeps its numbers and says Field not stored.
- Backed by:
GET/POST /v1/simulation/load-cases,POST /v1/simulation/named-faces,GET /v1/simulation/results,GET /v1/simulation/results/{id}/field. See Simulation results in 3D.
/files: files & artifacts
Every stored artifact, with download and file-link sync.
- Backed by:
GET /v1/twin/linksand the twin file routes.
/knowledge: ingested sources
The L1 knowledge corpus as a sortable, filterable table.
- Backed by:
GET /v1/knowledge/sources(the same data themetaforge://knowledge/sourcesMCP resource exposes). - Use it to: see what's in the KB, filter by
knowledge_typeor project, click a row to drill in. - Empty state: points you at
forge ingest <path>; seecli-reference.md.
/knowledge/sources/:id: source detail
Drill-in for one source. The CLI's sources show command is the
fuller view for now (see cli-reference.md).
/compliance: compliance
Regulatory coverage for the active project.
- Backed by:
GET /v1/compliance/{project_id}/checklistand/coverage, plus evidence under/v1/compliance/{project_id}/evidence.
/settings: settings & connection
Reached from Settings & connection at the foot of the nav rail, or the account icon in the topbar.
Gateway. Which gateway the dashboard sends its API calls to, as an
address and a port, with a Test connection button that probes
GET /health before you commit to the value. Leaving both fields empty
keeps the default behaviour: same-origin relative paths, which the Vite
dev server and the Docker image's nginx both proxy to the gateway
themselves. Setting an address is what lets a separately hosted
dashboard (on Vercel, say) reach a gateway running on your own machine.
See Vercel deployment for that setup.
The value is held in localStorage, so it is per-browser and never
leaves the machine. Saving one clears the React Query cache, since
everything already fetched came from the previous gateway. The page
warns when the combination will be blocked as mixed content (an HTTPS
dashboard calling an HTTP gateway).
Bring your own API key. Pick a provider, paste a key and choose the
active model. Keys go straight to the gateway
(POST /v1/harness/credentials, PUT /v1/harness/selection) and are
never stored in the browser. If the gateway sets
METAFORGE_HARNESS_ADMIN_TOKEN, enter it in the Admin token field;
it is sent with the request and not saved.
Before you expose a gateway. The gateway ships no authentication on its data routes; the page explains how to put it behind a private network or an authenticating proxy.
What's not on the dashboard yet
- No dedicated MCP-tool runner UI. Tool calls land via the CLI, Claude Code, or Codex; the dashboard shows their results (sessions, proposals, BOM updates) and asks for tool approvals.
- No sign-in. The gateway has no user accounts; access control is whatever network or proxy sits in front of it.
Boot recipes
| Goal | Command |
|---|---|
| Dev mode (hot reload) | docker compose up gateway dashboard-dev |
| Prod build (static) | cd dashboard && npm run build && npm run preview |
| Just the gateway, drive UI elsewhere | docker compose up gateway then point dashboard at it |