Watch
1
0
Fork
You've already forked SouveraineOS
0

tasks: rewrite 03 (boot timing, source-verified faults), add 17 (keyboard/dictation)

03: today's deep boot investigation — the splash-to-splash flicker is a
DRM-master handoff race on a panel with supportsAsyncCommit:false (56
EBUSY commits); the Hyprland splash text is killed (disable_splash_rendering);
the corner box is an unidentified top-right toast (NOT the error overlay,
which stays); the recovery-mode wedge is documented. The EBUSY handoff
fix is the one real remaining timing bug.

17: stevia OSK + mic key + dictation shipped this session; layout/UX
polish and TTS wiring remain.
This commit is contained in:
Fimeg 2026-07-21 11:37:16 -04:00
commit 8578e654d2
3 changed files with 175 additions and 50 deletions

View file

@ -1,72 +1,117 @@
# TASK 03 — Boot timing: fade splash → lock, no extra fixes
**Status:** open. This is Casey's framing of the real goal: "the dang
boot video is more than long enough if things were being timed right
such that we didn't need all these extra fixes."
**Status:** open, deeply diagnosed 2026-07-21. Casey's framing: "the dang
boot video is more than long enough if things were being timed right such
that we didn't need all these extra fixes." The boot is visually ugly even
on a *successful* boot — splash-to-splash flicker, a stray corner box, and
the desktop assembling in the open before the lock covers it. Three of the
faults are now root-caused to source; two fixes landed today.
## Goal
One clean boot timeline: boot video (splash) plays, fades directly onto
the lockscreen. No second PIN flash, no polkit interruption, no gap that
needs a fallback surface to paper over. The fixes from 2026-07-17
(splash deadlock removed, fallback made quiet, dismissnotify) are
patches; this task is making the timing actually right so the patches
stop being load-bearing.
the lockscreen. No flicker, no error box, no gap that shows the desktop.
## The desired timeline
```
kernel quiet ─ splash starts ─ DRM probes ─ splash animating
─ greetd initial session (souveraine-session)
─ souveraine-splash --wayland holder maps (act two)
─ sessiond acquires ext-session-lock, renders glance (TASK-02)
─ quickshell starts, registers, sessiond hands off the lock
─ shell lockscreen glance visible
splash fades onto the shell lockscreen ─ done
─ quickshell starts, BootBloom maps, holder crossfades to it
sessiond hands off the lock, shell lockscreen visible
splash → holder → bloom → lock, one continuous bloom ─ done
```
The splash must hold DRM master until the shell lockscreen has real
content, then release. Today `souveraine-splash.service` (oneshot) +
`souveraine-session` (greetd initial session, pokes `/run/souveraine-splash.sock`
and waits for it to vanish) own this handoff. The 30s deadlock that
gated every boot is fixed (`Pixel3Arch 93c1371`, `0ec54da`); verify the
handoff is now prompt, not riding the backstop watchdog.
## Root-caused faults (2026-07-21, source-verified)
The visible mess is THREE distinct problems, not one. Read the live boot
log: `journalctl -b -o short-monotonic` and the Hyprland instance log at
`/run/user/1000/hypr/<sig>/hyprland.log` (grep -v the GL_ extension dumps).
1. **The splash-to-splash flicker = a DRM-master handoff race on a panel
that can't async-commit.** aquamarine logs `drm: drmProps.supportsAsyncCommit:
false` for DSI-1, then `Clearing stale page-flip state for DSI-1 (pending
for 598ms)`, then **56 consecutive** `atomic drm request: failed to
commit: Device or resource busy`. The C splash leaves a page-flip pending
when it drops DRM master; Hyprland grabs the CRTC and, unable to queue
behind the stale flip (no async commit on this panel), spins EBUSY until
it retires. Source: hyprwm/aquamarine `src/backend/drm/DRM.cpp`
(`CDRMOutput::commitState` retries as blocking modeset, sets
`commitTainted` on fail — no per-frame backoff before that).
- Escape hatch (blunt): `env = AQ_NO_ATOMIC,1` forces legacy KMS
("heavily not recommended" upstream, but real). NOT applied.
- Proper fix (UNDONE): tighten souveraine-session's handoff so the C
splash fully drains its last page-flip and drops master BEFORE
Hyprland modesets — no overlapping pending flip. This is the real
TASK-03 work item.
2. **The "Hyprland :)" splash TEXT — FIXED 2026-07-21.** `disable_hyprland_logo`
(already set) only kills the mascot background; the handwritten splash
text is a SEPARATE option, `misc.disable_splash_rendering`, which
defaulted on. Now `= true` in overlays/hypr/hyprland.lua (Pixel3Arch
488139b), live-confirmed `set:true`.
3. **The small upper-right corner box during the flashes — NOT yet pinned
to a specific trigger.** It is one of Hyprland's top-right toasts
(`src/notification/NotificationOverlay.cpp`) — startup callers in
`Compositor.cpp` fire for: non-"Hyprland" XDG_CURRENT_DESKTOP, missing
`hyprland-dialog`, failed render assets (`m_failedAssetsNo > 0`, red),
no watchdog fd — and `PluginSystem.cpp` toasts a **failed plugin load**
(hyprgrass is loaded via `hyprpm reload` at boot step 2). Most likely
suspects here: failed-assets or a hyprgrass API-version toast. NEXT:
catch it live (it is transient; a boot screen-record or reading
`hyprctl notifications`/asset-fail count right after boot names it).
This is NOT the CHyprError overlay (`src/errorOverlay/`) — Casey
explicitly does NOT want that touched; leave error reporting intact.
Separately: **the desktop-visible ~12s gap.** Splash releases ~20.8s but
sessiond doesn't confirm the shell's lock until ~33s. The bloom holder
(souveraine-splash --wayland, added 2026-07-21, Pixel3Arch 20e72c5) is
meant to cover this whole window; on Casey's eyes it still leaks Hyprland
through. Verify the holder actually maps first and stays up until BootBloom
pokes it — a gap here is a holder-timing bug, not a new surface.
## Worse, separate: recovery-mode wedge
If `hyprland.lua` fails to load, Hyprland falls to `recoverycfg.lua`
(autogenerated), whose boot exec chain never runs the real step 0-4 wiring
AND which grabs the ext-session-lock itself, showing the built-in "Oopsie
daisy… lockscreen app died" screen. Then sessiond/shell can never take the
lock (`lock denied/revoked — another locker owns the session`) because
Hyprland owns it and Wayland only lets the locking client unlock. Observed
2026-07-21 after a shell restart landed on a prior recovery instance.
Recovery is: at the device, drop Hyprland's lock (the fallback screen
prints the commands) or cold boot. Watch for it: `hyprctl binds | grep -c
dispatcher` returns ~3 in recovery vs ~48 on the real config; `ls
/run/user/1000/hypr/<sig>/recoverycfg.lua` present is a tell but not proof
(Hyprland writes it as a fallback artifact even on good boots).
## Context
- `~/Projects/Pixel3Arch/rootfs-overlay/etc/systemd/system/souveraine-splash.service`
— oneshot, deliberately NOT `Before=greetd` (that was the deadlock leg 1).
- `~/Projects/Pixel3Arch/overlays/greetd/souveraine-session` — the
initial-session wrapper that fades the splash and waits for DRM release
before exec'ing start-hyprland.
- `~/Projects/Pixel3Arch/overlays/greetd/config.toml`
`[initial_session] command = "/usr/local/bin/souveraine-session"`.
- `~/Projects/Pixel3Arch/overlays/hypr/hyprland.lua` — boot step 3
restarts `souveraine-shell.service` (supervised, `Pixel3Arch d230764`).
- `~/Projects/SouveraineOS/docs/LOCK-DPMS-LESSONS.md` §5 — lock
architecture: sessiond takes the lock before the shell; ~1.05s handoff
gap; `lockdead_screen_delay = 4000` in hyprland.lua.
## Steps
1. **Instrument before touching.** Boot once with timestamps:
`journalctl -b -o short-monotonic | grep -E 'souveraine-splash|souveraine-session|souveraine-sessiond|souveraine-shell|greetd'`.
Find where the wall-clock actually goes. Don't tune blind.
2. Confirm the splash fades onto the shell lockscreen with no visible
second surface (validates TASK-02's quiet fallback end-to-end).
3. Confirm no polkit/NM password prompt interrupts boot (see handoff
doc "Boot password prompt" note — was NetworkManager 802.11 auth
timeout, patched live on the named SSIDs; verify it stays fixed).
4. Only if there's a real gap, tighten ordering. Target: splash visible
for its full intended duration, then a single fade to the lock.
- `Pixel3Arch/bootsplash/` — the C DRM splash (`main.c`) + the new
`--wayland` holder (`wayland.c`) + `splash-signal.c` (pokes both the
/run and $XDG_RUNTIME_DIR sockets). Builds native on the phone.
- `Pixel3Arch/overlays/greetd/souveraine-session` — greetd initial
session; pokes the DRM splash and waits for socket vanish before
exec start-hyprland. **This is where fault #1's handoff fix goes.**
- `Pixel3Arch/overlays/hypr/hyprland.lua` — boot step 0 launches the
holder, step 3 restarts souveraine-shell.service.
- `SouveraineOS/docs/LOCK-DPMS-LESSONS.md` §5 — lock arch, ~1.05s handoff
gap, `lockdead_screen_delay = 4000`.
- Panel fact: DSI-1, 1080x2160@60, `supportsAsyncCommit:false`,
freedreno/FD630 — the async-commit limit is hardware, not config.
## Acceptance
A cold boot shows: boot video → fade → lockscreen. One lock surface,
ever. No password prompt mid-fade. `systemd-analyze blame` shows the
splash isn't gating on the watchdog.
A cold boot shows: boot video → one continuous bloom → lockscreen. No
splash-to-splash flicker (no EBUSY storm, or it's hidden). No Hyprland
splash text (done). No stray corner toast. No desktop visible before the
lock. One lock surface, ever.
## Connects to
TASK-02 (the glance the splash fades onto), TASK-10 (mkinitcpio removes
the white initramfs text before the splash — separate but adjacent),
TASK-11 (souveraine-secrets, which must not prompt at boot).
TASK-02 (the glance the bloom fades onto), TASK-10 (mkinitcpio initramfs
text before the splash), TASK-11 (souveraine-secrets must not prompt at
boot). The EBUSY handoff fix (#1) is the last load-bearing timing bug.

View file

@ -0,0 +1,78 @@
# TASK 17 — Keyboard (stevia) + dictation polish
**Status:** shipped-partial 2026-07-21. The phone went from NO on-screen
keyboard to a working patched stevia with a mic key and live dictation in
one session. What shipped works; the follow-ons are UX/layout polish.
## What shipped (2026-07-21)
- **stevia is the OSK**, patched to run under Hyprland. Upstream stevia
hard-requires `zphoc_device_state_v1` (phoc-private) before creating a
keyboard surface, so under Hyprland it sat "Wayland not yet ready"
forever and the phone had NO keyboard. Patched optional.
`Pixel3Arch/pkgs/stevia` (0.56.0-3), builds native on-device.
Squeekboard retired (`SouveraineOS/blueline/squeekboard/` kept as
layout reference only).
- **Mic/dictation key** on every layout's bottom row (patch 0002): spawns
`souveraine-stt`, a record→transcribe→type toggle CLI. Config
(endpoint, enable) lives in Config.options.speech, surfaced in the new
**Settings → Speech** page (souveraine `402e887`), with a live /health
probe. STT hits Casey's VPN faster-whisper box (see
[[whisper-stt-server]] memory).
- **Terminal arrow keys** (patch 0002): the terminal layout's bottom row
gained ← ↓ ↑ → (the shortcuts bar always had them but sat under the pill).
- **Nav pill fix**: the SystemGestureRail was lifting the pill 315px (a
stale squeekboard height) which stacked it OVER the keys and stevia's
upward-opening layout menu — "keyboard selection not working" was this.
Now lifts to stevia's real 200px top edge; pill stays visible as the
dismiss handle, clear of the keys (souveraine `5e5bf04`).
- **souveraine-stt error honesty** (`67aeb25`): distinguishes server 5xx /
4xx / unreachable from a genuine empty transcript. The old "didn't catch
that" on any no-text result masked a whole server outage as a mic bug.
## Follow-ons (the polish that remains)
1. **Dictation UX**: the mic key spawns the CLI fire-and-forget — no
in-keyboard recording indicator (the CLI sends a notify-send "Listening…"
toast, which is coarse). A pressed/recording state on the key itself
(stevia has `--status` wired: `souveraine-stt --status` → idle|recording)
would be the honest tell. Needs a stevia patch to poll status and
restyle the key, OR drive it off the notify path.
2. **Size**: Casey settled on `gsettings set mobi.phosh.osk scaling "[]"`
(200px, no auto-scale). The taller auto value is
`['auto-portrait','auto-landscape']` (348px). Applies live, no restart.
Not a bug — just record the chosen default so a reflash keeps it.
3. **Layouts**: the vendored squeekboard `us+apple` (iOS-style) and
`us+hacker` designs are the target *feel*; stevia uses its own JSON
layout format (`src/layouts/*.json` in the stevia tree). Porting the
iOS layout to stevia is open design work — start from the squeekboard
header notes in `SouveraineOS/blueline/squeekboard/`.
4. **TTS**: Config.options.speech.tts + the Settings field exist but the
endpoint is empty — no TTS server on the VPN yet (VibeVoice runs on
10.10.20.19 but isn't wired to a REST route the shell can call). When
one exists, the settings field is ready.
## Context
- Patches: `Pixel3Arch/pkgs/stevia/0001-make-zphoc-device-state-optional.patch`
(Hyprland compat, upstream candidate) and
`0002-dictation-key-terminal-arrows.patch` (mic key + arrows).
- CLI: `Pixel3Arch/rootfs-overlay/usr/local/bin/souveraine-stt`.
- Shell: `souveraine/surfaces/quickshell/modules/settings/SpeechConfig.qml`,
`modules/common/Config.qml` (speech block), `settings-phone.qml`.
- The OSK D-Bus plumbing (sm.puri.OSK0 SetVisible, GlobalStates.oskOpen,
the "osk" IPC target) is unchanged from squeekboard — stevia's unit
claims the same bus name, so shell chrome tracking needed no changes.
## Acceptance
Keyboard usable in every app; mic key dictates real speech into the
focused field (verified 2026-07-21: "Hello, can you hear me?" end-to-end);
pill dismisses without eating key taps; a recording indicator makes the
mic key's state obvious.
## Connects to
[[whisper-stt-server]] (the STT backend), TASK-15 (dictation is a network
fetcher the idle coordinator must wake for), the stevia patches are
upstream candidates (Hyprland compat especially).

View file

@ -12,9 +12,9 @@ plus open threads from the 2026-07-17 session.
| # | Task | Status | Blocks / blocked-by |
|---|------|--------|---------------------|
| 1 | [Favorites — wife's name on her number](01-favorites-contacts.md) | blocked | needs a contacts-store decision (qtpim retired — see archive/06) |
| 1 | [Favorites — wife's name on her number](01-favorites-contacts.md) | blocked | blocked-by TASK-16 (culver-contacts is the store) |
| 2 | [Lockscreen-as-Rust-system: glance + swipe](02-lockscreen-rust-system.md) | in progress | stopgap shipped 2026-07-17 |
| 3 | [Boot timing: fade splash → lock, no extra fixes](03-boot-timing-splash-to-lock.md) | open | paired with TASK-11 (now archived) |
| 3 | [Boot timing: fade splash → lock, no extra fixes](03-boot-timing-splash-to-lock.md) | diagnosed | EBUSY handoff race is the last timing bug; splash text killed, holder added |
| 7 | [WCD9340 mic: functional-pinned; unpin + fidelity](07-mic-wcd9340-slim-tx.md) | functional 07-20 | defect: power pin; kernel `mic-race-fix` |
| 8 | [Device state manager / power profiles](08-device-state-manager.md) | open | blocked-by TASK-09 suspend item |
| 9 | [Suspend-resume FTS calibration race](09-suspend-resume-fts.md) | interim-fix | kernel-side; cold-boot only |
@ -22,6 +22,8 @@ plus open threads from the 2026-07-17 session.
| 13 | [Active Edge (squeeze) as a sensor input](13-active-edge-squeeze.md) | open | substrate up; bring-up unit not drafted |
| 14 | [Overview pane → phone app grid](14-overview-appgrid.md) | open | shell-side; appgrid prior art linked in-file |
| 15 | [Idle coordinator: freeze, doze, scheduled wakeups](15-idle-coordinator.md) | open | design; blocked-by 09 for suspend tier; eats half of 08 |
| 16 | [culver: usable messenger + contacts as Personal-class store](16-culver-messenger-contacts.md) | open | unblocks TASK-01; uses TASK-11 (keying) + archive/04 (NotifyEvents) |
| 17 | [Keyboard (stevia) + dictation polish](17-keyboard-stevia-dictation.md) | shipped-partial | OSK + mic key + STT live 07-21; layout/UX follow-ons |
Archived (see `archive/`): 05 crash reporter — done 2026-07-21,
CrashReporter.qml in-shell watcher, kill-test verified on device;