aboutsummaryrefslogtreecommitdiff
Commit message (Collapse)AuthorAgeFilesLines
* docs: close normalize-notify atomicity taskCraig Jennings2 days1-1/+3
|
* fix(sounds): make normalize-notify-sounds write atomically and clean upCraig Jennings2 days3-6/+162
| | | | | | The script re-encoded each notify sound with `cat "$tmp" > "$f"`, which truncates the target first -- a short or empty encode left a corrupt, repo-tracked sound file. The mktemp also had no trap, so an interrupted encode leaked a temp. Now it resolves the real target with `readlink -f` (SOUND_DIR is usually the stow-symlinked ~/.local copy), stages the temp beside it, guards on a non-empty encode, and moves it into place. The mv is atomic on one filesystem and replaces the real file, so the stow symlink still points at it. An EXIT trap removes the in-flight temp when a failed encode aborts the run.
* docs: close unguarded chmod/cp cluster taskCraig Jennings2 days1-1/+3
|
* fix(installer): guard the cp and chmod that install helper scriptsCraig Jennings2 days2-6/+87
| | | | | | With `set -e` off, the installer copied its zfs-replicate, zfs-pre-snapshot, and hypr-live-update-guard scripts into place with bare cp/chmod. A missing or partial source failed silently, leaving a systemd service with a dead ExecStart or a pacman hook pointing at a script that never landed. The two zfs scripts now install through install_executable, which guards the copy, warns on failure, and skips the chmod when the copy did not land. The two live-update-guard chmods (the guard binary and its hook) are guarded inline with error_warn.
* docs: close resume-idempotency cluster taskCraig Jennings2 days1-1/+3
|
* fix(installer): make three resume-fragile steps idempotent and correctCraig Jennings2 days2-11/+174
| | | | | | | | | | Each of these steps misbehaves on a resume re-run or under unexpected system state, so I pulled the logic into small testable helpers and fixed the behavior. The log-cleanup cron line was appended unconditionally. essential_services is one resume step, so a failure past that point plus a re-run stacked a second identical cron line every time. crontab_append_once now appends only when no existing line matches. The weekly ZFS scrub timer used `zpool list | head -1`, which picks an arbitrary pool when several exist and builds the malformed unit `zfs-scrub-weekly@.timer` when none do. zfs_scrub_timer_units emits one timer per pool and nothing for no pools, so every pool is scrubbed and an absent pool warns instead of enabling a broken unit. gamemode was enabled through `systemctl --user enable`, which the script itself documents fails at install time with no user session bus -- so gamemoded was likely left un-enabled behind a silent warning. It now wires the wants symlink by hand through enable_user_service, the same helper syncthing's block folds into.
* docs: close wireguard tunnel-ordering taskCraig Jennings2 days1-1/+3
|
* fix(wireguard): bring the tunnel down before the rename modifyCraig Jennings2 days3-3/+30
| | | | | | `nmcli connection import` activates a full-tunnel (0.0.0.0/0) Proton profile immediately. The import loop renamed and set autoconnect off, then brought the tunnel down. Under `set -euo pipefail`, a failed modify aborted the script before that down ran, leaving a live full-tunnel VPN routing all traffic through Proton -- the exact unasked-for VPN the script exists to prevent. The next-run guard catches the leftover profile but not the still-active tunnel. Moved the down ahead of the modify. It targets the UUID, which is stable across the rename, so downing first is safe and no failure between import and modify can leave the tunnel up.
* docs: close sudoers.pacnew validation taskCraig Jennings2 days1-1/+3
|
* fix(installer): validate sudoers.pacnew with visudo before copyingCraig Jennings2 days2-1/+94
| | | | | | The installer copied /etc/sudoers.pacnew over /etc/sudoers with no validation. A malformed pacnew that sudo refuses to parse would replace a working sudoers with an unparseable one, locking out privilege escalation right before the NOPASSWD rule gets appended. replace_sudoers_pacnew now runs `visudo -cf` on the pacnew first and only copies a file that validates; a failure warns and leaves the current sudoers in place. Extracted the logic into a function taking the pacnew and target as positional args defaulting to the system paths, so the guard can run against fixtures.
* docs: close run_step state + exit-code taskCraig Jennings2 days1-1/+3
|
* fix(installer): record the state marker on non-fatal step warningsCraig Jennings2 days2-11/+113
| | | | | | run_step marked a step complete only when its function returned 0. But error_fatal exits the script outright, so a step function that returns at all has already cleared every fatal check -- a non-zero return can only be a trailing non-fatal warning, since error_warn returns 1. The old code read that as a failure and withheld the state marker, so the step re-ran on every resume even though its body had finished. run_step now records the marker whenever the function returns and logs a warning line when the return was non-zero. Two smaller fixes alongside it. run_step's step_name/step_func and show_status's step/timestamp now use local so they stop leaking to global scope. The package-cache refresh loop captures pacman's real exit code instead of reading `$?` off the `$refresh_ok` test, which always read 1 and made error_fatal report "error code: 0" -- the same trap retry_install already documents.
* docs: close df disk-space pre-flight taskCraig Jennings2 days1-1/+3
|
* fix(installer): harden the disk-space pre-flight gateCraig Jennings2 days2-9/+118
| | | | | | | | `df /` wraps to two lines when the root device name is long -- a device-mapper or live-ISO root -- so `awk 'NR==2 {print $4}'` read the wrapped device line, found an empty Available field, and aborted a valid install with "Insufficient disk space." `df -P` forces POSIX single-line output. The gate now compares available KB against the minimum in KB rather than truncating to GB first, which rejected a disk sitting just under a whole-GB boundary. Non-numeric df output falls back to zero so a malformed read aborts loudly instead of crashing the arithmetic test. Extracted the logic into check_disk_space so it can be exercised directly.
* docs: close chpasswd guard taskCraig Jennings2 days1-1/+3
|
* fix(installer): guard the user-password chpasswdCraig Jennings2 days2-1/+65
| | | | set -e is off, so a chpasswd failure silently left the primary user with no password and no log entry, leaving an unloggable account on a fresh install. I extracted set_user_password, which aborts via error_fatal on a chpasswd failure. A fake-chpasswd test pins that the guard fires.
* chore: reorder open-work tasks in todo.orgCraig Jennings2 days1-244/+244
|
* docs: file waybar module-separator taskCraig Jennings3 days1-0/+4
|
* fix(security): decrypt the cmail password under a tight umaskCraig Jennings3 days2-2/+89
| | | | gpg writes its output at the process umask (often 0644), so the plaintext mail password was world-readable between the decrypt and the chmod. I extracted decrypt_to_secure, which runs the decrypt in a 0077 umask subshell so the file is 0600 from creation. A fake-gpg test pins that the write runs under the tight umask.
* feat: add inetutils to the install baseCraig Jennings3 days2-1/+59
| | | | TRAMP's /ftp: method shells out to a command-line ftp client, and Arch ships none by default. inetutils provides /usr/bin/ftp, so I added it to install_required_software. A static test asserts it stays in the base set.
* docs: file archsetup move off cgit to private remoteCraig Jennings3 days1-1/+4
|
* docs: close WireGuard leak task, note cgit follow-upCraig Jennings3 days2-17/+23
| | | | | | Record the resolution (keys expired, all 10 configs purged from history, server gc'd, verified via anonymous clone) and file the cgit-exposure audit as the systemic follow-up. Update the config README to the out-of-band workflow.
* fix(security): stop tracking plaintext WireGuard configsCraig Jennings3 days2-0/+24
| | | | | | The repo is public via cgit, so the three plaintext configs (added 849c3fa) exposed live Proton PrivateKeys. Remove them, gitignore plaintext, and document the encrypted-only workflow. Keys rotated at Proton; history purged separately.
* docs: log overnight code audit and file triaged installer bugsCraig Jennings3 days2-80/+286
| | | | | | Overnight sentry ran its hygiene passes plus a code-inspection pass over the installer and the scripts/ directory. It archived 6 completed tasks to Resolved and logged 19 bugs and 12 refactors in docs/design/2026-07-19-sentry-code-findings.org. I filed the bugs as tasks graded by severity times frequency. Nothing in the codebase changed, this was detection only. Two things stand out. The installer does no partitioning (it's a post-install config layer), so the real data-loss surface is boot config, and it overwrites GRUB_CMDLINE wholesale, which can drop a cryptdevice or resume param and leave a machine unbootable. Three WireGuard configs with live private keys are tracked in git, flagged for a rotate-or-keep decision.
* docs: expand desktop-settings spec and add panel prototypesCraig Jennings3 days10-46/+1440
| | | | | | | | I folded the home-handoff control-set decisions into the desktop-settings panel spec. The controls table gains night-light, Do Not Disturb, power profile, lock/suspend actions, scenes, and a wallpaper sub-view. Volume and theme are out of scope, with reasons, and the format pickers move to their own display-format sibling spec (stubbed). The scenes and wallpaper-shape decisions close. The spec stays DRAFT pending UI prototyping. I added five direction prototypes over a shared engine (tile grid, sectioned rack, scenes-first, instrument cluster, expandable rows) so the layout gets picked against working behavior. I closed the shipped Next Session Focus work in todo.org: notification loudness, clock right-click dismiss, date-only scrolling, the wired-interface indicator, and best-saved WiFi connect, plus velox boot recovery. The three design-blocked network and audio items stay TODO with their open question recorded.
* docs: set next session focusCraig Jennings3 days1-0/+14
| | | | Prioritize autonomous Waybar, panel, and audio work.
* docs: clarify clock panel dismissalCraig Jennings3 days1-3/+4
| | | | Reserve left click and use right click to close the open panel.
* docs: file interface design referencesCraig Jennings3 days44-0/+1980
| | | | Record the reconciled weather work and future component direction.\nFile the clock and retro-interface reference materials for later design work.
* docs: record weather comfort signalsCraig Jennings4 days1-0/+2
|
* docs: record weather baseline correctionCraig Jennings4 days1-0/+2
|
* docs: record weather bottom alignmentCraig Jennings4 days1-0/+2
|
* docs: record weather chip groupingCraig Jennings4 days1-0/+2
|
* docs: record weather alignmentCraig Jennings4 days1-0/+2
|
* docs: record weather glyph polishCraig Jennings4 days1-0/+2
|
* docs: record weather glyph sizingCraig Jennings4 days1-0/+2
|
* docs: record weather glyph updateCraig Jennings4 days1-0/+2
|
* docs: record weather location refreshCraig Jennings4 days1-0/+2
|
* docs: record weather forecast deliveryCraig Jennings4 days1-0/+3
|
* docs: clarify weather forecast toggleCraig Jennings4 days1-1/+1
|
* docs: define weather forecast click actionCraig Jennings4 days1-1/+1
|
* docs: define weather forecast viewsCraig Jennings4 days1-1/+1
|
* chore: track weather integrationCraig Jennings4 days1-1/+12
|
* docs: document single Dupre themeCraig Jennings4 days6-25/+14
| | | | Remove retired theme-switching guidance and Hudson references from active documentation.
* chore: track package and notification workCraig Jennings4 days1-0/+17
| | | | Add the installed-package drift audit and notification sound loudness tasks.
* fix: order pacman safety hooksCraig Jennings4 days6-10/+49
| | | | Install snapshot and live-update hooks before mkinitcpio removal, migrate legacy paths, and cover the ordering invariant.
* chore: archive resolved backlog tasks and note probe-flake recurrenceCraig Jennings4 days2-145/+146
| | | | Moved the completed maintenance, audio, and timer tasks into the archive, applied the lint counter fixes, and logged the batch-6/batch-9 fader-drag probe-flake recurrences on the existing tracking task.
* feat(gallery): bring the well-through-response ten to the extraction barCraig Jennings4 days2-79/+240
| | | | | | | | outputWell, toast, annunciator, tapeCounter, freqScale, patchBay, dataMatrix, warningFlag, seg14, and responseGraph each get the contract comment. The six DOM builders take dupre- prefixes across their markup and CSS blocks. patchBay's live jack and the annunciator's master caution adopt the kit-wide dupre-hot and dupre-on modifiers, and the annunciator board takes dupre-warn and dupre-fault. freqScale reuses the shared scoped dupre-tick and dupre-mk (as the tuner and dualknob do) and compounds its unit label to dupre-fs-band, since dupre-band is the EQ's. The four SVG-stage builders style through the shared .rsvg stage and inline attributes, so they get the SVG-built contract comment and no prefixes. The annunciator documented a 0..2 state per cell but painted an undefined class on an out-of-range initial state or set(). It now clamps at build and at set. Two probe checks cover the batch: one fails red without that clamp, one drives each renamed DOM builder through its handle to catch a class-name mismatch the exception scan can't see. Extraction audit: 104/111 contract comments.
* feat(gallery): bring the CRT-through-engraved ten to the extraction barCraig Jennings4 days2-35/+213
| | | | | | | | | | roundCrt, attitudeIndicator, headingBug, chartRecorder, verticalTape, twinNeedle, comfortMeter, badges, tabularReadout, and engravedLabel each get the contract comment. The three DOM builders take dupre- prefixes across their markup and CSS blocks: badges (dupre-badge with the kit-wide dupre-red plus a new dupre-ghost), tabularReadout (dupre-readout and dupre-unit), and engravedLabel (dupre-engrave and dupre-cnt). The rename surfaced a dead rule: .readout .u selects a descendant, but the unit caption is a sibling, so its style never applied. The caption now styles through a standalone .dupre-unit rule. The builders now enforce their documented domains at build, not only in set(): twinNeedle and comfortMeter clamp their initial opts through set(), headingBug normalizes opts.value to 0..360, and badges coerces junk variants through one norm() shared by build and set(). Two probe checks cover the gates and drive the renamed DOM. Extraction audit: 94/111 contract comments.
* feat(gallery): bring the needle-through-edge ten to the extraction barCraig Jennings4 days2-77/+232
| | | | | | | | crossNeedle, thermometer, bourdon, stripChart, corrMeter, battCells, mcVu, chromeMinMax, aviationGauge, and edgeMeter each get the contract comment. The six DOM builders take dupre- prefixes across their markup and CSS blocks. Internals go compound where gauge, fader, and nixie already hold the plain names: bourdon's needle, hub, and cap, corrMeter's face, arc, needle, and label, crossNeedle's arc, and thermometer's tube and fill. crossNeedle's nA and nB become dupre-fwd and dupre-rfl, and battCells moves its cell modifiers to the shared dupre-on and dupre-warn. The handles now enforce their documented domains: the needle meters clamp set() to 0-100, stripChart floors its history at two samples and survives a non-array set(), battCells floors its cell count at one, and mcVu clamps and paints opts.value at build instead of ignoring it for a hard-coded .35. Two probe checks cover the gates and drive the renamed DOM. Extraction audit: 84/111 contract comments.
* feat(gallery): bring the decade-through-EQ ten to the extraction barCraig Jennings4 days3-89/+260
| | | | | | | | decadeBox, twoHandSafety, voiceLoop, miniSig, fuelBar, radialRing, sparkline, waveStrip, scope, and eqBars each get the contract comment. The eight DOM builders take dupre- prefixes across their markup and CSS blocks. voiceLoop's inner bar becomes dupre-vk-bar to stay clear of fuelBar's dupre-bar, and miniSig and eqBars move their cell modifiers to the shared dupre-on, dupre-hot, and dupre-clip. Domain gates land where a handle could wedge: decadeBox coerces and clamps its digits, twoHandSafety ignores sides other than L and R, voiceLoop keeps the demo state to the stock loop set (a caller's shorter list used to crash on it), the meters clamp set() to their documented ranges, and sparkline and scope guard the one-sample case that drew NaN coordinates. Two probe checks cover the gates and drive voiceLoop's grammar through the prefixed DOM. probe-fams follows scope's rename. Extraction audit: 74/111 contract comments.