Skip to content

feat(tui): whiptail Fallback tier reaches feature parity with fzf Rich tier (ADR-0024 D10)#258

Merged
ycpss91255 merged 3 commits into
mainfrom
feat/tui-whiptail-parity
Jun 22, 2026
Merged

feat(tui): whiptail Fallback tier reaches feature parity with fzf Rich tier (ADR-0024 D10)#258
ycpss91255 merged 3 commits into
mainfrom
feat/tui-whiptail-parity

Conversation

@ycpss91255

Copy link
Copy Markdown
Owner

Summary

Phase 3 of the 0.1.0 TUI redesign: the whiptail Fallback tier now reaches
FEATURE parity with the fzf Rich tier (ADR-0024 D10 -- feature-equivalent,
render-degraded). Decisions are the converged PRD #242 set; no re-litigation.

Parity items delivered:

  • Nested category drill-down: main -> category -> sub-category (TAGS[0] bucket,
    only when >1) -> checklist leaf. Reuses a new shared tui_subtags producer.
  • Selected/total counts (D2): category rows count from the in-memory selection
    accumulator via a new pure tui_category_sel_stats (mirrors the fzf stat).
  • Recommended pre-selection (D4): entering the recommended category pre-checks
    is_recommended modules passing the platform filter, idempotent per session.
    Both tiers wrap one shared tui_recommended_preselect_modules producer.
  • Module detail on a separate read-only screen (reuses _tui_screen_detail +
    tui_detail_text), same fields the fzf preview shows.
  • Manage Secrets split into Token / GPG / SSH sub-menus, registered as shared
    screens (deepening docs: archetype cookbook + super-call override patterns #6 registry) so both tiers dispatch identically. Per
    ADR-0025 the screens delegate all text input by forking setup_secrets
    subcommands (verified against setup_secrets.sh) -- menus only, no value widget.

Architecture: shared pure producers live in lib/tui_backend.sh; the fzf
wrappers in lib/tui_render_fzf.sh now delegate to them so the two tiers cannot
drift. Manage Secrets was extracted out of the entrypoint into a new
lib/tui_secrets.sh to keep setup_ubuntu_tui.sh cohesive.

Parity contract

A new test asserts the key invariant: given an identical sequence of choices,
the whiptail accumulator and the fzf selstate yield byte-identical
setup_ubuntu install ... argv (incl. a platform-override variant).

Test plan

  • just -f justfile.ci test-unit (Docker) -- 4061 tests, 0 failures
  • just -f justfile.ci test-integration (Docker) -- 23 tests, 0 failures
  • just -f justfile.ci coverage (Docker, single-pass) -- 1191 ok, 0 not ok
  • shellcheck -x clean on the 4 changed .sh files
  • no decorative emoji in the diff
  • CI sharded coverage-merge asserts the AC-17 80% gate (authoritative)
  • New unit spec tui_whiptail_tier_spec.bats (shared producers + D2/D4 parity
    • the install-argv parity contract); rewritten tui_secrets_menu_spec.bats
      for the 3-way nesting; new smoke_flow_whiptail_parity.exp integration smoke
      (secrets 3 sub-menus + drill-down + Run/Proceed on live whiptail).

…h tier (ADR-0024 D10)

Bring the whiptail Fallback tier to feature equivalence with the fzf Rich
tier (render-degraded, not feature-degraded) per ADR-0024 D10 + ADR-0025.

Shared data layer (both tiers wrap one producer, so they cannot drift):
- tui_subtags / tui_subtag_count: the TAGS[0] bucketing (tui_fzf_subtags now
  delegates to it).
- tui_category_sel_stats: PRD D2 SELECTED/total category counts (mirrors
  tui_fzf_category_sel_stats); main/category-menu rows now show selected/total
  from the in-memory accumulator, not installed/total.
- tui_recommended_preselect_modules: PRD D4 pure recommended-set producer
  (tui_fzf_recommended_preselect now wraps it).
- tui_subcategory_sel_stats / tui_modules_in_subcategory /
  tui_selection_replace_subpage + a subtag filter on tui_checklist_entries.

Whiptail screens:
- _tui_screen_category becomes a dispatcher: recommended pre-selection fires
  on first entry (shared TUI_RECO_PRESELECTED guard), then a sub-category menu
  when the category has >1 TAGS[0] bucket, else straight to the leaf.
- _tui_screen_category_leaf + _tui_commit_checklist_page: the checklist leaf,
  page-replace scoped to the sub-category so unchecking one bucket never wipes
  another's picks.

Manage Secrets (extracted to lib/tui_secrets.sh): a three-way Token / GPG / SSH
picker, each a registry-dispatched sub-screen showing the kind's current list
(empty -> "none") plus its actions (Token: list/set/remove; GPG:
list/generate/import; SSH: list/generate/load/copy/remove). Per ADR-0025 every
text input forks the setup_secrets CLI on its own no-echo tty.

Tests: tui_whiptail_tier_spec covers the shared producers + the PARITY CONTRACT
(both tiers yield identical install argv for the same selection); the existing
whiptail e2e specs are updated for the drill-down; a live-whiptail expect smoke
covers the drill-down, the three secrets sub-menus and Run -> Proceed.
@ycpss91255 ycpss91255 enabled auto-merge (squash) June 22, 2026 16:21
ycpss91255 and others added 2 commits June 23, 2026 01:04
…h coverage to ~96%

The AC-17 merged coverage gate slipped to 79.95% (need >= 80%) because the
brand-new lib/tui_secrets.sh sat at 73.5%: the existing secrets specs drive the
screens by forking setup_ubuntu_tui.sh, so the screen-body lines run in
command-substitution grandchild subshells the kcov tracer does not attribute
back to the lib.

Add test/unit/tui_secrets_e2e_spec.bats: it sources lib/tui_secrets.sh directly
and replaces the backend render wrappers (tui_render_menu / input / msgbox /
yesno), i18n_t, and the registry dispatcher with in-process shell stubs, so the
three-way picker, every Token/GPG/SSH sub-screen action, the empty-list ("none")
vs populated render branches, the danger-tier confirm flows, and the per-action
setup_secrets fork dispatch all execute in the bats process and are attributed.

Refactor the screen bodies to hoist their per-render i18n label lookups into
named locals (behavior-identical): the previous inline
`_var="$(... $(i18n_t ...) ...)"` opener lines are mis-counted by kcov as
unexecuted even when the screen runs. With the labels in plain `${vars}`, the
menu/input openers are traced. The unreachable library guards and the multi-line
awk program text (a data string kcov counts as uncoverable, same class as the
i18n tables) are wrapped in kcov-exclude regions per repo convention.

Focused kcov over both secrets specs: lib/tui_secrets.sh 73.47% -> 96.27%
(72/98 -> 129/134 attributable lines). The five residual uncovered lines are the
`VAR=val tui_render_menu` env-prefix substitution openers kcov cannot trap.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NX5H2vuMTv4mBmjpPYoS3s
@ycpss91255 ycpss91255 merged commit 63875ed into main Jun 22, 2026
53 checks passed
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