Skip to content

fix: tone down branch-mismatch banner#4416

Merged
t3dotgg merged 2 commits into
mainfrom
t3code/rethink-branch-warning
Jul 24, 2026
Merged

fix: tone down branch-mismatch banner#4416
t3dotgg merged 2 commits into
mainfrom
t3code/rethink-branch-warning

Conversation

@t3dotgg

@t3dotgg t3dotgg commented Jul 24, 2026

Copy link
Copy Markdown
Member

What Changed

Reworks the "You're on a different branch" composer banner added in #2284, which fired on thread open, wasn't dismissible, and alarmed users doing routine branch switching (user report).

image
  • One-line copy: Branch changed — was <branch>, info variant, git-branch icon. No description block; the consequence ("sending will continue on the current branch") lives in the branch chip's tooltip. The current branch is never named — it's already visible in the branch picker below.
  • Intent gating: the banner only renders when the composer is focused or has draft content. Passively reading an old thread shows nothing beyond the existing branch-picker warning tint. Once revealed for a mismatch it stays mounted through blur (no flicker).
  • Single action: "Move thread here" is removed — sending already rebinds the thread via resolveThreadMetadataUpdateForNextTurn, so the button duplicated the default outcome. The remaining action is Restore branch (checkout the thread's branch).
  • Dirty-tree confirm: restoring with uncommitted changes prompts a confirm dialog ("They'll carry over to the other branch, or block the switch if they conflict"); a clean tree switches immediately, no dialog.
  • Dismissible: ✕ dismisses the specific threadId + threadBranch + currentBranch mismatch for the session. If the checkout later moves to a different branch, the banner is eligible again.

Mismatch detection (resolveLocalCheckoutBranchMismatch) is unchanged, as is the branch-picker tint/popover from #2284.

Design doc with mockups and the reasoning (including why we deliberately did not auto-sync thread.branch on turn completion): https://3vj3y2v9zb4u.postplan.dev

Why

The mismatch is valid but the old presentation punished the most common innocent flow: finish issue A, checkout a branch for issue B, reopen thread A just to read context → big non-dismissible amber warning at thread-open time, when the only actual risk exists at send time.

Follow-up (separate PR)

Server-persisted dismissal ack ({threadBranch, checkoutBranch} on thread metadata) so dismissals hold across devices; session-scoped for now.

Testing

  • New unit tests for the intent gate, dismissal keying, and hysteresis (ChatView.logic.test.ts)
  • bun run typecheck, bun run lint, full apps/web unit suite (1495 tests) all pass
  • Visual treatment needs a human eyeball — automated browsers can't see authenticated views locally

Checklist

  • This PR is small and focused
  • I explained what changed and why
  • Before/after screenshots — needs a paired session; mockups are in the design doc above

🤖 Generated with Claude Code


Note

Low Risk
Composer UX and when the banner renders; restore still uses existing git checkout and thread metadata RPCs, with no change to mismatch detection itself.

Overview
Reworks the local checkout vs thread branch composer banner so it no longer appears on every thread open.

Intent gating: New shouldShowBranchMismatchBanner logic only shows the notice when the composer has draft content (or after it was already shown for the same mismatch, so clearing the draft does not flicker it away). Passive reading stays quiet aside from existing branch-picker cues.

Copy and actions: The banner switches from a warning with two repair buttons to an info line (Branch changed — was <thread branch>) with tooltip context. Move thread here is removed; Restore branch checks out the thread’s branch (with an AlertDialog when the working tree is dirty).

Dismissal: Per-mismatch keys (threadId:threadBranch:currentBranch) can be dismissed for the session via a module-level set in ChatView.logic.ts, with unit tests for gating, keys, and dismissal.

Reviewed by Cursor Bugbot for commit b40c0f1. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Reduce branch-mismatch banner intrusiveness in ChatView

  • The banner now only appears when the composer has draft content or was already shown for the current mismatch, and can be dismissed per session via a new in-memory Set in ChatView.logic.ts.
  • The 'Move thread here' action is removed; a single 'Restore branch' action switches checkout to the thread's branch, updating thread metadata if needed.
  • If uncommitted changes exist, an AlertDialog confirmation prompt appears before switching branches.
  • Banner variant changed from warning to info, and the icon updated to GitBranchIcon.

Macroscope summarized b40c0f1.

Summary by CodeRabbit

  • New Features

    • Added a clearer notification when a chat’s branch differs from the current branch.
    • Added a single Restore branch action to return the chat to its original branch.
    • Prompts for confirmation before restoring when there are uncommitted changes.
    • Keeps the notification visible while composing to prevent unexpected flicker.
    • Added branch details and explanatory tooltip text to the notification.
  • Bug Fixes

    • Branch mismatch notifications can now be dismissed for the current session and remain dismissed across chat view refreshes.

The "You're on a different branch" warning fired the moment a mismatched
thread was opened, wasn't dismissible, and treated routine branch
switching as an anomaly. Reworked per user feedback:

- Copy is now a one-line fragment: "Branch changed — was <branch>", info
  variant with a git-branch icon. The consequence ("sending will
  continue on the current branch") lives in the chip tooltip.
- Banner only renders on send intent (composer focused or has draft
  content); passive browsing shows nothing beyond the existing
  branch-picker tint. Once revealed it stays mounted for the current
  mismatch so it doesn't flicker on blur.
- "Move thread here" is gone — sending already rebinds the thread.
  The single action is "Restore branch", with a confirm dialog only
  when the working tree has uncommitted changes.
- Dismissible, keyed on threadId + both branches, session-scoped for
  now (server-persisted cross-device ack is a planned follow-up).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Branch mismatch handling now includes deterministic keys, session-scoped dismissal, focus-aware banner visibility, a single restore action, and confirmation before switching branches when uncommitted working-tree changes exist.

Changes

Branch mismatch handling

Layer / File(s) Summary
Mismatch rules and session dismissal
apps/web/src/components/ChatView.logic.ts, apps/web/src/components/ChatView.logic.test.ts
Adds mismatch key generation, banner visibility rules, session dismissal tracking, and tests for these behaviors.
Restore action orchestration
apps/web/src/components/ChatView.tsx
Replaces the prior repair state with restoration state and adds confirmation handling for working-tree changes.
Banner presentation and composer interaction
apps/web/src/components/ChatView.tsx
Displays the updated branch banner, adds branch metadata tooltip and dismissal behavior, and tracks composer focus intent.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

  • pingdotgg/t3code#49: Both changes connect composer focus behavior with branch-related UI flows.

Suggested reviewers: juliusmarminge, jakeleventhal, aidenybai

Sequence Diagram(s)

sequenceDiagram
  participant Composer
  participant ChatView
  participant AlertDialog
  participant BranchCheckout
  Composer->>ChatView: Select Restore branch
  ChatView->>AlertDialog: Request confirmation for working-tree changes
  AlertDialog->>ChatView: Confirm restore
  ChatView->>BranchCheckout: Switch to thread branch
Loading

Poem

I’m a rabbit with branches to mend,
A banner now helps the flow round the bend.
Focus it, show it; dismiss when you please,
Restore with care among working-tree leaves.
Hop, hop—the right branch is found!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly summarizes the main change: softening the branch-mismatch banner.
Description check ✅ Passed The description is mostly complete and covers what changed, why, testing, checklist items, and a screenshot for the UI update.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch t3code/rethink-branch-warning

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:L 100-499 changed lines (additions + deletions). labels Jul 24, 2026

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit f64a69a. Configure here.

Comment thread apps/web/src/components/ChatView.tsx
Comment thread apps/web/src/components/ChatView.tsx
@macroscopeapp

macroscopeapp Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Approved

UX refinement that reduces branch-mismatch banner visibility to only when the user is drafting a message. Changes are self-contained, well-tested, and don't alter core branch detection or switching functionality.

You can customize Macroscope's approvability policy. Learn more.

Cursor Bugbot caught two real gating bugs:

- ChatView autofocuses the composer on every thread open, so "composer
  focused" was always true and the banner showed during passive reading
  anyway. Draft content is now the only intent signal.
- revealedBranchMismatchKey was never cleared, so a mismatch that
  resolved and later recurred bypassed the gate. The key now resets
  whenever the active mismatch changes or resolves.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
apps/web/src/components/ChatView.tsx (1)

3835-3846: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider extracting composerHasDraftContent into ChatView.logic.ts.

Every other predicate in this feature (branchMismatchKey, shouldShowBranchMismatchBanner) is a pure, independently-tested helper in ChatView.logic.ts. This "has draft content" check is left inline in the component, breaking that pattern and making it untestable without mounting the component.

♻️ Suggested extraction
+// ChatView.logic.ts
+export function composerDraftHasContent(
+  draft: { prompt: string; images: unknown[]; terminalContexts: unknown[]; elementContexts: unknown[]; previewAnnotations: unknown[]; reviewComments: unknown[] } | undefined,
+): boolean {
+  return Boolean(
+    draft &&
+      (draft.prompt.trim().length > 0 ||
+        draft.images.length > 0 ||
+        draft.terminalContexts.length > 0 ||
+        draft.elementContexts.length > 0 ||
+        draft.previewAnnotations.length > 0 ||
+        draft.reviewComments.length > 0),
+  );
+}
-  const composerHasDraftContent = useComposerDraftStore((store) => {
-    const draft = store.getComposerDraft(composerDraftTarget);
-    return Boolean(
-      draft &&
-      (draft.prompt.trim().length > 0 ||
-        draft.images.length > 0 ||
-        draft.terminalContexts.length > 0 ||
-        draft.elementContexts.length > 0 ||
-        draft.previewAnnotations.length > 0 ||
-        draft.reviewComments.length > 0),
-    );
-  });
+  const composerHasDraftContent = useComposerDraftStore((store) =>
+    composerDraftHasContent(store.getComposerDraft(composerDraftTarget)),
+  );
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/web/src/components/ChatView.tsx` around lines 3835 - 3846, Extract the
draft-content predicate from the composerHasDraftContent selector in ChatView
into a pure helper in ChatView.logic.ts, alongside branchMismatchKey and
shouldShowBranchMismatchBanner. Have the selector delegate to that helper while
preserving all existing prompt, image, terminal-context, element-context,
preview-annotation, and review-comment checks, and make the helper independently
testable.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@apps/web/src/components/ChatView.tsx`:
- Around line 3835-3846: Extract the draft-content predicate from the
composerHasDraftContent selector in ChatView into a pure helper in
ChatView.logic.ts, alongside branchMismatchKey and
shouldShowBranchMismatchBanner. Have the selector delegate to that helper while
preserving all existing prompt, image, terminal-context, element-context,
preview-annotation, and review-comment checks, and make the helper independently
testable.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 85e733f5-156d-4036-8819-8f410da8678b

📥 Commits

Reviewing files that changed from the base of the PR and between 2f41c07 and f64a69a.

📒 Files selected for processing (3)
  • apps/web/src/components/ChatView.logic.test.ts
  • apps/web/src/components/ChatView.logic.ts
  • apps/web/src/components/ChatView.tsx

@t3dotgg t3dotgg changed the title Calm down the branch-mismatch banner fix: tone down branch-mismatch banner Jul 24, 2026
@t3dotgg
t3dotgg merged commit fc3f78f into main Jul 24, 2026
16 checks passed
@t3dotgg
t3dotgg deleted the t3code/rethink-branch-warning branch July 24, 2026 01:38
jetblk added a commit to jetblk/t3code that referenced this pull request Jul 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L 100-499 changed lines (additions + deletions). vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant