<feed xmlns='http://www.w3.org/2005/Atom'>
<title>archsetup/tests, branch main</title>
<subtitle>Builds a full dev workstation from a bare Arch Linux install.
</subtitle>
<id>https://git.cjennings.net/archsetup/atom?h=main</id>
<link rel='self' href='https://git.cjennings.net/archsetup/atom?h=main'/>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/archsetup/'/>
<updated>2026-07-25T20:00:56+00:00</updated>
<entry>
<title>refactor(prototypes): merge casting widgets into Dupre kit</title>
<updated>2026-07-25T20:00:56+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-07-25T20:00:56+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/archsetup/commit/?id=2c416f5361ed10cd73eb72eb5062261b2e73c18f'/>
<id>urn:sha1:2c416f5361ed10cd73eb72eb5062261b2e73c18f</id>
<content type='text'>
</content>
</entry>
<entry>
<title>refactor(test): share runner and scenario contracts</title>
<updated>2026-07-25T20:00:44+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-07-25T20:00:44+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/archsetup/commit/?id=d52da959bc416e09bc0f3e54994563bd80e19c64'/>
<id>urn:sha1:d52da959bc416e09bc0f3e54994563bd80e19c64</id>
<content type='text'>
</content>
</entry>
<entry>
<title>fix(installer): harden the lock path against the AMD-iGPU DPMS lockout</title>
<updated>2026-07-25T04:16:56+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-07-25T04:16:56+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/archsetup/commit/?id=a9391c90f07a564693bb1994f4f8fe6470efb3a2'/>
<id>urn:sha1:a9391c90f07a564693bb1994f4f8fe6470efb3a2</id>
<content type='text'>
An idle lock on this Strix Halo box wedged the whole session: hyprlock died and the compositor stayed locked with no prompt, recoverable only from a console. It's a documented AMD-integrated-Radeon failure (hyprlock#953, Hyprland#5822) -- a display power cycle via DPMS invalidates the GPU resources the lock client holds, so hyprlock loses its surface and exits without unlocking. No coredump, no OOM; the GPU pulls the rug out.

Two installer changes, both scoped and tested:

update_grub_cmdline adds amdgpu.runpm=0 on AMD machines only. Disabling GPU runtime power management keeps those resources valid across a display cycle -- the root fix. A no-op on Intel/NVIDIA, and it rides the existing merge so no boot-critical token is touched.

configure_hyprlock_pam writes a complete PAM stack. The hyprlock package ships only `auth include login`, leaving account and session uninitialised so pam_end() crashes on cleanup -- a separate documented lockout cause. All three phases now resolve through login, inheriting the keyring the graphical login uses.

CALL_SITES pins both new wirings. 372 unit tests, exit 0; each addition proven by reverting it.
</content>
</entry>
<entry>
<title>fix(installer): derive the wireless regdom by pattern and retire two inert hooks</title>
<updated>2026-07-24T17:57:29+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-07-24T17:57:29+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/archsetup/commit/?id=249bb93b29ef471a6529d2d5bb96df87e1ac1a8a'/>
<id>urn:sha1:249bb93b29ef471a6529d2d5bb96df87e1ac1a8a</id>
<content type='text'>
The regulatory domain came from a fixed offset, "${current_lang:3:2}", which is right only for a two-letter language code. validate_config accepts three-letter languages and glibc ships 75 of them, so ber_DZ.UTF-8 yielded "_D" and C yielded "". A garbage region matches no line in the regdom file, sed exits 0, and the || error_warn could never fire, so WiFi sat on the conservative "00" domain in silence.

locale_country matches the _CC group instead of counting characters, and set_wireless_regdom verifies the substitution actually landed. That second half is the part worth keeping: every sed -i in this installer shares the stance that a no-match is indistinguishable from success, and this is the one where the silence costs something.

switch_udev_hook_to_systemd now rewrites keymap and consolefont to sd-vconsole when it performs the swap. Both are busybox-only run_hook scripts, so a systemd initramfs installs them and never runs them, and this machine's own HOOKS line carries all three today. Cosmetic on a box that just boots, and not cosmetic on one asking for a passphrase with a non-US layout, which is exactly what sd-vconsole restores. The two collapse into one entry and an existing sd-vconsole is not duplicated.

Before building it I answered the question the task left open. This machine is KEYMAP=us with no encrypt hook, so no prompt, but the function runs on LUKS machines where there is one, and sd-vconsole ships with mkinitcpio.

364 unit tests, exit 0. Adds tests/installer-steps/test_wireless_regdom.py (16) and 7 sd-vconsole cases. Every guard proven by deleting it, including one whose first version passed with the guard gone because a later check caught the same case.
</content>
</entry>
<entry>
<title>fix(installer): eight fixes from an overnight bug-hunt and its review</title>
<updated>2026-07-24T17:19:04+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-07-24T17:19:04+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/archsetup/commit/?id=3becfac66dc569e71d0f6085fb56e92cfa6d626a'/>
<id>urn:sha1:3becfac66dc569e71d0f6085fb56e92cfa6d626a</id>
<content type='text'>
I squashed these because the per-bug reasoning lives in todo.org, which this commit carries.

Two could cost a machine. configure_initramfs_hook swapped the udev hook for systemd on a LUKS root, leaving a standalone encrypt hook under an init that never runs it. The rebuild succeeds and the installer exits clean, then the root won't unlock at the next boot. trim_firmware ran pacman -Rdd against twelve firmware packages behind a DMI gate reading product_name, where "Framework" never appears. That left it dead on the hardware it targets, and dangerous to fix the obvious way: this machine is a Framework Desktop whose Ryzen iGPU needs the amdgpu firmware. It refuses on PCI modalias evidence now.

wipedisk discarded before it checked. blkdiscard ran with -f, which disables the exclusive open, so picking the wrong disk destroyed a live filesystem and then reported that nothing had happened.

Four more are smaller. The NVIDIA preflight aborted dwm and headless installs over a driver floor they never need. zfs-replicate exited 0 after every dataset failed. Unattended installs blocked on two prompts, and the first fix for that inherited a [Y/n] default into passwordless console login. A fresh install left the dotfiles repo permanently dirty.

The review found a pattern worth more than any single fix. Helpers had thorough tests and none proved they were called. Deleting the call left five suites green, including the guard on that pacman -Rdd. CALL_SITES now pins nine caller/callee pairs.

The suite runs 341 tests at exit 0, with no new shellcheck findings. I proved every guard by deleting it and watching the intended test go red.
</content>
</entry>
<entry>
<title>fix(probe): scroll instantly so the drag checks stop missing the widget</title>
<updated>2026-07-23T16:00:01+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-07-23T16:00:01+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/archsetup/commit/?id=afd2ff1459387af5dcd793071da5860fec83a1e8'/>
<id>urn:sha1:afd2ff1459387af5dcd793071da5860fec83a1e8</id>
<content type='text'>
The gallery sets `html{scroll-behavior:smooth}`, so `scrollIntoView` animates. The fader check slept a fixed 200ms and then read `getBoundingClientRect`, which sometimes caught the rect mid-scroll. The drag went out at stale coordinates. The press landed off the fader, and the check reported a dead widget about one run in six.

The probe has no reason to animate, so it now scrolls with `behavior:'instant'`. That removes the race rather than waiting it out. A `settledGeom` read still guards against zoom and column relayout. It requires the geometry to be stable twice over and on screen, because two matching samples taken before a relayout begins look identical to a settled one.

Both checks now assert the press landed on the widget before dispatching. That assertion earned itself during the fix. My first attempt polled for a stable rect and returned pre-scroll coordinates every time, and the press-landed check caught it by printing a point below the window.

The toggle check has the same fixed-sleep shape and failed that way while I was diagnosing, so it got the same fix.

Verified over 8 consecutive runs against the measured 1-in-6 rate. All three checks pass with the press point identical each time, so the result is deterministic rather than lucky.
</content>
</entry>
<entry>
<title>fix(installer): merge GRUB cmdline instead of overwriting it</title>
<updated>2026-07-21T11:50:12+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-07-21T11:50:12+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/archsetup/commit/?id=f9da097042a28c0bdff037b0b53ae20d567cd5c4'/>
<id>urn:sha1:f9da097042a28c0bdff037b0b53ae20d567cd5c4</id>
<content type='text'>
configure_grub replaced the whole GRUB_CMDLINE_LINUX_DEFAULT line with a fixed string. A base install that had set cryptdevice=, resume=, zfs=, or any other boot-critical parameter lost it, and the grub-mkconfig that follows baked an unbootable config.

update_grub_cmdline now reads the current value and merges: every existing token survives, archsetup's tokens are added, and where both set the same key archsetup's value wins. A safety check refuses to write when any existing token's key would vanish from the merge, and the write goes through awk + mv so paths with slashes can't break the substitution. The block also backs up /etc/default/grub before editing, which the other system-file edits already did.
</content>
</entry>
<entry>
<title>test: fix the weak assertions from the S6/S7 audit</title>
<updated>2026-07-21T04:44:35+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-07-21T04:44:35+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/archsetup/commit/?id=179fbd55795c27ed388860e3c6b3d8c0651422ac'/>
<id>urn:sha1:179fbd55795c27ed388860e3c6b3d8c0651422ac</id>
<content type='text'>
The Hyprland socket check passed a shell glob to `test -S`, which breaks at both edges: zero matches leaves the literal pattern and several instances pass multiple args, failing for reasons unrelated to the socket. It now resolves one socket with find -type s.

The gen_tokens degenerate-marker test asserted properties loose enough to pass on garbled output. It now pins the exact reconstruction as a characterization, with the impossible-input caveat stated, so any change to the branch surfaces.

The gallery tick count leaned on split-string omit-nulls arithmetic that happened to equal the match count. It now counts occurrences directly. gallery-widget-write-svg, the last uncovered public entry point, gets a Normal-case test.
</content>
</entry>
<entry>
<title>fix(design): clamp the gauge once so needle and readout agree</title>
<updated>2026-07-21T04:43:11+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-07-21T04:43:11+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/archsetup/commit/?id=552736ec479903edbc4576182f52469c6dc222c5'/>
<id>urn:sha1:552736ec479903edbc4576182f52469c6dc222c5</id>
<content type='text'>
The needle angle clamped to 0-100 while the readout rendered the raw value, so a gauge fed 150 pinned the needle at +60 degrees under a "150%" label. A shared gallery-widget--clamp-value now feeds both halves.

Two hygiene fixes ride along. The module requires cl-lib explicitly instead of leaning on svg.el's transitive require. The token-file load resolves its directory through gallery-widget--source-dir, which falls back to default-directory when the form is re-evaluated outside a load and outside a file buffer (both location variables are nil there and file-name-directory errored).
</content>
</entry>
<entry>
<title>fix(test): harden four VM-framework paths from the S5 audit</title>
<updated>2026-07-21T04:40:56+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-07-21T04:40:56+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/archsetup/commit/?id=866d327f0bad3a0730801a22654bac45e02ee14b'/>
<id>urn:sha1:866d327f0bad3a0730801a22654bac45e02ee14b</id>
<content type='text'>
init_vm_paths suffixed the disk and NVRAM by FS_PROFILE but left PID_FILE, MONITOR_SOCK, and SERIAL_LOG shared. A concurrent btrfs and zfs run read each other's PID file, so the second run's stop logic could kill the first run's VM. All runtime paths now carry the suffix.

kill_qemu sent kill -9 and returned without waiting, so the force-kill fallback could run qemu-img snapshot against a qcow2 the dying qemu still held locked (the restore failed silently behind || true, leaving the base image dirty). It now reaps or polls the process to death before returning.

debug-vm.sh hardcoded the btrfs base-disk name, so FS_PROFILE=zfs booted the wrong base or fatalled. It now takes DISK_PATH from init_vm_paths.

Both runners reported a completion-marker grep as "ArchSetup Exit Code," but the installer runs detached with set -e off, so its real exit status was never captured -- a run that errored mid-way and still reached its last line reported exit 0. The flag is now ARCHSETUP_COMPLETED with the report labeled honestly; Testinfra stays the pass/fail authority.
</content>
</entry>
</feed>
