v0.21.17.0 fix(ui): remote-host projects no longer split into two headers - #70
Merged
Conversation
sortRowsMineFirst grouped its contiguous runs by ProjectRoot, but the projectlist renderer emits project headers keyed on Project (basename). Remote rows carry an empty ProjectRoot (omitempty; older hosts' `canopy ls --json` never sent it), so the sort collapsed an entire host into one run and pinned every project's (main) row to the top — the renderer then split each project into a "(main)"-only header plus a separate workspaces header. Group the sort on (Host, Project), the same key the renderer uses, so the two can never disagree. Project is always populated on local and remote rows, so every mixed-version fleet groups correctly. Regression test uses empty-ProjectRoot remote rows: it fails under the old ProjectRoot key and passes under the Project key. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes a Workspaces-tab bug where a remote host rendered each project twice: a lone
(main)row under one project header, then the project's real workspaces under a second header for the same project. Every project on the host did it, so the list looked like it had twice as many projects as it does.Root cause: a key mismatch in the row pipeline. The
projectlistrenderer emits project headers keyed onProject(basename), butsortRowsMineFirstgrouped its contiguous runs onProjectRoot(canonical path). Remote rows carry an emptyProjectRoot(omitempty, and older hosts'canopy ls --jsonnever sent it), so the sort collapsed an entire host into one run and pinned every project's(main)row to the top — the renderer then split each project into a(main)-only header plus a separate workspaces header.Fix: group the sort on
(Host, Project)— the same key the renderer uses. The two can no longer disagree, and becauseProjectis always populated on local and remote rows, every mixed-version fleet groups correctly (not just hosts running a new-enoughcanopy).This was reached after an adversarial review of an earlier, narrower attempt (propagating
ProjectRootonto remote rows). That approach only fixed new-enough remotes and its test didn't actually exercise the changed line. This fix addresses the root-cause mismatch instead.Test Coverage
All new code paths have test coverage.
TestSortRowsMineFirst_RemoteProjectsStayGroupeduses empty-ProjectRootremote rows (matching production): it fails under the oldProjectRootkey (reproduces[(main) (main) free-robin kind-maple]) and passes under the newProjectkey. Verified by temporarily reverting the key during this ship.TestSortRowsMineFirstandTestSortRowsMineFirst_KeepsSectionsSeparateupdated to setProject(reflecting real rows) and still pass.go build ./...clean.go test ./...— all packages pass.Pre-Landing Review
Diff is 61 lines — specialists skipped (<50-line gate is for code; this is a single logical fix + tests). Always-on adversarial review (Claude subagent) ran and surfaced four findings on the original approach; all are resolved by the final fix:
ProjectRootempty on remote rows, so those comments stay correct.ProjectRoot→ avoided; no propagation in the final fix.Scope Drift
Scope Check: CLEAN. Intent: stop remote-host projects splitting into two headers. Delivered: exactly that.
Plan Completion
No plan file detected.
TODOS
No TODO items completed in this PR.
Test plan
go build ./...cleango test ./...— all packages pass🤖 Generated with Claude Code