- Exclude the 4 SettingsModal split files from the app backfill project
(greptile P1): the split dropped the bare "SettingsModal" entry from
qualityAppComponentTests, which had excluded the curated file from
backfill; without spreading qualityAppSettingsOnlyTests into
backfillAppExclude the split files matched the backfill `app/**` glob
and ran in two projects, doubling their wall-time. Now collected by
exactly one project (dashboard-app-quality-settings).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Reduce feedback-loop wall-time on the slowest dashboard test files
(FN-5048 velocity), behavior-preserving — no assertions, timeouts, or
worker counts changed.
- SettingsModal.test.tsx (231 tests, ~60.5s in one sequential worker)
split into 4 sibling files sharing SettingsModal.test-harness.tsx so
the settings project parallelizes them across workers (~60.5s -> ~25s
wall). Test bodies moved verbatim; static it() count preserved at 231.
vi.mock factories stay per-file and reference the harness-exported
mock fns; shared beforeEach/afterEach via installSettingsModalEnv().
- insights-routes.test.ts: lazily init the project-b store (only 1 of 24
tests needs it) instead of a second TaskStore.init() per test
(tests 5.78s -> 3.66s).
- evals-routes.test.ts: same lazy storeB pattern.
- vitest.config.ts: point the settings-only project at the 4 new files;
drop the bare "SettingsModal" entry from the component shards.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Remove the stale branch reattachment affordance from Task Detail while preserving self-healing as the recovery path.
- Remove unused rebind banner styles and mobile action-shell rules.
- Drop stale recoverBranchBinding mocks from TaskDetailModal tests.
- Add rendering coverage proving Task Detail and embedded content do not show reattachment affordances across missing, populated, workspace, and mobile branch states.
- Add a patch changeset for the published Fusion package.
Files changed:
.changeset/fn-6983-task-detail-rebind-warning.md | 7 ++
.../dashboard/app/components/TaskDetailModal.css | 42 +-----------
.../TaskDetailModal.create-pr-e2e.test.tsx | 1 -
.../TaskDetailModal.create-pr-integration.test.tsx | 1 -
.../__tests__/TaskDetailModal.rendering.test.tsx | 80 ++++++++++++++++++++++
.../__tests__/TaskDetailModal.test-helpers.ts | 1 -
6 files changed, 88 insertions(+), 44 deletions(-)
Fusion-Task-Id: FN-6983
Fusion-Task-Lineage: 1a45c38e-f7d0-4df7-94cf-7d1309758e0d
Ensure mobile Missions exits detail tabs back to the mission list before outer navigation.
- Track mobile detail visibility instead of selected mission IDs when adding navigation entries.
- Avoid duplicate history entries for Structure/Activity tab switches and mission refreshes.
- Cover browser back and visible Back button behavior across mobile tabs while desktop stays unchanged.
- Add a patch changeset for the published Fusion package.
Files changed:
.changeset/fn-6998-mobile-mission-back.md | 7 ++
.../dashboard/app/components/MissionManager.tsx | 35 +++++--
.../__tests__/MissionManager.swipe-back.test.tsx | 114 ++++++++++++++++++---
3 files changed, 131 insertions(+), 25 deletions(-)
Fusion-Task-Id: FN-6998
Fusion-Task-Lineage: d3dbf3b7-0195-498f-a57f-ed83c6a422e7
Fix 6 failing tests caused by intentional source changes that landed
without updating dependent test assertions:
- Core test-project: taskPrefix default changed from "FN" to undefined
(commit 800f845e1, derived from project name at runtime)
- Dashboard ScriptsModal.css: replace banned --text-primary with --text
- CLI package-config: update expected pi dep version ^0.79.1 -> ^0.79.9
- CLI skill-sync: document 4 new engine tools in engine-tools.md
- CLI version: update expected release:version script to include
run-ci-distill.mjs
- CLI bundled-plugin-freshness: rebuild stale dist directories
Add a GitHub issue and pull request picker that seeds new task prompts from detected remotes.
- Add remote-aware GitHub issue/PR selection to the New Task modal.
- Generate focused task descriptions for selected issues and pull requests while confirming before replacing user text.
- Style and document the compact picker and cover loading, errors, remote selection, and overwrite behavior in tests.
- Add a changeset for the published Fusion CLI package.
Files changed:
.changeset/fn-6946-github-reference-picker.md | 7 +
docs/dashboard-guide.md | 2 +
packages/dashboard/app/components/NewTaskModal.css | 53 ++++
packages/dashboard/app/components/NewTaskModal.tsx | 297 ++++++++++++++++++++-
.../app/components/__tests__/NewTaskModal.test.tsx | 199 +++++++++++++-
5 files changed, 555 insertions(+), 3 deletions(-)
Fusion-Task-Id: FN-6946
Fusion-Task-Lineage: e871827c-dfe5-4fc3-a525-c1161639d306
Mobile bottom navigation now keeps balanced horizontal spacing without disturbing existing viewport compensation.\n\n- Add tokenized inline padding to the fixed mobile nav bar while preserving ICB and safe-area behavior.\n- Cover symmetric side spacing in mobile nav CSS and component layout tests.\n- Add a patch changeset for the published CLI package.\n\nFiles changed:\n .changeset/fn-6999-mobile-nav-side-spacing.md | 7 +++++++\n packages/dashboard/app/__tests__/mobile-nav-bar-css.test.ts | 10 ++++++++++\n packages/dashboard/app/components/MobileNavBar.css | 5 +++++\n .../dashboard/app/components/__tests__/MobileNavBar.test.tsx | 8 ++++++++\n 4 files changed, 30 insertions(+)
Fusion-Task-Id: FN-6999
Fusion-Task-Lineage: 91bd2174-8e5d-4227-a035-ff621e309719
Reclaims task detail padding so inline diffs have more readable width on compact screens.
- Expand the compact Changes file list to consume detail-body padding without page overflow.
- Add mobile breakpoint coverage that matches the detail-body padding contract.
- Add regression tests for the phone-width inline diff surface and CSS rules.
- Add a patch changeset for the published Fusion CLI package.
Files changed:
.changeset/fn-6997-diff-panel-width.md | 7 ++
.../dashboard/app/components/TaskChangesTab.css | 20 ++++-
.../components/__tests__/TaskChangesTab.test.tsx | 98 +++++++++++++++++++++-
3 files changed, 121 insertions(+), 4 deletions(-)
Fusion-Task-Id: FN-6997
Fusion-Task-Lineage: a7f21c0d-89a8-430e-a84c-341a10af4a4a
Align the Quick Chat and Terminal footer launchers so they read as matching peer controls.
- Inherit the footer font and color contract for the Terminal footer launcher.
- Preserve usable hover, focus, padding, and scripts chevron behavior in the footer variant.
- Document the footer Quick Chat launcher placement and add release notes.
- Expand status bar tests to lock the shared footer launcher styling.
Files changed:
.changeset/fn-6959-footer-launcher-style.md | 7 ++
docs/dashboard-guide.md | 2 +-
.../dashboard/app/components/ExecutorStatusBar.css | 2 +-
.../dashboard/app/components/TerminalLauncher.css | 37 +++++++-
.../__tests__/ExecutorStatusBar.test.tsx | 105 +++++++++++++++++++--
5 files changed, 138 insertions(+), 15 deletions(-)
Fusion-Task-Id: FN-6959
Fusion-Task-Lineage: 539307cb-dc81-441f-8baf-8823826e2195
From the multi-agent /ce-code-review of PR #1749 (no P0/P1 correctness bugs; these
are perf, race-hardening, and convention fixes):
- P1 (perf/reliability): the workspace diff ran git subprocesses serially per
sub-repo AND per file — an N×M explosion with no aggregate cap. Add a bounded
mapWithConcurrency helper (order-preserving) and parallelize the per-file patch
loop (cap 8) and the per-sub-repo loop (cap 4). Deleted files still fetch their
patch (skipping it would drop deletes from /file-diffs and zero /diff stats).
- P2 (frontend race): GitManagerModal's workspace-detection could be clobbered by
a previous project's in-flight fetch on a rapid projectId switch / close-reopen.
Add a detectionGenerationRef guard — only the latest detection run may mutate
state; the effect cleanup bumps the generation to abandon superseded runs.
- P2 (DRY): reuse the existing parseStatusCode instead of re-inlining the
status-code mapping.
- P2 (convention): FNXC-tag the new functions/branches per CLAUDE.md.
- P3: extract DIFF_TIMEOUT_MS/FILE_DIFFS_TIMEOUT_MS constants, drop a dead
catch-assignment, note the done-fallback oldPath limitation.
Tests: order-preservation after parallelization; rapid-project-switch generation
guard (a stale workspace verdict must not suppress a new project's real error).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The Changes / Files-changed tab showed nothing for multi-repo workspace tasks:
the task-diff backend is single-repo throughout, and a workspace task has null
task.worktree/task.branch (its per-repo state lives in workspaceWorktrees), so
every path fell back to git-diff against the non-git workspace root → empty.
Backend (register-session-diff-routes): extract the single-repo per-worktree
detailed-diff into one shared helper (computeWorktreeDetailedFiles) and add a
workspace branch to BOTH /tasks/:id/diff and /tasks/:id/file-diffs that runs
before the single-repo logic: iterate sorted workspaceWorktrees, compute each
sub-repo's diff in its own live worktree against that repo's baseCommitSha (done
tasks fall back to the per-repo landed range in the sub-repo root), and aggregate
with `${repoRel}/`-prefixed paths. Single-repo behavior is byte-for-byte
preserved (renamed→modified fold retained; 58 existing diff-route tests pass).
Frontend: TaskChangesTab takes an isWorkspace prop and no longer shows the
single-repo "No worktree available" empty state for workspace tasks;
TaskDetailModal passes isWorkspace={isWorkspaceTask(workingTask)}.
Tests: backend aggregation (repo-prefixed paths + stats) and frontend rendering
of workspace changes instead of the empty state.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Opening the Git Manager on a workspace project rendered the sub-repo dropdown
correctly but ALSO toasted "Not a git repository". On open the section fetch
fires immediately with no repoPath (selectedRepo unresolved), hitting the non-git
browse-only workspace root; fetchWorkspaceRepos resolves a tick later and the
fetch re-runs against a real sub-repo. We now track workspace detection in a ref
and suppress that one benign root-race error (no repoPath + "Not a git
repository" while detection is pending or has confirmed a workspace). A genuinely
broken non-workspace project still surfaces the error: once detection settles as
non-workspace, a single guarded re-fetch re-surfaces it (no redundant fetch in
the common non-workspace path, preserving existing call-count expectations).
Tests: add the missing fetchWorkspaceRepos api mock (pre-existing gap that broke
the whole GitManagerModal suite at import), plus a positive (workspace → no
toast) and negative-control (non-workspace broken → toast) regression.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- dashboard: remove the "Branch needs reattachment" banner. It fired for any
in-review task with a null singular task.branch — the NORMAL state for a
workspace task (attachment is per-sub-repo worktrees in workspaceWorktrees), so
it was a permanent false positive. Genuine lost bindings are already reattached
automatically by self-healing's reconcileInReviewBranchRebind (event-driven on
move-to-in-review + sweep), so no manual user action is needed. Delete the
now-obsolete rebind-banner test + its registry entry.
- engine/self-healing: reconcileInReviewBranchRebind now explicitly skips
workspace tasks (never rebind candidates — their fusion/<id> branches live in
the sub-repos, not the non-git browse root; null root branch is healthy).
- engine/merger-ai: pre-merge prune treats an absent ai-merge search root (ENOENT)
as "nothing to prune" instead of warning on every workspace merge.
- test: add ToggleRight to the TaskDetailModal lucide mock (pre-existing gap from
FN-6880 that broke the whole suite at import).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- core/store: include workspaceWorktrees in the slim and activity-log-limited
SELECT lists (rowToTask reads it, but the explicit column lists omitted it, so
slim/limited reads dropped the field and could misclassify workspace tasks);
add regression tests for both read surfaces
- dashboard/register-git-github: validate caller-supplied repoPath in resolveGitDir
via isPathWithin containment check (path-traversal hardening for all git
endpoints); make loadWorkspaceConfig a static @fusion/core import per AGENTS.md
- dashboard/legacy: preserve repoPath in the string-form pullBranch overload
- dashboard/GitManagerModal: revalidate selectedRepo against the fetched repo list
so a stale selection can't persist across project switches
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Add GET /api/git/workspace-repos endpoint returning sub-repo list
- Add resolveGitDir() helper: resolves repoPath query param to sub-repo dir
- Update all 34 git endpoints to use resolveGitDir for workspace targeting
- Add repoPath param to 30+ frontend git API functions
- GitManagerModal: auto-detect workspace repos on mount, show repo selector
dropdown at top of sidebar, pass selected repo to all git API calls
- Auto-select first repo when workspace mode is detected
- Repo change triggers data refetch via gitRepoPath dependency
- Align dashboard prefix validation to 1-5 chars (was 1-10) matching CLI cap
- Fix distributed-task-id.ts fallback from KB to FN (3 occurrences)
- Move taskPrefix/defaultWorkflowId persistence outside interactive-only block
so non-interactive CLI registration also gets defaults
- Wrap both TaskStore lifecycles in try/finally to guarantee close() on error
- Add POST /api/projects/detect-workspace endpoint for sub-repo scanning
- Modify POST /api/projects to accept workspaceMode + taskPrefix params
- SetupWizardModal: auto-detect sub-repos when path is entered, show
workspace mode checkbox with detected repo count, add task prefix field
auto-derived from project name
- Wire workspaceMode and taskPrefix through registration API call
- Close first TaskStore before creating second in interactive registration (P1)
- Revert defaultWorkflowId default to undefined; set explicitly in onboarding only (P1)
- Add alpha-only filter + 2-char min to interactive prefix input (P2)
- Move suggestTaskPrefix to @fusion/core, share between CLI and dashboard (P2)
- Fix suggestTaskPrefix JSDoc to match implementation (P2)
- Fix workspace detection: change workspaceMode default from false to
undefined so isWorkspaceModeExplicitlyDisabled no longer blocks
auto-detection on fresh projects (config.json was being written with
workspaceMode:false during store.init(), causing the guard to skip
detection before it ever ran)
- Derive task prefix from project name (first 2-4 chars) instead of
hardcoded 'FN' as the suggested default
- Default workflow is now builtin:coding instead of undefined
- CLI registerProjectInteractive: onboarding prompt for task prefix
confirmation after project name
- Dashboard POST /api/projects: auto-derive prefix and set default
workflow for new registrations
Address the 6 findings from the focused re-review of the hardening commit
(test-only; no production changes):
- useDashboardHealth: drop the false-confidence unmount-state assertion
(React 19 silently drops setState on unmounted components, so it pinned
nothing) and document the cancelled-guard as a React-19-untestable-via-state
invariant; keep the meaningful mount-fetch assertions.
- sseSplitIntegration: assert the onReconnect split explicitly
(mailbox onReconnect wired, approval onReconnect undefined); prove the
mailbox approval:requested handler actually refreshes (fetchUnreadCount
called); replace the magic 2x microtask drain with a deterministic waitFor.
- Extract the duplicated msg()/message() SSE-event helper to a shared
sseTestHelpers.ts (per-file vi.mock factories stay — vitest hoists them).
- useChatUnreadBadge: add a cross-project filter case for
chat:room:message:added.
Full hook suite green (1366 tests); App.test.tsx unchanged; typecheck + eslint clean.