Drop the extension, and give the test suite a front door
Phase 6, the last of the fresh-install spec. 159 scripts lose their .sh: 110 contracts, 47 Vicinae commands, 2 compositor contracts. A shebang and the executable bit already select the interpreter. The extension only ever added something that had to stay in sync, and the rename proved the point twice over in the space of an hour. The spec's stated risk was Vicinae's script discovery. One script was renamed and reloaded on its own before the other 46 followed; it came back as scripts:panama.capture and all 47 resolve. What the probe turned up instead is that the extension was never only a filename: Vicinae's command IDs embed it, so every ID changed. Nothing in this repository refers to them, so nothing breaks. The only trace is Vicinae's metadata.json, whose visited map had two Panama entries that are now orphaned -- two commands lost their usage ranking and will earn it back. Worth knowing before anyone renames these again on a machine that has a keybind pointing at one. Rewriting the references by exact filename missed two things it structurally could not see: a name built from a variable, settings-$page.sh, and a glob, -name '*.sh'. Both were in the contract that counts the generated commands, which promptly reported 47 expected and 0 found. The mechanical part of a rename is the part that looks finished. The three subcommands. panama doctor fronts a health check that already existed and already ran at the end of every install but could not be reached from a terminal. panama upgrade re-runs the installer from anywhere. panama test runs the suite, which had no entry point at all -- 121 files that were the main safety net in this repository and were invisible in it. Writing that runner found three tests nothing was running. calendar_agenda_bridge_test, home_assistant_bridge_test and kdeconnect_bridge_test are unittest suites without the executable bit, so no contract invoked them and the first draft of the runner skipped them silently. All three pass, and have passed unobserved for weeks. The runner collects *_test.py as well now, because a runner with a blind spot is worse than no runner for the same reason a dependency checker with one is: it reports PASS. Six worktrees pruned. Each was re-checked rather than trusted to the spec's list, and two needed it: panama-commands is not on feat/panama-commands but on feat/gnome-tweaks-parity, and fix/panama-displays-review reads [ahead 3] -- ahead of its remote, not of main, with every commit patch-equivalent to landed work. roadmap-completion stays; it has five commits that are genuinely unlanded. The branches are left alone: pruning a worktree costs nothing, deleting a branch is a decision. 121 contracts pass. Claude-Session: https://claude.ai/code/session_01NvgBuSWB5sE43yWmg21ozj
This commit is contained in:
Executable
+103
@@ -0,0 +1,103 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# What Panama does while a game runs must be undone afterwards -- to what was
|
||||
# there before, not to a default.
|
||||
#
|
||||
# That distinction is the whole feature. If ending a game forced Do Not Disturb
|
||||
# off, it would silently undo a Do Not Disturb someone set by hand; if it forced
|
||||
# the power profile to "balanced", it would undo a deliberate choice. Both are
|
||||
# worse than doing nothing at all, because both look like the desktop
|
||||
# misbehaving rather than a setting being wrong.
|
||||
#
|
||||
# Read-only: it reads gaming state and never engages Game Mode.
|
||||
|
||||
set -uo pipefail
|
||||
|
||||
repo_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||
helper="$repo_dir/config/dot/quickshell/scripts/panama-gaming"
|
||||
service="$repo_dir/config/dot/quickshell/services/Gaming.qml"
|
||||
page="$repo_dir/config/dot/quickshell/modules/settings/GamingPage.qml"
|
||||
shell_file="$repo_dir/config/dot/quickshell/shell.qml"
|
||||
|
||||
fail() {
|
||||
printf 'gaming contract: %s\n' "$1" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
for path in "$helper" "$service" "$page" "$shell_file"; do
|
||||
[[ -r "$path" ]] || fail "missing $path"
|
||||
done
|
||||
[[ -x "$helper" ]] || fail 'panama-gaming is not executable'
|
||||
|
||||
# ── The hook restores, it does not impose ───────────────────────────────────
|
||||
hook_body="$(sed -n '/^def hook/,/^def /p' "$helper")"
|
||||
[[ -n "$hook_body" ]] || fail 'the hook is missing'
|
||||
grep -q 'state_path' <<<"$hook_body" \
|
||||
|| fail 'the hook records nothing about the state before a game, so it cannot restore it'
|
||||
grep -qE 'before\.get\("profile"\)' <<<"$hook_body" \
|
||||
|| fail 'the power profile is not restored to what it was'
|
||||
# Do Not Disturb is no longer the hook's business at all. It belongs to the
|
||||
# Gaming focus mode, which records what Do Not Disturb was before it took over
|
||||
# and puts that back -- the same protection, in one place instead of two. The
|
||||
# assertion is therefore stricter than it was: the hook must not touch it.
|
||||
grep -qE 'setDnd|dndState' <<<"$hook_body" \
|
||||
&& fail 'the hook writes Do Not Disturb again, so it and the focus mode both own it'
|
||||
grep -q 'gameStarted' <<<"$hook_body" \
|
||||
|| fail 'the hook does not report the game to the shell, so no mode can react to it'
|
||||
grep -qE 'set.*"balanced"' <<<"$hook_body" \
|
||||
&& fail 'the hook restores a hardcoded profile rather than the previous one'
|
||||
|
||||
# ── The shell can be told, not only toggled ─────────────────────────────────
|
||||
# A toggle is the wrong primitive here: if notifications were already silenced,
|
||||
# toggling at game start would unsilence them.
|
||||
grep -q 'function setDnd(enabled: bool)' "$shell_file" \
|
||||
|| fail 'there is no explicit way to set Do Not Disturb, only a toggle'
|
||||
grep -q 'function dndState()' "$shell_file" \
|
||||
|| fail 'there is no way to read Do Not Disturb, so the hook cannot know what to restore'
|
||||
# setDnd and dndState remain the right primitives and are still asserted above --
|
||||
# a toggle would flip an already-silent machine back on. What changed is who
|
||||
# calls them: not this hook, which now reports the game and lets the Gaming
|
||||
# focus mode decide. Asserted in the other direction a few lines up.
|
||||
|
||||
# ── The hook does not depend on the shell being up ──────────────────────────
|
||||
# A game can start after a shell restart; a hook that asked the shell for its
|
||||
# settings would silently do nothing.
|
||||
grep -q 'settings.json' <<<"$hook_body" \
|
||||
|| fail 'the hook reads its settings from somewhere other than the settings file'
|
||||
|
||||
# ── Honest reporting ────────────────────────────────────────────────────────
|
||||
grep -q 'governorAlreadyThere' "$service" \
|
||||
|| fail 'the service cannot tell when Game Mode would change nothing'
|
||||
page_code="$(grep -vE '^\s*//' "$page")"
|
||||
grep -q 'already runs that governor' <<<"$page_code" \
|
||||
|| fail 'the page does not say when Game Mode has no effect on this machine'
|
||||
# Proton is listed, never chosen: Steam owns that per game.
|
||||
grep -qiE 'setProton|selectProton|chooseProton' <<<"$page_code" \
|
||||
&& fail 'the page claims to choose the Proton build, which Steam owns per game'
|
||||
|
||||
# ── Polling stops when nobody is looking ────────────────────────────────────
|
||||
grep -q 'running: root.watching' "$service" \
|
||||
|| fail 'the poll timer runs regardless of whether the page is open'
|
||||
grep -q 'Gaming.watching = false' "$page" \
|
||||
|| fail 'the page never stops the poll timer, so it would poll forever after being closed'
|
||||
|
||||
command -v jq >/dev/null 2>&1 || { printf 'gaming contract: SKIP (no jq)\n'; exit 0; }
|
||||
|
||||
state="$("$helper" snapshot 2>/dev/null)" || fail 'snapshot failed'
|
||||
jq -e '(.gpus | type == "array") and (.gameMode | type == "object") and (.library | type == "object")' \
|
||||
<<<"$state" >/dev/null || fail 'the snapshot is incomplete'
|
||||
jq -e '.gameMode | has("active") and has("daemonRunning") and has("hooksInstalled")' <<<"$state" >/dev/null \
|
||||
|| fail 'Game Mode state is incomplete'
|
||||
# An integrated GPU reporting no video memory must not be described as discrete.
|
||||
jq -e '[.gpus[] | select(.discrete) | .vramTotalBytes > 0] | all' <<<"$state" >/dev/null \
|
||||
|| fail 'a card with no video memory is reported as the discrete one'
|
||||
|
||||
[[ -n "$("$helper" set-overlay-preset nonsense 2>/dev/null | jq -r '.error // ""')" ]] \
|
||||
|| fail 'an unknown overlay preset was accepted'
|
||||
[[ -n "$("$helper" hook nonsense 2>/dev/null | jq -r '.error // ""')" ]] \
|
||||
|| fail 'an unknown hook phase was accepted'
|
||||
|
||||
printf 'gaming contract: PASS (%s GPU(s), %s games, hooks %s)\n' \
|
||||
"$(jq '.gpus | length' <<<"$state")" \
|
||||
"$(jq -r '.library.games' <<<"$state")" \
|
||||
"$(jq -r 'if .gameMode.hooksInstalled then "installed" else "not installed" end' <<<"$state")"
|
||||
Reference in New Issue
Block a user