diff --git a/docs/explanations/2026-07-19-explanation-remove-focus-mode-daemon.html b/docs/explanations/2026-07-19-explanation-remove-focus-mode-daemon.html new file mode 100644 index 00000000..f2ca5a70 --- /dev/null +++ b/docs/explanations/2026-07-19-explanation-remove-focus-mode-daemon.html @@ -0,0 +1,738 @@ + + +Removing the Focus-Mode Daemon + + +
+

Removing the Focus-Mode Daemon

+

Commit 937c597 · digital_wellbeing/ · 2026-07-19

+ + + +
+

Background

+ +

+ digital_wellbeing/ is a folder of small personal-automation + daemons and installers that fight the author's own bad habits: a + compulsive-app-opening blocker, a LeechBlock installer, a pacman wrapper + that adds friction to installing distracting software, and — until this + change — a focus-mode daemon. +

+ +

+ The idea behind focus-mode was simple: Steam (gaming) and web browsers + compete for the same attention, so let only one of them run at a time. + Concretely, focus_mode_daemon.py ran as a + systemd user service that polled /proc + every 2 seconds, and enforced a rule: whichever category — Steam or + browsers — started first "wins", and the daemon pkills the + other one the moment it shows up. A companion + whitelist subcommand let the author temporarily allow + browsers during a gaming session, for things like a two-factor-auth + prompt that has to open in a browser. +

+ +

+ This lived across four files: focus_mode_daemon.py (the + state machine and CLI), _focus_mode_lib.py (process + detection via /proc/*/stat, logging, and the actual + pkill calls), install_focus_mode_daemon.sh + (installs a systemd --user service), and a bare + __init__.py whose only content was a one-line docstring + naming the daemon. +

+ +
+ Why remove it instead of fixing it? The installer's own + comments reveal it: check_and_enable_services.sh — the + script that audits and self-heals all the other daemons on this system — + repairs a broken service by running its installer as + sudo -u <user> bash install_focus_mode_daemon.sh install. + But sudo -u changes the UID without granting a login + session: XDG_RUNTIME_DIR and + DBUS_SESSION_BUS_ADDRESS stay unset, so every + systemctl --user call inside the installer died with + "Failed to connect to user scope bus". The installer tried to route + around this by deriving those paths from the effective UID — but by the + time this change landed, the daemon was confirmed not + installed and not running on the live system at all. A feature + that silently never actually deployed is exactly what "unusable in its + current state" means here. +
+
+ +
+

Intuition

+ +

+ Before removal, the daemon's core was a three-state machine. Read it as: + "no app category has claimed the screen yet, until one shows up — at + which point the other gets killed on sight." +

+ +
+
No mode
+
→
+
+
Steam appears
+
+
→
+
Gaming
(browsers killed)
+
+
+
No mode
+
→
+
+
Browser appears
+
+
→
+
Browsing
(Steam killed)
+
+ +

+ The one subtlety worth internalizing — because it's easy to misread — + is what happened if both Steam and a browser were already + running the moment the daemon started, with no mode active yet. The + code didn't wait to see which one the user "meant" first: it directly + entered gaming mode and killed the browsers. Steam wins ties. +

+ +

Play with the old state machine

+

+ This logic is gone from the repo now, but it's small enough to simulate. + Toggle which processes are "running", hit Tick to step + the daemon's update() once, and watch the mode and log + react exactly as the deleted code would have. +

+ +
+
+ + + +
+
+ Mode: none + + +
+
Waiting for first tick…
+
+ +

+ Try it: check both boxes, leave whitelist off, then hit Tick once from a + cold start — gaming mode wins and the log shows a browser kill. Now + reset, enter gaming mode with just Steam, turn on whitelist, + then check the browser box and Tick — no kill this time, + because the whitelist suppresses the enforcement while gaming mode is + already active. +

+ +

+ With the daemon deleted, none of this runs anymore. The system-level + picture also gets simpler: +

+ + + + + + + + + + + + + + + + + + +
BeforeAfter
check_and_enable_services.sh --status row count12 services, incl. focus_mode11 services
install_core_system.sh secondary modules8, incl. Focus-mode daemon7
test_security_hardening.sh checks27 tests, incl. 3 focus-mode tests24 tests
+
+ +
+

Code

+ +

1. Delete the daemon itself

+

+ All four files under digital_wellbeing/ that made up the + daemon are gone: the 320-line state machine, the 227-line process/kill + library it imported, the 241-line systemd installer, and the + one-line package docstring. +

+
- focus_mode_daemon.py           (320 lines: FocusMode state machine + CLI)
+- _focus_mode_lib.py             (227 lines: /proc scanning, pkill helpers)
+- install_focus_mode_daemon.sh   (241 lines: systemd --user unit + install/uninstall/status)
+- __init__.py                    (1 line: package docstring)
+ +

2. Stop offering to install it

+

+ install_core_system.sh's secondary-module menu drops item + 8 outright — it was the last entry, so no renumbering was needed for + the remaining seven. +

+
-ask_install "Focus-mode daemon" \
+-    bash "$LINUX_CONFIG/scripts/periodic_background/digital_wellbeing/install_focus_mode_daemon.sh" install
+-
+ +

3. Stop auditing and self-healing it

+

+ check_and_enable_services.sh is the script that checks all + 12 (now 11) personal services and repairs anything broken. Its entire + check_focus_mode() function — the one whose repair path + hit the sudo -u session-bus bug described above — is + deleted, along with its variable, its slot in the summary table, and + its call site. The remaining services in the printed list shift down + by one number so item 8 (Thorium startup) doesn't leave a gap where 6 + used to be. +

+
-  6. Focus mode daemon    - Steam/Browser mutual exclusion (user service)
+-  7. Compulsive blocker   - Limits messaging apps to one launch per hour
++  6. Compulsive blocker   - Limits messaging apps to one launch per hour
++  7. Thorium startup      - Auto-launch Thorium with Fitatu on boot
+   ...
+-	for service in ... hosts focus_mode compulsive_blocker ...; do
++	for service in ... hosts compulsive_blocker ...; do
+   ...
+-	check_hosts
+-	check_focus_mode
+-	check_compulsive_blocker
++	check_hosts
++	check_compulsive_blocker
+ +

4. Drop the test coverage, keep the numbering sane

+

+ test_security_hardening.sh loses its three focus-mode + checks (daemon file exists, installer exists, service is active). The + tests that followed — for an unrelated screen-locker component — get + their comment numbers shifted down from 23–27 to 20–24, purely so the + numbering stays sequential. test_result() tallies + pass/fail/skip by counting calls, not by parsing these comments, so + this renumbering is cosmetic, not functional. +

+
-# Test 20: Focus mode daemon script exists
+-if [[ -f "$focus_daemon" ]]; then ...
+-# Test 21: Focus mode installer exists
+-...
+-# Test 22: Focus mode daemon is running (user service)
+-...
+-
+-# Test 23: Screen locker exists
++# Test 20: Screen locker exists
+ +

5. Update the docs

+

+ CLAUDE.md's directory map and "Key scripts" list both + drop their mentions of the daemon. +

+
-│       ├── digital_wellbeing/      # Compulsive-opening blocker, focus daemon, LeechBlock
++│       ├── digital_wellbeing/      # Compulsive-opening blocker, LeechBlock
+ ...
+-- `scripts/periodic_background/digital_wellbeing/focus_mode_daemon.py` — Linux digital-wellbeing daemon
+ +

+ Left untouched on purpose: phone_focus_mode/ (a completely + separate GPS-based Android app-restriction system that just happens to + share the word "focus"), and the historical design docs + (docs/SECURITY_HARDENING_ANALYSIS.md, + docs/superpowers/evidence|contracts) that originally + proposed and tracked this daemon — those stay as append-only history of + what was tried. +

+
+ +
+

Quiz

+

Five questions on the reasoning behind this change. Click an option to check it.

+
+
Score: 0 / 5
+
+ + +
+ + diff --git a/docs/explanations/index.html b/docs/explanations/index.html index 6dd9caaa..b2444c40 100644 --- a/docs/explanations/index.html +++ b/docs/explanations/index.html @@ -3,7 +3,7 @@ -explanations — change explanations +testsAndMisc — change explanations