From 6bcc351e07e6cb3b8b45e10f3482375a65e11004 Mon Sep 17 00:00:00 2001 From: Gabriel Brown Date: Thu, 20 Aug 2026 18:38:50 -0400 Subject: [PATCH] Write down what a fresh install actually lacks Panama installs a desktop it does not fully provision. The shell names fonts nothing installs, a cursor theme nothing installs, and a wallpaper that does not exist; five packages that settings pages depend on are declared nowhere. None of it shows here, because this machine accumulated those pieces under sunhat and never lost them. The spec covers closing that gap, moving every question the installer needs to the front of the run, and porting the parts of sunhat that still matter. Claude-Session: https://claude.ai/code/session_01Q84axqUE5inJhf5Jz9CFy1 --- .../2026-08-20-panama-fresh-install-design.md | 345 ++++++++++++++++++ 1 file changed, 345 insertions(+) create mode 100644 docs/superpowers/specs/2026-08-20-panama-fresh-install-design.md diff --git a/docs/superpowers/specs/2026-08-20-panama-fresh-install-design.md b/docs/superpowers/specs/2026-08-20-panama-fresh-install-design.md new file mode 100644 index 0000000..48d51ed --- /dev/null +++ b/docs/superpowers/specs/2026-08-20-panama-fresh-install-design.md @@ -0,0 +1,345 @@ +# Panama: fresh-install correctness and the sunhat deprecation + +Panama installs a desktop it does not fully provision. The Quickshell shell names +fonts nothing installs, a cursor theme nothing installs, and a wallpaper that does +not exist; four settings pages shell out to packages no list declares. None of this +shows up on this machine, because this machine accumulated those pieces under +sunhat and never lost them. It shows up on the next machine. + +This spec closes that gap, moves every question the installer needs to the front of +the run, and ports the parts of sunhat that still matter — so sunhat can be deleted. + +## Why now + +Recent work built a large Quickshell desktop: a bar, dock, overview, lock screen, +notification server, and a Settings application with thirty-odd pages. Almost all +of it was verified against a running system. Almost none of it was verified against +a system where Panama had just been installed for the first time. + +The existing `tests/quickshell/declared-dependencies-contract.sh` was written for +exactly this class of bug and reports PASS, because it checks only commands that +scripts invoke. Fonts, cursor themes, wallpapers, and packages consumed by GUI +handoffs are invisible to it. A dependency checker with a blind spot is worse than +none, because it reports PASS — that argument is already in that file's own header +comment, and it applies to the checker itself. + +## What is broken + +Verified against the repository at `3b01f1e`. + +| Gap | Evidence | +|---|---| +| No fonts installed | `setup/scripts/change-settings:32` sets `VictorMono Nerd Font 10`. `kitty`, `ghostty`, `hyprlock.conf.template`, `Theme.qml` and `Fonts.qml` all name Nerd Font families. No stage installs a font. `Fonts.qml` documents that a mono family without Nerd glyphs replaces every icon in the shell with tofu. | +| No cursor theme installed | `oreo_blue_cursors` is named in `change-settings:33`, both GTK `settings.ini.template` files, `config/dot/uwsm/env:23`, `config/dot/hypr/env.lua:52`, and `PreferenceSchema.qml:985`. It exists in `~/.local/share/icons` on this machine only. | +| No wallpaper | `PreferenceSchema.qml:826` defaults `wallpaperPath` to `""`. No image ships. | +| `gnome-control-center` undeclared | `SystemSettings.qml:599` allow-lists 23 GNOME panels; 15+ rows across `OnlineAccountsPage`, `RegionPage`, `AccessibilityPage`, `ConnectivityPage`, `ShortcutsPage`, `SoundPage`, `PrivacyPage` and `HealthPage` call `openGnomePanel`. No list declares the package. | +| `snapper` undeclared | `panama-snapshots` drives the entire Snapshots page through `snapper`. | +| `espanso`, `ghostty` undeclared | Both have configs symlinked by `link-dotfiles`; neither is in a package list. | +| `.bashrc` breaks on a fresh box | `config/bash/.bashrc` ends with an unguarded `. "$HOME/.cargo/env"`, which does not exist until `rustup-init` has run, so every shell start emits an error. The file also carries a committed `# >>> Codex installer >>>` block. | +| README describes a desktop that is not installed | The Desktops section advertises GNOME with Forge, Dash-to-Dock, Openbar and Vitals. Nothing in `setup/` installs or configures any of it. | + +## Decisions + +These were settled during design and are recorded so the plan does not relitigate +them. + +**The GNOME session is cut; `gnome-control-center` stays.** These are different +things and only the first is free. The session — Forge, Dash-to-Dock, Openbar, +Vitals, the README's parity claims — is dead weight and goes. The control center is +load-bearing: it is where Panama's own Settings sends you for Online Accounts, +Region, Accessibility, Color, Wellbeing and Wacom, and commit `0c364f3` shows the +handoff list was deliberately narrowed to what Panama does not own. It becomes a +declared dependency rather than an accident of this machine's history. + +**The installer asks everything up front, then runs untouched.** sunhat's failure +mode was a prompt or a failure twenty minutes in, with a person needed at the +keyboard to get past it. Panama's answer is not "no prompts" — some of what the +installer needs genuinely changes between machines and between years, and a stale +hardcoded answer is worse than a question. The answer is that every question is +asked before any package work begins, and nothing asks again afterwards. + +**Answers are not persisted.** Each run asks fresh. The only default shown is the +current hostname, which today's installer already shows. This is a deliberate +simplicity choice: no answers file to keep in sync, nothing personal on disk, and +re-answering six questions costs less than maintaining state that can go stale. + +**Hardware is detected, then confirmed.** Gib's machines span AMD, Intel and NVIDIA +in both laptops and desktops. The installer probes — `lspci` for GPUs, +`mokutil --sb-state` for secure boot, DMI for chassis — and asks a question that +names what it found: *"Found NVIDIA RTX 4070 — install akmod-nvidia and enroll a +secure boot key? [Y/n]"*. Detection alone would remove the ability to decline a +proprietary driver on a machine that has the card; asking alone would make a person +recite their own hardware. + +**Nothing personal is committed.** Panama should be cloneable by someone else and +yield a working desktop. Identity, hostnames and machine layout come from prompts +and from gitignored local state — `~/.config/panama/settings.json` is already +exactly this — never from files in the repository. + +**Applications are admitted one at a time, and dnf or flatpak only.** A short +always-installed core, plus categories chosen from a `gum` checklist during the +interview, so a work laptop does not acquire emulators and a desktop does not skip +Steam. But the menu starts nearly empty and grows by explicit decision, one +application per decision — it is not seeded by porting sunhat's list. + +The rule is that every application Panama installs must come from dnf or from +Flathub, with flatpak preferred where both exist. Anything requiring a pinned RPM +URL, a `curl | bash`, or a source build is admitted only case by case, with a stated +reason why no packaged form exists. + +sunhat is the argument for this rule, not a counterexample to it. Its +`sources/dotenv.sh` hardcodes `upscayl-2.11.5`, `lact-0.5.4-0.x86_64.fedora-40.rpm` +and `jdk-22` — URLs that were stale within a release cycle and that turn a re-run +into a 404. Every application Panama declares is one it has to keep installable, +and the cost of that promise is only bearable if a package manager is keeping it. + +Panama already carries three exceptions: the `oh-my-posh` and `bun` install scripts +in `install-packages`, and `helium-browser-bin` from the `lionheartp/Hyprland` COPR. +They stay — each is idempotent and guarded — but they are the ceiling, not a +precedent. New exceptions need the same standard. + +**Bash scripts carry no `.sh` extension.** A shebang and the executable bit select +the interpreter; the extension is redundant. This applies to the 159 existing files +as well as new ones. + +## Design + +### The installer + +`./install` becomes four movements: + +``` +ascii → interview → stages (uninterrupted) → panama-doctor summary +``` + +The interview is a new script, `setup/scripts/interview`, that runs before any +other stage and asks, in order: + +- Hostname — defaulting to the current one, as today +- Git identity: name, email, editor +- Run `gh auth login`? — skipped without asking when `gh auth status` already passes +- Generate an SSH key? — skipped without asking when one already exists +- Hardware confirmations, each naming what was detected +- Remove Fedora's preinstalled bloat? +- Update firmware with `fwupdmgr`? +- Which application extras to install — a `gum` checklist + +Answers reach the stages as `PANAMA_*` environment variables, but not by the +obvious route. Stages run as child processes of `install`, and a child cannot export +into its parent or its siblings — so the interview cannot simply set variables and +expect the next stage to see them. Instead it writes `KEY=value` lines to a +`mktemp` file whose path `install` chose; `install` sources that file, exports the +names, and deletes it on the existing `trap ... EXIT INT TERM` that already restores +the idle settings. The answers exist for the length of one run and never touch a +durable path, which is what keeps "no persisted answers" honest. + +The interview is therefore listed in `STAGES` like any other stage, but `install` +treats it specially: it is the only stage whose output the installer reads back. + +A stage that needs an answer reads its variable and treats absent as "no". This +matters for re-running an individual stage by hand, which is a debugging path that +must keep working, and it means the interview is never a prerequisite for repairing +one piece of a machine. + +The run ends with `panama-doctor`, printing its summary. Today the only signal that +an install worked is a failed-stage count; a failure that produced no non-zero exit +— a font that did not land, a service that did not start — scrolls past unnoticed. +`docs/UPSTREAM-INSPIRATION.md` already lists a first-run health check as a +candidate; the health check now exists, so this is wiring rather than building. + +### Stages + +The `STAGES` array in `install` is explicit rather than glob-ordered, and stays that +way. The new order: + +| Stage | Status | Responsibility | +|---|---|---| +| `interview` | new | Every prompt, before any work | +| `install-packages` | edited | Repos, package lists, extras, debloat | +| `install-fonts` | new | Nerd Fonts and MS core fonts | +| `install-cursors` | new | Cursor themes into `~/.local/share/icons` | +| `install-hardware` | new | NVIDIA, akmods, secure boot, firmware — all conditional | +| `link-dotfiles` | edited | Adds the wallpaper; drops `forge` | +| `change-settings` | edited | Unchanged in substance | +| `link-vicinae-scripts` | unchanged | | +| `setup-identity` | new | git config, `gh auth login`, SSH key | + +Ordering constraints that are not obvious and must not be broken: + +- `install-fonts` and `install-cursors` precede `change-settings`, because + `change-settings` sets gsettings values naming a font family and a cursor theme. + Setting them before they exist is not an error, but it does mean the first + session renders with fallbacks. +- `setup-identity` follows `install-packages`, because `gh` is installed there. +- `install-hardware` runs late; MOK enrollment sets a password consumed at the next + boot, and firmware updates may prompt for a reboot. Neither should sit in front of + twenty minutes of package work. + +Every stage stays independently re-runnable and independently failable, which is +the existing contract and the reason Panama exists. + +### Fonts + +`install-fonts` installs the families the desktop actually names, checking +`fc-list` before downloading so a re-run costs nothing. VictorMono Nerd Font is +required — it is the shell's icon font. The remaining sunhat families (CascadiaMono, +FiraMono, JetBrainsMono, Meslo, iA Writer Mono) are carried across because Panama's +own font picker exists to choose among them, and a picker with one option is not a +picker. MS core fonts come from the msttcore installer, as in sunhat. + +### Cursors + +`install-cursors` places `oreo_blue_cursors` — and the purple and white variants, +since `PreferenceSchema.qml` exposes `cursorTheme` as a choice — into +`~/.local/share/icons`. Skipped per-theme when already present. + +### Wallpaper + +`config/wallpapers/faroe_islands.jpg` (4000x2444, 644K) is committed. `link-dotfiles` +copies it to `~/Pictures/Wallpapers/` when absent, and seeds `wallpaperPath` in +`settings.json` **only when that key is still empty**. An existing choice is never +overwritten — the same rule every other seeding step in `link-dotfiles` already +follows for kitty, tmux, GTK and hyprlock. + +### The assets contract + +A new `tests/quickshell/declared-assets-contract` covers what the dependency +contract structurally cannot. It fails when: + +- A font family named in `config/` or in a `gsettings set` call is not installed by + `install-fonts` +- A cursor theme named in `config/` is not installed by `install-cursors` +- The default `wallpaperPath` names a file the repository does not ship +- A package invoked by a settings-page helper or a GUI handoff is undeclared +- A directory under `config/dot/` is neither in `link-dotfiles`'s `dirs` array nor + given explicit handling + +All four of today's gaps fail this test. That is the acceptance criterion for the +test itself: it must be written against the broken tree and observed to fail before +the fixes land. + +It is named without a `.sh` extension from the start, while its siblings still carry +one until phase 6. That inconsistency is deliberate and temporary — new files follow +the convention immediately rather than being written wrong and renamed later. + +### GNOME excision + +`config/dot/forge` is deleted and `forge` removed from `link-dotfiles`'s `dirs`. +The README's Desktops section is rewritten to describe one desktop. The comment in +`link-dotfiles` explaining why GNOME and Hyprland coexist goes with it. + +`gnome-control-center` is added to `desktop-packages` with a comment naming what +depends on it, so a future reader does not mistake it for GNOME-session residue and +remove it. `gnome-settings-hyprland.desktop` stays. + +### The `panama` command + +Three subcommands are added to the existing dispatcher, which was written to grow +this way: + +- `panama doctor` — front `panama-doctor` from a terminal, not only from Settings +- `panama test` — run every contract under `tests/`, with a pass/fail summary. The + suite is 119 files with no entry point and no mention in the README; it is the + main safety net and it is currently invisible. +- `panama upgrade` — re-run `./install` from anywhere + +### Dropping the `.sh` extension + +159 files: 110 under `tests/quickshell`, 47 under +`config/local/share/vicinae/scripts`, 2 under `tests/hypr`. All are already +executable and all already carry a bash shebang, so the rename is `git mv` plus +three comment references and two lines in `panama-settings-commands`, which +generates the Vicinae command files and globs for `settings-*.sh`. + +The one real risk is Vicinae's script discovery. Its documentation states a script +command needs a plain text file, the three `@vicinae.*` directives, a shebang, and +the executable bit — no extension requirement. That is close enough to a guarantee +to act on, but not close enough to skip verifying: after the rename, reload with +`vicinae cmd launch core:reload-scripts` and confirm all 47 commands still resolve +before the change is considered done. If discovery does key off the extension, the +Vicinae directory is exempted and the reason recorded here. + +### Machine-specific configuration + +Monitor layout, keybinds and input differences stay in gitignored local state — +`~/.config/panama/settings.json`, written by the Settings application. Panama ships +defaults; a machine deviates locally. No per-hostname directories enter the +repository, because that is what "cloneable by someone else" costs. + +This is a decision to *not* build something. sunhat branched its Hyprland config on +hostname and it was one of the things that made sunhat unmaintainable. + +## Ported from sunhat + +Everything below blocks sunhat's deletion. + +- **NVIDIA and secure boot** — `akmod-nvidia`, `xorg-x11-drv-nvidia-cuda`, MOK + enrollment via `mokutil --import`, and the `rebuild_nvidia_kmod` helper. sunhat's + version opened an editor to hand-edit grub; the replacement does not, because a + step that requires a human mid-run is the failure this repository exists to avoid. +- **Fedora debloat** — removing `gnome-contacts`, `gnome-tour`, `gnome-maps`, + `totem` and the LibreOffice components, behind an interview question. +- **Firmware** — `fwupdmgr refresh` and `update`, behind an interview question. + Panama's Updates page covers this after installation; the installer covers the + first run. +- **Applications** — the extras *mechanism*, and only the applications explicitly + agreed. sunhat's ~70 installers are a list of candidates to review, not a manifest + to port; most of them install by methods that no longer work. Reviewing them is + its own task, done one application at a time against the dnf/flatpak rule above, + and it does not block anything else in this spec. +- **Firefox** — the package itself, which Panama does not currently install, plus + sunhat's `themes/firefox` userChrome. Helium is the daily driver; Firefox stays + because web development means checking a second engine. If Helium supports + userChrome, it gets the equivalent treatment. + +Deliberately not ported: sunhat's `configs/docker/compose_files`. Those are server +workloads — caddy, gitea, homeassistant, nextcloud_aio, pihole, vaultwarden, +wireguard, searxng — and belong to whatever runs them, not to a workstation +installer. They remain recoverable from sunhat's git history. + +## Repository hygiene + +- Prune the worktrees under `.worktrees/` whose branches are already in `main`. +- Audit the seven branches that are not: `feat/panama-displays`, + `feat/panama-settings-pages`, `feat/roadmap-completion` (5 commits), + `feat/settings-home-completion` (3), `feat/settings-sound`, + `fix/settings-backup-complete` (2), `fix/settings-sidebar-scroll`. Report what is + in each before anything is merged or deleted. +- Guard the `.cargo/env` source in `config/bash/.bashrc` and remove the Codex block. +- Rewrite the README: one desktop, the real install stages, and the test suite. +- Audit `docs/settings.md` (21K) for claims that no longer hold. + +## Phases + +Each phase is independently shippable and independently useful. + +1. **Install correctness** — fonts, cursors, wallpaper, the five undeclared + packages, the `.bashrc` fixes, and the assets contract. Closes every gap that + would visibly break a fresh machine. +2. **The interview** — up-front prompts, hardware detection, `panama-doctor` at the + end of the run. +3. **Hardware flows** — NVIDIA, secure boot, debloat, firmware. +4. **Applications and theming** — the extras menu mechanism, Firefox and its theme. + The menu ships with the categories agreed at the time and nothing more; filling + it is ongoing work, one application per decision, not part of this phase. +5. **GNOME excision and documentation** — Forge removal, README rewrite, + `docs/settings.md` audit. +6. **Hygiene** — the `.sh` rename, worktree pruning, the branch audit, and the three + new `panama` subcommands. + +## Verification + +Panama is not going to be tested by reinstalling Fedora. Confidence comes from +three places instead: + +- **The assets contract**, which encodes each gap found here as a test that fails on + the broken tree. +- **`panama doctor` at the end of every install**, which reports what is actually + running rather than what was attempted. +- **`panama test`**, which makes the existing 119 contracts runnable in one command + for the first time. + +The open risk this leaves is anything that breaks only on a genuinely clean system +and that none of the three notices — a repository that fails to enable, a package +renamed between Fedora releases. That risk is accepted; a VM run can settle it later +if the fixes here prove insufficient.