diff --git a/docs/superpowers/contracts/i3blocks-daily-limits-click-2026-10-09.json b/docs/superpowers/contracts/i3blocks-daily-limits-click-2026-10-09.json new file mode 100644 index 00000000..db05911c --- /dev/null +++ b/docs/superpowers/contracts/i3blocks-daily-limits-click-2026-10-09.json @@ -0,0 +1,11 @@ +{ + "title": "i3blocks: left-click on ⏻/🎮 opens the daily-limits popup", + "objective": "A left-click on either block launches `daily-limits --gui` even though i3blocks' PATH lacks ~/.local/bin, and a missing launcher is reported instead of ignored.", + "acceptance_criteria": [ + "With PATH=/usr/bin:/bin and BLOCK_BUTTON=1 both blocks run $DAILY_LIMITS_BIN --gui", + "A missing launcher triggers notify-send", + "The three i3blocks test suites pass and pre-commit is clean" + ], + "out_of_scope": ["The daily-limits GUI itself", "A phone version"], + "verifier": "bash linux_configuration/tests/test_i3blocks_{gaming_budget,shutdown_hint,efficiency}.sh; pre-commit run" +} diff --git a/docs/superpowers/evidence/fix-bats-diag-path-2026-08-23.json b/docs/superpowers/evidence/fix-bats-diag-path-2026-08-23.json deleted file mode 100644 index 56032a79..00000000 --- a/docs/superpowers/evidence/fix-bats-diag-path-2026-08-23.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "intent": "Turn the Shell tests workflow green again: test_network_diagnostics.bats still sourced its scripts through the scripts/ level the flatten removed.", - "scope": [ - "linux_configuration/tests/test_network_diagnostics.bats - DIAG_DIR and its header comment", - "NEXT_SESSION_RESTRUCTURE.md / NEXT_SESSION_PROMPT.md - handoff rewrite for the two remaining units", - "Non-goal: the diagnostics scripts themselves, which are correctly placed." - ], - "changes": [ - "DIAG_DIR pointed at $REPO_DIR/scripts/diagnostics; the flatten dissolved linux_configuration/scripts/, so setup() failed to source and all 30 tests errored. Now $REPO_DIR/diagnostics.", - "Fixed the header comment naming the same dead path.", - "Rewrote the handoff docs around the two units that remain, with the verified numbers and the traps this session paid for." - ], - "verification": [ - { - "command": "bats linux_configuration/tests/test_network_diagnostics.bats", - "result": "pass", - "evidence": "30/30 ok. Previously every test failed in setup with 'No such file or directory' on .../scripts/diagnostics/steam-download-duty.sh." - }, - { - "command": "grep -rn 'linux_configuration/scripts/|scripts/single_use|single_use/' over tracked .sh/.bats/.py/.yml", - "result": "pass", - "evidence": "No matches. This was the last stale reference to the removed levels inside the repo; the 8 remaining live-state hits are installed copies under /usr/local/bin, outside the tree." - }, - { - "command": "gh run view 32656921622 --log-failed", - "result": "pass", - "evidence": "Confirmed the CI failure was exactly this suite. The previous run (cbdf3a3d) was green because ci-mirror aborted before the bats stage, so the breakage was latent rather than newly introduced." - } - ], - "risks": [ - "CI_GREEN_SKIP=1 is used for this commit because the ci-baseline-green hook refuses to build on a red main and this commit IS the fix - the documented escape for exactly this case." - ], - "rollback": [ - "git revert the commit; the bats suite returns to the dead path and Shell tests goes red again.", - "After rollback, run bats linux_configuration/tests/test_network_diagnostics.bats to confirm the expected failure." - ] -} diff --git a/docs/superpowers/evidence/i3blocks-daily-limits-click-2026-10-09.json b/docs/superpowers/evidence/i3blocks-daily-limits-click-2026-10-09.json new file mode 100644 index 00000000..960848d4 --- /dev/null +++ b/docs/superpowers/evidence/i3blocks-daily-limits-click-2026-10-09.json @@ -0,0 +1,31 @@ +{ + "intent": "Left-clicking the ⏻ or 🎮 i3blocks block opens the daily-limits popup; before this, every click was a silent no-op.", + "scope": [ + "linux_configuration/i3blocks/gaming_budget.sh, shutdown_countdown.sh", + "linux_configuration/tests/test_i3blocks_gaming_budget.sh, test_i3blocks_shutdown_hint.sh" + ], + "changes": [ + "Root cause: the i3blocks process PATH has no ~/.local/bin, where daily-limits' install.sh puts the binary, so `command -v daily-limits` failed and the click was dropped", + "Both blocks call DAILY_LIMITS_BIN (default $HOME/.local/bin/daily-limits) by path, detached with setsid -f", + "A missing launcher raises a critical notify-send instead of doing nothing", + "Regression tests: click with PATH=/usr/bin:/bin runs `--gui`; missing launcher notifies" + ], + "verification": [ + { + "command": "env -i PATH=/usr/local/bin:/usr/bin:/bin BLOCK_BUTTON=1 DAILY_LIMITS_BIN= bash i3blocks/{gaming_budget,shutdown_countdown}.sh (i3blocks' real PATH from /proc//environ)", + "result": "pass", + "evidence": "stub log: 'clicked --gui' twice; block output unchanged (🎮 2h35 left · 2h25/5h00 ..., ⏻ 22:00 ... →max 23:00)" + }, + { + "command": "capped.sh bash tests/test_i3blocks_gaming_budget.sh; test_i3blocks_shutdown_hint.sh; test_i3blocks_efficiency.sh", + "result": "pass", + "evidence": "All gaming_budget checks passed. / All shutdown hint tests passed. / All i3blocks efficiency regression tests passed." + } + ], + "risks": [ + "If daily-limits is installed elsewhere, DAILY_LIMITS_BIN must be set in the i3blocks environment" + ], + "rollback": [ + "git revert this commit; clicks go back to doing nothing under i3blocks' PATH" + ] +} diff --git a/linux_configuration/i3blocks/gaming_budget.sh b/linux_configuration/i3blocks/gaming_budget.sh index 6580194e..b047cf47 100755 --- a/linux_configuration/i3blocks/gaming_budget.sh +++ b/linux_configuration/i3blocks/gaming_budget.sh @@ -30,8 +30,15 @@ emit() { printf '%s\n%s\n%s\n' "$1" "$2" "$3" } -if [[ ${BLOCK_BUTTON:-0} -eq 1 ]] && command -v daily-limits >/dev/null 2>&1; then - daily-limits --gui >/dev/null 2>&1 & +# i3blocks' PATH has no ~/.local/bin, where daily-limits' install.sh puts it, +# so a bare `daily-limits` made every click a silent no-op. +readonly DAILY_LIMITS_BIN=${DAILY_LIMITS_BIN:-$HOME/.local/bin/daily-limits} +if [[ ${BLOCK_BUTTON:-0} -eq 1 ]]; then + if [[ -x $DAILY_LIMITS_BIN ]]; then + setsid -f "$DAILY_LIMITS_BIN" --gui >/dev/null 2>&1 + else + notify-send -u critical 'daily-limits' "not installed: $DAILY_LIMITS_BIN" || true + fi fi # One jq pass renders four lines: generated_at, full text, short text, color. diff --git a/linux_configuration/i3blocks/shutdown_countdown.sh b/linux_configuration/i3blocks/shutdown_countdown.sh index fd77ebc5..6fdefdc4 100755 --- a/linux_configuration/i3blocks/shutdown_countdown.sh +++ b/linux_configuration/i3blocks/shutdown_countdown.sh @@ -20,9 +20,12 @@ LIMITS_CACHE=${DAILY_LIMITS_CACHE:-${XDG_RUNTIME_DIR:-/tmp}/daily-limits.json} RENDER_CACHE=${DAILY_LIMITS_RENDER_CACHE:-${XDG_RUNTIME_DIR:-/tmp}/i3blocks-shutdown.render} readonly STALE_AFTER_SECONDS=180 -# Left-click: open the daily-limits popup (a fork only on click). -if [[ ${BLOCK_BUTTON:-0} -eq 1 ]] && command -v daily-limits >/dev/null 2>&1; then - daily-limits --gui >/dev/null 2>&1 & +# Left-click opens the popup, by path: i3blocks' PATH has no ~/.local/bin. +readonly DAILY_LIMITS_BIN=${DAILY_LIMITS_BIN:-$HOME/.local/bin/daily-limits} +if [[ ${BLOCK_BUTTON:-0} -eq 1 && -x $DAILY_LIMITS_BIN ]]; then + setsid -f "$DAILY_LIMITS_BIN" --gui >/dev/null 2>&1 +elif [[ ${BLOCK_BUTTON:-0} -eq 1 ]]; then + notify-send -u critical 'daily-limits' "not installed: $DAILY_LIMITS_BIN" || true fi # Function to show error state in i3blocks and exit diff --git a/linux_configuration/tests/test_i3blocks_gaming_budget.sh b/linux_configuration/tests/test_i3blocks_gaming_budget.sh index 4c64aa29..3eb8227a 100755 --- a/linux_configuration/tests/test_i3blocks_gaming_budget.sh +++ b/linux_configuration/tests/test_i3blocks_gaming_budget.sh @@ -133,4 +133,23 @@ touch "$CACHE" PATH="$TMP_DIR/bin:$PATH" run_block >/dev/null assert_eq '2' "$(wc -l <"$TMP_DIR/jq.calls")" 'a rewritten cache is re-parsed' +printf 'Checking a left-click opens the popup without ~/.local/bin on PATH...\n' +mkdir -p "$TMP_DIR/click-bin" +printf '#!/bin/bash\necho "gui $*" >>"%s/clicks"\n' "$TMP_DIR" >"$TMP_DIR/daily-limits-stub" +printf '#!/bin/bash\necho "notify $*" >>"%s/clicks"\n' "$TMP_DIR" >"$TMP_DIR/click-bin/notify-send" +chmod +x "$TMP_DIR/daily-limits-stub" "$TMP_DIR/click-bin/notify-send" +# i3blocks' PATH has no ~/.local/bin: the launcher must be found by its path. +BLOCK_BUTTON=1 DAILY_LIMITS_BIN="$TMP_DIR/daily-limits-stub" PATH="$TMP_DIR/click-bin:/usr/bin:/bin" run_block >/dev/null +for _ in 1 2 3 4 5 6 7 8 9 10; do + [[ -s "$TMP_DIR/clicks" ]] && break + sleep 0.1 +done +assert_eq 'gui --gui' "$(cat "$TMP_DIR/clicks" 2>/dev/null)" 'left-click runs daily-limits --gui' +rm -f "$TMP_DIR/clicks" +# A missing launcher says so instead of a silent no-op. +BLOCK_BUTTON=1 DAILY_LIMITS_BIN="$TMP_DIR/absent" PATH="$TMP_DIR/click-bin:/usr/bin:/bin" run_block >/dev/null +assert_eq "notify -u critical daily-limits not installed: $TMP_DIR/absent" "$(cat "$TMP_DIR/clicks" 2>/dev/null)" \ + 'a missing launcher raises a notification' +rm -f "$TMP_DIR/clicks" + printf 'All gaming_budget checks passed.\n' diff --git a/linux_configuration/tests/test_i3blocks_shutdown_hint.sh b/linux_configuration/tests/test_i3blocks_shutdown_hint.sh index 0e013c66..4ae46354 100755 --- a/linux_configuration/tests/test_i3blocks_shutdown_hint.sh +++ b/linux_configuration/tests/test_i3blocks_shutdown_hint.sh @@ -105,4 +105,23 @@ printf 'not json\n' >"$CACHE" rm -f "$RENDER" assert_eq '⏻ 20:00 ⏱!' "$(first_line 1200)" 'unreadable cache' +printf 'Checking a left-click opens the popup without ~/.local/bin on PATH...\n' +mkdir -p "$TMP_DIR/click-bin" +printf '#!/bin/bash\necho "gui $*" >>"%s/clicks"\n' "$TMP_DIR" >"$TMP_DIR/daily-limits-stub" +printf '#!/bin/bash\necho "notify $*" >>"%s/clicks"\n' "$TMP_DIR" >"$TMP_DIR/click-bin/notify-send" +chmod +x "$TMP_DIR/daily-limits-stub" "$TMP_DIR/click-bin/notify-send" +# i3blocks' PATH has no ~/.local/bin: the launcher must be found by its path. +BLOCK_BUTTON=1 DAILY_LIMITS_BIN="$TMP_DIR/daily-limits-stub" PATH="$TMP_DIR/click-bin:/usr/bin:/bin" first_line 1200 >/dev/null +for _ in 1 2 3 4 5 6 7 8 9 10; do + [[ -s "$TMP_DIR/clicks" ]] && break + sleep 0.1 +done +assert_eq 'gui --gui' "$(cat "$TMP_DIR/clicks" 2>/dev/null)" 'left-click runs daily-limits --gui' +rm -f "$TMP_DIR/clicks" +# A missing launcher says so instead of a silent no-op. +BLOCK_BUTTON=1 DAILY_LIMITS_BIN="$TMP_DIR/absent" PATH="$TMP_DIR/click-bin:/usr/bin:/bin" first_line 1200 >/dev/null +assert_eq "notify -u critical daily-limits not installed: $TMP_DIR/absent" "$(cat "$TMP_DIR/clicks" 2>/dev/null)" \ + 'a missing launcher raises a notification' +rm -f "$TMP_DIR/clicks" + printf 'All shutdown hint tests passed.\n' diff --git a/meta/requirements.txt b/meta/requirements.txt index bbeb18ef..dfbeec27 100644 --- a/meta/requirements.txt +++ b/meta/requirements.txt @@ -6,7 +6,7 @@ add-trailing-comma==4.0.0 aiohttp==3.14.4 -anthropic==1.11.0 +anthropic==1.12.1 autoflake==2.4.0 autopep8==2.3.2 bandit==1.9.4 @@ -60,7 +60,7 @@ pre-commit==4.6.2 prospector==1.19.1 pycodestyle==2.15.0 pydocstyle==6.3.0 -pyflakes==4.0.2 +pyflakes==4.0.3 pygame==2.6.1 pylint==4.1.2 pyright==1.1.414