Skip to content

The Dashboard

Mehmet Nuraydın edited this page Jul 19, 2026 · 1 revision

The Dashboard

The CLI and hooks handle the agent side. But context is a shared layer, and the human needs a way to work with it too. Opening markdown files in a text editor works, but it is not the best experience for managing tasks, reviewing sleep state, or browsing features. The dashboard fills that gap.

dreamcontext dashboard starts a local HTTP server and opens a web UI. No external services, no accounts, no network calls. It reads and writes the same _dream_context/ files the CLI uses. There is no separate database.

What it provides

Kanban board. Tasks displayed as cards across columns (To Do, In Progress, Completed). Drag a card to change status. Multi-select filters for status, priority, urgency, tags, version, and — on a cloud backend — assignee. Type-ahead search for long lists. Sort by date, priority, urgency, or name. Group by status, priority, urgency, tags, or version, with optional sub-grouping. Click a task to open a Notion-style detail panel with properties block, rendered markdown body, changelog, a start/due date-range editor, the assignee picker, and any project-declared custom fields.

Sprint-aware version filter. Teams that use the version field as sprints get a dedicated filter that knows the difference between the current sprint (the active planning version), planning sprints, released sprints, and bare version strings that only ever existed as a task tag. Each row shows a status icon and task count; planning rows carry inline Set as current sprint and Mark sprint completed actions, and completed sprints collapse to the bottom with their release date. Setting a sprint "current" persists to state/.active-version.json so new tasks attach to it automatically — and a bare version string can be promoted to current in one tap (the dashboard lazily creates its planning entry). A released sprint can never be made current.

Project task overrides (Settings). A "Task Format & Custom Fields" panel renders the project's overrides/task.md: a raw-markdown editor for the task template, and a structured editor for the custom-field schema (add/remove fields with name, type, options, sync targets, and an agent prompt). Validation warnings surface inline. See Project task overrides below for the mechanism.

Cloud-token entry & provision preview (Settings). The remote backend's API token can be entered straight from the dashboard — written to the gitignored state/.secrets.json (never echoed back, never committed), with a masked status showing whether the token comes from secrets or the environment. Test Connection and Provision persist the token first, and provisioning runs as a two-step preview: a dry run reports exactly which custom fields/labels would be created versus already exist before you commit to creating them.

Eisenhower matrix. A 2x2 priority-urgency quadrant view (Do First, Schedule, Delegate, Don't Do). Automatically excludes completed tasks so the matrix focuses on actionable work. Toggle between Kanban, the matrix, and the time-axis views from the filter bar.

Time-axis task views. Beyond Kanban and the Eisenhower matrix, three views read the task date range directly. A Timeline (Gantt) lays tasks out as bars whose width and position come from their start_date/due_date, so sprint overlap and slippage are visible at a glance. A Calendar drops each task onto its due (and start) day for month-at-a-time planning. An Activity heatmap colors a date grid by how much task work landed on each day, surfacing bursts and gaps in delivery.

Core file editor. All core files listed in a sidebar. Markdown files open in a split-pane editor: textarea on the left, live preview on the right. JSON files display formatted. SQL files (like 5.data_structures.sql) render a visual ER diagram showing entities, fields, types, and foreign key relationships.

Knowledge manager. Search across knowledge files by name, description, and tags. Pin and unpin files directly. Clicking a file shows its full content.

Features viewer. All feature PRDs listed with status badges and tags. Click to see the full PRD: Why, User Stories, Acceptance Criteria, Constraints, Technical Details, Changelog.

Sleep tracker. A debt gauge with color coding by level (green through red). Session history timeline showing what happened in each session. A list of every dashboard change made since the last consolidation.

Brain graph. An interactive force-directed graph of your project's knowledge. Nodes are memory entries, knowledge docs, features, and core decisions; edges are explicit cross-references and inferred links from shared tags and slugs. A node drawer opens the full content inline when you click. A settings panel controls layout strength, link weighting, label visibility, and node-type filters. The graph view is what "scattered context" looks like when it's actually connected.

Council Hall. Every multi-persona debate rendered as a searchable card grid. Click a card to open a full-page detail view with a back button and three tabs:

  • Overview — the Problem (the question that triggered the debate) as the hero block, followed by the synthesized final report. Sections are extracted dynamically from the report's ## headings — the synthesizer is free to emit whatever sections fit the decision (Why, What was missing, Risks, Revision priorities, etc.) and they all render as cards. Citation chips in the format slug in RN jump to the matrix cell; inline **slug** mentions jump to the agent's full transcript.
  • Agents — a persona-centric, searchable, collapsible transcript. One block per persona with all rounds LIFO, plus the cross-context they received before each round and any research notes they persisted.
  • Matrix — persona × round grid. Each cell shows a compact position chip (GO / DEFER / HOLD / PIVOT, color-coded) plus the round's executive summary. Click a cell to expand it in place with the full Position / Reasoning / Reactions / Open questions sections. Peer reaction chips are clickable. Keyboard navigation: ← → across rounds, ↑ ↓ across personas, Esc to clear. Text filter dims non-matching cells.

Search & Ask (Sleepy). A dashboard-native retrieval surface with two tiers. The first is instant local search — the same BM25 ranker the CLI uses (src/server/routes/recall.tsbuildCorpus/bm25Search), served per-keystroke with an 8-second per-vault corpus cache so a burst of debounced queries reuses one disk scan. From those hits it can compose a zero-token "Ask" — an extractive answer stitched from the recall snippets, no model call. The second tier is Sleepy chat: a genuine, multi-turn Claude Code conversation that runs inside the active vault's project directory. The dashboard spawns a headless claude -p --output-format stream-json and streams its thinking, tool use, and answer back over Server-Sent Events (src/server/routes/sleepy-chat.ts); follow-up turns reuse the same session via --resume. Two model tiers are exposed as normal and intelligent with the real model names hidden. The session id and transcript persist server-side, per-vault, in the vault state dir — so a conversation survives the desktop app's per-launch loopback-port change (which wipes localStorage). The chat is read-only by construction — see Security model.

Saved views and board persistence. The board's configuration is no longer ephemeral browser state. Each saved view carries its own independently persisted filter / sort / grouping combination; a two-pane include/exclude filter, a Versions popover, default card Properties, and an at-risk alert round out the surface. Persistence is split deliberately (src/server/routes/board.ts, mirroring ui-settings.ts): "save for all" writes the version-controlled _dream_context/overrides/board.json (git-tracked, survives dreamcontext update, travels with the repo — the team's source of truth for what views exist), while "save for yourself" writes the git-ignored _dream_context/state/board.local.json (per-machine: personal view overrides, local-only views, and inherently per-machine UI state like the active view). The client merges them on read (local overrides win, local-only views are appended), and a SaveScopeDialog lets you pick the scope per edit. Both blobs are opaque to the server — it only stores/returns the client's JSON with a size cap, reading a missing/corrupt file back as {}.

Task Manager — a task's own Claude session. The task detail gets a third zone: a real Claude Code session pinned to that task, which maintains the document rather than building it. The design decision worth recording is why a terminal and not a bespoke chat UI: a Claude Code session asks interactive questions — permission prompts, plan approvals, "which did you mean?" — and a custom stream must reimplement every one or silently drop them, stranding you with an agent that stopped for a reason you can't see or answer. The terminal handles all of it for free and is the runtime we already ship.

The second decision is that the task page owns no session at all. AgentSurface is mounted once above the router and globally owns the things a session needs to be unique: the session-id counter, the per-vault roster (.agent-sessions.json), and the live-conversation set that stops two PTYs attaching to one transcript. A second instance would race the roster and could double-attach. So the task page renders an empty slot and the one global surface re-parents the terminal's DOM into it — the same raw-DOM hoist that already survives navigation — and portals the shared PaneComposer strip (files, skills, model/effort pickers, context/cost readout) into the pane's anchor with React's own portal mechanism. Navigate away mid-edit and the same live agent is still there. Bypass-permissions defaults on (its writes are scoped to the task in view; per-edit approval made every quick action a chore) — the same default Delegate carries, but for the opposite reason: Delegate runs unattended, so a prompt would stall it. The pane is opt-in everywhere — an agent session is a spend, and "open this task properly" must not imply "start an agent on it".

Anchored doc comments. Select a span of the rendered task document → a floating 💬 Comment button → a popover → pending chips → one Send delivers the whole batch to the task's Task Manager session as a single bracketed-paste message (multiline survives the readline). Two constraints shaped it: anchors are quotes, not offsets, because the agent rewrites the doc live and a position-pinned anchor would break mid-edit; and comments are ephemeral until sent — the agent's edits and the task changelog are the record, not a parallel comment store. Sending with the pane closed opens it and holds until the session's first ready/asking status (a booting readline drops early bytes). Alongside it, the doc refreshes live as the agent edits and a git-style "changes this session" line diff shows what moved.

Delegate to Claude from the board. A task card lives deep in the board's React tree while the session-spawning AgentSurface is mounted above the router — they can't share a ref, so the Delegate composer asks for an agent by dispatching a window event the surface listens for (the same decoupled bridge already used for dreamcontext-navigate / dreamcontext-zoom / the Sleep + brain-resolve agents). No prop threading across the tree. From the board the spawned session backgrounds to a corner chip; from a task's full-page view it reveals instead — that screen was the thing you were doing.

The dashboard's navigation is organized into four groups — Workspace, Memory, Brain, and Control Panel — so the surface scales as the brain grows rather than presenting one flat list.

Change tracking

This is the piece that ties the dashboard back to the agent. Every action taken through the dashboard (creating a task, editing a core file, pinning knowledge, updating a field) is recorded in .sleep.json as a dashboard_changes entry. Each entry captures the timestamp, entity type, action, target, and a human-readable summary.

When the agent starts its next session, the snapshot includes these changes. The RemSleep specialists read them during consolidation and fold the human's work into the project context. This closes the loop: the agent learns what you did between sessions without you telling it.

Field-level change tracking goes further. If you change a task's priority from "medium" to "high," the change record captures both the old and new values, not just "task updated." Net-change detection folds redundant changes: if you change priority from medium to high, then high to critical, only one record survives (medium to critical). If you change something and then change it back, the record is removed entirely. This keeps the change list clean for the agent to process.

Technical decisions

The server uses Node.js native http module with zero new runtime dependencies. Routes are thin wrappers around the same src/lib/ utilities the CLI uses. The React app (React 19 + Vite 6) is built separately and the output is copied to dist/dashboard/ during the build. React dependencies live in dashboard/package.json, isolated from the CLI's dependencies.

The design uses a custom CSS system with design tokens (violet brand gradient, HSL colors, 4px grid, light/dark mode with system preference detection) — the v0.10.0 violet rebrand also refreshed the logo and regenerated the Tauri app icon. No CSS framework. The Visby CF font is the brand font with a system font fallback for environments where it is not installed.

Version manager. A full-width modal showing planning and released versions with task counts and status badges. Planning versions have a "Release" button that transitions their status and sets the release date. Create new planning versions directly from the dashboard, and rename or delete any version in place — a rename re-points every task on that version and moves the active-sprint pointer (409 on a name collision); a delete warns and clears references off the affected tasks first. Both are backed by PATCH-rename / DELETE release routes. Versions and releases are unified in RELEASES.json (a version is a release entry with status: planning).

Security model

The dashboard is a local-only tool with no login — it reads and writes your _dream_context/ files (soul, tasks, sleep state) over unauthenticated routes. That is the right tradeoff only if the server is reachable by you and nothing else, so the server is locked down to enforce exactly that. These four mitigations were hardened before the first public release:

  • Loopback bind. server.listen binds to 127.0.0.1, not 0.0.0.0. The dashboard is reachable only from your own machine — never from other devices on the same Wi-Fi or LAN. (Binding to 0.0.0.0 would have exposed read/write access to your project files to anyone on the network.)
  • CSRF guard. Mutating routes (POST/PUT/PATCH/DELETE) check the request's Origin/Host and reject cross-site writes. Without this, any website you visit with the dashboard open could silently POST to localhost and edit your project.
  • CORS lockdown. No wildcard Access-Control-Allow-Origin: *. The server reflects only the loopback origin, so cross-origin reads are refused.
  • Path-traversal guard. Request-derived file paths (e.g. /api/core/:filename) pass through safeChildPath, which resolves the target and confirms it stays inside the context directory. ../ escape attempts are rejected before any file is touched.
  • Read-only Sleepy chat. The agent chat route (src/server/routes/sleepy-chat.ts) spawns a real claude -p inside your project but cannot mutate it, enforced three independent ways because a single flag isn't enough in headless mode where no human approves actions: (1) --permission-mode plan — the load-bearing guard — blocks every mutating/action tool, including connected MCP write tools we can't enumerate, while still allowing Read/Grep/Glob; (2) --disallowedTools strips the orchestration tools (Task/Skill/Agent/Cron*/Bash/Edit/Write…) so the chat can't fan out into sub-agents or be hijacked by the project's own SessionStart directives into running a maintenance/consolidation flow; (3) a guard system prompt tells Sleepy to ignore such directives and just answer.

The full threat model and the anti-regression invariants live in _dream_context/knowledge/dashboard-server-security.md, backed by tests/unit/server-security.test.ts.

Release management

Versions and releases live in a single RELEASES.json file. A "version" is a release entry with status: planning. When released, the status changes to released and the date is set automatically. This eliminates the need for a separate versioning system.

The core releases add command auto-discovers unreleased items when creating a release: completed tasks without a released_version, active features without a released_version, and changelog entries since the last release. In interactive mode, you select which items to include via checkboxes. In non-interactive mode (--yes), everything unreleased is included automatically. Use --status planning to create a version placeholder without auto-discovery, with empty task/feature/changelog arrays.

After recording a released entry, the command back-populates released_version on included features. This means the next release correctly excludes already-released items. core releases list and core releases show let you review release history.

The snapshot includes both "Upcoming Versions" (planning entries) and "Latest Release" (most recent released entry) so the agent always knows what is planned and what was last shipped. Tasks can be assigned to planning versions via the version field, and the sleep agent checks whether all tasks for a planning version are complete during consolidation. The active planning version — the "current sprint" that new tasks auto-attach to — is held in state/.active-version.json and re-validated against RELEASES.json on every read (a released or missing pointer auto-clears); the CLI exposes it as core releases active [version] [--clear].


Part of the dreamcontext deep dive — Home · README

Clone this wiki locally