diff --git a/docs/superpowers/evidence/extract-hosts-blocker-2026-08-23.json b/docs/superpowers/evidence/extract-hosts-blocker-2026-08-23.json deleted file mode 100644 index 225c617e..00000000 --- a/docs/superpowers/evidence/extract-hosts-blocker-2026-08-23.json +++ /dev/null @@ -1,88 +0,0 @@ -{ - "intent": "Extract linux_configuration/periodic_background/hosts/ into its own public repo (github.com/kuhyx/hosts-blocker, cloned at ~/src/hosts-blocker), clearing 5 of the 46 remaining directory-depth violations without disturbing the live hosts blocker or its guards.", - "scope": [ - "REMOVED: linux_configuration/periodic_background/hosts/ (15 files)", - "ADDED: linux_configuration/lib/extracted_repos.sh (shared resolver)", - "REWRITTEN callers: check_and_enable_services.sh, setup_periodic_system.sh, features/setup_dns_blocker.sh, fixes/migrate_hosts_guard_to_guard_lib.sh, install_core_system.sh, fresh-install/main.sh", - "DOCS: CLAUDE.md, INSTALLER_FIX_TASK.md, docs/shell-split-recipes.md", - "NON-GOAL: digital_wellbeing (41 violations) — separate commit", - "NON-GOAL: removing --warn from the depth hook — only valid once BOTH units are out", - "NON-GOAL: reformatting the guard plugins with shfmt" - ], - "changes": [ - "git filter-repo across ALL FOUR historical roots (hosts/, linux_configuration/hosts/, linux_configuration/scripts/periodic_background/hosts/, linux_configuration/periodic_background/hosts/) with matching --path-rename rules. Filtering on the current path alone would have preserved 1 commit instead of 51 — the flatten had moved the unit recently, so a naive single --path silently truncates the history to the flatten commit.", - "No source file was modified during extraction: all 15 files are byte-identical to the monorepo copies (verified with diff -r). install.sh already resolved its lib/ via BASH_SOURCE and needed no rewrite.", - "Added linux_configuration/lib/extracted_repos.sh: resolves an extracted repo's checkout via SUDO_USER's passwd entry (NOT $HOME, which is /root under sudo), supports a _DIR override, and fails loudly with a clone instruction when absent.", - "Reused the existing REAL_HOME resolution already present in check_and_enable_services.sh for screen-locker rather than sourcing a second resolver into it.", - "fresh-install/main.sh uses \"$HOME/src/hosts-blocker/install.sh\" because it runs from ~/src/testsAndMisc/linux_configuration (a different repo) and is not sudo-re-exec'd at that point.", - "The new repo ships NO shfmt hook, deliberately: guard/plugins/*.sh are space-indented and unformatted in the monorepo too (its git hook shfmt's only STAGED files), and their installed copies under /usr/local/share/guard-lib-plugins/ are byte-identical by md5. An shfmt hook would rewrite both on first commit and break that identity." - ], - "verification": [ - { - "command": "git log --all --oneline -- | wc -l", - "result": "pass", - "evidence": "current path alone: 1 commit; union of all four roots: 54. Extracted repo retains 51 + 1 setup commit = 52, reaching back to 'feat: block websites uising etc hosts'." - }, - { - "command": "diff -r --exclude=.git ", - "result": "pass", - "evidence": "IDENTICAL — no source file altered by the extraction." - }, - { - "command": "./lib/tests/run_all.sh (in ~/src/hosts-blocker, the real clone location)", - "result": "pass", - "evidence": "test_hosts_protect.sh: 25 passed, 0 failed. All hosts install test files passed." - }, - { - "command": "pre-commit run --all-files --hook-stage pre-push (in the extracted repo)", - "result": "pass", - "evidence": "shebangs/merge-conflict/end-of-file/line-ending/trailing-whitespace/shellcheck all Passed; 'hosts lib tests' Passed. No file modified by any hook." - }, - { - "command": "sudo -n bash -c 'source lib/extracted_repos.sh; extracted_repo_dir hosts-blocker'", - "result": "pass", - "evidence": "HOME=/home/kuhy resolved=/home/kuhy/src/hosts-blocker — resolves the invoking user, not /root/hosts-blocker." - }, - { - "command": "HOSTS_BLOCKER_DIR=/nonexistent require_extracted_repo hosts-blocker", - "result": "pass", - "evidence": "Returned 1 and printed the expected path plus a 'git clone ...' instruction — fails loudly, never a silent skip." - }, - { - "command": "shellcheck on the helper + all six rewritten callers", - "result": "pass", - "evidence": "ALL CLEAN (no --severity filter, matching the repo hook)." - }, - { - "command": "bash linux_configuration/tests/test_setup_dns_blocker.sh", - "result": "pass", - "evidence": "PASS: test_setup_dns_blocker.sh — the existing suite passes against the rewritten GEN path." - }, - { - "command": "./meta/scripts/check_directory_depth.sh --all | head -1", - "result": "pass", - "evidence": "46 -> 41 violations; all 5 hosts entries cleared, the 41 remaining are digital_wellbeing." - }, - { - "command": "diff live-state-before.txt live-state-after.txt", - "result": "pass", - "evidence": "NO CHANGE — the 8 pre-existing stale references are unchanged and none were added." - }, - { - "command": "md5sum /usr/local/share/guard-lib-plugins/resolved-plugin.sh vs repo copy", - "result": "pass", - "evidence": "5f4216dc0ffdbc3feeece2346d330c56 on both — the live guards load /usr/local/share/, not a repo path, so the move does not disturb them." - } - ], - "risks": [ - "A machine without ~/src/hosts-blocker cloned now fails the hosts install step. That is deliberate and loud (clone instruction printed); the previous silent-skip mode is what caused five months of dead self-repair after the 2026-05-15 reorg.", - "install_core_system.sh on a genuinely fresh machine will hard-stop at the hosts module until the repo is cloned. INSTALLER_FIX_TASK.md already tracks fresh-machine installer defects and has been updated to name the new location.", - "The guard plugins remain unformatted. If an shfmt hook is ever added to hosts-blocker it will rewrite them, and the installed copies must be re-synced in the same change.", - "The live hosts blocker was NOT re-installed as part of this change; /etc/hosts and its guards are untouched and still enforced from their installed copies." - ], - "rollback": [ - "git revert the monorepo commit — this restores linux_configuration/periodic_background/hosts/ in full and reverts all six callers to repo-relative paths.", - "The extracted repo can be left in place; nothing in the monorepo depends on it after a revert.", - "After rollback validate: ./meta/scripts/check_directory_depth.sh --all reports 46 again, bash linux_configuration/tests/test_setup_dns_blocker.sh passes, and guardctl file-guard status resolved still names /usr/local/share/guard-lib-plugins/." - ] -} diff --git a/docs/superpowers/evidence/i3blocks-gaming-budget-automation-2026-10-05.json b/docs/superpowers/evidence/i3blocks-gaming-budget-automation-2026-10-05.json new file mode 100644 index 00000000..a45deee9 --- /dev/null +++ b/docs/superpowers/evidence/i3blocks-gaming-budget-automation-2026-10-05.json @@ -0,0 +1,30 @@ +{ + "intent": "The i3 gaming-budget block shows the new earned_time 0.3.0 automation earner (anki-guard's Automation-deck quota) as ⚙ instead of its text label.", + "scope": [ + "linux_configuration/i3blocks/gaming_budget.sh", + "linux_configuration/tests/test_i3blocks_gaming_budget.sh", + "Non-goal: no change to how the budget is computed or rendered; only the icon map grows" + ], + "changes": [ + "Icon map gains automation: ⚙ beside workout 💪, leetcode 🧩, reading 📖, anki 🗂", + "The .rules.earners test case lists an automation earner and expects '+⚙30m' in registry order" + ], + "verification": [ + { + "command": "bash linux_configuration/i3blocks/gaming_budget.sh (live enforcer API, after restarting on earned-time 0.3.0)", + "result": "pass", + "evidence": "'🎮 1h19 left · 5h44/7h03 +📖1h +🗂30m +⚙30m'; /api/budget lists automation 0/1800" + }, + { + "command": "bash linux_configuration/tests/test_i3blocks_gaming_budget.sh", + "result": "pass", + "evidence": "All gaming_budget checks passed, incl. '+🧩1h +📖1h +🗂30m +⚙30m +Piano1h30'" + } + ], + "risks": [ + "⚙ (U+2699) without a variation selector may render as text-style in some fonts; it is still one glyph wide." + ], + "rollback": [ + "git revert this commit; automation then shows as its label 'Automation30m'" + ] +} diff --git a/linux_configuration/i3blocks/gaming_budget.sh b/linux_configuration/i3blocks/gaming_budget.sh index c492247c..06693535 100755 --- a/linux_configuration/i3blocks/gaming_budget.sh +++ b/linux_configuration/i3blocks/gaming_budget.sh @@ -2,7 +2,7 @@ # ============================================================================ # Gaming time budget for i3blocks: time left today, used/total, and which -# bonuses (workout, LeetCode, reading, Anki, ...) are still there to be earned. +# bonuses (workout, LeetCode, reading, Anki, Automation, ...) are still there to be earned. # # Reads steam-backlog-enforcer's /api/budget, which already folds in the # screen-locker, leetcode-guard and book-guard bonuses. Like screen_locker.sh @@ -74,7 +74,7 @@ rendered=$(jq -r \ # registry (.rules.earners) is the source of truth, so a new earner # needs only an icon here; an older daemon falls back to the three # fixed fields and the configured sizes. - | {workout: "💪", leetcode: "🧩", reading: "📖", anki: "🗂"} as $icon + | {workout: "💪", leetcode: "🧩", reading: "📖", anki: "🗂", automation: "⚙"} as $icon | (if .rules.earners then [.rules.earners[] | [.earned_seconds, .bonus_seconds, ($icon[.name] // .label)]] else diff --git a/linux_configuration/tests/test_i3blocks_gaming_budget.sh b/linux_configuration/tests/test_i3blocks_gaming_budget.sh index 3b72e704..b1615382 100755 --- a/linux_configuration/tests/test_i3blocks_gaming_budget.sh +++ b/linux_configuration/tests/test_i3blocks_gaming_budget.sh @@ -96,12 +96,12 @@ earner() { jq -nc --arg n "$1" --arg l "$2" --argjson e "$3" --argjson s "$4" \ '{name: $n, label: $l, earned_seconds: $e, bonus_seconds: $s}' } -jq -n --argjson earners "[$(earner workout workout 7200 7200),$(earner leetcode LeetCode 0 3600),$(earner reading reading 0 3600),$(earner anki Anki 0 1800),$(earner piano Piano 0 5400)]" \ +jq -n --argjson earners "[$(earner workout workout 7200 7200),$(earner leetcode LeetCode 0 3600),$(earner reading reading 0 3600),$(earner anki Anki 0 1800),$(earner automation Automation 0 1800),$(earner piano Piano 0 5400)]" \ '{ok: true, state_status: "ok", today: {seconds_used: 0, budget_seconds: 14400, seconds_remaining: 14400, blocked: false}, rules: {bonuses: {}, earners: $earners}}' >"$FIXTURE" printf '{"leetcode_bonus_seconds":0}\n' >"$CONFIG_FILE" -assert_eq '🎮 4h00 left · 0h00/4h00 +🧩1h +📖1h +🗂30m +Piano1h30' "$(line 1)" \ +assert_eq '🎮 4h00 left · 0h00/4h00 +🧩1h +📖1h +🗂30m +⚙30m +Piano1h30' "$(line 1)" \ 'registry order, earned omitted, sizes from the daemon not the config, label for an unmapped earner' rm -f "$CONFIG_FILE"