§0Positioning

Reasonix's dashboard is the rich-medium companion to the TUI — not a mirror, not a replacement. It does what a 13-row terminal pane cannot: long-form reading, real charts, multi-file editing, large-table inventory browsing. The TUI keeps the things terminals are good at — instant feedback, slash commands, typing-loop latency.

Why not mirror the TUI? Slavishly recreating the terminal in a browser produces an unusable portfolio gimmick. Charts, hover tooltips, drag, and dense tables are web-native; pretending otherwise wastes the medium.

Why not replace the TUI? Web input + AI streaming has higher latency than a raw stdin keystroke loop. The TUI wins on responsiveness and stays the primary surface; the dashboard is opened in a second tab when you want to read, look, or configure.

§1Tokens

Same core palette as the TUI mockup so that switching between TUI and dashboard feels like one product. Slightly higher chroma allowed for chart series.

Surfaces

--bg#0a0c10
--bg-elev#11141a
--bg-elev-2#161a22
--bg-input#0d1015
--bg-code#06080c
--bg-hover#1a1f29

Text

--fg-0 primary#e6edf3
--fg-1 body#c9d1d9
--fg-2 secondary#8b949e
--fg-3 dim#6e7681
--fg-4 separator#484f58

Accents role-coded — same meanings as TUI

--c-brand skyin-progress, links
--c-accent purplereasoning, plan
--c-violetsub-agent
--c-ok greensuccess
--c-warn amberapproval, warning
--c-err coralerror

Chart spectrum six-stop series — distinguishes without shouting

s1 skyprimary
s2 tealsecondary
s3 minttertiary
s4 amberquaternary
s5 coralaccent / negative
s6 purplemodel boundary

Type

Sans-serif (Inter) for prose; monospace (JetBrains Mono) for code, data, file paths, counts, glyphs, and section labels. Smaller text steps below 12px stay monospace — readability holds better at small sizes than narrow sans.

28 / 700Headline · 28px
22 / 700 monoSection title · 22px
14 / 400Body — default reading size for prose. 14px Inter at 1.55 line-height.
12.5 / 400 monoCode / data — JetBrains Mono
11 / 600 monoSECTION LABEL · 11PX UPPERCASE

Glyphs single-char icons reused from the TUI

brand
chat
edit
plan
sessions
$usage
tools
permissions
+system
semantic
Mmcp
Sskills
·memory
Hhooks
settings
streaming
reload
delta-up
delta-down
status-dot

§2Shell

The frame: sidebar, top context bar, body, status row. Sidebar collapses to icon-only at narrow widths or on user toggle (state persisted). Top bar carries the high-frequency context — workspace path, session, model, cost — so panel content can be uncluttered.

— Default: sidebar expanded, Chat panel active

~/work/reasonix feat/dashboard-v2 2026-04-30-2014
TUI · #2 modeldeepseek-chat balance¥48.20 turn12
— panel content slot —
tools 23 mcp 2 1 deferred last event 12s ago

— Sidebar collapsed (icon-only)

Why a left sidebar instead of top tabs? 14 panels won't fit horizontally. Vertical also lets us section them (workspace · observe · configure) so muscle memory builds. Collapse-to-icons keeps the option of tight-vertical dashboards (laptop) without losing the layout.

§3Components

Building blocks every panel composes. Sharp corners and 1px hairlines inherited from the TUI; web affordances (hover, focus rings, real form controls) are added rather than emulated.

Cards

Every panel is a stack or grid of cards. The 2px left border encodes role: brand for in-progress, accent for plan/reasoning, warn for approval, err for failures.

streaming · assistant2.3s · 1.2k tok
Looking up the exit code Windows uses when SIGTERM is delivered to a console subsystem process…
plan · awaiting approval5 steps
Refactor session sidecar lifecycle so .events.jsonl rename/delete tracks the parent.
shell · awaiting approvaldeepseek
npm publish
tool error · run_commandexit 1
Cannot publish over the previously published versions: 0.18.0.

Pills

Status chips. Always uppercase mono, always small. Use sparingly — too many pills in one row turns into noise.

● ok ▲ warn ✕ error ⏵ active ⊞ plan idle passed 1665 deprecated retry 3/3

Tables

Dense by default. Numeric columns are tabular-nums and right-aligned. Path / id columns get monospace. Header is uppercase 10.5px to keep the eye on the data.

ToolSourcelast callcallsavg ms
read_filenative · fssrc/cli/ui/App.tsx1428
edit_filenative · fssrc/cli/ui/PromptInput.tsx3814
run_commandnative · shellnpm run verify1123,400
grep_filesnative · fs"workspace" src/942
github__get_prmcp · githubesengine/reasonix#134280

Toasts

Top-right stack, auto-dismiss in 3s. Border-left encodes kind. One-line by default; expandable for tracebacks.

Published reasonix@0.18.1 to npm
×
3 events forwarded to events.jsonl
×
0.18.0 has a deprecation notice — surface to users on launch?
×
Failed to load skill @reasonix/python-runner — ENOENT
×

Code blocks

Kept close to the TUI's terminal feel — slightly darker than the panel surface, monospace, no ligatures-from-noise. Inline highlighting reuses accent colors.

1export function listSessionsForWorkspace(workspace: string): SessionInfo[] { 2 // Strict match — legacy untagged sessions are hidden; 3 // resume by name still works. 4 return listSessions().filter((s) => s.meta.workspace === workspace); 5}

Diff view

Unified by default; side-by-side toggle lives in the §7 Edit review panel. Add/remove rows tinted ~6% opacity over the code surface; syntax highlighting reuses the .kw / .str / .com tokens from the code block, so the diff blends with surrounding code visually. Word-level intra-line diff via .word-add / .word-rem highlights only the bytes that actually changed.

src/cli/commands/chat.tsx+1 · -2
@@@@ -346,8 +346,7 @@ export async function chatCommand
346346 session={resolvedSession}
347347 />,
348348 // patchConsole:false — winpty/MINTTY redraw-glitch source.
349 // debug:true forces full-frame writes; log-update's diff drops frames…
350 { exitOnCtrlC: true, patchConsole: false, debug: true },
349 { exitOnCtrlC: true, patchConsole: false },
351350 );

Charts

Title in 11px uppercase mono · current value in 22px mono · sparkline below. Hover drives a tooltip with the date and exact value (handled by the chart lib at impl time, not in the mockup). Series follow the spectrum tokens.

cost · 7 day▲ 12%
¥18.40/day
tokens in · 7 day▼ 4%
142k/day
latency p95— flat
2.4s

Progress replaces every default browser bar

The current dashboard leans on <progress> default styling — chrome-grey trough, OS-tinted fill, no role coding. Replace with a single .progress primitive: 6px tall, 3px thin variant, 10px thick variant, role tints (ok / warn / err / acc). Always paired with a tabular-nums numeric label. Indeterminate is a shimmer slice, not a spinning circle.

linear · with caption
turn iters
3 / 10
budget
¥78 / 100
over cap
103%
cache hit
94%
reasoning
streaming
indeterminate · for unknown duration
npm install

A 30%-wide slice slides left-to-right on a 1.4s loop. No spinner — spinners read as "tab is busy"; a sliding bar reads as "this specific task is in flight."

thin · inline beside text
verify
1665 / 1665
segmented · breakdown of one whole

For ratios where each slice has its own meaning. Cache-hit / cache-miss is the canonical case.

cache · 7d
100%
● hit · 74% ● miss · 18% ● error · 8%
step · plan / wizard progress
1
2
3
4
5
planreviewapproveexecutecommit
ring · for KPIs that compress to a single number
94%cache
3/10iters
78%budget

Form controls

Monospace inputs; the focus ring is a 1px brand-color border, no glow. Labels in 10.5px uppercase mono so they sit visually as "field tags" rather than competing with the input itself.

Soft cap; warn at 80%, refuse new turn at 100%.
Enable plan-then-edit flow
Auto-launch dashboard on reasonix code
Use streaming for sub-agents

§4Chat

A first-class chat surface, not a viewer. Full composer, slash menu, file attachments, paste handling. The dashboard wins anywhere the TUI's renderer breaks down — older PowerShell, non-ConPTY consoles, mosh-over-flaky-network, or terminals where Ink redraws the same row twice. A small status pill in the topbar tells you which surface the loop currently considers "active writer."

Why does the dashboard need its own chat? The TUI assumes a modern terminal — true cursor reporting, ConPTY, raw stdin. On legacy PowerShell hosts (Win10 cmd, ConEmu, very-old WT builds) Ink's diff-based renderer can re-paint the same card row, leak ANSI sequences, or drop frames mid-stream. The dashboard's chat is HTML — it can't have those bugs. Treating it as fallback-only means users hit the bugs first and only then discover the workaround. Better: full peer.

Single-writer is still enforced: only one of {TUI, dashboard} owns the input lock at a time. The pill says which. Switching is one click; re-entering the TUI on first keystroke is automatic.

— TUI online, dashboard reading; user can submit from either

~/work/reasonix feat/dashboard-v2 2026-04-30-2014
TUI · terminal #2 balance¥48.20
user20:12:33
npm publish 报 0.18.0 已存在,但我没看到之前 publish 成功,能不能查一下 registry?
reasoning3 paragraphs · 240 tok
The user reports a publish conflict. Root-cause direction: did the earlier rejected tool actually upload before the rejection signal landed? Plan: query the registry for 0.18.0 timestamp + gitHead, compare against local commit history…
run_command240ms · ok
npm view reasonix@0.18.0 time gitHead
{"0.18.0":"2026-05-01T03:20:58.310Z"}
gitHead = 'f8e156c898…'
assistantstreaming · 1.2k tok

0.18.0 was published at 03:20:58Z, with gitHead=f8e156c — the local "release: 0.18.0" commit from before the bug fix. So the rejected tool actually uploaded to the registry; the rejection signal stopped the local CLI before the confirmation print.

Next step: bump to 0.18.1 with the fix, then deprecate 0.18.0 on the registry…

slash commands
//plandraft a step-by-step plan
//budgetset or clear the cost cap
//sessionsswitch / rename / forget
//cwddeprecated
//diffunsubmitted edits since last turn
@ src/cli/ui/PromptInput.tsx× [paste · 248 lines]×
/p
send · ⇧↵ newline · ⌘K commands · @ attach 3.4k tok · ¥0.21 est send →
tools 23 mcp 2 last event 2s ago

— TUI offline (renderer hung); dashboard auto-promoted to active writer

~/work/reasonix 2026-04-30-2014
TUI offline · 14s ¥48.20
TUI hasn't drained its event queue in 14 seconds — likely a renderer hang. Dashboard now owns input. force-quit TUI · reattach
assistantstreaming continues here
…the deprecate command will mark 0.18.0 with the warning text on the registry. Once it's done, anyone who runs npm install reasonix@0.18.0 will see the deprecation banner and get pointed at 0.18.1.

Composer states how the input bar reads in different conditions

One composer, four states. Border + foot copy carry the difference; geometry stays put so the eye doesn't reorient.

idle
type a message · slash for commands · at-sign for files
send · ⌘K commands0 tok
composing · with attachments
@ src/cli/ui/App.tsx× @ src/cli/ui/PromptInput.tsx× [paste · 84 lines]×
find every place we still pass debug:true to ink and replace with the default
send · ⇧↵ newline1.2k tok · ¥0.07 estsend →
disabled · model is responding
…waiting for response · esc to abort
streaming · 240 tok so farelapsed 2.1s
locked · TUI owns input
TUI · terminal #2 has the input lock. take over here
switching is one click; releasing back to TUI is automatic on focus

Approval modal tool-call confirmations mirror from the loop

When the model wants to run a non-allowlisted command, both the TUI and the dashboard show the same approval. Either side can resolve. The dashboard frames it as a centered dialog (more body, can show full diff/output preview), the TUI shows it inline as a card. Same dispatch path either way.

approve · run_commanddeepseek · turn 14

The model wants to run a command that is not on the auto-approve allowlist:

npm publish
cwd: ~/work/reasonix
prefix used by allowlist match: npm
y approve · a always for prefix · n deny

Command palette Ctrl/⌘+K opens a global jump bar

Slash commands, panels, sessions, even MCP tools — all addressable through one fuzzy search. The popover from inside the composer is the same component, just anchored differently and pre-filtered to slash commands. Avoids the dashboard ever needing menus.

esc
slash commands
//deprecatemark a published version as deprecated
panels
Toolsbrowse registered tools
Permissionsedit allowlist
recent sessions
2026-04-30-1908tui-card-stream redesign
2026-04-29-1602v0.14 event-log kernel

§5Overview

The cockpit. A four-column widget grid that answers "what's the system doing right now, what did it just do, what should I worry about" in one screen. Every widget is a link into the corresponding panel for depth.

~/work/reasonix feat/dashboard-v2 2026-04-30-2014
TUI · #2 modeldeepseek-chat balance¥48.20
balance
¥48.20
▼ ¥1.84 today
tokens · 7d
994k
▲ 12% vs prior
cache hit
94%
— stable
tool calls · 24h
412
▲ 38
current sessionopen in chat →
2026-04-30-2014started 19:08 · 12 turns
Investigating npm publish conflict; deprecating 0.18.0 and shipping 0.18.1 with the ghost-frame fix.
prompt tok
42,318
completion tok
8,041
cost
¥1.84
avg latency
2.1s
cost · 14 day▲ 12%
¥18.40/day avg
recent planssee all →
finalize card-stream migration · 4 steps2h ago
events.jsonl sidecar lifecycle · 3 steps1h ago
release 0.18.1 + deprecate 0.18.0 · 5 steps · 3/5now
dashboard redesign · draftedqueued
tool activity · last hourfull feed →
run_command npm publish02:31
run_command git push --follow-tags02:31
run_command npm publish (rejected)02:30
edit_file src/cli/commands/chat.tsx02:28
run_command npm run verify02:25
run_command npm publish (over taken)02:22
tools loaded
23/24
native 14 · mcp 9
mcp servers
2/2
all up
memory entries
14
+1 this session
version
0.18.1
latest
tools 23 mcp 2 last event 2s ago

Layout principles

Top row: 4 KPIs (balance · token volume · cache hit · tool calls) — the four numbers you check first when picking up an in-progress agent. Wider middle: current session + cost trend, side by side. Lower middle: plan history + tool feed — the "what's been happening" pair. Bottom KPIs: configuration health (tools / MCP / memory / version).

Every widget is a link into the corresponding panel. Hover reveals "open" affordance; click opens the deeper view.

§6Sessions

The high-traffic browse view. List on the left (filter, sort, search), detail on the right. Designed so you can land here a week later, find the session you half-remember, and either resume it, copy a prompt out, or delete the whole branch of dead-end work.

Why list+detail and not a card grid? Sessions have a strong temporal axis (you almost always want "what did I do today" or "what was that thing last week"). A vertical list with date affordances beats a card grid for that. The detail pane on the right gives room for the transcript preview + plan history + cost breakdown that you actually came here for.
~/work/reasonix sessions 2026-04-30-2014
total42 sessions disk128 MB balance¥48.20
2026-04-30-2014 active Investigating npm publish conflict; deprecating 0.18.0… 12 turns · ¥1.84 · 1h ago
2026-04-30-1908 tui-card-stream redesign; finalize migration + drop workspace tool 38 turns · ¥4.20 · today
2026-04-29-1602 v0.14 event-log kernel — approach D; reducer + sidecar 52 turns · ¥6.10 · yesterday
2026-04-28-2244 0.12.16 → 0.12.22 perf + budget + doctor + commit 71 turns · ¥8.94 · 2d ago
2026-04-28-1130 dashboard sidebar Editor tab — file tree + CodeMirror 45 turns · ¥5.30 · 2d ago
2026-04-27-1922 stale scrollback redraw fix — still broken on Win10 cmd 8 turns · ¥0.42 · 3d ago
2026-04-26-1015 semantic index v2; chunk by logical block instead of LOC 22 turns · ¥2.10 · 4d ago
2026-04-25-2030 memory system spec — auto + manual; types: user/feedback/project/reference 31 turns · ¥3.20 · 5d ago
2026-04-30-2014 ~/work/reasonix · feat/dashboard-v2
turns
12
prompt tok
42,318
cost
¥1.84
cache hit
94%

Activity · last 4h

● tools ● assistant ● reasoning ● errors ● idle

Recent turns

12 › /deprecate reasonix@0.18.0
11 › 没问题,开始 npm publish
10 › 可以的,按推荐路径
9 › 帮我查一下 0.18.0 是怎么发出去的
8 › publish 居然成功了?我以为我拒绝了

Plans in this session

release 0.18.1 + deprecate 0.18.05 / 5
dashboard redesign · draftedin progress
tools 23 mcp 2 42 sessions · 128 MB

Empty state first launch / fresh workspace

Don't show a sad cloud illustration — show what the user can do next.

› ›
No sessions yet in this workspace
Sessions are scoped to the launch directory. Open one with reasonix code in the terminal, or import a transcript from another machine.

Bulk operations

Select multiple rows (shift-click range, ⌘-click toggle) → action bar slides in at the bottom of the list pane: delete, archive (move to .archive/, hidden by default), export (zip with sidecars), tag. No bulk-rename — one session at a time keeps the timestamp invariant intact.

§7Edit review

Where the agent's edit_file output becomes a thing you actually read before it lands. Multi-file aggregator at the top, per-file collapsible cards underneath, GitHub-style diff with syntax highlighting, expand-context chevrons, intra-line word diff, and a unified ↔ split toggle. Inline diffs in chat (§3) are the quick read; this panel is the full review.

Multi-file summary

Top-of-page aggregator. Stat row, mode toggle, bulk approve/reject. The Apply all button is disabled until every file is either approved or explicitly skipped — same gate the kernel will enforce.

3 files changed +24 · −18

Per-file card · expanded

Default state for any file with under ~80 changed lines. Header shows path + per-file stat + per-file approve/reject. Clicking the chevron collapses to header-only. Approval is sticky across panel re-renders so a long review doesn't lose state.

src/cli/commands/chat.tsx +1 −2
@@@@ -346,8 +346,7 @@ export async function chatCommand
346346 session={resolvedSession}
347347 />,
348348 // patchConsole:false — winpty/MINTTY redraw-glitch source.
349 // debug:true forces full-frame writes; log-update's diff drops frames…
350 { exitOnCtrlC: true, patchConsole: false, debug: true },
349 { exitOnCtrlC: true, patchConsole: false },
351350 );
↕ expand 14 lines
@@@@ -402,3 +401,3 @@ chatCommand
402401 teardown();
403 await session.flush();
402 await session.flushAndClose();
404403 }

Per-file card · collapsed

Default for files past the line-count threshold, or after the user has approved/rejected them. Header stays interactive — re-open with one click.

Side-by-side mode

Activates from the toggle in the top summary. Two panes share row alignment so the eye scans horizontally. Empty cells in either pane render as the elevated background, signalling pure adds/removes vs. modifications. Word diff inside the cells survives the mode swap.

src/cli/commands/chat.tsx+1 · −2
@@@@ -346,8 +346,7 @@ export async function chatCommand@@
348 // patchConsole:false — winpty/MINTTY redraw-glitch source.348 // patchConsole:false — winpty/MINTTY redraw-glitch source.
349 // debug:true forces full-frame writes…
350 { exitOnCtrlC: true, patchConsole: false, debug: true },349 { exitOnCtrlC: true, patchConsole: false },
351 );350 );

Empty + error states

Three visual states for the panel:

  • No pending edits — single line in elevated background: — no edit_file calls in this turn —. Clicking opens the most recent reviewed turn (read-only).
  • One edit, all approved — summary collapses to a single chip: ✓ 1 file applied · src/cli/commands/chat.tsx. Re-expand from the chip.
  • Test red after apply (RFC #25 stage 2) — diff stays visible, file card gains a red footer: test_run failed · vitest -t "<name>" · status fail · auto-reverted. Approve gate blocks until the model re-tries or the user opts into /refactor.

Wiring

Data source: events.jsonl via the dashboard's /api/events stream. Each tool.dispatched for edit_file + its paired tool.result + (post-#25) test_run compose one card. Apply / reject are no-ops in the design — the actual side-effect is in the kernel; the panel only reflects state.

§8Plans

Plans live longer than a turn — they survive across sessions if the work isn't done. The Plans panel is where they're browsed (left list), inspected (right detail), and resumed. The headline element is the horizontal step timeline at the top of the detail — done / active / pending / failed at a glance, click a step to drill into its dispatched tool calls and outputs.

~/work/reasonix plans release 0.18.1
TUI · #2 balance¥48.20
all 2 active 1 archived 12 failed 3
release 0.18.1 + deprecate 0.18.0 active Drop zombie commit, bump 0.18.1, publish, deprecate previous 5 steps · 3 / 5 done · 4m
dashboard redesign · drafted Build §1-§13 design mockups for web companion 8 steps · 5 / 8 done · 1h
tui-card-stream finalize done Migrate last UI surfaces onto card pipeline; drop legacy modules 6 / 6 done · today
events.jsonl sidecar lifecycle done Filter from listing; rename/delete moves; drop model.delta 3 / 3 done · today
remove change_workspace tool done Drop racy mid-session cwd switch; pin workspace at launch 4 / 4 done · today
dashboard sidebar Editor done File tree + CodeMirror integration in dashboard 5 / 5 done · 2d ago
scrollback wheel scroll fix failed Couldn't reproduce on Win10 cmd; needs different repro env 2 / 6 · 3d ago
release 0.18.1 + deprecate 0.18.0 2026-04-30-2014 · 4m elapsed

Step timeline

step 1 drop zombie commit git reset · 2s
step 2 bump 0.18.1 npm version · 4s
step 3 build & verify in progress · 23s
step 4 npm publish pending
step 5 deprecate 0.18.0 pending
steps done
3 / 5
elapsed
4m 12s
tokens used
12,840
cost
¥0.62

Step 3 · build & verify › in progress

run_commandnpm run verify · 23s elapsed
✓ tests/session.test.ts (8)
✓ tests/loop.test.ts (12)
✓ tests/event-sink-jsonl.test.ts (4)
✓ tests/hydrate-cards.test.ts (8)
⏵ tests/jobs.test.ts (running…)
tools 23 2 active plans

§9Usage

Cost & token analytics. Time-range tabs at the top, big stacked area chart in the middle (cost-per-day, stacked by tool source), donut breakdown for the selected range, and a top-N tools table at the bottom. The four KPI cards above the chart are the same set used on Overview — consistency, not duplication.

~/work/reasonix usage last 14 days
balance¥48.20 budget78 / 100
24h 7d 14d 30d all group by
total cost
¥31.84
▲ 12% vs prior 14d
tokens · in
1.42M
▲ 8%
tokens · out
186k
— flat
cache hit
94%
▲ 2 pts
cost · 14 day · stacked by source¥18.40 / day avg
native · fs¥14.20
native · shell¥10.40
mcp · *¥4.80
subagent¥2.44
cost share · 14d
fs 45%
shell 33%
mcp 15%
subagent 7%
top tools · by cost14d
ToolSourcecallstokenscost
read_filenative · fs3,420812k¥9.40
edit_filenative · fs412340k¥4.20
run_commandnative · shell128280k¥3.10
grep_filesnative · fs6242k¥0.68
github__get_prmcp · github1438k¥0.52
tools 23 refreshed 12s ago

§10Inventories

Five panels share one pattern: filter chips → big table → detail drawer. Tools, MCP servers, Skills, Memory entries, Permissions allowlist. The schema of the data differs; the layout doesn't. Build one component, parameterize it. Showing Tools as the master mock; the variants below render the same surface with different data.

— Tools panel: master mock

~/work/reasonix tools edit_file
loaded23 / 24
all 23 native · fs 7 native · shell 3 native · web 2 mcp · github 5 mcp · slack 4 subagent 2 failed 1×
ToolSourceLast callcalls · 7d
read_filenative · fsApp.tsx1,420ok
edit_filenative · fsPromptInput.tsx312ok
grep_filesnative · fs"workspace"62ok
run_commandnative · shellnpm run verify128ok
run_backgroundnative · shellnpm run dev14ok
github__get_prmcp · githubesengine/reasonix#138ok
github__create_prmcp · github0idle
slack__post_messagemcp · slack#dev3ok
python_runnersubagent0load fail
23 of 24 loaded 1 failed last refresh 8s

— Same pattern, different data: MCP, Skills, Memory, Permissions

M · MCP servers
running 2 stopped 0 errored 0
ServerTransporttoolsState
githubstdio5● up · 14m
slackstreamable-http4● up · 14m
S · Skills
all 8 subagent 2 inline 6
SkillKindruns
initinline3
reviewinline12
security-reviewsubagent2
simplifyinline8
claude-apiinline4
· Memory entries
all 14 user 3 feedback 5 project 5 reference 1
FB No Co-Authored-By trailer
FB No conversation in code comments
FB Tokenization facts (DeepSeek BPE)
PJ v0.18 dashboard redesign queue
PJ 0.18.1 ghost-frame deprecation
U User env: PowerShell + RMB
▎ Permissions allowlist
deny 2 allow 18 ask 5
PatternVerdicthits
npm *allow128
git *allow94
npm publishask3
rm -rf *deny0
git push --force *deny0

§11System

The diagnostic surface — answering "is anything wrong" in one screen. A health grid (each check is a labeled card with a left-edge state stripe), an environment info table, and a live log tail at the bottom for the agent's own structured events. When something's broken, this is the first place a user looks.

~/work/reasonix system 1 warning
uptime2h 14m

Health checks

api · deepseek ● ok
240ms p50
last call 2s ago
mcp · github ● up
stdio · 14m
5 tools loaded
mcp · slack ● up
streamable-http · 14m
4 tools loaded
subagent · python_runner ▲ load fail
ENOENT
retry in 30s · 3rd attempt
disk · sessions ● ok
128 / 50,000 MB
42 sessions · 0.3% used
events.jsonl sidecar ● flushing
12,840 events buffered
flush every 5s · 100ms p99
hooks ● 4 active
PreToolUse · PostToolUse · UserPromptSubmit · Stop
version ● latest
0.18.1
released 14m ago
environment
platformwin32 · 10.0.26200
nodev22.7.0
terminalWindows Terminal · ConPTY
cwd~/work/reasonix
tmpdir$LOCALAPPDATA/Temp
memory1.4 / 16 GB
tzAsia/Shanghai · +08:00
events · last 50open events.jsonl
02:34:18 ok subagent spawn end · python_runner · 2.4s · 240 tok 02:34:14 info tool run_command · npm publish · started 02:33:58 warn loop turn 14 · iter 3/10 · approval pending 02:33:41 ok tool edit_file · PromptInput.tsx · 1+ 2- 02:33:22 info model deepseek-chat · streaming · 1.2k tok 02:33:12 err subagent spawn fail · python_runner · ENOENT 02:32:48 ok session appendMessage · 2026-04-30-2014.jsonl 02:32:48 ok events flush · 14 events · 8ms 02:32:34 info user prompt submit · 248 chars 02:31:12 ok cache hit · 412 tok · saved 280ms
1 warn · python_runner tail · streaming

§12Semantic

The semantic-search panel: a search bar at the top, an indexing-status sidebar, and result cards with snippets and highlight. Distinct from the global command palette — the palette navigates known things; semantic search finds code by what it means, given a vector index over the project.

~/work/reasonix semantic
indexed1,842 chunks last build42m ago
14 results · 0.18s · cosine ≥ 0.62 sort by
src/loop.ts L1208 – L1288 · CacheFirstLoop.step 0.91
// When change_workspace fires its WorkspaceConfirmationError, // any subsequent calls in the same parallel batch would dispatch // against the OLD sandbox before the user has approved… let workspaceSwitchPending = false; for (const call of repairedCalls) {
src/tools/shell.ts L277 – L298 · runCommand 0.84
const onAbort = () => { aborted = true; killChildTree(); }; // Check synchronously first — if the signal aborted before listener attach if (opts.signal?.aborted) onAbort();
src/tools/jobs.ts L240 – L252 · JobRegistry.spawn 0.78
const onAbort = () => this.stop(id, { graceMs: 100 }); if (opts.signal?.aborted) { onAbort(); } else { opts.signal?.addEventListener("abort", onAbort, { once: true }); }
src/tools/subagent.ts L150 – L175 · spawnSubagent 0.71
// Wire parent-abort → child-abort. Two pitfalls we have to handle: // 1. The signal may already be aborted at attach time… const abortChild = () => childLoop.cancel(parentSignal.reason);
index fresh 14 results · 0.18s

Config preview dry-run output before saving

Clicking Preview on the index-config card POSTs the pending config to /api/index-config/preview, which runs the chunker walker without writing. Shows the projected delta + a sample of files that would change category. No state is mutated.

preview · pending changesunsaved
files now312
files after save287 (−25)
chunks delta~−140
excluded by reason
dirs14
exts8
patterns2
.gitignore1
sample (first 5 of 25)
tests/fixtures/large-trace.json · patterns
.cache/parser.bin · dirs
assets/screenshot-12.png · exts
build.lock · exts
scripts/dev-only.sh · .gitignore

Build progress when index is being rebuilt

building index · 312 files
scan
312 / 312
chunk
1,842 / 1,842
embed
1,142 / 1,842
write
pending
38s elapsed · ~22s remaining

§13Configuration

Hooks and Settings share a layout: a left rail with sub-sections, a main pane with the form. Hooks gets an extra concept — the event matrix — showing at a glance which hook script fires on which LoopEvent. Settings is mostly form-controls; the only non-trivial widget is the JSON view on the raw settings.json.

~/work/reasonix hooks event matrix
active4 hooks
Event matrix
+Add hook
Reload
Recent failures3
jump · settings
General
$Budget
Permissions
MMCP servers
{}Raw settings.json

Event matrix

Which hook script fires on which LoopEvent. Click a cell to edit timing, glob, or to disable. Adding a new hook (left rail) drops a row; the script lives in .reasonix/hooks/.

script
PreToolUse
PostToolUse
UserPromptSubmit
Stop
Notification
SessionEnd
format-on-edit.sh
edit_file
block-secrets.sh
edit_file
/\.env/
notify-slack.sh
always
archive-session.sh
always

Recent runs

02:34:18 ok PostToolUse format-on-edit.sh · 42ms · edit_file PromptInput.tsx 02:33:41 ok PostToolUse format-on-edit.sh · 38ms · edit_file chat.tsx 02:32:18 err PreToolUse block-secrets.sh · denied · edit_file .env.local 02:30:04 ok Stop notify-slack.sh · 280ms · #dev
4 hooks active last fired 12s

Settings · Raw JSON view when forms aren't enough

For everything not exposed via form (custom keymap, env passthroughs, exotic MCP transport overrides), the raw editor is one click away — same CodeMirror as the Editor panel, with JSON schema validation for autocomplete and warnings.

settings.json
~/.claude/settings.jsonuser
1{
2 "$schema": "https://reasonix.dev/schema/settings.json",
3 "model": "deepseek-chat",
4 "budgetUsd": 100,
5 "hooks": {
6 "PostToolUse": [
7 { "matcher": "edit_file", "command": "./scripts/format-on-edit.sh" }
8 ]
9 },
10 "permissions": {
11 "deny": ["rm -rf *", "git push --force *"],
12 "allow": ["npm *", "git *", "yarn *"]
13 }
14}
settings.json json · LF · UTF-8 saved · hot-reloaded Ln 7, Col 42

§14Open questions

Decisions deliberately deferred until implementation begins.

Take-over UX

When the dashboard takes input, does the TUI show the streaming response live (read-only), or pause until the dashboard releases? Lean toward live read so terminal-2 keeps reading while terminal-1 has the keyboard.

Sidebar grouping

Three groups (workspace · observe · configure) feel natural now. If the panel count grows past 14, may need a second axis (collapsible sub-sections) — defer until pressure exists.

Mobile / narrow

Out of scope for v1. The dashboard is a localhost development tool; phone-screen layout would only matter if Reasonix ever runs as a hosted service.

Theming

Single dark theme for v1. Light theme is a 1-week effort and not on the path right now — the TUI is dark-only too, theme parity is a non-goal.

Editor panel

Not mocked here. Lives in the same shell, but its core is a CodeMirror instance + tabs + tree view — those have their own design language already (CodeMirror's default dark + our palette overrides). A separate doc when we touch Editor.