Improve worktree ownership first, then reduce repeated work

Proposal for Wout. Read-only assessment of T3 Code at commit 8e6a7323050ea8d2891f745f7e624e84f9fd8d9a. The implementation already has substantial cleanup safeguards. The largest opportunities are inconsistent lifecycle paths, expensive status reads for small UI needs, and an inventory that helps users take action safely.

Recommendation: close the explicit-removal gap first. Then ship a lightweight branch subscription and skip cleanup scans for worktrees whose time-based rules are not due. Consolidate attachment and setup readiness in small steps. Build inspection and selected cleanup on those shared rules.

Evidence is source inspection, including relevant tests. Failure scenarios are plausible interleavings, not reproduced incidents. No speedup has been measured. No repository files, worktrees, running servers, or live databases were changed. PostPlan uses true-black fallback colors because the active client theme could not be read.

Priorities

OrderChangeBenefitRelative scope
1Durable, server-owned thread-plus-worktree removalFresh ownership checks and completion after client disconnectMedium; contract, server, web, mobile
2Lightweight branch subscription for sidebar rowsAvoid calculating file and branch diffs to read one branch nameMedium; status contract and subscribers
3Skip ineligible scans; target final reads; make size accounting optionalCut unnecessary Git work, repeated global queries, and checkout lease occupancySmall to medium; server
4Consistent attachment, readiness, and recoveryFewer orphaned checkouts and fewer turns starting before setupMedium, delivered in separate behavior slices
5Read-only inventory, per-checkout keep, selected removalUnderstand ownership and reclaim space without enabling broad policyMedium to large; server and clients
6Batch branch-name probes; improve ref and remote refresh pathsFewer subprocesses and filesystem probes as repositories growSmall independent improvements after measurement

1. Make explicit removal use the server's safety rules

Current path: web checks occupants, asks whether to remove the checkout, stops the thread session, deletes the thread, navigates, then separately requests removeWorktree(force: true). The Git workflow delegates directly to the Git driver. Automatic cleanup has fresh occupant/session/terminal checks and a workspace lease; this explicit path does not share that complete protection.

Failure example: a second device starts using the checkout while the confirmation is open. The earlier occupant check cannot authorize removal safely anymore. A disconnect after deleting the thread can also leave the second operation unfinished.

Smallest useful change: include removal intent in durable thread deletion and process it server-side after deletion effects finish. Recheck current users under the same checkout lease used by cleanup and startup. Surface blocked, removed, and failed results to the client. The explicit discard choice can authorize discarding files; it cannot authorize deleting another thread's workspace. Keep the normal automatic preservation rules intact.

Proof: coordinate another occupant arriving, an active terminal, a turn starting, delayed deletion effects, and a response lost after commit using Deferreds. Retry the same command and establish one durable outcome.

Sources: client deletion sequence; direct removal; cleanup rechecks.

2. Stop computing review totals for a branch label

Confirmed: sidebar rows subscribe to local VCS status. In this version, the retained status is read for refName to show a branch mismatch. GitManager.readLocalStatus nevertheless requests branch-change totals. On a cache miss, the lower layer reads porcelain status, working-tree numstat, review merge-base and untracked-file data, and branch numstat.

Proposal: provide a narrow branch subscription, derived from existing branch notifications plus a fresh initial read. Keep complete status for the active Changes view and other consumers that need it. Verify the search-row path and mobile consumers too. If the status broadcaster shares one checkout among subscribers, aggregate demand so a light subscriber does not request detailed work and a detailed subscriber still gets complete data.

Preserve: external terminal branch switches, detached HEAD, deleted paths, reconnect freshness, and detailed counts. Do not reuse a branch-only result as a complete status result. Existing viewport leases and local-only sidebar subscriptions should remain.

Measurement: mount and scroll synthetic 10, 50, and 200-checkout datasets; record actual subscribed paths, subprocess counts by span, server CPU, and status latency. With only lightweight subscribers, detailed diff calls should be absent. Repeat with one detailed subscriber and an external branch switch. These are test dataset sizes, not measured results.

Sources: sidebar subscription; branch-name consumer; full local read; status implementation.

3. Skip work before making the remaining checks cheaper

The simplest win: cleanup checks the Git branch and local changes before calculating age and settlement eligibility. With only a 30-day inactivity rule enabled, a one-day-old idle checkout still gets those Git reads each sweep. Calculate the cheap time-based decision first. If no time-based rule is due and neither merge nor unchanged cleanup can apply, return a not-yet-due reason without inspecting Git. Keep all final destructive checks for eligible candidates.

This changes report meaning: “not yet due” is not proof that files are clean. Full on-demand inspection should remain available separately. Measure Git command counts for many recent worktrees under age-only rules, alongside eligible and merge-rule cases. Do not add sweep-wide concurrency to hide unnecessary work.

Confirmed: each eligible candidate's final check reads all worktree-thread paths, filters them in memory, loads matching shells, and decodes all non-stopped provider sessions. It repeats this before and after size measurement. With C eligible candidates, T worktree references and S session records, the global reference/session portions do work proportional to C × (T + S). This describes query shape, not measured runtime.

For candidates that remain eligible, the candidate holds its checkout lease while measuring bytes. Measurement has a configured 30-second timeout and can visit up to two million entries. That is a cap, not an observed duration. Provider startup uses the same lease.

Proposal: query current thread shells and sessions relevant to the target path, including descendant session working directories. Use a stable normalized key that preserves existing platform and path-alias semantics. Add an index only after examining its query plan. Keep final activity checks fresh. Make size accounting optional or move it before the lease; revalidate removal eligibility after acquiring the lease. A failed estimate already does not need to prevent safe removal.

Proof and measurement: compare rows returned and decoded, SQL calls, filesystem calls, lease occupancy, and competing startup completion on synthetic datasets. Preserve tests for activity arriving during measurement, subagents, archived threads, shared paths, symlinks, and project roots. Any moved measurement becomes an estimate and must be labelled accordingly.

Sources: final checks; measurement; unfiltered reference read.

4. Make attachment and readiness consistent

Attach exactly once

Mobile creates a checkout, then sends a separate thread metadata update. MCP already rechecks thread state, conditionally binds with expectedWorktreePath: null, and rolls back known binding failures. Extract that behavior into a shared server module for mobile and MCP first. Preserve command idempotency and distinguish a known failure from an interrupted response whose binding may have committed. Adopt launch only after its cancellation behavior fits; avoid a repository-wide rewrite.

Start a turn only after required setup

Launch observes setup completion and waits for synchronous scripts. MCP queues its durable continuation before running setup and does not request completion observation. Missing-checkout recreation restores Git files without rerunning project setup in that block. Persist continuation intent early, but gate execution until required setup succeeds. Honor scripts explicitly configured asynchronous. Run setup after actual recreation, rather than on every resume. Retries must not blindly duplicate script side effects.

Distinguish a recovered checkout from a new baseline

Startup accepts an existing path without checking its repository identity in the recreation guard. If the checkout and local/remote branch are all gone, recovery can create the old branch name from the project/default baseline. An existing integration test explicitly expects this fallback, so changing it is a product decision rather than an accidental bug fix.

Validate the checkout's common Git directory and reconcile intentional branch changes. When the original branch is unavailable, explain the lost recovery identity and require a deliberate baseline choice rather than imply the old files were restored. Evaluate checkpoint-backed recovery only after establishing what those refs actually preserve. Keep no-remote and alternate-remote projects supported.

Proof: duplicate and competing attachment commands, dropped responses after commit, synchronous setup held by a Deferred, asynchronous setup, old callbacks after retry, wrong repository at an existing path, and missing local/remote branch. Assert the files and repository identity seen by the provider.

Sources: mobile creation; MCP conditional binding; continuation and setup ordering; recovery fallback; expected fallback behavior.

5. Turn the cleanup report into an actionable inventory

Existing settings already explain why a worktree was kept and report orphan folders. Those are results of the last cleanup sweep, capped at 200 entries. They are not a complete current inventory, and orphan reporting is reached through enabled cleanup policies.

Proposed behavior: inspect checkouts with cleanup disabled; see owning threads, branch, readiness, last relevant activity, and an explicit keep/remove reason; choose individual eligible checkouts for removal. Measure sizes on request with unknown and stale states. Keep this separate from turning on future automatic deletion.

Useful additions are a reversible per-checkout keep control and paginated orphan inspection. Start with project/machine context and the same server inspection rules used by removal. Prefer inspection on demand when cleanup is disabled, so making inventory visible does not add an expensive hourly scan by default. Git registration and thread projections remain authoritative; avoid a second full worktree registry. A manual keep override would be only a small piece of durable policy. External checkouts and ordinary folders need distinct labels; no thread reference alone does not make a directory disposable.

Web and desktop already share settings. Mobile currently exposes creation/submodule/branch-naming settings but lacks equivalent cleanup controls in its worktree page. Any inventory/removal capability should use the same permission-checked server methods for clients and agents. Visual design belongs in a later Paper run after the product scope is chosen.

Sources: orphan reporting; existing report UI; mobile worktree settings.

6. Smaller performance and usability candidates

Sources: name allocation; ref snapshots; remote freshness guard; cache bypass options.

Keep the working parts

Automatic cleanup already protects active sessions and terminals, shared thread families, pending deletion effects, late file edits, submodule history, managed paths, and project roots. Missing-checkout recreation, previous directory support, shared fetch/backoff, viewport subscription leases, local refresh single-flight, repository-path caches, and parallel Git checkout already exist. Preserve these rather than rebuilding them.

Avoid writable shared node_modules, one filesystem watcher per worktree, unconditional higher checkout parallelism, automatic deletion of every orphan, a second worktree registry, and broad cleanup concurrency as starting points. Each either introduces ownership risk or optimizes work the code already coordinates.

Council synthesis

One independent assessment each from the requested three members, all at high effort. The lead verified the main paths against the source and nearby tests. This was an exploratory architecture assessment, so scores were not requested or invented.

MemberStrongest contributionHow it shaped this proposal
GPT 6.1 SolServer ownership, conditional binding, readiness, identity-aware recovery, repeated final-check readsUse a small shared lifecycle module; retain existing orchestration and Git owners
Claude Opus 5.5Forced explicit deletion, setup omitted on recreation, disabled-by-default inventory, early eligibility, mobile parityPromote eligibility filtering; make inventory independent of cleanup policy; include setup after recreation
Grok 4.7Status overfetching, cache behavior, repeated ref stats, branch allocation probesPromote the narrow branch subscription and batch probes; investigate cache changes conservatively

Judgment calls and disagreements

Across surfaces: web and desktop share the client path; mobile needs the same server operations and cleanup visibility. Agents and scheduled launches should call service methods. Contracts carry permissions and typed results. Keep local, remote, relay, and tunnel operations on the destination environment. Cover retries, cancellation, disconnect after commit, external Git changes, shared child threads, and archived owners. Provider-specific startup behavior stays in adapters; these ownership rules should not vary by provider.

Decision before implementation

Recommended first slice: server-owned explicit removal, the narrow sidebar branch subscription, and early cleanup eligibility, each measured and tested with its own behavior. Then attachment/setup parity. Keep inventory and selected cleanup as a follow-up product decision. This proposal does not publish tickets or authorize implementation.