ADR 0001: Replace the dashboard with a workbench interface¶
- Status: accepted, 2026-09-29
- Spec:
workflow/interface-spec.md - Decision record:
workflow/interface-redesign.md(D1–D18, with the reasoning and the facts verified along the way) - Supersedes: the page layout in
workflow/dashboard-ux-plan.md. That plan's data, privacy and write-gate decisions (P1–P6, A1–A5, T1–T3) still apply.
Context¶
The dashboard at scripts/dashboard_static/ is an admin panel. Its navigation
mirrors the back-end modules (Capture, Work, Search, Library, Operate), and each
page is a form around one endpoint. It configures the tools that do the work
elsewhere, in Claude Code and Cursor, instead of being a place to do the work.
No model can be reached from it. Status words disagree between pages: an
automation can read "healthy", "never run" and "not installed" at once.
The back end is sound: a standard-library HTTP server with about 40 JSON endpoints backed by tested modules, none of which depend on the page layout.
Much of the work this tool supports is authoring and adapting tools (skills, patterns, prompts), sometimes inside a project and often not. There was no place to test a tool across models, or across variations of itself.
Decision¶
Replace the front end with a workbench: a place to work in, open all day.
- Shell. An icon rail of surfaces on the left, a project selector at the top that sets context for every surface (like the Google Cloud console), one tab per surface, a status bar, a ⌘K command bar, and an Ask side panel (⌘J). The selected project is in the URL; "All projects" means both "show everything" and "no project".
- Surfaces. Today (a briefing with actions on every row), Projects (a hub per project), Lab (test a tool as variants × models × inputs), Library (reading material and tools in one place), Agents (traces, costs and a budget, automations), Settings (the rarely visited admin).
- Models run only on explicit action, and every call shows its model, route and data classification before it is sent. Nothing is generated in the background.
- Agents run as headless Claude Code (
claude -p), so hooks, including the AnyType write gate, apply unchanged. The Lab also has a prompt-only mode for quick comparisons across providers. Lab runs get stand-in tools instead of real write tools. - A routing policy in
tool-chest.toml, enforced on the server:private_sensitivedata only ever goes to local models (Ollama),internalgoes to local models or a named list of cloud routes, and a per-machine file can only narrow the policy. - Tracing through OpenTelemetry, with no vendor coupling. Everything emits OTLP to one configured endpoint (Phoenix, locally, to start). The Workbench draws Traces and Costs from its own run index, and links to the tracer only for span detail.
- Front end: Preact + htm, copied into the repo, no build step. One accepted exception: CodeMirror 6, bundled once for the Lab editor. New structured state goes in SQLite under the data root.
- Rollout: build alongside, then switch all at once. The new interface
grows at
/nextin vertical slices while the old dashboard is frozen; a security epic and red-team session must finish before the first slice that launches agents.
Consequences¶
- The server becomes able to start agent sessions. That makes it a
code-execution surface for any web page that can reach it, so the existing
Host,Originand per-launch token checks become acceptance criteria with tests on every agent-launching endpoint, and the security epic gates slice 5. - New private state (experiments, runs, conversations, budget) lives in
data_root/tool-chest.db. Each machine running tool-chest is its own island; nothing syncs between them. - The project and the classification set by it now decide where data may be sent, so a mistake in the policy is a data leak. The policy has known-bad tests, and it can only be narrowed per machine.
- A tracing vendor can disappear without breaking the Workbench; only the detailed span view is lost until the endpoint is repointed.
- Two small dependencies enter the repo as committed single files: Preact + htm, and a CodeMirror bundle. Browser tests use Playwright for Python, so no Node toolchain is required to run or test the interface.
- Open issues that add UI to the old dashboard are paused (
needs-info) and their back-end parts move into the matching slice.
Alternatives considered¶
| Question | Rejected | Why |
|---|---|---|
| Overall shape | A calm single-column "desk"; a command-line-first shell | The desk hides utility behind a calm layout; command-first loses at-a-glance state. The desk's reading order survives as Today's briefing. |
| Models in the UI | None (a model-free control panel); ambient generation | The Lab needs models. Output nobody asked for turns an all-day workspace into noise. |
| Hub | Project-first (IDE-style); object-first (database views) | Tool work would have to pose as a project; every screen would become a query. |
| Running as | A native wrapper now | A web app installed from the browser costs nothing now and leaves room for one. |
| Lab side effects | Refusing writes; a throwaway AnyType space | Refusal makes runs unrealistic after the first write. The throwaway space is kept as the fallback if stand-in tools prove too limited. |
| Front end | Plain JS; a framework with a build step | Plain JS can't manage a UI this size; a build step and node_modules add supply-chain surface for little gain here. |
| Tracing | LangSmith; Langfuse now; a tool-chest adapter layer | LangSmith self-hosting needs an enterprise plan. Langfuse needs six services, too heavy for the current laptop. An adapter layer is machinery OpenTelemetry already provides. |
| Rollout | Replacing pages one at a time inside the new shell | Weeks of two design languages on one screen, for no saving: both UIs call the same API. |