Prevent mission list descriptions from overflowing the stacked mobile layout and displacing adjacent controls.
- add a stacked-mobile MissionManager CSS rule that clamps mission description text to two lines
- apply the clamp to both the description container and nested markdown body content to preserve truncation behavior
- add a mobile CSS regression test that asserts the new clamp selectors and properties are present
Files changed:
packages/dashboard/app/components/MissionManager.css | 9 +++++++++
.../app/components/__tests__/MissionManager.mobile-css.test.ts | 9 +++++++++
2 files changed, 18 insertions(+)
Fusion-Task-Id: FN-5701
Fusion-Task-Lineage: b5f97fd3-7c9b-4c47-9165-0db3cf66c20f
Use the shared favorites hook so agent model pickers stay in sync with project favorites.
- Switch AgentDetailView config tab model favorites to use useFavorites instead of local settings update logic.
- Switch NewAgentDialog model favorites to the same shared favorites flow and remove duplicated toggle handlers.
- Extend AgentDetailView/NewAgentDialog tests and dropdown test helpers to exercise provider/model favorite toggles through the shared global settings path.
Files changed:
.../dashboard/app/components/AgentDetailView.tsx | 44 +++------------------
.../dashboard/app/components/NewAgentDialog.tsx | 45 +++-------------------
.../AgentDetailView.advanced-settings.test.tsx | 41 ++++++++++++++++++++
.../__tests__/AgentDetailView.test-helpers.ts | 25 +++++++++++-
.../components/__tests__/NewAgentDialog.test.tsx | 36 +++++++++++++++++
5 files changed, 111 insertions(+), 80 deletions(-)
Fusion-Task-Id: FN-5669
Fusion-Task-Lineage: 8ace8ea1-1c88-409c-a9ae-6b5deb391f46
Prevent mobile settings prose from splitting words mid-token while keeping long code snippets overflow-safe.
- add a CSS regression test that inspects the mobile media-query rules in SettingsModal.css
- switch mobile backup code wrapping to overflow-wrap:anywhere with normal word-break
- update mobile settings helper text to keep break-word wrapping with normal word-break
Files changed:
.../app/__tests__/settings-mobile-wrap.test.ts | 46 ++++++++++++++++++++++
.../dashboard/app/components/SettingsModal.css | 5 ++-
2 files changed, 49 insertions(+), 2 deletions(-)
Fusion-Task-Id: FN-5668
Fusion-Task-Lineage: eb66376e-a535-4d07-adfe-4a2d048b93a7
Enable mission target branch to be viewed and updated from the mission edit form.
- Add `baseBranch` to mission form state and initialize it when opening edit mode.
- Include trimmed `baseBranch` in mission PATCH updates.
- Add target branch input to both MissionManager edit form render paths.
- Add MissionManager tests for pre-filling and saving edited target branch values.
Files changed:
.../dashboard/app/components/MissionManager.tsx | 24 ++++++++
.../components/__tests__/MissionManager.test.tsx | 66 ++++++++++++++++++++++
2 files changed, 90 insertions(+)
Fusion-Task-Id: FN-5665
Fusion-Task-Lineage: b25cf171-b681-4dd8-b5f4-70f0e8714774
Replace mock-only goals behavior with live API-backed create/edit/archive flows in GoalsView.
- load goals from GET /api/goals when initial goals are not injected
- add inline create form that POSTs goals and handles active-goal cap 409 errors
- add per-goal edit form that PATCHes title/description changes
- add archive/unarchive actions wired to archive endpoints with inline error handling
- refresh GoalsView docs and expand component tests for loading, CRUD, cap errors, and status transitions
Files changed:
docs/dashboard-guide.md | 18 +-
packages/dashboard/app/components/GoalsView.css | 52 ++-
packages/dashboard/app/components/GoalsView.tsx | 402 +++++++++++++++++----
packages/dashboard/app/components/__tests__/GoalsView.test.tsx | 250 +++++++++++--
4 files changed, 600 insertions(+), 122 deletions(-)
Fusion-Task-Id: FN-5649
Fusion-Task-Lineage: b787c8e3-64c5-4243-ae84-0f20f12ea09e
- Commit message: the merge agent writes a concise change-summary subject (not
just the task title), and every landed squash carries board-association
trailers — `Fusion-Task-Id: <taskId>` plus the canonical lineage trailer when
the task has a lineageId — guaranteed via an idempotent amend
(ensureTaskTrailersOnHead) even if the agent omits them.
- Verify-and-fix: the merge prompt instructs the agent to run the project's
tests / type-check / lint after resolving the merge and fix any NEW failure
the merge introduced (not pre-existing breakage) before committing.
- Editable prompt: the AI merge agent's base persona is the editable "merger"
role prompt (Settings -> Prompts); the clean-room / verification / trailer
rules are always appended so a custom prompt can't drop them.
- Reviewer model: reviewer uses the project reviewer/validator model lane
(resolveValidatorSettingsModel); the bespoke merger.reviewerModel setting is
removed.
Changeset updated to cover all AI merger changes.
Tests: trailer present on landed commit (+ lineage when set), editable-prompt
incorporation, new-breakage verification wording.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Drop the bespoke `merger.reviewerModel` setting. The AI merge reviewer now
resolves its model via `resolveValidatorSettingsModel` — the same
reviewer/validator lane used elsewhere (project validator → global validator →
project default) — falling back to the merger model only if that lane is empty.
Removes the setting from the type, schema default, and the Settings UI.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
New default merge path (merger.mode="ai"), self-contained in merger-ai.ts and
dispatched from ProjectEngine.onMerge instead of the legacy aiMergeTask pipeline
(kept for merger.mode="deterministic").
Flow: clean-room detached worktree at the target branch tip → AI agent merges
the task branch + squashes (resolving conflicts) → fresh read-only AI reviewer
audits with corrective retries (blocking vs advisory; advisory lands, unfixable
correctness hard-fails via AiMergeBlockedError; fail-safe verdict parsing) →
land via `git merge --ff-only` when the checkout is on the target (else
update-ref CAS) → sync the local checkout (stash → ff → restore; AI reconciles
a conflicting restore and keeps the original edits in a backup stash;
un-stashable dirt advances the ref + warns) → finalize (delete task branch —
never the integration branch — task→done, remove temp worktree).
- Per-task target branch honored (falls back to the default integration
branch); local checkout synced only when on that target.
- Structurally immune to the dirty-clobber and stale-base/non-FF bug classes of
the legacy path (clean room + FF-by-construction).
- Progress surfaced on the task status pill + task log stream.
- Clear error when the target branch has no local ref.
Settings: merger.mode / merger.reviewerModel / merger.maxReviewPasses, surfaced
in Settings → Merge; legacy merge-mechanics settings hidden when AI mode is on.
Tests: merger-ai.test.ts (verdict parser, clean merge, blocking hard-fail,
advisory land, empty no-op, target-branch isolation, missing-target error,
landSquash clean/other-branch/dirty-restore/AI-resolved). Legacy
merge-orchestration tests pinned to deterministic mode.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Implements graceful suppression of transient `task.json` ENOENT errors in the executor, logging a suppression signal and surfacing a banner in the UI, with test coverage for both the executor behavior and notification service. Documentation in `docs/architecture.md` and a changeset for `@runfusion/f
Fusion-Task-Id: FN-5624
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Fusion-Task-Id: FN-5624
React 19 removed the global JSX namespace; matches the pattern already used in agentHealth.tsx.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Implements source issue delete handling in the task dashboard, including prompt-based user interaction for source-linked issues, new GitHub tracking state logic in `packages/dashboard/src/github-tracking-state.ts`, and corresponding test coverage, with documentation added to `docs/task-management.md
Fusion-Task-Id: FN-5618
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Fusion-Task-Id: FN-5618
Adds padding expansion to the PR notice block in PrPanel.css with corresponding test assertions to verify the token values, completing the FN-5619 styling pass.
Fusion-Task-Id: FN-5619
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Fusion-Task-Id: FN-5619
Added OpenAI Responses API as a new custom provider type, wiring `apiType: "responses"` through the core registry, engine routes, and dashboard UI with a dropdown selector; includes test coverage across the registry, routes, and component layers.
Fusion-Task-Id: FN-5601
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Fusion-Task-Id: FN-5601
This merge implements an OAuth relogin banner feature (FN-5595) that displays in the dashboard when OAuth tokens expire. The feature includes a new `OAuthReloginBanner` component with styling and tests, an OAuth validity logger in the engine for tracking token state, and corresponding API route inte
Fusion-Task-Id: FN-5595
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Fusion-Task-Id: FN-5595
When the user clicks 'Latest' while loadMoreMessages was in-flight,
the scroll restoration (which maintains position when older messages
are prepended) was undoing the explicit jump-to-bottom.
Fix: clear scrollRestoreSnapshotRef and set isUserScrollingRef=false
before anchorToBottom so the layout effect's restoration is skipped.
Multiple coordinated fixes for the perceived "dashboard takes forever to
load" complaint. Per-page-load HTTP requests drop from ~177 to ~101 and
duplicate per-project InProcessRuntime creation is eliminated.
- engine: shouldUseHybridExecutor no longer auto-enables for local-only
multi-project setups (set FUSION_HYBRID_EXECUTOR=1 to force). The
duplicate-runtime path was running self-healing twice per project and
contending on the same SQLite file. ProjectEngineManager already
handles N local projects with one InProcessRuntime each.
- dashboard cli: parallelized independent store inits, started
CentralCore.init early in background, ran plugin loading concurrently
with extension resolution. Sequenced SQLite store inits to avoid a
TOCTOU race in addColumnIfMissing migrations across TaskStore /
AutomationStore / PluginStore / AgentStore (all open the same
.fusion/fusion.db). Restored try/catch around HybridExecutor.initialize
and engineManager.ensureEngine so a paused or broken cwd project no
longer aborts dashboard startup.
- dashboard client: added in-flight request dedupe wrapped around the
top API offenders. /api/plugins/ui-slots drops from 17x to 1x per load.
dedupe.forceFresh redirects ALL in-flight waiters to receive the fresh
post-mutation response, not just the forcing caller. Generation
counters in useAgents and AgentListModal protect against slow polls
overwriting fresh state.
- dashboard SSE: agent event handler now debounces 250ms with a
trailing-edge guard so multi-agent activity bursts coalesce to at
most 2 refetches per burst.
- dashboard route: PATCH /api/projects/:id with isolationMode change
returns 503 with actionable guidance when HybridExecutor is
unavailable, instead of silently persisting a config the live runtime
won't honor.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a dedicated Secrets section to the SettingsModal navigation, removes the now-unused secrets footer callback wiring from App and AppModals, updates the related tests, and documents the secrets location in the dashboard guide.
Fusion-Task-Id: FN-5588
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Fusion-Task-Id: FN-5588
- Remove fetchAllMessagesInChat/fetchAllMessages helpers
- Keep limit:50 for initial load in useChat and useQuickChat
- Add IntersectionObserver sentinel at top of ChatView message list to
trigger loadMoreMessages() when user scrolls to the top
- Keep stale-session guards (activeSessionRef checks) from original PR
- Tests: revert assertions back to { limit: 50 }
Adds planning branch controls to the PlanningModeModal, extending the planning API and routes with supporting tests and documentation. The changeset bumps `@runfusion/fusion` as a minor release.
Fusion-Task-Id: FN-5585
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Fusion-Task-Id: FN-5585
Implements the allow-resurrection toggle for task deletion, letting users prevent deleted tasks from being automatically restored. Changes span the `ConfirmDialog` component, `TaskDetailModal`, and the `useConfirm` hook, with comprehensive test coverage across the dashboard API and UI layers.
Fusion-Task-Id: FN-5475
Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
Fusion-Task-Id: FN-5475
Three coupled fixes to make `pnpm test:full` exit cleanly when the local
`fn` dashboard is running:
1. scripts/check-test-isolation.mjs — replace timing-based "is the
engine writing?" heuristic with a deterministic check: if
`.fusion/engine.lock.lock/` exists (proper-lockfile's held-lock
marker), the dir is engine-active and auto-skipped from violation
reporting. The 2-second mutability probe is retained as a backstop
for dirs with another external writer but no live lock. Also adds
`engine.lock` / `engine.lock.lock/` to RUNTIME_IGNORE_PATTERNS so
a mid-test engine start/stop doesn't trip the signature compare.
2. packages/dashboard/.../__tests__/GitManagerModal.test.tsx — prune
the Status-panel Sync button + Recent-advances-events describe
blocks. Their UI was removed in 5d35b64bd ("remove duplicate
integration-advances UI") but the tests stayed and were timing
out at 1s each. The Remotes-panel Sync describe is kept because
the `remotes-sync-integration-tip-btn` still exists.
3. packages/engine/.../merge-reuse-task-worktree.slow.test.ts —
update the happy-path assertion to reflect 4c31e885b
("merger auto-syncs project-root checkout after ref advance").
Before that change, the merger's `update-ref` advance left the
project root's working tree stale, so `git status --porcelain`
would differ after the merge. With auto-sync, the new file is
tracked + clean at HEAD, so status doesn't change. Verify the
file actually landed via `git ls-files` instead.
After this, `pnpm test:full` exits 0 with the local dashboard running.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two cleanups in Git Manager → Status:
- Removed the duplicate "Sync local tip" button (gm-integration-actions)
and the second "Recent integration advances" list (gm-recent-advances)
that rendered above the highlighted block. Also dropped the dead
mergeAdvanceEvents state, fetcher, and SSE subscription that only
fed the deleted UI.
- Sync working tree is now pure-local. Added skipOriginFetch to
PullGitBranchOptions.integration (and the matching POST /api/git/pull
body field). When set, pullGitBranch skips tryFastForwardFromOrigin
entirely — the sequence is just auto-stash → git reset --hard
refs/heads/<integration> → restore stash. The Sync button passes
skipOriginFetch: true because the "N need action" recovery is for
catching the worktree up to a *local* merger ref-advance; touching
origin could silently pull in unrelated remote commits.
Help disclosure rewritten to reflect the pure-local behavior.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
`notice` is `events.find(...)` which returns `undefined` (not `null`)
when no match. `waitFor(() => expect(...).not.toBeNull())` exited
immediately because `undefined !== null` — the test never actually
waited for the api mock to resolve. Sometimes the followup assertions
happened to land after the events fetched (test passed by luck);
sometimes they ran while notice was still undefined and the assertions
failed.
Switched all five waitFor sites to `.toBeDefined()` so they actually
block on the events-fetch resolution.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Settings inheritance (high):
- Restored the value !== initialProjectValue gate on the non-model
project branch. Previously every effective/inherited project key was
persisted as an explicit override on every save.
Git Manager UI lie in remote-only mode:
- "Local <branch> vs origin" card now renders "no local tracking"
instead of a green "Synced" badge when integrationTipSource ===
"remote-only" (no local branch to compare).
- New dedicated "HEAD vs origin/<branch>" card surfaces a meaningful
distance in remote-only mode.
isIndexStale correctness:
- Walks up to 16 reflog entries so multi-hop misses (A→B→C without
sync) are detected; the prior check only consulted @{1}.
- Gated on isOnIntegrationBranch === true so a feature-branch worktree
whose HEAD happens to descend from <integration>@{1} no longer trips
the FN-INDEX-DESYNC warning.
Enumeration-failed events surfaced:
- collectRecentMergeAdvances pairs events with (taskId, newSha) when
both are present, falls back to taskId-only for early-failure events
(e.g. "enumeration-failed") that have neither path nor newSha. The
diagnostic outcome now surfaces on the matching advance instead of
being silently dropped.
aheadOfIntegration semantics no longer shift:
- Split into three distinct fields: aheadOfIntegration (HEAD vs local),
aheadOfIntegrationRemote (HEAD vs origin/<branch>),
aheadOfOriginIntegration (local vs origin). Consumers no longer have
to read integrationTipSource to know which comparison they got.
currentBranch failure no longer masks wrong-branch state:
- Distinguish "command threw" (transient git error) from "command
succeeded with empty stdout" (legitimate detached HEAD). New
currentBranchDetectionFailed field lets the UI surface "branch
detection unavailable" on a real failure.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Settings (data-loss):
- Non-model project keys now use null-as-delete instead of dropping
undefined via JSON.stringify, so clearing Integration branch (Use
dropdown / auto-detect) actually clears the persisted value.
isIndexStale (false-positive AND false-negative):
- Replaced the empty-worktree heuristic with a reflog-anchored check:
stale iff refs/heads/<integration>@{1} exists, HEAD is descendant of
it, and `git diff-index --cached <prevTip>` is empty.
Auto-sync attribution in collectRecentMergeAdvances:
- Match auto-sync events by (taskId, newSha) instead of taskId-only;
re-merged tasks no longer have older advances mislabeled with the
newest outcome.
- Compare worktreePath after realpathSync on both sides; macOS symlink
paths no longer cause permanent "needs action" false positives.
Extended path no longer 500s:
- Route wraps computeExtendedGitStatus in try/catch and falls back to
basic status on failure. Inner `branch --show-current` wrapped too
so detached HEAD / non-git rootDir doesn't throw.
Integration branch falls back to remote-only ref:
- When refs/heads/<branch> is missing, use refs/remotes/origin/<branch>
as the integration tip. New `integrationTipSource` field
("local"|"remote-only"|"missing") drives a UI badge.
Copy commit hash:
- Short-SHA copy is the default and matches what's displayed; a
separate "full" button copies the 40-char headSha. Previously the
single button silently copied the full SHA when extended was on.
Detached HEAD:
- isOnIntegrationBranch left undefined when currentBranch is empty so
the UI doesn't render "(not on <integration>)" against a
no-branch state.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>