Whether two picker rows collide cannot depend on a third row's width - #380
Merged
Conversation
all_distinct was asked of the padded labels, so a workspace whose repo is one character wider than another's widened the repo column, gave the split row a trailing space the whole-name row did not have, and hid the cross-shape collision from the guard written to catch it. What is left on screen is two rows one space apart, and dl rm opens this picker. shared_key is now the row's own unpadded label, which sees every shape of collision in one pass and de-splits only the rows that collided -- the scoping README.md:211 already promised. The whole-list fallback in offered() is deleted: with the key fixed it never fires. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Reviewer's GuideFix picker row collision detection by using each row’s canonical unpadded display label as its shared key, allowing named() to catch cross-shape collisions and de-split only affected rows; remove the unreachable list-wide padded-label fallback and add focused regression tests. Flow diagram for picker collision detectionflowchart TD
A["Workspace rows"] --> B["named(workspaces, cache_dir)"]
B --> C["shared_key(naming)"]
C --> D["label(naming, 0, 0)"]
D --> E{"Canonical labels collide?"}
E -->|No| F["Keep row's split or whole-name display"]
E -->|Yes| G["De-split only colliding rows"]
G --> H["drawn(namings)"]
F --> H
H --> I["offered labels used by chosen"]
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
Merged
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.
Found by a retrospective
wf-reviewof #336/#350/#363/#370 — four iterations on the picker landed inside three hours, none of them reviewed (Sourcery was out of quota for all four).The defect
ec5c882added a guard for the cross-shape collision: a two-column whole-name row<owner> | <name>can be drawn identically to a three-column split row<owner> | <repo> | <ref>when the name is those last two columns.chosenmaps a label back to a workspace, anddl rmis one of the verbs this picker opens for, so two rows drawn alike is one workspace deleted in place of another.The guard asked
all_distinctof the padded labels. Padding is a fact about the widest row in the list. So a third workspace — nothing to do with either of the two — whose repo is one character wider thandevlaunchwidens the repo column, the split row gains one trailing space the whole-name row does not, the two labels stop being equal as strings, and the guard, which is looking for equal strings, does not fire.Reproduced on
mainas a failing test before any fix:Two rows one space apart. Nobody can tell those apart on a terminal, and the row you pick is the workspace that gets deleted.
There were two notions of "same row" in the file and neither was the one that matters:
shared_keyignored padding but was blind across shapes, andall_distinctsaw across shapes but counted padding.The fix
shared_keybecomes the row's own unpadded label —label(naming, 0, 0). Askinglabelrather than restating its format is the point: the separator, the column count and which fields are drawn are decided in one place, so a change to how a row is drawn cannot leave the key describing the old drawing. It also retires the NUL delimiter, since|is the delimiter and it is the one on screen.That makes the existing key pass in
named()see every shape of collision, which has a second benefit: it de-splits only the rows that collided. The old fallback put every split row in the listing back to its id over one ambiguous pair — which contradictedREADME.md:211, promising that "both rows go back to their full ids", and the principlea_collision_only_pulls_in_the_rows_that_collidealready pins for the same-shape case. This brings the code to the doc rather than the other way round.offered()'s whole-list fallback andall_distinctare deleted. Not on taste — measured: with the key fixed, stubbing the fallback toif false && …leaves all 23select::tests green. It cannot fire, and an unreachable safety net that no test can pin is worse than no net.Verified
a_wider_third_row_cannot_hide_the_cross_shape_collisionfails onmainwith the output above and passes after. It asserts on the labels with runs of whitespace squashed, deliberately — the raw strings do differ, by exactly the difference a person cannot see, soassert_ne!on them would pass while the bug stands.a_cross_shape_collision_also_only_pulls_in_the_rows_that_collide— the collided pair goes back to ids, a third row of the same repository keeps its columns.cargo test -p dl114 lib + every test binary green,cargo clippy --locked --all-targets -- -D warningsclean,cargo fmt --checkclean.Not in this PR
The same review confirmed several non-blocking items I have left for separate changes: no column budget (one long repo name pushes the branch column off-screen),
widestcounting scalar values while its doc claims terminal cells, the picker pty harness having no three-column row in it at all, and aCHANGELOG.md:535claim that--stopopens a multi-select picker when it is a retirement refusal. Full report on #370.🤖 Generated with Claude Code
Summary by Sourcery
Make picker collision handling depend only on the rows being compared and align disambiguation with their visible labels.
Bug Fixes:
Enhancements:
Tests:
Chores: