Tier 0: render what the services already decided, honestly
Claude-Session: https://claude.ai/code/session_01Ms2FbjQy31TVf3CEvQhGM8
This commit is contained in:
@@ -1,32 +1,85 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Bluetooth discovery holds the radio, so who turns it on and off is a contract
|
||||
# rather than a detail.
|
||||
#
|
||||
# The pins below started life against modules/quicksettings/BluetoothList.qml,
|
||||
# which computed the desired state and wrote BlueZ itself. That was correct
|
||||
# until a second surface -- the settings page, through Connectivity.qml -- began
|
||||
# doing the same thing from its own visibility flag, at which point the last
|
||||
# writer decided for both: closing the settings page stopped discovery under an
|
||||
# open quick-settings panel, which then sat on "Searching…" over a radio that
|
||||
# had stopped.
|
||||
#
|
||||
# The fix moved the BlueZ write into Connectivity.qml behind named holds, so the
|
||||
# same five guarantees are checked there, plus a sixth that keeps them there:
|
||||
#
|
||||
# 1. the desired state is calculated from state, not toggled;
|
||||
# 2. a redundant start is not issued;
|
||||
# 3. discovery this desktop did not start is never stopped;
|
||||
# 4. merely constructing an inactive picker does not write BlueZ state;
|
||||
# 5. an adapter appearing does not start discovery nobody asked for;
|
||||
# 6. a surface releases its hold when it is destroyed.
|
||||
#
|
||||
# Read-only. It changes no Bluetooth state.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
repo_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||
bluetooth="$repo_dir/config/dot/quickshell/modules/quicksettings/BluetoothList.qml"
|
||||
service="$repo_dir/config/dot/quickshell/services/Connectivity.qml"
|
||||
|
||||
rg -q 'const shouldDiscover = root\.active && root\.adapter\.enabled' "$bluetooth" || {
|
||||
for path in "$bluetooth" "$service"; do
|
||||
[[ -r "$path" ]] || {
|
||||
printf 'bluetooth discovery contract: missing %s\n' "$path" >&2
|
||||
exit 1
|
||||
}
|
||||
done
|
||||
|
||||
rg -q 'const shouldDiscover = root\.discoveryWanted' "$service" || {
|
||||
printf 'bluetooth discovery contract: desired state is not calculated idempotently\n' >&2
|
||||
exit 1
|
||||
}
|
||||
rg -q 'shouldDiscover && !root\.adapter\.discovering' "$bluetooth" || {
|
||||
rg -q 'shouldDiscover && !root\.adapter\.discovering' "$service" || {
|
||||
printf 'bluetooth discovery contract: redundant BlueZ starts are not guarded\n' >&2
|
||||
exit 1
|
||||
}
|
||||
rg -q '!shouldDiscover && root\.discoveryOwned && root\.adapter\.discovering' "$bluetooth" || {
|
||||
printf 'bluetooth discovery contract: the picker may stop discovery it does not own\n' >&2
|
||||
rg -q '!shouldDiscover && root\.discoveryOwned && root\.adapter\.discovering' "$service" || {
|
||||
printf 'bluetooth discovery contract: the service may stop discovery it does not own\n' >&2
|
||||
exit 1
|
||||
}
|
||||
! rg -q 'Component\.onCompleted: root\.syncDiscovery' "$bluetooth" || {
|
||||
printf 'bluetooth discovery contract: inactive construction still writes BlueZ state\n' >&2
|
||||
|
||||
# The holds themselves. Without both halves the refcount is decorative and the
|
||||
# two surfaces are back to overwriting each other.
|
||||
rg -q 'function acquireDiscovery' "$service" || {
|
||||
printf 'bluetooth discovery contract: nothing can take a hold on discovery\n' >&2
|
||||
exit 1
|
||||
}
|
||||
rg -Uq 'onAdapterChanged:[^{\n]*\{[^}]*if \(root\.active\)' "$bluetooth" || {
|
||||
printf 'bluetooth discovery contract: adapter changes are not gated by an open panel\n' >&2
|
||||
rg -q 'function releaseDiscovery' "$service" || {
|
||||
printf 'bluetooth discovery contract: a hold on discovery cannot be released\n' >&2
|
||||
exit 1
|
||||
}
|
||||
rg -Uq 'Component\.onDestruction:[^{\n]*\{[^}]*root\.discoveryOwned' "$bluetooth" || {
|
||||
printf 'bluetooth discovery contract: owned discovery is not released on destruction\n' >&2
|
||||
rg -q 'discoveryWanted: root\.discoveryHolders\.length > 0' "$service" || {
|
||||
printf 'bluetooth discovery contract: discovery does not follow the outstanding holds\n' >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
# 6. One writer. A picker that still wrote adapter.discovering would reopen the
|
||||
# exact fight the holds exist to settle, and it would do it silently.
|
||||
! rg -q 'discovering *=' "$bluetooth" || {
|
||||
printf 'bluetooth discovery contract: the picker writes BlueZ discovery directly, behind the holds\n' >&2
|
||||
exit 1
|
||||
}
|
||||
! rg -q 'Component\.onCompleted: root\.(syncDiscovery|acquireDiscovery)' "$bluetooth" || {
|
||||
printf 'bluetooth discovery contract: inactive construction still takes a discovery hold\n' >&2
|
||||
exit 1
|
||||
}
|
||||
rg -Uq 'onActiveChanged:[^{\n]*\{[^}]*Connectivity\.acquireDiscovery' "$bluetooth" || {
|
||||
printf 'bluetooth discovery contract: discovery is not gated by an open panel\n' >&2
|
||||
exit 1
|
||||
}
|
||||
rg -q 'Component\.onDestruction: Connectivity\.releaseDiscovery' "$bluetooth" || {
|
||||
printf 'bluetooth discovery contract: the hold is not released on destruction\n' >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user