Make Applications a real app manager, and clean up storage without the racket

Claude-Session: https://claude.ai/code/session_01Ms2FbjQy31TVf3CEvQhGM8
This commit is contained in:
Gabriel Brown
2026-08-24 17:18:14 -04:00
parent b30bf40407
commit 5a0643357f
29 changed files with 5024 additions and 314 deletions
+189 -1
View File
@@ -107,6 +107,194 @@ grep -q 'keeping whatever is there now' "$page" \
grep -q 'Not measured' "$page" \
|| fail 'the page reports a per-snapshot size it cannot actually measure'
printf 'snapshots contract: PASS (%d volume(s), %d snapshot(s), no rollback)\n' \
# ── 5. The browser opens from a card that is closed ─────────────────────────
#
# The bug this pins: the file browser used to be drawn INSIDE the volume card's
# expanded body, so "Browse this snapshot" from a collapsed card set the
# browsing state and rendered nothing. The user pressed a button and the page
# did not move.
#
# Nesting is the whole failure, so nesting is what is checked: the browser has
# to be a card of its own, not a delegate inside the Repeater that draws one
# card per volume, and its visibility may not mention the volume card's open
# state.
python3 - "$page" <<'PY' || fail 'the snapshot browser cannot open from a collapsed volume card'
import re
import sys
source = open(sys.argv[1], encoding="utf-8").read()
# Comment lines go first, so a `//` aside about the browser cannot be mistaken
# for the browser.
text = "\n".join("" if line.strip().startswith("//") else line
for line in source.splitlines())
target = text.find("Snapshots.browseEntries")
if target < 0:
print("the page never lists the entries of the snapshot it is browsing", file=sys.stderr)
raise SystemExit(1)
# The chain of QML types enclosing one position. Braces also appear inside
# strings ("\u{F0413}") and comments, so the scan has to know the difference or
# the nesting it reports is fiction.
def enclosing(text: str, position: int) -> list[str]:
ancestors: list[str] = []
index = 0
length = len(text)
while index < length:
if index >= position:
return [name for name in ancestors if name]
char = text[index]
if char == "/" and text.startswith("//", index):
index = text.find("\n", index)
if index < 0:
break
continue
if char == "/" and text.startswith("/*", index):
end = text.find("*/", index + 2)
if end < 0:
break
index = end + 2
continue
if char in "\"'`":
index += 1
while index < length:
if text[index] == "\\":
index += 2
continue
if text[index] == char:
index += 1
break
index += 1
continue
if char == "{":
head = text[max(0, index - 80):index].rstrip()
match = re.search(r"([A-Z][A-Za-z0-9_.]*)\s*$", head)
ancestors.append(match.group(1) if match else "")
elif char == "}" and ancestors:
ancestors.pop()
index += 1
return [name for name in ancestors if name]
ancestors = enclosing(text, target)
if "SettingsCard" not in ancestors:
print(f"the browser is not inside a card at all ({ancestors})", file=sys.stderr)
raise SystemExit(1)
# The listing is a Repeater of its own, which is fine. What matters is what
# encloses the CARD: a Repeater above it is the per-volume one, and that is the
# bug -- the browser only exists while that volume's card is drawn expanded.
outside = ancestors[:len(ancestors) - 1 - ancestors[::-1].index("SettingsCard")]
if "Repeater" in outside:
print(f"the browser card is a delegate of the per-volume Repeater ({ancestors})", file=sys.stderr)
raise SystemExit(1)
PY
# And the visibility that gates it is about browsing, not about a card being
# open. Written out because `volumeCard.open` was exactly the expression that
# made the button do nothing.
grep -qE 'visible:.*volumeCard\.open.*browsing' <<<"$page_code" \
&& fail 'the browser still renders only while the volume card it came from is expanded'
grep -qE 'visible:.*(browsingOpen|Snapshots\.browsingConfig)' <<<"$page_code" \
|| fail 'nothing on the page is shown because a snapshot is being browsed'
# ── 6. Retention is editable, and the page is what edits it ─────────────────
#
# `Snapshots.setRetention` existed with no caller for three phases: the Keep row
# printed "24 hourly, 7 daily, 4 weekly" and there was no way to change any of
# them. A service function nobody calls is not a feature.
grep -Fq 'Snapshots.setRetention(' <<<"$page_code" \
|| fail 'the page never calls setRetention, so the keep counts are still read-only'
for horizon in Hourly Daily Weekly; do
grep -Fq "\"$horizon\"" <<<"$page_code" \
|| fail "the page has no $horizon control, so that horizon cannot be edited"
done
grep -q 'function setRetention(config: string, hourly: int, daily: int, weekly: int): void' "$service" \
|| fail 'the service does not take the three horizons separately'
# The helper validates them. Run against a snapper that records and does
# nothing: this is the only way to exercise a write verb without changing how
# this machine keeps its snapshots.
work="$(mktemp -d /tmp/panama-snapshots-contract.XXXXXX)"
trap 'rm -rf "$work"' EXIT
mkdir -p "$work/bin"
cat >"$work/bin/snapper" <<STUB
#!/usr/bin/env bash
printf 'snapper %s\n' "\$*" >>"$work/calls"
exit 0
STUB
chmod +x "$work/bin/snapper"
: >"$work/calls"
resolved="$(env -i PATH="$work/bin:/usr/bin:/bin" bash -c 'command -v snapper')"
[[ "$resolved" == "$work/bin/snapper" ]] \
|| fail "snapper resolves to '$resolved', not the stub; refusing to run a write verb against the real one"
runh() {
env -i PATH="$work/bin:/usr/bin:/bin" HOME="$work" LANG=C LC_ALL=C "$helper" "$@"
}
# This helper answers a refusal the way the page reads one: fresh state with an
# `error` in it, not an exit code. So a refusal is checked from that field, and
# from the absence of a write in the log -- the helper reads the configuration
# list on its way back out either way, and counting "did anything run" would
# mistake that read for the write it refused to do.
retention_error() { runh "$@" 2>/dev/null | jq -r '.error // ""'; }
wrote() { grep -c 'set-config' "$work/calls"; }
: >"$work/calls"
[[ -z "$(retention_error set-retention home 24 7 4)" ]] \
|| fail 'setting the keep counts failed against the stub'
grep -Eq 'snapper -c home set-config .*TIMELINE_LIMIT_HOURLY=24' "$work/calls" \
|| fail "the hourly count did not reach snapper: $(cat "$work/calls")"
grep -Eq 'TIMELINE_LIMIT_DAILY=7' "$work/calls" \
|| fail "the daily count did not reach snapper: $(cat "$work/calls")"
grep -Eq 'TIMELINE_LIMIT_WEEKLY=4' "$work/calls" \
|| fail "the weekly count did not reach snapper: $(cat "$work/calls")"
# One command, not three: a partial write would leave the three horizons
# disagreeing with what the page shows.
[[ "$(wrote)" == "1" ]] \
|| fail "the three horizons were written separately: $(cat "$work/calls")"
# The horizons have a ceiling, and it is the same number in the helper and in
# the service. A dropdown that offers a value the helper refuses is a dropdown
# that fails after the user has chosen.
: >"$work/calls"
[[ -z "$(retention_error set-retention home 50 50 50)" ]] \
|| fail 'the highest keep count the page offers was refused by the helper'
: >"$work/calls"
[[ -n "$(retention_error set-retention home 51 7 4)" ]] \
|| fail 'a keep count above the ceiling was accepted'
[[ "$(wrote)" == "0" ]] \
|| fail "a keep count above the ceiling still reached snapper: $(cat "$work/calls")"
grep -q 'retentionMax' "$service" \
|| fail 'the service does not publish the ceiling, so the page has to keep a second copy of it'
[[ "$(grep -oE 'retentionMax[^0-9]*[0-9]+' "$service" | grep -oE '[0-9]+' | head -1)" == "50" ]] \
|| fail 'the service and the helper disagree about how high the keep counts go'
# Each horizon is checked before anything is written. Checking that nothing was
# written is what says the refusal came first: snapper would refuse most of
# these too, so "did it error" alone would pass with the validation deleted.
for bad in 'abc' '-1' '5.5' '' '1e3' '99999' '7; reboot'; do
for position in 1 2 3; do
case "$position" in
1) arguments=("$bad" 7 4) ;;
2) arguments=(24 "$bad" 4) ;;
3) arguments=(24 7 "$bad") ;;
esac
: >"$work/calls"
[[ -n "$(retention_error set-retention home "${arguments[@]}")" ]] \
|| fail "an impossible keep count was accepted in position $position: ${bad@Q}"
[[ "$(wrote)" == "0" ]] \
|| fail "a refused keep count still reached snapper: ${bad@Q}: $(cat "$work/calls")"
done
done
: >"$work/calls"
[[ -n "$(retention_error set-retention '../../etc' 24 7 4)" ]] \
|| fail 'a configuration name that is not one was accepted'
[[ "$(wrote)" == "0" ]] || fail "a refused configuration still reached snapper: $(cat "$work/calls")"
printf 'snapshots contract: PASS (%d volume(s), %d snapshot(s), no rollback, retention editable)\n' \
"$(jq '.configs | length' <<<"$snapshot")" \
"$(jq '[.configs[].snapshots[]?] | length' <<<"$snapshot")"