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
118 lines
5.5 KiB
Bash
Executable File
118 lines
5.5 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
|
|
# Rebinding a keyboard shortcut.
|
|
#
|
|
# This is the highest-consequence write in the settings app: a mistake here
|
|
# costs the user their keymap, and the keymap is how they reach everything else.
|
|
# The properties that matter:
|
|
#
|
|
# * an override moves exactly the bind it names and nothing else -- keying by
|
|
# description moved every bind sharing one, which silently cost the
|
|
# XF86Calculator hardware key when SUPER+C was rebound;
|
|
# * only the chord is ever stored, never the action;
|
|
# * a chord already in use is refused rather than shadowing the existing bind;
|
|
# * resetting returns the exact shipped keymap.
|
|
|
|
set -euo pipefail
|
|
|
|
repo_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
harness="$repo_dir/config/dot/quickshell/keybinds-harness.qml"
|
|
service="$repo_dir/config/dot/quickshell/services/Keybinds.qml"
|
|
|
|
fail() {
|
|
printf 'keybind rebind contract: %s\n' "$1" >&2
|
|
exit 1
|
|
}
|
|
|
|
rg -Fq 'function overrideOccupantFor(chord: string, exceptShipped: string): string' "$service" \
|
|
|| fail 'resetBind has no override collision guard'
|
|
rg -Fq 'root.overrideOccupantFor(shipped, shipped)' "$service" \
|
|
|| fail 'resetBind does not check whether another override occupies its shipped chord'
|
|
|
|
if [[ "${PANAMA_KEYBINDS_STATIC_ONLY:-0}" == "1" ]]; then
|
|
printf 'keybind rebind contract: PASS (static)\n'
|
|
exit 0
|
|
fi
|
|
|
|
config_home="$(mktemp -d /tmp/panama-rebind-config.XXXXXX)"
|
|
|
|
# The compositor is the live one -- that is the point -- but preferences are
|
|
# isolated so this cannot leave an override in the user's real settings.
|
|
# hyprctl reload re-reads the real settings file, so the compositor is only
|
|
# exercised through the shipped configuration here; the override logic itself is
|
|
# what is under test.
|
|
qs_for_harness() {
|
|
XDG_CONFIG_HOME="$config_home" qs -p "$harness" "$@"
|
|
}
|
|
|
|
cleanup() {
|
|
qs_for_harness ipc call keybinds-test resetAll >/dev/null 2>&1 || true
|
|
qs_for_harness kill >/dev/null 2>&1 || true
|
|
rm -rf "$config_home"
|
|
}
|
|
trap cleanup EXIT
|
|
|
|
XDG_CONFIG_HOME="$config_home" qs -p "$harness" --daemonize >/dev/null
|
|
for _ in $(seq 1 40); do
|
|
qs_for_harness ipc show 2>/dev/null | rg -q '^target keybinds-test$' && break
|
|
sleep 0.1
|
|
done
|
|
qs_for_harness ipc show 2>/dev/null | rg -q '^target keybinds-test$' || fail 'test IPC target did not start'
|
|
|
|
for _ in $(seq 1 40); do
|
|
[[ "$(qs_for_harness ipc call keybinds-test status | jq -r .loaded)" == "true" ]] && break
|
|
sleep 0.1
|
|
done
|
|
|
|
# ── Nothing is overridden to begin with ──────────────────────────────────────
|
|
[[ "$(qs_for_harness ipc call keybinds-test overrideState | jq -r .count)" == "0" ]] \
|
|
|| fail 'the isolated store started with overrides'
|
|
|
|
# ── A chord already in use is refused ────────────────────────────────────────
|
|
terminal="$(qs_for_harness ipc call keybinds-test chordFor Terminal)"
|
|
[[ -n "$terminal" ]] || fail 'could not find the Terminal bind'
|
|
files="$(qs_for_harness ipc call keybinds-test chordFor Files)"
|
|
[[ -n "$files" ]] || fail 'could not find the Files bind'
|
|
|
|
[[ "$(qs_for_harness ipc call keybinds-test rebind "$terminal" "$files")" == "false" ]] \
|
|
|| fail 'rebinding onto a chord already in use was accepted'
|
|
[[ "$(qs_for_harness ipc call keybinds-test overrideState | jq -r .count)" == "0" ]] \
|
|
|| fail 'a refused rebind still stored an override'
|
|
|
|
# ── A rebind stores only the chord, keyed by the shipped chord ───────────────
|
|
[[ "$(qs_for_harness ipc call keybinds-test rebind "$terminal" "SUPER + SHIFT + F9")" == "true" ]] \
|
|
|| fail 'a valid rebind was refused'
|
|
|
|
state="$(qs_for_harness ipc call keybinds-test overrideState)"
|
|
jq -e --arg k "$terminal" '.overrides[$k] == "SUPER + SHIFT + F9"' <<<"$state" >/dev/null \
|
|
|| fail "the override was not keyed by the shipped chord: $state"
|
|
jq -e '.count == 1' <<<"$state" >/dev/null || fail "exactly one override expected: $state"
|
|
|
|
# Only a chord is stored. Nothing resembling an action or command may appear,
|
|
# because that is what keeps a user-editable file from being executable.
|
|
jq -e '[.overrides[]] | all(type == "string" and (length < 64))' <<<"$state" >/dev/null \
|
|
|| fail 'an override value is not a plain chord'
|
|
|
|
# ── Reset clears it ──────────────────────────────────────────────────────────
|
|
qs_for_harness ipc call keybinds-test resetAll >/dev/null
|
|
sleep 0.5
|
|
[[ "$(qs_for_harness ipc call keybinds-test overrideState | jq -r .count)" == "0" ]] \
|
|
|| fail 'resetAll left overrides behind'
|
|
|
|
# Terminal moved away from its shipped chord, then Files moved into it.
|
|
# Resetting Terminal must refuse instead of producing two binds on one chord.
|
|
qs_for_harness ipc call keybinds-test seedResetCollision \
|
|
"$terminal" "SUPER + SHIFT + F9" "$files" >/dev/null
|
|
sleep 0.2
|
|
[[ "$(qs_for_harness ipc call keybinds-test resetBind "SUPER + SHIFT + F9")" == "false" ]] \
|
|
|| fail 'resetBind reclaimed a shipped chord occupied by another override'
|
|
collision_state="$(qs_for_harness ipc call keybinds-test overrideState)"
|
|
jq -e --arg terminal "$terminal" --arg files "$files" \
|
|
'.count == 2 and .overrides[$terminal] == "SUPER + SHIFT + F9" and .overrides[$files] == $terminal and (.lastError | length > 0)' \
|
|
<<<"$collision_state" >/dev/null \
|
|
|| fail "a refused reset changed overrides or gave no explanation: $collision_state"
|
|
|
|
trap - EXIT
|
|
cleanup
|
|
printf 'keybind rebind contract: PASS\n'
|