9.4 KiB
Souveraine shell — plan (2026-07-13)
What this is now
qs -c souveraine — our own quickshell config, composed by deploy.sh:
our files (shell.qml, SouveraineFamily, services/, modules/settings/) are
symlinked from this repo; untouched ii directories are borrowed as whole-dir
symlinks; directories where we override any file are composed file-by-file.
ii's tree stays pristine — no overlay symlinks, no .upstream backups. We
step on end-4's toes nowhere; we borrow deliberately and vendor per-directory
only when a reason forces it.
SouveraineFamily.qml is the convergence scoreboard: one family, both modes
(desktop + phone). Panels that differ by form factor get an
extraCondition: Config.options.souveraine.phone gate; a panel is
"homogenized" when its gate is deleted. No panelFamily switching — the
config loads SouveraineFamily, period.
Live on laptop + phone as of 2026-07-13. Hyprland flipped: laptop via the
qsConfig var, phone via sed across its 4 hardcoded qs -c ii sites
(backup: hyprland.lua.bak-souveraine-config).
The Settings app
souveraine/settings-phone.qml — purpose-built mobile shell (list → push →
back, 68px finger rows, fullscreen, no titlebar), launched by
settings-launch.sh (imports compositor env from the user manager) via
souveraine-settings.desktop in the phone's app grid. Page registry shape
[{name, icon, component}] is identical to desktop settings.qml, so pages
are interchangeable. Pages edit Config.options (JsonAdapter → config.json,
hot-apply + persist).
Current pages: Device (souveraine.phone flag + future per-device overrides), Lock screen, Dock, Pill, Keyboard, About (ii's stock page verbatim).
Doctrine — "tie back down"
Two tiers, nothing app-private:
- Shell settings →
Config.options.*→ config.json. Idempotent, survives redeploy, diffable. Zero new persistence. - System settings → the owning daemon, live: NetworkManager, ModemManager
(mmcli -J), bluez, UPower, brightnessctl. The app is a view over system
state — kill it and the system is still the truth. No shadow copies, no
sync jobs. Privileged writes via polkit (
pkexec— setuid fixed 2026-07-13, Pixel3Arch gap #7), zero sudo.
Upstream drift — the decision (2026-07-13)
Our overrides were forked against the phone's vintage of ii. The laptop runs a newer ii. Drift is large and one-directional. The question is not "rebase everything" — it's "which upstream additions do we actually want, and which are desktop features we're deliberately not running on a phone-first shell."
Laptop-upstream additions we do NOT have:
background.widgets.{visualizer,stats,systemResources}— desktop widgets (the pctrade-style widget system landed upstream). Phone doesn't use these. BUT: borrowedBackground.qmlreadsConfig.options.background.widgets.*unconditionally → TypeError spam on laptop. Fix: backport the keys into our Config.qml (small surgical add), don't pull the widget machinery.claudeUsage— Claude Pro/Max subscription gauge in the bar. Want later.fileSearch— indexed file search in the launcher. Want later.sidebar.width/widthExtended, AiChatfontSizelive-binding. Want.dpmsTimeout,unlockHook,autoIdleInhibit,termBgTone— small knobs.- GlobalStates super-press timing (
superPressTime,shouldSuppressSuperReleaseSearch) — press-and-hold-super suppresses search. Conflicts with ourdockRevealPulsepatch (both touch the super-release block). Rebase needed eventually; not breaking anything today. - Ai.qml: OpenAI Responses API strategy,
thoughtSignature, FileView chat save. Our Ai.qml is heavily customized (no hardcoded models, Bifrost server, agent/resume) — rebase here is the dangerous one; do carefully or not at all.
What we have that upstream DROPPED:
Config.options.dock.stacks(fan-out stacks, "stackId|appId,appId") — upstream removed it. OurDockStack.qmlarc feature is ours alone now, not just forked. Keep.
Drift action items (priority order)
- Backport
background.widgets.*keys into our Config.qml — kills the laptop TypeError spam. Small, surgical, no widget machinery pulled in. - GlobalStates.qml — rebase
dockRevealPulseonto upstream's super-press rewrite. Both touch the same block. - Ai.qml — decide: carry our customizations forward on the old base (safe, current path) or rebase onto upstream's Responses-API Ai.qml (risky, gets us thoughtSignature + FileView). Defer until we want one of those.
- SidebarLeft/Right/Overview — dedupe the OSK flee-fix (15 lines triplicated) into one place after any rebase.
- Don't rebase dock/osk/TaskbarApps unless a specific breakage forces it.
Pages still to build (deferred, "in time")
- Display — brightness (Brightness service), screen toggle
(
blueline-screen-toggle), idle/suspend timeouts. hypridle.conf is user-owned: regenerate whole from template, don't sed. - Network — WiFi via existing Network service; Cellular needs a new
services/Cellular.qmlwrappingmmcli -J(data toggle, APN, signal, operator, SIM PIN). The real new work. - Bluetooth — BluetoothStatus exists, but org.bluez activation fails on the phone (journal, every boot). Fix the service before building UI on it.
- App health check — surfaced as a wanted page; not built.
- Dock pins/stacks editor — list editor with rename; consumes the staged
renameStack/stackNameplumbing in TaskbarApps/DockStack.
Other open items
- Phone cold-boot verification (autostart line flipped, unproven until next boot).
souveraine.phonedefaults false; Device page exposes the toggle but deploy.sh --phone doesn't flip it. Tying it down on phone deploy would satisfy idempotency doctrine.- Services rename to be more "souveraine-esq" — deferred.
- Licensing sweep — see ~/Projects/MAKE_PROPER_LICENSES_FOR_ALL_PROJECTS.md.
Ecosystem — the framework basics (2026-07-13)
Reference studied: PostMarketOS-Blueline/references/phosh (full source
checkout). Phosh's shell is built on three primitives we can learn from
without copying its C: PhoshLayerSurface (every visible thing subclasses
it), the zwlr_layer_shell_v1 layer it sits on (BACKGROUND/TOP/OVERLAY), and
zphoc_stacked_layer_surface_v1 — phoc's extension for ordering
layer-surfaces above/below each other within a layer (bare wlr can't).
Plus a PhoshState bitmask (NONE / MODAL_SYSTEM_PROMPT / BLANKED / LOCKED /
SETTINGS / OVERVIEW) that the shell reads to gate layer visibility —
use_top_layer = !phosh_shell_get_locked() is the canonical line. State
drives layer visibility, not the reverse.
Note: PhoshDockedManager is NOT "the dock" in our sense — it's hardware
docking (phone → external display: disables OSK, stops auto-maximize, flips
is-phone). It's the system-level analog of our souveraine.phone flag:
the phone-becomes-desktop transition. They rhyme; they aren't the same
feature.
What we're actually building (three pieces, compose incrementally)
-
Layer registry + ShellState — declare each surface's layer and stack-order explicitly, driven by a state bitmask. GlobalStates is almost this already (it has the state bits) but doesn't own the layer/stack declarations. Quickshell gives us layer-shell via
PanelWindowanchors + exclusive zones — same protocol as Phosh, QML binding not C. Steal the model, not the code. -
Live manifest per surface — the dock (then each major surface) projects its current state as a queryable structure: pinned apps, stacks, positions, visibility, mode. This is a service, not UI —
dock.manifest→{pinned, stacks, hidden, mode}. The dock already holds this state internally; the manifest is the projection for external consumers. -
Method abstraction for the agent — the agent does NOT get a new toolcall integration. Souveraine is already a fully-fleshed harness with its own integration state; we use THAT. What lives on the dock side is a small method surface (
dock.pin(appId),dock.restack(...),dock.reveal()) with state checks baked in — validate inputs, refuse mutation when the dock is in a state that forbids it (locked, mid-drag), return real results. The agent calls a guarded method, not a footgun; it genuinely can't mess up the dock through the abstraction.
Where the lessons live — Souveraine School
The teaching of these methods — what they are, when to use them, the lessons — is NOT inline in the dock or the agent. It lives in the Souveraine School, taught to agent and human in proper time. The dock's job is to expose the abstraction; the School's job is to teach it. Two separate concerns; do not conflate.
First artifact
The dock manifest is the smallest concrete first step: the dock is built, working, and already has internal state (pinned apps, stacks, reveal/pulse). Projecting it into a queryable manifest + guarded method surface proves the pattern. The layer registry generalizes it after.
Reference material on disk
- Phosh source:
~/Projects/PostMarketOS-Blueline/references/phosh(full C/meson checkout, read-only reference — do not edit). - end4-pC fork (pctrade):
~/Projects/end4-pC— studied for the panelFamilies + background-widget patterns; not copied. - Pure Maps (Kirigami/QML nav) — referenced in
Pixel3Arch/docs/car-and-dock-references.md; not yet pulled as source.