Skip to content

Add worktree control center - #429

Merged
SergeSerb2 merged 2 commits into
mainfrom
surgecode/worktree-control-center
Jul 31, 2026
Merged

Add worktree control center#429
SergeSerb2 merged 2 commits into
mainfrom
surgecode/worktree-control-center

Conversation

@SergeSerb2

Copy link
Copy Markdown
Owner

Summary

  • Add a native Worktree Control Center that inventories every thread-owned worktree from persisted thread metadata.
  • Show ownership, branch, thread status, dirty/unknown state, allocated disk use, setup health, and path, with direct handoff to the owning thread.
  • Support scoped create, Git repair, refresh, and safe removal flows while protecting active threads and warning before discarding dirty or unverified worktrees.
  • Add the scoped vcs.repairWorktree contract and server implementation; no host-wide inventory or status API is introduced.

Area

  • apps/mac — native macOS app
  • apps/windows — Windows desktop app
  • apps/mobile — iPhone companion app
  • apps/server — backend server
  • Shared packages (packages/contracts, packages/shared, packages/client-runtime, …) or relay (infra/relay)
  • Build, CI, or release tooling
  • Docs

Release size

  • size:XS — tiny internal, documentation, or narrowly scoped fix
  • size:S — small user-visible fix or contained improvement
  • size:M — normal feature or meaningful behavior change
  • size:L — broad feature or substantial cross-package change
  • size:XL — release-system, architecture, or ecosystem-scale change

Verification

  • pnpm run verify --all passes (check + typecheck + tests; adds lint:mobile and the Swift suite)
  • pnpm run test:mac passes (required for apps/mac changes)
  • Manually verified (describe below)

The debug macOS executable launched successfully. Interactive desktop verification was unavailable because the automation host denied screen capture.

Screenshots / Recordings

Not attached: macOS screen recording is unavailable to the automation host (screencapture returned could not create image from display).

Release notes

Manage thread-owned worktrees from a native control center with status, disk use, handoff, repair, creation, and safe removal controls.

@SergeSerb2 SergeSerb2 added the size:M Normal feature or meaningful behavior change label Jul 31, 2026

@SergeSerb2 SergeSerb2 left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Good foundation for worktree control, but critical correctness issue: new ChatThread fields must be added to displayEquivalent(). The diff adds branch and worktreePath fields to ChatThread and updates the equality operator, but does not show updates to the displayEquivalent(to:) method. Per the project memory (PR #328), missing displayEquivalent changes cause thread upserts to silently drop—any worktree-related metadata changes won't persist to the database. The rest of the implementation is solid: clean separation between UI, AppModel, and backend layers; proper rollback on failures; reasonable error messages.

Could not anchor

  • blocking apps/mac/Sources/SurgeCodeMac/Model/Entities.swift — The new branch and worktreePath fields were added to ChatThread and included in the == operator, but the diff does not show updates to displayEquivalent(to:). Per project memory, this causes thread upserts to silently fail when these fields change. Verify that displayEquivalent() includes both new fields, and add any missing memoization tests.

SurgeCode auto-review · model=codex/gpt-5.6-luna · head=021e704a815d

}
}

private func remove(_ snapshot: WorktreeControlSnapshot) {

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

nit: Minor: runOperation() only updates the selected snapshot after reload if it matches the edited thread. Consider also showing an inline success toast or re-selecting the repair result to confirm the action completed.

: projectOrder == .orderedAscending
}
}

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

info: The worktreeSnapshots() loop fetches archived threads one page at a time. On repos with many archived threads, this could be slow. Consider caching or paginating results if performance becomes an issue.

@SergeSerb2 SergeSerb2 left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Three critical issues block merge:

  1. Window lifecycle bug (memory leak): WorktreeControlCenterWindowController holds isReleasedWhenClosed = false, making the window persist forever and unable to reopen cleanly.

  2. VCS refresh scope mismatch: Line 1790 refreshes input.path (worktree subdir) but should refresh input.cwd (repo root) to match removeWorktree's pattern. git status requires a working repository root.

  3. Missing memoization test for branch field: The branch field was added to displayEquivalent() but AppModelRenderMemoizationTests.displayEquivalentComparesAllButUpdatedAt does not test it. A branch-only upsert will silently drop (per [[displayEquivalent field trap]]). Add a test case.

Diff compiles and structure is solid otherwise. No test for the new UI itself (that requires approvals).

Could not anchor

  • important apps/mac/Tests/SurgeCodeMacTests/AppModelRenderMemoizationTests.swift:247 — Missing test case for branch field changes. The field is now in displayEquivalent() but this test does not verify it. Add a case after the snooze test (after line 246):
// Branch-only upsert must not be judged equivalent.
b = a
b.branch = "feature/other"
#expect(!a.displayEquivalent(to: b))

Without this, a thread that changes branches silently skips sidebar upsert.


SurgeCode auto-review · model=codex/gpt-5.6-luna · head=f4abf694f899

panel.titlebarAppearsTransparent = true
panel.title = "Worktree Control Center"
panel.isReleasedWhenClosed = false
panel.delegate = self

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

blocking: Window is never released: isReleasedWhenClosed = false and the controller is a static singleton. After first open, the window persists and cannot be closed/reopened cleanly. Change to true:

panel.isReleasedWhenClosed = true

Comment thread apps/server/src/ws.ts
WS_METHODS.vcsRepairWorktree,
gitWorkflow.repairWorktree(input).pipe(Effect.tap(() => refreshGitStatus(input.path))),
{ "rpc.aggregate": "vcs" },
),

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

blocking: VCS refresh uses wrong path scope. repairWorktree refreshes input.path (the worktree subdir) but should refresh input.cwd (the repo root), matching removeWorktree below it. Git status needs the working repository root:

gitWorkflow.repairWorktree(input).pipe(Effect.tap(() => refreshGitStatus(input.cwd)))

@SergeSerb2
SergeSerb2 merged commit 75b65e0 into main Jul 31, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:M Normal feature or meaningful behavior change

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant