<feed xmlns='http://www.w3.org/2005/Atom'>
<title>archsetup/tests, branch claude/hungry-franklin-09fffc</title>
<subtitle>Builds a full dev workstation from a bare Arch Linux install.
</subtitle>
<id>https://git.cjennings.net/archsetup/atom?h=claude%2Fhungry-franklin-09fffc</id>
<link rel='self' href='https://git.cjennings.net/archsetup/atom?h=claude%2Fhungry-franklin-09fffc'/>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/archsetup/'/>
<updated>2026-10-17T12:07:25+00:00</updated>
<entry>
<title>feat(keyring): ship a patched gnome-keyring and install it from the repo</title>
<updated>2026-10-17T12:07:25+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-10-17T12:07:25+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/archsetup/commit/?id=cd6e11b46ecf2b56bf63d6c11886dccc70875e10'/>
<id>urn:sha1:cd6e11b46ecf2b56bf63d6c11886dccc70875e10</id>
<content type='text'>
gnome-keyring 48 through 51.1 aborts when a client's first OpenSession is dispatched before the daemon has created its per-client record. Each abort drops the unlocked state and the next request pops the password dialog. On ratio that happened about three times a day from 2026-10-11, when the ChatGPT desktop app's codex app-server started bursting keyring reads. The same-day glib and keyring upgrades were a coincidence, since 50.0 reproduces it too. Upstream's fix (merge request !112) has sat unmerged since August.

packages/gnome-keyring is Arch's recipe for 1:51.1-1 built from GNOME's tarball plus one patch. It registers the client at the point of use and returns a real D-Bus error instead of completing OpenSession with a NULL body. It resolves a collection alias only after the caller's session exists. It also locks the alias table so the worker-thread filter can't read an entry the main thread is freeing, a pre-existing defect that AddressSanitizer confirmed. I chose pkgrel 1.1 so stock sorts below the rebuild and any future Arch or upstream fix replaces it on a normal upgrade. Nothing pins the package.

install_gnome_keyring_fix replaces the plain stock install, which pacman's --needed would otherwise use to downgrade the rebuild on every re-run. It installs stock only when no gnome-keyring is present, compares versions with vercmp, then stages the recipe into the source dir and builds it with makepkg as the user. The rebuild is a no-op, anything newer skips with a visible note, and a failed build leaves stock in place. I also corrected the keyring-dir comment: the default alias only picks the collection and doesn't unlock anything under autologin.

working/keyring-crash holds the probe, the guest fixture and runners, and the deployment script with backup and rollback. status.org there records the cause and results. Stock aborts on the probe's first batch. The patched daemon, its sanitizer build and the installed package pass every mode, and the upstream suite scores the same for both.
</content>
</entry>
<entry>
<title>feat: install the ChatGPT desktop app from OpenAI's pacman repo</title>
<updated>2026-10-12T06:19:10+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-10-12T06:19:10+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/archsetup/commit/?id=e530fd597b62bda520f9804be7da216eea582750'/>
<id>urn:sha1:e530fd597b62bda520f9804be7da216eea582750</id>
<content type='text'>
OpenAI publishes the ChatGPT (Codex) desktop app for Arch as chatgpt-bin from its own signed pacman repository, and that's how ratio and velox got it on 2026-10-11. configure_openai_chatgpt_repo reproduces that setup. It fetches the repository signing key and accepts it only when the primary fingerprint matches the pin OpenAI's installer carries. It writes the same drop-in bytes OpenAI's installer writes, so a machine that installer already configured is left alone. Then it adds the Include to pacman.conf and refreshes the databases. supplemental_software installs chatgpt-bin next to claude-desktop.

I took the repository over the AUR chatgpt-desktop repackage: the repo keeps the app on ordinary pacman upgrades under OpenAI's signature, and the AUR package conflicts with chatgpt-bin on a machine already using the repo.

An untrusted key imports nothing and leaves pacman.conf alone. With the Include present and the key untrusted, TrustedOnly would fail every later pacman transaction. The step returns non-zero instead and chatgpt-bin is skipped.
</content>
</entry>
<entry>
<title>feat(audio): ship the Framework 13 AMD internal-mic workaround</title>
<updated>2026-10-06T19:58:40+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-10-06T19:58:40+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/archsetup/commit/?id=5189b7cdc7647f92382ddda3ca7d1a30fcffa4ce'/>
<id>urn:sha1:5189b7cdc7647f92382ddda3ca7d1a30fcffa4ce</id>
<content type='text'>
On the Framework 13 with AMD Ryzen AI 300 the kernel's ACP PDM digital-mic path returns a constant full-scale rail instead of audio. With UCM on, that broken DMIC is the only internal mic PipeWire offers, so the built-in microphone is dead in every app. The new step installs a WirePlumber drop-in that turns UCM off and disables the standalone PDM card so nothing can select it. Without UCM the ALC285 falls back to its analog-stereo profile, whose internal-mic pin carries real audio.

I gated it on DMI rather than on the acp-pdm-mach card or the hostname. The card exists on every AMD laptop with a PDM mic and stays present after the upstream fix lands, so its presence says nothing about whether the workaround belongs. The fix belongs to the board, not to one machine. The match needs all three of Framework, "Laptop 13" and "AMD Ryzen AI 300". That rules out the Framework Desktop ("AI Max 300") along with the Intel 13, the Ryzen 7040 13 and the 16. The rule inside the drop-in matches the PDM card by name pattern for the same reason: the gate covers the whole board family, so the rule can't pin the one PCI path velox enumerates.

The drop-in ships as a file under configs/, so a re-run refreshes it and deleting it plus a wireplumber restart reverts the machine. The installer doesn't restart wireplumber itself: there's no user session bus during install, so the drop-in applies when wireplumber next starts.

The audio failure taxonomy gains a row for the bug and a Cluster 5 note, so the audio doctor's use-ucm remedies treat the installed drop-in as the fix rather than as an offending config to remove.
</content>
</entry>
<entry>
<title>docs(bluetooth): say what tlp resume does on the resume edge</title>
<updated>2026-10-06T07:41:47+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-10-06T07:41:47+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/archsetup/commit/?id=d6c51ae3ae0cd51b3f3832e831e1d2c28f6b2276'/>
<id>urn:sha1:d6c51ae3ae0cd51b3f3832e831e1d2c28f6b2276</id>
<content type='text'>
The hook's header and its test docstring said the resume edge has no owner, which contradicted the later paragraph on tlp resume replaying the saved rfkill state. Both now say the replay puts bluetooth back where it was at suspend and applies no policy, so a block present at suspend stays.
</content>
</entry>
<entry>
<title>fix(tlp): install the bluetooth resume hook where systemd-sleep looks</title>
<updated>2026-10-06T04:34:54+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-10-06T04:33:44+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/archsetup/commit/?id=1e134f5022f626ed36279915369c06fa6df0e76e'/>
<id>urn:sha1:1e134f5022f626ed36279915369c06fa6df0e76e</id>
<content type='text'>
systemd-sleep scans only /usr/lib/systemd/system-sleep/. The step created /etc/systemd/system-sleep/ and installed the hook there, so it never ran. Velox came out of about fifteen hibernate cycles since 08-21 with bluetooth rfkilled, and TLP's own resume hook carried the block forward every time. I dropped the mkdir, since the systemd package owns the real directory. The step's test now asserts the full install list, so a copy under /etc fails it.
</content>
</entry>
<entry>
<title>fix: declare libreoffice-fresh, imv and git-lfs</title>
<updated>2026-09-25T17:05:29+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-09-25T17:05:29+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/archsetup/commit/?id=dec0446b7e990177edc34b071ba63c4942e867be'/>
<id>urn:sha1:dec0446b7e990177edc34b071ba63c4942e867be</id>
<content type='text'>
Each of the three backs something the dotfiles already ship, and each fails quietly rather than loudly when it's missing.

libreoffice-fresh is what the mimeapps.list defaults for presentations, documents and spreadsheets resolve to. With the package absent, xdg-mime doesn't error. It falls through to the next application claiming the type, which on a machine carrying the winvm dotfiles is powerpoint.desktop. Every .pptx opened a Windows VM for a month and the only symptom was that it felt slow.

imv is what gui-open --image execs. git-lfs is required by any repo tracking globs in LFS, which fails every checkout without it.

All three were installed before the 2026-08-13 rebuild and gone after it, because nothing in the installer declared them.

I made the test run supplemental_software with pacman_install stubbed as a recorder, asserting each package is invoked exactly once under both desktop environments. Running the function beats matching its source because only a run resolves the conditionals that could make "every machine installs this" false. A fourth assertion pins ranger present under dwm and absent under hyprland, so a harness that stopped resolving conditionals fails loudly instead of passing the other three vacuously.
</content>
</entry>
<entry>
<title>fix(install): mask passim when fwupd is installed</title>
<updated>2026-09-13T12:02:39+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-09-13T12:02:39+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/archsetup/commit/?id=495d16d17b49d37336f1c1189543928b396449e0'/>
<id>urn:sha1:495d16d17b49d37336f1c1189543928b396449e0</id>
<content type='text'>
passim is fwupd's LAN metadata-sharing daemon and listens publicly on 0.0.0.0:27500. Any fwupdmgr run D-Bus-activates it. The unit is static, so disabling it is a no-op and it comes back on the next run. Masking is what holds. Every install gets it, not only laptops: the listener is public on any host, and ratio has run with it masked since July without fwupd missing it. Velox had only been disabled and got the mask on 2026-09-12.
</content>
</entry>
<entry>
<title>fix(install): stop enabling the WS-Discovery host daemon</title>
<updated>2026-09-13T12:02:39+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-09-13T12:02:39+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/archsetup/commit/?id=64ccceff708535cba51e16c36cd77c14b6c8a729'/>
<id>urn:sha1:64ccceff708535cba51e16c36cd77c14b6c8a729</id>
<content type='text'>
wsdd.service advertises this machine as a Samba host to Windows clients. Nothing the installer sets up runs Samba, so it advertised a share server that doesn't exist while listening on every interface, VPN and tailscale links included. Browsing Windows shares is the other direction: gvfs-wsdd spawns its own wsdd in discovery mode, so the package stays and only the service goes. A re-run on a machine set up before this disables the unit. A fresh install has no unit yet and skips quietly.
</content>
</entry>
<entry>
<title>feat(install): default DNS over TLS off on the Proton tunnel links</title>
<updated>2026-09-13T12:02:39+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-09-13T12:02:39+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/archsetup/commit/?id=20196051d9cd25d775373a48fcf05dc258497301'/>
<id>urn:sha1:20196051d9cd25d775373a48fcf05dc258497301</id>
<content type='text'>
The resolved drop-in pins DNSOverTLS=yes for every link. Proton VPN (proton0) and the static Proton WireGuard profiles (wgpvpn) push an in-tunnel resolver (10.2.0.1) that answers plain port 53 and never completes TLS on 853, so every lookup through the tunnel hung. The Proton client recreates its NetworkManager profile on each connect, so a per-profile setting can't stick.

NM pushes a [connection-tunnel-dot] default matched on those two interface names to resolved on every activation. Wifi and everything else keep the strict setting. Ratio and velox already carry the drop-in by hand. This makes a rebuild carry it too.
</content>
</entry>
<entry>
<title>feat(bluetooth): restore the radio after a sleep cycle</title>
<updated>2026-08-21T14:25:23+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-08-21T14:25:23+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/archsetup/commit/?id=d67c9fb1c397072beb3ca57e6bfd4ef54dcc266a'/>
<id>urn:sha1:d67c9fb1c397072beb3ca57e6bfd4ef54dcc266a</id>
<content type='text'>
Masking systemd-rfkill so TLP can own radios leaves the resume edge with no owner at all. TLP's sleep hook runs `tlp resume`, but its setting is DEVICES_TO_ENABLE_ON_STARTUP, and TLP has no ON_RESUME to pair with it. WiFi survives anyway because NetworkManager unblocks itself. Bluetooth has no equivalent, so it comes back soft-blocked and stays that way.

After a hibernate the controller is wedged as well, which is why unblocking alone is not the fix. I cleared rfkill by hand on velox this morning and scanning still returned nothing. Zero devices, in a room that gave seventeen a minute after the driver was reloaded. bluetoothd had logged "Failed to set mode" and "Failed to add device" at the instant of resume.

So the hook reloads btusb on a hibernate-class wake, then unblocks. That order matters: a freshly loaded btusb can come up blocked and would undo an earlier unblock. A plain suspend only unblocks, since it brings USB back intact and reloading there would tear down a working adapter for nothing.

It acts only where TLP's own config asks for bluetooth, so it re-asserts a declared intent instead of inventing one and a machine that deliberately keeps the radio off keeps it off. It exits zero on every path, because a failing sleep hook logs noise that outlives the cause.

The install sits next to the mask that creates the gap rather than beside the other installs, and creates /etc/systemd/system-sleep first. Arch does not ship it and install_executable is a plain cp, so without the mkdir a fresh machine would warn and end up with no hook.

Ten tests, and I checked they bite: dropping the TLP intent guard fails three, moving the unblock before the reload fails one.

This surfaced because hibernate was switched back on. The gap predates that and would have shown up the first time the laptop slept.
</content>
</entry>
</feed>
