Merges a batch of five commits delivering dashboard performance optimizations (new composite indexes on `tasks.column`/`tasks.updatedAt` and debounced detail fetches), hardened chat SSE parsing with new test coverage, restored typecheck gates, split-pane task-link navigation (FN-3191), and research
Fusion-Task-Id: FN-3243
Group the planning model selector and depth controls under a collapsible
"Advanced planning settings" disclosure (defaultOpen) in the Planning Mode
modal. Adds a defaultOpen prop to OnboardingDisclosure and tightens the
node.capabilities readonly typing in NodeDetailModal.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Speeds up task-list load and interaction for projects with 100+ tasks.
- Migration 59 adds idxTasksColumn and idxTasksUpdatedAt; listTasks()
filters by "column" on every board load and SSE/refresh paths sort by
updatedAt, so each query was previously a full table scan plus a temp
B-tree sort.
- Debounce handleEmbeddedOpenDetail in ListView so rapid keyboard/mouse
navigation no longer fires a heavy /tasks/:id (log + comments) per
selection; stale-target requests short-circuit.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Fixes task link navigation in ListView so embedded links continue working when the split pane is active, including a new test case for FN-3191.
Fusion-Task-Id: FN-3191
Merged branch delivers Docker node onboarding with a new dedicated modal for guided setup, auto-installs the dependency graph plugin in CLI daemon and dashboard processes, hardens the research view with broader test coverage and a stuck-submit fix, plumbs planning draft text and model selection end-
Fusion-Task-Id: FN-3016
updateDraft preserved summarizedFor whenever the plan text was
unchanged, but the modal re-syncs the draft on model selection
changes too. Result: blur under model A, switch to model B without
editing text, click Start, and the running session kept model A's
title. The skip condition in startExistingSession only checks
text equality, not model identity.
Now updateDraft preserves summarizedFor only when both the plan
text AND the persisted modelProvider/modelId are unchanged, so a
model switch implicitly invalidates the marker and the start path
re-summarizes under whatever model the user just picked.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Closes the loop on the blur/close summarize work. Previously the sidebar
always rendered `session.preview` for drafts, so the model call fired by
blur/close updated `session.title` to a value the user never saw, and
then `startExistingSession` summarized again on Start Planning even when
blur had already produced a title for the exact same text.
The sidebar now shows the summarized title once available and falls
back to the inputPayload preview only while the title is still the
placeholder. summarizeDraftTitle persists the plan text it summarized
from via a new `markDraftSummarized` store method, and updateDraft
preserves that marker only while the persisted text still matches it
(text edits invalidate it automatically). startExistingSession reads
the marker and skips its own summarize when the persisted plan equals
what was summarized last, eliminating the redundant model call.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The planning modal's draft sync only updated SQLite, never the in-memory
session, so Start Planning ran the agent against whatever fragment was
typed before the first 500ms debounce and silently dropped everything
after. Drafts that survived a backend restart couldn't be started at
all (lazy rebuild was missing for the start path), reopened drafts left
the textarea empty, and the model override the user picked at create
time was forgotten on reopen — Start Planning would silently use the
project default instead.
This change rehydrates initialPlan from SQLite in startExistingSession,
flushes the latest text from the request body before reading it back,
lazy-rebuilds the in-memory session from the persisted row when needed,
and round-trips the model override through inputPayload so reopen
restores it into modal state. Sidebar drafts now render a derived
preview (from inputPayload, never persisted as title) so multiple
drafts are distinguishable while editing, and summarizeDraftTitle fires
on textarea blur and modal close — gated on status rather than title
content so blur-then-edit still refreshes rather than locking to the
first snapshot.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
`TriageProcessor.finalizeApprovedTask` (added in FN-3056) called
`store.updateTask({title})` while the task was still in column='triage',
which triggered a pre-existing regen path in `TaskStore.updateTask` that
overwrote the agent's just-written specification with the bootstrap stub
(`# {id}: {title}\n\n{description}\n`). Tasks shipped to `todo` (and
through to `done`) with empty 70–200 byte specs while the executor only
saw the original one-line user description. The same regen path also
silently dropped `## Review Level` / `## Frontend UX Criteria` and any
section outside a fixed whitelist whenever a non-triage task's title or
description was edited.
Replaces the regen with wrapper-shape-exact stub detection (compare to
the bytes `createTask` would have written for the pre-update title and
description) plus surgical edits for real specs: title changes splice
only the leading `# ...` heading, description changes rewrite only the
body of `## Mission`, and every other section is preserved verbatim.
`finalizeApprovedTask` now applies the prompt-declared title after
`moveTask("todo")` as defense in depth. New regression tests cover real
specs surviving title sync, long bootstrap stubs, stubs whose body
contains `##` markdown or `**Created:**` text, and the end-to-end
triage finalize sequence on a real `TaskStore`.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Add managed Docker node API client/routes, onboarding hook, and NodesView integration with new onboarding modal UX
- Refine Add Node, mobile nav, agent/node detail, and onboarding-related component styling/interaction flows
- Expand dashboard/core/cli test coverage for onboarding flows, node routes, insights persistence, and mobile regressions
- Add CLI/plugin packaging updates including bundled dependency-graph install and plugin schema initialization support
Fusion-Task-Id: FN-3108
Merged the three-step FN-3239 feature: auto-installing the dependency graph plugin on first run across the `daemon`, `dashboard`, and `serve` commands, with the core logic centralized in `bundled-plugin-install.ts`, bundling configuration in `tsup.config.ts`, and a documentation file for the default
Fusion-Task-Id: FN-3239
This merge lands FN-3015's insights feature: it adds persistence-backed coverage for the insights routes, expands the insight store lifecycle tests, closes route validation regressions, and fixes workspace verification gates. The implementation in `insights-routes.ts` was streamlined, and a new rese
Fusion-Task-Id: FN-3015
- Add mobile nav graph entry wiring in MobileNavBar to expose the graph view on mobile
- Update mobile navigation behavior to preserve existing routes while including the restored graph path
- Adjust MobileNavBar tests to cover the restored graph entry and expected rendering/interaction
Fusion-Task-Id: FN-3235
Merge brings FN-3067 UX revisions across onboarding components, including reduced ModelOnboardingModal complexity, new fallback styling for PluginSlot, and polish to OnboardingDisclosure, OnboardingResumeCard, and PostOnboardingRecommendations with minor updates to PlanningModeModal and SettingsModa
Fusion-Task-Id: FN-3067
This merge brings in model fallback notifications, async planning draft sync, mobile scroll container fixes, plugin schema initialization hooks, a new ResearchTaskActionModal, viewport-conditional mission split layout, and test infrastructure improvements including split vitest environments and a cr
Fusion-Task-Id: FN-3206
This merge consolidates a large batch of feature work, test infrastructure improvements, and performance fixes. Dashboard highlights include a viewport-conditional mission split layout for mobile (FN-3130), async planning draft sync (FN-3229), the ResearchTaskActionModal with model-fallback notifica
Fusion-Task-Id: FN-3130
This merge delivers three coordinated features: it adds a notification when task execution falls back to default AI model settings, scopes ResearchView's safe-area padding to mobile only, and hardens the pi-autoresearch analysis with a target matrix and preflight checks. The changes touch the core s
Fusion-Task-Id: FN-3012
Merges FN-3008 to add a "fallback-used" notification system: the engine now emits events when AI model fallbacks are triggered, dispatches notifications via ntfy/webhook providers, surfaces a session banner in the dashboard, and exposes a settings toggle to enable or disable these alerts.
Fusion-Task-Id: FN-3008
The merge brings in a fix (FN-2998) that scopes the bottom safe-area padding in ResearchView to mobile devices only, correcting an over-application of the padding on desktop or larger screens.
Fusion-Task-Id: FN-2998
Merged two changes: a new feature (FN-3229) that syncs planning drafts to the server in real-time while the user types, adding a PlanningModeModal component, a new ai-session-store for managing session state, and a dashboard API route to persist drafts; plus a performance fix replacing a blocking `e
Fusion-Task-Id: FN-3229
The dashboard backend (Express routes, services) lives under src/ and is pure
Node logic, but every test was paying for jsdom env init plus full CSS-import
processing. Split via environmentMatchGlobs so app/** keeps jsdom (React UI)
and src/** runs in node by default. Two src tests opt back into jsdom:
- status-bar.test.ts uses window.matchMedia.
- proxy-routes.test.ts asserts on DOMException constructor.name, which jsdom
reports as "AbortError" but node reports as "DOMException".
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Split packages/dashboard/app/__tests__/api.test.ts (~6.4k lines) into per-area files (auth, git, missions, projects, settings, tasks), and src/__tests__/routes.test.ts (~20k lines) into per-area files (agents, auth, automation, git, github, planning, settings, system, tasks, tasks-ops). The monolith files were dominating wall-clock for the dashboard suite under file-parallel execution.
Also document in packages/core/vitest.config.ts why the core suite cannot move to "threads": vitest-setup gates per-worker cwd on isMainThread (false in worker_threads, so isolation breaks), and setup-test-isolation writes process.env.HOME unconditionally (threads share env, so concurrent workers race).
Drop an unused execFileSync import from scripts/test-with-lock.mjs.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
`git diff --no-index` exits 1 when files differ — that's the success case for synthetic untracked-file diffs, not an error. Use spawn directly so we can resolve on exit 0 or 1, independent of how callers (or test mocks) wrap execFile / promisify.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- scripts/test-with-lock.mjs: pnpm test:locked acquires an exclusive
~/.fusion/test.lock (O_EXLOCK) before running pnpm test, so several
Claude Code worktrees on the same machine serialize their vitest
fan-out instead of saturating the box. Prints the holding PID and
worktree path while waiting.
- dev-server-manager test children now park on stdin instead of
setInterval so manager.shutdown() can deterministically exit them
via stdin close, preventing orphaned node processes outliving the
test run.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Introduce a draft lifecycle for planning sessions: typing into the
PlanningModeModal textarea now creates a server-side draft after a
300ms debounce, persisted with status='draft' so the user's in-flight
plan survives modal close/reopen and shows up immediately in the
session list.
- planning.ts: new createDraftSession path; persistSession status
union widened to include 'draft'; Session gains an explicit title
field so subsequent updates don't clobber it.
- register-planning-subtask-routes.ts: wires the createPlanningDraft
POST endpoint that the modal calls on debounce.
- ai-session-store.ts: tracks the draft status across queries so the
session list and locks behave the same as any active session.
- legacy.ts: client wrapper for createPlanningDraft.
- PlanningModeModal styling, tests, and ModalReentry coverage updated
for the new flow.
- docs/architecture.md notes the expanded ai_sessions.status lifecycle.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- pnpm build now excludes @fusion/desktop and @fusion/mobile by default
(recursive build still available as pnpm build:all). Saves time on
workspace-wide builds that don't need the native shells.
- Hoist the per-package max-worker computation into a shared
packages/core/src/__test-utils__/vitest-workers.ts util. Every
vitest.config.ts now calls computeMaxWorkers(), which honors
VITEST_MAX_WORKERS, FUSION_TEST_TOTAL_WORKERS, and a per-config
defaultCap, clamped to cpus-1.
- pnpm test sets VITEST_MAX_WORKERS=2 so the workspace run keeps total
fan-out modest with --workspace-concurrency=2.
- Switch dashboard vitest pool from forks to threads so jsdom/React
suites share a V8 heap instead of duplicating ~500MB per worker.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Inline object passed to setPlanningSessions widened type/status to string,
breaking AiSessionSummary's literal union. Hoist the draft into a typed
variable so the literals narrow correctly.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Refreshes the built-in model catalog feeding ModelRegistry with the
latest entries upstream pi-ai generates from models.dev (Anthropic,
OpenAI, Codex, Bedrock, etc.). No Fusion-side API changes; upgrades
applied in cli, dashboard, and engine package.json plus lockfile.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two sync hot paths were stalling the dashboard event loop on a periodic
timer:
1. `pgrep -f vitest` ran via `execSync` in `getVitestProcessIds`
(`/api/system-stats`, `/api/kill-vitest`) and `killVitestProcesses`
(TUI memory-pressure check). On a busy machine pgrep walking the
process table can take 100ms+; execSync blocks the entire Node event
loop for that duration, so every concurrent dashboard request hangs
while pgrep runs. The TUI variant fired on every memory-pressure tick
(~2s when over threshold), the dashboard variant fired on every
system-stats poll (5s while the modal is open). Both now use execFile
with a callback wrapped in a Promise.
2. `discoverDashboardPiExtensions` (called from 3 /api/settings/pi-
extensions routes) did 6+ blocking existsSync/readFileSync calls per
invocation across legacy and fusion settings paths. Converted to
fs.promises.readFile/access and parallelized via Promise.all.
Behavior preserved:
- TUI memory-pressure detection still works (sync os.totalmem path
unchanged); auto-kill still fires on threshold breach.
- The `lastAutoKillAt` 30s re-fire gate is set before the async kill
starts, so concurrent ticks can't trigger duplicate kills.
- system-stats still polls every 5s while the modal is open and still
returns vitestProcessCount.
- All 7 system-stats / kill-vitest tests pass; all 6 settings/pi-
extensions tests pass. Test mocks updated for the (err, stdout, stderr)
callback signature.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Reduce default ListView columns to title, status, and column while keeping optional columns user-configurable
- Move task ID into the title cell and add supporting ListView styles for stacked ID/title presentation
- Add explicit Bulk Edit mode toggle so selection checkboxes and bulk toolbar only appear when enabled
- Update ListView tests for new defaults, sorting behavior, conditional bulk-edit controls, and column span expectations
Fusion-Task-Id: FN-3221
Three merger fallback commit paths (auto-resolve-all-conflicts,
-X theirs/ours side strategy, AI-agent-didn't-commit) hard-coded
`feat(FN-XXXX): merge fusion/fn-xxxx` as the subject and never used
the AI subject summarizer. Route them through buildDeterministicMergeMessage
so they pick up aiSubject when available.
When the AI subject summarizer returns null, derive the subject from
the branch's first step commit (with conventional-commit prefix
stripped, plus `(+N more)` for multi-commit branches) instead of the
bare `merge <branch>` template.
Bump DEFAULT_COMMIT_SUBJECT_TIMEOUT_MS 15s → 30s so slow-first-token
providers complete instead of silently falling back.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This merge delivers the full draft planning feature (FN-3106) — API routes for planning subtasks, the AiSessionStore backend for draft sessions, a draft planning API client, auto-creation of planning drafts from user input, and corresponding UI polish in PlanningModeModal with accessibility-focused
Fusion-Task-Id: FN-3106
This merge brings FN-3155's plugin `createAiSession` API (types, DI hooks, engine adapter, context wiring, docs, and tests), FN-3056's task title sanitization, and FN-3129's tokenized footer and mobile initialization for MissionManager. It also adds CentralCore Docker node management, a new AddNodeM
Fusion-Task-Id: FN-3155
Aligns updateSchedule validation with createSchedule so step-based
automations (auto-summarize, memory dreams) can sync at startup
without hitting "Command cannot be empty".
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This merge ships several feature and infrastructure improvements across the codebase. Task title validation is strengthened in triage with stricter rejection of malformed titles and preference for prompt-declared titles (FN-3056), while task creation now preserves priority settings (FN-3210). The Mi
Fusion-Task-Id: FN-3056
On high-core dev machines (e.g. 28-core M-series), per-package vitest
defaulted to cpus().length - 1 workers (27), and `pnpm test` ran 4
workspace packages concurrently — easily 100+ vitest threads per sweep.
When the dashboard had agents running tests, 2+ concurrent sweeps would
saturate CPU and the UI became sluggish.
- Cap defaultMaxWorkers to min(6, cpus()-1) in cli/dashboard/desktop/
mobile/plugin-sdk vitest configs (engine and core were already capped)
- Lower root `pnpm test` workspace-concurrency 4 → 2
- VITEST_MAX_WORKERS override still respected for explicit fast runs
Worst-case fan-out drops from ~108 workers to ~12 per `pnpm test`.
CI runners with fewer cores are unaffected (cap doesn't bind).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The merge adds token-based CSS styling for the MissionManager footer and mobile initialization, with 17 lines of new CSS rules in `MissionManager.css` and 4 lines of adjustments in `MissionManager.tsx`. This improves theme consistency and mobile responsiveness for the mission management component.
Fusion-Task-Id: FN-3129
This merge lands the managed Docker nodes feature (FN-3107) end-to-end: core schema migration for nodes with types, CRUD operations in CentralCore, and dashboard UI (AddNodeModal, NodeDetailModal) with accessibility fixes and tokenized CSS. Also included are manual PR linking (FN-3202), plugin schem
Fusion-Task-Id: FN-3107
The nested action row kept "Create Single Task" and "Break into Tasks"
side-by-side on small screens, pushing the primary button offscreen.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Add scheduler logic to create dependency-linked follow-up tasks when actionable PR feedback remains after a PR is merged or closed
- Update engine runtime/project wiring to support manual PR create flows and branch publish behavior for fusion/<task-id>
- Add dashboard route coverage for manual PR creation/linking behavior and corresponding engine/runtime tests
- Document manual PR branch conventions and follow-up behavior in task management and dashboard docs
Fusion-Task-Id: FN-3202
This merge introduces three major features: task reset now preserves existing progress with explicit user confirmation (FN-3185), the engine's soft-pause/unpause behavior is restored and documented (FN-3201), and the plugin system gains schema hook aggregation with new navigation placement and icons
Fusion-Task-Id: FN-3154
This merge implements a "preserve progress" option for task resets across the system. FN-3185 adds a `preserveProgress` flag to `moveTask` that keeps status/history when resetting tasks back to `todo`, with required explicit confirmation dialogs to prevent accidental resets. The feature is wired thr
Fusion-Task-Id: FN-3185
The merge restores the engine's unpause merge sweep logic in `project-engine.ts` and documents the soft-pause merge resume behavior across architecture and settings reference docs, with associated test coverage added.
Fusion-Task-Id: FN-3201
- Add TodoModal component and styling, and mount it through AppModals
- Remove dedicated Todos view routing and drive todos access through modal state/actions
- Update Header and MobileNavBar interactions plus modal manager/view-state hooks for modal flow
- Expand dashboard tests to cover modal rendering, open/close behavior, and updated app/header expectations
Fusion-Task-Id: FN-3104