219 files, 2.0 MB, untracked in souveraine/docs and existing nowhere else. The volume is at 100% with no snapshots.
13 KiB
| title | status | created | supersedes | related | reference_on_disk | ||||
|---|---|---|---|---|---|---|---|---|---|
| First-Class Souveraine Apps — the requirements for belonging to the family | draft — living standard | 2026-07-19 | nothing (this gap had no doc) |
|
|
First-Class Souveraine Apps
The iOS line. The default built-in app line. There must be cohesion.
This is the missing layer. We had a doc for packaging a surface
(quickshell-surface-package.md) and a doc for the framework surfaces plug
into (souveraine-shell-ecosystem.md). We had no doc that says what an app
must be to count as one of ours. This is that doc.
An app is first-class when a person cannot tell, from feel alone, whether it shipped in the base image or arrived later. Same skin, same gestures, same manners, same relationship to the agent and to the lock. Cohesion is not a coat of paint applied at the end — it is a set of contracts each app satisfies by construction.
Think of Apple's built-in line: Phone, Messages, Notes, Settings. They differ wildly in purpose and share almost nothing in code, yet nobody mistakes one for a third-party bolt-on. That is the bar. Not "looks similar" — belongs.
0. The one law
One family, two modes — never two apps. (DECISIONS.md, Jul 13.) An app is a
single implementation that expresses itself on laptop and phone through
form-factor gates, exactly as SouveraineFamily.qml loads one panel set for
both and sheds Config.options.souveraine.phone gates as the modes converge.
A "phone version" and a "desktop version" as separate code is an automatic disqualification. The differences are density and reach, declared inline, not a fork.
The nine requirements
A surface earns "first-class" only when it satisfies all nine. They are listed in the order you'd build them.
1. Original, not borrowed
Every primitive an app is built from is original Souveraine AGPL or a
shared Souveraine widget — never a vendored ii file. (DECISIONS.md: cut the
cord from ii by rewriting, not vendoring; vendoring inherits GPLv3, which we
refuse.)
- A borrowed
iimodule is a temporary host, allowed only while its original replacement is being built. It is a debt, tracked, not a resting state.SouveraineFamily.qmlis the scoreboard of that convergence — the goal is fewerqs.modules.ii.*imports over time, each replaced byqs.modules.souveraine.*. - Test: grep the app's imports. Any
qs.modules.ii.*is a listed debt with a rewrite task, or the app is not first-class yet.
Today the shared original vocabulary is thin on purpose — modules/common/widgets
holds ContentPage and StyledToolTip; most primitives are still borrowed.
That thinness is the work, not a reason to keep borrowing. New apps grow the
shared vocabulary; they don't deepen the ii dependency.
2. The single skin
An app draws from one appearance source and threads no bespoke accent.
- Colors, spacing, radii, typography, elevation resolve through the shared
appearance system (
modules/common/Config.qml+ the atmosphere the agent controls). This mirrors the TUI's law (CLAUDE.md, rule 3): don't threadaccent: Colorthrough constructors —apply_atmospherefeeds the palette and the primary lands on the agent's color. The QML surfaces obey the same discipline: the atmosphere is the instrument; an app reads it, never hardcodes around it. - Consequence — cohesion is automatic, not curated. When the agent shifts its atmosphere, every first-class app moves together, because none of them pinned their own palette. An app with a hardcoded color is the one that visibly falls out of the family the moment the mood changes.
- Test: grep for literal color values and per-component accent args. Both are smells; the theme resolves them.
3. Native to both reaches — touch and pointer, close and far
The same surface must be honestly usable by thumb on a Pixel and by pointer on a laptop — not merely present on both.
- Hit targets, gesture affordances, and reveal/dismiss must satisfy the phone;
information density and secondary controls may expand on the laptop. These
are
extraCondition/ mode gates on one surface (Config.options.souveraine.phone), the same mechanism the family already uses, not a second layout file. - The phone's system gestures (
SystemGestureRail) are part of the contract: an app must not fight the edge-swipe navigation the shell owns. - Test: drive the app by thumb only, then by pointer only. If either reach needs a different build to be usable, it isn't one app yet (violates §0).
4. State-gated, layer-honest
An app declares its layer and stack-order and lets shell state gate its visibility — it does not decide on its own when it may appear.
- The canonical line is Phosh's
use_top_layer = !locked: state drives layer visibility, not the reverse. (souveraine-shell-ecosystem.md§1.) An app that paints over a locked screen, or ignores OSK suppression, or fights another surface for the top layer, is misbehaving regardless of how it looks. - Until the
LayerRegistry/ formalizedShellStateexists, an app still reads the existing state bits (GlobalStates: barOpen, oskOpen, screenLocked, overviewOpen…) rather than tracking visibility privately. - Test: lock the screen, open the OSK, enter overview. A first-class app responds correctly to each without being told per-case.
5. Lock-aware and trust-tiered
An app respects the lock trust split and the capability tiers (see the
lock memories / souveraine-lock-primitives.md): screenLocked (requested)
is not screenLockSecure (compositor-acknowledged), and personal data gates
on secure, never on the request.
- Ambient content may show on the lock surface (media card, agent card, the
glanceable line). Personal content and any mutation gate on secure + fresh
auth; step-up is a
PamContextprompt, not a soft check the app rolls itself. - Test: what does the app show, and permit, on the lock surface before authentication? If anything personal leaks or any mutation lands, it fails.
6. A live manifest — the app is legible to the system
A first-class app projects its state as a read-only, queryable structure
(souveraine-shell-ecosystem.md §2) — not because everything needs it today,
but because legibility is what lets the shell, the agent, and other apps relate
to it without parsing its QML.
- Shape follows the dock's model:
app.manifest → { …read-only snapshot… }, transported over the qs IPC surface. Build the projection when a real consumer exists; don't speculatively manifest a surface nothing acts on yet. - Test: can another surface (or the agent) learn this app's current state without reaching into its internals? If the only way to know what it's doing is to read its component tree, it isn't legible yet.
7. Agent-actionable through guarded methods — never a footgun
Where the agent (or any external actor) should be able to act on an app, the
app exposes a guarded method surface, not raw controls
(souveraine-shell-ecosystem.md §3).
- We do not build a new toolcall integration per app. Souveraine is
already a fully-fleshed harness; apps expose guarded methods
(
dock.pin(appId),app.reveal(), …) that the existing harness reaches. - Every method: validates inputs; refuses mutation in states that forbid it (locked, mid-drag, manifest stale) with a real refused-with-reason result; is idempotent where it can be; never fire-and-forget. The agent genuinely cannot break the app through the abstraction. That is the whole point.
- The teaching of these methods lives in the Souveraine School, never inlined into the method surface or an agent prompt. The app exposes; the School teaches. Two concerns, kept apart.
- Test: call a mutating method while the screen is locked. A first-class app returns a refusal with a reason. A footgun does the mutation.
8. Packaged reversibly — adoption, never ambush
An app ships through the surface-package safety contract
(quickshell-surface-package.md): it belongs to a device profile
(souveraine-surface-laptop / -phone) with shared QML in a common layer, and
installing or upgrading it never silently replaces a person's existing
shell.
- Adoption defaults to dry-run and names every managed / new / replacement
target. Replacements require explicit
--adoptand back up each conflict to a timestamped*.pre-souveraine-*file. Upgrades update assets but never auto-adopt. Uninstall restores originals and removes only Souveraine-managed symlinks — never user data or agent memory. - Idempotent by construction (feedback: db-changes-are-research): the same install must work on a fresh machine and as an upgrade. A first-class app's package does not assume a clean slate and does not corrupt on re-run.
- Test: install the app twice, then uninstall. Nothing user-owned changed without consent; the machine is left as it was found.
9. Provenance and license clean
An app is AGPL, original, attribution-clean end to end. No vendored GPLv3
ii source in its lineage (§1). Commits to the public branch carry no
Co-Authored-By, no Claude attribution, no third-party attribution, no PII in
source or history (public-branch memories). Behavioral reference from other
projects (ii, Phosh, sayonara) is studied, not copied — the C/C++ stays on
disk as a reference checkout, and our implementation is ours.
- Test: could this app's tree and history ship publicly, today, with no redaction and no license contradiction? If not, it isn't first-class.
The built-in line (the "iOS default apps" of Souveraine)
These are the apps that define the family — the ones a person meets first and measures every later app against. Each must be exemplary on all nine requirements, because they are the reference.
- Settings — the canonical "shows the standard" app. (The ecosystem doc
referenced a
SETTINGS-APP-PLAN.mdthat isn't on disk yet; when Settings is built native it becomes requirement §1–§2's showcase — one skin, original widgets, both reaches.) - Lock surface — already the strictest §4/§5 exemplar:
LockSurfaceHost,LockAgentCard,LockMediaCard. It is where trust-tiering is proven. - Dock — the §6/§7 exemplar: the first surface to get a manifest + guarded method surface, chosen for smallest blast radius.
- The agent surfaces (SidebarLeft / AiChat, the pill) — the app line's relationship to the consciousness itself; these carry the atmosphere most visibly and so hold §2 to the highest standard.
- Phone / Calls, Messages, Media — the device-native line. Where the phone reach (§3) and the system-gesture contract are non-negotiable.
An app joining this line is a promotion, held to the reference standard. An app outside it still must pass all nine to be called first-class — the line is just where the bar is demonstrated.
How to use this doc
- Building a new app: walk the nine in order. §1–§3 are structural (get them wrong and no later fix helps). §4–§5 are behavioral (state and trust). §6–§7 are legibility and agency (build when a consumer is real). §8–§9 are shipping.
- Reviewing an app: each requirement has a Test — a concrete thing to do, not a vibe. An app that passes all nine tests is first-class. Name which it fails; that failure is the task.
- Reference on disk:
~/Projects/sayonara-playeris a mature real-world Qt app pulled for structure only (how a serious app organizes Components/Gui/DBus/Interfaces, how it separates engine from view). It is not a dependency and not a thing we vendor — same rule as ii and Phosh: study the shape, write our own.
What this doc is not
- Not a widget catalog. The shared vocabulary (
modules/common/widgets) is thin today and grows as apps are rewritten off ii; this doc names the standard those widgets serve, not the widgets themselves. - Not a rewrite mandate. Borrowed
iimodules keep working; §1 makes their replacement a tracked debt, not an emergency. - Not a policy engine. Like the rest of Souveraine, the substrate provides contracts and guardrails; the app (and the agent) decide. Doctrinal guarantees (lock trust split, adoption safety, forward-only provenance) are not runtime knobs (feedback: no-doctrinal-knobs).