July 29, 2026
38 recorded commits · 4 named repos · 6 private repos
- Private work 6 private repos
Activity was recorded across 6 private repos. Names, subjects, counts, and links stay private.
- Pixel3Arch14 changes
- ci: publish the device packages into the edge archive11:21 p.m. · daa9d37 · internal Gitea record only
Everything in pkgs/ has been hand-built on [host] and installed with pacman -U, which is invisible to -Syu and to souveraine-updater -- it groups by the repo a package came from (updater-core/src/pacman.rs:442), so a hand-installed package is never offered as an update whatever it is named. TASK-25.
- souveraine-ucm-blueline: package the UCM tree10:39 p.m. · 7647ff6 · internal Gitea record only
Every routing decision the device makes lives in these three files and none of them was owned by a package -- they were hand-copied from rootfs-overlay, which is the failure mode TASK-25 exists to end. alsa-ucm-conf ships nothing for this board, so there is no file conflict.
- PAF: phone calls carry two-way audio; correct the callaudiod verdict10:31 p.m. · 8417a2c · internal Gitea record only
The 'structurally dead on this card' line was wrong and cost real time: ACP never probes hw:0,2, it probes the PCMs the verb declares. Records the five defects, the one-backend-per-front-end rule, and why the earpiece/speaker toggle cannot live in callaudiod here.
- callaudio: add `speaker on|off`10:28 p.m. · f954b73 · internal Gitea record only
Moves a live call between the loudspeaker and the earpiece by writing the two amps directly. Not through alsaucm: enabling a UCM device from a one-shot process means setting the verb first, and re-running the verb EnableSequence mid-call would re-poke the Q6 voice mixers under a live session.
- ucm: split the voice verb into Earpiece, Speaker and Mic devices10:16 p.m. · 3d6f6ec · internal Gitea record only
Calls came out of the loudspeaker with no way back, because the verb had one device named Speaker that carried both directions. callaudiod toggles speakerphone by selecting a sink port (cad-pulse.c:961) and matches ports by type, skipping any card without both a speaker and an earpiece port (:495).
- q6voiced: do not latch a failed open10:00 p.m. · e2c2e15 · internal Gitea record only
tinyalsa's pcm_open() returns a non-NULL handle even when the open failed, and q6voiced_open() stores it before checking, behind an "if (v->tx) return" guard. One failed open therefore silences the whole call.
- callaudio: tear down the voice backends on revert, rename to souveraine-callaudio10:00 p.m. · 4e5dfb5 · internal Gitea record only
The unmap list now covers the QUAT voice mixer and puts MultiMedia1 back on the amps, since a front-end takes one backend and losing it leaves the speakers dead.
- ucm: name the voice verb "Voice Call", give it backends, route downlink to the amps10:00 p.m. · eb48b7a · internal Gitea record only
Three defects, all of which had to be fixed before a call could carry audio both ways:
- blueline-callaudio: unmap the outgoing capture leg on each verb switch6:46 p.m. · b9b2cc5 · internal Gitea record only
alsaucm keeps its UCM context in-process, so a one-shot `set _verb` has no previous verb to dismantle and never runs its DisableSequence. TX5 (HiFi) and TX7 (VoiceCall) both stayed mapped into AIF1_CAP across a call; SLIM_0_TX then carried two channels into a mono front-end and hw:0,1 read exact zeros. The microphone was dead after every call until TX7 was cleared by hand.
- callaudio: reconcile on call state, not on CallDeleted1:57 p.m. · 0bf4cbd · internal Gitea record only
First real call, 2026-07-29: the call terminated at 13:48:40 and ModemManager did not emit CallDeleted until 13:49:11. For those 31s every stream on the device stayed routed into the modem.
- callaudio: package the call-route hook, drop it from the overlay1:36 p.m. · 796308a · internal Gitea record only
The hook and its unit shipped only via rootfs-overlay, so no package owned them and /etc/systemd/system shadowed anything packaged later.
- keyboards: move both into the souveraine- namespace, and own their units9:13 a.m. · cd8b67e · internal Gitea record only
squeekboard sat at extra's exact version, 1.43.1-5, and stevia sat above it, pkgrel 9 against 1. Both positions are bad: equal means pacman cannot tell our fixed build from the broken one it replaces, which is what stranded the /usr/local regression fix; above means a real upstream bump is hidden forever. Under our own names the version is ours. provides/conflicts/replaces take over from the IgnorePkg entry, which stops being load-bearing.
- edge-sense: bind the producer to sessiond, not graphical-session.target9:00 a.m. · 230d7dd · internal Gitea record only
Nothing on this device starts graphical-session.target — greetd runs Hyprland directly and hyprland.lua starts units by name, and this one is not in that list. So the producer was `enabled` and dead from every boot. It ran on 07-28 only because a session started it by hand; that instance was stopped at 21:40:52 and nothing brought it back, which is why grip "stopped working".
- squeekboard: restore meson --prefix=/usr, and guard the prefix8:34 a.m. · 87efdd0 · internal Gitea record only
Dropping arch-meson (it injects host -march/-mtune) also dropped the --prefix=/usr it supplies, so meson used its default /usr/local. All 39 files shipped there: ExecStart=/usr/bin/squeekboard failed 203/EXEC, and the GSettings schema landed where glib never looks, so the paths that did reach the binary died on g_error. Regression from the 2026-07-27 rebuild that went out as 1.43.1-5; 1.43.1-1 shipped usr/bin correctly.
- souveraine11 changes
- Selection: size the window to the chip instead of masking a fullscreen shield6:13 p.m. · 147f7f2 · internal Gitea record only
The masked-fullscreen version was completely untappable on device. Rather than keep guessing at the mask, the window is now sized to the card and moved by margins, so every pixel of it is the chip. Also stops an invisible fullscreen surface sitting over the app's own selection UI.
- Selection: fix untappable chip and the chip that never cleared6:05 p.m. · 5a643d7 · internal Gitea record only
Two bugs from the first on-device run.
- Selection: import qs for GlobalStates5:54 p.m. · 808f063 · internal Gitea record only
Deployed and verified on blueline: watcher starts, a selection settles with an anchor, and the lock gate kills the watcher.
- Selection menu surface: chip that expands to actions (TASK-18)5:47 p.m. · 0bebc77 · internal Gitea record only
Overlay layer following DialHost, keyboardFocus None throughout, and an input mask limited to the card so taps elsewhere reach the app underneath. Anchors to the pointer hint above the touch point, clamped on screen.
- Selection service: compositor-wide primary selection for TASK-185:25 p.m. · 147999a · internal Gitea record only
Verified on blueline that Hyprland advertises primary-selection plus both data-control managers, so wl-paste --primary --watch sees every selection with no per-app hooks. Settles TASK-18's selection-detection question as compositor-level; viewtop is not needed.
- sessiond: delete the fallback login; this daemon locks and never unlocks11:37 a.m. · c8507e9 · internal Gitea record only
It authenticated through PAM, called unlock_and_destroy(), and returned Unlocked — and then the shell, whose GlobalStates.screenLocked is a separate bool, re-registered, was told must_lock=true, and locked again. The protocol has a lock directive and no unlock directive, so the fallback could open the compositor's lock and had no way to tell the session it had. Casey hit it exactly that way: "it logs in, and then qs says Locked still."
- lock: dismiss the boot bloom on a level, not only on an edge10:54 a.m. · 0a635ff · internal Gitea record only
bootBloomActive defaults true on every scene construction and bootDismissed resets with it, but the only thing that cleared them was the secure EDGE. A scene reload during an already-secure lock never moves `secure`, so nothing cleared the bloom and the phone sat under a full-screen white overlay until the shell was restarted. Casey hit it and reported it as "the hyprland error screen"; `grim` returned a solid white 9KB frame, and hyprctl configerrors was empty the whole time.
- shell: "already registered" is a transient, not a verdict10:43 a.m. · 45fbbea · internal Gitea record only
A scene reload re-runs SessiondBridge while the outgoing connection is still open, so the reload's shell_ready races its own predecessor's EOF and sessiond refuses it — correctly, since the lease is genuinely held. The bug was the shell then giving up for good: it read the refusal as "another shell owns the lease, we are not the authority", cleared nothing, and left registerRetry idle. A second later that EOF landed, shell_alive dropped, and sessiond spent the rest of the session believing there was no shell — while a live one sat on a connected socket.
- gestures: osk-switch is packaged now, not a /usr/local stray9:14 a.m. · de64480 · internal Gitea record only
- surfaces: commit the quickshell README, stranded since 07-108:39 a.m. · 8db55cf · internal Gitea record only
It existed only in [host]'s checkout — never added, not gitignored, absent from the laptop. Two sections had gone stale against the code and are corrected in the same change: modules/ is no longer "coming", and deploy.sh composes the whole config rather than swapping Ai.qml.
- shell: recover the phone-only edits into the repo8:27 a.m. · 5965c64 · internal Gitea record only
~/souveraine-surfaces/quickshell on the phone is not a git repo and was the only copy of five files. Brought back verbatim:
- SouveraineOS9 changes
- TASK-18: shipped-partial; detection settled, chip still not tappable6:17 p.m. · d811870 · internal Gitea record only
- TASK-32: record that --phone rsyncs --delete with no snapshot5:55 p.m. · 39c9a02 · internal Gitea record only
Folds in the 2026-07-29 finding instead of a new task file; TASK-32 already owns how these surfaces ship.
- TASK-49: the shell surface has no package, and --phone deletes on-device edits5:48 p.m. · 00d2449 · internal Gitea record only
deploy.sh:246 rsyncs --delete into the phone's surface tree with no snapshot, unlike the local compose which snapshots to .ii-previous. Root cause is installing into $HOME, which has no file-ownership model. Payload belongs in /usr/share owned by a package; $HOME keeps only overrides.
- tasks: 46 is no longer 'never produced a frame'11:57 a.m. · aef29b8 · internal Gitea record only
- task-46: the front camera works — first frame captured, recipe recorded11:57 a.m. · 38c9be0 · internal Gitea record only
- tasks 47/48: auto-brightness is unbuilt; the shell dies on a lock-owing reload10:45 a.m. · 85b172a · internal Gitea record only
- dump 07-29: status — section 8 closed, grip answered, pkgs/ has no CI8:46 a.m. · e421975 · internal Gitea record only
- task-46: the front pair was already wired; the rear IMX363 was the gap8:28 a.m. · 1628915 · internal Gitea record only
- docs: session dump — squeekboard /usr/local prefix regression, pill lift across OSK swap, TASK-38 consolidation6:38 a.m. · daad100 · internal Gitea record only
- souveraine-speech4 changes
- Retire chatterbox; shim :7863 to Qwen3-TTS so the phone keeps working4:51 p.m. · a766683 · internal Gitea record only
chatterbox-ttsd is disabled. The phone points at :7863 in config.toml and the shell config.json and speaks the old {input,voice,model} contract, so tts-compat.py translates it to the OpenAI shape on :7865. Any voice name maps to annie, matching the old fallback-rather-than-400 behaviour.
- Add Qwen3-TTS service: qwentts.cpp/Vulkan on the RX 560, Annie cloned voice4:49 p.m. · 02d2dd9 · internal Gitea record only
Replaces chatterbox for the dashboard. RTF 1.0 vs 3.36, first audio 0.33s vs 43s, so progressive playback works. Runs on :7865; chatterbox stays on :7863 until the phone is repointed. Voice registry is in-memory, so the unit re-registers on start.
- ttsd: revert sentence chunking, set cfm-steps 73:44 p.m. · 19c0e30 · internal Gitea record only
The chunking duplicated tts-cli's own --max-sentence-chars splitting, which already crossfades its segments; ours re-ran the process per chunk and joined separately-encoded mp3s with no crossfade. Reverted.
- ttsd: stream mp3 by sentence chunks2:16 p.m. · 48b5caa · internal Gitea record only
tts-cli is one-shot, so a paragraph meant minutes before the first byte. Split on sentence boundaries and send each piece as it finishes via chunked transfer. 499 chars: first audio at 43s instead of 167s.