The manifests could no longer describe the machine; this detects it, and the second commit fixes it
install.sh installs desired − installed. That guarantees a fresh box reaches the declared state —
and nothing anywhere asked whether the declared state still describes the machines. It did not:
measured against the Calamares install day, blankpad had 41 hand-installed packages and packages/blankpad.pkgs declared exactly one line, amd-ucode. A rebuild today would have produced
a box without tmux, obsidian, ghostty-nautilus, sshfs, and 20 others, and every check would
have said green. Refs #51.
scripts/pkg-inventory.sh — installed − declared
undeclared additions (declare these): 25 ← exit 1 when > 0
repo (sync dbs): bun cachyos-gaming-applications curlftpfs flatpak fuse2 …
aur (needs a helper): nirimod-git
installer-provided, undeclared (a reinstall provides these): 160
accountsservice alacritty alsa-firmware …
declared, not installed (install.sh installs these):
orphans ignored (pacman -Qtdq): python-build python-hatchling python-installer
flatpak (reported only; install.sh installs no flatpaks): com.discordapp.Discord
--emit prints the additions as manifest lines. Exit: 0 clean · 1 drift · 2 usage · 3 cannot classify. Read-only: it never installs.
The baseline is the install day, read from the pacman log (oldest [ALPM] installed date,
inclusive, rotated pacman.log.N and .N.gz included). Without it the report is useless — a base
install shows several hundred "additions". Rotated logs matter: past the rotation window the install
day exists only there, and skipping them reports the whole base system as hand-installed.
Refuse rather than guess the AUR split. Classification is "can any sync db serve this name", so
Chaotic-AUR counts as a repo and only true AUR routes to aur.pkgs. Unavailable sync dbs → exit 3,
because guessing dumps every package into yay and turns a manifest into a pile of source builds.
Classified with pacman -Sl: pacman -Sqq refuses to run unprivileged, and a tool that merely
reads the machine must not need sudo to answer a question about it.
The two bugs the fixtures were too small to catch
sort | head -1 under set -o pipefail: head closes the pipe, sort takes SIGPIPE, the
command substitution returns 141, set -e aborts. It passed the fixtures and died on the first
real run (1033 install-day lines). Fixed with a one-pass awk min — and the lookalike awk 'NR==1{print; exit}' is also wrong, it closes the pipe the same way. Test 15 feeds a
20 000-line log; the failure was observed directly, not inferred.
tr '\n' ' ' eats the final newline and fold preserves that absence, so the next section
label got glued onto the last name — declared, not installed vanished from a sliced report.
Fixed with { tr …; echo; }, and test 3 now asserts the section boundary survives.
One more test-shape note: four of these tests were passing vacuously before the section helper
was fixed — it prepended ^ to patterns that already carried one, so the range ran to end-of-output
and every absence assertion was trivially true. The suite is only trustworthy because the assertions
now slice a named section with a start-inclusive, terminator-exclusive range.
Commit 2 declares what was measured
24 repo additions folded into packages/blankpad.pkgs. Verified both directions on the real box:
install.sh --host blankpad → repo pkgs: delta empty — nothing to install (so the running box
changes nothing; this is purely what a rebuild reproduces)
Known gap, documented rather than papered over:aur.pkgs is read by every host, so a
desktop-only AUR package cannot be declared there without making the headless servers build a niri
module. nirimod-git stays undeclared until install.sh grows a per-host AUR manifest. Flatpak has
the same shape in reverse: install.sh installs no flatpaks, so the Discord app is reported, not
declared.
Verification
bats tests/87/87 (71 existing + 16 new), kb-lint.sh omp-docs 0, kb-coverage.py 0. Real-box
runs shown above against /var/log/pacman.log and the live package database — the tests stub pacman and flatpak and touch neither.
Docs: omp-docs/tools/packages.md now owns the package tier (install.sh had never been
documented), with the rebuild procedure, the baseline law, the -Sl law, the orphan law, and both
pipe traps. Indexed in omp-docs/tools/index.md.
# The manifests could no longer describe the machine; this detects it, and the second commit fixes it
`install.sh` installs `desired − installed`. That guarantees a fresh box reaches the declared state —
and nothing anywhere asked whether the declared state still describes the machines. It did not:
measured against the Calamares install day, **blankpad had 41 hand-installed packages** and
`packages/blankpad.pkgs` declared exactly one line, `amd-ucode`. A rebuild today would have produced
a box without `tmux`, `obsidian`, `ghostty-nautilus`, `sshfs`, and 20 others, and every check would
have said green. Refs #51.
## `scripts/pkg-inventory.sh` — installed − declared
```
undeclared additions (declare these): 25 ← exit 1 when > 0
repo (sync dbs): bun cachyos-gaming-applications curlftpfs flatpak fuse2 …
aur (needs a helper): nirimod-git
installer-provided, undeclared (a reinstall provides these): 160
accountsservice alacritty alsa-firmware …
declared, not installed (install.sh installs these):
orphans ignored (pacman -Qtdq): python-build python-hatchling python-installer
flatpak (reported only; install.sh installs no flatpaks): com.discordapp.Discord
```
`--emit` prints the additions as manifest lines. Exit: `0` clean · `1` drift · `2` usage ·
`3` cannot classify. Read-only: it never installs.
**The baseline is the install day, read from the pacman log** (oldest `[ALPM] installed` date,
inclusive, rotated `pacman.log.N` and `.N.gz` included). Without it the report is useless — a base
install shows several hundred "additions". Rotated logs matter: past the rotation window the install
day exists only there, and skipping them reports the whole base system as hand-installed.
**Refuse rather than guess the AUR split.** Classification is "can any sync db serve this name", so
Chaotic-AUR counts as a repo and only true AUR routes to `aur.pkgs`. Unavailable sync dbs → exit `3`,
because guessing dumps every package into `yay` and turns a manifest into a pile of source builds.
Classified with `pacman -Sl`: **`pacman -Sqq` refuses to run unprivileged**, and a tool that merely
reads the machine must not need sudo to answer a question about it.
## The two bugs the fixtures were too small to catch
- **`sort | head -1` under `set -o pipefail`**: `head` closes the pipe, `sort` takes `SIGPIPE`, the
command substitution returns `141`, `set -e` aborts. It passed the fixtures and died on the first
real run (1033 install-day lines). Fixed with a one-pass `awk` min — and the lookalike
`awk 'NR==1{print; exit}'` is *also* wrong, it closes the pipe the same way. Test 15 feeds a
20 000-line log; the failure was observed directly, not inferred.
- **`tr '\n' ' '` eats the final newline and `fold` preserves that absence**, so the next section
label got glued onto the last name — `declared, not installed` vanished from a sliced report.
Fixed with `{ tr …; echo; }`, and test 3 now asserts the section boundary survives.
One more test-shape note: four of these tests were passing **vacuously** before the `section` helper
was fixed — it prepended `^` to patterns that already carried one, so the range ran to end-of-output
and every absence assertion was trivially true. The suite is only trustworthy because the assertions
now slice a named section with a start-inclusive, terminator-exclusive range.
## Commit 2 declares what was measured
24 repo additions folded into `packages/blankpad.pkgs`. Verified both directions on the real box:
- `install.sh --host blankpad` → `repo pkgs: delta empty — nothing to install` (so the running box
changes nothing; this is purely what a rebuild reproduces)
- `pkg-inventory.sh` → 1 remaining addition: `nirimod-git`, deliberately undeclared
**Known gap, documented rather than papered over:** `aur.pkgs` is read by *every* host, so a
desktop-only AUR package cannot be declared there without making the headless servers build a niri
module. `nirimod-git` stays undeclared until `install.sh` grows a per-host AUR manifest. Flatpak has
the same shape in reverse: `install.sh` installs no flatpaks, so the Discord app is reported, not
declared.
## Verification
`bats tests/` **87/87** (71 existing + 16 new), `kb-lint.sh omp-docs` 0, `kb-coverage.py` 0. Real-box
runs shown above against `/var/log/pacman.log` and the live package database — the tests stub
`pacman` and `flatpak` and touch neither.
Docs: `omp-docs/tools/packages.md` now owns the package tier (`install.sh` had never been
documented), with the rebuild procedure, the baseline law, the `-Sl` law, the orphan law, and both
pipe traps. Indexed in `omp-docs/tools/index.md`.
---
cwd: /home/blank/.omp/wt/wt-20261019-125509-7c8a99d · session: 01a155bb-6474-7366-be9d-629f74af0e05
Build worktree: `~/.omp/wt/wt-pkginv` off `origin/main`. Refs #51
install.sh installs desired − installed from packages/*.pkgs, which guarantees a fresh box reaches
the declared state — and nothing anywhere checked that the declared state still describes the
machines. Measured against the Calamares install day, blankpad carries 41 hand-installed packages
and packages/blankpad.pkgs declared one line.
pkg-inventory.sh walks the other direction: installed − declared, with the pacman log as the
baseline so installer furniture is never reported as a hand install. It splits additions by
whether a sync db can serve them (Chaotic-AUR counts, so only true AUR routes to aur.pkgs),
excludes orphans from --emit, reports flatpak apps without pretending install.sh installs them,
and exits 3 rather than guessing when the sync databases are unreadable.
16 black-box tests over fixtures and a stubbed pacman. Two of them exist because the first
implementation was wrong in ways a small fixture cannot show: sort|head under pipefail dies of
SIGPIPE on a real 1000-line install-day log (awk-exit closes the pipe exactly as head does), and
tr without a trailing echo glues the next report label onto the last folded name.
Docs: omp-docs/tools/packages.md now owns the package tier — install.sh was never documented.
Refs #51
The point of the new inventory tool is this commit: packages/blankpad.pkgs declared one package
while the machine had 24 more that no rebuild would reproduce. Declaring them changes nothing on
the running box (install.sh installs only what is missing — its delta is now empty) and makes a
fresh install faithful to this one.
nirimod-git is left undeclared on purpose: it is AUR-only and aur.pkgs is fleet-wide, so declaring
it there would make the headless servers build a niri module.
Refs #51
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
The manifests could no longer describe the machine; this detects it, and the second commit fixes it
install.shinstallsdesired − installed. That guarantees a fresh box reaches the declared state —and nothing anywhere asked whether the declared state still describes the machines. It did not:
measured against the Calamares install day, blankpad had 41 hand-installed packages and
packages/blankpad.pkgsdeclared exactly one line,amd-ucode. A rebuild today would have produceda box without
tmux,obsidian,ghostty-nautilus,sshfs, and 20 others, and every check wouldhave said green. Refs #51.
scripts/pkg-inventory.sh— installed − declared--emitprints the additions as manifest lines. Exit:0clean ·1drift ·2usage ·3cannot classify. Read-only: it never installs.The baseline is the install day, read from the pacman log (oldest
[ALPM] installeddate,inclusive, rotated
pacman.log.Nand.N.gzincluded). Without it the report is useless — a baseinstall shows several hundred "additions". Rotated logs matter: past the rotation window the install
day exists only there, and skipping them reports the whole base system as hand-installed.
Refuse rather than guess the AUR split. Classification is "can any sync db serve this name", so
Chaotic-AUR counts as a repo and only true AUR routes to
aur.pkgs. Unavailable sync dbs → exit3,because guessing dumps every package into
yayand turns a manifest into a pile of source builds.Classified with
pacman -Sl:pacman -Sqqrefuses to run unprivileged, and a tool that merelyreads the machine must not need sudo to answer a question about it.
The two bugs the fixtures were too small to catch
sort | head -1underset -o pipefail:headcloses the pipe,sorttakesSIGPIPE, thecommand substitution returns
141,set -eaborts. It passed the fixtures and died on the firstreal run (1033 install-day lines). Fixed with a one-pass
awkmin — and the lookalikeawk 'NR==1{print; exit}'is also wrong, it closes the pipe the same way. Test 15 feeds a20 000-line log; the failure was observed directly, not inferred.
tr '\n' ' 'eats the final newline andfoldpreserves that absence, so the next sectionlabel got glued onto the last name —
declared, not installedvanished from a sliced report.Fixed with
{ tr …; echo; }, and test 3 now asserts the section boundary survives.One more test-shape note: four of these tests were passing vacuously before the
sectionhelperwas fixed — it prepended
^to patterns that already carried one, so the range ran to end-of-outputand every absence assertion was trivially true. The suite is only trustworthy because the assertions
now slice a named section with a start-inclusive, terminator-exclusive range.
Commit 2 declares what was measured
24 repo additions folded into
packages/blankpad.pkgs. Verified both directions on the real box:install.sh --host blankpad→repo pkgs: delta empty — nothing to install(so the running boxchanges nothing; this is purely what a rebuild reproduces)
pkg-inventory.sh→ 1 remaining addition:nirimod-git, deliberately undeclaredKnown gap, documented rather than papered over:
aur.pkgsis read by every host, so adesktop-only AUR package cannot be declared there without making the headless servers build a niri
module.
nirimod-gitstays undeclared untilinstall.shgrows a per-host AUR manifest. Flatpak hasthe same shape in reverse:
install.shinstalls no flatpaks, so the Discord app is reported, notdeclared.
Verification
bats tests/87/87 (71 existing + 16 new),kb-lint.sh omp-docs0,kb-coverage.py0. Real-boxruns shown above against
/var/log/pacman.logand the live package database — the tests stubpacmanandflatpakand touch neither.Docs:
omp-docs/tools/packages.mdnow owns the package tier (install.shhad never beendocumented), with the rebuild procedure, the baseline law, the
-Sllaw, the orphan law, and bothpipe traps. Indexed in
omp-docs/tools/index.md.cwd: /home/blank/.omp/wt/wt-20261019-125509-7c8a99d · session: 01a155bb-6474-7366-be9d-629f74af0e05
Build worktree:
~/.omp/wt/wt-pkginvofforigin/main. Refs #51