The schema is the single source of truth for what a setting IS. It cannot express what a setting USED to be -- and renaming a key, changing its units, or splitting one setting into two all leave a stored value the new schema does not recognise. Unrecognised keys are deliberately carried through untouched so that rolling back to an older Panama does not discard a newer version's settings, which means the user's choice silently stops taking effect with nothing to explain it. settings.json now carries a schemaVersion, and load() runs every pending migration before anything reads a value. The list is empty: the point is that the first breaking schema change becomes a routine edit rather than an emergency, and Omarchy carries eighty of these. The behaviours that make it safe to run against a real user's file: A file with no schemaVersion predates this and is STAMPED, not migrated -- running the list against it would apply upgrades designed for schemas it never had. A file from a NEWER Panama is left completely alone. Downgrading keys is not something this can do correctly, and unknown keys already survive, so an older build simply ignores what it does not understand. A step that throws stops at the last good version. Skipping past it would lose that conversion forever; failing the whole load would cost the user every setting. The list being empty is exactly why this is tested now: the first time it runs for real will be against somebody's actual settings during an upgrade, which is a poor moment to find out how it behaves. The harness supplies fixture steps including one that throws, and the contract pins all four behaviours above plus the promise that unknown keys survive. One thing the contract earned its place on: stamping a pre-versioning file changes it without running any step, so writing only on "migrated" left the stamp in memory to be redone on every launch. It now writes whenever the version moves, and explicitly does not write a file from the future. Claude-Session: https://claude.ai/code/session_01BRvzt4H8XXLPVH5MyYdk9L
Panama
Formerly Sunhat. A personal config for Fedora, with the intention of helping a user set up their Fedora system with one command.
git clone https://git.gbrown.org/gib/Panama.git ~/.local/share/Panama
~/.local/share/Panama/install
install runs everything in setup/scripts/ in order:
| Script | Does |
|---|---|
install-packages |
Repos (RPM Fusion, Terra, Hyprland COPR), then the four package lists in setup/packages/ |
link-dotfiles |
Symlinks config/dot/<name> → ~/.config/<name> |
change-settings |
Copies config/copy/ over /, applies gsettings, enables user services |
Existing configs are moved to config/old/ rather than overwritten.
Desktops
Panama configures two desktops that coexist. Both sessions stay available in GDM, so you can switch back and forth while you settle in.
GNOME
The original setup: Forge for tiling, Dash-to-Dock, Openbar, Vitals,
AppIndicator support. Configured through config/dot/forge/ and gsettings.
Hyprland
A from-scratch replacement built to reproduce the GNOME setup closely enough that muscle memory transfers — same keybinds, same panel contents, same dock, same Tokyo Night Moon palette.
| Piece | What it is |
|---|---|
config/dot/hypr/ |
Compositor config. Lua, not hyprlang — see its README |
config/dot/quickshell/ |
The shell: bar, dock, Continuum overview, Settings, Screen Intelligence, focus sessions, quick settings, notifications, screenshot UI |
config/dot/vicinae/ |
Raycast-style launcher, themed |
config/dot/uwsm/ |
Session environment (see the uwsm caveat in the hypr README) |
config/dot/wofi/ |
Fallback launcher, in case the shell fails to start |
config/dot/xdg-desktop-portal/ |
Portal backend routing |
Start here: config/dot/hypr/README.md — it
covers the Lua migration, the uwsm environment gotcha, the HDR decision, the
full keymap, and troubleshooting.
Log in as "Hyprland (uwsm-managed)", not plain "Hyprland".
Layout
bin/ Small user-facing commands on PATH
config/
bash/ .bashrc, aliases, env (env is gitignored)
copy/ Files copied verbatim over / (needs sudo)
dot/ Symlinked into ~/.config
old/ Backups of whatever was replaced (gitignored)
setup/
packages/ One package per line
scripts/ Run in order by ./install