Hardening pass over the script. Every fix below was reproduced before being
made; two findings that looked serious were tested and dropped.
THE ONE THAT MATTERED. The write was not atomic and could destroy a day's
todos. It did:
cp -p "$src" "$dst" # yesterday's UNPRUNED note lands at the target
cat "$tmp" > "$dst" # then gets overwritten with the real content
Interrupted between those, today's note silently becomes yesterday's,
unpruned. Interrupted during the redirect, it is truncated. Content is now
built in a temp inside the notes directory and renamed over the target, so
the target is either the old file or the new one and never a half-written
mixture.
⚠ `cp -p` was NOT redundant, and a review pass that recommended removing it
was wrong. A bare `cat > "$dst"` preserves the mode only when the target
already exists; on the normal path the target is new and takes 0644 from the
umask. Measured: source 600 -> new file via redirect 644, pre-existing file
600. The mode is now set explicitly with `chmod --reference` before the
rename, which is correct in both cases.
OUTPUT THAT LIED. The success line always said "(pruned)", including when
the prune script was missing and the plain-copy fallback ran. That is the
same defect as the error message that advertised a --force flag before one
existed: a user-facing string asserting work the code did not do. The
message now reports which path was taken, and a prune script that FAILS is
reported instead of aborting on set -e with no explanation.
ARGUMENT HANDLING. Anything that was not --force became a date, so
`todo_rollover --dry-run` failed with "source note .../--dry-run.md not
found" -- a missing-file error for what was really a bad flag. Unknown
options are now rejected, `--` and `-h/--help` work, arity is checked, and
dates are validated for shape AND realness (2026-13-45 is caught up front).
ALSO FIXED
- `[[ -x $PRUNE ]]` was the wrong test: PRUNE is run as `python3 "$PRUNE"`
and needs no execute bit, so a working prune script was silently skipped.
Now -f.
- src == dst aborted on a raw `cp: ... are the same file`. Checked explicitly
up front with a clear message. (It did fail safe -- a review pass claiming
the source was destroyed was tested and is not reproducible.)
- The heading regex rejected "# 2026-09-21 " with a trailing space and
prepended a duplicate heading. Now tolerates surrounding whitespace.
- `${tmp}.new` escaped the EXIT trap and leaked on interrupt.
- Dead `mkdir -p "$NOTES"` after the source check had already proven the
directory exists, and a `mode=` variable that was assigned and never used.
Verified: unknown flag/bad date/missing source exit 1, existing target exits
2, --help exits 0, mode 600 survives the rollover, a non-executable prune
script is used, a failing one leaves the target untouched, the trailing-space
heading is retitled rather than duplicated, and no temp files are left behind.
Moonlight went on the work laptop as a flatpak, which neither apt-mark nor
pacman can see — so a rebuild from the manifests would have silently left
it out. pkg-restore adds the flathub user remote before installing, since
Ubuntu ships flathub as a system remote that --user cannot resolve.
System-scope flatpaks are deliberately not recorded: those belong to
whoever administers the machine, not to these dotfiles.
The NAS is a minimal Arch install with no inetutils, so hostname(1) does
not exist and install died on its first line of real work. uname -n is
POSIX and always present. The same assumption was in dots, pkg-snapshot,
pkg-restore and zshenv's fallback.
Promoting the work laptop's copy to common/ carried its hardcoded fdfind,
which is the Debian/Ubuntu binary name. On Arch the binary is fd, so the
script would have failed on system76 — quietly, since it runs from a timer.
pkg-snapshot and dots used plain sort, which is locale-dependent: the same
package set ordered differently on two machines, so dots reported drift
that did not exist. A maintenance tool that always cries wolf is the exact
failure mode dots exists to avoid.
The laptops are themed by wallust, whose config and five templates lived
only in ~/.config/wallust on each machine. A fresh install would have had
no colours at all: hyprland.conf sources colors.conf on its first line and
waybar's style.css imports colors.css on its first line, and nothing in
the repo generated either. Both generated files are now linked per host so
bootstrap works, and wallust writes back through the symlinks.
update_colors.sh was its predecessor and is now actively harmful: it
writes sway/config and wofi/style.css, neither of which exists on any
machine any more, and it regenerates waybar/style.css from a template that
was never carried across -- so running it would have overwritten the
shared Hyprland waybar style with sway-era output.
common/colors (255 theme files, 1.1 MB) was only ever its input.
Replaces the previous repo, which had split into two histories that never
met (mainframe on a dead GitLab remote, the laptops on Gitea) with 146
dirty files across three machines and the NAS never enrolled at all.
Branch-per-machine is gone. One main, with host differences expressed as
small files under hosts/<hostname>/ rather than as branches, so there is
nothing to merge. The reconciled zsh layer reduces 15-33 line forks to
1-7 effective lines per host; distro differences (oh-my-zsh prefix,
syntax-highlighting path, fd vs fdfind) are probed in common/ instead.
Fresh history: the old one carried six plaintext credentials, 45 MB of
mail caches, browser caches and vendored binaries. 5,096 tracked files
and 144 MB become 462 files and 2.6 MB. The .gitignore is now an
allowlist, which is what keeps that true.
Root cause of the rot: ~/.local/bin was a symlink to scripts/ with GOPATH
inside it, so every go install wrote into version control (2.2 GB on the
work laptop). PATH now points at the repo instead of the reverse.
Also: Hyprland replaces sway and is sourced in two halves so $browser is
defined before use; singleton automations carry ConditionHost= alongside
host-layer-only placement; ddns moves from cron to a guarded timer;
package manifests and pkg-snapshot/pkg-restore replace the X11-era
install_scripts/; networkmanager-dmenu added to system76 (the binding
always existed, the package never did).