The dashboard

The dashboard is a page on your own machine where you watch the studio work and steer it. Every desk is a small bot at its own desk. A bot wakes up while its desk is working and goes back to sleep when it is done. The job moves along a track from the brief to the sign-off, and the review board shows every rough, the pick and each round's score.

The clip above is a real job from this repo, the launch card in clips/fixtures/card, replayed and sped up. Fifty seconds, with captions.

Open it

In Claude Code, once per project:

/desk:dashboard setup
/desk:dashboard start

start prints a link like http://127.0.0.1:7433/?token=.... Open it in your browser. The token changes on every start, and /desk:dashboard status prints the link again. stop ends the server and disable turns the dashboard off for the project.

Outside Claude Code (Codex, Cursor, Gemini CLI, Copilot, Windsurf, OpenCode or a plain terminal), the same commands come with the npm package:

npx --package deskstudio desk-dashboard setup
npx --package deskstudio desk-dashboard start

Add --project DIR for another folder and --port N for another port. The page only listens on 127.0.0.1 and every request needs the token.

Then start a job the usual way, /desk:design a launch card for LinkedIn in Claude Code, or ask your agent in plain words. The dashboard picks the job up by itself, there is nothing to refresh.

The dashboard before any job
The dashboard before any job

What you see

The job

The top of the page is the job you picked in the Job menu: the command and the deliverable, the job id, and a track with seven stations: Brief, Roughs, Pick, Polish, Proof, Sign-off, Done. The ticket sits on the station the job is at, and says which round it is on. Next to it are four numbers: the round out of the most allowed, the latest score, the cost so far and how long the job has been running.

The job: the track and the four numbers
The job: the track and the four numbers

Pause at next step holds the job before its next step. Stop job ends it at the next step. Nothing interrupts a desk in the middle of a step.

The floor

The 42 desks in four rooms, one per tier: the Front desk (the Design Manager), the Directors' office, the Senior studio and the Workshop, plus the Proof booth.

The floor of a brand-kit job at its busiest
The floor of a brand-kit job at its busiest

Click any desk to open its panel: what it owns, the model it is on, what it has done on this job, a model picker, and a box for a note. A note goes into that desk's next prompt on this job. The event log only records that a note was left and how long it was.

A desk's panel: model and note
A desk's panel: model and note

At work now and the studio log

On the right, At work now lists every desk that is working, with its task, how long it has been at it and how many tools it has used. Under it, the studio log says what happened in plain words: a desk started or finished and what it cost, the job moved on, a round was scored, you paused or picked. Tick tool calls to see every tool call too.

At work now and the studio log
At work now and the studio log

The review board

The roughs are pinned on the left. The picked one gets a hand-drawn ring, with the Art Director's reason under it. Pick this on another rough picks it yourself: it overrides the Art Director at the pick step and is recorded as your call.

Roughs and the pick
Roughs and the pick

On the right, one tab per polish round with its average and its proof result, a line of the averages against the bar, and the selected round: its render, the proof result, and the Creative Director's eight scores. Click a tab to see an older round.

Rounds and the scorecard
Rounds and the scorecard

Roles and cost

Which model each tier is on, the cost by tier at list price, a bar of the three, the tokens, the cost per desk, and who did what: every desk run on a time line, so you can see what ran side by side. The brief is folded at the bottom.

Roles, cost and who did what
Roles, cost and who did what

Studio memory

The brand the desks read first (.desk/DESIGN.md: name, colours, type), .desk/project.json and .desk/taste.md when there is one.

Follow a running job

  1. Keep the page open while the job runs. A green Live at the top right means it is connected. If it says Offline, the server stopped: run /desk:dashboard start again and open the new link.
  2. Watch the ticket move along the track and the bots wake up. At work now says who is on what right now.
  3. When the roughs land, check the board. Pick one yourself if you disagree with the Art Director, before the pick step ends.
  4. During polish, each round adds a tab with its score. A round under the bar goes round again on its own.
  5. When the job needs you, an orange banner says so at the top: Approve keeps it as it is, recorded as your call, and Send back opens a box for what to change.
A job waiting on you (a demo state, the replayed job never asked)
A job waiting on you (a demo state, the replayed job never asked)

Read the result

The light scheme
The light scheme

The Light and Dark button switches the scheme. That choice is the only thing the page keeps in your browser.

What each control does

Every control is a write to a file the Design Manager reads before its next step. Nothing interrupts a desk mid-step. In native mode a running subagent cannot be stopped partway anyway, so pause means "hold at the next step" everywhere, and the button says so.

ControlWhereWhat it writes
Pause at next step, Continuethe jobcontrol.json paused: true or false
Stop jobthe jobcontrol.json stop: true
Change model, "This job"a room sign, a desk's panel, or Roles and costcontrol.json models.<tier> or models.desks.<desk>
Change model, "Save as default"the samethe above, and models in .desk/project.json
Use the tier modela desk with its own modelremoves models.desks.<desk> from control.json
Send notea desk's panelappends to inbox/<desk>.md, which the Manager puts in that desk's next prompt and then deletes
Pick thisa roughcontrol.json pick: N, which overrides the Art Director and is recorded as your call
Approvethe bannercontrol.json approve: { at }
Send backthe bannercontrol.json sendBack: { notes, at }

Every control also appends one line to .desk/events.jsonl, type control.<kind>, with the job id and names or numbers: paused, the model names, the rough number, the desk a note went to and how long it was. Never the note itself.

Model names are provider/model, for example anthropic/sonnet or openai/nano. The picker lists the ones the project already uses plus the common ones, and takes any other name typed in.

Where state lives

PathWhat
.desk/dashboard.jsonenabled and port, written by setup
.desk/dashboard.runtime.jsonpid, port and token of the running server, removed on stop
.desk/dashboard.logstart, stop, controls and errors, one line each
.desk/events.jsonlthe event log the dashboard tails
.desk/jobs/<id>/state.jsonwhat the job, the floor and the review board show, kept by the Manager
.desk/jobs/<id>/control.jsonyour controls, merged key by key, read by the Manager
.desk/jobs/<id>/inbox/<desk>.mdyour notes to a desk
.desk/jobs/<id>/renders/rough-N/, round-N/the screenshots the board shows
.desk/out/<id>/the files the desks made

Routes

All routes need the token, in the URL as ?token= or in the header X-Desk-Token.

The screenshots and clips

The pictures on this page come from node clips/shots.mjs, and the clips on the site from node clips/frames.mjs --all. Both replay real job records from this repo (clips/fixtures/ and evals/golden/) through the same files the Manager writes, on the real dashboard. The clips run the page on a virtual clock and take one screenshot per frame, so they are 60 frames a second.

Tests

node --test "dashboard/test/*.test.mjs"