Skip to content

fix: 1.0 polish — setup wizard UX, prompt cancel, project depth, docker spin#43

Merged
mswdev merged 2 commits intodevelopfrom
fix/setup-and-prompt-1.0-polish
May 5, 2026
Merged

fix: 1.0 polish — setup wizard UX, prompt cancel, project depth, docker spin#43
mswdev merged 2 commits intodevelopfrom
fix/setup-and-prompt-1.0-polish

Conversation

@mswdev
Copy link
Copy Markdown
Owner

@mswdev mswdev commented May 5, 2026

Summary

Bundled post-launch fixes uncovered by a real-user dry-run after PR #41 merged. Six issues across the setup wizard, the launcher's run flow, and _core_prompt_input.

Issue 1 + 2 — setup customize picker UX

  • Header now reads "Pick keys to customize (SPACE to mark, ENTER to confirm)" so the multi-select contract is obvious. Same hazard as PR fix: sync picker hint + worktree list perf and output leak #41's sync picker — users habitually pressed ENTER on the first row (single-select muscle memory) and silently advanced with no overrides.
  • Detected-configuration table now carries a DESCRIPTION column so users can read what each key does without entering the picker. Polished schema descriptions so bool keys state what true means explicitly (the user reported confusion: "what is aliases_auto_source").

Issue 3 — sync between existing accounts

Cross-account sync was offered only after the wizard added a NEW account. Re-running ckipper setup on an established multi-account install never surfaced the sync feature. Added a top-level offer that fires when accounts ≥ 2.

Issue 4 — docker image build via setup

_core_prompt_spin wraps gum spin -- "$@", which execs argv as a binary. Passing a shell function failed with executable file not found in $PATH. Drop the spinner — the build streams its own progress over ~5 min, which the user wants to see anyway.

Issue 5a — _core_prompt_input cancel propagation

gum input exits non-zero with empty stdout on Esc / Ctrl-C, but the helper substituted the default in both the cancel and empty-submit cases. The launcher branch prompt then created a worktree on feature/dev even when the user pressed Ctrl-X to back out.

  • Now propagates rc: cancel → return non-zero, no stdout. Empty submit → rc=0, default echoed (existing contract preserved).
  • Updated lib/setup/dispatcher.zsh callers (account-name, customize-loop) to skip on cancellation rather than commit empty values.

Issue 5b — launcher project autodetect depth

Maxdepth was 3, but the existing comment claimed support for the <projects_dir>/<group>/<org>/<repo> layout. .git sits one level deeper than the repo root, so depth 4 is needed. The user had a real project (~/Developer/AFF/happyhippo/hippo-vmail) hidden by the off-by-one. ~5 ms cost on the author's tree.

Test plan

  • make test-unit — 510/510 shell tests pass
  • make lint-shell lint-zsh lint-fmt lint-merge-guards — all clean
  • New regressions:
    • _core_prompt_input returns non-zero with no stdout on EOF
    • _ckipper_setup_offer_existing_sync skips < 2 accounts
    • _ckipper_setup_offer_existing_sync invokes / skips sync_dispatch per y/n

mswdev added 2 commits May 4, 2026 18:10
…er spin

Bundled post-launch fixes uncovered by a real-user dry-run:

setup customize picker (Issue 1, 2)
  - Header now reads "Pick keys to customize (SPACE to mark, ENTER to confirm)"
    so the multi-select contract is obvious — same hazard as PR #41's sync
    picker. Users were habitually pressing ENTER on the first row (single-
    select muscle memory) and silently advancing with no overrides.
  - Detected-configuration table now carries a DESCRIPTION column so users
    can read what each key does without entering the picker. Polished the
    schema descriptions so bool keys state what `true` means explicitly
    (the user's reported confusion: "what is aliases_auto_source").

setup sync between existing accounts (Issue 3)
  - Cross-account sync was offered only after the wizard added a NEW
    account. Re-running `ckipper setup` on an established multi-account
    install never surfaced the sync feature. Added a top-level offer
    that fires when accounts >= 2.

docker image build via setup (Issue 4)
  - `_core_prompt_spin` wraps `gum spin -- "$@"`, which execs argv as a
    binary. Passing a shell function failed with "executable file not
    found in $PATH". Drop the spinner — the build streams its own
    progress over ~5 min, which the user wants to see anyway.

`_core_prompt_input` cancel propagation (Issue 5a)
  - `gum input` exits non-zero with empty stdout on Esc / Ctrl-C, but the
    helper substituted the default in both the cancel and empty-submit
    cases. The launcher branch prompt then created a worktree on
    `feature/dev` even when the user pressed Ctrl-X to back out.
  - Now propagates rc: cancel → return non-zero, no stdout. Empty submit
    → rc=0, default echoed (existing contract preserved).
  - Updated setup/dispatcher.zsh callers (account-name, customize-loop)
    to skip on cancellation rather than commit empty values.

launcher project autodetect depth (Issue 5b)
  - Maxdepth was 3, but the existing comment claimed support for the
    `<projects_dir>/<group>/<org>/<repo>` layout — `.git` sits one level
    deeper than the repo root, so depth 4 is needed. The user had a
    real project (~/Developer/AFF/happyhippo/hippo-vmail) hidden by
    the off-by-one. ~5 ms cost on the author's tree.

Tests
  - 510/510 shell tests pass, including the new regressions:
    * _core_prompt_input returns non-zero with no stdout on EOF
    * _ckipper_setup_offer_existing_sync skips < 2 accounts
    * _ckipper_setup_offer_existing_sync invokes / skips sync_dispatch
Three follow-ups uncovered by self-review:

1. Detected-config DESCRIPTION column was unrenderable (Critical)
   The 4th column added in this PR overflowed the fixed-width table
   (`%-22s` does not truncate; descriptions run 90+ chars), making the
   table effectively unreadable. Render descriptions on the line below
   each row instead — keeps the three-column alignment intact and gives
   the description unlimited room. Picker labels also enriched with
   `key — description` so the user sees what each setting does while
   choosing what to customize, with awk extracting the bare key on the
   way out.

2. `lib/config/set.zsh` missed `_core_prompt_input` cancel audit
   (Important). With the new rc-propagating contract, an Esc/Ctrl-C
   during `ckipper config set <key>` (no value arg) would silently blank
   the key. Added `if ! value=$(...); then return 1` guard and a
   regression test that pipes EOF and asserts the writer is bypassed.

3. `_core_prompt_spin` was orphaned (Important). The only caller (setup)
   was removed in this PR. Per CLAUDE.md "no half-finished
   implementations" — the function and its two tests are gone. Its
   `gum spin -- $@` design was incompatible with shell functions, which
   is the predominant pattern in this codebase, so re-using it later
   would require redesigning it anyway.

Tests: 510/510 pass (net unchanged: -2 spin tests, +1 description-row,
+1 config-set cancel). Lint clean.
@mswdev mswdev marked this pull request as ready for review May 5, 2026 00:32
@mswdev mswdev merged commit e00903a into develop May 5, 2026
1 check passed
@mswdev mswdev deleted the fix/setup-and-prompt-1.0-polish branch May 5, 2026 00:38
mswdev added a commit that referenced this pull request May 5, 2026
Round 3 polish in response to user feedback that the previous card layout
"still looks bad" and that the completion screen "doesn't match the format
as the rest of the setup with the color and stuff."

Detected configuration
  Renders through `gum table -p` with a rounded border tinted to gum's
  prompt-accent pink (212), so the block visually belongs to the same
  wizard as the Yes/No prompt below it. Auto-sizes columns to longest
  value; over-long values (e.g. 50+ char filesystem paths) are
  truncated with `…` to keep the table inside narrow terminals.
  Descriptions intentionally drop out of the summary — they appear as
  labels on the pick-keys-to-customize picker (added in PR #43), where
  they matter most. A dim-text tip below the table points users there.

  Falls back to a plain key/value/source list under CKIPPER_NO_GUM (tests,
  CI, hosts without gum). Same data, no border. Loop locals hoisted out
  of the body to dodge zsh's `local var` echo-on-redeclare quirk.

Completion screen
  Wrapped in `gum style --border rounded --padding "1 2"` with a colored
  build-status row (✓ green / ✗ red / ○ dim) and bold section headers
  inside. Plain fallback path preserved for non-gum environments. Two
  sections — Getting started, Maintenance — each list 3-4 commands so
  users can find `ckipper worktree rebuild-image` and `ckipper account
  sync` without re-running the wizard.

Tests
  - 521/521 pass.
  - Replaced the "renders descriptions inline" regression with two new
    pinning the new contract: descriptions DON'T appear in the summary
    body, and the summary points users at the picker for them.
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