Skip to content

WIP: Better defaults for worktree vs local checkout - #3957

Closed
t3dotgg wants to merge 4 commits into
mainfrom
t3code/plan-ux-review
Closed

WIP: Better defaults for worktree vs local checkout#3957
t3dotgg wants to merge 4 commits into
mainfrom
t3code/plan-ux-review

Conversation

@t3dotgg

@t3dotgg t3dotgg commented Jul 14, 2026

Copy link
Copy Markdown
Member

Implements the new-thread workspace model (plan): every new thread starts from the resolved default workspace mode instead of inheriting the previous thread's choices, and worktree threads pin a fresh remote base with visible provenance.

Surface-derived defaults

  • New deriveThreadEnvModeFromSurface server setting (default on). The attention test decides the default: surfaces attached to a visible local checkout (desktop app, loopback browser on a host-managed environment) default to the current checkout; detached surfaces (remote browser, mobile, relay/SSH environments) default to a fresh worktree. A browser reaching the primary server over LAN/domain counts as detached — the checkout lives on a machine the user can't see.
  • Kept defaultThreadEnvMode as the legacy local | worktree literal so older clients still decode server-config snapshots; "Auto" is expressed by the new boolean rather than a third literal.
  • Sticky inheritance retired. Sidebar and shortcut new-thread actions no longer seed mode/branch/worktree from the active thread or draft. chat.newLocal remains the explicit current-checkout exception (draft-only).
  • "Always … for this project" action in the workspace picker writes a per-project override (projectThreadEnvModeOverrides, client setting) — the only sanctioned way a composer choice retrains the default. Slots into project scope when the settings-scope architecture lands.

Fresh worktree bases without a blocking fetch

  • WorktreeBasePlanner (server): a fetch of origin within the 30s freshness window counts as fresh and pins immediately; concurrent fetches dedupe per repository; a failed fetch with a successful fetch within the last hour proceeds on the last-known remote commit (fail-visible, stale provenance); with no usable data the bootstrap fails closed — it never silently falls back to a local ref.
  • Composer prefetch: opening a worktree-mode draft fires the new vcs.prefetchRemote RPC so the send-time check almost always hits the fresh window.
  • newWorktreesStartFromOrigin now defaults on.

Provenance and registry

  • Bootstrap records a worktree.base-pinned thread activity (base ref, commit sha, provenance, fetch time). The branch toolbar turns the promise into the fact post-send: "Worktree · a1b2c3f", provenance on hover; stale pins get an error-tone activity.
  • WorktreeRegistry persists thread↔worktree records to stateDir/worktree-registry.json (registered at bootstrap, marked on thread deletion) — every worktree is attributable to a thread by construction, groundwork for lifecycle/GC tooling.
  • Deterministic branch naming: worktree branches derive from the thread id (t3code/<first-8-uuid-hex>), with server-side collision suffixing (uniquifyNewRefName).

Composer UX

  • Dirty-checkout disclosure both directions: checkout mode warns edits mix with uncommitted work; worktree mode warns uncommitted changes won't be included — each with a one-click switch.
  • Workspace picker labels the mode choice "this draft only".
  • Mobile now resolves its defaults from environment settings (detached surface) instead of hard-coding local.

Verification

  • vp run typecheck, vp check, and vp run -r test all pass (full suite incl. server, web, mobile, desktop).
  • Independent gpt-5.6-sol (Codex) review ran pre-PR; its findings (wire-compat of the settings literal, remote-primary misclassification, mobile ignoring environment defaults, a stray NUL byte) are all addressed in this diff.

🤖 Generated with Claude Code


Note

High Risk
Touches git bootstrap, default settings, and thread-creation flows across server, web, and mobile; incorrect surface or fetch logic could start threads in the wrong checkout or fail sends when remotes are down.

Overview
New threads no longer inherit workspace mode, branch, or worktree from the active thread or draft; they start from a resolved default (surface + settings + optional per-project override). deriveThreadEnvModeFromSurface (default on) maps attached clients to local checkout and detached surfaces (mobile, remote browser, relay) to worktree; projectThreadEnvModeOverrides and the branch toolbar “Always for this project” action are the only ways to persist a project default.

Worktree bootstraps now pin a remote base via WorktreeBasePlanner (fresh fetch, deduped prefetch, stale-within-horizon, or fail closed), record worktree.base-pinned activities, register paths in WorktreeRegistry, use thread-id-derived branch names with collision suffixing, and expose vcs.prefetchRemote for composer-open warmup. Defaults flip to newWorktreesStartFromOrigin: true; UI shows dirty-checkout hints, pinned SHA labels, and an Auto new-thread mode in settings.

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

Note

Add worktree base pinning, a worktree registry, and surface-derived workspace mode defaults

  • Introduces WorktreeBasePlanner (WorktreeBasePlanner.ts), which deduplicates remote fetches per repository and resolves a base commit with provenance (fresh, stale, or local) using configurable freshness and usable-horizon windows.
  • Introduces WorktreeRegistry (WorktreeRegistry.ts), a durable JSON-backed service that records worktree-to-thread associations with atomic writes and serialized access; thread deletion marks associated records as unowned.
  • Bootstrap now uses WorktreeBasePlanner.pinRemoteBase to select the base commit for new worktrees, appends a worktree.base-pinned activity with provenance, and sets uniquifyNewRefName: true so retries create a unique branch instead of failing.
  • New threads and reused drafts derive their default env mode (local/worktree) from the client surface (attached-checkout vs detached) or a per-project override stored in client settings, replacing hardcoded local defaults.
  • The branch toolbar and env mode selector display the pinned base's short commit SHA and provenance in the locked-worktree label and tooltip; the settings UI gains an Auto option for surface-derived mode.
  • Adds a vcsPrefetchRemote WebSocket RPC and a background prefetch effect in ChatViewContent that warms freshness checks when composing a worktree draft with startFromOrigin=true.
  • Behavioral Change: newWorktreesStartFromOrigin now defaults to true; new thread seed context no longer inherits mode, branch, or worktree path from the active thread.

Macroscope summarized d31b1c8.

…ee registry

Implements the new-thread workspace model: every new thread starts from the
resolved default workspace mode instead of inheriting the previous thread's
choices, and worktree threads pin a fresh remote base with visible provenance.

Surface-derived defaults
- New `deriveThreadEnvModeFromSurface` setting (default on): attached
  surfaces (desktop app, loopback browser on a host-managed environment)
  default to the current checkout; detached surfaces (remote browser,
  mobile, relay/SSH) default to a fresh worktree. Explicit
  `defaultThreadEnvMode` still pins a mode globally when derivation is off,
  keeping the wire value decodable by older clients.
- Sticky per-draft inheritance retired: sidebar/new-thread actions no longer
  seed mode/branch/worktree from the active thread. `chat.newLocal` remains
  the explicit current-checkout exception (draft-only).
- Per-project override (`projectThreadEnvModeOverrides` client setting) via
  an explicit "Always … for this project" action in the workspace picker —
  the only sanctioned way a composer choice retrains the default.

Fresh worktree bases without a blocking fetch
- `WorktreeBasePlanner`: fetch within the 30s freshness window counts as
  fresh; concurrent fetches dedupe per repository; a failed fetch with a
  successful fetch within the last hour proceeds on the last-known remote
  commit (fail-visible, "stale" provenance); otherwise the bootstrap fails
  closed and never silently falls back to a local ref.
- Composer prefetch: worktree-mode drafts fire `vcs.prefetchRemote` on open
  so the send-time check almost always hits the fresh window.
- `newWorktreesStartFromOrigin` now defaults on.

Provenance and registry
- Bootstrap records a `worktree.base-pinned` thread activity (base ref, sha,
  provenance, fetch time); the branch toolbar renders the pinned fact
  ("Worktree · a1b2c3f") with provenance on hover.
- `WorktreeRegistry` persists thread↔worktree records to
  `stateDir/worktree-registry.json`, updated at bootstrap and on thread
  deletion — groundwork for lifecycle/GC tooling.
- Deterministic branch naming: worktree branches derive from the thread id
  (`t3code/<uuid-prefix>`), with collision suffixing server-side.

Composer UX
- Dirty-checkout disclosure both directions with one-click mode switch.
- Workspace picker labels choices as "this draft only".

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

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 40a17049-d91e-43a0-b2ce-496a4481f478

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch t3code/plan-ux-review

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:XXL 1,000+ changed lines (additions + deletions). labels Jul 14, 2026
Comment thread apps/mobile/src/features/threads/new-task-flow-provider.tsx
Comment thread apps/server/src/git/WorktreeRegistry.ts
? { startFromOrigin: seedContext.startFromOrigin }
: {}),
}),
handleNewThread(scopeProjectRef(member.environmentId, member.id)),

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.

🟡 Medium components/Sidebar.tsx:1838

When creating a new thread from the new-thread menu for a grouped project, the selected member's surface default is not passed to handleNewThread, so the draft can default to the wrong env mode. If useNewThreadHandler reuses the logical project group's existing draft (from a different member/environment), setLogicalProjectDraftThreadId detects a project change and createDraftThreadState resets the draft to "local" instead of deriving the selected member's surface default. Picking a detached/remote member can therefore create a current-checkout draft when it should default to a worktree. Pass the selected member's resolved default env mode into handleNewThread, or re-resolve defaults when a reused draft changes project.

🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file @apps/web/src/components/Sidebar.tsx around line 1838:

When creating a new thread from the new-thread menu for a grouped project, the selected member's surface default is not passed to `handleNewThread`, so the draft can default to the wrong env mode. If `useNewThreadHandler` reuses the logical project group's existing draft (from a different member/environment), `setLogicalProjectDraftThreadId` detects a project change and `createDraftThreadState` resets the draft to `"local"` instead of deriving the selected member's surface default. Picking a detached/remote member can therefore create a current-checkout draft when it should default to a worktree. Pass the selected member's resolved default env mode into `handleNewThread`, or re-resolve defaults when a reused draft changes project.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed in 233e0b4: useNewThreadHandler now detects when a stored draft is being reused for a different project member and re-derives the env mode (and startFromOrigin) from that member's surface default instead of relying on the store's hard-coded local reset.

Comment thread apps/server/src/git/WorktreeRegistry.ts Outdated
Comment thread apps/server/src/server.ts Outdated
Comment thread apps/web/src/components/ChatView.tsx
Comment thread apps/server/src/vcs/GitVcsDriverCore.ts
Comment thread apps/server/src/git/WorktreeBasePlanner.ts
@macroscopeapp

macroscopeapp Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

1 blocking correctness issue found. This PR introduces significant new features including surface-derived workspace defaults, a worktree registry, and pinned worktree bases with freshness tracking. The scope includes new services, new RPC endpoints, settings changes, and UI modifications. An unresolved review comment also identifies a potential bug in thread creation for grouped projects.

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

- Share a single WorktreeRegistry instance between the deletion reactor and
  the ws layer (previously two instances with divergent in-memory state).
- WorktreeRegistry: take the semaphore for reads too so a first-access disk
  load can't clobber a concurrent mutation, and only update the in-memory
  registry after the persist succeeds.
- WorktreeBasePlanner: claim the in-flight fetch slot synchronously so
  concurrent pin/prefetch calls can't start duplicate fetches.
- Recognize uniquified retry suffixes (t3code/<hex>-N) as temporary worktree
  branches so auto-rename still applies after a partial bootstrap retry.
- Re-derive startFromOrigin when a draft's mode flips via branch selection,
  and when a stored draft is reused for a different project member; the
  reused draft also re-derives its surface default instead of inheriting the
  store's hard-coded local reset.
- Mobile waits for environment settings before auto-persisting a workspace
  selection, so the pre-config fallback can't freeze the wrong default.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Comment thread apps/web/src/hooks/useHandleNewThread.ts
An explicit options.startFromOrigin passed without envMode was overwritten
by the cross-project re-derivation. Guard on the option's presence.

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

@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 1 potential issue.

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 e4061d4. Configure here.

Comment thread apps/web/src/hooks/useHandleNewThread.ts Outdated
… reuse

Passing only startFromOrigin no longer skips the env-mode re-derive (and
vice versa) when a stored draft is reused for a different project member.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@t3dotgg t3dotgg changed the title Surface-derived workspace defaults, pinned worktree bases, and worktree registry WIP: Better defaults for worktree vs local checkout Jul 14, 2026
@t3dotgg

t3dotgg commented Jul 30, 2026

Copy link
Copy Markdown
Member Author

Closing as superseded piecemeal by #4240, #4276, #4411, and #4964. Those landed the useful origin-main, remote-default, no-inheritance, and machine-switch behaviors without reviving this older 36-file workspace model.

@t3dotgg t3dotgg closed this Jul 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL 1,000+ 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