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:
Gabriel Brown
2026-08-20 21:55:55 -04:00
parent 47f29f9fa9
commit e1faaf7a76
185 changed files with 533 additions and 377 deletions
+105
View File
@@ -0,0 +1,105 @@
#!/usr/bin/env bash
# Every compositor-backed setting must actually change the compositor.
#
# settings-hyprland-write-contract proves this for five settings. There are
# sixty-six, and the ones it does not reach are exactly where a dead setting
# hides: the write path no-ops, nothing fails, nothing logs, and the row simply
# does not do anything. That is what a user reports as "the settings app does
# not work", and it is not visible from any shape-checking test.
#
# Each setting is driven through SystemSettings.commitPreference -- the entry
# point a settings row uses -- flipped to a value it does not hold, read back
# from the live compositor, and put straight back before the next one is
# touched.
#
# This one talks to the running compositor on purpose. Preferences are written
# to an isolated config home so nothing lands in the real settings.json.
set -uo pipefail
repo_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
harness="$repo_dir/config/dot/quickshell/settings-system-harness.qml"
sweep="$repo_dir/tests/quickshell/settings_write_sweep.py"
fail() {
printf 'settings write sweep contract: %s\n' "$1" >&2
exit 1
}
[[ -r "$harness" ]] || fail 'the settings system harness is missing'
[[ -r "$sweep" ]] || fail 'the sweep is missing'
command -v qs >/dev/null 2>&1 || { printf 'settings write sweep contract: SKIP (no quickshell)\n'; exit 0; }
command -v hyprctl >/dev/null 2>&1 || { printf 'settings write sweep contract: SKIP (no compositor)\n'; exit 0; }
command -v jq >/dev/null 2>&1 || { printf 'settings write sweep contract: SKIP (no jq)\n'; exit 0; }
hyprctl -j getoption decoration:rounding >/dev/null 2>&1 \
|| { printf 'settings write sweep contract: SKIP (compositor not answering)\n'; exit 0; }
config_home="$(mktemp -d /tmp/panama-write-sweep.XXXXXX)"
qs_for_harness() {
XDG_CONFIG_HOME="$config_home" qs -p "$harness" "$@"
}
cleanup() {
qs_for_harness kill >/dev/null 2>&1 || true
rm -rf "$config_home"
}
trap cleanup EXIT
# The sibling contract drives the SAME harness file with the compositor seam
# stubbed. Quickshell identifies an instance by config path, so an instance left
# over from that run would answer here and every write would miss the compositor
# entirely -- passing for the worst possible reason.
if qs_for_harness ipc show 2>/dev/null | grep -q '^target settings-system-test$'; then
fail 'a harness instance is already running; a stale one would answer these writes instead of the compositor'
fi
XDG_CONFIG_HOME="$config_home" PANAMA_SETTINGS_TEST_ISOLATE_COMPOSITOR=0 \
qs -p "$harness" --daemonize >/dev/null 2>&1
for _ in $(seq 1 40); do
qs_for_harness ipc show 2>/dev/null | grep -q '^target settings-system-test$' && break
sleep 0.25
done
qs_for_harness ipc show 2>/dev/null | grep -q '^target settings-system-test$' \
|| fail 'the harness did not start'
result="$(cd "$repo_dir" && XDG_CONFIG_HOME="$config_home" \
timeout 600 python3 "$sweep" "$config_home" "$harness")" \
|| fail 'the sweep did not finish'
verified="$(jq -r '.verified | length' <<<"$result")"
skipped="$(jq -r '.skipped | length' <<<"$result")"
failed="$(jq -r '.failures | length' <<<"$result")"
total="$(jq -r '.total' <<<"$result")"
if [[ "$failed" != "0" ]]; then
printf 'settings write sweep contract: %s setting(s) did not reach the compositor:\n' "$failed" >&2
jq -r '.failures[] | " \(.[0]): \(.[1])"' <<<"$result" >&2
exit 1
fi
# Skipping is legitimate for a shape with no scalar to compare, but a sweep that
# skips most of what it was pointed at is not evidence of anything.
if (( verified * 2 < total )); then
jq -r '.skipped[] | " \(.[0]): \(.[1])"' <<<"$result" >&2
fail "only $verified of $total settings were actually exercised"
fi
# ── Settings Panama stores itself ───────────────────────────────────────────
# No compositor to ask, so the question is whether the value comes back out of
# the store. A setting that silently fails to persist is the same dead row.
local_failed="$(jq -r '.localFailures | length' <<<"$result")"
local_verified="$(jq -r '.localVerified | length' <<<"$result")"
local_skipped="$(jq -r '.localSkipped | length' <<<"$result")"
if [[ "$local_failed" != "0" ]]; then
printf 'settings write sweep contract: %s stored setting(s) did not round-trip:\n' "$local_failed" >&2
jq -r '.localFailures[] | " \(.[0]): \(.[1])"' <<<"$result" >&2
exit 1
fi
printf 'settings write sweep contract: PASS (%d of %d compositor settings verified live, %d skipped; %d stored settings round-tripped, %d skipped)\n' \
"$verified" "$total" "$skipped" "$local_verified" "$local_skipped"