Skip to content

fix: checkout switches apply visibly and worktrees no longer trip the parent-repo gate - #177

Merged
badcuban merged 6 commits into
mainfrom
worktree-checkout-switch-visibility
Aug 23, 2026
Merged

badcuban merged 6 commits into
mainfrom
worktree-checkout-switch-visibility

Conversation

@badcuban

Copy link
Copy Markdown
Collaborator

Problem

Switching a stopped thread from a worktree back to the project root looked like it did nothing: the thread's worktree setting updated, but the stale session-scoped effectiveCwd kept shadowing it in every panel until the next session started. Separately, several switch actions returned silently when blocked, and the parent-repository safety gate could false-fire on healthy checkouts whenever git's resolved root differed from the configured cwd by symlinks, casing, or separators.

Fix

  • A checkout move on a thread with no live session now emits a thread.effective-cwd-set clear from the decider, so the in-memory fold, the SQLite projection, and the web store all repoint immediately. A session stopping with a queued switch completes it the same way. Branch-only updates carry the unchanged path and never clear a valid cwd-follow value.
  • No switch path fails silently anymore: blocked actions surface their reason, failed dispatches roll back the optimistic update (including the session it cleared) with an error toast, and rollbacks are keyed to their dispatch so a stale rejection cannot overwrite newer state.
  • The parent-repository relation check realpaths both sides and treats only a genuine parent directory as "ancestor"; residual divergence reads as gated rather than silently bypassing safety. The empty changes list now says when the parent-repo confirmation is what's pausing it.

Three review rounds (two independent); server 1944 / web 2285 / browser 484 tests green.

…e parent-repo gate

Three linked fixes for a switch that looked like it did nothing:

- A stopped thread's checkout switch now applies immediately: the stale
  session-scoped effectiveCwd stops shadowing the new worktree the moment
  thread.meta-updated lands with no live session. Running sessions keep
  the queued-switch behavior and chip.
- Refused source-control actions say why. The parent-repository safety
  gate now toasts its reason from switch/create/merge handlers instead of
  silently returning, a failed thread.meta.update dispatch reports itself
  in the panel and the composer branch control, and the changes empty
  state explains when the gate is what emptied it.
- repositoryRootRelation only reads "ancestor" for a genuine parent
  directory. Plain string comparison used to raise the confirmation gate
  for healthy checkouts whenever git's resolved root differed from the
  configured cwd by symlinks, casing, or separators.
…dings

Reworks the previous commit after independent review:

- The stale-effectiveCwd clearing moves from a special case in one
  projector fold into the decider, which now emits a real
  thread.effective-cwd-set(null) alongside thread.meta-updated when a
  stopped thread's worktree changes. Every projection (in-memory fold,
  SQLite pipeline, web store) already folds that event, so snapshots,
  live streams, and restarts all agree.
- The same rule applies when a session stops with a queued switch:
  thread.session.set(stopped) in a checkout that differs from the
  thread's configured one also emits the clear. A session stopping in
  its own checkout keeps its cwd-follow effectiveCwd.
- resolveRepositoryRootRelation resolves symlinks before comparing and
  treats any residual divergence as "ancestor": a false gate is a
  visible banner, a false "same" would silently expose parent-repository
  actions (e.g. a cwd symlinked into a subdirectory of a larger repo).
- Failed thread.meta.update dispatches now roll back the optimistic
  branch/worktree update instead of leaving the UI lying, and a missing
  environment connection reports itself instead of silently skipping the
  dispatch.
Follow-up to the second review pass:

- The decider only emits the effective-cwd clear when the worktree path
  actually changes; branch-only updates carry the unchanged path and
  must not wipe a valid cwd-follow value.
- Rolling back a failed checkout dispatch now restores the full snapshot
  through a new store restoreThreadCheckout action, including the
  session the optimistic setThreadBranch cleared.
- Rollbacks are keyed to their dispatch: a stale rejection can no longer
  overwrite state a newer dispatch already replaced.
@vercel

vercel Bot commented Aug 23, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
threadlines Skipped Skipped Aug 23, 2026 4:14am

Request Review

@github-actions github-actions Bot added vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. size:L labels Aug 23, 2026
@badcuban
badcuban merged commit f886fe0 into main Aug 23, 2026
15 checks passed
@badcuban
badcuban deleted the worktree-checkout-switch-visibility branch August 23, 2026 04:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L 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