feat(dashboard): expose mergeAdvanceAutoSync in the project settings modal
Adds the missing form control for the auto-sync mode introduced by the merger hook. Lives next to Direct merge commit routing / Integration worktree inside the merge-strategy panel and only renders when mergeStrategy === "direct". Three options with descriptions cribbed from docs/settings-reference.md: - Stash + fast-forward (default): preserve local edits across the snap - Fast-forward only: skip dirty worktrees, surface the banner instead - Off: legacy behavior; project root stays stale until manual pull Value is normalized through normalizeMergeAdvanceAutoSyncMode on both the merged-settings and scoped-settings load paths so a missing/invalid stored value cleanly falls back to the default. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
13
.changeset/merge-advance-auto-sync-settings-ui.md
Normal file
13
.changeset/merge-advance-auto-sync-settings-ui.md
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
---
|
||||||
|
"@fusion/dashboard": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
feat(dashboard): expose `mergeAdvanceAutoSync` in the project settings modal
|
||||||
|
|
||||||
|
Adds the missing form control for the auto-sync mode introduced by the merger hook. Lives next to the existing Direct merge commit routing / Integration worktree controls inside the merge-strategy panel and only renders when `mergeStrategy === "direct"`. Three options with the same labels and descriptions as `docs/settings-reference.md`:
|
||||||
|
|
||||||
|
- **Stash + fast-forward (default)** — preserve local edits across the auto-snap
|
||||||
|
- **Fast-forward only** — skip dirty worktrees, surface the banner instead
|
||||||
|
- **Off** — legacy behavior, project root stays stale until manual pull
|
||||||
|
|
||||||
|
Value is normalized through `normalizeMergeAdvanceAutoSyncMode` on both the merged-settings and scoped-settings load paths so a missing/invalid stored value cleanly falls back to the default without spamming validation errors.
|
||||||
@@ -11,6 +11,7 @@ import {
|
|||||||
resolveProjectDefaultModel,
|
resolveProjectDefaultModel,
|
||||||
resolveTitleSummarizerSettingsModel,
|
resolveTitleSummarizerSettingsModel,
|
||||||
normalizeMergeIntegrationWorktreeMode,
|
normalizeMergeIntegrationWorktreeMode,
|
||||||
|
normalizeMergeAdvanceAutoSyncMode,
|
||||||
} from "@fusion/core";
|
} from "@fusion/core";
|
||||||
import type { AgentPermissionPolicyRules, Settings, GlobalSettings, ThemeMode, ColorTheme, ModelPreset, NtfyNotificationEvent, AgentPromptsConfig, ThinkingLevel } from "@fusion/core";
|
import type { AgentPermissionPolicyRules, Settings, GlobalSettings, ThemeMode, ColorTheme, ModelPreset, NtfyNotificationEvent, AgentPromptsConfig, ThinkingLevel } from "@fusion/core";
|
||||||
import { fetchSettings, fetchSettingsByScope, updateSettings, updateGlobalSettings, fetchAuthStatus, loginProvider, logoutProvider, cancelProviderLogin, saveApiKey, clearApiKey, fetchModels, testNotification, fetchBackups, createBackup, exportSettings, importSettings, fetchMemoryFile, fetchMemoryFiles, saveMemoryFile, compactMemory, fetchGlobalConcurrency, updateGlobalConcurrency, installQmd, testMemoryRetrieval, triggerMemoryDreams, fetchGitRemotes, fetchGitRemotesDetailed, fetchProjects, fetchDashboardHealth, checkForUpdates, fetchRemoteSettings, updateRemoteSettings, fetchRemoteStatus, installCloudflared, startRemoteTunnel, stopRemoteTunnel, killExternalTunnel, regenerateRemotePersistentToken, generateShortLivedRemoteToken, fetchRemoteQr, fetchRemoteUrl, submitProviderManualCode } from "../api";
|
import { fetchSettings, fetchSettingsByScope, updateSettings, updateGlobalSettings, fetchAuthStatus, loginProvider, logoutProvider, cancelProviderLogin, saveApiKey, clearApiKey, fetchModels, testNotification, fetchBackups, createBackup, exportSettings, importSettings, fetchMemoryFile, fetchMemoryFiles, saveMemoryFile, compactMemory, fetchGlobalConcurrency, updateGlobalConcurrency, installQmd, testMemoryRetrieval, triggerMemoryDreams, fetchGitRemotes, fetchGitRemotesDetailed, fetchProjects, fetchDashboardHealth, checkForUpdates, fetchRemoteSettings, updateRemoteSettings, fetchRemoteStatus, installCloudflared, startRemoteTunnel, stopRemoteTunnel, killExternalTunnel, regenerateRemotePersistentToken, generateShortLivedRemoteToken, fetchRemoteQr, fetchRemoteUrl, submitProviderManualCode } from "../api";
|
||||||
@@ -443,6 +444,7 @@ export function SettingsModal({
|
|||||||
autoMerge: true,
|
autoMerge: true,
|
||||||
mergeStrategy: "direct",
|
mergeStrategy: "direct",
|
||||||
mergeIntegrationWorktree: "reuse-task-worktree",
|
mergeIntegrationWorktree: "reuse-task-worktree",
|
||||||
|
mergeAdvanceAutoSync: "stash-and-ff",
|
||||||
recycleWorktrees: false,
|
recycleWorktrees: false,
|
||||||
executorAllowSiblingBranchRename: false,
|
executorAllowSiblingBranchRename: false,
|
||||||
worktreeNaming: "random",
|
worktreeNaming: "random",
|
||||||
@@ -688,6 +690,7 @@ export function SettingsModal({
|
|||||||
const normalizedSettings = {
|
const normalizedSettings = {
|
||||||
...s,
|
...s,
|
||||||
mergeIntegrationWorktree: normalizeMergeIntegrationWorktreeMode(s.mergeIntegrationWorktree),
|
mergeIntegrationWorktree: normalizeMergeIntegrationWorktreeMode(s.mergeIntegrationWorktree),
|
||||||
|
mergeAdvanceAutoSync: normalizeMergeAdvanceAutoSyncMode(s.mergeAdvanceAutoSync),
|
||||||
};
|
};
|
||||||
setForm(normalizedSettings);
|
setForm(normalizedSettings);
|
||||||
setInitialValues(normalizedSettings); // Store initial values to detect explicit clears
|
setInitialValues(normalizedSettings); // Store initial values to detect explicit clears
|
||||||
@@ -699,6 +702,9 @@ export function SettingsModal({
|
|||||||
mergeIntegrationWorktree: scoped.project.mergeIntegrationWorktree === undefined
|
mergeIntegrationWorktree: scoped.project.mergeIntegrationWorktree === undefined
|
||||||
? undefined
|
? undefined
|
||||||
: normalizeMergeIntegrationWorktreeMode(scoped.project.mergeIntegrationWorktree),
|
: normalizeMergeIntegrationWorktreeMode(scoped.project.mergeIntegrationWorktree),
|
||||||
|
mergeAdvanceAutoSync: scoped.project.mergeAdvanceAutoSync === undefined
|
||||||
|
? undefined
|
||||||
|
: normalizeMergeAdvanceAutoSyncMode(scoped.project.mergeAdvanceAutoSync),
|
||||||
},
|
},
|
||||||
}); // Store initial scoped values for null-as-delete
|
}); // Store initial scoped values for null-as-delete
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
@@ -4673,6 +4679,40 @@ export function SettingsModal({
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
<div className="form-group">
|
||||||
|
<label htmlFor="mergeAdvanceAutoSync">Auto-sync project checkout after merge</label>
|
||||||
|
<select
|
||||||
|
id="mergeAdvanceAutoSync"
|
||||||
|
className="select"
|
||||||
|
value={form.mergeAdvanceAutoSync ?? "stash-and-ff"}
|
||||||
|
onChange={(e) =>
|
||||||
|
setForm((f) => ({
|
||||||
|
...f,
|
||||||
|
mergeAdvanceAutoSync: e.target.value as "off" | "ff-only" | "stash-and-ff",
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
data-testid="merge-advance-auto-sync-select"
|
||||||
|
>
|
||||||
|
<option value="stash-and-ff">Stash + fast-forward (default) — preserve local edits</option>
|
||||||
|
<option value="ff-only">Fast-forward only — skip dirty worktrees</option>
|
||||||
|
<option value="off">Off — leave the project root stale (legacy behavior)</option>
|
||||||
|
</select>
|
||||||
|
<details className="settings-option-details">
|
||||||
|
<summary>More details</summary>
|
||||||
|
<small>
|
||||||
|
After Fusion advances the integration branch ref, the merger can auto-sync other
|
||||||
|
worktrees still checked out on that branch (typically your project-root
|
||||||
|
checkout). <code>Stash + fast-forward</code> snapshots real local edits as a patch
|
||||||
|
against the previous tip, snaps the worktree to the new tip, then reapplies the
|
||||||
|
patch — untracked files that collide with newly-tracked paths are left in a temp
|
||||||
|
dir for manual recovery. <code>Fast-forward only</code> snaps cleanly when the
|
||||||
|
worktree has no edits and skips otherwise. <code>Off</code> is the legacy
|
||||||
|
behavior: <code>git status</code> in your project root will show the new commits
|
||||||
|
inverted as "staged changes" until you pull manually. Only applies to direct
|
||||||
|
merges.
|
||||||
|
</small>
|
||||||
|
</details>
|
||||||
|
</div>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{form.mergeStrategy === "pull-request" && (
|
{form.mergeStrategy === "pull-request" && (
|
||||||
|
|||||||
Reference in New Issue
Block a user