docs: give the shell a memory budget
This commit is contained in:
parent
c95df22be6
commit
3bf40c800d
2 changed files with 127 additions and 0 deletions
126
docs/tasks/75-the-shell-must-have-a-memory-budget.md
Normal file
126
docs/tasks/75-the-shell-must-have-a-memory-budget.md
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
# TASK 75 — the shell must have a memory budget
|
||||
|
||||
**Status:** scoped 2026-08-13; no implementation or fresh measurement yet.
|
||||
**Owners:** `souveraine` (current Quickshell shell and agent pane),
|
||||
`souveraine-sessiond` (session authority), and a possible new Rust surface
|
||||
runtime. **Adjacent:** TASK-72 (agent pane), TASK-59 (face), TASK-43
|
||||
(compositor), TASK-02/08 (lock and device authority).
|
||||
|
||||
## The problem
|
||||
|
||||
The daily-driver phone has 3.5 GB RAM. Three distinct observations must not be
|
||||
collapsed into one vague claim about QML:
|
||||
|
||||
- QuickShell was measured at **610 MB RSS** during the Live2D spike.
|
||||
- The summoned WebKit face added **283 MB RSS**; together that moment was
|
||||
about 893 MB before the rest of the phone's workload.
|
||||
- A separate chat-rendering failure drove QuickShell to **952 MB anon RSS**
|
||||
and the kernel OOM-killed the shell. The visible pathology was a dynamic-QML
|
||||
and per-stream-chunk rendering storm; it is not proof that every QML surface
|
||||
costs that much.
|
||||
|
||||
The present `PanelLoader` calls itself lazy but only gates on `Config.ready`.
|
||||
Once the shell is ready it constructs almost the entire panel family in one
|
||||
QuickShell process. A `Loader` can destroy an inactive item, but it cannot
|
||||
guarantee its allocator returns pages to the OS, and it cannot isolate a crash
|
||||
or leak from the session shell.
|
||||
|
||||
The goal is not an arbitrary low RSS number. It is a shell whose permanent
|
||||
cost, optional cost, and failure domains are named, measured, and enforced so
|
||||
new surfaces do not quietly turn the phone into a 900 MB bet.
|
||||
|
||||
## The non-negotiable resident core
|
||||
|
||||
These stay in the supervised session shell until an equally strong replacement
|
||||
exists:
|
||||
|
||||
- the sessiond heartbeat and lock handoff;
|
||||
- the secure lock surface and lock-tier disclosure gate;
|
||||
- display/idle projection, device-state directives, and the small system
|
||||
gesture/power paths that must remain available when optional UI dies;
|
||||
- the minimal bar and error/recovery indication.
|
||||
|
||||
They are authority-adjacent. The lock in particular must not sit behind a
|
||||
`LazyLoader`: TASK-48 proved that a missing item during scene reload loses the
|
||||
session-lock adoption path.
|
||||
|
||||
## Three viable paths
|
||||
|
||||
### A. Make the current QuickShell tree honestly lazy
|
||||
|
||||
Keep one QuickShell process. Turn optional panes into real `Loader` boundaries:
|
||||
instantiate on deliberate open, destroy after close (with a short state-save
|
||||
grace where needed), and keep services independent of their views.
|
||||
|
||||
This is the cheapest first improvement. It can cut construction and active
|
||||
object cost, but it does **not** prove an RSS reduction until measured; Qt's
|
||||
heap may retain released pages. It also leaves a rendering storm in the same
|
||||
failure domain as the lock shell.
|
||||
|
||||
### B. A small resident shell plus supervised surface processes
|
||||
|
||||
Keep the authority shell small. Run the agent panel, Live2D/webview, and any
|
||||
future expensive explorer or renderer as explicit, separately supervised
|
||||
processes. A process begins only on a user/authority-approved presentation
|
||||
request and exits after dismissal or an idle timeout. Its crash is an event to
|
||||
the core shell, not a dead lock screen.
|
||||
|
||||
The AI panel is the first candidate. It needs a narrow data/action contract:
|
||||
the substrate remains the conversation authority, sessiond remains the
|
||||
session/lock authority, and the panel is a renderer with no authority to wake,
|
||||
unlock, or expose personal state while locked. Restarting it must restore from
|
||||
the authoritative conversation state, not a QML object graph.
|
||||
|
||||
This is the recommended product path now. It gives unload semantics that the
|
||||
OS can actually observe and preserves the QML work already paid for.
|
||||
|
||||
### C. A native Rust shell runtime
|
||||
|
||||
Build a client-side Wayland shell in Rust: layer-shell surfaces, rendering,
|
||||
input, text, D-Bus/PipeWire integrations, settings, and a supervised surface
|
||||
protocol. This is not a compositor rewrite; ViewTop remains the compositor and
|
||||
sessiond remains device authority.
|
||||
|
||||
This is credible, not free. Smithay Client Toolkit supplies Rust client
|
||||
plumbing for layer-shell and session-lock, but it is not a complete visual
|
||||
shell. Replacing QuickShell means owning layout, text shaping, rendering,
|
||||
accessibility, animation, protocol integration, and every ugly recovery edge
|
||||
the current QML tree already contains. Native Rust does not itself solve memory
|
||||
growth; the same core/worker process boundary must be designed either way.
|
||||
|
||||
Noctalia v5 is useful evidence, not a base to copy: it chose a native
|
||||
C++/Wayland/OpenGL ES shell with no Qt/GTK dependency. Its direction validates
|
||||
the category, but its full dependency and feature surface are the cost a fresh
|
||||
runtime inherits.
|
||||
|
||||
## First proof, before a rewrite decision
|
||||
|
||||
1. **Measure the baseline.** On the phone, record RSS/PSS for QuickShell and
|
||||
each child process at idle, agent pane closed/open, and a representative
|
||||
streamed tool turn. Pair every number with the exact surface/process tree.
|
||||
2. **Extract the agent pane.** Build an explicit `souveraine-agent-surface`
|
||||
worker with its own process lifetime. Give it a narrow, typed conversation
|
||||
stream and restore key; do not give it a second session authority. Prove
|
||||
close destroys the worker and its memory disappears from the OS view.
|
||||
3. **Fault proof.** Kill the worker during a stream. The core shell and secure
|
||||
lock must remain alive; reopening restores from substrate truth.
|
||||
4. **Rust spike.** In a separate repository/package, create only one native
|
||||
Rust layer-shell card plus one sessiond-fed directive. Measure resident RSS,
|
||||
launch latency, and failure behavior on desktop first, then a non-invasive
|
||||
phone test. No lock takeover, no global shell replacement.
|
||||
5. Choose with measurements: continue the split QuickShell architecture, or
|
||||
fund the Rust shell runtime because its measured core is materially better
|
||||
and its required protocol surface is understood.
|
||||
|
||||
## Acceptance
|
||||
|
||||
- The resident core and every optional surface have an owner, startup trigger,
|
||||
shutdown rule, and measured memory line.
|
||||
- Closing the AI pane ends its worker process; no claimed unload rests on a
|
||||
hidden QML item.
|
||||
- Killing an optional surface cannot drop the session lock or authority
|
||||
heartbeat.
|
||||
- The agent surface cannot disclose Personal-class content while locked or
|
||||
directly cause wake/unlock/device-state changes.
|
||||
- The Rust spike is a package/CI result plus measured client behavior, not a
|
||||
desktop screenshot or a theoretical crate list.
|
||||
|
|
@ -64,6 +64,7 @@ Four that are cheap relative to what they unblock:
|
|||
|
||||
| # | Task | What's left |
|
||||
|---|------|-------------|
|
||||
| 75 | [The shell must have a memory budget](75-the-shell-must-have-a-memory-budget.md) | **Scoped 2026-08-13.** QuickShell's resident core must stay small; the AI panel is the first separately supervised, genuinely unloadable surface. Measure before choosing a full native-Rust shell rewrite. |
|
||||
| 74 | [LibrePods: an admitted accessory](74-librepods-as-an-admitted-accessory.md) | **Scoped 2026-08-13.** AirPods protocol stays in LibrePods; sessiond must admit/audit presentation events and Quickshell projects the card. First gate: capture a redacted case-open fixture — connection is not proof of opening. |
|
||||
| 73 | [One spine, two skins: the self-dashboard](73-self-dashboard.md) | Designed 2026-08-11, nothing built. RedFlag Desktop (Tauri, public) + house Gatehouse (wry + QML glance, lens skeleton), spine is agent-side pacman closure resolution that retires souveraine-updater's `pkexec --noconfirm`. Name and home-repo decisions are Casey's. |
|
||||
| 71 | [QmlMobileScanner as a components app candidate](71-qml-mobile-scanner-candidate.md) | Recon 2026-08-11, verdict owed. GPL-3.0 Qt Quick scanner+writer (zxing-cpp/QZXing/zint, SQLite history). Compatible inside AGPL; camera gate is TASK-46; ffmpeg backend needs a freedreno check; fontawesome-qml likely **not** needed (no FontLoader in the tree — unverified). |
|
||||
|
|
|
|||
Loading…
Reference in a new issue