Finish the wonderland: System told truthfully, in eight tabs instead of ten
Claude-Session: https://claude.ai/code/session_01Ms2FbjQy31TVf3CEvQhGM8
This commit is contained in:
@@ -78,10 +78,46 @@ rg -Fq 'SystemSettings.openGnomePanel("system", "users")' "$settings_dir/HealthP
|
||||
&& fail 'System Health still hands Users to GNOME, but Panama owns that page'
|
||||
rg -Fq 'SystemSettings.openGnomePanel("sharing")' "$settings_dir/HealthPage.qml" \
|
||||
&& fail 'System Health still hands Sharing to GNOME, but Panama owns that page'
|
||||
# Colour profiles left the card for the same reason Users and Sharing did:
|
||||
# Displays offers a colour profile, a bit depth, an SDR brightness and an SDR
|
||||
# saturation per output, which is more than GNOME's panel can say in a session
|
||||
# it does not manage. Digital wellbeing stays, because nothing here does it.
|
||||
rg -Fq 'SystemSettings.openGnomePanel("color")' "$settings_dir/HealthPage.qml" \
|
||||
|| fail 'Fedora ownership boundary lost the Color profiles handoff'
|
||||
&& fail 'System Health still hands Colour profiles to GNOME, but Displays owns them per output'
|
||||
rg -Fq 'SystemSettings.openGnomePanel("wellbeing")' "$settings_dir/HealthPage.qml" \
|
||||
|| fail 'Fedora ownership boundary lost the Digital wellbeing handoff'
|
||||
|
||||
# ── A repair says what it will run, before it runs it ───────────────────────
|
||||
#
|
||||
# "Restart Vicinae" and "Release duplicate inhibitors" are what the buttons
|
||||
# say. What they do is run a command as the person pressing them, and the row
|
||||
# never said which. Somebody who wants to know what a button is about to do to
|
||||
# their machine should not have to read panama-doctor to find out -- and after
|
||||
# the fact is not the same answer, because by then the decision is made.
|
||||
rg -Fq 'repairCommand' "$settings_dir/HealthPage.qml" \
|
||||
|| fail 'a repair row never shows the command it is about to run'
|
||||
python3 - "$settings_dir/HealthPage.qml" <<'PY' || fail 'the repair command is only shown once the repair has run, which is after the moment it was worth knowing'
|
||||
import re
|
||||
import sys
|
||||
|
||||
text = open(sys.argv[1], encoding="utf-8").read()
|
||||
for line in text.splitlines():
|
||||
if "repairCommand" not in line:
|
||||
continue
|
||||
# Gating the command on a repair result, a repairing id, or a "working"
|
||||
# state makes it after-the-fact reassurance rather than a decision aid.
|
||||
if re.search(r'\b(lastRepair|repairingId|repairing|working|repairFailed)\b', line):
|
||||
raise SystemExit(1)
|
||||
raise SystemExit(0)
|
||||
PY
|
||||
|
||||
# ── One row, re-checked; and the report saved rather than copied ────────────
|
||||
rg -Fq 'Health.refreshCheck(' "$settings_dir/HealthCheckRow.qml" \
|
||||
|| rg -Fq 'Health.refreshCheck(' "$settings_dir/HealthPage.qml" \
|
||||
|| fail 'no row offers a re-check, so a row fixed by hand stays wrong until the whole scan runs again'
|
||||
rg -Fq 'Health.saveReport(' "$settings_dir/HealthPage.qml" \
|
||||
|| rg -Fq 'Health.saveReport(' "$settings_dir/HealthSummary.qml" \
|
||||
|| fail 'the report can only be copied, never saved'
|
||||
# Exact authored handoffs are asserted above. Also prove every panel named by
|
||||
# this boundary is accepted by SystemSettings, so a typo cannot ship a dead
|
||||
# button even if its copy still looks correct.
|
||||
@@ -122,7 +158,9 @@ labels = (
|
||||
'if (status === "error") return "Action required";',
|
||||
'return "Not set up";',
|
||||
)
|
||||
assert all(label in page for label in labels)
|
||||
# The four status words belong to the row that shows them. They used to be
|
||||
# written on the page, one indirection away from the Text that rendered them.
|
||||
assert all(label in row for label in labels)
|
||||
assert 'group: "desktop-foundation"' in page
|
||||
assert 'group: "input-media"' in page
|
||||
assert 'group: "integrations"' in page
|
||||
@@ -140,7 +178,7 @@ assert 'pendingConfirmation' in page
|
||||
assert 'ddc-permissions' in page
|
||||
PY
|
||||
|
||||
fixture='{"schemaVersion":1,"generatedAt":"2026-08-18T12:00:00Z","summary":{"status":"error","healthy":2,"warnings":2,"errors":1,"unconfigured":1},"context":{"session":"hyprland","versions":[{"id":"quickshell","version":"0.3.0"}]},"checks":[{"id":"desktop.vicinae","group":"desktop-foundation","title":"Vicinae","status":"warning","detail":"The launcher service is stopped.","action":{"kind":"repair","label":"Restart Vicinae","confirm":false}},{"id":"desktop.quickshell","group":"desktop-foundation","title":"Quickshell","status":"error","detail":"Panama shell needs to restart.","action":{"kind":"repair","label":"Restart Panama","confirm":true}},{"id":"input.pipewire","group":"input-media","title":"PipeWire","status":"ok","detail":"Audio graph is responding."},{"id":"integration.bluebubbles","group":"integrations","title":"BlueBubbles","status":"unconfigured","detail":"Messaging integration has not been enabled."},{"id":"integration.calendar","group":"integrations","title":"Calendar","status":"warning","detail":"Calendar probe timed out.","action":{"kind":"open","label":"Open Date & Time","confirm":false,"target":"datetime"}},{"id":"panama.caffeine","group":"panama-tools","title":"Caffeine","status":"ok","detail":"No duplicate sleep inhibitors."}]}'
|
||||
fixture='{"schemaVersion":1,"generatedAt":"2026-08-18T12:00:00Z","summary":{"status":"error","healthy":2,"warnings":3,"errors":1,"unconfigured":1},"context":{"session":"hyprland","versions":[{"id":"quickshell","version":"0.3.0"}]},"checks":[{"id":"desktop.vicinae","group":"desktop-foundation","title":"Vicinae","status":"warning","detail":"The launcher service is stopped.","action":{"kind":"repair","label":"Restart Vicinae","confirm":false},"repairCommand":"systemctl --user restart vicinae.service"},{"id":"desktop.quickshell","group":"desktop-foundation","title":"Quickshell","status":"error","detail":"Panama shell needs to restart.","action":{"kind":"repair","label":"Restart Panama","confirm":true},"repairCommand":"panama-action restart-shell"},{"id":"input.pipewire","group":"input-media","title":"PipeWire","status":"ok","detail":"Audio graph is responding."},{"id":"integration.bluebubbles","group":"integrations","title":"BlueBubbles","status":"unconfigured","detail":"Messaging integration has not been enabled."},{"id":"integration.calendar","group":"integrations","title":"Calendar","status":"warning","detail":"Calendar probe timed out.","action":{"kind":"open","label":"Open Date & Time","confirm":false,"target":"datetime"}},{"id":"panama.caffeine","group":"panama-tools","title":"Caffeine","status":"ok","detail":"No duplicate sleep inhibitors."},{"id":"panama.updates","group":"panama-tools","title":"Software updates","status":"warning","detail":"3 pending updates carry a security advisory.","action":{"kind":"open","label":"Open Software Update","confirm":false,"target":"updates"}}]}'
|
||||
|
||||
state_home="$(mktemp -d /tmp/panama-health-ui.XXXXXX)"
|
||||
config_path="$state_home/quickshell"
|
||||
@@ -251,6 +289,39 @@ ShellRoot {
|
||||
});
|
||||
}
|
||||
|
||||
// The Software Update action, end to end: the doctor authors the
|
||||
// target, Health accepts it, and the row has to actually go there.
|
||||
function offerUpdatesAction(): bool {
|
||||
ShellState.settingsPage = "services";
|
||||
return Health.consumeSnapshot(JSON.stringify({
|
||||
schemaVersion: 1,
|
||||
generatedAt: "2026-08-24T12:00:00Z",
|
||||
summary: { status: "warning", healthy: 0, warnings: 1, errors: 0, unconfigured: 0 },
|
||||
context: { session: "hyprland", versions: [] },
|
||||
checks: [{
|
||||
id: "panama.updates",
|
||||
group: "panama-tools",
|
||||
title: "Software updates",
|
||||
status: "warning",
|
||||
detail: "3 pending updates carry a security advisory.",
|
||||
action: {
|
||||
kind: "open",
|
||||
label: "Open Software Update",
|
||||
confirm: false,
|
||||
target: "updates"
|
||||
}
|
||||
}]
|
||||
}), Health.acceptedGeneration + 1);
|
||||
}
|
||||
|
||||
function followUpdatesAction(): string {
|
||||
const requested = settingsShell.requestHealthAction("panama.updates");
|
||||
return JSON.stringify({
|
||||
requested: requested,
|
||||
page: ShellState.settingsPage
|
||||
});
|
||||
}
|
||||
|
||||
function activateIndicator(): string {
|
||||
ShellState.settingsPage = "home";
|
||||
ShellState.settingsOpen = false;
|
||||
@@ -311,19 +382,19 @@ jq -e '
|
||||
{objectName:"health-check-row:issue:desktop.vicinae", id:"desktop.vicinae", section:"issue", statusText:"Needs attention"},
|
||||
{objectName:"health-check-row:issue:desktop.quickshell", id:"desktop.quickshell", section:"issue", statusText:"Action required"},
|
||||
{objectName:"health-check-row:issue:integration.calendar", id:"integration.calendar", section:"issue", statusText:"Needs attention"},
|
||||
{objectName:"health-check-row:issue:panama.updates", id:"panama.updates", section:"issue", statusText:"Needs attention"},
|
||||
{objectName:"health-check-row:quiet:input.pipewire", id:"input.pipewire", section:"quiet", statusText:"Healthy"},
|
||||
{objectName:"health-check-row:quiet:integration.bluebubbles", id:"integration.bluebubbles", section:"quiet", statusText:"Not set up"},
|
||||
{objectName:"health-check-row:quiet:panama.caffeine", id:"panama.caffeine", section:"quiet", statusText:"Healthy"}
|
||||
]
|
||||
and (.renderedRows | map(.id) | length) == 6
|
||||
and (.renderedRows | map(.id) | unique | length) == 6
|
||||
and (.renderedRows | map(.id) | length) == 7
|
||||
and (.renderedRows | map(.id) | unique | length) == 7
|
||||
and .emptyQuietGroups == ["desktop-foundation"]
|
||||
and .fedoraHandoffs == [
|
||||
{id:"color", label:"Color profiles", action:"Open color"},
|
||||
{id:"wellbeing", label:"Digital wellbeing", action:"Open wellbeing"}
|
||||
]
|
||||
and .summaryHeight == 126
|
||||
and (.rowHeights | length) == 6
|
||||
and (.rowHeights | length) == 7
|
||||
and (.rowHeights | all(. >= 62))
|
||||
and .checking == true
|
||||
and .checkingText == "Checking…"
|
||||
@@ -363,7 +434,7 @@ jq -e '.renderedRows[] | select(.id == "desktop.vicinae") | .statusText == "Repa
|
||||
>/dev/null <<<"$failed_repair_state" || fail "repair failure was not shown inline: $failed_repair_state"
|
||||
[[ "$(jq -c .rowHeights <<<"$failed_repair_state")" == "$settled_heights" ]] \
|
||||
|| fail 'repair failure changed row geometry'
|
||||
[[ "$(jq -r '.renderedRows | map(.id) | unique | length' <<<"$failed_repair_state")" == 6 ]] \
|
||||
[[ "$(jq -r '.renderedRows | map(.id) | unique | length' <<<"$failed_repair_state")" == 7 ]] \
|
||||
|| fail 'repair state duplicated a health action row'
|
||||
for _ in $(seq 1 40); do
|
||||
failed_repair_state="$(run ipc call health-ui-test state)"
|
||||
@@ -432,6 +503,32 @@ jq -e '
|
||||
and .refreshRequested == true
|
||||
' >/dev/null <<<"$activation_state" || fail "indicator activation did not open and refresh System Health: $activation_state"
|
||||
|
||||
# ── The Software Update action goes to Software Update ──────────────────────
|
||||
#
|
||||
# The two health checks that offered "Open Software Update" carried no target
|
||||
# at all, so the button rendered, focused, and did nothing -- the failure this
|
||||
# whole target mechanism exists to make impossible, sitting inside it. Fixing
|
||||
# it needed a change on both sides at once (panama-doctor authors the target,
|
||||
# Health.settingsTargets accepts it), and each half is pinned where it lives.
|
||||
# This is the third piece: the row acts on it.
|
||||
[[ "$(run ipc call health-ui-test offerUpdatesAction)" == "true" ]] \
|
||||
|| fail 'a check offering the Software Update action was rejected by the service'
|
||||
# Both conditions: the row rendered, and the scan the page starts when it loads
|
||||
# has settled. A row action is disabled while a check is running, which is
|
||||
# correct and would otherwise read here as a dead button.
|
||||
for _ in $(seq 1 60); do
|
||||
updates_state="$(run ipc call health-ui-test state)"
|
||||
jq -e '.checking == false and (.renderedRows | any(.id == "panama.updates"))' \
|
||||
>/dev/null <<<"$updates_state" && break
|
||||
sleep 0.1
|
||||
done
|
||||
jq -e '.checking == false and (.renderedRows | any(.id == "panama.updates"))' \
|
||||
>/dev/null <<<"$updates_state" \
|
||||
|| fail "the Software Update check did not render on a settled page: $updates_state"
|
||||
follow_state="$(run ipc call health-ui-test followUpdatesAction)"
|
||||
jq -e '.requested == true and .page == "updates"' >/dev/null <<<"$follow_state" \
|
||||
|| fail "the Software Update action did not open Software Update: $follow_state"
|
||||
|
||||
if rg -i 'QQml|ReferenceError|TypeError|binding loop|failed to load component' "$shell_log"; then
|
||||
fail 'isolated fixture emitted QML errors or warnings'
|
||||
fi
|
||||
|
||||
Reference in New Issue
Block a user