diff --git a/docs/superpowers/specs/2026-04-13-installer-architecture-design.md b/docs/superpowers/specs/2026-04-13-installer-architecture-design.md new file mode 100644 index 0000000..371275a --- /dev/null +++ b/docs/superpowers/specs/2026-04-13-installer-architecture-design.md @@ -0,0 +1,228 @@ +# Installer Architecture Redesign + +**Status:** Design approved, pending implementation plan +**Date:** 2026-04-13 +**Scope:** Installer architecture only. Toolset overhaul, CLI browser, and TTY-font story are queued as follow-up specs. + +## Context + +The current Archduino installer is a Bash entry point (`install.sh`) that sources 13 files under `lib/`. It has grown beyond what Bash handles comfortably: + +- **Monolithic / ordering-fragile.** `install.sh` sources lib files in a fixed order at top level; hard to run a single step and hard to re-run safely. +- **Not idempotent / no resume.** Re-running either duplicates work or silently skips things. +- **Mixed concerns per file.** Detection, templating, package install, and theme application are intertwined in every lib. +- **Thin UX.** Only choices are theme + verbose; no dry-run, no module selection, no preview. +- **Platform coupling.** Arch vs Ubuntu-WSL branches are scattered throughout. + +The goal is a principled rebuild across all five axes. + +## Decision summary + +| Area | Choice | +|---|---| +| Language / runtime | Go, single static binary | +| UI | Bubble Tea + Huh (wizard for input, dashboard for execution) | +| Pre-install shim | Minimal Bash script: ensures `sudo`, `git`, `curl`, `ca-certificates`, fetches binary | +| Module contract | Four-phase: `Detect → Plan → Apply → Verify` | +| Configuration | Embedded defaults + optional `~/.config/archduino/overlay.yaml` | +| Privilege model | User-mode binary; `sudo` per privileged command; upfront `sudo -v` + keepalive goroutine | +| State / logging | `Detect()` is authoritative; run log is advisory UX only | +| Failure policy | DAG execution; failed node fails its descendants; siblings proceed; `--halt-on-error` flag available | +| Subcommands | One binary: `install`, `theme`, `new`, `doctor` | +| Platform scope (v1) | Arch-only native + WSL2; `PackageManager` interface ready for Debian/Fedora later | + +## Architecture + +### Module contract + +Every installable unit implements: + +```go +type Module interface { + Meta() ModuleMeta // id, human name, description, deps []string + Detect(ctx) (State, error) // read current system + Plan(ctx, State) (Plan, error) // diff vs desired; []Action with descriptions + Apply(ctx, Plan, *Reporter) error // execute; stream progress to Reporter + Verify(ctx) error // confirm desired state reached +} +``` + +- `State` is module-defined. +- `Plan` is a flat list of `Action{Description, Kind, Exec}` so the engine can render plans without knowing modules. +- `Reporter` decouples modules from the TUI. +- **`Detect()` is authoritative.** No cached state overrides what the system reports. + +Initial module set (1:1 with today's `lib/*.sh`): +`packages`, `fonts`, `zsh`, `starship`, `zellij`, `neovim`, `yazi`, `arduino-cli`, `renode`, `git` (lazygit), `scaffold`, `shell-config`, `themes`. + +### Execution engine + +- **DAG-scheduled.** Modules declare `deps []string`. Engine topologically sorts and runs independent branches concurrently, default parallelism 4. +- **Failure policy.** Failed node marks its descendants `skipped-due-to-dep`; siblings proceed. End-of-run summary: `X succeeded, Y failed, Z skipped`. +- **Dry-run** (`--dry-run`, or wizard "Review plan" step): runs Detect + Plan for every selected module, renders the combined plan, exits. +- **Halt mode** (`--halt-on-error`): opts into fail-fast for scripted use. +- **Resume hint.** If a prior run log exists, TUI offers "last run: `starship` failed — resume? / start over? / pick modules?" Hint only; Detect decides what actually runs. + +### Configuration + +- **Embedded defaults** via `//go:embed`: package lists, theme files, Zellij layouts, config templates. +- **Optional overlay** at `~/.config/archduino/overlay.yaml`: + ```yaml + version: 1 + extra_packages: [htop, ncdu] + disabled_modules: [renode] + theme_overrides: + starship: ~/my-starship.toml + ``` +- Overlay is merged at load time. Schema is versioned; a migration path exists when it grows. + +### Privilege model + +- Binary runs as the invoking user. +- On `install` start, one `sudo -v` call; a keepalive goroutine refreshes the timestamp every 50 seconds while the installer is live. +- Privileged operations shell out to `sudo pacman …`, `sudo usermod …`. If sudo needs to re-prompt (timestamp expired), the TUI is suspended via `tea.ExecProcess`. +- Config files, git clones, and anything under `$HOME` run as the user — no root-owned files in home. + +### Platform abstraction + +```go +type PackageManager interface { + Install(pkgs []string) error + IsInstalled(pkg string) (bool, error) + Update() error + Name() string +} +``` + +- V1 ships `Pacman` only. Debian/Ubuntu/Fedora impls are stubbed; wiring them is a later spec. +- `Platform{Distro, IsWSL, Arch}` detection is centralized. WSL path sets `/etc/wsl.conf` hints and adds the user to `dialout`/`uucp`. + +### State and logs + +- `~/.local/state/archduino/runs/.json` — per-run structured log: `{module, phase, status, duration, error}`. +- `~/.local/state/archduino/last.json` — most recent run; read by TUI for the resume hint. +- Log rotation: keep the last 10 runs. +- Safe to delete at any time; never read for correctness decisions. + +## User-facing surface + +### Pre-install shim (`scripts/bootstrap.sh`) + +~30 lines of Bash, served from a stable URL. Invocation: + +```bash +curl -fsSL https://archduino.dev/install.sh | bash +``` + +Responsibilities in order: + +1. Refuse to run as root. +2. Detect distro; abort with a clear message if not Arch (v1 scope). +3. Ensure `sudo`, `git`, `curl`, `ca-certificates` are installed (`pacman -S --needed`). +4. Detect architecture, fetch the matching release binary from GitHub Releases, verify its SHA256 against the published checksum file, install to `~/.local/bin/archduino`, `chmod +x`. +5. `exec archduino install`. + +Everything further (base-devel, multilib, user groups, toolset) is handled by modules inside the binary. + +### Subcommands + +| Command | Purpose | +|---|---| +| `archduino install` | Default; wizard + dashboard flow | +| `archduino theme [name]` | Switch colorscheme across all tools; no arg = TUI picker | +| `archduino new ` | Scaffold a project (current `arduino-new` behavior) | +| `archduino doctor` | Run Detect across all modules; report drift in a read-only dashboard | +| `archduino --version` / `completion ` | Standard | + +Global flags: `--dry-run`, `--halt-on-error`, `--verbose`, `--no-tui`, `--only `, `--skip `. + +Shell aliases `theme` and `arduino-new` are preserved via thin wrappers dropped in `~/.local/bin` by the `shell-config` module. + +### TUI flow (`install`) + +**Wizard phase (Huh forms):** + +1. Welcome — detected platform, WSL status, binary version. +2. Theme picker — 5 options, palette preview. +3. Module selector — checkbox list; pre-ticked from overlay or "all" by default; disabled modules grayed with reason. +4. Advanced (collapsible) — parallelism, halt-on-error toggle, overlay path override. +5. Review plan — rendered `Plan()` grouped by module, expandable. Back-navigable. +6. Confirm — `[y/N]` gate into Apply. + +**Apply phase (Bubble Tea dashboard):** + +``` +┌─ archduino install ──────────────────────────────────┐ +│ ✓ packages 12/12 2.4s │ +│ ✓ fonts 1/1 0.8s │ +│ ⠋ neovim 3/7 running: :MasonInstall… │ +│ ⠋ arduino-cli 2/4 running: core install… │ +│ · zellij pending (waits: packages) │ +│ ✗ starship failed — config template parse │ +│ ⊘ shell-config skipped (dep: starship) │ +├──────────────────────────────────────────────────────┤ +│ [tab] focus module [enter] expand log [q] abort │ +└──────────────────────────────────────────────────────┘ +``` + +- Concurrent modules visible live. +- `enter` on a row expands a scrollable log tail. +- On completion: summary view; footer offers `[r]etry failed / [v]iew full log / [q]uit`. + +## Project layout + +``` +archduino/ +├── cmd/archduino/ # main.go, cobra wiring +├── internal/ +│ ├── engine/ # DAG scheduler, Reporter, dry-run, resume +│ ├── module/ # Module interface, Plan/Action types, registry +│ ├── modules/ # one package per module +│ │ ├── packages/ +│ │ ├── zsh/ +│ │ └── ... +│ ├── platform/ # detection; PackageManager interface + pacman impl +│ ├── privilege/ # sudo -v + keepalive +│ ├── config/ # embedded defaults + overlay merge +│ ├── state/ # run log, last-run tracking +│ ├── theme/ # theme model + switcher +│ └── tui/ +│ ├── wizard/ # Huh forms +│ └── dashboard/ # Bubble Tea Apply view +├── assets/ # embedded configs, themes, zellij layout, templates +│ ├── themes/ +│ ├── templates/ +│ └── layouts/ +├── scripts/bootstrap.sh +├── .github/workflows/ +│ ├── ci.yml +│ └── release.yml # goreleaser +└── docs/ +``` + +## Testing + +- **Unit** — every module's `Detect` and `Plan` against an injected `System` fake; table-driven cases for clean / already-installed / partial / drifted. +- **Engine** — DAG scheduling, failure propagation, dry-run, resume; pure Go, fully covered. +- **Integration** — Dockerfile on `archlinux:base` runs `archduino install --no-tui --yes` end-to-end in CI; asserts key files/packages/services. One matrix entry for native Arch. WSL smoke test remains manual for v1. +- **TUI** — `teatest` for wizard navigation and dashboard state transitions. No screenshot tests. + +## Distribution + +- **GitHub Releases** via `goreleaser`: `linux/amd64`, `linux/arm64` static binaries; `SHA256SUMS` file. +- No AUR package for v1; the shim is the distribution channel. AUR can follow later. +- Semver; breaking changes bump major. + +## Migration + +- Existing `install.sh` + `lib/*.sh` stay on `master` at a tagged `v0-bash-final` so users can still run the old installer if they want. +- `feat/renode-dap` merges first; a new `feat/go-rewrite` branch hosts the rewrite. +- First Go release (`v1.0.0`) lands when feature parity with the current Bash installer is verified on a clean Arch VM. + +## Out of scope (queued for follow-up specs) + +- Toolset redesign (re-evaluating which CLI tools are curated). +- CLI browser integration. +- Nerd Font rendering on bare TTY (constrained by the Linux console's 512-glyph PSF limit; needs a separate design covering `fbterm`/`kmscon` or the "SSH-in-from-a-real-terminal" fallback). +- Non-Arch platform support (Debian/Ubuntu/Fedora). +- AUR packaging.