feat(sidebar): show worktree health (ahead/behind, dirty count, last-commit age)#157
feat(sidebar): show worktree health (ahead/behind, dirty count, last-commit age)#157liam-russell wants to merge 8 commits into
Conversation
…commit age) Adds getWorktreeHealth/getWorktreesHealth to @sproutgit/git (ahead/behind vs. upstream or the repo's default branch, dirty-file count, last-commit timestamp), exposed via a new batched worktree:healthBatch IPC channel and rendered as compact badges on each worktree-item row in the sidebar. Closes #88 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Adds a “worktree health” snapshot pipeline to support new sidebar badges indicating attention-needed status per worktree (ahead/behind vs upstream or default remote branch, dirty-file count, and last-commit recency).
Changes:
- Introduces
WorktreeHealthtyping and a new batched IPC endpoint to fetch health for multiple worktrees at once. - Implements git-side health computation (
getWorktreeHealth/getWorktreesHealth) with concurrency limiting plus unit coverage. - Renders new ahead/behind and last-commit-age badges in the worktree sidebar and wires periodic fetching + invalidation into the workspace route; adds e2e coverage for the badges.
Reviewed changes
Copilot reviewed 12 out of 12 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/types/src/ipc.ts | Adds new IPC route typing for batched worktree health. |
| packages/types/src/git.ts | Defines the WorktreeHealth data contract. |
| packages/git/src/index.ts | Re-exports new health helpers from the git package. |
| packages/git/src/health.ts | Implements health computation + batched concurrency-limited aggregation. |
| packages/git/src/tests/health.test.ts | Unit tests for upstream/baseRef behavior, dirty counting, and batch semantics. |
| packages/git/package.json | Exposes ./health export entry. |
| e2e/specs/worktree-health.spec.ts | E2E coverage for new sidebar badges. |
| app/src/renderer/workspace/WorktreeSidebar.tsx | Renders ahead/behind + last-commit badges and adds test IDs. |
| app/src/renderer/routes/workspace.tsx | Fetches/propagates health data and invalidates it on relevant events. |
| app/src/renderer/queries.ts | Adds useWorktreeHealth query + query key. |
| app/src/preload/index.ts | Adds preload API surface for the new batched IPC call. |
| app/src/main/ipc/git.ts | Registers IPC handler that chooses baseRef and computes batched health. |
| baseRef?: string | null, | ||
| concurrency = DEFAULT_CONCURRENCY | ||
| ): Promise<Record<string, WorktreeHealth>> { | ||
| const result: Record<string, WorktreeHealth> = {}; |
| const workerCount = Math.min(concurrency, worktreePaths.length); | ||
| await Promise.all(Array.from({ length: workerCount }, worker)); |
| {changeCount > 0 && ( | ||
| <span className="shrink-0 rounded-full bg-(--sg-warning)/20 px-1.5 py-0 text-[9px] leading-4 font-semibold text-(--sg-warning)"> | ||
| <span | ||
| className="shrink-0 rounded-full bg-(--sg-warning)/20 px-1.5 py-0 text-[9px] leading-4 font-semibold text-(--sg-warning)" | ||
| data-testid="worktree-dirty-count" | ||
| > | ||
| {changeCount} | ||
| </span> |
| 'worktree:getMeta': { args: [args: { workspacePath: string; worktreePath: string }]; result: WorktreeMetaRow | null }; | ||
| 'worktree:setMeta': { args: [args: { workspacePath: string; worktreePath: string; branch?: string; sourceRef?: string; rootRepoPath?: string; issueRef?: string | null; issueTitle?: string | null }]; result: void }; | ||
| 'worktree:pruneMetadata': { args: [args: { workspacePath: string; activeWorktreePaths: string[] }]; result: void }; | ||
| 'worktree:healthBatch': { args: [args: { repoPath: string; worktreePaths: string[] }]; result: Record<string, WorktreeHealth> }; |
…-health # Conflicts: # app/src/renderer/queries.ts # app/src/renderer/routes/workspace.tsx # app/src/renderer/workspace/WorktreeSidebar.tsx
|
Addressed all 5 review findings:
Also rebased onto |
The clone in the ahead/behind upstream test doesn't inherit user.name/ user.email from the seed repo, and CI runners have no global git identity configured — committing there failed with "Please tell me who you are" on ubuntu-latest and windows-latest (passed locally/on macOS only because those already had a global identity set). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
These edits (Object.create(null) for the health-batch result map, the concurrency clamp, dropping the redundant dirtyCount computation, and the Partial<Record<>> type for possibly-missing health entries) were made mid-merge but never staged, so the merge commit landed without them — CI's concurrency=0 test failure surfaced that the clamp fix was actually still missing from what got pushed. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…-health # Conflicts: # app/src/renderer/queries.ts # app/src/renderer/workspace/WorktreeSidebar.tsx
…-health # Conflicts: # app/src/renderer/queries.ts # packages/types/src/ipc.ts
…-health # Conflicts: # app/src/preload/index.ts # packages/git/package.json # packages/git/src/index.ts # packages/types/src/git.ts # packages/types/src/ipc.ts
Type
Summary
Why
Closes #88 — the sidebar could only show a live-agent badge, so it couldn't answer "which worktree needs my attention?" without opening each one.
Screenshots / recordings
Verified locally via a WebdriverIO screenshot of a worktree row showing all three new badges together: an "↑1" ahead badge, a "1" dirty-file-count badge, and a "🕐 just now" last-commit-age badge, alongside the existing branch name and type label. (Not attaching an image file here since I don't have a way to upload one through this CLI-only session — happy to add one if a maintainer prefers.)
Breaking changes
None
Test plan
getWorktreeHealth/getWorktreesHealthunit tests inpackages/git/src/__tests__/health.test.ts(6 tests, all passing) covering: no-remote fallback, dirty-file counting, real ahead/behind against a configured upstream, and falling back to a base ref when there's no upstream.e2e/specs/worktree-health.spec.ts(3 tests, all passing) covering the last-commit-age badge, the ahead badge with a real unpushed commit, and the dirty-count badge.pnpm lint && pnpm typecheck && pnpm testall pass (including the full e2e suite).