Watch
1
0
Fork
You've already forked SouveraineOS
0
SouveraineOS/docs/FIRST-CLASS-APPS.md

252 lines
13 KiB
Markdown
Raw Permalink 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.

---
title: First-Class Souveraine Apps — the requirements for belonging to the family
status: draft — living standard
created: 2026-07-19
supersedes: nothing (this gap had no doc)
related:
- docs/tasks/souveraine-shell-ecosystem.md (layer registry, manifests, guarded methods)
- docs/tasks/quickshell-surface-package.md (how surfaces are packaged / adopted)
- docs/DECISIONS.md ("One family, two modes"; "Cut the cord from ii")
reference_on_disk:
- ~/Projects/sayonara-player (pulled 2026-07-19 as a real-app structural reference, NOT a dependency)
---
# 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 `ii` module is a *temporary* host, allowed only while its
original replacement is being built. It is a debt, tracked, not a resting
state. `SouveraineFamily.qml` is the scoreboard of that convergence — the
goal is fewer `qs.modules.ii.*` imports over time, each replaced by
`qs.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 thread
`accent: Color` through constructors — `apply_atmosphere` feeds 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` / formalized `ShellState` exists, 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 `PamContext` prompt, 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 `--adopt` and 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.md` that 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-player` is 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 `ii` modules 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*).