aboutsummaryrefslogtreecommitdiff
Commit message (Collapse)AuthorAgeFilesLines
* docs: add per-host override spec and backlog tasksCraig Jennings2026-05-262-4/+260
| | | | | | I added a spec for a per-machine override mechanism (docs/PLAN-per-host-overrides.org). It proposes per-host stow tiers keyed on hostname, with the existing conf.d glob as the first tenant, so HiDPI scaling can differ between ratio and velox without per-app patches. It's gated on review. I also updated the task list: added a pocketbook development backlog and a waybar timer module task, fleshed out the per-host task with a spec link, and rewrote the stale velox "pocketbook not installed" review item as a dated entry now that nothing installs it.
* refactor: fold pocketbook in-tree and drop its install stepsCraig Jennings2026-05-2620-3/+1054
| | | | | | Pocketbook is nowhere near ready, so I pulled it back from publication: deleted the github mirror and the cjennings.net repo, removed the server mirror hook, and copied the package into pocketbook/ here until it's ready to spin back out. Dropped the steps that provisioned it on a fresh install: the gtk4-layer-shell dep and the pip install in archsetup, and the clone in post-install.sh. That clone pointed at the now-deleted github repo, so it would have failed a fresh run regardless. Re-wiring the install is tracked in the pocketbook backlog.
* chore: log dotfiles-separation progress and file processed handoffsCraig Jennings2026-05-235-0/+193
|
* fix(testing): expect ~/.dotfiles symlink target in dotfiles validationCraig Jennings2026-05-221-2/+4
|
* docs(claude): correct velox/ratio desktop env (both run Hyprland)Craig Jennings2026-05-221-2/+3
|
* docs(claude): document the external dotfiles repo and migrationCraig Jennings2026-05-221-3/+41
| | | | | | Phase 2 moved the dotfiles into their own repo at git.cjennings.net/dotfiles.git, so a fresh install clones DOTFILES_REPO to ~/.dotfiles and stows per DESKTOP_ENV. I documented that, the DOTFILES_REPO/BRANCH/DIR config keys, the DOTFILES= Makefile override, the post-install pull-and-restow flow, and the steps to migrate an existing machine off the in-repo dotfiles/. The migration note calls out quitting Hyprland first: unstowing while it runs makes it write a stub hyprland.conf into the gap, which blocks the restow. The in-repo dotfiles/ stays until ratio and velox migrate, then it gets removed.
* refactor(testing): clone the dotfiles repo into the VM testCraig Jennings2026-05-222-1/+32
| | | | The dotfiles live in a separate repo now, so the VM test clones that source, bundles it, and clones from the bundle into /tmp/dotfiles-test on the VM. archsetup's in-VM clone of DOTFILES_REPO then resolves against the local path with no network. DOTFILES_SOURCE overrides the source for testing a local checkout instead of the published repo. The cleanup trap tracks the host-side bundle and clone, so a mid-run abort leaves nothing behind.
* feat(archsetup): clone dotfiles repo and stow per DESKTOP_ENVCraig Jennings2026-05-222-33/+84
| | | | | | Dotfiles now live in their own repo (git.cjennings.net/dotfiles.git), so archsetup clones DOTFILES_REPO to the user's ~/.dotfiles and stows the right tree for the chosen DESKTOP_ENV: dwm and hyprland get common/ plus their own layer, none gets the standalone minimal/ tree. The clone runs as the target user, so the working tree is user-owned with no chown-after race. Add DOTFILES_REPO / DOTFILES_BRANCH / DOTFILES_DIR config keys with the same injection guard the other repo keys carry. If the clone doesn't produce a git checkout, error_fatal stops the install rather than silently skipping the restore step that reverts what stow --adopt pulled in. That restore now runs for every DESKTOP_ENV, including none — minimal/ ships the .bashrc/.bash_profile that collide with /etc/skel, so its --adopt needs the same cleanup.
* docs(plan): use SSH push URL and add claude.sh to minimal treeCraig Jennings2026-05-221-2/+6
|
* chore: tag sub-30-minute tasks with :quick:Craig Jennings2026-05-211-18/+18
| | | | I walked the open work and tagged 18 tasks finishable in 30 minutes or less — CSS spacing tweaks, single-file archsetup edits, gitignore-and-untrack cleanups, and config-flag gates. The tag filters the agenda down to quick wins.
* chore: cancel obsolete X11 VT-switch task, demote 6 undated A's to BCraig Jennings2026-05-211-9/+180
| | | | | | | | The priority scheme requires an [#A] to carry a date. Six were undated aspirational backlog with no active work — the CI/CD pipeline, recovery-script generation, the package-inventory trio (inventory system, monthly review, automated comparison), and security education — so I dropped them to [#B]. I cancelled "Prevent X termination and VT switching." Its whole approach is xorg.conf.d with DontVTSwitch and DontZap, dead now that these machines run pure Wayland with no XWayland. I also recorded today's review date on the open tasks I walked.
* feat(notify): add --silent flag, volume knob, and level sound filesCraig Jennings2026-05-2112-10/+271
| | | | | | | | The touchpad toggle's notification was too loud, and the eight notify sounds varied by ~13 dB in RMS loudness — bug and fail came out two to three times louder than info or security. I added a --silent flag to notify (shows the popup, plays no sound) and a NOTIFY_VOLUME knob (paplay scale, default 65536) so the master level can drop without re-encoding. toggle-touchpad now passes --silent on both enable and disable. normalize-notify-sounds.sh measures each .ogg and shifts it to a uniform -31 dB mean. It writes through the file instead of mv-ing over it, so the stow symlinks survive when the script runs against the live sound dir. I re-encoded all eight sounds to the new level. Tests: a new tests/notify suite (12 tests) covers --silent, the volume knob, flag composition, and the error paths.
* chore: log UI follow-up bugs and ignore dated secret backupsCraig Jennings2026-05-212-56/+61
| | | | I logged three follow-ups: uneven waybar indicator spacing, airplane-mode toggle hardening (a laptop guard and a brightness fallback), and rectangular wlogout exit-menu buttons. I also moved the finished touchpad and airplane-mode entries into the resolved section, and added a gitignore rule so dated .bak backups stay on disk but out of git.
* feat(hyprland): add airplane-mode waybar toggleCraig Jennings2026-05-217-0/+652
| | | | | | | | I added a laptop-only waybar button that drops the machine into a low-power state and restores it on a second click. Engaging turns wifi off, sets the CPU energy-performance preference to power, dims the backlight to 35%, and stops network-only services (tailscale, proton-vpn, avahi, cups, wsdd, geoclue, sshd, fail2ban, syncthing). Bluetooth is left alone so earbuds keep working. Disengaging replays the state recorded when airplane mode was engaged rather than writing hardcoded defaults. A lever already in its low-power position is left untouched: wifi that was already off stays off, and a service that was already stopped isn't restarted. The indicator hides itself on machines with no battery, so desktops never show the button. State lives in $XDG_RUNTIME_DIR/airplane-state, and the bar refreshes the moment the toggle fires via a realtime signal.
* feat(authinfo): add calendar feed URLs for calendar-syncCraig Jennings2026-05-211-0/+0
| | | | The .ics feed URLs are secret tokens, so I keep them in the encrypted store instead of a plaintext config. Emacs calendar-sync looks them up at sync time through auth-source on the calendar-google, calendar-proton, and calendar-deepsat hosts. They travel across machines now with the rest of authinfo.
* chore(btop): sync config to v1.4.7 defaultsCraig Jennings2026-05-201-2/+16
|
* feat(hyprland): rebind monitor to Super+R, touchpad toggle to Super+MCraig Jennings2026-05-201-3/+3
| | | | Super+M now toggles the touchpad (M for Mouse), and the monitor scratchpad moves to Super+R (R for Resources, since btop is a resource monitor). The touchpad toggle was on Super+F9, which is now free. Super+Shift+M still switches to the monocle layout.
* chore: document a priority scheme and close two tasks in todo.orgCraig Jennings2026-05-201-5/+70
| | | | | | I added an A/B/C/D priority scheme to the task list: what each level means, the assignment criteria, and the rule that an [#A] task needs a scheduled or deadline date so priorities stay grounded in time. I also closed two finished tasks: the rm -rf path guard (done, with a pointer to the safe_rm_rf helper and its tests) and the touchpad toggle indicator (spec, implementation, and deploy record).
* feat(hyprland): add touchpad state indicator to waybarCraig Jennings2026-05-207-0/+212
| | | | | | | | The $mod+F9 toggle and the toggle-touchpad / touchpad-auto scripts already worked, but the scripts lived only in ~/.local/bin and were never committed, and there was no way to see the touchpad's state at a glance. I committed both scripts into the repo so stow installs them, and added a waybar indicator: a waybar-touchpad status script and a custom/touchpad module that shows a mouse glyph when the touchpad is on and a mouse-off glyph (in the dupre orange) when it's off. The scripts signal the module with pkill -RTMIN+9 waybar after each state change, so the icon updates the moment the touchpad toggles. touchpad-auto now runs at login via exec-once. The waybar-touchpad script has unit tests under tests/waybar-touchpad/ covering the enabled, disabled, and missing-state-file cases.
* fix(installer): guard constructed-path rm -rf deletesCraig Jennings2026-05-202-3/+229
| | | | | | | | Three rm -rf sites in archsetup delete paths built from variables: $state_dir for --fresh, and $source_dir/$prog_name for the git and AUR clone-retry cleanups. If a path variable were empty or malformed (preflight skipped, a degenerate git URL), the delete could expand to a top-level or otherwise unintended directory. I added a safe_rm_rf <path> <allowed_prefix> helper that refuses to run unless the target is absolute, free of '..', deeper than a bare top-level dir, strictly inside the allowed prefix, and a real directory rather than a symlink. On the happy path it delegates to rm -rf, so successful installs are unchanged. The helper is self-contained and defined before the top-level --fresh handler, which runs before the logging helpers exist. I covered the guard with unit tests under tests/safe-rm-rf/ that source the real function and exercise normal, boundary, and error cases against temp directories.
* chore: clean dead todo.org doc-links and file three docs to outboxCraig Jennings2026-05-204-6/+190
| | | | | | I removed three dead `file:` links in todo.org. They pointed at docs that were never written: testing-strategy.org, firmware-cleanup.org, and PLAN-browser-themes.org. Each task body already carries that content inline, so the links were dead weight. I also reworded the Testinfra task's lead-in that claimed the testing-strategy doc exists. I filed three resolved reference docs into assets/outbox/: the calendar-sync scrub note, the tmux copy-mode handoff, and the 2026-05-19 lint follow-up report.
* chore: log the calendar-secret scrub and queue URL rotationCraig Jennings2026-05-201-11/+21
| | | | Recorded the git-history scrub of the private calendar config and added a follow-up to rotate the three exposed feed URLs once I'm at ratio's GUI. Also closed the testing-docs and shellcheck tasks that shipped this round.
* fix: correct a POSIX-sh bashism in init and document two SC2034 casesCraig Jennings2026-05-203-2/+4
| | | | | | init runs under #!/bin/sh but used $(<file) to read /etc/hostname, a bashism that breaks on a strict POSIX sh. I switched it to $(cat) and quoted $interface_up in the same script. The two VM_IP assignments in the test scripts are read by the sourced validation.sh, which shellcheck can't follow, so they now carry a documented disable=SC2034 instead of a bare suppression. The rest of the shellcheck findings across the scripts are intentional (word-splitting on $SSH_OPTS, integer tests in [ ]) or already accepted, so I left them alone.
* docs: document the unit-test layer and add a make test-unit targetCraig Jennings2026-05-202-2/+37
| | | | | | The README only covered the VM integration harness. The unit suites under tests/ (one directory per script, fake binaries on PATH) went unmentioned, so a contributor adding a .local/bin script had no way to know they existed or how to run them. I added a make test-unit target that runs every tests/*/test_*.py suite explicitly. unittest discover can't find them because the per-script directory names are hyphenated and aren't valid package paths. Then I split the README Testing section into Unit and VM-integration layers, added a guide for adding a suite, and pointed Contributing at test-unit for script changes.
* chore: log AUR exit-0 fix verification and close the sub-taskCraig Jennings2026-05-191-6/+4
| | | | | | Two updates in todo.org. I marked the two dconf entries in the `[#B] Fix install errors` parent body as RESOLVED in `dc06895`, since the system-wide dconf db migration verified clean in last night's VM run. I converted the `*** TODO [#B] AUR exit-0 logged as error` sub-task into a dated event-log entry. The root cause was a bash if-compound semantics trap in `retry_install` (the if-compound exits 0 when no condition tested true, overwriting the eval's actual exit code that `last_exit_code=$?` was trying to capture). Today's VM run verified the fix: `mkinitcpio-firmware` and `tidaler` now report `error code: 1` instead of the misleading `error code: 0`.
* feat(tmux-util): add rename subcommand (fzf pick + prompt)Craig Jennings2026-05-193-9/+174
| | | | | | | | | | | | | | tmux-util rename closes out the original six-subcommand plan. The flow: 1. fzf-pick a session from the list. 2. Prompt for a new name on stdin. 3. Bail with a useful message on empty input, same-as-old, or conflict with an existing session. 4. Otherwise `tmux rename-session -t <old> <new>` and confirm. The conflict check uses `tmux has-session -t =<new>` with the same `=`-prefix exact-match guard as the go subcommand. Without it, tmux's default prefix matching would let `rename foo` succeed even when a session named `foobar` already exists, then surprise the user later. 5 new tests cover Normal cases (pick + rename happy path) and Boundary cases (no sessions, fzf cancel, empty new name, same-as-old no-op, conflict with existing session). The test harness's run_script grew an `stdin=` param so tests can feed the prompt input. fake-tmux picked up a rename-session handler that mutates the state file. Total suite: 48 tests, all green. Six subcommands shipped: go, pick, ls, find, reap, rename. The original "no args prints help" requirement still holds, and the stub-test for unimplemented subcommands got removed since everything's wired now.
* feat(tmux-util): add pick subcommand (fzf session switcher)Craig Jennings2026-05-193-4/+139
| | | | | | | | | | | | | tmux-util pick lists every session ("attached"/"detached" plus name), pipes it through fzf, and attaches or switch-clients to the chosen one (matching the go subcommand's inside-vs-outside-tmux discipline). If the user cancels fzf (Ctrl-C, Esc, empty selection), the pipeline returns empty and pick exits 0 without touching tmux state. The new fake-fzf testing fake is driven by env vars: - FAKE_FZF_CHOICE_LINE=<N>: return the Nth line of stdin as the selection - FAKE_FZF_CHOICE=<string>: return the literal string (ignores stdin) - (neither): exit 130 to simulate cancel 4 new tests cover Normal cases (pick second line, inside/outside tmux behavior) and Boundary cases (no sessions, user cancellation). Total suite: 43 tests, all green.
* feat(tmux-util): add find subcommandCraig Jennings2026-05-193-6/+130
| | | | | | | | | | | | | tmux-util find <pattern> walks every pane across every session, queries each pane's foreground command (`#{pane_current_command}`), and prints the location of any pane whose command matches the pattern. Output format: `<session>:<window>.<pane> <command>`, one per line. Pattern is a basic ERE (passed through `grep -E`), so anchors and alternation work. Substring matching is the common case. Exit code: - 0 with output: matches found - 1 with no output: no matches (lets you script around it) - 2 with usage on stderr: missing or empty pattern 7 new tests cover Normal cases (single match, multi-match across sessions, format verification) and Boundary cases (no matches, no sessions, missing pattern, empty pattern). fake-tmux now parses pid:cmd entries in the state file so panes can carry a synthetic command name. Total suite: 39 tests, all green.
* feat(tmux-util): add go subcommand (attach-or-create)Craig Jennings2026-05-193-1/+159
| | | | | | | | | | | tmux-util go <name> attaches to a session named <name> if it exists, creates it otherwise. Behavior depends on whether the caller is already inside tmux: - Outside tmux: `tmux attach-session -t <name>` (existing) or `tmux new-session -s <name> -c $PWD` (new). - Inside tmux (TMUX env set): `tmux switch-client -t <name>` (existing) or `tmux new-session -d -s <name> -c $PWD` followed by `switch-client` (new). Attaching from inside tmux would nest sessions and break the outer view, so the inside path uses switch-client instead. The existence check uses `tmux has-session -t =<name>` with the leading `=` to force exact-match. Without it, tmux does prefix matching, which would let `go foo` resolve to a session named `foobar`. I added 6 new tests covering both inside/outside-tmux paths, both create/attach paths, plus error handling for missing or empty name arguments. fake-tmux picked up handlers for new-session (mutates state), attach-session and switch-client (record-only), and the `=`-prefix form of has-session. Total suite: 32 tests, all green.
* feat(tmux-util): add ls subcommandCraig Jennings2026-05-193-21/+240
| | | | | | | | | | | | tmux-util ls is an opinionated replacement for `tmux ls` with columns for state (attached/detached), name, idle time (humanized), window count, and the current pane's cwd (tilde-fied if it sits under $HOME). The cwd query goes through `tmux display -p -t <session> '#{pane_current_path}'`, which returns the cwd of the active pane of the active window. That's close enough to "what the session is about" for a one-line summary. Idle calculation reads `date +%s` by default and accepts an override via the TMUX_UTIL_NOW env var so tests can pin "now" to a known epoch. 12 new tests cover Normal cases (attached / detached, multiple sessions) and Boundary cases (no sessions, idle exactly at minute / hour / day boundaries, $HOME tilde). One existing dispatch test got reworked because the original stub target (`ls`) is no longer unimplemented. Total suite is 26 tests, all green. The fake-tmux harness picked up two things along the way: real format-string parsing for `list-sessions -F` and a new handler for `display -p`. The state file format extended to include activity epoch, window count, and cwd, with sensible defaults for older 3-tuple test inputs so the reap tests keep passing untouched.
* feat(tmux-util): add script skeleton and reap subcommandCraig Jennings2026-05-195-0/+463
| | | | | | | | | | | | A new utility in dotfiles/common/.local/bin/ for managing tmux sessions. The eventual plan covers six subcommands (go, pick, ls, find, reap, rename). This commit ships the skeleton, the dispatch + help, and the first subcommand: reap. reap walks every unattached tmux session whose name doesn't match $TMUX_UTIL_REAP_SKIP (default `^aiv-`), sends SIGHUP to each pane's PID (the same signal that fires when you close a terminal window), waits up to three seconds for the session to wind down, and falls back to `tmux kill-session` if anything's still alive. Tests live under tests/tmux-util/ with the same fake-binary-on-PATH pattern layout-navigate uses. fake-tmux reads canned session state from a file and records every invocation. fake-kill records signal calls without sending them. fake-sleep is a no-op so tests don't actually wait. 14 tests cover Normal / Boundary cases for dispatch + reap. Run them with: cd tests && python3 -m unittest tmux-util.test_tmux_util The other five subcommands stub out for now and exit non-zero with "not implemented yet" so future TDD turns can drop them in one at a time.
* fix(archsetup): log the real exit code from failed installsCraig Jennings2026-05-191-2/+7
| | | | | | `retry_install` was logging "error code: 0" for every retry-exhausted install (6 AUR packages on the 2026-05-16 run, 2 on the 2026-05-18 run). Root cause: `last_exit_code=$?` ran AFTER an `if eval ...; then return 0; fi` block. Bash defines an if-compound's exit status as zero when no condition tested true. With no else clause and a failing eval, that's exactly what happens, so `$?` captures 0 instead of eval's actual exit code. Now eval runs unconditionally, `$?` lands in `last_exit_code` right after it, and the if just compares against the captured value. Same control flow, accurate codes in the summary. Verified with a small bash test: the old pattern reports `$?=0` after a failing if, the new pattern reports the real exit.
* chore: close LICENSE task and log emacs-fix VM verificationCraig Jennings2026-05-191-3/+8
| | | | | | Two updates in todo.org. I closed the LICENSE-file subtask as a dated event-log entry. The canonical GPL-3 text landed at `LICENSE` in 028f144 on 2026-05-11. The SPDX/NOTICE-headers follow-up splits into its own subtask for the eventual public release. I also logged the 7cd7392 emacs-stow fix verification under `[#B] Fix install errors`. The 2026-05-18 `make test` exercised the new three-branch path (git init + fetch + checkout -B main against a stow-populated `~/.emacs.d`) and the exit-128 error is gone from the Error Summary.
* docs(claude): correct cursor theme referenceCraig Jennings2026-05-191-1/+1
| | | | CLAUDE.md listed `capitaine-cursors-light`, but every other place in the repo names `Bibata-Modern-Ice`: `hyprland.conf` XCURSOR_THEME, the three Xresources files, `gtk-3.0/settings.ini`, the dconf defaults block in `archsetup`, and the `aur_install bibata-cursor-theme-bin` line. I fixed CLAUDE.md to match.
* fix(archsetup): write desktop defaults as a system dconf dbCraig Jennings2026-05-191-17/+26
| | | | | | The previous block ran two `sudo -u $user dbus-launch dconf write …` calls during install to set GTK file-chooser and GNOME interface defaults. Both exited 1 on a headless install. The user had no real session bus, so dconf couldn't create `~/.config/dconf/user`. They also showed up in the 2026-05-11 / 16 / 18 VM-run error summaries. I rewrote the block to compile a system-wide dconf db under `/etc/dconf/db/site.d/`. `dconf update` runs as root and needs no D-Bus. Settings apply to every user and can still be overridden per-user. Bonus: portal-gtk reads these on first login without the ~50s settings-proxy timeout the prior code aimed to prevent but couldn't deliver, since the per-user writes failed during install.
* chore(todo): collapse stray **NOTE:** label to italicCraig Jennings2026-05-191-1/+1
| | | | | | Lint flagged the **NOTE:** marker as a possibly-misplaced heading (double-asterisk at start of line). Switching to single-asterisk italic keeps the visual emphasis without the heading collision.
* fix(hyprland): paint scratchpad active border goldenrodCraig Jennings2026-05-191-2/+1
| | | | | | | | | | Pyprland 3.4+ applies `group deny` to scratchpads, which routes their border through col.nogroup_border* instead of col.*_border. Set col.nogroup_border_active to #daa520 (same as col.active_border) so the scratchpad and tiled active borders read identically. Also drop `pseudotile = true` from the unused dwindle{} block — this config uses layout = master, so the dwindle settings never take effect.
* fix(tmux): pipe M-w copies to the system clipboard via wl-copyCraig Jennings2026-05-181-4/+7
| | | | | | | | | `copy-selection' writes only to tmux's internal paste buffer, so a paste in any other app got stale content. Switch the M-w binding to `copy-pipe-no-clear "wl-copy"' (same engine as the existing y binding, minus the -and-cancel so M-w stays in copy-mode for repeated grabs). The selection stays visible after copy to make multi-range copying easier to follow.
* feat(tmux): mirror vterm-copy-mode key story in tmux copy-modeCraig Jennings2026-05-181-2/+10
| | | | | | | | | | | M-w copies and stays in mode so multiple selections can be grabbed in a row (was copy-selection-and-cancel). C-g exits (was clear-selection). Enter is unbound so RET has no copy-and-exit shortcut. q and Escape already cancel by default and are kept as-is. Mirrors the cj/vterm-tmux-history and vterm-copy-mode story on the Emacs side, so all three surfaces for lifting text out of a vterm share one key story.
* fix(archsetup): handle stow-created ~/.emacs.d when cloning dotemacsCraig Jennings2026-05-172-3/+21
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | The 2026-05-16 VM run surfaced `setting up emacs configuration files (error code: 128)` with `fatal: not a git repository`. Root cause: `dotfiles/common/.emacs.d/calendar-sync.local.el` lives in the stow tree, so by the time the dotemacs clone step runs, `~/.emacs.d/` already exists with a stowed symlink inside. The previous two-branch check `[ -d "$emacs_dir" ]` then took the `git pull` path and failed because the dir isn't a git checkout. I replaced the check with three branches: - `.git` exists → `git pull --recurse-submodules` (the existing upgrade path). - Dir missing or empty → `git clone --recurse-submodules` (the fresh install path). - Dir exists with content but no `.git` (the stow case, today's bug) → `git init` + `remote add origin` + `fetch --recurse-submodules` + `checkout -B main origin/main` + `submodule update --init --recursive`. This pulls the tree in on top of the stowed file without losing it. The one-line `[ -d ]` → `[ -d .git ]` fix wouldn't have been enough on its own: the clone branch would still fail because `git clone` refuses a non-empty target. `bash -n archsetup` is clean. The VM test run that surfaced the bug exercised the old two-branch path. The new third branch wants a fresh `make test` to verify before this lands on a real machine. todo.org picks up two things this commit touches. The stale gitrepos.sh reference under "Remove/template personal info from scripts" comes out. A new sub-TODO under `[#B] Fix install errors` expands the existing tidaler item: the same "AUR exit 0 logged as error" pattern now hits six packages instead of one (tidaler, mkinitcpio-firmware, speedtest-go-bin, rar, masterpdfeditor, zsh-fast-syntax-highlighting-git). The recommendation points at `aur_install` and the error-summary aggregator as trace targets.
* fix(testing): cleanup traps, arg validation, and two real bugsCraig Jennings2026-05-179-48/+146
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Two real bugs and a sweep of hygiene across the harness. `make test` passed cleanly on this branch with the same 52/0/5 profile as the 2026-05-11 run, so the wiring is verified end-to-end. Real bugs: - `lib/vm-utils.sh` `snapshot_exists` was running `qemu-img snapshot -l | grep -q "$snapshot_name"`, which matches the name as a substring anywhere in the output — including inside dates or filenames in other fields. Replaced with an awk field extraction on the TAG column plus `grep -Fxq` for a whole-line literal match. - `run-test-baremetal.sh` was setting `VALIDATION_PASSED=true|false` after validation, but `validation.sh` already uses `VALIDATION_PASSED` as a pass counter. The test report then referenced `$VALIDATION_PASSED_COUNT`, which is defined nowhere. Renamed the boolean to `TEST_PASSED` (matching run-test.sh's pattern) and report the actual counter. Cleanup traps and arg validation: - `run-test.sh` now installs a top-level EXIT trap that, on abort, kills QEMU and restores the clean-install snapshot. A `CLEANUP_DONE=1` sentinel keeps the existing normal-path cleanup from double-firing. This is the recurring pain from 2026-05-11 where two failed runs left orphaned QEMU processes and dirty base disks behind. - `create-base-vm.sh` and `debug-vm.sh` got the same kind of trap, plus `debug-vm.sh` now rejects non-`.qcow2` paths up front instead of letting QEMU fail later. - `run-test.sh`, `run-test-baremetal.sh`, and `cleanup-tests.sh` now validate that options with required values actually receive one (`${var:?msg}` for `--script`/`--snapshot`/`--host`/`--password`, numeric check for `--keep`). - `run-test-baremetal.sh` traps the temp git bundle for cleanup if the script aborts before its explicit `rm`. The ZFS rollback loop now uses `while IFS= read -r ds` and quotes `$ds` inside the ssh_cmd so dataset names with whitespace wouldn't break it. Smaller hygiene: - `vm-utils.sh` `check_ovmf` also checks `OVMF_VARS_TEMPLATE`; `start_qemu` validates disk and ISO paths before building the QEMU command; numeric tests quoted. - `cleanup-tests.sh` find expression for temp disks wrapped in `\( ... -o ... \)`, all `while read` loops use `IFS= read -r`, orphaned QEMU cleanup tries SIGTERM with a 2s sleep before SIGKILL. - `create-base-vm.sh` moved the "Copy an archangel-*.iso" info line before its `fatal` instead of after (unreachable), and added the serial-log path to the final summary. - `lib/logging.sh` `stop_timer` no longer produces `$((end - ))` when the named timer was never started. - `lib/network-diagnostics.sh` `read` → `IFS= read -r`. - `setup-testing-env.sh` now installs all missing pacman packages in one transaction instead of one-at-a-time (avoids half-installed state if package N fails). KVM check also verifies the user has read+write on `/dev/kvm` and prints the `gpasswd -a $(id -un) kvm` fix if not. A few items from the review I deliberately skipped: replacing the codebase-wide unquoted `$SSH_OPTS` string with an array (cosmetic, would need to be done everywhere at once), `set -e` adds where the existing fall-through-on-failure is intentional, and a `--force` gate on `create-base-vm.sh` (would break the expected workflow).
* refactor(scripts/games): array + loop, flathub remote check, failure summaryCraig Jennings2026-05-171-29/+50
| | | | | | | | | | | | | | | | | | | | Three things were wrong with the old script: - One line had a duplicate `flathub` argument (`flatpak install ... flathub flathub org.gnome.Crosswords`). Flatpak tolerated it; still a typo. - Nothing added the flathub remote, so a fresh machine without it would fail on every line. - 24 nearly identical `flatpak install` lines with no central handling if one app failed. I moved the app IDs into a single `APPS=()` array (sorted) and looped. The script now runs `flatpak remote-add --if-not-exists --user flathub https://flathub.org/repo/flathub.flatpakrepo` up front, collects any failed apps into a `failed` array, and prints a summary plus a non-zero exit at the end if anything didn't install. I used `set -uo pipefail` rather than `-euo` on purpose so a single failed install doesn't abort the rest of the run.
* fix(cmail-setup-finish): verify both Bridge ports and dump status on failureCraig Jennings2026-05-171-2/+8
| | | | | | | | | | | | | | The "verifying Bridge is listening" check used a regex `127\.0\.0\.1:(1143|1025)` against `ss -ltn` output. That matches if *either* port is listening, but the success message claims both are. So a half-broken Bridge (IMAP up, SMTP down or vice versa) would pass the check. I split the check into two greps and report which port is missing. When the check fails, the script now also prints the last 10 lines of `systemctl --user status protonmail-bridge` to stderr so the operator sees the service state immediately instead of being told to go run the command themselves.
* refactor(scripts/post-install): consolidate gitrepos.sh and harden the scriptCraig Jennings2026-05-173-52/+73
| | | | | | | | | | | | | | | | | | | | | | | | gitrepos.sh did the same `~/.dotfiles` / `~/.emacs.d` remote swap that post-install.sh already had, plus a `git pull --set-upstream origin main` follow-on that post-install was missing. I folded the pull into the post-install remote-rewrite block and dropped gitrepos.sh. While in the file, I also: - Quoted every variable (`"$logfile"`, `"$HOME"` paths, `"$(whoami)"`). - Sent the remote-rewrite block to the log file like the other blocks do (was leaking to stdout). - Made the remote-rewrite idempotent. A re-run used to break the `cd && remote remove && remote add` chain because remove fails when origin is already the desired URL. The loop now uses `git -C "$dir" remote set-url` when origin exists and `remote add` when it does not. - Pre-created `~/sync`, `~/pictures`, `~/code`, `~/projects` so the clones don't fail on missing parent dirs. - Wrapped each `git clone` in a `clone_if_missing` helper so a re-run skips destinations that already exist instead of erroring out. README.md picks up the gitrepos.sh removal in the forking note.
* chore(scripts): drop dead and superseded scriptsCraig Jennings2026-05-165-101/+0
| | | | | | | | | | | | | | | | | Audit pass: each of these had no references anywhere in the repo (excluding self-references and review notes). - wip-bootcandy.sh — "wip" prefix, non-executable. Comments mention a boot animation but the script only installs ly and disables getty@tty2. - protonmail-bridge.sh — `pacman_install protonmail-bridge` (the package landed in extra) plus cmail-setup-finish.sh now cover this. - wireguard-proton.sh — hardcoded USGA tunnel and a relative `../assets/wireguard-config/*.conf` path that depends on the caller's pwd. - create-archiso-zfs.sh — one-off ISO build snippet, non-executable. - scripts/testing/lib/finalize-base-vm.sh — libvirt-era leftover. The test stack moved to direct QEMU and nothing sources or calls it.
* Update En Croissant AppImage pinCraig Jennings2026-05-161-2/+2
|
* Install setup-chess prerequisites automaticallyCraig Jennings2026-05-162-77/+520
|
* chore(todo): bump GitHub-prep subtask priorities to ACraig Jennings2026-05-151-14/+14
|
* chore: close out dotfile-separation spec reviewCraig Jennings2026-05-151-1/+5
|
* docs(plan): fold review decisions into dotfile-separation specCraig Jennings2026-05-141-30/+60
| | | | | | | | | | | | | | | I locked the URL (https://git.cjennings.net/dotfiles.git), the bare repo path (/var/git/dotfiles.git), and the Phase 1 scope. Added environment.d/envvars.conf (rofi path stripped) and systemd/user/emacs.service to the minimal/ tree. Promoted the DESKTOP_ENV=none VM test from optional to required. Made Phase 2's clone explicit (sudo -u, not chown-after). Tightened Phase 3's order so unstow and restow happen without a Hyprland reload between them. Added a note that the new dotfiles repo can't go on GitHub until the secrets cleanup ships, plus the post-install update flow in Step 3.3. Replaced the "Before starting" question block with a Status table since all five open questions are now resolved.