## §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 sky in-progress, links
--c-accent purple reasoning, plan
--c-violet sub-agent
--c-ok green success
--c-warn amber approval, warning
--c-err coral error

### Chart spectrum six-stop series — distinguishes without shouting

s1 sky primary
s2 teal secondary
s3 mint tertiary
s4 amber quaternary
s5 coral accent / negative
s6 purple model 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 / 700 Headline · 28px
22 / 700 mono Section title · 22px
14 / 400 Body — default reading size for prose. 14px Inter at 1.55 line-height.
12.5 / 400 mono Code / data — JetBrains Mono
11 / 600 mono SECTION LABEL · 11PX UPPERCASE

### Glyphs single-char icons reused from the TUI

◈brand
◆chat
✎edit
⊞plan
›sessions
$usage
▣tools
▎permissions
+system
≈semantic
M mcp
S skills
·memory
H hooks
⌘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

◈REASONIX 0.18.1
workspace
◆Chat
✎Edit review
⊞Plans 1
›Sessions
observe
◈Overview
$Usage
+System
configure
▣Tools
▎Permissions
M MCP 2
S Skills
·Memory
H Hooks
⌘Settings
localhost:8742 «
~/work/reasonix › feat/dashboard-v2 › 2026-04-30-2014
TUI · #2 model deepseek-chat balance¥48.20 turn 12
— panel content slot —

— Sidebar collapsed (icon-only)

◈
◆
✎
⊞
›
◈
$
+
▣
M
⌘
»
~/work/reasonix › feat/dashboard-v2
TUI · #2 ¥48.20
— collapsed sidebar trades labels for icons; tooltips on hover —
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 · assistant 2.3s · 1.2k tok
Looking up the exit code Windows uses when SIGTERM is delivered to a console subsystem process…
⊞plan · awaiting approval 5 steps
Refactor session sidecar lifecycle so `.events.jsonl` rename/delete tracks the parent.
▲shell · awaiting approval deepseek
npm publish
✕tool error · run_command exit 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.

Tool | Source | last call | calls | avg ms
--- | --- | --- | --- | ---
`read_file` | native · fs | src/cli/ui/App.tsx | 142 | 8
`edit_file` | native · fs | src/cli/ui/PromptInput.tsx | 38 | 14
`run_command` | native · shell | npm run verify | 11 | 23,400
`grep_files` | native · fs | "workspace" src/ | 9 | 42
`github__get_pr` | mcp · github | esengine/reasonix#13 | 4 | 280

### 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.

1 export 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
346 346 session={resolvedSession}
347 347 />,
348 348 // 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 },
351 350 );

### 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
plan review approve execute commit
ring · for KPIs that compress to a single number
94%cache
3/10 iters
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.

Workspace path
Model
Budget cap (CNY) Soft cap; warn at 80%, refuse new turn at 100%.
Code mode
✓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

◈REASONIX 0.18.1
◆Chat
✎Edit review
⊞Plans 1
›Sessions
observe
◈Overview
$Usage
localhost:8742«
~/work/reasonix › feat/dashboard-v2 › 2026-04-30-2014
TUI · terminal #2 balance¥48.20
◆user 20:12:33
npm publish 报 0.18.0 已存在，但我没看到之前 publish 成功，能不能查一下 registry？
⊞reasoning 3 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_command 240ms · ok
npm view reasonix@0.18.0 time gitHead
{"0.18.0":"2026-05-01T03:20:58.310Z"}
gitHead = 'f8e156c898…'
⏵assistant streaming · 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…

Active plan
1
2
3
4
✓investigate registry timestamp
✓confirm gitHead = pre-fix commit
⏵release 0.18.1 with the fix
○deprecate 0.18.0 on registry
Session
turns 12
prompt tok 42,318
completion 8,041
cost¥1.84
cache hit
94%
Tool budget
turn iters
3 / 10
tok this turn
3.4k / 8k
budget
¥78 / ¥100
slash commands
//plan draft a step-by-step plan
//budget set or clear the cost cap
//sessions switch / rename / forget
//cwd deprecated
//diff unsubmitted 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 →

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

◈REASONIX 0.18.1
◆Chat
✎Edit review
⊞Plans
›Sessions
«
~/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
⏵assistant streaming 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 commands 0 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 · ⇧↵ newline 1.2k tok · ¥0.07 est send →
disabled · model is responding
…waiting for response · esc to abort
streaming · 240 tok so far elapsed 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_command deepseek · 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
//deprecate mark a published version as deprecated↵
panels
▣Tools browse registered tools
▎Permissions edit allowlist
recent sessions
›2026-04-30-1908 tui-card-stream redesign
›2026-04-29-1602 v0.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.

◈REASONIX 0.18.1
◆Chat
✎Edit review
⊞Plans
›Sessions
observe
◈Overview
$Usage
+System
localhost:8742«
~/work/reasonix › feat/dashboard-v2 › 2026-04-30-2014
TUI · #2 model deepseek-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 session open in chat →
◆2026-04-30-2014 started 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 plans see all →
✓finalize card-stream migration · 4 steps 2h ago
✓events.jsonl sidecar lifecycle · 3 steps 1h ago
⏵release 0.18.1 + deprecate 0.18.0 · 5 steps · 3/5 now
○dashboard redesign · drafted queued
tool activity · last hour full feed →
●run_command npm publish 02:31
●run_command git push --follow-tags 02:31
▲run_command npm publish (rejected)02:30
●edit_file src/cli/commands/chat.tsx 02:28
●run_command npm run verify 02: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

### 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.
◈REASONIX 0.18.1
◆Chat
✎Edit review
⊞Plans
›Sessions 42
observe
◈Overview
$Usage
localhost:8742«
~/work/reasonix › sessions › 2026-04-30-2014
total 42 sessions disk 128 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.0 5 / 5
⏵dashboard redesign · drafted in progress

### 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
346 346 session={resolvedSession}
347 347 />,
348 348 // 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 },
351 350 );
↕ expand 14 lines
@@@@ -402,3 +401,3 @@ chatCommand
402 401 teardown();
403 await session.flush();
402 await session.flushAndClose();
404 403 }

### 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.

src/loop.ts +18 −14 approved
tests/loop.test.ts +5 −2

### 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.

◈REASONIX 0.18.1
◆Chat
✎Edit review
⊞Plans 2
›Sessions
observe
◈Overview
localhost:8742«
~/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_command npm 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…)

## §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.

◈REASONIX 0.18.1
◆Chat
✎Edit review
⊞Plans
›Sessions
observe
◈Overview
$Usage
+System
localhost:8742«
~/work/reasonix › usage › last 14 days
balance¥48.20 budget 78 / 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 cost 14d

Tool | Source | calls | tokens | cost |
--- | --- | --- | --- | --- | ---
`read_file` | native · fs | 3,420 | 812k | ¥9.40 |
`edit_file` | native · fs | 412 | 340k | ¥4.20 |
`run_command` | native · shell | 128 | 280k | ¥3.10 |
`grep_files` | native · fs | 62 | 42k | ¥0.68 |
`github__get_pr` | mcp · github | 14 | 38k | ¥0.52 |

## §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

◈REASONIX 0.18.1
◆Chat
✎Edit review
configure
▣Tools 23
▎Permissions
M MCP
S Skills
·Memory
localhost:8742«
~/work/reasonix › tools › edit_file
loaded 23 / 24
all 23 native · fs 7 native · shell 3 native · web 2 mcp · github 5 mcp · slack 4 subagent 2 failed 1×

Tool | Source | Last call | calls · 7d |
--- | --- | --- | --- | ---
`read_file` | native · fs | App.tsx | 1,420 | ok
`edit_file` | native · fs | PromptInput.tsx | 312 | ok
`grep_files` | native · fs | "workspace" | 62 | ok
`run_command` | native · shell | npm run verify | 128 | ok
`run_background` | native · shell | npm run dev | 14 | ok
`github__get_pr` | mcp · github | esengine/reasonix#13 | 8 | ok
`github__create_pr` | mcp · github | — | 0 | idle
`slack__post_message` | mcp · slack | #dev | 3 | ok
`python_runner` | subagent | — | 0 | load fail

▣edit_file native · fs

SEARCH/REPLACE block editor; the safe mode wraps every edit in a content hash check before write.

schema
"file_path": string // required "old_string": string // required "new_string": string // required "replace_all": boolean // default false
recent calls
02:31 · PromptInput.tsx · ok
02:28 · chat.tsx · ok
02:22 · App.tsx · ok
02:14 · session.ts · retry 1

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

M · MCP servers
running 2 stopped 0 errored 0

Server | Transport | tools | State
--- | --- | --- | ---
`github` | stdio | 5 | ● up · 14m
`slack` | streamable-http | 4 | ● up · 14m

S · Skills
all 8 subagent 2 inline 6

Skill | Kind | runs
--- | --- | ---
`init` | inline | 3
`review` | inline | 12
`security-review` | subagent | 2
`simplify` | inline | 8
`claude-api` | inline | 4

· 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

Pattern | Verdict | hits
--- | --- | ---
`npm *` | allow | 128
`git *` | allow | 94
`npm publish` | ask | 3
`rm -rf *` | deny | 0
`git push --force *` | deny | 0

## §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.

◈REASONIX 0.18.1
◆Chat
✎Edit review
observe
◈Overview
$Usage
+System!
localhost:8742«
~/work/reasonix › system › 1 warning
uptime 2h 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

platform | win32 · 10.0.26200
node | v22.7.0
terminal | Windows Terminal · ConPTY
cwd | ~/work/reasonix
tmpdir | $LOCALAPPDATA/Temp
memory | 1.4 / 16 GB
tz | Asia/Shanghai · +08:00

events · last 50 open 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

## §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.

◈REASONIX 0.18.1
◆Chat
✎Edit review
observe
≈Semantic
$Usage
localhost:8742«
~/work/reasonix › semantic
indexed 1,842 chunks last build 42m 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 status● fresh
chunks 1,842
files 312
model bge-small-zh-v1.5
dim 512
size 14 MB
last build 42m ago
stale chunks
8%
index config reset
exclude dirs
node_modules× dist× .git× .cache× + add
exclude files
package-lock.json× pnpm-lock.yaml× + add
exclude exts
.lock× .snap× .png× .webp× + add
exclude patterns · glob
**/*.test.ts× fixtures/**× + add
✓respect `.gitignore`
max file bytes skip files larger than ~2 MiB

### 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 changes unsaved
files now 312
files after save 287 (−25)
chunks delta~−140
excluded by reason
dirs 14
exts 8
patterns 2
.gitignore 1
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`.

◈REASONIX 0.18.1
◆Chat
✎Edit review
configure
▣Tools
▎Permissions
H Hooks
⌘Settings
localhost:8742«
~/work/reasonix › hooks › event matrix
active 4 hooks

### 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

### 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.json user
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.