Build the two applications nobody packages, on purpose rather than in passing

Claude Desktop and ChatGPT Desktop ship for macOS and Windows. The Linux path for
both is a community wrapper that converts the official build into an RPM -- so
what lands is still a package dnf owns and can remove, which is the part of the
dnf/flatpak rule that actually matters. What they need an exception for is the
build itself, and there is no packaged form to prefer over it.

`panama app` builds one by name, and is deliberately not part of ./install. A
source build is slow, wants the network throughout, and depends on an upstream
that moves -- twenty minutes in, an error, with nobody at the keyboard, which is
the exact failure the interview exists to prevent. Asking for one is something
you do on purpose, and it is also the rebuild path when a new version ships.

Nothing is pinned. Each build takes the current default branch and the current
upstream release, and reports a failure rather than working around it, leaving
the tree where the error can be read. sunhat pinned versions and every pin was a
404 within a release cycle.

Adding one is adding a file to setup/apps/, and the file has to say why the
exception exists -- the contract fails a definition that does not, because the
guard against this list growing by habit is having to write the reason down.
sunhat had seventy-odd installers and a reason recorded for none of them.

The contract had a bug worth recording: `while read` on the right of a pipe runs
in a subshell, so two of its three per-definition checks recorded findings into
an array that went out of scope at the end of the loop. It reported PASS on a
definition with no description and no build function. Found by standing one in
deliberately and noticing only the third check spoke up.

Also: nautilus-open-any-terminal is now declared, and Panama's copy of the
extension is gone. Fedora packages that extension AND its gsettings schema, and
Panama shipped its own fork of the .py over the same path while declaring
neither -- so a fresh machine got an extension whose schema did not exist. It
worked here only because the RPM has been installed since sunhat. The fork was
also 63 lines behind the packaged version, missing its newer Nautilus and Caja
handling.

Auditing the rest of config/copy for the same shape found nothing else: dnf.conf
is a config file its package expects to be replaced, and the GPU udev rules are
Panama's own.

125 contracts pass.

Claude-Session: https://claude.ai/code/session_01NvgBuSWB5sE43yWmg21ozj
This commit is contained in:
Gabriel Brown
2026-08-20 23:00:21 -04:00
parent d61792433c
commit f457c1eb9f
7 changed files with 265 additions and 614 deletions
+13
View File
@@ -88,6 +88,7 @@ config/
old/ Backups of whatever was replaced (gitignored)
wallpapers/ Copied into ~/Pictures/Wallpapers when absent
setup/
apps/ Applications built from source, one file each
packages/ One package per line; extras/ holds the optional categories
scripts/ Run in order by ./install
tests/ Contracts. See below
@@ -126,8 +127,20 @@ panama edit # open it in Neovim
panama doctor # what is actually running, not what was installed
panama test # every contract, or a subset by pattern
panama upgrade # re-run ./install from anywhere
panama app # applications no repository carries; build one by name
```
`panama app` is deliberately not part of `./install`. Everything else Panama
installs comes from dnf or Flathub; these are built from source because no
packaged form exists, and a source build is slow, wants the network throughout,
and depends on an upstream that moves. That is the failure the interview exists
to prevent, so asking for one is something you do on purpose — and it is also
how you rebuild when a new version ships. Nothing is pinned: each build takes
the current upstream and reports a failure rather than working around it.
Adding one is adding a file to `setup/apps/`, and the file has to say why the
exception exists.
None of the scripts in this repository carry a `.sh` extension. A shebang and
the executable bit already select the interpreter, and the extension only
becomes something to keep in sync — which it did not stay.