# Applications, Storage & Snapshots redesign

Approved mock: `home-mocks/applications.html` (scratchpad, :8642). Spec wins over mock on
conflict. Scope: the Applications *tab* (Gaming and Screen Intelligence untouched) plus the
System category's Storage and Snapshots tabs, folded into this phase by decision.

## Goals

1. **Applications becomes an app manager**: searchable installed list (DesktopEntries +
   flatpak metadata), flatpak uninstall, honest system-package rows, permission summaries,
   autostart toggle per app, jump chips to the app's rules elsewhere.
2. **The panama apps catalog goes native**: category chips → entries with installed state,
   installing through the same dnf/flatpak paths (polkit prompts). Same files as the CLI.
3. **Defaults modernized**: role dropdowns replace the hand-rolled accordion; a single-file-type
   override ("One file type") for when a role's family is too broad.
4. **Storage**: proportional breakdown bar with honest captions; folder bars share ONE scale;
   "Clean up, honestly" — itemized, sized, click-each, nothing pre-selected, nothing nags
   (the anti-racket rules are contract-pinned); the duplicate container affordances merge.
5. **Snapshots**: retention becomes editable (wire the caller-less `Snapshots.setRetention`);
   the file browser becomes its own card that opens regardless of source-card state (fixes the
   collapsed-card bug); rollback stays absent (contract).

Non-goals: dnf package removal from Settings (honest refusal row instead), full flatpak
permission *editing* (summary + Open Flatseal when installed), per-app storage classification
beyond the flatpak sizes, portal-grant editing, snapshot space measurement (quotas honesty
stays).

## New helper: `scripts/panama-applications` (pinned verbs)

House discipline (validated inputs, JSON out, mutations return fresh state, bounded timeouts):
- `flatpaks` → `[{ id, name, size, origin }]` (from `flatpak list` machine-readable columns).
- `permissions <app-id>` → `{ summary: [human strings], raw: {...} }` from `flatpak info
  --show-permissions` — curated buckets: Camera, Microphone, Full file system access, Home
  folder, Network, Devices; unknown keys summarized honestly, never dropped silently.
- `uninstall <app-id>` → `flatpak uninstall --noninteractive` (app only; a second verb
  `unused-runtimes` lists what `--unused` would remove, and `clean-unused` removes them —
  the Storage cleanup row uses these).
- `catalog` → categories + entries from `setup/packages/extras/*` (same parser rules as
  `setup/lib/extras-catalog`: `flatpak:<id> | Label`, bare dnf names, indented continuations),
  each entry `{ id, label, kind: flatpak|dnf, installed }` (installed via flatpak info / rpm -q).
- `install <category> <entry-id>` → flatpak: `flatpak install --noninteractive flathub <id>`;
  dnf: `pkexec dnf install -y <pkg>`. Refuses ids not present in the catalog files (no
  arbitrary package installation through this surface — contract-pinned).

## Extended helpers

- `scripts/panama-default-apps`: `search-types <query>` → matching MIME types with their
  current handler + candidate apps; `set-type <mime> <desktop-id>` → `xdg-mime default`
  (validated against installed desktop entries). Role behavior untouched.
- `scripts/panama-disks`: `breakdown` → segments `{ home, applications, caches, system, free }`
  in bytes — home = the scan targets under ~ minus caches, applications = flatpak app+runtime
  sizes (user-readable sums), caches = ~/.cache, system = used − the others ("System &
  everything else" caption — honest arithmetic remainder), free from the fs. `cleanables` →
  `[{ id, label, detail, bytes, privileged }]` for: cache (~/.cache), trash, flatpak-unused,
  dnf-cache (privileged). `clean <id>` executes exactly one, refusing unknown ids; trash via
  `gio trash --empty`; cache via a guarded rm of ~/.cache/* (never follows symlinks out).
- `scripts/panama-snapshots`: retention already supported (`set-retention`); add
  `set-retention` argument validation for the three horizons if not already split
  (hourly/daily/weekly numbers 0-50).

## Services (A)

- **`services/AppLibrary.qml`** (new): merges `DesktopEntries.applications` with the flatpak
  metadata (id-matched via the entry's flatpak export), exposes `apps: [{ entryId, name, icon,
  kind: flatpak|system, flatpakId, size }]`, `permissionsFor(id)` (cached),
  `uninstall(flatpakId)`, catalog state (`categories`, `entriesFor(cat)`, `install(...)`),
  `busy/lastError`, seam `PANAMA_APPLICATIONS_HELPER`.
- **`services/DefaultApps.qml`**: `searchTypes(query)`, `setType(mime, desktopId)` wrappers.
- **`services/Disks.qml`**: `breakdown`, `cleanables`, `clean(id)` wrappers; cleanables
  refresh after any clean.
- **`services/Snapshots.qml`**: nothing new needed beyond confirming `setRetention`'s shape
  matches three-horizon editing; adjust if it takes a single string.

## UI (B)

**ApplicationsPage.qml** rebuilt: Installed applications card (search field filters name +
id; rows: letter/icon tile, name + source badge, subtitle id·size or "Installed by the system
package manager"; expanded: Permissions row (+ Open Flatseal when installed), Start with the
session (autostart toggle — creates/toggles the autostart entry for that app), "Elsewhere in
Settings" jump chips (shown only where a rule exists: Notifs.appRule customized/present →
notifications; app in AudioDevices applications → sound; PrivacyPage relevance is static —
link when the app id appears in its rules if cheaply knowable, else omit), Uninstall (flatpak,
two-stage danger) or the honest dnf refusal row with the exact command). Browse-the-catalog
card (category chips, entry rows, Install with polkit caption for dnf entries, INSTALLED
badge). Default applications card: role dropdowns via OptionPickerRow-style (candidates from
the existing role matching; keep all 10 roles + family-count details), then the "One file
type" row expanding to a search field over `searchTypes` with per-type app pickers. Autostart
card: existing rows restyled with the standardized confirm; compositor autostart row kept
read-only. The Search card stays but the hardcoded "Super+Space" is replaced by a live
`Keybinds` lookup (launcher description match, literal fallback).

**StoragePage.qml**: breakdown stack bar + legend at top (honest captions; "System &
everything else" for the remainder); Folders card bars share one scale (the disk, or the
largest — pick the DISK so it's comparable to the free-space bar, with a note); "Clean up,
honestly" card from `cleanables` (row per item: label, honest cost detail, size, Clear… with
two-stage confirm; privileged rows carry the password caption; zero-byte rows render inert
with "nothing to do"); the "Unused container images" kitty-terminal row is REMOVED (the
Containers card below already routes properly — one affordance, not two). Drive/Filesystems/
Removable/Swap cards kept.

**SnapshotsPage.qml**: per-config Keep row becomes three dropdowns (hourly/daily/weekly →
setRetention); the browser moves OUT of the volume card into its own card rendered whenever
browsing state is set (opening from a collapsed card now works — this is the bug fix);
timeline/preview/expander kept; Space card kept with its honesty.

## Search & docs (C)

New entries: Installed applications, Uninstall an application, Application permissions,
Install applications (catalog), Autostart, File associations → applications; Clean up storage,
Application caches → storage; Snapshot retention → snapshots. Docs regen only if schema
changes (none expected — verify).

## Contracts (C — write, never run)

- `applications-settings-contract`: reconcile with the rebuilt page (role labels, card
  titles, AutostartAppPicker, the bun matcher extraction).
- NEW `app-library-contract`: fixture-driven (stub flatpak/rpm): catalog parser agreement
  with `setup/lib/extras-catalog` rules, install refuses off-catalog ids, uninstall argv
  shape, permissions summary buckets, dnf never invoked for removal anywhere in the helper.
- `disks-contract`: extend — breakdown arithmetic honesty (segments sum ≤ used, remainder
  labeled), cleanup rules: every cleanable itemized with bytes, nothing auto-selected, no
  cleanable executes without its id being explicitly passed, cache rm is guarded, trash via
  gio. Pin the anti-racket copy stance (no urgency language).
- `snapshots-contract`: extend — setRetention wired from the page, browser renders
  independent of the volume card's open state, rollback still absent.
- `default-apps-contract` family rules untouched; new needles for set-type validation.
- Backlog: Phase 10. README count line if count changes.

## Agent ownership (parallel)

- **A**: `scripts/panama-applications` (new), `scripts/panama-default-apps`,
  `scripts/panama-disks`, `scripts/panama-snapshots` (only if retention args need splitting),
  `services/AppLibrary.qml` (new), `services/DefaultApps.qml`, `services/Disks.qml`,
  `services/Snapshots.qml`.
- **B**: `modules/settings/ApplicationsPage.qml`, `StoragePage.qml`, `SnapshotsPage.qml`,
  new components + qmldir.
- **C**: `services/SettingsSearch.qml`, contracts above, backlog, README count line.

Hard rules for everyone: no live mutations (no flatpak install/uninstall, no dnf, no rm, no
gio trash, no snapper writes, no xdg-mime writes) — read-only probes and stub fixtures only;
no test runs; valid QML/Python at every save. B programs against the pinned APIs.
