Commit Graph

3138 Commits

Author SHA1 Message Date
gsxdsm
f6dd4467cc fix(dashboard): repair desktop modal widths and NodesView CSS regressions
- 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>
2026-04-24 22:25:57 -07:00
Fusion
e06d4efdb5 feat(FN-2391): add clickable per-file diffs in Git Manager
- 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
2026-04-24 22:25:57 -07:00
Fusion
babef7b97f fix(dashboard): close SSE connections on outbound backpressure
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>
2026-04-24 22:25:57 -07:00
gsxdsm
e7466804c7 fix(dashboard): close SSE connections on outbound backpressure
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>
2026-04-24 22:02:40 -07:00
Fusion
937cb42dce fix(FN-2402): preserve onboarding stepper done state on completion
- 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
2026-04-24 21:48:08 -07:00
Fusion
69ddecf470 feat(FN-2398): restore compact project switch in tablet header
- 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
2026-04-24 21:42:07 -07:00
Fusion
baaaaf2a2d feat(FN-2395): style settings panel scrollbars
- Add cross-browser scrollbar styling for settings sidebar and content panes using existing design tokens
- Extend settings mobile CSS tests to assert the new scrollbar rules for WebKit and Firefox-compatible properties
- Refactor dashboard TUI app tests to render via React.createElement for compatibility with current test tooling
- Tidy mission e2e mock ID generators to satisfy formatting/quality gate checks
2026-04-24 21:08:35 -07:00
Fusion
452d1c7a2f fix(FN-2396): align directory picker select button styling
- Switch the DirectoryPicker Select action to shared btn btn-primary classes with explicit button type
- Move select-button padding to component-scoped DirectoryPicker.css tokens and remove hardcoded !important overrides
- Extend DirectoryPicker tests to assert the expected class contract before click behavior
2026-04-24 20:58:45 -07:00
Fusion
1d06305f57 fix(FN-2469): show GitHub OAuth instructions during onboarding login
- Resolve merge conflicts while preserving current provider grouping and onboarding flow
- Render GitHub login instructions in the onboarding connect CTA while auth is pending
- Add a dedicated test id for the GitHub onboarding instruction message to satisfy regression coverage
2026-04-24 20:50:46 -07:00
gsxdsm
c1f0abb2a4 chore(release): v0.2.0
Version bump via changesets.
2026-04-24 20:45:05 -07:00
gsxdsm
4029335d99 chore(dashboard): drop duplicate ChatView.css eager import
A rebase preserved two copies of the eager `import "./components/ChatView.css"`
in App.tsx (one at the top of the imports block, one inline among the
lazy view declarations). Keep the top-of-file copy, remove the inline
duplicate.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 20:43:37 -07:00
gsxdsm
5ba4395d4e fix(dashboard): mobile chat New Chat button placement + merged fn-2469
- ChatView mobile: hide the top sidebar header's "New Chat" button on
  <=768px and show the existing footer button as a pinned full-width
  action with safe-area padding. Both buttons existed in JSX; only
  CSS was needed.
- styles.css: comments compose/edit textareas use var(--surface) so
  they remain visible against the task detail modal's --card panel
  (in light theme --bg and --card both resolve to #ffffff).
- Includes resolved-conflict changes from fusion/fn-2469 in
  ModelOnboardingModal and SettingsModal (and the related test).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 20:43:22 -07:00
Fusion
820582b722 fix(dashboard): chat mobile single-pane, comment-box theme, ChatView FOUC
- ChatView mobile: add @media (max-width: 768px) so the session-list
  sidebar goes full-width and the thread is hidden when the sidebar
  is visible (and vice-versa). The component already had the state,
  back-button (ChevronLeft), and visibility toggling — only the CSS
  was missing.
- TaskComments compose/edit textareas now use var(--surface) for
  background. The base .spec-editor-feedback uses var(--bg), which
  in light theme resolves to #ffffff — the same as the task detail
  modal's --card panel — making the comment box invisible.
- ChatView CSS is now imported eagerly from App.tsx so it bundles
  into the main CSS file. Previously the lazy ChatView JS chunk
  loaded its CSS via an async <link> tag, producing a brief flash
  of unstyled chat UI on first render.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 20:43:21 -07:00
gsxdsm
aa11e93cee fix(dashboard): chat mobile single-pane, comment-box theme, ChatView FOUC
- ChatView mobile: add @media (max-width: 768px) so the session-list
  sidebar goes full-width and the thread is hidden when the sidebar
  is visible (and vice-versa). The component already had the state,
  back-button (ChevronLeft), and visibility toggling — only the CSS
  was missing.
- TaskComments compose/edit textareas now use var(--surface) for
  background. The base .spec-editor-feedback uses var(--bg), which
  in light theme resolves to #ffffff — the same as the task detail
  modal's --card panel — making the comment box invisible.
- ChatView CSS is now imported eagerly from App.tsx so it bundles
  into the main CSS file. Previously the lazy ChatView JS chunk
  loaded its CSS via an async <link> tag, producing a brief flash
  of unstyled chat UI on first render.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 20:35:58 -07:00
gsxdsm
53896cdc63 fix(dashboard): repair AgentsView mobile header and reorder actions
The AgentsView mobile header was wrapping onto two rows because
DocumentsView.css carried leaked agents-view-* rules forcing
flex-wrap: wrap on the header/controls/title and width: 100% on the
primary-actions group. Removed those stale overrides so AgentsView's
own mobile rules can take effect.

Header changes:
- Drop the Refresh button.
- Reorder: Bot icon, view-toggle, then primary-actions on the far right
  (Controls icon left of New Agent +). Controls is now icon-only with
  no text label on every viewport.
- Bump the Bot icon to 24px (was 20) and give its mobile container a
  32x32 footprint to align with the view-toggle pill.
- Pin primary-actions to the right via space-between on the controls
  flex container; everything stays on one row at <=320px.

Layout: move AgentMetricsBar (stats cards) above the agent collection
so stats sit on top of the list/board/tree/org views.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 20:28:13 -07:00
gsxdsm
2a8ca04d95 feat(cli): TUI grid threshold, narrow agents view, new-task popup, browser launch
- Lower the multi-panel grid threshold from 100×24 to 80×20 so standard
  terminals render the full layout instead of falling back to single-pane.
- Agents view stacks list above detail when cols < 80; Tab focus model
  preserved.
- Board view: press [n] to open a centered create-task popup. Title is
  edited via ink-text-input; submit calls TaskStore.createTask, refresh
  the task list on success.
- System panel: shows the tokenized auth URL alongside the token, says
  "no auth" plainly when --no-auth was used, and prompts with
  "Press [Enter] to open in browser". Enter (while System is focused)
  spawns the platform's URL opener (open / start / xdg-open).
- Tighten log-row layout: each row is now a single Text with inline
  spans + fixed-width prefix slot so columns align and the highlight
  bar spans the full row.
- Default status section is "logs" so the live feed is what users see
  when they run `fn`.
- Drop the ASCII circle brand mark from the splash; FUSION block
  letters + tagline + spinner only, pinned to the top-left.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 20:07:41 -07:00
gsxdsm
314f8acccc feat(cli): default focus to logs panel and tighten splash + log row layout
- Default activeSection is now "logs" so the live log feed is what the
  user sees first when running `fn`.
- Splash drops the ASCII circle brand mark; only the FUSION block-letter
  word and tagline remain. The splash always pins to the top-left rather
  than centering, so it doesn't shift on resize. Compact terminals show
  plain "FUSION" text instead of block letters.
- Log rows render as a single Text with inline-colored spans (marker,
  timestamp, level, prefix, message). Prefix sits in a fixed 14-char
  slot so the message column aligns across rows, and the selection
  highlight now spans the full row width.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 19:46:48 -07:00
gsxdsm
58c7d88a3f docs: document CSS extraction, test consolidation, and TUI merge
- AGENTS.md: per-component CSS layout, loadAllAppCss test helper, ESLint
  guardrail, lazy view list + Suspense pattern; __tests__/ convention;
  TUI now invoked via fn (no separate @fusion/tui package).
- docs/contributing.md: Dashboard CSS organization + test layout sections;
  workspace package table reflects fn CLI bundling the TUI.
- docs/architecture.md: CSS architecture subsection; updated CLI/TUI
  references to packages/cli/src/commands/dashboard-tui/.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 19:43:00 -07:00
gsxdsm
74ebd238e8 chore: add pi-claude-cli + plugin-sdk __tests__/ dirs (test consolidation)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 19:39:20 -07:00
gsxdsm
d13aec45e6 chore: consolidate remaining tests into __tests__/ dirs and refactor mocks
Mostly mechanical cleanup left over from the earlier test-consolidation pass:
- Update import paths to ../../ for mocks now that test files moved deeper
- Simplify mock setup (drop usePluginUiSlots inline mock, etc.)
- Move engine ipc + runtimes tests into __tests__/ subdirs
- Move dashboard utils tests into __tests__/ subdir
- Refresh fusion-plugin-hermes-runtime/dist artifacts

build-exe.test.ts: spawn-import fix from a parallel branch (resolved during
worktree merge of the CSS extraction work).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 19:39:20 -07:00
gsxdsm
c3d1716fdd refactor(dashboard): split monolithic styles.css into per-component files
Split app/styles.css from ~40k lines down to ~4.5k. Created 56 co-located
component CSS files in app/components/, each imported by its owning .tsx.
The remainder of styles.css holds genuinely global rules (design tokens,
.btn/.card/.modal/.form-input primitives, cross-component @media overrides).

- Lazy-load 13 heavy views (AgentsView, RoadmapsView, NodesView, etc.) via
  React.lazy + Suspense; prefetch all chunks on idle so first navigation is
  instant. Initial JS bundle: 1.58 MB → 1.16 MB (-26%). Initial CSS bundle:
  635 kB → 471 kB (-26%); the rest splits into 13 per-view chunks.

- Add app/test/cssFixture.ts exposing loadAllAppCss() + loadAllAppCssBaseOnly()
  so CSS regression tests load the full per-component bundle (mirroring Vite
  source order). Migrate 30+ tests off direct readFileSync('../styles.css').

- Enable test.css: { include: [/.+/] } in vitest.config.ts so component CSS
  imports actually inject styles in jsdom (fixes getComputedStyle assertions).

- Add ESLint rule (no-restricted-syntax) banning direct styles.css reads in
  dashboard test files; points at loadAllAppCss() instead.

- Restore lost utility classes (.text-muted, .text-secondary, .text-dim,
  .form-input) and rescue dropped chat tool-call rules into QuickChatFAB.css.

- Mobile fixes along the way: scroll containment for view containers
  (min-height:0 + -webkit-overflow-scrolling), QuickChatFAB full-screen on
  mobile (with safe-area-inset for iOS home bar), AgentsView single-row
  header layout, ActivityLogModal close button on right, model-combobox
  z-index above the mobile quick-chat panel.

- Bug fix: SkillsView toggle was display:none which hid the input from the
  accessibility tree; replaced with the visually-hidden pattern so screen
  readers + getByRole still find the checkbox.

- Bug fix: standalone Delete button in TaskDetailModal for triage-column
  tasks (Actions dropdown is hidden in triage state, so previously no way
  to delete a freshly-created task without status change first).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 19:39:20 -07:00
gsxdsm
a9436f63d4 fix(cli): make logs panel cursor visible and auto-follow tail
The selection arrow on the dashboard TUI's logs panel was rendering for
the wrong row: the list was reversed (newest-first) but selectedLogIndex
is in chronological order, and the viewport was anchored to the buffer's
tail. On a long log buffer the cursor pointed at the oldest entry, which
was always offscreen.

Switch the display to chronological order (oldest top, newest bottom —
matches tail/less/k9s), compute the viewport from the cursor so the
highlighted row is always visible, and surface "↑ N more / ↓ N more"
hints when entries scroll out of view. addLog now keeps the cursor
pinned to the tail when the user hasn't navigated away, so live logs
follow the latest event.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 19:39:20 -07:00
gsxdsm
a283ef2b79 feat(cli): merge @fusion/tui into fn dashboard with Ink TUI
Replace the legacy ANSI-based DashboardTUI with an Ink/React rewrite
under packages/cli/src/commands/dashboard-tui/, delete the standalone
@fusion/tui package, and make `fn` (no args) launch the dashboard.

The new TUI keeps the existing 5-panel status mode (system, logs,
utilities, stats, settings) but adds an interactive mode (b/a/g) with
three views: a kanban board with project picker and per-task detail,
an agents list+detail with state management, and a settings editor.
Bordered focus-aware panels, solid-background help overlay, static
all-blue FUSION splash that adapts to small terminals. DashboardTUI
and DashboardLogSink public API are unchanged so dashboard.ts only
needed import-path updates plus interactiveData/loadingStatus wiring.

Also adds zod to @fusion/dashboard to satisfy a peer dep introduced
by pi-coding-agent 0.70.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 19:39:20 -07:00
gsxdsm
bdcb048e20 chore: consolidate test files into __tests__/ dirs and clean stray engine artifacts
- Move all co-located *.test.* files into sibling __tests__/ directories so the
  layout is consistent across packages (159 renames + content-rewrite moves).
  Updates relative imports, vi.mock specifiers, and __dirname/import.meta.url
  path resolutions where tests read fixtures from disk.
- Drop tracked tsc-emit alongside engine .ts sources (auth-storage/logger/
  skill-resolver/context-limit-detector/pi.{js,d.ts,*.map}). These were
  accidentally committed in a merge and the stale pi.js was masking a real
  test-mock vs source mismatch (tests imported "../pi.js" and vite preferred
  the stale build over pi.ts).
- Add packages/engine/.gitignore to block future src/*.{js,d.ts,map}.
- Refactor plugin pi-module seams (openclaw/paperclip/hermes) to ESM-import
  createFnAgent / promptWithFallback / describeModel from @fusion/engine
  instead of require()-ing packages/engine/src/pi.js. Adds @fusion/engine to
  the two plugin package.jsons that were missing it; exports describeModel
  from the engine public API.
- Fix engine test mocks now that they run against current pi.ts: add
  ModelRegistry.create static to mocks in pi.test.ts and pi-create-fn-agent
  .test.ts; switch three boundary-result toEqual assertions to toMatchObject
  so the new content/isError fields don't trip exact-match comparison.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 19:39:20 -07:00
Fusion
5ca0df728f fix(FN-2484): limit progress visibility to active execution
- Gate unified progress display in ListView to tasks that are executing or in-progress
- Gate TaskCard progress bar and steps toggle behind the same active-execution visibility rule
- Update ListView and mobile list-card tests to cover hidden progress for non-executing todo tasks
- Expand TaskCard and mobile board tests to assert progress/toggle visibility for executing vs queued states
2026-04-24 19:39:20 -07:00
Fusion
bb75e73e24 feat(FN-2339): add configurable ntfy server support
- Add ntfy base URL to global settings schema/types with persistence coverage and regression tests
- Extend dashboard settings API/routes and Settings modal UI to edit and save a custom ntfy server
- Update notifier runtime to use configured ntfy base URL when sending notifications
- Document the new setting and include a changeset for @runfusion/fusion
2026-04-24 19:39:20 -07:00
gsxdsm
3c0fcda6ad Update README.md 2026-04-24 17:09:42 -07:00
gsxdsm
31a0c7cc07 Delete .planning directory 2026-04-24 17:02:11 -07:00
gsxdsm
613ac75b81 chore(release): v0.1.3
Version bump via changesets.
2026-04-24 15:35:21 -07:00
gsxdsm
38d73031f8 chore: track .changeset/ instead of ignoring it
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 15:34:40 -07:00
gsxdsm
39deb052f4 fix(cli): bundle pi-claude-cli into published package
Drops @fusion/pi-claude-cli from runtime dependencies so
`pnpm install -g @runfusion/fusion` no longer 404s on the unpublishable
private workspace package. The pi extension is staged into
dist/pi-claude-cli/ at build time and resolved relative to the running
module, with the workspace require.resolve preserved as a dev fallback.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 15:34:06 -07:00
Fusion
a6dd98957c feat(FN-2389): add task priority controls across dashboard flows
- Wire priority through task creation and update API payloads for dashboard clients
- Add priority selection to TaskForm, New Task modal, and inline create card with default/reset behavior
- Support priority editing and display in Task Detail modal plus non-default priority badges on task cards
- Extend dashboard styles and component tests to cover priority selectors, rendering, and mobile behavior
2026-04-24 15:00:43 -07:00
Fusion
269b13ecb1 feat(FN-2481): align Hermes runtime artifacts and compatibility coverage
- Regenerate fusion-plugin-hermes-runtime build artifacts and manifest metadata for runtime packaging
- Refactor Hermes runtime source into dedicated pi-module, runtime-adapter, and shared type modules
- Expand Hermes and engine plugin-runner tests to validate cross-runtime compatibility behavior
- Update getting-started, settings reference, and Hermes README docs to reflect the current runtime integration guidance
2026-04-24 14:44:04 -07:00
Fusion
db6bf20db4 feat(FN-2382): finalize presets-first new-agent tab flow
- Split New Agent step-0 into Presets and Custom tabs with presets-first default behavior
- Refine tab layout and styling in dashboard styles to address review feedback and improve responsive presentation
- Expand NewAgentDialog and AgentsView tests to cover tab switching flows and related route behavior
- Update agent documentation to reflect the revised new-agent dialog flow
2026-04-24 14:31:12 -07:00
Fusion
ad46484126 feat(engine): allow read of sibling task PROMPT.md/task.json for deps
Agents working on a task that depends on other tasks (e.g. documentation
alignment tasks needing the sibling tasks' specs) were repeatedly
rejected by the worktree boundary when reading .fusion/tasks/FN-NNNN/PROMPT.md,
which also contributed to the malformed-tool-result crash we just fixed.

Add a read-only exception to isWorktreeAllowedPath: the read/glob/grep
tools may access .fusion/tasks/*/PROMPT.md and .fusion/tasks/*/task.json
at the project root. Writes and bash cwd remain restricted.

Update the system-prompt boundary docs (executor.ts) so agents know the
exception exists and stop burning turns re-trying rejected reads.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 14:24:34 -07:00
gsxdsm
5eba97fc80 fix(engine): shape worktree-boundary rejections as proper tool results
wrapToolsWithBoundary returned a bare {ok:false,error} object when an
agent tried to read/write/bash outside the worktree. pi-coding-agent
wraps tool returns into a toolResult message whose content field it
expects to be an array of content blocks; a bare object leaves content
undefined, which later crashes downstream with "Cannot read properties
of undefined (reading 'filter')" — the failure we've been chasing on
FN-2479 and similar.

Return { content:[{type:"text",text:...}], isError:true, ok:false, error:... }
so pi records a valid toolResult block while existing callers that
inspect .ok / .error still work.

Diagnostic evidence: transcript tail for the failing task showed three
consecutive `read` toolResults with content=array(len=0) (normalized
from undefined by our earlier guard) immediately before the assistant
message with stopReason="error".

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 14:00:34 -07:00
Fusion
563e3a2361 feat(FN-2380): add fast mode toggle to quick entry
- Add QuickEntryBox state for a Fast toggle and include executionMode="fast" in create payloads when enabled
- Reset fast mode during form reset flows so follow-up creates default back to standard mode
- Add regression tests for fast toggle pressed state and payload behavior across Enter and Save submission paths
- Add tests ensuring fast mode is omitted when inactive and cleared after successful create, Plan, and Subtask flows
2026-04-24 13:51:55 -07:00
gsxdsm
92d90f799a diag(engine): log transcript tail when pi state error looks like null access
pi-coding-agent swallows its own exceptions onto session.state.errorMessage
without preserving a stack, so fusion sees only "Cannot read properties
of undefined (reading 'filter')" with no indication which message is
malformed. When promptSessionAndCheck rethrows an error that matches the
generic TypeError shape, dump the last few state.messages (role, content
type, toolName, stopReason) so the offending message can be identified
next run.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 13:49:40 -07:00
Fusion
7beb103db8 feat(FN-2378): add popup controls and reorder agents view sections
- Move secondary agent sections below the collection area to support the agents-first layout
- Add a popup controls panel in AgentsView with updated styling and responsive behavior
- Expand dashboard tests to cover popup controls, CSS classes, and the new mobile/section ordering
- Resolve engine logger lint issues and update agents documentation/README for the new layout
2026-04-24 13:34:23 -07:00
gsxdsm
1bbc5fcce7 fix(merger,dashboard): handle empty-squash commitSha to keep card and Changes tab in sync
When a branch contained commits already on main (duplicate cherry-picks),
the merger's local squash collapsed to an empty commit. The merger then
recorded that empty commit's SHA on mergeDetails.commitSha. The actual
content landed later on main as a different SHA via PR merge, but the
task kept pointing at the orphaned empty commit.

Symptom: TaskCard showed "N files changed" (falling back to
task.modifiedFiles), but the Changes tab in the modal showed nothing
because the API hit `git diff sha^..sha` on the empty commit and
returned no files.

Two fixes:

1. merger.ts: detect empty squash commits and skip storing commitSha,
   logging clearly. recoverInterruptedMergingTasks → findLandedTaskCommit
   already exists to backfill the right SHA when the real commit lands;
   a missing commitSha is a known fallback path the UI already handles.

2. TaskChangesTab.tsx: when the API returns no files for a done task,
   fall back to task.modifiedFiles (paths only, no patches) with a clear
   note. Mirrors the existing 3-tier fallback in TaskCard.tsx:1090-1124
   so card and modal always agree.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 13:31:34 -07:00
gsxdsm
e788400a7d fix(engine): normalize user message content to survive malformed session state
pi-coding-agent's _getUserMessageText calls content.filter(...) on user
messages; if a message lands in state.messages with content === undefined
(string or array expected), the library throws
"Cannot read properties of undefined (reading 'filter')", which gets
caught and stored on session.state.errorMessage and rethrown without a
stack. Fusion's existing message-content guard already normalized
assistant/toolResult messages — extend it to user messages as well, and
sweep state.messages once when the guard is installed so content loaded
from a session file is repaired before the first event fires.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 13:30:27 -07:00
Fusion
0d8d69b2bf feat(FN-2482): add severity filtering for dashboard logs
- Add severity filter controls to the dashboard log viewer and apply filtering to rendered entries
- Extend dashboard-tui log streaming command to support severity filtering in output
- Update and stabilize dashboard and settings modal tests covering filter behavior and loading readiness
- Document the severity filter usage in CLI reference and add a changeset for @runfusion/fusion
2026-04-24 13:29:48 -07:00
Fusion
c3a7669f11 fix: restore claude-cli work silently dropped by FN-2370 squash and add audit guardrails
The FN-2370 auto-resolved squash (de5dd6f7d) reverted three commits' worth
of refinements to the claude-cli provider because the branch contained
rebased duplicates of commits already on main. The auto-resolver picked
the older side line-by-line and dropped the newer.

Restored:
- /api/models filter logic (was inverted; emptied every model picker)
- Claude Opus 4.7 catalog entry in pi-claude-cli
- Provider card status text and toast messages (no longer claim a restart
  is needed — the extension is always-loaded now)
- POST /api/auth/claude-cli returns restartRequired: false

Prevention:
- Regression tests on the /api/models useClaudeCli filter
- scripts/audit-squash-merge.mjs flags duplicate-cherry-pick risk and
  touched-file overlap on any squash commit
- AGENTS.md documents the rebase-before-squash rule and requires the
  merging agent to run the audit and triage every flagged item itself

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 13:24:49 -07:00
Fusion
4272ce10bf feat(FN-2442): improve workflow step fallback labeling
- Resolve workflow step names by preferring lookup entries, then workflow result names, then raw step IDs
- Shorten task card workflow phase badges to concise pre-merge/post-merge labels while preserving tooltip context
- Expand TaskCard coverage for phase badge rendering and workflow name fallback behavior when lookup values are blank or missing
2026-04-24 13:03:40 -07:00
Fusion
c413407157 fix(FN-2365): tokenize Git Manager light-theme surface styles
- Add semantic neutral surface tier tokens in root and light-theme blocks for subtle, muted, emphasis, and stronger hover states
- Replace Git Manager light-theme hardcoded neutral rgba backgrounds with surface tokens and token-based color-mix accent states
- Add dedicated Git Manager theme styling tests to guard token usage and prevent regressions to raw rgba values
- Expand status color/theme token tests and stabilize SettingsModal navigation selectors used by related test coverage
2026-04-24 12:57:24 -07:00
gsxdsm
1fe1b395be feat(engine): capture stack traces at task-failure catch sites
Add formatError() helper that extracts both message and full stack from
unknown caught values, and use it at every status:"failed" catch site in
executor, agent-heartbeat, and triage. Stack traces now land in
store.logEntry outcome (persisted to task.log/activityLog) and in stderr
logger output, so failures like "Cannot read properties of undefined
(reading 'filter')" can be diagnosed without re-running.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 12:54:13 -07:00
gsxdsm
4e488f5b0e docs(agents): add test-culling criteria to testing guidance
Codify the patterns that the recent dashboard test trim removed so
they don't grow back: CSS-class permutation tests, field-presence
tests that duplicate payload-roundtrip tests, React.memo tautologies,
and structural CSS assertions. Keep FN-linked regression tests,
integration tests, and lean core/engine units unconditionally.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 12:37:29 -07:00
Fusion
f2ead692e5 feat(FN-2451): enforce connected-first AI onboarding provider order
- Rework ModelOnboardingModal provider rendering to use a single curated ordering across OAuth, API key, and Claude CLI cards while excluding GitHub
- Prioritize already-connected providers first, then apply deterministic family/alias sorting so Claude CLI appears near the top and provider controls remain intact
- Add onboarding-flow regression tests that verify provider ordering and preserved per-provider interactions after reordering
- Update dashboard styling to use token-based onboarding error text class and adjust related tests for resilient async and accessible section targeting
2026-04-24 12:35:48 -07:00
Fusion
e3070ec9bd fix(FN-2336): standardize surface-hover token contract
- Define --surface-hover in base styles and in theme-data.css across color theme variants
- Normalize dashboard styles and tests to rely on the explicit hover token instead of ad-hoc fallbacks
- Add regression coverage in status-colors-theme tests to verify surface-hover is present for all themes/modes
- Update TaskDetailModal test expectations to align with the new hover token behavior
2026-04-24 12:27:08 -07:00
gsxdsm
33853f9f2b test(dashboard): consolidate AppModals ScheduledTasksModal forwarding
The 4 ScheduledTasksModal projectId tests differed only by the
projectId input value. Collapse the three "schedulesOpen=true" cases
into an it.each; keep the "schedulesOpen=false" test as a discrete
guard since it asserts the opposite (modal not invoked).

Also hoist the huge per-call prop fixture to a shared commonProps
object so the test body is readable.

367 → 314 LOC (-53). 7 tests still passing.

Phase 3 note: this file's 22 vi.mock() count looked alarming but 17
are just `() => null` shims to stop child-modal dep trees from
mounting — legitimate infrastructure for a glue-component test, not
tautological mocking. No further trim warranted here.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 12:18:10 -07:00