Files
Panama/tests/quickshell/panama-doctor-contract
T
Gabriel Brown e1faaf7a76 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
2026-08-20 21:55:55 -04:00

769 lines
33 KiB
Bash
Executable File

#!/usr/bin/env bash
# The doctor is deliberately exercised through its command boundary. The
# fixture commands include sensitive-looking output so this test proves the
# report only retains explicitly parsed, non-sensitive observations.
set -euo pipefail
repo_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
doctor="$repo_dir/config/dot/quickshell/scripts/panama-doctor"
theme_helper="$repo_dir/config/dot/quickshell/scripts/panama-theme-apps"
fixture_root="$repo_dir/tests/quickshell/fixtures/doctor"
fail() {
printf 'panama doctor contract: %s\n' "$1" >&2
exit 1
}
fixture="$(mktemp -d /tmp/panama-doctor.XXXXXX)"
child_pids=()
cleanup() {
for pid in "${child_pids[@]}"; do
kill "$pid" >/dev/null 2>&1 || true
wait "$pid" >/dev/null 2>&1 || true
done
rm -rf "$fixture"
}
trap cleanup EXIT
home="$fixture/home"
config_home="$home/.config"
state_home="$home/.local/state"
runtime_dir="$fixture/runtime"
bin_dir="$fixture/bin"
data_home="$home/.local/share"
mkdir -p "$config_home" "$state_home" "$runtime_dir" "$bin_dir" "$data_home/vicinae/scripts"
cp "$fixture_root/bin/"* "$bin_dir/"
chmod +x "$bin_dir"/*
# These are intentionally tiny stand-ins for authored executable probes. The
# named fixture scripts above cover probes whose output needs branch coverage.
for tool in hyprctl wl-paste grim tesseract kitty nextcloud rustdesk kdeconnect-cli; do
cat >"$bin_dir/$tool" <<'EOF'
#!/usr/bin/bash
case "${0##*/}" in
hyprctl) printf 'Hyprland 0.50.0\n' ;;
esac
EOF
chmod +x "$bin_dir/$tool"
done
cat >"$bin_dir/flatpak" <<'EOF'
#!/usr/bin/bash
if [[ "${1:-}" == "info" && "${2:-}" == "app.bluebubbles.BlueBubbles" \
&& "${PANAMA_DOCTOR_FIXTURE_BLUEBUBBLES:-installed}" == "installed" ]]; then
printf 'BlueBubbles fixture-secret-token\n'
exit 0
fi
exit 1
EOF
chmod +x "$bin_dir/flatpak"
cat >"$bin_dir/calendar-agenda" <<'EOF'
#!/usr/bin/bash
if [[ "${1:-}" != "probe" ]]; then
exit 2
fi
case "${PANAMA_DOCTOR_FIXTURE_CALENDAR:-ready}" in
ready) printf '{"eds":true,"sourceRegistry":true,"enabledSources":2,"event":"fixture clipboard body"}\n' ;;
malformed) printf 'calendar AA:BB:CC:DD:EE:FF\n' ;;
timeout) /usr/bin/sleep 2; printf '{"enabledSources":2}\n' ;;
*) printf '{"eds":true,"sourceRegistry":true,"enabledSources":0}\n' ;;
esac
EOF
chmod +x "$bin_dir/calendar-agenda"
cat >"$bin_dir/panama-brightness" <<'EOF'
#!/usr/bin/bash
case "${PANAMA_DOCTOR_FIXTURE_BRIGHTNESS:-ready}" in
ready) printf '{"displays":[{"connector":"AA:BB:CC:DD:EE:FF"}],"error":""}\n' ;;
denied) printf '{"displays":[],"error":"fixture-secret-token"}\n' ;;
malformed) printf 'fixture clipboard body\n' ;;
esac
EOF
chmod +x "$bin_dir/panama-brightness"
mkdir -p "$config_home/autostart"
touch "$config_home/autostart/Nextcloud.desktop"
for name in hypr quickshell uwsm vicinae; do
ln -s "$repo_dir/config/dot/$name" "$config_home/$name"
done
ln -s "$repo_dir/config/local/share/vicinae/scripts" "$data_home/vicinae/scripts/panama"
mkdir -p "$state_home/panama"
printf '%s\n' 'background { path = /fixture-secret-wallpaper.jpg }' \
>"$state_home/panama/hyprlock.conf"
printf '%s\n' '{"generated":true,"path":"/fixture-secret-wallpaper.jpg","fallback":false,"error":""}' \
>"$state_home/panama/hyprlock-status.json"
run_doctor() {
HOME="$home" \
PATH="$bin_dir" \
XDG_CURRENT_DESKTOP=Hyprland \
PANAMA_HOME_ASSISTANT_URL='https://fixture.invalid' \
PANAMA_HOME_ASSISTANT_TOKEN='fixture-secret-token' \
PANAMA_DOCTOR_ROOT="$repo_dir" \
PANAMA_DOCTOR_HOME="$home" \
PANAMA_DOCTOR_CONFIG_HOME="$config_home" \
PANAMA_DOCTOR_STATE_HOME="$state_home" \
PANAMA_DOCTOR_RUNTIME_DIR="$runtime_dir" \
PANAMA_DOCTOR_PATH="$bin_dir" \
PANAMA_DOCTOR_SCRIPTS_DIR="$bin_dir" \
PANAMA_DOCTOR_TIMEOUT="${PANAMA_DOCTOR_TIMEOUT:-0.2}" \
/usr/bin/python3 "$doctor" "$@"
}
expected_order=$'desktop.hyprland\ndesktop.quickshell\ndesktop.notifications\ndesktop.portals\ndesktop.hyprpaper\ndesktop.hypridle\ndesktop.hyprlock\ndesktop.vicinae\ninput.pipewire\ninput.clipboard\ninput.wallpaper\ninput.capture\ninput.ocr\ninput.brightness\nintegration.nextcloud\nintegration.rustdesk\nintegration.kdeconnect\nintegration.bluebubbles\nintegration.home-assistant\nintegration.calendar\npanama.updates\npanama.runtime-links\npanama.vicinae-commands\npanama.selected-terminal\npanama.selected-launcher\npanama.processes\npanama.caffeine'
assert_schema_and_redaction() {
local snapshot="$1"
jq -e '.schemaVersion == 1
and (.generatedAt | type == "string")
and (.summary.status | IN("healthy", "warning", "error"))
and (.context.session | IN("hyprland", "other"))
and (.context.versions | type == "array")
and ([.checks[].id] | length == 27)
and ([.checks[].id] | unique | length == 27)
and ([.checks[].status] | all(IN("ok", "warning", "error", "unconfigured")))' \
>/dev/null <<<"$snapshot" || fail "invalid schema: $snapshot"
[[ "$(jq -r '.checks[].id' <<<"$snapshot")" == "$expected_order" ]] \
|| fail "checks are not in the authored order"
! grep -Fq 'fixture-secret-token' <<<"$snapshot" \
|| fail 'report exposed a fixture secret'
! grep -Fq 'fixture clipboard body' <<<"$snapshot" \
|| fail 'report exposed clipboard or calendar content'
! grep -Fq 'AA:BB:CC:DD:EE:FF' <<<"$snapshot" \
|| fail 'report exposed a device address'
}
check_status() {
local snapshot="$1" id="$2" expected="$3"
[[ "$(jq -r --arg id "$id" '.checks[] | select(.id == $id) | .status' <<<"$snapshot")" == "$expected" ]] \
|| fail "$id did not report $expected: $snapshot"
}
snapshot="$(run_doctor --json)"
assert_schema_and_redaction "$snapshot"
check_status "$snapshot" panama.vicinae-commands ok
check_status "$snapshot" desktop.hyprlock ok
printf '%s\n' '{"generated":false,"path":"/fixture-secret-wallpaper.jpg","fallback":true,"error":"fixture-secret-token"}' \
>"$state_home/panama/hyprlock-status.json"
# A linked source checkout contains the template; installation renders the
# ignored machine-local fallback beside it. Model that installed state without
# writing an untracked file into the worktree under test.
rm "$config_home/hypr"
mkdir "$config_home/hypr"
cp "$repo_dir/config/dot/hypr/hyprlock.conf.template" \
"$config_home/hypr/hyprlock.conf.template"
HOME="$home" XDG_CONFIG_HOME="$config_home" "$theme_helper" dark >/dev/null
fallback_lock="$(run_doctor --json)"
check_status "$fallback_lock" desktop.hyprlock warning
assert_schema_and_redaction "$fallback_lock"
rm -rf "$config_home/hypr"
missing_lock="$(run_doctor --json)"
check_status "$missing_lock" desktop.hyprlock error
assert_schema_and_redaction "$missing_lock"
ln -s "$repo_dir/config/dot/hypr" "$config_home/hypr"
printf '%s\n' '{"generated":true,"path":"/fixture-secret-wallpaper.jpg","fallback":false,"error":""}' \
>"$state_home/panama/hyprlock-status.json"
# The diagnostic follows the actual installer contract: the scripts parent is
# a directory and only its Panama child is an authored link.
rm "$data_home/vicinae/scripts/panama"
unlinked_vicinae="$(run_doctor --json)"
check_status "$unlinked_vicinae" panama.vicinae-commands warning
PANAMA_PATH="$repo_dir" VICINAE_DATA_DIR="$data_home/vicinae" HOME="$home" \
PATH="$bin_dir:/usr/bin" "$repo_dir/setup/scripts/link-vicinae-scripts"
relinked_vicinae="$(run_doctor --json)"
check_status "$relinked_vicinae" panama.vicinae-commands ok
[[ -L "$data_home/vicinae/scripts/panama" \
&& "$(readlink "$data_home/vicinae/scripts/panama")" == "$repo_dir/config/local/share/vicinae/scripts" ]] \
|| fail 'authored Vicinae helper did not create the diagnosed child link'
# A healthy systemd-backed service stays healthy.
check_status "$snapshot" desktop.hyprpaper ok
# Arbitrary parent environment values are not propagated into probes.
sealed_environment="$(PANAMA_DOCTOR_FIXTURE_PROBE_SECRET=fixture-secret-token run_doctor --json)"
assert_schema_and_redaction "$sealed_environment"
check_status "$sealed_environment" desktop.hyprpaper ok
# An OS-level launch failure is contained as a check result, never a failed
# doctor invocation or a partial snapshot.
chmod 0644 "$bin_dir/systemctl"
if ! launch_failure="$(run_doctor --json)"; then
chmod +x "$bin_dir/systemctl"
fail 'launch failure prevented the doctor from emitting JSON'
fi
chmod +x "$bin_dir/systemctl"
assert_schema_and_redaction "$launch_failure"
check_status "$launch_failure" desktop.hyprpaper error
# A missing required executable is an error rather than a crash.
mv "$bin_dir/qs" "$bin_dir/qs.off"
missing_qs="$(run_doctor --json)"
check_status "$missing_qs" desktop.quickshell error
mv "$bin_dir/qs.off" "$bin_dir/qs"
# Optional integrations stay neutral until the user configures them.
rm "$config_home/autostart/Nextcloud.desktop"
unconfigured_nextcloud="$(run_doctor --json)"
check_status "$unconfigured_nextcloud" integration.nextcloud unconfigured
touch "$config_home/autostart/Nextcloud.desktop"
# A configured integration whose process isn't running is actionable with an
# authored label, never an application name or command derived from probe
# output. Nextcloud has no systemd unit behind it, so this is a process
# check (PANAMA_DOCTOR_FIXTURE_PROCESSES), not a service check.
stopped_nextcloud="$(PANAMA_DOCTOR_FIXTURE_PROCESSES=nextcloud:missing run_doctor --json)"
check_status "$stopped_nextcloud" integration.nextcloud warning
jq -e '.checks[] | select(.id == "integration.nextcloud")
| .action == {kind:"open", label:"Open Nextcloud", confirm:false}' \
>/dev/null <<<"$stopped_nextcloud" || fail 'Nextcloud action was not authored'
# DDC errors are classified without retaining connectors or bus addresses.
denied_brightness="$(PANAMA_DOCTOR_FIXTURE_BRIGHTNESS=denied run_doctor --json)"
check_status "$denied_brightness" input.brightness warning
jq -e '.checks[] | select(.id == "input.brightness")
| .action == {kind:"instructions", label:"View setup instructions", confirm:false, target:"ddc-permissions"}' \
>/dev/null <<<"$denied_brightness" || fail 'DDC instructions were not authored'
# A bounded probe timeout becomes a result, never a helper failure.
timed_calendar="$(PANAMA_DOCTOR_FIXTURE_CALENDAR=timeout PANAMA_DOCTOR_TIMEOUT=0.05 run_doctor --json)"
check_status "$timed_calendar" integration.calendar warning
jq -e '.checks[] | select(.id == "integration.calendar")
| .action == {kind:"open", label:"Open Date & Time", confirm:false, target:"datetime"}' \
>/dev/null <<<"$timed_calendar" || fail 'calendar action was not authored'
# Exact Panama/Caffeine inhibitor rows detect duplicates without exposing PIDs.
duplicated_caffeine="$(PANAMA_DOCTOR_FIXTURE_CAFFEINE=duplicate run_doctor --json)"
check_status "$duplicated_caffeine" panama.caffeine warning
jq -e '.checks[] | select(.id == "panama.caffeine")
| .action == {kind:"repair", label:"Release duplicate inhibitors", confirm:false}' \
>/dev/null <<<"$duplicated_caffeine" || fail 'Caffeine repair action was not authored'
! jq -r '.checks[] | select(.id == "panama.caffeine") | .detail' <<<"$duplicated_caffeine" | grep -Eq '[0-9]{3,}' \
|| fail 'Caffeine detail exposed inhibitor PIDs'
# Process counts use only exact authored names and never expose command lines or PIDs.
duplicated_processes="$(PANAMA_DOCTOR_FIXTURE_PROCESSES=qs:duplicate run_doctor --json)"
check_status "$duplicated_processes" panama.processes warning
! jq -r '.checks[] | select(.id == "panama.processes") | .detail' <<<"$duplicated_processes" | grep -Eq '[0-9]{3,}' \
|| fail 'process detail exposed a PID'
missing_quickshell_process="$(PANAMA_DOCTOR_FIXTURE_PROCESSES=qs:missing run_doctor --json)"
check_status "$missing_quickshell_process" panama.processes error
# Invalid output for a non-Quickshell authored process is not a normal zero
# count that can be hidden by the running Quickshell process.
malformed_processes="$(PANAMA_DOCTOR_FIXTURE_PROCESSES=hyprpaper:malformed run_doctor --json)"
check_status "$malformed_processes" panama.processes warning
# Panama/Caffeine-shaped rows that do not satisfy the fixed inhibitor schema
# are unavailable rather than reported as a healthy no-inhibitor state.
malformed_caffeine="$(PANAMA_DOCTOR_FIXTURE_CAFFEINE=malformed run_doctor --json)"
check_status "$malformed_caffeine" panama.caffeine warning
# A decoding error raised inside a concurrent probe is converted to a complete
# snapshot rather than escaping from Future.result().
if ! invalid_probe="$(PANAMA_DOCTOR_FIXTURE_BUS=invalid-utf8 run_doctor --json)"; then
fail 'unexpected probe exception prevented the doctor from emitting JSON'
fi
assert_schema_and_redaction "$invalid_probe"
check_status "$invalid_probe" desktop.portals warning
# Configured Home Assistant failures route to the exact authored Settings page.
rm "$config_home/quickshell"
mkdir -p "$config_home/quickshell/scripts"
home_assistant_failure="$(run_doctor --json)"
check_status "$home_assistant_failure" integration.home-assistant warning
jq -e '.checks[] | select(.id == "integration.home-assistant")
| .action == {kind:"open", label:"Open Home settings", confirm:false, target:"home-phone"}' \
>/dev/null <<<"$home_assistant_failure" || fail 'Home Assistant action was not routed to home-phone'
# Invalid probe text is contained in its own check and never copied to JSON.
malformed_calendar="$(PANAMA_DOCTOR_FIXTURE_CALENDAR=malformed run_doctor --json)"
check_status "$malformed_calendar" integration.calendar warning
assert_schema_and_redaction "$malformed_calendar"
summary="$(run_doctor --summary)"
[[ "$summary" =~ ^Panama\ system\ health:\ (healthy|warning|error)\ \([0-9]+\ ok,\ [0-9]+\ warnings,\ [0-9]+\ errors,\ [0-9]+\ unconfigured\)$ ]] \
|| fail "summary is not concise: $summary"
# Repairs run against a second, disposable Panama root. Every process boundary
# records its argv, and every filesystem assertion is confined to this fixture.
repair_root="$home/.local/share/Panama"
repair_log="$runtime_dir/repair.log"
mkdir -p "$repair_root/config/dot" "$repair_root/config/local/share/vicinae/scripts" \
"$repair_root/setup/scripts"
for name in hypr quickshell uwsm vicinae; do
mkdir -p "$repair_root/config/dot/$name"
done
cp "$repo_dir/setup/scripts/link-vicinae-scripts" "$repair_root/setup/scripts/link-vicinae-scripts"
mv "$bin_dir/systemctl" "$bin_dir/systemctl-probe"
cat >"$bin_dir/systemctl" <<'EOF'
#!/usr/bin/bash
set -euo pipefail
if [[ "${1:-}" == "--user" && "${2:-}" == "restart" ]]; then
printf 'systemctl' >>"$XDG_RUNTIME_DIR/repair.log"
printf '|%s' "$@" >>"$XDG_RUNTIME_DIR/repair.log"
printf '\n' >>"$XDG_RUNTIME_DIR/repair.log"
[[ ! -e "$XDG_RUNTIME_DIR/fail-repair" ]] || exit 5
exit 0
fi
exec "${0%/*}/systemctl-probe" "$@"
EOF
cat >"$bin_dir/panama-action" <<'EOF'
#!/usr/bin/bash
set -euo pipefail
printf 'panama-action' >>"$XDG_RUNTIME_DIR/repair.log"
printf '|%s' "$@" >>"$XDG_RUNTIME_DIR/repair.log"
printf '\n' >>"$XDG_RUNTIME_DIR/repair.log"
EOF
cat >"$bin_dir/systemd-inhibit" <<'EOF'
#!/usr/bin/bash
set -euo pipefail
printf 'systemd-inhibit' >>"$XDG_RUNTIME_DIR/repair.log"
printf '|%s' "$@" >>"$XDG_RUNTIME_DIR/repair.log"
printf '\n' >>"$XDG_RUNTIME_DIR/repair.log"
count_file="$XDG_RUNTIME_DIR/caffeine-list-count"
count=0
[[ ! -f "$count_file" ]] || read -r count <"$count_file"
count=$((count + 1))
printf '%s\n' "$count" >"$count_file"
read -r preserved duplicate <"$XDG_RUNTIME_DIR/caffeine-pids"
uid="$(/usr/bin/id -u)"
mode="$(<"$XDG_RUNTIME_DIR/caffeine-mode")"
if [[ "$mode" == disappear && "$count" -ge 2 ]]; then
/usr/bin/touch "$XDG_RUNTIME_DIR/release-disappearing-pid"
for _ in $(/usr/bin/seq 1 100); do
[[ ! -e "/proc/$duplicate" ]] && break
/usr/bin/sleep 0.01
done
fi
preserved_comm=systemd-inhibit
if [[ "$mode" == preserve-altered && "$count" -ge 2 ]]; then
preserved_comm=changed-command
fi
printf 'Panama %s fixture-user %s %s sleep:idle Caffeine block\n' "$uid" "$preserved" "$preserved_comm"
if [[ "$mode" != multiplicity || "$count" -lt 2 ]]; then
printf 'Panama %s fixture-user %s systemd-inhibit sleep:idle Caffeine block\n' "$uid" "$preserved"
fi
if [[ "$mode" == altered && "$count" -ge 2 ]]; then
printf 'Panama %s fixture-user %s systemd-inhibit sleep:idle Other block\n' "$uid" "$duplicate"
else
printf 'Panama %s fixture-user %s systemd-inhibit sleep:idle Caffeine block\n' "$uid" "$duplicate"
fi
printf 'Other %s fixture-user 4999 systemd-inhibit sleep:idle Caffeine block\n' "$uid"
printf 'Panama 99999 fixture-user 4998 systemd-inhibit sleep:idle Caffeine block\n'
printf 'Panama %s fixture-user 4997 systemd-inhibit sleep:idle Other block\n' "$uid"
printf 'Panama %s fixture-user 4996 systemd-inhibit sleep:idle Caffeine delay\n' "$uid"
EOF
chmod +x "$bin_dir/systemctl" "$bin_dir/panama-action" \
"$bin_dir/systemd-inhibit" "$repair_root/setup/scripts/link-vicinae-scripts"
run_repair() {
HOME="$home" \
PATH="$bin_dir:/usr/bin" \
XDG_CURRENT_DESKTOP=Hyprland \
PANAMA_DOCTOR_ROOT="$repair_root" \
PANAMA_DOCTOR_HOME="$home" \
PANAMA_DOCTOR_CONFIG_HOME="$config_home" \
PANAMA_DOCTOR_STATE_HOME="$state_home" \
PANAMA_DOCTOR_RUNTIME_DIR="$runtime_dir" \
PANAMA_DOCTOR_PATH="$bin_dir:/usr/bin" \
PANAMA_DOCTOR_SCRIPTS_DIR="$bin_dir" \
PANAMA_DOCTOR_TIMEOUT=0.2 \
/usr/bin/python3 "$doctor" "$@"
}
repair_output=""
repair_status=0
invoke_repair() {
set +e
repair_output="$(run_repair --repair "$1" --json)"
repair_status=$?
set -e
}
assert_repair_result() {
local id="$1" accepted="$2" exit_code="$3"
jq -e --arg id "$id" --argjson accepted "$accepted" --argjson exitCode "$exit_code" '
(keys | sort) == ["accepted", "checkId", "exitCode", "message", "schemaVersion"]
and .schemaVersion == 1
and .checkId == $id
and .accepted == $accepted
and .exitCode == $exitCode
and (.message | type == "string" and length > 0)
' >/dev/null <<<"$repair_output" || fail "invalid repair result for $id: $repair_output"
}
for repair_case in \
'desktop.hyprpaper|systemctl|--user|restart|hyprpaper.service' \
'desktop.hypridle|systemctl|--user|restart|hypridle.service' \
'desktop.vicinae|systemctl|--user|restart|vicinae.service' \
'desktop.quickshell|panama-action|restart-shell'; do
IFS='|' read -r repair_id executable arg1 arg2 arg3 <<<"$repair_case"
: >"$repair_log"
invoke_repair "$repair_id"
[[ "$repair_status" == 0 ]] || fail "$repair_id returned $repair_status"
assert_repair_result "$repair_id" true 0
expected="$executable|$arg1"
[[ -z "$arg2" ]] || expected+="|$arg2"
[[ -z "$arg3" ]] || expected+="|$arg3"
[[ "$(<"$repair_log")" == "$expected" ]] \
|| fail "$repair_id argv was not exact: $(<"$repair_log")"
done
# Known process failures still return complete JSON and preserve the command's
# exit status for the QML state machine.
touch "$runtime_dir/fail-repair"
: >"$repair_log"
invoke_repair desktop.vicinae
rm "$runtime_dir/fail-repair"
[[ "$repair_status" == 5 ]] || fail "failed repair returned $repair_status instead of 5"
assert_repair_result desktop.vicinae true 5
[[ "$(<"$repair_log")" == 'systemctl|--user|restart|vicinae.service' ]] \
|| fail 'failed repair changed the authored argv'
# The real authored Vicinae helper converges the exact child link diagnosed by
# panama-doctor under the isolated HOME.
rm -f "$data_home/vicinae/scripts/panama"
before_vicinae_repair="$(run_repair --json)"
check_status "$before_vicinae_repair" panama.vicinae-commands warning
invoke_repair panama.vicinae-commands
[[ "$repair_status" == 0 ]] || fail "Vicinae command repair returned $repair_status"
assert_repair_result panama.vicinae-commands true 0
after_vicinae_repair="$(run_repair --json)"
check_status "$after_vicinae_repair" panama.vicinae-commands ok
[[ -L "$data_home/vicinae/scripts/panama" \
&& "$(readlink "$data_home/vicinae/scripts/panama")" == "$repair_root/config/local/share/vicinae/scripts" ]] \
|| fail 'Vicinae repair did not install the diagnosed child link'
# Runtime-link repair may replace only absent links or symlinks whose lexical
# target proves Panama ownership. Every other object remains untouched.
for name in hypr quickshell uwsm vicinae; do
path="$config_home/$name"
if [[ -e "$path" || -L "$path" ]]; then
mv "$path" "$fixture/pre-repair-$name"
fi
done
ln -s "$repair_root/config/dot/hypr" "$config_home/hypr"
correct_inode="$(stat -c %i "$config_home/hypr")"
ln -s "$repair_root/config/dot/quickshell" "$config_home/uwsm"
ln -s "$fixture/external-broken-link" "$config_home/vicinae"
ln -s "$fixture/untouched" "$config_home/not-panama"
: >"$repair_log"
invoke_repair panama.runtime-links
[[ "$repair_status" == 1 ]] || fail "blocked runtime-link repair returned $repair_status"
assert_repair_result panama.runtime-links true 1
[[ -L "$config_home/hypr" && "$(readlink "$config_home/hypr")" == "$repair_root/config/dot/hypr" ]] \
|| fail 'correct runtime link changed'
[[ "$(stat -c %i "$config_home/hypr")" == "$correct_inode" ]] \
|| fail 'correct runtime link was replaced instead of left untouched'
[[ -L "$config_home/quickshell" && "$(readlink "$config_home/quickshell")" == "$repair_root/config/dot/quickshell" ]] \
|| fail 'absent quickshell link was not created'
[[ -L "$config_home/uwsm" && "$(readlink "$config_home/uwsm")" == "$repair_root/config/dot/uwsm" ]] \
|| fail 'provably Panama-owned stale link was not repaired'
[[ -L "$config_home/vicinae" && "$(readlink "$config_home/vicinae")" == "$fixture/external-broken-link" ]] \
|| fail 'external broken symlink was replaced'
[[ -L "$config_home/not-panama" && "$(readlink "$config_home/not-panama")" == "$fixture/untouched" ]] \
|| fail 'runtime-link repair touched an unauthored link name'
[[ ! -s "$repair_log" ]] || fail 'runtime-link repair launched a process'
# Regular files and directories also remain untouched.
rm "$config_home/vicinae"
rm "$config_home/uwsm"
printf 'user-owned file\n' >"$config_home/uwsm"
mkdir "$config_home/vicinae"
invoke_repair panama.runtime-links
[[ "$repair_status" == 1 ]] || fail 'file/directory blockers did not make repair incomplete'
[[ -f "$config_home/uwsm" && "$(<"$config_home/uwsm")" == 'user-owned file' ]] \
|| fail 'runtime-link repair replaced a regular file'
[[ -d "$config_home/vicinae" && ! -L "$config_home/vicinae" ]] \
|| fail 'runtime-link repair replaced a user-owned directory'
# An injected exchange failure occurs after the authored candidate symlink is
# made; the original link must still be intact.
/usr/bin/python3 - "$doctor" "$repair_root" "$fixture/atomic-config" <<'PY' \
|| fail 'atomic replacement failure did not preserve the original link'
import importlib.util
import importlib.machinery
import os
import sys
from pathlib import Path
doctor_path, root_text, config_text = sys.argv[1:]
loader = importlib.machinery.SourceFileLoader("panama_doctor_contract", doctor_path)
spec = importlib.util.spec_from_loader(loader.name, loader)
module = importlib.util.module_from_spec(spec)
sys.modules[spec.name] = module
loader.exec_module(module)
root = Path(root_text)
config_home = Path(config_text)
config_home.mkdir(parents=True)
destination = config_home / "hypr"
original = root / "config/dot/quickshell"
destination.symlink_to(original, target_is_directory=True)
config = module.DoctorConfig(root, config_home.parent, config_home, config_home.parent / "state", config_home.parent / "runtime", "", 0.2)
real_exchange = module.rename_exchange
module.rename_exchange = lambda source, target: (_ for _ in ()).throw(OSError("fixture exchange failure"))
try:
result = module.repair_runtime_links(config)
finally:
module.rename_exchange = real_exchange
assert result.exit_code == 1
assert destination.is_symlink()
assert os.readlink(destination) == str(original)
assert not list(config_home.glob(".panama-link-*"))
PY
# A deterministic swap at the ownership/replacement boundary must be detected
# from the exchanged-out object and rolled back, preserving the external link.
/usr/bin/python3 - "$doctor" "$repair_root" "$fixture/toctou-config" "$fixture/external-race-target" <<'PY' \
|| fail 'runtime-link exchange did not restore a boundary-swapped external link'
import importlib.machinery
import importlib.util
import os
import sys
from pathlib import Path
doctor_path, root_text, config_text, external_text = sys.argv[1:]
loader = importlib.machinery.SourceFileLoader("panama_doctor_toctou", doctor_path)
spec = importlib.util.spec_from_loader(loader.name, loader)
module = importlib.util.module_from_spec(spec)
sys.modules[spec.name] = module
loader.exec_module(module)
root = Path(root_text)
config_home = Path(config_text)
config_home.mkdir(parents=True)
for name, relative in module.RUNTIME_LINK_TARGETS:
(config_home / name).symlink_to(root / relative, target_is_directory=True)
destination = config_home / "uwsm"
destination.unlink()
destination.symlink_to(root / "config/dot/quickshell", target_is_directory=True)
external = Path(external_text)
real_exchange = module.rename_exchange
first = True
def race_exchange(candidate, target):
global first
if first:
first = False
target.unlink()
target.symlink_to(external, target_is_directory=True)
real_exchange(candidate, target)
module.rename_exchange = race_exchange
config = module.DoctorConfig(root, config_home.parent, config_home, config_home.parent / "state", config_home.parent / "runtime", "", 0.2)
try:
result = module.repair_runtime_links(config)
finally:
module.rename_exchange = real_exchange
assert result.exit_code == 1
assert destination.is_symlink()
assert os.readlink(destination) == str(external)
assert not list(config_home.glob(".panama-link-*"))
PY
# Caffeine repair deduplicates rows, pins each distinct duplicate with a
# pidfd, revalidates authored metadata, and signals only the duplicate.
/usr/bin/sleep 30 &
preserved_pid=$!
child_pids+=("$preserved_pid")
/usr/bin/sleep 30 &
duplicate_pid=$!
child_pids+=("$duplicate_pid")
printf '%s %s\n' "$preserved_pid" "$duplicate_pid" >"$runtime_dir/caffeine-pids"
printf 'dedupe\n' >"$runtime_dir/caffeine-mode"
rm -f "$runtime_dir/caffeine-list-count"
: >"$repair_log"
invoke_repair panama.caffeine
[[ "$repair_status" == 0 ]] || fail "Caffeine repair returned $repair_status"
assert_repair_result panama.caffeine true 0
expected_caffeine=$'systemd-inhibit|--list|--no-pager|--no-legend\nsystemd-inhibit|--list|--no-pager|--no-legend'
[[ "$(<"$repair_log")" == "$expected_caffeine" ]] \
|| fail "Caffeine repair did not preserve/filter exact inhibitors: $(<"$repair_log")"
kill -0 "$preserved_pid" >/dev/null 2>&1 || fail 'repeated inhibitor rows killed the preserved process'
for _ in $(seq 1 40); do
kill -0 "$duplicate_pid" >/dev/null 2>&1 || break
sleep 0.05
done
! kill -0 "$duplicate_pid" >/dev/null 2>&1 || fail 'distinct duplicate inhibitor was not terminated'
# Changed second-list metadata invalidates the candidate before any signal.
/usr/bin/sleep 30 &
altered_preserved=$!
child_pids+=("$altered_preserved")
/usr/bin/sleep 30 &
altered_duplicate=$!
child_pids+=("$altered_duplicate")
printf '%s %s\n' "$altered_preserved" "$altered_duplicate" >"$runtime_dir/caffeine-pids"
printf 'altered\n' >"$runtime_dir/caffeine-mode"
rm -f "$runtime_dir/caffeine-list-count"
invoke_repair panama.caffeine
[[ "$repair_status" == 1 ]] || fail 'altered inhibitor metadata was not safely refused'
assert_repair_result panama.caffeine true 1
kill -0 "$altered_preserved" >/dev/null 2>&1 || fail 'metadata refusal signaled the preserved process'
kill -0 "$altered_duplicate" >/dev/null 2>&1 || fail 'metadata refusal signaled the candidate process'
# Changing metadata on the preserved row is also a full-identity mismatch,
# even though every duplicate PID remains present.
/usr/bin/sleep 30 &
preserve_changed_keep=$!
child_pids+=("$preserve_changed_keep")
/usr/bin/sleep 30 &
preserve_changed_duplicate=$!
child_pids+=("$preserve_changed_duplicate")
printf '%s %s\n' "$preserve_changed_keep" "$preserve_changed_duplicate" >"$runtime_dir/caffeine-pids"
printf 'preserve-altered\n' >"$runtime_dir/caffeine-mode"
rm -f "$runtime_dir/caffeine-list-count"
invoke_repair panama.caffeine
[[ "$repair_status" == 1 ]] || fail 'preserved-row metadata change was not safely refused'
assert_repair_result panama.caffeine true 1
kill -0 "$preserve_changed_keep" >/dev/null 2>&1 || fail 'preserved-row mismatch signaled the preserved process'
kill -0 "$preserve_changed_duplicate" >/dev/null 2>&1 || fail 'preserved-row mismatch signaled the duplicate process'
# A repeated exact row disappearing between lists changes multiplicity and is
# refused before signaling any pinned duplicate.
/usr/bin/sleep 30 &
multiplicity_keep=$!
child_pids+=("$multiplicity_keep")
/usr/bin/sleep 30 &
multiplicity_duplicate=$!
child_pids+=("$multiplicity_duplicate")
printf '%s %s\n' "$multiplicity_keep" "$multiplicity_duplicate" >"$runtime_dir/caffeine-pids"
printf 'multiplicity\n' >"$runtime_dir/caffeine-mode"
rm -f "$runtime_dir/caffeine-list-count"
invoke_repair panama.caffeine
[[ "$repair_status" == 1 ]] || fail 'inhibitor row multiplicity change was not safely refused'
assert_repair_result panama.caffeine true 1
kill -0 "$multiplicity_keep" >/dev/null 2>&1 || fail 'multiplicity mismatch signaled the preserved process'
kill -0 "$multiplicity_duplicate" >/dev/null 2>&1 || fail 'multiplicity mismatch signaled the duplicate process'
# The production pidfd release function preflights every candidate before any
# SIGTERM. A refused second preflight leaves both disposable children alive.
/usr/bin/sleep 30 &
preflight_first=$!
child_pids+=("$preflight_first")
/usr/bin/sleep 30 &
preflight_second=$!
child_pids+=("$preflight_second")
/usr/bin/python3 - "$doctor" "$preflight_first" "$preflight_second" <<'PY' \
|| fail 'pidfd preflight failure signaled a disposable duplicate'
import errno
import importlib.machinery
import importlib.util
import os
import signal
import sys
doctor_path = sys.argv[1]
pids = [int(value) for value in sys.argv[2:]]
loader = importlib.machinery.SourceFileLoader("panama_doctor_preflight", doctor_path)
spec = importlib.util.spec_from_loader(loader.name, loader)
module = importlib.util.module_from_spec(spec)
sys.modules[spec.name] = module
loader.exec_module(module)
pidfds = [os.pidfd_open(pid, 0) for pid in pids]
calls = []
def sender(pidfd, sig, siginfo, flags):
calls.append(sig)
if sig == 0 and pidfd == pidfds[1]:
raise PermissionError(errno.EPERM, "fixture preflight refusal")
signal.pidfd_send_signal(pidfd, sig, siginfo, flags)
try:
outcome = module.signal_caffeine_pidfds(pidfds, sender)
finally:
for pidfd in pidfds:
os.close(pidfd)
assert outcome == "preflight-failed"
assert calls == [0, 0]
for pid in pids:
os.kill(pid, 0)
PY
kill -0 "$preflight_first" >/dev/null 2>&1 || fail 'preflight refusal killed the first duplicate'
kill -0 "$preflight_second" >/dev/null 2>&1 || fail 'preflight refusal killed the second duplicate'
# A candidate that disappears after pidfd acquisition and second-list request
# is a safe failure; an unrelated disposable process must remain untouched.
/usr/bin/sleep 30 &
unrelated_pid=$!
child_pids+=("$unrelated_pid")
(
/usr/bin/sleep 30 &
disappearing_pid=$!
trap 'kill "$disappearing_pid" >/dev/null 2>&1 || true; wait "$disappearing_pid" >/dev/null 2>&1 || true' EXIT
printf '%s\n' "$disappearing_pid" >"$runtime_dir/disappearing-pid"
while [[ ! -e "$runtime_dir/release-disappearing-pid" ]]; do
/usr/bin/sleep 0.01
done
kill "$disappearing_pid"
wait "$disappearing_pid" >/dev/null 2>&1 || true
trap - EXIT
) &
disappearance_controller=$!
child_pids+=("$disappearance_controller")
for _ in $(seq 1 100); do
[[ -s "$runtime_dir/disappearing-pid" ]] && break
sleep 0.01
done
[[ -s "$runtime_dir/disappearing-pid" ]] || fail 'disappearing PID fixture did not start'
disappearing_pid="$(<"$runtime_dir/disappearing-pid")"
printf '%s %s\n' "$altered_preserved" "$disappearing_pid" >"$runtime_dir/caffeine-pids"
printf 'disappear\n' >"$runtime_dir/caffeine-mode"
rm -f "$runtime_dir/caffeine-list-count"
invoke_repair panama.caffeine
[[ "$repair_status" == 1 ]] || fail 'disappeared inhibitor PID was not safely refused'
assert_repair_result panama.caffeine true 1
wait "$disappearance_controller"
kill -0 "$unrelated_pid" >/dev/null 2>&1 || fail 'PID disappearance signaled an unrelated process'
# Rejected IDs are complete JSON, exit 2, and cause neither a process launch
# nor a filesystem mutation.
fixture_state() {
/usr/bin/python3 - "$fixture" <<'PY'
import hashlib
import os
import stat
import sys
from pathlib import Path
root = Path(sys.argv[1])
digest = hashlib.sha256()
for path in sorted(root.rglob("*"), key=lambda item: os.fsencode(str(item.relative_to(root)))):
relative = os.fsencode(str(path.relative_to(root)))
metadata = path.lstat()
digest.update(relative + b"\0" + oct(stat.S_IMODE(metadata.st_mode)).encode() + b"\0")
if path.is_symlink():
digest.update(b"link\0" + os.fsencode(os.readlink(path)) + b"\0")
elif path.is_file():
digest.update(b"file\0" + hashlib.sha256(path.read_bytes()).digest())
elif path.is_dir():
digest.update(b"dir\0")
else:
digest.update(b"other\0")
print(digest.hexdigest())
PY
}
for rejected_id in unknown.check integration.home-assistant input.brightness \
desktop.notifications ../../escape 'desktop.vicinae;touch injected'; do
: >"$repair_log"
before_state="$(fixture_state)"
invoke_repair "$rejected_id"
[[ "$repair_status" == 2 ]] || fail "$rejected_id returned $repair_status instead of 2"
assert_repair_result "$rejected_id" false 2
[[ ! -s "$repair_log" ]] || fail "$rejected_id launched a process"
[[ "$(fixture_state)" == "$before_state" ]] || fail "$rejected_id mutated the filesystem"
done
printf 'panama doctor contract: PASS\n'