From 3ea0359748371706305ff2a7322d794da01565d0 Mon Sep 17 00:00:00 2001 From: Krzysztof kuhy Rudnicki Date: Mon, 12 Oct 2026 08:22:43 +0200 Subject: [PATCH] i3blocks: XDG_RUNTIME_DIR falls back to /run/user/$UID, not /tmp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A session started while logind was off the system bus had no XDG_RUNTIME_DIR, so the gaming and shutdown blocks looked for daily-limits' cache in /tmp and showed '⏱? no data'. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01DHsV8jzhhX162EcxxEXbca --- ...locks-runtime-dir-fallback-2026-10-12.json | 14 +++++++++ ...locks-runtime-dir-fallback-2026-10-12.json | 31 +++++++++++++++++++ linux_configuration/i3blocks/gaming_budget.sh | 4 +-- linux_configuration/i3blocks/screen_locker.sh | 2 +- .../i3blocks/shutdown_countdown.sh | 4 +-- .../tests/test_i3blocks_efficiency.sh | 7 +++++ 6 files changed, 57 insertions(+), 5 deletions(-) create mode 100644 docs/superpowers/contracts/i3blocks-runtime-dir-fallback-2026-10-12.json create mode 100644 docs/superpowers/evidence/i3blocks-runtime-dir-fallback-2026-10-12.json diff --git a/docs/superpowers/contracts/i3blocks-runtime-dir-fallback-2026-10-12.json b/docs/superpowers/contracts/i3blocks-runtime-dir-fallback-2026-10-12.json new file mode 100644 index 00000000..956c4b97 --- /dev/null +++ b/docs/superpowers/contracts/i3blocks-runtime-dir-fallback-2026-10-12.json @@ -0,0 +1,14 @@ +{ + "title": "i3blocks: XDG_RUNTIME_DIR falls back to /run/user/$UID", + "objective": "The gaming, shutdown and screen-locker blocks read daily-limits' cache from /run/user/$UID when the X session has no XDG_RUNTIME_DIR, instead of looking in /tmp and showing '⏱? no data'.", + "acceptance_criteria": [ + "With XDG_RUNTIME_DIR unset, gaming_budget.sh and shutdown_countdown.sh render the live cache (time left, unearned earners)", + "No i3blocks script falls back to /tmp for XDG_RUNTIME_DIR (checked in test_i3blocks_efficiency.sh)", + "test_i3blocks_{efficiency,shutdown_hint,gaming_budget}.sh pass" + ], + "out_of_scope": [ + "Reconnecting systemd-logind to the system bus (needs a logind restart and relogin)", + "Finding what re-parses systemd units every ~6 s" + ], + "verifier": "bash linux_configuration/tests/test_i3blocks_efficiency.sh && bash linux_configuration/tests/test_i3blocks_shutdown_hint.sh && bash linux_configuration/tests/test_i3blocks_gaming_budget.sh" +} diff --git a/docs/superpowers/evidence/i3blocks-runtime-dir-fallback-2026-10-12.json b/docs/superpowers/evidence/i3blocks-runtime-dir-fallback-2026-10-12.json new file mode 100644 index 00000000..b3a48b2b --- /dev/null +++ b/docs/superpowers/evidence/i3blocks-runtime-dir-fallback-2026-10-12.json @@ -0,0 +1,31 @@ +{ + "intent": "i3blocks gaming/shutdown/screen-locker blocks find daily-limits' cache even when the X session started without XDG_RUNTIME_DIR.", + "scope": [ + "linux_configuration/i3blocks/{gaming_budget,shutdown_countdown,screen_locker}.sh", + "linux_configuration/tests/test_i3blocks_efficiency.sh", + "Non-goal: repairing logind's lost bus connection (needs a logind restart/relogin)" + ], + "changes": [ + "XDG_RUNTIME_DIR fallback changed from /tmp to /run/user/$UID (bash builtin, no fork)", + "Regression check: no i3blocks script may fall back to /tmp for XDG_RUNTIME_DIR" + ], + "verification": [ + { + "command": "env -u XDG_RUNTIME_DIR bash linux_configuration/i3blocks/gaming_budget.sh (and shutdown_countdown.sh)", + "result": "pass", + "evidence": "🎮 5h00 left · 0h00/5h00 +🧩1h +📖1h +⚙1h / ⏻ 20:40 +🧩50m +📖30m +⚙1h →max 23:00; live bar confirmed by user" + }, + { + "command": "bash linux_configuration/tests/test_i3blocks_{efficiency,shutdown_hint,gaming_budget}.sh", + "result": "pass", + "evidence": "all three suites exit 0" + } + ], + "risks": [ + "A non-systemd login would still have no /run/user/$UID; the blocks then show the existing missing-cache state" + ], + "rollback": [ + "git revert this commit", + "Re-run the three i3blocks test suites" + ] +} diff --git a/linux_configuration/i3blocks/gaming_budget.sh b/linux_configuration/i3blocks/gaming_budget.sh index b047cf47..ac41f80c 100755 --- a/linux_configuration/i3blocks/gaming_budget.sh +++ b/linux_configuration/i3blocks/gaming_budget.sh @@ -14,8 +14,8 @@ set -euo pipefail -readonly LIMITS_CACHE="${DAILY_LIMITS_CACHE:-${XDG_RUNTIME_DIR:-/tmp}/daily-limits.json}" -readonly RENDER_CACHE="${DAILY_LIMITS_RENDER_CACHE:-${XDG_RUNTIME_DIR:-/tmp}/i3blocks-gaming-budget.render}" +readonly LIMITS_CACHE="${DAILY_LIMITS_CACHE:-${XDG_RUNTIME_DIR:-/run/user/$UID}/daily-limits.json}" +readonly RENDER_CACHE="${DAILY_LIMITS_RENDER_CACHE:-${XDG_RUNTIME_DIR:-/run/user/$UID}/i3blocks-gaming-budget.render}" readonly STALE_AFTER_SECONDS=180 # Dracula-ish palette, matching screen_locker.sh. diff --git a/linux_configuration/i3blocks/screen_locker.sh b/linux_configuration/i3blocks/screen_locker.sh index 27d8f3af..2c4e5ba0 100755 --- a/linux_configuration/i3blocks/screen_locker.sh +++ b/linux_configuration/i3blocks/screen_locker.sh @@ -16,7 +16,7 @@ set -euo pipefail readonly API='http://127.0.0.1:8770/api/status' readonly HEALTH='http://127.0.0.1:8770/api/health' readonly UI='http://127.0.0.1:8770/' -readonly CACHE="${XDG_RUNTIME_DIR:-/tmp}/i3blocks-screen-locker.cache" +readonly CACHE="${XDG_RUNTIME_DIR:-/run/user/$UID}/i3blocks-screen-locker.cache" # Dracula-ish palette, matching the other blocks in this bar. readonly RED='#FF5555' diff --git a/linux_configuration/i3blocks/shutdown_countdown.sh b/linux_configuration/i3blocks/shutdown_countdown.sh index 6fdefdc4..dd0d79de 100755 --- a/linux_configuration/i3blocks/shutdown_countdown.sh +++ b/linux_configuration/i3blocks/shutdown_countdown.sh @@ -16,8 +16,8 @@ set -euo pipefail SHUTDOWN_CONFIG=${SHUTDOWN_CONFIG:-/etc/shutdown-schedule.conf} SKIP_DATES_FILE=${SKIP_DATES_FILE:-/etc/shutdown-skip-dates} OVERRIDES_FILE=${OVERRIDES_FILE:-/etc/shutdown-schedule-overrides.conf} -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} +LIMITS_CACHE=${DAILY_LIMITS_CACHE:-${XDG_RUNTIME_DIR:-/run/user/$UID}/daily-limits.json} +RENDER_CACHE=${DAILY_LIMITS_RENDER_CACHE:-${XDG_RUNTIME_DIR:-/run/user/$UID}/i3blocks-shutdown.render} readonly STALE_AFTER_SECONDS=180 # Left-click opens the popup, by path: i3blocks' PATH has no ~/.local/bin. diff --git a/linux_configuration/tests/test_i3blocks_efficiency.sh b/linux_configuration/tests/test_i3blocks_efficiency.sh index 86409e40..9798a3cc 100755 --- a/linux_configuration/tests/test_i3blocks_efficiency.sh +++ b/linux_configuration/tests/test_i3blocks_efficiency.sh @@ -235,4 +235,11 @@ shutdown_minutes_output=$(TZ=UTC NOW_EPOCH="$(epoch_utc '2026-05-01 18:31:00')" assert_equals '⏻ SHUTDOWN' "$(printf '%s\n' "$shutdown_minutes_output" | sed -n '1p')" \ 'shutdown countdown should be due once the shutdown minute has passed' +# A session started while logind is off the bus has no XDG_RUNTIME_DIR; a /tmp +# fallback then misses daily-limits' cache in /run/user/$UID (2026-10-12). +if grep -n 'XDG_RUNTIME_DIR:-/tmp' "$I3BLOCKS_DIR"/*.sh; then + printf 'FAIL: XDG_RUNTIME_DIR must fall back to /run/user/, not /tmp\n' >&2 + exit 1 +fi + printf 'All i3blocks efficiency regression tests passed.\n'