Watch
1
0
Fork
You've already forked SouveraineOS
0
SouveraineOS/docs/tasks/19-settings-control-center.md
Fimeg bde961c6f2 saf: one spine — device, state, and work under the index
PAF becomes saf/device (history kept), STATE.md dissolves
into saf/state.md with the dated era archived, the substrate
SAF moves up from souveraine, and every agreement points at
saf/INDEX.md and nowhere else. one map, nothing to remember
2026-08-18 09:47:30 -04:00

139 lines
7.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# TASK 19 — First-party Settings control center
**Status:** in progress (worktree, device acceptance blocked while phone is
locked). **Repo:** `~/Projects/souveraine`.
## Goal
Replace the removed ii settings application with one Souveraine-owned control
center for phone and desktop. One page model, responsive chrome: stack
navigation on phone, side rail on desktop. Every control is a view over the
owning Config/service/daemon; no success-shaped switches.
## Current surface
Owned pages: Device, Lock, Wallpaper, Dock, Navigation, Idle, Keyboard,
Speech. Worktree adds Network & internet (Network/Cellular/Bluetooth), Display
(real Brightness), and Sound & microphone (PipeWire defaults).
Still required: app permissions/health, storage, accounts, updates, battery/
power profile, and explicit unavailable/error states.
## Launch/fullscreen failure found 2026-07-21
- Qt `Window.FullScreen` did not match the pill's compositor-owned fullscreen
path and could hide the navigation rail. Start windowed, then target the
Settings PID with the same Hyprland Lua fullscreen dispatcher the pill uses.
- The launcher allowed duplicate `qs -p` instances. Repeated taps coincided
with repeated full-shell run IDs, notification/polkit contention, lost
sessiond heartbeat, and fail-closed locks. Launcher must resolve the
standalone QML realpath and use `qs --no-duplicate -p <file>`.
- Until a controlled launch proves the log says `Launching config: .../
settings.qml`, the phone desktop entry stays hidden. Never accept another
`shell.qml` run ID from the Settings button.
## Polish sweep — folded in from TASK-22
Scoped by Casey 2026-07-22 as "smaller individual tweaks to the
souveraine-settings app and fullscreen apps in general". It was never a
separate work unit: its own text said the candidates come from this task's
"still required" list, and its status line read "items to enumerate with Casey
at session start", which is a placeholder rather than a task.
**A. Settings paper cuts.** The small items only — unavailable/error states,
battery/power profile stub, and whatever else Casey names. Not new pages.
The idle page's seconds-spinners were one of these and are done (2026-07-26:
presets, and the lock-screen timers now read live from sessiond). That work
turned up the sharper version of the same rule: reading the owning daemon is
only half of "no success-shaped switches". `SetPolicy` mutated memory and
persisted nothing, so a timer set in Settings was live until the next restart
and then silently back to the built-in 15 s. The page was honest; the daemon
forgot. Fixed 2026-07-26 — the policy is written to
`$XDG_CONFIG_HOME/souveraine/device-state-policy.json` and a failed save comes
back as `ok:false` instead of a control that looks like it took.
**Worth sweeping for elsewhere:** every other control this page owns should be
checked against the same two questions — does it read the authority, and does
the authority remember?
**B. Fullscreen apps in general.** Qt `Window.FullScreen` and the pill's
compositor-owned fullscreen path diverge; apps should start windowed and be
fullscreened through the same Hyprland Lua dispatcher the pill uses, per-PID.
Dock/ScreenCorners fullscreen detection needs the QML fallback for `qs -p`
windows — applied hot on both devices but stranded per-device until the tree
unification (`unify-shell-trees-laptop-phone.md`).
Ground rules: each tweak is its own small diff with its own device check, no
batching. Anything touching the ii-base layer gets promoted to the laptop repo
the same session (divergence doctrine).
## The state machine needs a surface here (Casey, 2026-07-28)
> "the souveraine-settings section needs to have all these state machine
> mechanics health checks built"
Everything the device state machine knows is currently legible only through
`forensic.jsonl` and `qs ipc call sessiond device_state`. No screen shows any of
it. Three separate gaps, already named elsewhere and converging here:
- **TASK-08(f) — `sensors_degraded` reaches no surface.** §10 source health
landed: a source that reported and then went silent for 90 s is `Down`, not
quiet, and every forensic snapshot carries the flag. The trail knows; nothing
else does. The crash reporter structurally cannot help — the SLPI outage
exited status 0. Needs an ambient indicator saying *which* source and *since
when*, and `device_state` already ships `sensor_health` over IPC, so the data
is there.
- **TASK-08(g) — the confidence gates are computed, logged, and never branched
on.** A settings page over them would be honest only as a *readout*, not as
controls, until (g)'s decision is made. Showing a slider for a threshold
nothing consults is TASK-19's own "no success-shaped switches" rule broken.
- **Grip is now a fourth evidence source with no readout** (2026-07-28,
`saf/device/edge-sense.md`). A refused squeeze — the pocket veto — is recorded via
`record_decision` and visible nowhere. "Why didn't my squeeze work" has no
answer on the device today.
**Ordering follows TASK-19's existing rule:** expose what actually actuates.
Health and evidence are *observations* and can be shown honestly right now —
state, panel, evidence, confidence, per-source health and last-seen, the
`sensors_degraded` flag, and the recent decision entries. Controls over the
confidence bands wait on (g).
That split matters: a health readout that is merely accurate is useful the day
it ships, whereas a controls page over a machine that ignores its own thresholds
would be lying.
**Built 2026-07-31 (souveraine `1c90d7f`) — the readout half.** `DeviceEvidence`
was egress-only: every surface could *report* input to the machine and none could
*read* it back. It now has an ingress that polls `device_state` and
`forensic_log`, and the Device page renders state, lock phase, panel, dimmed,
display-active, idle, observed + confidence, wake-suppression, per-source health
with the `sensors_degraded` banner, and the last twelve decisions with their
reasons. Strictly a projection — it holds nothing the protocol owns and branches
on nothing, so it does not become §1's eighth actor; and it polls only while a
surface is watching, so an open settings page is not a request per second all
day. `available: false` renders as "sessiond is not answering on this device"
rather than healthy-looking zeroes, which is §10's rule that "no evidence" and
"evidence says nothing is happening" are different states.
Still owed here: **last-seen per source** (TASK-19 asked for it; `device_state`
ships health and freshness but no timestamp, so it is not shown rather than
faked), and **grip as a fourth evidence source** — it is absent from
`device_state` entirely, so "why didn't my squeeze work" still has no answer on
the device. Controls remain blocked on TASK-08(g).
## Acceptance
1. Hammer Settings icon ten times: exactly one standalone PID/window, zero new
`shell.qml` run IDs, one notification owner, one polkit agent, sessiond
heartbeat continuous.
2. Settings log identifies `settings.qml`; close exits the process.
3. Phone opens using the pill's fullscreen state; pill remains usable and its
double tap returns the app to windowed.
4. Every page parses and scrolls at 540×1080; controls change the real service
and reflect failure/unavailability.
5. Desktop uses the same page components in a normal resizable window.
## Connects to
TASK-08 (power/update state), TASK-17 (keyboard/speech), TASK-07 (microphone),
TASK-15 (idle), deploy maintenance lease in TASK-08.