Skip to content

fix: sync picker hint + worktree list perf and output leak#41

Merged
mswdev merged 2 commits intodevelopfrom
feature/sync-empty-pick-hint
May 5, 2026
Merged

fix: sync picker hint + worktree list perf and output leak#41
mswdev merged 2 commits intodevelopfrom
feature/sync-empty-pick-hint

Conversation

@mswdev
Copy link
Copy Markdown
Owner

@mswdev mswdev commented May 4, 2026

Summary

Three bugs across two commands — kept in one PR per request.

1. ckipper account sync interactive wizard exited silently

gum choose --no-limit exits 0 with empty stdout when ENTER is pressed without first SPACE-toggling items. The single-select source picker right before it accepts ENTER on its own, so muscle memory made this trivially easy to do — and the wizard returned 1 silently in the targets case and printed a bare No types selected. in the types case, leaving users to think the picker was broken.

Both empty-selection paths now print the same SPACE/ENTER hint to stderr before returning 1.

2. ckipper worktree list was slow — ≈25× faster

find recursed unbounded under $CKIPPER_WORKTREES_DIR with only */node_modules/* excluded, so heavy build/cache trees (dist, .next, target, __pycache__, etc.) were walked in full. On a 6 GB / 380k-file worktrees tree the scan took ~700 ms. Pruning the known-heavy directory names brings it to ~30 ms. Bounding by depth was not viable because branches contain slashes (e.g. feature/OGD-320-…).

# before
$ time ckipper worktree list
...real 0m0.726s

# after
$ time ckipper worktree list
...real 0m0.033s

3. ckipper worktree list leaked spurious branch='…' lines

local branch (no =value) inside the per-iteration loop body behaves like typeset -p branch once the variable already carries a value from a prior iteration, leaking literal lines under each bullet. Hoisted the loop locals above the while, matching the existing precedent for the same hazard in lib/account/sync/preview.zsh.

Test plan

  • bats lib/account/sync/dispatcher_test.bats — 19/19 pass (incl. 2 new regressions for the picker hint)
  • bats lib/worktree/worktree_test.bats — 15/15 pass (incl. 3 new regressions for pruning, slash-branches, leak)
  • make test-unit — 504/504 shell tests pass
  • make lint-shell lint-zsh lint-fmt lint-merge-guards — all clean
  • Manual: ckipper account sync shows hint when ENTER pressed without SPACE-selecting
  • Manual: ckipper worktree list is visibly instant and shows no branch= lines

mswdev added 2 commits May 4, 2026 14:46
`gum choose --no-limit` exits 0 with empty stdout when the user presses
ENTER without first toggling items with SPACE. The single-select source
picker right before it accepts ENTER on its own, so muscle memory makes
this trivially easy to do — and the wizard previously returned 1 silently
in the targets case and printed only a bare "No types selected." in the
types case, leaving users to think the picker was broken.

Both empty-selection paths now print the same SPACE/ENTER hint to stderr
before returning 1. Adds two regression tests exercising the fallback
path (CKIPPER_NO_GUM=1) which mirrors the gum empty-stdout branch.
Two bugs surface together in `ckipper worktree list`:

1. Slow scan. `find` recursed unbounded under $CKIPPER_WORKTREES_DIR with
   only `*/node_modules/*` excluded, so the heavy build/cache trees
   (`dist`, `.next`, `target`, `__pycache__`, etc.) were walked in full.
   On a 6 GB / 380k-file worktrees tree the scan took ~700 ms. Pruning
   the known-heavy directory names brings it to ~30 ms (≈25× faster);
   bounding by depth was not viable because branches contain slashes
   (e.g. `feature/OGD-320-…`).

2. Spurious `branch='…'` lines under each bullet. `local branch` (no
   `=value`) inside the per-iteration loop body behaves like
   `typeset -p branch` once the variable already carries a value from a
   prior iteration. Hoist `branch` (and the other loop locals) above the
   `while` and assign without `local` inside, matching the existing
   precedent in lib/account/sync/preview.zsh.

Adds three regression tests:
- pruning skips `.git` files inside node_modules / dist / __pycache__
- branches with slashes still resolve (no depth bound)
- output never contains literal `branch=` lines
@mswdev mswdev changed the title fix(sync): hint SPACE/ENTER when interactive picker returns empty fix: sync picker hint + worktree list perf and output leak May 4, 2026
@mswdev mswdev marked this pull request as ready for review May 5, 2026 00:00
@mswdev mswdev merged commit f2f5594 into develop May 5, 2026
1 check passed
@mswdev mswdev deleted the feature/sync-empty-pick-hint branch May 5, 2026 00:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant