fix(dashboard): hand off cleanly from setup wizard to model onboarding

On a fresh install useAuthOnboarding's effect ran at mount before the
setup wizard's 500ms auto-open timer fired. The one-shot ref locked,
and the resolved fetch could either stack model onboarding on top of
the wizard or never re-trigger after the wizard closed.

- Gate the trigger on projectId being set so the wizard owns the
  bootstrap phase; the auth check only fires once a project exists.
- Re-check setupWizardOpen via a ref when the auth fetch resolves to
  avoid stacking onboarding on top of a wizard opened mid-fetch.
- Release the one-shot in that suppressed branch so the effect retries
  when the wizard closes.

Adds two regression tests: fresh-install handoff and mid-fetch wizard
suppression.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Fusion
2026-04-28 14:30:38 -07:00
committed by gsxdsm
parent 5e5020b78f
commit 5e6dbbb6f3
7 changed files with 300 additions and 109 deletions

View File

@@ -2770,6 +2770,22 @@ export function SettingsModal({
</small>
</div>
)}
<div className="form-group">
<label htmlFor="worktreeRebaseLocalBase" className="checkbox-label">
<input
id="worktreeRebaseLocalBase"
type="checkbox"
checked={form.worktreeRebaseLocalBase !== false}
onChange={(e) =>
setForm((f) => ({ ...f, worktreeRebaseLocalBase: e.target.checked }))
}
/>
Also rebase onto local default-branch HEAD
</label>
<small>
In addition to the remote rebase above, also rebase the task branch onto the local default-branch HEAD (rootDir). This catches sibling tasks that merged locally but haven't been pushed yet — without it, two concurrent tasks where one deletes code can have the other silently re-introduce it via the fallback strategy. Enabled by default; only disable if it causes issues with your workflow.
</small>
</div>
</>
);
case "commands":
@@ -2937,6 +2953,24 @@ export function SettingsModal({
</label>
<small>When enabled, lock files (package-lock.json, pnpm-lock.yaml, etc.) are resolved using 'ours' strategy, generated files (dist/*, *.gen.ts) using 'theirs' strategy, and trivial whitespace conflicts are auto-resolved without spawning an AI agent. Complex code conflicts still require AI review.</small>
</div>
<div className="form-group">
<label htmlFor="mergeConflictStrategy">Conflict Fallback Strategy</label>
<select
id="mergeConflictStrategy"
value={form.mergeConflictStrategy ?? "smart"}
onChange={(e) =>
setForm((f) => ({ ...f, mergeConflictStrategy: e.target.value as "smart" | "ai-only" | "prefer-main" | "abort" }))
}
>
<option value="smart">Smart — AI, then auto-resolve, then prefer task branch (default)</option>
<option value="ai-only">AI only — never silently pick a side</option>
<option value="prefer-main">Prefer main — fall back to keeping main's version</option>
<option value="abort">Abort — fail merge if AI can't resolve (manual review)</option>
</select>
<small>
What to do on the final merge attempt when AI and auto-resolve haven't fully resolved the conflict. <strong>Smart</strong> matches the historical behavior — the task branch wins on hard conflicts, which is fast but can resurrect code an earlier sibling task deleted. <strong>Prefer main</strong> sides with main instead, protecting just-merged sibling work. <strong>AI only</strong> retries the AI agent rather than silently choosing a side. <strong>Abort</strong> requires manual resolution for any conflict the AI can't handle.
</small>
</div>
<div className="form-group">
<label htmlFor="pushAfterMerge" className="checkbox-label">
<input