Watch
1
0
Fork
You've already forked SouveraineOS
0

docs: give the shell a memory budget

This commit is contained in:
Fimeg 2026-08-13 15:46:32 -04:00
commit 3bf40c800d
2 changed files with 127 additions and 0 deletions

View 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.

View file

@ -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). |