Skip to content

feat(tui): stream slug/preset installs through the wizard pipeline#146

Merged
fullstackjam merged 1 commit into
mainfrom
feat/slug-install-pipeline
Jul 11, 2026
Merged

feat(tui): stream slug/preset installs through the wizard pipeline#146
fullstackjam merged 1 commit into
mainfrom
feat/slug-install-pipeline

Conversation

@fullstackjam

Copy link
Copy Markdown
Member

What does this PR do?

Makes install <slug> / install <preset> apply through the wizard's full-screen streaming pipeline instead of the linear "OpenBoot Installer / Step N" output — the real fix for "this UI is still old".

Why?

install <slug> resolves to a RemoteConfig and applied through installer.RunContext (linear). The earlier #143 unified runSyncInstall — a different path (the saved sync-source flow), which slug installs don't take. So the slug UI stayed old.

installer.ApplyContext is the same engine both the wizard and RunContext already use — only the reporter differs (ConsoleReporter vs the wizard's channel reporter). On a TTY we now resolve the plan with the linear pre-flight (installer.PlanForConfig: checkDependencies + Plan), then apply it via wizard.RunPipeline with a chanReporter. The installer's section headers ("Step 1: Git Configuration", "Step 4: Installation", …) already map to pipeline phases via the wizard's headerPhase, so it streams identically to the wizard.

  • installer.PlanForConfig(cfg) — the RunContext flow up to (not including) apply.
  • wizard.RunPipeline's run closure now receives an installer.Reporter; wizard.PhasesForPlan seeds the sidebar from an InstallPlan.
  • runPipelineInstall wires it, mirroring the wizard's abort / screen-recording-reminder / save-sync-source handling.
  • Non-TTY / --silent / --dry-run keep RunContext (linear) unchanged.

Testing

  • go vet ./..., full internal/... suite, go test -race (wizard), make build — green
  • TestPhasesForPlan locks the plan→sidebar mapping
  • Verified on a real install <slug> in a tmux pty: renders the PIPELINE sidebar (✓ Git identity, ✓ Homebrew 24/24, ✓ Applications 18/18, ⠋ Dotfiles, …) + streaming log; zero "Step N" / "OpenBoot Installer" output

Scope

Covers every interactive TTY install that used RunContext — slug, preset, and file/RemoteConfig sources — so they all share the pipeline now. dry-run stays linear (shows what would happen).

Cross-repo checklist

  • Docs/content update in openboot.dev? — No
  • CLI ↔ server API contract change? — No

Notes for reviewer

https://claude.ai/code/session_01HLoiSd2k9EJJ1HPjjDgUgo

@github-actions github-actions Bot added installer Package installation logic tests Tests only ui Terminal UI labels Jul 11, 2026
`install <slug>` / `install <preset>` (RemoteConfig) applied through the
linear "OpenBoot Installer / Step N" output while bare `install` used the
full-screen wizard — the actual mismatch behind "this UI is still old".
(The earlier #143 unified runSyncInstall, a *different* path — the saved
sync-source flow — not this one.)

ApplyContext is the same engine both paths already use; only the reporter
differs. On a TTY, resolve the plan with the linear pre-flight prep
(installer.PlanForConfig: checkDependencies + Plan), then apply it through
wizard.RunPipeline with a chanReporter, so the section headers stream as
pipeline phases exactly like the wizard. Non-TTY / --silent / --dry-run
keep RunContext.

- installer.PlanForConfig(cfg): the RunContext flow up to (not incl.) apply.
- wizard.RunPipeline's run now receives an installer.Reporter; add
  wizard.PhasesForPlan to seed the sidebar from an InstallPlan.
- runPipelineInstall wires it, mirroring the wizard's abort/reminder/save
  handling.

Verified on a real `install <slug>` (tmux): shows the PIPELINE sidebar +
streaming log, zero "Step N" output.

Claude-Session: https://claude.ai/code/session_01HLoiSd2k9EJJ1HPjjDgUgo
@fullstackjam fullstackjam force-pushed the feat/slug-install-pipeline branch from 2966b9a to a96dd5b Compare July 11, 2026 10:07
@fullstackjam fullstackjam merged commit b7aa669 into main Jul 11, 2026
14 checks passed
@fullstackjam fullstackjam deleted the feat/slug-install-pipeline branch July 11, 2026 10:09
fullstackjam added a commit that referenced this pull request Jul 16, 2026
…l, wizard lows (#147)

* fix(tui): repair ctrl+c abort lifecycle

Three defects in the install-wizard abort/force-quit machinery (added in
#144), all surfaced by an adversarial re-review of the v0.63.0..HEAD range:

- C1: a single ctrl+c cancels the context, which SIGKILLs the in-flight
  brew/npm subprocess, so ApplyContext returns a non-nil error ("signal:
  killed"). The `m.aborting && installErr == nil` guard therefore never
  assigned ErrAborted for the common case (abort during the package phase),
  and the CLI misreported the abort as an install failure and nagged the
  screen-recording reminder. Now join ErrAborted with the cause whenever
  aborting, so errors.Is(err, ErrAborted) holds.

- M3: the config steps (shell/dotfiles/macOS/post-install) take no ctx and
  ApplyContext had no ctx.Err() gate, so an aborted install kept symlinking
  dotfiles and rewriting macOS defaults after the user asked to stop. Gate
  between phases so an abort skips not-yet-started work.

- C2: force-quit (2nd ctrl+c) returned tea.Quit immediately while the
  install goroutine was still running, racing the deferred os.Stdout
  restore and letting the engine mutate the system after control returned.
  Run/RunPipeline now join the goroutine (via a done channel closed once
  ApplyContext returns) before restoring stdout.

The prior abort test only injected a nil-error installDoneMsg -- the one
case that already worked -- masking C1. Updated to inject a killed-
subprocess error, plus tests for the ctx gate and the goroutine join.

Claude-Session: https://claude.ai/code/session_01LyhLX9U3aNuUUAKX1H9JbQ

* fix(cli): run post-install scripts on interactive slug installs

R1 regression from #146: routing `install <slug>` / `-u` / `--from` on a
TTY through the wizard pipeline forces plan.Silent=true to keep prompts out
of the alt-screen. But Silent is overloaded -- applyPostInstall also *skips
execution* when Silent, so a RemoteConfig carrying a post_install block had
its script silently dropped, where v0.63.0 (linear, Silent=false) previewed,
confirmed, and ran it.

The alt-screen can't host the script preview + confirm, so defer post-install
to after teardown on a normal terminal (mirroring the screen-recording
reminder): strip it from the streamed plan via splitPostInstall and run it via
installer.RunPostInstallAfterTUI on a clean run, before the reminder (Step 8
order). A failing script stays a soft error, not fatal.

Scope: RemoteConfig sources only. Presets, --packages-only, the bare wizard
(no PostInstall in the plan), and sync are unaffected.

Claude-Session: https://claude.ai/code/session_01LyhLX9U3aNuUUAKX1H9JbQ

* fix(tui): npm package count, resize scroll, elapsed clock, select lows

Grab-bag of install-wizard defects from the v0.63.0..HEAD review:

- M1: the npm outer retry (applyNpm) re-runs the install and re-emits an
  "already installed" skip for every package a prior attempt installed,
  breaking the one-terminal-event-per-package invariant. The renderer's
  skippedPkgs counter (unclamped, unlike incPhase) inflated past the total,
  so the completion footer could show "-1 packages". Dedup: ignore a skip
  for a package that already produced a terminal event (terminalSeen).

- M2: WindowSizeMsg updated height but didn't re-clamp scroll. selectList
  renders from a re-clamped copy while selectHitTest reads the stored
  scroll, so a click right after a resize (before any mouse motion
  re-clamps) could toggle the wrong package. Re-clamp on resize.

- C3: the tick loop never stopped, so the completion footer's elapsed clock
  kept counting up while the user read it. Freeze it at done.

- selVisible counted height-6 but selectList renders height-4 rows, leaving
  two blank rows at the bottom and two the keyboard cursor can't reach.
  Align the counts.

- A click on the vertical divider column toggled the adjacent package;
  treat the divider as neither pane.

- Fix the stale hover comment (hover no longer skips the keyboard-cursor row).

Claude-Session: https://claude.ai/code/session_01LyhLX9U3aNuUUAKX1H9JbQ

* chore(installer): log install_started on the pipeline install path

The wizard pipeline path (PlanForConfig -> RunPipeline -> ApplyContext)
skips runInstallContext, so a streamed slug/preset install logged
install_completed with no matching install_started. Emit it in
PlanForConfig, mirroring runInstallContext's event.

Claude-Session: https://claude.ai/code/session_01LyhLX9U3aNuUUAKX1H9JbQ
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

installer Package installation logic tests Tests only ui Terminal UI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant