- Import SettingsModal.css directly in SettingsModal to ensure extracted styles load reliably
- Adjust mobile auth provider layout so non-info header actions stay right-aligned
- Align API key controls to the right on mobile by updating section, row, and button alignment rules
- Extend mobile CSS regression assertions to cover the new auth alignment rules
- Remove the standalone Pi Extensions section from settings navigation and route legacy initialSection values to Plugins
- Add accessible subsection tabs in Plugins for Fusion Plugins and Pi Extensions with proper tab/tabpanel semantics
- Render plugin content conditionally per active subsection so only the selected manager is mounted
- Add SettingsModal tests covering sidebar navigation, tab accessibility, and subsection switching behavior
- Add subsection styling in SettingsModal.css and preserve extracted styles.css boundaries
- eslint: ignore .claude/** so agent worktree scripts don't pull thousands of
spurious "console/process is not defined" errors into the workspace lint.
- AgentsView: lift the controls popup max-height/overflow into a CSS modifier
(.agent-controls-panel--scrollable) instead of an inline style so the
inline-style discipline test stays at 4 (only runtime health colors).
- board-mobile.test: scan every @media (max-width: 768px) block in the loaded
CSS bundle, not just the legacy section in styles.css. After the cards/
inline-create extraction those rules now live in component CSS files.
- AgentsView.test: update the layout assertion for the new "stats above the
collection" placement and open the controls popup before asserting on
the token-usage panel (which now lives there, not in the main view body).
- agents-view-mobile.test: open the controls popup before asserting on the
token-stats panel content.
- TaskDetailModal.test: bump tab-count assertions from 7→8 (or 6→7 for
triage/todo) to include the new Stats tab, and switch to the Stats tab
before asserting on token-usage text in the optimistic-loading suite.
- Column.test: add MoreVertical/ChevronDown/ChevronUp/Archive to the
lucide-react vi.mock so the new column dropdown menu renders.
After this: pnpm lint = 0 errors, pnpm typecheck = clean, pnpm test = all
9132 dashboard + plugin/engine/cli/desktop/mobile/core tests pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
That flag was added for diagnosing the Ink/perf_hooks leak. Now that the
leak is fixed, leaving it on means a 1GB+ snapshot file gets dumped to
cwd whenever heap pressure hits — noisy and not useful day-to-day.
Inspector flag forwarding is kept for normal --inspect usage.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a dropdown menu (MoreVertical icon) to the Todo column header with a
Respecify All action that bulk-moves every visible todo task back to Triage
after a confirm dialog. Toasts surface partial-failure counts when the
parallel onMoveTask calls don't all succeed. Click-outside and Escape close
the popover. Supporting .column-menu / .column-menu-popover / .column-menu-item
styles added in styles.css.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- AgentsView: agent metrics bar moves to the top of the scrolling
content (was below the agent list); token-usage panel moves into
the controls dropdown next to the heartbeat slider, which is now
scrollable so the added content fits on short viewports.
- AgentsView mobile: 2x2 grid for the metric cards with tighter
padding and smaller value/label text so labels don't crowd off
the card on narrow phones.
- TaskDetailModal: new "Stats" tab at the end of the fixed tab list
hosts the token-usage panel, removed from the inline definition
body.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sweep 3 of the styles.css split. Five settings-related blocks moved out of
the monolith into a single co-located SettingsModal.css. styles.css 3304 →
2509 (–795 lines).
Blocks moved:
- /* === Settings Layout === */ (194 lines)
- /* === Notifications Settings === */ (28 lines)
- /* === Memory Settings === */ (112 lines)
- /* === Auth Provider Cards === */ +
/* === Provider Connection Status === */ (165 lines combined)
- /* === Settings: Model Presets === */ (93 lines)
- Settings lane badges + descriptions (~27 lines)
Mobile @media rules whose selectors matched the moved blocks were also
migrated into SettingsModal.css's mobile block.
Kept global with documented reasons:
- .auth-status-badge (and color modifiers) — consumed outside settings by
ModelOnboardingModal and ClaudeCliProviderCard.
- .settings-empty-state, .settings-muted, .settings-loading — broad reuse
across BackendConnectionErrorPage, PluginManager, MemoryView etc.
Cross-component imports added: MemoryView.tsx and ModelOnboardingModal.tsx
now import SettingsModal.css to reach memory-* / auth-provider-card classes
they consume.
Test: settings-mobile.test.tsx had two assertions that incorrectly matched
literal pixel values; updated to the actual var(--space-*) token values
present in the rules. Verified live in browser (settings + memory tabs
render cleanly).
styles.css total reduction across the three sweeps: 4551 → 2509 (–45%).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
TUI: vitest memory-guard threshold and on/off toggle now persist to
global settings (vitestAutoKillEnabled / vitestKillThresholdPct), so
they survive dashboard restarts. Stats panel shows the system-memory
used percentage next to used/free. Utilities panel exposes [+/-] to
adjust the threshold in 5% steps (50–99%).
Release: scripts/release.mjs auto-syncs a root CHANGELOG.md aggregated
from every packages/*/CHANGELOG.md, grouped by version with one
sub-block per package.
Versioning: all private @fusion/* packages joined the changesets fixed
group with the public cli + cli-alias and were aligned to 0.2.5, so
every release bumps every package and produces per-package CHANGELOG
entries that the aggregator picks up.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sweep 2 of the styles.css split. Three card-related blocks moved out of the
monolith into co-located component CSS files. styles.css 4488 → 3304
(–1184 lines).
- /* === Cards === */ (583 lines) → new TaskCard.css
- /* === Card Inline Editing === */ (266 lines) → appended to TaskCard.css
- /* === Inline Create Card === */ (342 lines) → new InlineCreateCard.css
Mobile rules for the moved selectors that previously sat in the global mobile
@media block also followed to their respective component CSS files.
Kept global: .dep-dropdown* (4 consumers — InlineCreateCard, NewTaskModal,
TaskDetailModal, TaskForm). Moving without adding imports to the other 3
consumers would silently break their styling.
CSS imports added at TaskCard.tsx:1 and InlineCreateCard.tsx:1. Verified by
visual smoke test against the live dev server (board view + cards render
correctly).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sweep 1 of the styles.css split. Rules where the desktop counterpart already
lived in a component CSS file have been moved out of the global mobile @media
block in styles.css and appended to the matching component file's mobile
block. styles.css 4551 → 4488.
Affected components: Header, AgentsView, DocumentsView, ListView, QuickChatFAB,
QuickEntryBox, ScriptsModal, TaskDetailModal, TerminalModal, WorkflowResultsTab.
Truly global mobile rules (input font-size for iOS auto-zoom, html/body
overflow, root safe-area padding, base .btn touch targets, bare element
selectors) intentionally stay in styles.css. The remaining bulk of the mobile
block (settings, auth, board/card, inline-create) cannot move yet because
their desktop rules are still in styles.css — those will fall out as part of
the cards and settings extraction sweeps.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
NODE_OPTIONS is inherited by every grandchild node process. With --inspect
in there, every vitest worker, agent subprocess, and claude invocation the
dashboard spawned tried to bind 127.0.0.1:9229 and emitted "address already
in use" — those failure strings ended up in agent logs (visible in heap
diagnostics). Pass inspector flags directly on the node command line so
they apply only to the dashboard process.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Apply shared button utility classes across PluginManager action controls for consistent dashboard styling
- Move PluginManager mobile responsive overrides into PluginManager.css and update code background fallback to token-based color-mix
- Improve PiExtensionsManager remove controls with touch-target sizing, hover feedback, and descriptive aria-labels
- Stabilize ModelOnboardingModal helper-text assertions by waiting for provider fetch/render state before checking visibility
Ink/react-reconciler in dev mode emits performance.mark/measure() on every
render. Without a PerformanceObserver, Node retains every entry in the User
Timing buffer forever — ~165 renders/sec in the TUI accumulate ~600MB of
PerformanceMeasure objects + diff strings ("Components ⚛", "Changed Props",
"– children", etc.) over 20-30min, which is what was OOM-crashing long dev
sessions. A 30s interval calling clearMeasures()/clearMarks() drains it.
Also teach scripts/dev-with-memory.mjs to forward --inspect /
--inspect-brk, plus auto-add --heapsnapshot-near-heap-limit=3 so any future
heap-limit OOM auto-dumps. The script now invokes node directly with tsx's
preflight + loader (resolved via require.resolve) instead of going through
the .bin/tsx wrapper, so the inspector attaches to the real app process
instead of a parent wrapper that exits before serving the websocket.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Preserve markdown prose container rendering for thinking/text log rows in AgentLogViewer.
- Keep AgentLogViewer and TerminalModal regression coverage for markdown wiring and terminal refresh fallback behavior.
- Carry forward terminal startup fallback styling and retry handling in TerminalModal.
- Record squash merge completion for fusion/fn-2464 on top of current main state.
[k] kills all running vitest processes via pgrep -f. [v] toggles a
sampler-driven guard that SIGKILLs vitest when system memory exceeds
90%, throttled to once per 30s. Auto-kill defaults ON.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Add refresh-page actions alongside retry/reinitialize in TerminalModal bootstrap and xterm init error states
- Add terminal error action-group styling so multiple recovery buttons render cleanly
- Extend TerminalModal tests to cover new refresh controls and verify window reload behavior
- Remove unused TaskLogEntry import and drop the stale hooks eslint suppression in dashboard-tui app
- Introduce a new "project-setup" onboarding step between GitHub and first-task actions
- Gate first-task navigation on project selection and add contextual setup-wizard guidance
- Centralize ordered onboarding step definitions in shared state and reuse them for navigation/progress labels
- Extract ModelOnboardingModal styles into a dedicated component CSS file and update tests/mocks for the new step order
- Emit an auth-token recovery event when dashboard daemon calls fail with bearer-token 401 responses
- Render a non-dismissable recovery dialog with set-token and clear-token reload actions
- Keep the recovery overlay topmost while aligning modal markup with shared modal conventions
- Add focused regression coverage for dialog behavior and include a patch changeset for @runfusion/fusion
Several layered defenses against the layout occasionally rendering
at the wrong height (header pushed off-screen, content too tall).
Still not fully resolved — flag for further debug — but this
combination materially reduces frequency, especially under tmux/ssh.
* Enter alt-screen on start (\x1b[?1049h), leave on stop. Gives the
TUI a dedicated fullscreen surface that doesn't share scrollback,
so frames taller than the viewport clip cleanly instead of
scrolling the header into shell history.
* Controller subscribes to process.stdout 'resize' and calls
inkInstance.clear() — resets log-update's previous-frame line
count so the next render isn't skewed by a stale clear region.
* App-level resize listener bumps a state counter and the root Box
uses key={`${cols}x${rows}`} so React unmounts and remounts the
whole tree on dimension change, bypassing any Yoga layout cache.
* Root Box gets explicit width={cols} + overflow="hidden" so
overflow gets clipped instead of pushing siblings out.
* StatusBar: Text children now wrap="truncate-end" with
flexShrink={0} on the outer Box. Default wrap="wrap" was letting
long hotkey+URL strings wrap to 2 rows, throwing the row budget
off by 1 — the proximate cause of the 151x46 wedge.
* MainHeader outer Box: flexShrink={0} + overflow="hidden" as
belt-and-suspenders.
* Settings + Utilities side-by-side now match heights: UtilitiesPanel
uses flexGrow={1} (was flexShrink={0}), so cross-axis stretch
applies equally.
* StatusModeGrid receives rows as a prop again (single source of
truth from App's render, instead of each child reading its own
useStdout snapshot).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* HelpOverlay used `key={key}` on each shortcut row, but several keys
legitimately repeat across contexts (`[t]` for Git view AND
Toggle engine pause; `[r]` for Refresh stats AND Refresh agent
detail). React threw "Encountered two children with the same
key" and the overlay crashed. Switch to index-based keys with a
comment explaining each row is unique by position.
* Refresh the help text: unified header tabs, Settings/Files/
Agents ←/→ pane swap, Git push/fetch shortcuts, Files hidden-
files toggle, Logs G jump-to-end.
* Main (status mode) header sometimes vanished after a tmux pane
switch and only resize fixed it. Two fixes:
- Remove the `rows < 10` auto-hide in MainHeader. tmux pane
switches can briefly report stale or zero dimensions, and the
transient `return null` orphaned the header.
- Wrap MainHeader and StatusBar in flexShrink={0} boxes inside
StatusModeGrid/StatusModeSingle (matching the prior
InteractiveMode fix) so Yoga can't squeeze them to 0 rows.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Drop the separate section + interactive tab strips for a single
unified strip: [m] Main [b] Board [a] Agents [g] Settings [t] Git
[e] Explorer. Width tiers tightened to fit every terminal size:
full labels at cols ≥ 90, glyph-only at 50–89, FUSION + active
pill only below 50.
New 'm' shortcut switches to status mode (Main); 's' kept as alias.
Number keys 1–5 still jump to status sections under the hood.
Settings view: ←/→ now switch between list and detail panes,
matching Agents view.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Yoga's default flexShrink: 1 was letting the MiniLogo and tab pills
shrink when the header row's collective content exceeded the
terminal width — driving FUSION onto a second line. Pin every
fixed-content header child (logo, dividers, tab pills, help-hint) to
flexShrink={0}; the trailing flexGrow filler still absorbs slack.
Belt-and-suspenders: wrap="truncate-end" on FUSION so any future
constraint truncates instead of wrapping.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Catch-up commit for the v0.2.1 publish — the prior release script's
git commit step failed silently (allowFail), so the version bumps
and consumed changeset were left uncommitted while the npm publish
still succeeded.
The global "switch to Settings" shortcut matched both g and G, so
uppercase G never reached the logs panel and the vim-style "jump to
end" never fired. Restrict the global handler to lowercase g; G
falls through to the active panel — including the status-mode logs
list (jump-to-newest) and any other scrollable panel that opts in.
Agents view: ← focuses the list pane, → focuses the detail pane,
matching the visual layout. Tab still cycles either direction.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- BoardView columns now window their card list against the available
rows so the cursor stays visible. Cards beyond the viewport scroll
in via "↑ N more" / "↓ N more" indicators. Fixes the "can't reach
cards in the Done column" bug — selectedIndex was advancing but the
cards were clipped.
- Logs section: G (vim-style, in addition to End) jumps to newest
entry. Lowercase g stays as the global Settings shortcut.
- MainHeader gains two new collapse tiers: cols < 50 renders just
FUSION + the active tab pills (no inactive tabs, no dividers); rows
< 10 hides the header entirely so the row isn't wasted.
- ExpandedLog stretches to width="100%" so the panel keeps its width
when an entry is expanded — was collapsing to content width before.
- Lighter blue palette throughout (cyanBright / cyan); status-mode
grid widened to 5:6 so Stats panel gets ~45% of cols.
- Stats: heap limit always on its own continuation row; bytes
formatted with a space ("450 MB") so a forced wrap, if it ever
happens, breaks after the unit.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Switch the .agents-view-title flex behavior from wrap to nowrap and apply
white-space: nowrap on the title and its h2, so the Bot icon and "Agents"
label stay on one row and the view-toggle / primary actions push to the
right edge. Drop the dedicated 32px icon-only width override that came from
the previous wrapping layout.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Add a Git interactive view (hotkey [t]/[4]): branch + ahead/behind,
recent commits with detail strip, staged/unstaged/untracked files
panel, branches list, worktrees panel, and a [P] push modal with
pre-flight commit list and capture of stdout/stderr. ←→ cycles
status → branches → worktrees → commits → changes; ↑↓ navigates
rows in the focused list. 5s poll while mounted.
- Project-scoped task stats: BoardView pushes its selected project
path into the controller; refreshTUIStats now reads from a per-
project TaskStore via a shared getProjectStore helper. Project
switch triggers an immediate refresh via onBoardScopeChange.
- Unified MainHeader used by status and interactive modes — section
tabs ([1]–[5]) and interactive view tabs ([b]/[a]/[g]/[t]) always
visible; previously the interactive header replaced the main one.
- Logs panel keeps its size when expanding a single entry.
- 'f' cycles severity filter from any panel in status mode.
- BoardView: explicit cross-view shortcuts so g/a/t always switch
views regardless of input-handler ordering.
- BoardView column overlap fix: flexShrink={0} on structural rows.
- TaskCard always shows the title with wrap="wrap"; single short-id
pill, no duplicate id-as-title fallback.
- Stats panel reorg: StatRow helper, bold section headers, narrow-
width wrap for Heap and Memory trailing fragments.
- Lighter blue palette: cyanBright fg accents / cyan active bg /
logo gradient whiteBright → white → cyanBright → cyan → blue.
- System stats sampler: RSS, heap (V8 limit-aware color), external,
CPU%, load avg, system used/free. Heap thresholds scale off
--max-old-space-size automatically.
- Test fixture updated for the new InteractiveData.git block.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
A duplicate agent id slipping through useAgents (race between initial fetch
and an SSE refresh, or backend pagination edge case) was flooding React with
"Encountered two children with the same key" warnings. With the active panel
re-rendering on every transcript event the warning fired every few ms and
snowballed the console buffer until the page crashed with OOM.
Dedupe by id at the hook (so every consumer benefits) and again in
ActiveAgentsPanel as belt-and-braces.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The hook was prepending every SSE log entry into React state without bound.
Long-lived active agents flooded the array (hundreds of MB) and the dashboard
eventually died with an out-of-memory crash. Cap the buffer at 200 entries —
the UI only ever renders the first 20, so the cap is generous but finite.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Restores the stats-first ordering on the Agents view. Stats (AgentMetricsBar)
and live agents (ActiveAgentsPanel) now render above the tree/list collection
instead of underneath it.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace the Colossal "888" large-logo variant with a vertically doubled
ANSI Shadow render so the bigger splash keeps the same block-letter
aesthetic as the small variant — the prior variant looked stylistically
inconsistent next to the small block letters. 47 cols × 12 rows.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Resize the dashboard TUI splash logo to scale with the terminal: a Colossal-font variant kicks in on terminals ≥70 cols × 16 rows; gradient palette is now strictly white → blueBright → blue (no cyan/magenta)
- Lower-clip TUI Kanban cards to a single-line title and constrain each column with overflow hidden so cards no longer bleed past the header row
- Center the TerminalModal vertically by overriding overlay alignment via :has() and trimming the modal height
- Match GitHub Import modal width to the file browser modal (90vw / 1600px max)
- Render the Usage indicator as a top-right popover on desktop instead of a centered modal (mobile keeps the full-screen sheet)
- Widen SetupWizard / Model Onboarding modals (540→880px, 560→880px) and replace the washed-out done step indicator background with a solid success swatch
- Drop the duplicate mailbox icon button from the desktop header (view switch covers it; overflow menu still surfaces it on compact)
- Loosen agent health detection: bump the heartbeat grace multiplier 2× → 4× and the staleness floor 60s → 5min so transient ticks don't flag agents Unresponsive
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Batch 2 of runMaintenance() runs ~10 recover passes back-to-back, each
calling listTasks({ column: ... }) without slim. On busy boards this
materializes every task's activity log into memory ~10× per cycle,
walking the dashboard heap toward the 8 GB V8 limit until OOM. The
archive pass at line 610 already had this fix; extend it to the in-progress
and in-review recover passes that only read steps / paused / worktree /
mergeDetails / postReviewFixCount — all included in the slim projection.
Triage recovers are left non-slim because hasLatestSpecReviewApproval
scans task.log to find the most recent spec review; the triage column
is small so the memory cost is bounded.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Move misclassified .mesh-topology, .connect-node, .nodes-view-topology, .node-status-indicator, and pulse-warning keyframe from AgentReflectionsTab.css to NodesView.css; relocate node-related mobile @media rules from SettingsSyncLog.css so NodesView renders standalone
- Add missing CSS import on GitManagerModal so its .gm-* styles load without depending on ScriptsModal being mounted
- Bump desktop max-widths for FileBrowser, Terminal, GitManager, GitHubImport, and WorkflowStepManager modals; raise selector specificity to .modal.X so the base .modal { width: 480px } can't win the cascade
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Add a new /api/git/diff/file endpoint with path and staged query handling, including validation and untracked-file diff support
- Introduce fetchGitFileDiff API client wiring and update GitManagerModal to load diffs for selected staged/unstaged file rows
- Improve file-row UX with active/focus styling, keyboard activation, and guarded async diff loading/error states
- Expand dashboard route and modal tests to cover per-file diff behavior, query validation, and stage/unstage interaction isolation
The global SSE broadcast called res.write() without checking the return
value, so a paused or backgrounded client would silently accumulate
every store event for every entity (tasks, missions, plugins, agents,
chat, ...) into res.outputData until the dashboard process OOMed.
Add a 4 MB writableLength threshold; when exceeded, tear down the
connection so the OS releases the buffer.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The global SSE broadcast called res.write() without checking the return
value, so a paused or backgrounded client would silently accumulate
every store event for every entity (tasks, missions, plugins, agents,
chat, ...) into res.outputData until the dashboard process OOMed.
Add a 4 MB writableLength threshold; when exceeded, tear down the
connection so the OS releases the buffer.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Treat the completion view as an effective final step so all prior indicators/connectors remain marked done
- Mark the first-task step as completed before tracking onboarding completion and clear it from skipped state
- Replace hardcoded success colors in setup wizard stepper done styles with theme-aware done status tokens
- Add regression assertions verifying all step indicators and connectors stay in done state on the completion screen
- Render the compact project switch trigger for tablet layouts by gating on compact mode instead of mobile-only mode
- Extend ProjectSelector compact-switch styles to the tablet media range so the trigger and dropdown render correctly beside the logo
- Update tablet header control regression coverage to expect the compact switch affordance and absence of the desktop selector trigger
- Add Header tablet interaction tests covering Escape key, outside click, and project selection closing behavior for the compact switch dropdown