check_and_enable_services.sh was lying about two things and tracking a third
the author no longer uses.
1. workout_locker: paths still pointed at python_pkg/screen_locker/, which
stopped existing when screen-locker was extracted to its own repo
(~/screen-locker). The service was installed, enabled and active the whole
time, but the checker reported a red error and its "fix" silently did
nothing. Now resolves ~/screen-locker via SUDO_USER + getent (the script
re-execs under sudo, so $HOME is /root).
2. User-service queries could never work: `sudo -u <user> systemctl --user`
has no XDG_RUNTIME_DIR/DBUS_SESSION_BUS_ADDRESS, so it always failed with
"Failed to connect to user scope bus" and reported EVERY user service as
not-enabled. Added user_systemctl() using
`systemctl --user --machine=<user>@.host` (systemd's own suggested fix) and
rerouted all 6 call sites (focus-mode, thorium, workout-locker).
3. Removed thesis_tracker entirely — not used; unit not-found, script never
installed. Help list renumbered.
focus_mode and guest_mode_removal keep reporting error: those are GENUINE
(focus-mode.service is not-found; the guest-mode policies are absent).
Verified: ground truth (as kuhy) workout-locker.service = enabled+active.
Before: checker said "not enabled". After: workout_locker ok, all 3 checks ✓.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JsCs1GUyiinA7bg68YvAh1
setup_hosts_guard.sh died partway on every run under `set -euo pipefail`,
which is why hosts-guard.path / hosts-bind-mount.service / nsswitch-guard.path
sat installed-but-disabled since 2026-02-02 and resolved-guard.path was never
installed at all — /etc/hosts had no watcher to restore it if tampered.
Two aborts, both silent:
1. `local_dot=$(grep -E '^\s*DNSOverTLS\s*=' ... | tail -1 | sed ...)` — grep
exits 1 when the key is absent, pipefail propagates, the assignment returns
1, set -e kills the script. DNSOverTLS is absent from a default
resolved.conf, so this fired on EVERY run, right before resolved-guard.path
was enabled. The fix-if-missing branch below it was unreachable because
detecting "missing" aborted first. Same trap on the ReadEtcHosts probe.
check_and_enable_services.sh already guarded its copy with `|| true`.
2. `systemctl restart systemd-resolved` — resolved is MASKED here (dnsmasq
serves DNS), so the restart failed and aborted the script on its last step,
after everything was already configured. Now skipped with a note.
Before: exit 1, resolved-guard disabled, nsswitch-guard start-limit-hit.
After: exit 0; hosts-guard.path, hosts-bind-mount.service, nsswitch-guard.path
and resolved-guard.path all enabled+active. DNS still resolves; getent ahosts
on doubleclick/googlesyndication/facebook returns 0.0.0.0; /etc/hosts still
immutable at 176888 lines.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JsCs1GUyiinA7bg68YvAh1
Both tools swallowed failures. execute_with_log appended one line to a log
nobody reads and returned 0, so the systemd oneshot reported SUCCESS.
check_and_enable_services printed one err line among ~100 and exited 0.
That is why the 2026-05-15 reorg silently killed the pacman-wrapper and
hosts self-repair for five months with no visible symptom: check_hosts
detected the disabled hosts guards on every single run, could not find
setup_hosts_guard.sh at the stale $CONFIG_DIR/hosts path, and still exited 0.
Driver — any failure now surfaces four ways:
1. journal at ERROR priority (logger -p user.err)
2. the systemd unit FAILS (exit 1) -> systemctl --failed / red timer
3. a critical desktop notification that never auto-dismisses
4. /var/lib/periodic-system-maintenance/LAST_RUN_FAILED breadcrumb
It also collects ALL failures rather than aborting on the first.
check_and_enable_services — a missing repair script means this tool cannot
do its job, so all 12 "script not found" sites now route through
err_missing_script(): journal ERROR priority, a terminal banner, and exit 2.
Healthy runs still exit 0 and clear the breadcrumb.
Verified: driver fed nonexistent paths -> exit 1, all 3 failures on stderr,
breadcrumb written, 3 journal ERROR entries. Live regenerate + real run ->
rc=0, all steps ok, no breadcrumb. Checker --status -> rc=0, no banner.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JsCs1GUyiinA7bg68YvAh1
Immutable git-tracked files break git tooling. The pre-push ci-mirror gate
runs `pre-commit run --all-files`; end-of-file-fixer opens every file rb+
and dies with "PermissionError: Operation not permitted", so no push from a
normal checkout could ever succeed — every push had to be routed through a
throwaway worktree (a fresh checkout gets new, non-immutable inodes). The
same immutability also breaks pre-commit's unstaged-changes stash: the
`git checkout -- .` unlink fails, the restore never runs, and unrelated
unstaged edits are silently reverted.
Enforcement does not depend on these sources being locked: /etc/hosts is
itself chattr +i, hosts-guard.path watches and re-enforces it via
enforce-hosts.sh against the locked-hosts canonical snapshot, and
hosts-bind-mount.service pins it. Editing the sources changes nothing until
install.sh is re-run as root, which regenerates the guarded artifacts.
Companion to 6e3d61d (same fix for setup_midnight_shutdown.sh).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JsCs1GUyiinA7bg68YvAh1
An immutable tracked file with uncommitted edits breaks every commit/push:
pre-commit clears unstaged changes with `git checkout -- .`, that unlink
fails ("Operation not permitted"), the context manager aborts, and the
restore never runs — silently reverting the user's OTHER unstaged edits
(seen twice today; an unrelated i3/config WIP had to be recovered from
pre-commit's rescue patch).
Enforcement is unaffected and does not depend on the source being locked:
/etc/shutdown-schedule.conf stays chattr +i, guard-lib keeps a canonical
snapshot + path watcher that re-enforces it, the monitor service protects
the timer, and screen_locker's ratchet only accepts same-or-stricter
values. Editing this source changes nothing until setup is re-run as root,
which rewrites the guarded config anyway.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JsCs1GUyiinA7bg68YvAh1
Vendored makepkg's run_pacman() waits on /var/lib/pacman/db.lck existence
with no timeout BEFORE invoking pacman, so an orphaned lock hangs
`makepkg -i` forever ("Pacman is currently in use, please wait...") and
the pacman wrapper's stale-lock cleanup never gets a turn. Wrap makepkg
(mirroring the pacman-wrapper pattern) to clear an orphaned lock up front.
- Extract stale-lock logic into shared pacman_lock_lib.sh; pacman_wrapper
sources it after integrity verification and it is in the SHA256 manifest
- makepkg_wrapper: pre-flight clear for -i/--install; bypass on FAKEROOTKEY
and non-install; FAIL OPEN if the lib is missing (never brick makepkg)
- Cross-user pacman_process_running() pgrep guard: unprivileged makepkg can
detect a root pacman its own fuser cannot see (avoids removing a live lock)
- Upgrade survival: rewrap hook (Install/Upgrade only) + check_makepkg_wrapper
drift verifier + both wrappers wired into the hourly periodic driver
- Fix reorg path bugs: stale ~/linux-configuration periodic path,
$CONFIG_DIR/hosts -> scripts/periodic_background/hosts, test WRAPPER_DIR;
refresh .orig only when target isn't already our symlink (no exec loop)
- Tests: test_makepkg_wrapper.sh (18) incl. hook Operation validity; add
lib-integrity coverage to test_pacman_wrapper_security.sh (also path-fixed)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JsCs1GUyiinA7bg68YvAh1
Add a self-expiring override system so specific late-night live events
(2026 World Cup matches) can be watched without the auto-shutdown killing
the PC, without weakening the permanent schedule ratchet:
- shutdown-override-manager.sh (add/list/remove): add is friction-gated
(typed confirmation phrase + 30s delay, max 12h, max 3 windows) and
writes absolute-epoch windows to /etc/shutdown-schedule-overrides.conf.
- day-specific-shutdown-check.sh now skips the shutdown decision inside an
active window (pruning expired entries) and gained a DRY_RUN guard around
rtcwake/poweroff/hibernate for safe testing.
i3blocks shutdown_countdown.sh: show the exact next-shutdown clock time
(not a relative countdown), show the overridden end time in green when an
override is active or will rescue the upcoming shutdown, and make the
skip-dates check fork-free (builtin read loop) so the hot path spawns no
external processes.
install.sh: deploy i3blocks by symlinking each repo file into
~/.config/i3blocks instead of cp -r (the source of silent drift), cd to
the script dir so it runs from anywhere, and chmod +x only *.sh so the
config file stays 0644.
Repair the previously-dead i3blocks regression suite (stale path), update
the shutdown assertion to the new absolute-time output, add hermetic
override coverage, and keep the <=1-exec fork-count guarantee.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NmwYH5RfThZGkVD2jE61Da
Replaces the bespoke chattr/bind-mount/systemd-watcher implementations for
/etc/hosts and /etc/shutdown-schedule.conf with the new shared guard-lib
(~/guard-lib, guardctl), so screen-locker and steam-backlog-enforcer's new
block-gaming feature stop maintaining parallel copies of the same
tamper-resistance mechanism.
- pacman_wrapper.sh: pre/post hook fallbacks now call guard-lib's generic
unlock-all/relock-all scripts (covers every registered guard instance,
not just /etc/hosts)
- setup_midnight_shutdown.sh: installs/updates its guarded config via
guardctl file-guard instead of hand-rolled chattr + systemd unit
generation; the schedule ratchet logic (block-if-more-lenient) stays
bespoke since guardctl's generic unlock can't represent it
- new hosts/guard/plugins/nsswitch-plugin.sh, resolved-plugin.sh
Also fixes, at user's request even though pre-existing: 3 shellcheck
SC2329 false positives in pacman_wrapper.sh (functions invoked indirectly
by name, not actually dead) and 1 SC2001 style warning (echo|sed VM-name
extraction replaced with parameter expansion, verified equivalent output).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AFNiYQQgSLAkiBXswyimPq
The hibernate-on-alarm-night case block in create_shutdown_check_script()
escaped $ as \$ for an unquoted heredoc, but the heredoc is opened with the
quoted delimiter <<'EOF', so the backslashes were written literally into
the deployed check script. Every timer fire since commit 0d54c5d hit a bash
syntax error and exited before evaluating the shutdown window, so the PC
never auto-shut-down (confirmed failing at 23:00/23:01 last night and 07:01
this morning). Also fixed an off-by-one (seq ... 24 -> 23) that was emitting
invalid OnCalendar=24:00:00 entries systemd had to ignore.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LGHLfbAPmak6HtGJby57eo
Split diet_guard/_gatelock.py, wake_alarm/_alarm.py, and the
usage_report.py/_usage_report_parsing.py pair into focused
sub-modules so every Python file is <= 500 lines, satisfying
test_file_length.py. Install python-kasa into .venv (declared in
requirements but missing after the 3.13->3.14 venv upgrade),
fixing 8 failing smart_plug tests and restoring 100% coverage.
Also includes prior in-progress work from the working tree: the
wake_alarm Progress/View/Hardware field-grouping refactor,
brother_printer query module + tests, diet_guard foodbank/state/cli
updates, new shared coerce/logging_setup helpers, morning_routine
orchestrator tweaks, dwm window-manager config, gaming scripts, and
misc maintenance/digital-wellbeing script updates.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Deleted screen_replay.sh (x11grab ring-buffer) and kill_stale_recorders.sh
(stale-ffmpeg killer). Removed autostart exec and Mod+Shift+F12 binding from
i3/config; removed replay startup from dwm-session. Both scripts archived in
testsAndMisc-archive.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
atop's `-P PRC` output inserts the clock-tick rate (HZ=100) between the
`state` and `utime` columns. Both the Python parser and the native C
aggregator read that constant as utime for every record, charging a flat
1 CPU-second per record — so cpu_seconds collapsed to pid_count and
short-lived fork-storm commands (xset, dd, chronyc) topped the CPU table
(xset showed 67h). The old test fixtures lacked the HZ field, so code and
tests agreed on the bug.
- _parse_prc / atop_agg.c: read utime/stime past the HZ field (after+2/+3,
tokens[10]/[11]); bump the length guards accordingly
- restore C/atop_agg (deleted in 89b4f59) under linux_configuration/C/,
where the build path resolves; corrected test fixtures to include HZ
- _atop_agg_binary: fall back to the Python parser when the C source tree
is gone instead of trusting an orphaned cached binary
- add regression tests proving HZ is not summed as CPU
- bundle the in-progress since-last-report multi-day aggregation (segments,
-b/-e bounding, persisted state, window merging) and its tests/conftest
- meta: gate linux_configuration/tests in pytest_changed_packages.py
Verified by running usage_report.py --date 20260604: Top CPU now led by
SkyrimSE; xset/dd/chronyc fall to ~0. C unit tests + full pytest suite green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- install.sh: install sleep-hook.sh to systemd-sleep hooks (step 3) and
shutdown-wrapper.sh to /usr/local/bin/shutdown (step 5)
- shutdown-wrapper.sh: new script that intercepts shutdown calls and
redirects to rtcwake -m disk on alarm nights (Mon/Fri/Sat/Sun), pass-
through for reboots and cancel commands
- sleep-hook.sh: new systemd-sleep hook that restarts wake-alarm.service
for each logged-in user after hibernate resume
- setup_midnight_shutdown.sh: check wake-alarm day; if yes set RTC alarm
and hibernate, otherwise fall back to systemctl poweroff
- linux_configuration/tests: update script paths after periodic_background/
reorganisation (hosts_file_monitor, makepkg_capped, music_parallelism,
shutdown_timer_monitor, usage_monitoring_installer_efficiency)
- test_i3blocks_efficiency.sh: remove checks for HEARTBEAT_INTERVAL_S and
WARP_POLL_INTERVAL_S constants that no longer exist
- test_pacman_wrapper_security.sh: remove tests 20-21 (builtin time helpers /
external date calls) that are no longer applicable; update path
- generate_hosts_file.sh: add sed unblock rules for delio.com.pl and
loverslab.com to stay consistent with install.sh whitelist
- steam_backlog_enforcer/scanning.py: remove unplayable_reason arg from
logger.info call (too many format args); drop matching test assertion
- steam_backlog_enforcer/tests/test_protondb.py: add
test_unplayable_reason_no_trending_tier to restore 100% branch coverage
on protondb.py line 97 (was previously covered indirectly)
- Screen locker: disable VT switching (Ctrl+Alt+Fn) via setxkbmap
srvrkeys:none on startup; restore on close (production mode only).
Gracefully skips if setxkbmap is not installed (shutil.which).
Tests: 7 new tests, 100% branch coverage maintained.
- Midnight shutdown: restore real schedule values (Mon-Wed 21:00,
Thu-Sun 22:00, morning end 05:00); re-enable the three commented-out
leniency checks in check_schedule_protection(); self-lock script with
chattr +i at end of enable_midnight_shutdown().
- Hosts install: add UNBLOCK_STATE_FILE tracking for whitelisted domains;
check_unblock_entries_protection() blocks installation if the unblock
list grows; save state after install; self-lock install.sh and
generate_hosts_file.sh with chattr +i.
- Move all linux_configuration scripts into two semantic categories:
- single_use/: scripts run once manually (fresh install, fixes, setup)
- periodic_background/: scripts run by systemd timers or daemons
- Preserve existing subdirectory structure within each category
- Fix lib/common.sh source paths for new directory depths
- Fix CONFIG_DIR depth in setup_periodic_system.sh and check_and_enable_services.sh
- Update all references in tests, fresh-install/main.sh, nix modules, and docs
- Fix check_polling_antipatterns.sh false positives (||, regex |, case patterns, jq strings)
- Fix pre-existing mypy exclusion path and type annotations for moved tools/ directory
- Rewrite check_polling_antipatterns.sh using awk (no bash regex loops); add require_serial: true