11 KiB
System redesign — the finale, 10 tabs become 8
Approved mock: home-mocks/system.html (scratchpad, :8642). Spec wins over mock on conflict.
Storage, Snapshots, Containers keep their shipped designs (Containers gets the root-type fix
only). TEST GRANT ACTIVE: agents may RUN contracts related to their work as they build; the
full suite stays the orchestrator's call.
Consolidation (pinned)
System tabs become: About · Software Update · System Health (services) · Storage ·
Snapshots · Containers · Date, Time & Region (datetime) · Sync & Backup. The region
tab retires (retired-map → datetime). The manual tab retires as a TAB but manual stays
a routable leaf (opened from About's Manual card and existing deep links) — B determines the
mechanism (hidden-leaf list or equivalent) and C reconciles settings-nav-contract's
category/leaf counts.
Bug kills (all in scope)
SystemSettings.quickshellVersion hardcoded "0.3.0" → read qs --version; the auto-download
switch nested in the Firmware card; Health's target-less "Open Software Update" actions (add
target: "updates" AND extend Health.settingsTargets in the same change — adding one
without the other invalidates whole snapshots); CHECK_TITLES missing panama.updates
(KeyError on timeout); the SettingsShell.healthDiagnostics TypeError (guard the binding on
typeof item.uiDiagnostics === "function"); RegionPage's stale "Open appearance" handoff;
panama-updates' stale dnf-automatic comment; the backup color gap
(layoutFromStoredDisplays + layoutsEqual extended to vrrMode, colorProfile,
bitdepth, sdrBrightness, sdrSaturation, mirrorOf — optional/back-compat like
Displays.isPersistedLayoutEntry); Restore without confirmation; stray settings.json.*
temp files (clean once, and if the writer can leak them, fix the leak); dead code
(Containers.wouldExpose, SettingsSync.left, SettingsBackup.lastAction,
HealthPage.statusLabel; DateTime.localTime/universalTime/rtcTime become USED by the
manual-set flow rather than deleted).
A — services & scripts (pinned APIs)
scripts/panama-about: rows gainFirmware(DMIbios_version+bios_date, bootctl only as fallback — it reports "n/a" here) andSecure Boot(mokutil, absent-tolerant); aPanamarow (git describe --tags --always --dirty+%cr, absent-tolerant outside a checkout), placed right afterOperating system.SystemSettings.quickshellVersionnow parsed fromqs --version, kept as a one-shot Process started fromrefresh()and guarded byquickshellVersionRead— so a plain binding onquickshellVersionis enough and B needs no extra call. The literal stays as the fallback only.scripts/panama-updates:changelog <source> <name>→{source, name, kind, text, error}wherekindisadvisory|changelog|none(dnf:dnf5 advisory info --json --updates --contains-pkgs=first, elsednf5 changelog --upgradeswith dnf's two header lines stripped; flatpak:remote-info --cached --logHistory, which Flathub does not populate, so in practice the honest "publishes no release notes"; firmware: the fwupd release notes cached bycheck). Names constrained byNAME_PATTERNbefore argv.checkgains per-itembytesand per-sourcedownloadBytes, present only when EVERY item in that source was priced (dnf fromdnf5 repoquery --queryformat %{downloadsize}, flatpak parsed from its rendereddownload-sizecolumn — its--jsonomits the column).apply flatpak <app-id>appends one ID, checked against the last scan. Stale dnf-automatic comments fixed;DNF_TIMERnamed.services/Updates.qml:changelogFor(source, name)→ record ornull, cached bysource/name, serial queue, reactive viachangelogRevision(read it in the binding, DesktopPreferences.get pattern);applyFlatpakApp(id);downloadBytes+downloadSize(string) +sourceDownloadSize(s)formatBytes(n);loadingChangelog.
scripts/panama-doctor:CHECK_TITLES["panama.updates"]; updates actions gettarget: "updates"; snapshot gainsrepairCommand(a joined argv string) on the five ids in REPAIR_COMMANDS only — the three in-process repairs have no command line and claim none; newpanama-doctor check <id>verb printing a FULL snapshot envelope holding that one check (same schema, same summary arithmetic, so callers reusevalidSnapshot).services/Health.qml:settingsTargetsgains "updates";refreshCheck(id)(splices one validated row in, recomputes the summary, drops a reply the newer full scan superseded) withrefreshingId/refreshingCheck;saveReport(path)viatee(defaultdefaultReportPath=~/panama-health-report.txt) reporting throughlastSaveResult, andreportText()for the plain-text body;safeCheck/validCheckcarryrepairCommand.services/DateTime.qml:setTime(iso)viatimedatectl set-time(validated againsttimePattern=YYYY-MM-DD HH:MM[:SS]AND round-tripped through Date, refused while NTP is on AND whilestatusReadis still false — "not looked yet" is not "off");localTime/universalTime/rtcTimewired fromTimeUSec/RTCTimeUSec, advanced locally once a second whileDateTime.trackingis true (B sets it while the Clock card is on screen; no process per tick).scripts/panama-locale+services/SystemLocale.qml:get [cat]/set <cat> <locale|"">/categories/overrides(one JSON object for all five) —LC_TIME,LC_NUMERIC,LC_MONETARY,LC_MEASUREMENT,LC_PAPER. "Match language" is""and is implemented by re-issuinglocalectl set-localewith every OTHER assignment, since localectl replaces locale.conf with exactly what it is given. Service exposescategories,categoryValue(cat)(""= match language),categoryLabel(cat),setCategory(cat, locale),categoryRevision(read it in bindings),busy, and the pendingRestart discipline.scripts/panama-settings-backup:create [name] [homeState](label sanitized byLABEL_RE, stored INSIDE the envelope — the filename stays the timestamp SNAPSHOT_RE pins, since that is what ordering, pruning and restore confinement rely on),delete <name>(throughsnapshot_source, the same confinement gate restore uses), list gainsbytesandlabel. Stale-temp sweep extended to Quickshell's QSaveFile leftovers (settings.json.XXXXXX, no leading dot) with a one-hour age guard so an in-flight write is never destroyed.services/SettingsBackup.qml:create(name),deleteBackup(name), deadlastActionremoved, the color-gap fix per above (storedDisplayFields,validStoredField, both used bylayoutFromStoredDisplaysandlayoutsEqual).services/SettingsSync.qml+ helper: preview output gainschanges: [{key, from, to}]with both values rendered to text by the helper (render(),VALUE_LIMIT120), capped atCHANGE_LIMIT40 with achangeCountfor what is not shown; the import still applies everything. Service gainschangeCount; deadleftremoved.modules/settings/SettingsShell.qml: the healthDiagnostics guard (A owns this single edit — B is told hands-off that binding).
B — UI
AboutPage.qml showpiece per mock (hero, truthful versions block, Hardware card with
GraphicsDevices joined, Device-name → Sharing row, Manual card listing chapters with titles
READ FROM THE FILES (first heading), Read → openSettings("manual") + section); Design
principles card dropped (the manual carries the philosophy). ManualPage.qml: chapter titles
from file first-headings (make the old comment true); onLinkActivated routes
panama://settings/<page> (or equivalent scheme) links through ShellState.openSettings,
everything else external; keep the tabs/reader shape. UpdatesPage.qml: restructure per
mock (headline; System packages with changelog expanders + download size; Automatic card
with BOTH switches; Applications & firmware rows with per-app flatpak updates when present;
history kept). DateTimePage.qml becomes the merged tab (Clock card with manual-set
revealed when NTP off; Timezone; Language & formats card — language picker, four category
dropdowns [Match language + installed locales], first-day-of-week fact row, live preview
grid from Qt.locale() for the chosen format locales); RegionPage.qml DELETED (retired
route). HealthPage.qml: repair rows show repairCommand in the detail before running,
per-check Re-check button (refreshCheck), Save-report row, Color-profiles handoff row
REMOVED (Digital wellbeing stays), the updates action now navigating. SyncPage.qml:
preview diff list from changes (mono-ish tabular rows, del/add tones), Restore + Reset
two-stage confirms (the house danger pattern), backup rows with name field on create +
size + Delete…, reset subtitle mentions themes. ContainersPage.qml: root becomes
SettingsPage per convention (logs drill-in preserved). SettingsRoutes.qml: the 8-tab
strip + region retired + manual leaf mechanism (KEEP the categories array literal-shaped).
C — periphery
Search: hostname/kernel/model/memory/uptime/serial → about; export settings/import
settings → sync; currency/formats/measurement/paper/first day → datetime; region retired
routing verified; manual entries keep working (leaf). Contracts: settings-nav-contract
(counts + retired map), settings-pages-contract (routing list: add updates/containers/
datetime, drop region), updates-contract (changelog verb read-only pins; per-app flatpak
apply argv), health contracts (target "updates" validity BOTH sides, repairCommand shown
before run, single-check verb full-shape, KeyError fixture), settings-backup-contract +
-live (extended fields round-trip — a stored record with color fields restores them and
layoutsEqual detects color-only drift; delete confinement; name sanitation),
settings-sync-contract (changes shape, no secrets in stringified values), manual-contract
(titles-from-files, in-app link routing), gnome-handoff-contract (region handoffs gone —
consider OWNED), NEW system-pages-contract if the pins don't fit existing files (C's
judgment; keep count growth honest). Backlog Phase 15. README count line. Docs/commands
regen at the end. RUN the contracts you write/touch as you go (grant active); report results.
Hard rules still: no live mutations of system state (no dnf/flatpak installs, no timedatectl/localectl writes, no backup restores against the real store — the scratch-daemonizing harness pattern and hermetic stubs are the sanctioned test paths, and the grant means you may RUN those freely). Valid QML at every save. B programs against A's pinned APIs; A updates this spec before changing them.