Install the desktop this repository already describes

The shell named a font, a pointer theme and a wallpaper that no package list
installed and no stage placed. It went unnoticed because this machine collected
all three under sunhat and never lost them; a fresh Fedora box would have come
up with tofu for every shell glyph, the default pointer, and no wallpaper --
while Wallpaper.qml called that missing file `shippedPath`.

The dependency contract reported PASS throughout, because it reads commands that
scripts invoke and none of these are one. The new assets contract covers what it
structurally cannot: fonts and pointer themes named in configs and gsettings, a
shipped wallpaper that must exist, commands launched from QML, and directories
nothing puts into service. Written against the broken tree first, where it found
ten faults.

Four of those were packages nobody had noticed were missing -- gnome-calendar,
podman, pipewire-utils and flatpak -- alongside gnome-control-center, which backs
fifteen rows of Panama's own Settings and is commented so it is not mistaken for
GNOME-session residue later.

Fonts turned out to need no install stage at all. Terra, which install-packages
already enables, packages every Nerd Font, so sunhat's wget-and-unzip is replaced
by five lines in a package list. The pointer theme had no such luck: it is
packaged nowhere, so it is vendored rather than downloaded from a URL that can
rot.

espanso stays undeclared. It is in no enabled repository, and building it from
source is the install method this repository is trying to stop using.

Claude-Session: https://claude.ai/code/session_01Q84axqUE5inJhf5Jz9CFy1
This commit is contained in:
Gabriel Brown
2026-08-20 18:57:47 -04:00
parent 6bcc351e07
commit 96e4085919
72 changed files with 341 additions and 31 deletions
+4 -5
View File
@@ -24,8 +24,7 @@ else
fi
export PATH
fi
. "$HOME/.cargo/env"
# >>> Codex installer >>>
export PATH="/home/gib/.local/bin:$PATH"
# <<< Codex installer <<<
# rustup writes this file, and initial-packages installs rustup rather than
# running rustup-init -- so on a fresh machine it does not exist yet and an
# unguarded source made every single shell start with an error.
[ -f "$HOME/.cargo/env" ] && . "$HOME/.cargo/env"
@@ -0,0 +1,2 @@
[Icon Theme]
Inherits=oreo_blue_cursors
@@ -0,0 +1,3 @@
[Icon Theme]
Name=Oreo Blue Cursors
Comment=design by varlesh | colour by convert.rb
Binary file not shown.

After

Width:  |  Height:  |  Size: 643 KiB

@@ -31,10 +31,11 @@ Verified against the repository at `3b01f1e`.
|---|---|
| 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. |
| No wallpaper, though the code says one ships | `Wallpaper.qml:32` declares `shippedPath` as `~/Pictures/Wallpapers/faroe_islands.jpg`. `panama-lock:139` falls back to the same path, `hyprpaper.conf` names it twice, and `lock-screen-settings-harness.qml` uses it as a fixture. Nothing ships it. `PreferenceSchema.qml:826` defaults `wallpaperPath` to `""`. |
| `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. |
| Four more undeclared, found by the new contract | Crawling QML for launched commands — which the dependency contract never reads — also caught `gnome-calendar` (`CalendarAgenda.qml:180`), `podman` (`Containers.qml:149`, the Containers page backend), `pipewire-utils` for `pw-dump`/`pw-play` (`PrivacyState.qml:169`, `SoundTest.qml:35`), and `flatpak`, which `install-packages` itself uses. |
| `espanso`, `ghostty` undeclared | Both have configs symlinked by `link-dotfiles`; neither is in a package list. `ghostty` is in Terra and is now declared. **`espanso` is in no enabled repository** — sunhat built it from source — so under the dnf/flatpak rule it stays undeclared. Its config remains linked, which costs nothing, and installing it is a separate decision. |
| `.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. |
@@ -156,8 +157,7 @@ way. The new order:
|---|---|---|
| `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-cursors` | new | Vendored cursor theme 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 |
@@ -166,10 +166,10 @@ way. The new order:
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.
- `install-cursors` precedes `change-settings`, because `change-settings` sets a
gsettings value naming a cursor theme. Setting it before the theme exists is not
an error, but it does mean the first session renders with a fallback. Fonts are
installed by `install-packages`, which already runs first.
- `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
@@ -180,18 +180,42 @@ 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.
There is no font stage. Terra — which `install-packages` already enables — packages
every Nerd Font as an RPM (`victormono-nerd-fonts` and siblings, 3.5.0-1.fc44), and
Fedora now packages the Microsoft core fonts as `mscore-fonts`. So the entire gap
closes by adding five lines to `desktop-packages`:
- `victormono-nerd-fonts` — required. It is what `change-settings` sets as
`monospace-font-name` and what the shell draws every icon with.
- `jetbrainsmono-nerd-fonts`, `firamono-nerd-fonts`, `cascadiamono-nerd-fonts`
Panama ships a font picker; a picker with one option is not a picker.
- `mscore-fonts` — web development means checking pages that assume Arial and
Georgia resolve.
This supersedes sunhat's approach, which downloaded release zips from GitHub with
`wget` and unpacked them into `~/.local/share/fonts`. That is precisely the install
method this repository is trying to stop using, and it turned out to be unnecessary.
### 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.
`oreo_blue_cursors` is packaged nowhere — not Fedora, not RPM Fusion, not Terra. It
comes from `varlesh/oreo-cursors` on GitHub, so the dnf/flatpak rule cannot apply and
one of its exceptions has to.
The theme is **vendored into the repository** at
`config/local/share/icons/oreo_blue_cursors` (3.3MB, 63 files, no symlinks) and
exposed by `link-dotfiles` with the same narrow per-directory symlink it already uses
for Panama's application icons and Vicinae themes. Downloading at install time was
rejected: it would add a fourth network exception and a URL that can rot, which is
the failure this spec exists to prevent. Vendoring costs 3.3MB in git once, and then
the cursor is simply always there.
Only the blue variant ships, and nothing else needs changing to make that true:
`cursorTheme` is a free string in the schema, and `panama-desktop-style`'s `catalog()`
enumerates the pointer themes actually present in the XDG icon directories. The picker
therefore offers exactly what is installed, so shipping one theme instead of three
removes options from the list rather than leaving it advertising themes that are not
there.
### Wallpaper
@@ -299,12 +323,24 @@ 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.
- **Done.** The seven branches that appeared unmerged were audited with
`git cherry -v main <branch>`. Six of them — `feat/panama-displays`,
`feat/panama-settings-pages`, `feat/settings-home-completion`,
`feat/settings-sound`, `fix/settings-backup-complete` and
`fix/settings-sidebar-scroll` — had every commit marked patch-equivalent to one
already in `main`, and every file they added was verified present in `main`. They
looked unmerged only because the work re-landed through a different route. Deleted,
along with their worktrees.
- `feat/roadmap-completion` is the sole branch with unlanded work: 5 commits, 13
files absent from `main`, 62 commits behind. It carries a theme-profile system, a
dock context menu, overview-thumbnail capture reliability, and wallpaper/lock
hardening, with its own spec and plan. It does not conflict with this work — it
*depends* on it, since its lock generator falls back to the shipped wallpaper this
spec makes exist. Rebasing it is its own task.
- Six worktrees remain on branches already merged to `main`
(`home-accessories-customization`, `panama-commands`, `panama-displays-review`,
`panama-settings-home-phone`, `settings-notification-rules`, `settings-ownership`).
They are clutter, not risk; pruning them stays in phase 6.
- 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.
@@ -313,9 +349,9 @@ installer. They remain recoverable from sunhat's git history.
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.
1. **Install correctness** — fonts and the four other undeclared packages, the
vendored cursor theme, the shipped wallpaper, 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.
+18
View File
@@ -1,11 +1,21 @@
adw-gtk3-theme
akmods
alsa-plugins-pulseaudio
cascadiamono-nerd-fonts
decibels
desktop-file-utils
dnf-plugins-core
ffmpeg
firamono-nerd-fonts
fuse-libs
# Ghostty and its config in config/dot/ghostty are linked by link-dotfiles.
ghostty
# The Calendar agenda opens events in this; see CalendarAgenda.qml.
gnome-calendar
# NOT GNOME-session residue. Panama's own Settings hands off to this for the
# panels it deliberately does not own -- Online Accounts, Region, Colour,
# Accessibility, Wellbeing, Wacom. See SystemSettings.qml's panel allow-list.
gnome-control-center
gstreamer1-plugin-fmp4
gstreamer1-plugin-gif
gstreamer1-plugin-gtk4
@@ -21,6 +31,7 @@ gstreamer1-plugins-good-gtk
gstreamer1-plugins-good-qt
gstreamer1-plugins-good-qt6
hipblas
jetbrainsmono-nerd-fonts
kernel-devel
lame
lame-devel
@@ -33,6 +44,8 @@ mesa-libGLU
mesa-libOpenCL
mokutil
mozilla-openh264
# Arial, Georgia and Times, so web pages that assume them render correctly.
mscore-fonts
nautilus
nautilus-extensions
nautilus-python
@@ -42,7 +55,12 @@ opus-devel
papers
python3-dnf-plugin-versionlock
rocm-opencl
# The Snapshots page is snapper end to end; see scripts/panama-snapshots.
snapper
sushi
# Fonts the desktop names by family and would otherwise render as tofu.
# VictorMono is required: it draws every glyph in the shell.
victormono-nerd-fonts
wine
wine-core
wine-mono
+2
View File
@@ -19,6 +19,8 @@ pipx
php
php-fpm
pnpm
# Rootless containers, and the backend for the Containers settings page.
podman
python3-devel
python3-gobject
python3-tkinter
+2
View File
@@ -26,6 +26,8 @@ libnotify
nm-connection-editor
orca
pamixer
# pw-dump backs the privacy indicators, pw-play the sound-test button.
pipewire-utils
playerctl
qrencode
qt6-qtwayland
+3
View File
@@ -4,6 +4,9 @@ btop
cargo
curl
eza
# install-packages installs flatpaks with it; Fedora Workstation ships it,
# but a minimal install does not.
flatpak
fontconfig
fwupd
fzf
+45
View File
@@ -243,6 +243,51 @@ for icon_file in "$PANAMA_ICON_DIR"/*.svg; do
done
gtk-update-icon-cache -f -t "$HOME/.local/share/icons/hicolor" 2>/dev/null || true
# The pointer theme. oreo_blue_cursors is packaged by no Fedora repository -- it
# is a GitHub project -- so rather than download it at install time and depend on
# a URL that can rot, the theme is vendored into this repository and exposed the
# same way Panama's application icons are. Without it, every gsettings value,
# GTK settings.ini and XCURSOR_THEME naming it resolves to nothing and the
# session falls back to the default pointer.
PANAMA_CURSOR_DIR="$PANAMA_PATH/config/local/share/icons"
USER_CURSOR_DIR="$HOME/.local/share/icons"
mkdir -p "$USER_CURSOR_DIR"
for cursor_src in "$PANAMA_CURSOR_DIR"/*_cursors; do
[ -d "$cursor_src" ] || continue
cursor_name="$(basename "$cursor_src")"
cursor_dst="$USER_CURSOR_DIR/$cursor_name"
if [ -L "$cursor_dst" ]; then
rm "$cursor_dst"
elif [ -e "$cursor_dst" ]; then
log "Keeping existing cursor theme at $cursor_dst"
continue
fi
ln -s "$cursor_src" "$cursor_dst"
log "Linked cursor theme → $cursor_dst"
done
# The wallpaper the desktop already believes it ships. Wallpaper.qml calls this
# path `shippedPath` and substitutes it whenever wallpaperPath is unset, which is
# its default; panama-lock falls back to the same file, and hyprpaper.conf names
# it directly. Copied rather than symlinked, because ~/Pictures/Wallpapers is a
# directory the user owns and adds their own images to -- a symlink there would
# be a repository file masquerading as one of their own.
PANAMA_WALLPAPER_DIR="$PANAMA_PATH/config/wallpapers"
USER_WALLPAPER_DIR="$HOME/Pictures/Wallpapers"
if [ -d "$PANAMA_WALLPAPER_DIR" ]; then
mkdir -p "$USER_WALLPAPER_DIR"
for wallpaper_src in "$PANAMA_WALLPAPER_DIR"/*; do
[ -f "$wallpaper_src" ] || continue
wallpaper_dst="$USER_WALLPAPER_DIR/$(basename "$wallpaper_src")"
if [ -e "$wallpaper_dst" ]; then
log "Keeping existing wallpaper at $wallpaper_dst"
else
cp "$wallpaper_src" "$wallpaper_dst"
log "Copied wallpaper → $wallpaper_dst"
fi
done
fi
PANAMA_APPLICATION_DIR="$PANAMA_PATH/config/local/share/applications"
USER_APPLICATION_DIR="$HOME/.local/share/applications"
mkdir -p "$USER_APPLICATION_DIR"
+200
View File
@@ -0,0 +1,200 @@
#!/usr/bin/env bash
# Every asset Panama's desktop names must be something Panama installs.
#
# This is the sibling of declared-dependencies-contract.sh, and it exists because
# that contract has a structural blind spot. It checks commands that scripts
# invoke -- so it reports PASS on a machine where the shell's icon font, the
# pointer theme, the wallpaper and four GUI applications are all missing, because
# none of those are a command in a script.
#
# That was not hypothetical. At the time this was written:
#
# * change-settings set monospace-font-name to a Nerd Font that no package list
# installed, and Fonts.qml documents that a mono family without Nerd glyphs
# replaces every icon in the shell with tofu.
# * oreo_blue_cursors was named in six files and installed by none.
# * Wallpaper.qml declared a `shippedPath` that the repository did not ship,
# and panama-lock fell back to the same non-existent file.
# * gnome-control-center backed fifteen rows of Panama's own Settings and was
# declared nowhere, as were gnome-calendar, podman and pipewire-utils.
#
# Every one of those presents as a feature that quietly is not there, which is
# the same failure mode -- and the same argument -- as the dependency contract.
set -uo pipefail
repo_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
findings=()
note() { findings+=("$1"); }
declared="$(cat "$repo_dir"/setup/packages/* 2>/dev/null \
| sed 's/#.*//' | tr -d ' ' | grep -v '^$' | sort -u)"
[[ -n "$declared" ]] || { printf 'declared assets contract: no package lists found\n' >&2; exit 1; }
is_declared() { grep -qix -- "$1" <<<"$declared"; }
# ── 1. Fonts ─────────────────────────────────────────────────────────────────
#
# A family name is not a package name. Nerd Font packages follow one rule --
# "VictorMono Nerd Font" is victormono-nerd-fonts -- and everything else needs
# naming, so an unmapped family is reported as a real omission rather than
# silently skipped.
font_package() {
local family="$1"
# Style and width suffixes are not part of the family that ships.
family="${family% Mono}"
family="${family% Propo}"
family="$(sed -E 's/ (Light|Regular|Medium|SemiBold|Bold|Thin|Black|Italic)$//' <<<"$family")"
if [[ "$family" == *" Nerd Font" ]]; then
printf '%s-nerd-fonts' "$(tr '[:upper:]' '[:lower:]' <<<"${family% Nerd Font}" | tr -d ' ')"
return
fi
case "$family" in
"Adwaita Sans"|"Adwaita Mono") printf 'adwaita-sans-fonts' ;;
Cantarell) printf 'abattis-cantarell-fonts' ;;
*) printf '?%s' "$family" ;;
esac
}
# Sources are named individually rather than crawled, because a font family is a
# bare string: crawling for one finds every English phrase in the repository.
fonts="$(
grep -hoE "font-name '[^']+'" "$repo_dir"/setup/scripts/* 2>/dev/null \
| sed -E "s/.*'(.*)'/\1/"
grep -hoE '^gtk-font-name=.*' "$repo_dir"/config/dot/gtk-""*/settings.ini.template 2>/dev/null \
| sed 's/^gtk-font-name=//'
grep -hoE '^font-family = ".*"' "$repo_dir"/config/dot/ghostty/config 2>/dev/null \
| sed -E 's/^font-family = "(.*)"/\1/'
grep -hoE '^\s*font_family = .*' "$repo_dir"/config/dot/hypr/hyprlock.conf.template 2>/dev/null \
| sed -E 's/^\s*font_family = //'
grep -hoE 'key: "(interfaceFont|iconFont|monospaceFont|documentFont|applicationFont)", type: "string", def: "[^"]+"' \
"$repo_dir"/config/dot/quickshell/config/PreferenceSchema.qml 2>/dev/null \
| sed -E 's/.*def: "([^"]+)".*/\1/'
)"
while IFS= read -r family; do
[[ -n "$family" ]] || continue
# Trailing point size, as gsettings and GTK both write it.
family="$(sed -E 's/ [0-9]+$//' <<<"$family")"
package="$(font_package "$family")"
if [[ "$package" == \?* ]]; then
note "font family '${package#?}' is named in the desktop but maps to no known package"
elif ! is_declared "$package"; then
note "font '$family' needs package '$package', which no package list declares"
fi
done < <(sort -u <<<"$fonts")
# ── 2. Cursor themes ─────────────────────────────────────────────────────────
#
# Satisfied either by a package or by a theme vendored into the repository --
# oreo_blue_cursors is packaged by no Fedora repository, so it ships here.
cursors="$(
grep -hoE "cursor-theme '[^']+'" "$repo_dir"/setup/scripts/* 2>/dev/null \
| sed -E "s/.*'(.*)'/\1/"
grep -hoE '^gtk-cursor-theme-name=.*' "$repo_dir"/config/dot/gtk-""*/settings.ini.template 2>/dev/null \
| sed 's/^gtk-cursor-theme-name=//'
grep -hoE '^export XCURSOR_THEME=.*' "$repo_dir"/config/dot/uwsm/env 2>/dev/null \
| sed 's/^export XCURSOR_THEME=//'
grep -hoE 'key: "cursorTheme", type: "string", def: "[^"]+"' \
"$repo_dir"/config/dot/quickshell/config/PreferenceSchema.qml 2>/dev/null \
| sed -E 's/.*def: "([^"]+)".*/\1/'
)"
while IFS= read -r theme; do
[[ -n "$theme" ]] || continue
[[ -d "$repo_dir/config/local/share/icons/$theme/cursors" ]] && continue
is_declared "${theme%%_*}-cursor-theme" && continue
is_declared "$theme" && continue
note "cursor theme '$theme' is named in the desktop but is neither vendored nor packaged"
done < <(sort -u <<<"$cursors")
# ── 3. The shipped wallpaper ─────────────────────────────────────────────────
#
# Wallpaper.qml calls one path `shippedPath` and panama-lock falls back to it.
# A fallback that does not exist is not a fallback.
while IFS= read -r image; do
[[ -n "$image" ]] || continue
[[ -f "$repo_dir/config/wallpapers/$image" ]] \
|| note "wallpaper '$image' is referenced as shipped, but config/wallpapers/$image does not exist"
done < <(grep -rhoE 'Pictures/Wallpapers/[A-Za-z0-9._-]+\.(jpg|jpeg|png|webp)' \
"$repo_dir"/config/dot/quickshell/services "$repo_dir"/config/dot/quickshell/scripts \
"$repo_dir"/config/dot/hypr 2>/dev/null | sed 's|.*/||' | sort -u)
# ── 3b. Shipping is not installing ───────────────────────────────────────────
#
# A vendored asset that link-dotfiles never puts into place is exactly as absent
# as one that was never vendored -- and it would satisfy the two checks above,
# which only prove the repository contains the file. Both directories must
# actually be consumed by the installer.
for vendored in config/local/share/icons config/wallpapers; do
[[ -e "$repo_dir/$vendored" ]] || continue
grep -qF "$vendored" "$repo_dir/setup/scripts/link-dotfiles" 2>/dev/null \
|| note "$vendored is shipped but link-dotfiles never installs it, so a fresh machine still does without"
done
# ── 4. Commands the shell launches ───────────────────────────────────────────
#
# The dependency contract reads scripts. These are launched from QML, so it
# never sees them -- which is how the application backing fifteen Settings rows
# went undeclared.
qml_package() {
case "$1" in
hyprctl) printf 'hyprland' ;;
wl-copy|wl-paste) printf 'wl-clipboard' ;;
xdg-open) printf 'xdg-utils' ;;
pw-dump|pw-play) printf 'pipewire-utils' ;;
*) printf '%s' "$1" ;;
esac
}
# Provided by the base system or the shell itself; nothing installs these
# separately, and listing them would be noise.
QML_BASELINE='^(sh|bash|rm|test|systemd-inhibit|loginctl|timedatectl|gsettings|gapplication|systemctl|busctl)$'
while IFS= read -r command_name; do
[[ -n "$command_name" ]] || continue
[[ "$command_name" =~ $QML_BASELINE ]] && continue
package="$(qml_package "$command_name")"
is_declared "$package" \
|| note "the shell launches '$command_name' from QML, but '$package' is undeclared"
done < <(grep -rhoE '(command: |execDetached\(|command = )\["[a-z][a-z0-9._-]*"' \
"$repo_dir"/config/dot/quickshell --include='*.qml' 2>/dev/null \
| sed -E 's/.*\["([^"]*)"/\1/' | sort -u)
# ── 5. Directories nothing puts into service ─────────────────────────────────
#
# A config directory nobody links is a directory nobody has. This catches the
# case where a new dot/ directory is added and link-dotfiles' `dirs` array is not
# updated, which fails silently -- the config is simply never in place.
#
# Linking is not the only way to be used: ohmyposh is loaded straight out of the
# repository by config/bash/shell, which needs no symlink at all. So the shell
# configuration counts as a consumer too, and only a directory nothing at all
# refers to is reported.
consumers=("$repo_dir/setup/scripts/link-dotfiles" "$repo_dir"/config/bash/*)
for dot_dir in "$repo_dir"/config/dot/*/; do
name="$(basename "$dot_dir")"
grep -qE "(^|[^A-Za-z0-9_-])$name([^A-Za-z0-9_-]|$)" "${consumers[@]}" 2>/dev/null \
|| note "config/dot/$name is referenced by neither link-dotfiles nor the shell configuration, so nothing puts it into service"
done
# ── Report ───────────────────────────────────────────────────────────────────
# One asset named by three different files is one finding, not three.
if (( ${#findings[@]} > 0 )); then
mapfile -t findings < <(printf '%s\n' "${findings[@]}" | sort -u)
printf 'declared assets contract: %d finding(s)\n' "${#findings[@]}" >&2
printf ' - %s\n' "${findings[@]}" >&2
exit 1
fi
printf 'declared assets contract: PASS\n'