Watch
1
0
Fork
You've already forked souveraine
0

shell: dock reorder, fullscreen detection fix, idle-power, sessiond, misc shell work

- Dock drag-to-reorder for pinned apps (insertion gap, quick-slide vs dwell)
- Fullscreen detection: scan all windows via HyprlandData.windowList
- IdleCoordinator, GlobalStates, Session.qml updates
- Deploy script, qmldir, settings, wallpaper, visualizer fixes
- sessiond server, memory module updates

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Fimeg 2026-07-22 22:18:20 -04:00
commit 19fe6b1d0e
59 changed files with 19450 additions and 139 deletions

View file

@ -0,0 +1,160 @@
# Maintainer: Zoddo <archlinux+aur@zoddo.fr>
# Contributor: Thaodan <AUR+me@thaodan.de>
# Contributor: Manuel Hüsers <aur@huesers.de>
# Contributor: huyizheng
# Contributor: johnnyapol <arch@johnnyapol.me>
# Based off the discord community repo PKGBUILD by Filipe Laíns (FFY00) <lains@archlinux.org>
_pkgname=discord
_electron=electron
pkgname=${_pkgname}_arch_electron
pkgver=1.0.146
pkgrel=1
epoch=1
pkgdesc="Discord using system provided ${_electron} for increased security and performance"
arch=('x86_64')
provides=("${_pkgname}")
conflicts=("${_pkgname}")
url='https://discord.com'
license=('LicenseRef-custom')
options=('!strip')
install="$pkgname.install"
depends=("${_electron}" 'libxss')
makedepends=('asar'
'jq'
'python-pyelftools' # Required for Krisp patcher
'python-capstone') # Required for Krisp patcher
optdepends=('libpulse: Pulseaudio support'
'xdg-utils: Open files')
source=("https://dl.discordapp.net/apps/linux/${pkgver}/${_pkgname}-${pkgver}.tar.gz"
'LICENSE.html::https://discord.com/terms'
'OSS-LICENSES.html::https://discord.com/licenses'
'discord-launcher.sh'
'krisp-patcher.py'
# Discord modules (from 'curl "https://updates.discord.com/distributions/app/manifests/latest?channel=stable&platform=linux&arch=x64"')
"core-${pkgver}.tar.br::https://stable.dl2.discordapp.net/distro/app/stable/linux/x64/${pkgver}/full.distro"
"discord_desktop_core-${pkgver}.tar.br::https://stable.dl2.discordapp.net/distro/app/stable/linux/x64/${pkgver}/discord_desktop_core/1/full.distro"
"discord_zstd-${pkgver}.tar.br::https://stable.dl2.discordapp.net/distro/app/stable/linux/x64/${pkgver}/discord_zstd/1/full.distro"
"discord_krisp-${pkgver}.tar.br::https://stable.dl2.discordapp.net/distro/app/stable/linux/x64/${pkgver}/discord_krisp/1/full.distro"
"discord_rpc-${pkgver}.tar.br::https://stable.dl2.discordapp.net/distro/app/stable/linux/x64/${pkgver}/discord_rpc/1/full.distro"
"discord_utils-${pkgver}.tar.br::https://stable.dl2.discordapp.net/distro/app/stable/linux/x64/${pkgver}/discord_utils/1/full.distro"
"discord_voice-${pkgver}.tar.br::https://stable.dl2.discordapp.net/distro/app/stable/linux/x64/${pkgver}/discord_voice/1/full.distro"
"discord_game_utils-${pkgver}.tar.br::https://stable.dl2.discordapp.net/distro/app/stable/linux/x64/${pkgver}/discord_game_utils/1/full.distro"
"discord_erlpack-${pkgver}.tar.br::https://stable.dl2.discordapp.net/distro/app/stable/linux/x64/${pkgver}/discord_erlpack/1/full.distro"
"discord_modules-${pkgver}.tar.br::https://stable.dl2.discordapp.net/distro/app/stable/linux/x64/${pkgver}/discord_modules/1/full.distro"
"discord_spellcheck-${pkgver}.tar.br::https://stable.dl2.discordapp.net/distro/app/stable/linux/x64/${pkgver}/discord_spellcheck/1/full.distro"
"discord_dispatch-${pkgver}.tar.br::https://stable.dl2.discordapp.net/distro/app/stable/linux/x64/${pkgver}/discord_dispatch/1/full.distro")
sha512sums=('b90cf25a6e615e836e0ef248b3090f23d90c2a6d73faeb21954ba669fb39275c26ac58a8f9e92445254c29fb90716cab54bee06f701cde88f74578c4228bc22b'
'2590151db4404a9ecbae2c45269c74f5ea26479967de5bc0b221a9143f4efeeeda9e335c23cf1f982680ab4fd895dcf3fd5731bd230908b3b2568eee0ec223ec'
'5cccf397a772fed0db9b19253e496e78af27810227325ce681de985e2e0fbbdb195873d9642574ad3ae7bfcf4699bfdaa16933ca50c13cb98a1ff53c86c6de05'
'd5373e2b2e9754bdcdbcda81fc1392ce6605939651839868823f3c3e0bf16f6fba2fb73bc1a0ff230eebd743d1463f2e8b2295503248f14845b3a56e57385bd1'
'85da93530b2b92faad58cc84d0f7ddaea6b22fbec413e806c1981eb6369a301ec995eee6bfbe6b828e2cd0baf6168e578bbde456f2450a13199994ff9a1e7cd0'
'dca9bbf55afcb1425af7bd0fc4d891fe0cc37859436a8b2a1ae9d29689d72e574dc2f974d468a86527c0420847fd27af5517da7b02075ee2eebe8bd2d43a7d09'
'88024b0faf4e4db8e283af9081b88072ec8ab908e4b72a7c0bab8ae7fcb3c5ae09b2b3e34af0b35429b51f46be359dc39983191e376a762c6c4831d4918a9792'
'b7a676bd7e7cb1a9d57aaad1c828499f7ecdc0360bb982a3138181bb29710354509c50998344004c73ca2b87f7866a603109c2cb79b848b3240fa08a35578b16'
'7283eb04ec779a5f679e7c90f793c5d9bb2a6a44507f966156d831f0013ebcad507fbcc07d037719740861f3e23f95a532b5c9d0da707932a0da9ffd7e17349d'
'6e75733d93b41b4a3d59749ce5331b9da4d6fb5fad1266bf639a948cc497c05b03ea3f15bfd6384e819730bd220dbf34dbb967c90f9683bb2529f73a4ed5897b'
'e88e7ce27b6d406d1fe631964b772067bc52808a74ecb57a96b5585199ec2aceab405321f9d9faf9199870fd83823afc6c5402eb85c9ed41826cedb60d8925d9'
'fc5dd9840f0b13bf8b66dc4901833c701293a914c54765c04c18414755f237284e93e1a7bf2660120507263500f9ab385c1c9966fe0f33e368b3fae9d695b432'
'f23b41b7e55c6611a77767c4ccd7010bfc5a901d241ac91826b191ca7d894e5960eda1afde05a0b52564b555006ce762620b08c9378eff733a0b89c5fd49a3e4'
'755e37330fc16d99f630ed0036eaabf83b4f137123c72eae35fe47996d243ceaed1ddc6f1891efaeffb3f28fa4c0eb9b2be76308df50d98a97be24c37f5e41f0'
'83454dd202685f368d0934c3949e2d3e2ecb30dd89c7a593f9210318828cd3b3b4813e216e45bcc1752b0b0d214f1979bf7ba14d00e0840e0c238cfeec88aa63'
'aee5b9cd718d803618e6f83f4f004853f283e89c5c8bc87b61b85189787aed0c710bba2b9ab81e148e18c685e5a1bcbe18554cc67f6123be360c3299349d0edc'
'959e30368d9c4d4e82fe4beb2457f3287365e1fcb3ee5e44eb8d8e648e30b2b2ec2aa346b7bfa46d24b8f496193b154c89c471c997fe9d7d5b64277dcc5092ca')
# Skip "LICENSE.html" and "OSS-LICENSES.html" files hashes as they are unstable
# Since "updpkgsums"/"pkgctl version upgrade" overwrite the checksum array with
# literal hashes, set them to SKIP with indexed assignments (pacman-contrib#119)
# https://gitlab.archlinux.org/pacman/pacman-contrib/-/issues/119
sha512sums[1]='SKIP'
sha512sums[2]='SKIP'
# Colored makepkg-like functions
_all_off="$(tput sgr0)"
_bold="${_all_off}$(tput bold)"
_blue="${_bold}$(tput setaf 4)"
_red="${_bold}$(tput setaf 1)"
msg_blue() {
printf "${_blue} ->${_bold} $1${_all_off}\n"
}
error() {
printf "${_blue}==>${_red} ERROR:${_bold} %s${_all_off}\n" "$1" >&2
exit 1
}
prepare() {
msg_blue "Extracting core-${pkgver}.tar.br"
tar -xf core-${pkgver}.tar.br --use-compress-program=brotli --one-top-level=core --strip-components=1 files/
for module in discord_*.tar.br; do
msg_blue "Extracting ${module}"
tar -xf "${module}" --use-compress-program=brotli --one-top-level="modules/${module%%-*}" --strip-components=1 files/
done
# prepare launcher script
sed -i -e "s|@PKGNAME@|${_pkgname}|" \
-e "s|@ELECTRON@|${_electron}|" \
discord-launcher.sh
# fix the .desktop file
sed -i -e "s|Exec=.*|Exec=/usr/bin/${_pkgname}|" ${_pkgname^}/$_pkgname.desktop
}
build() {
msg_blue 'Patching app.asar to use system electron'
asar e core/resources/app.asar core/resources/app
rm core/resources/app.asar
sed -i \
-e "s|^Exec=\${exePath}$|Exec=/usr/bin/${_pkgname}|" \
-e "s|^Name=\${appName}$|Name=${_pkgname^}|" \
-e "s|^Icon=\${iconPath}$|Icon=${_pkgname}|" \
-e "s|resourcesPath=path_1\.default\.join(require\.main\.filename,\"\.\.\",\"\.\.\"),|resourcesPath='/usr/share/${_pkgname}/resources',|" \
-e "s|process\.resourcesPath|'/usr/share/${_pkgname}/resources'|g" \
core/resources/app/bundle.js
sed -i -e "s|process\.resourcesPath|'/usr/share/${_pkgname}/resources'|" core/resources/app/splashScreenPreload.js
# This is required to properly show the window icon under wayland
jq ".desktopName = \"${_pkgname}.desktop\"" core/resources/app/package.json > tmp.json
mv tmp.json core/resources/app/package.json
asar p core/resources/app core/resources/app.asar
rm -rf core/resources/app
# Set the newUpdater key to false in build_info.json in order to disable the new Discord updater (released on 2026-05-04, version 1.0.136),
# Also add a localModulesRoot key that points to the Discord modules we're packaging.
jq ".newUpdater = false | .localModulesRoot = \"/usr/share/${_pkgname}/modules\"" core/resources/build_info.json > tmp.json
mv tmp.json core/resources/build_info.json
msg_blue 'Patching Krisp to run with system electron'
# original: https://github.com/sersorrel/sys/blob/main/hm/discord/krisp-patcher.py
python krisp-patcher.py "modules/discord_krisp/discord_krisp.node" \
|| error 'Krisp patcher failed. You can comment it out in the PKGBUILD, but Krisp will not work.'
# Krisp fails to initialize if this directory is not present (or if can't be created). Logs:
# [MediaEngineStore] Failed to load Krisp module: Failed to setup Krisp module, error code: -4
# [AVError] AV error reported: noise-canceller-error {"underlyingError":"NoiseCancellerError.KRISP_INIT_ERROR_GLOBAL_INIT"}
mkdir -p "modules/discord_krisp/KMS/logs"
}
package() {
# create necessary directories
install -d "${pkgdir}/usr/share/${_pkgname}"
# copy relevant data
cp -r core/resources/ "${pkgdir}/usr/share/${_pkgname}/"
cp -r modules/ "${pkgdir}/usr/share/${_pkgname}/"
# intall icon and desktop file
install -Dm 644 ${_pkgname^}/$_pkgname.png "${pkgdir}/usr/share/icons/hicolor/256x256/apps/${_pkgname}.png"
install -Dm 644 ${_pkgname^}/$_pkgname.desktop "${pkgdir}/usr/share/applications/${_pkgname}.desktop"
# install the launch script
install -Dm 755 discord-launcher.sh "${pkgdir}/usr/bin/${_pkgname}"
# install licenses
install -Dm 644 LICENSE.html "${pkgdir}/usr/share/licenses/${pkgname}/LICENSE.html"
install -Dm 644 OSS-LICENSES.html "${pkgdir}/usr/share/licenses/${pkgname}/OSS-LICENSES.html"
}

View file

@ -0,0 +1,104 @@
# Task: Official Discord + BetterDiscord on SouveraineOS (Pixel 3, aarch64)
**Status:** BLOCKED on native Electron. Plan: cross-build Electron (and/or the Discord
package) on an x86 box or a beefier arm64 host, ship the artifacts back to the phone.
**Goal:** Run the *official* Discord desktop client on this device and inject
BetterDiscord into it (theming + plugins). Wrappers (Vesktop/WebCord/Legcord) were
explicitly rejected — we want stock Discord + BetterDiscord specifically.
---
## Device facts (verified on-device 2026-07-14)
- OS: **Souveraine OS (Arch Linux ARM)**, `ID_LIKE=arch`, BUILD_ID 20260707
- Arch: **aarch64**
- Pkg mgrs: `pacman`, `flatpak` (flathub system remote). AUR helpers present: `yay`, `paru`.
- Repo `bun` NOT present; installed manually to `~/.bun/bin/bun` (v1.3.14 aarch64).
- `~/.config/discord` exists but is EMPTY (no app version dir) — nothing to inject into yet.
### What's in the repos (checked with pacman -Ss)
- ✅ `extra/betterdiscordctl 1.6.1-1` — BD manager CLI, installable, needs a Discord install to act on
- ✅ `extra/asar 4.1.2-1` — makedep for the Discord PKGBUILD
- ✅ `jq`, `brotli` — present
- ❌ **NO `electron` package in any form** (no `electron`, no `electron36/37/...`).
`pacman -Ssq electron` returns nothing. THIS IS THE BLOCKER.
### Flathub (all bundle their own Electron → run on arm64, but can't take BetterDiscord)
Vesktop `dev.vencord.Vesktop`, Equibop `org.equicord.equibop`, WebCord
`io.github.spacingbat3.webcord`, Dissent `so.libdb.dissent`, Legcord, GoofCord.
(Vesktop/Equibop have Vencord = plugins+themes, the closest BD-equivalent. Rejected for now.)
---
## Already done (in /home/casey/projects/BetterDiscord)
BetterDiscord cloned from GitHub + built successfully:
- `git clone --depth 1 https://github.com/BetterDiscord/BetterDiscord.git`
- `bun install` (510 pkgs) then `bun run build:production` → **succeeded**
- Artifacts in `dist/`: `betterdiscord.js` (555kb), `main.js`, `preload.js`,
`earlyRenderer.js`, editor bundle. Version **1.13.14**, commit `c7ef1c5`.
- `bun run inject` → fails as expected: "No version dir in /home/casey/.config/discord"
(no official Discord app to patch).
Injector expects a Discord Desktop install dir like `app-X.Y.Z` under the config path.
---
## The install path (why it's *almost* possible)
The AUR pkg **`discord_arch_electron`** (v1:1.0.146, 209 votes) is the target. Its PKGBUILD
(saved alongside this file as `PKGBUILD.x86-reference`) shows Discord's Linux app is
**JavaScript + app.asar + brotli `.distro` module blobs — arch-independent**. The ONLY
native pieces are:
1. **Electron** (the runtime) — pulled via `depends=(electron)`. ← the real arch dependency
2. **`discord_krisp/discord_krisp.node`** — x86-64 native noise-cancel lib; its
`krisp-patcher.py` step WILL fail on aarch64. Krisp is OPTIONAL — comment it out,
Discord still runs (PKGBUILD even says so).
So a working aarch64 build = **provide an arm64 Electron** + **skip Krisp** + flip `arch`.
### Concrete cross-build plan
1. Get/build an **aarch64 Electron** matching Discord's required major (Discord 1.0.146
era ships on a recent Electron — check `core/resources/app/package.json` `electronVersion`
or the bundled build_info after extracting the tarball). Options:
- Build the Arch `electron` PKGBUILD for aarch64 on a cross host (heavy: Chromium).
- OR reuse a prebuilt arm64 Electron (e.g. from npm `electron` arm64 dist, or Alpine/Debian
arm64 electron) and repackage.
2. Fork the `discord_arch_electron` PKGBUILD:
- `arch=('x86_64' 'aarch64')`
- point `depends` at whatever electron pkg you produce (or vendor it)
- COMMENT OUT the Krisp patcher block in `build()` (lines ~131-139) and the
`discord_krisp` source line — OR guard it `[[ $CARCH == x86_64 ]]`.
- The `.distro` module URLs are `/linux/x64/` paths but contents are JS/resources;
they should work under arm64 electron. Verify at runtime.
3. `makepkg -si` on-device (or build the pkg on the cross host and `pacman -U` the .pkg.tar.zst here).
4. Once `/usr/share/discord` + `/usr/bin/discord` exist and launch:
- `betterdiscordctl install` (repo pkg already available) — OR —
- from `~/projects/BetterDiscord`: `bun run inject` (now finds the app dir).
5. Launch Discord, confirm BetterDiscord loaded (Settings → BetterDiscord panel).
### Risks / open questions
- Electron major-version mismatch vs what Discord's app.asar expects → white screen / API breakage.
Match the major as closely as possible.
- Voice: without Krisp, noise suppression off, but voice/WebRTC should still work (Electron's).
- Discord may nag to "update" — PKGBUILD already sets `newUpdater=false` in build_info.json; keep that.
- app.asar is repacked to use system electron paths (`/usr/share/discord/resources`); BetterDiscord
injects into that asar/app — make sure BD inject runs AFTER the pkg's asar repack, against the
installed `/usr/share/discord/resources/app.asar`.
---
## Resume checklist (fast path)
- [ ] Produce aarch64 Electron artifact (matching Discord's electron major)
- [ ] Fork PKGBUILD: arch+=aarch64, swap electron dep, disable Krisp
- [ ] Build pkg (cross or on-device), install with pacman
- [ ] `discord` launches to login screen on the phone
- [ ] Inject BetterDiscord (`betterdiscordctl install` or `bun run inject` from ~/projects/BetterDiscord)
- [ ] Verify BD settings panel appears; load a theme/plugin
## Key paths
- BetterDiscord source+build: `/home/casey/projects/BetterDiscord` (dist/ built, v1.13.14)
- This task dir: `/home/casey/projects/unfinished/discord-betterdiscord/`
- Reference PKGBUILD: `./PKGBUILD.x86-reference`
- bun binary: `~/.bun/bin/bun`

View file

@ -0,0 +1,24 @@
#!/usr/bin/env bash
set -euo pipefail
name=@PKGNAME@
electron=@ELECTRON@
# Support .config/discord-flags.conf file
flags_file="${XDG_CONFIG_HOME:-$HOME/.config}/${name}-flags.conf"
if [[ -r "${flags_file}" ]]; then
mapfile -t < "${flags_file}"
fi
declare -a flags
for line in "${MAPFILE[@]}"; do
if [[ ! "${line}" =~ ^[[:space:]]*#.* ]] && [[ -n "${line}" ]]; then
flags+=("${line}")
fi
done
exec /usr/bin/${electron} \
/usr/share/${name}/resources/app.asar \
"${flags[@]}" "$@"

View file

@ -0,0 +1,16 @@
post_upgrade() {
# return if old package version is greater than or equal to 1:1.0.143-1...
(( $(vercmp $2 '1:1.0.143-1') >= 0 )) && return
cat <<EOF
==> Krisp (noise suppression) is now patched at build time.
If you used '~/.config/discord.conf' previously to enable it, you can remove the file as it's not used anymore.
EOF
}
post_install() {
cat <<EOF
==> In case of performance problems consider setting additional flags
in '~/.config/discord-flags.conf'.
EOF
}

View file

@ -0,0 +1,85 @@
import sys
import shutil
from elftools.elf.elffile import ELFFile
from capstone import *
from capstone.x86 import *
if len(sys.argv) < 2:
print(f"Usage: {sys.argv[0]} [path to discord_krisp.node]")
# "Unix programs generally use 2 for command line syntax errors and 1 for all other kind of errors."
sys.exit(2)
executable = sys.argv[1]
elf = ELFFile(open(executable, "rb"))
symtab = elf.get_section_by_name('.symtab')
krisp_initialize_address = symtab.get_symbol_by_name("_ZN7discordL17DoKrispInitializeEv")[0].entry.st_value
isSignedByDiscord_address = symtab.get_symbol_by_name("_ZN7discord4util17IsSignedByDiscordERKNSt4__Cr12basic_stringIcNS1_11char_traitsIcEENS1_9allocatorIcEEEE")[0].entry.st_value
text = elf.get_section_by_name('.text')
text_start = text['sh_addr']
text_start_file = text['sh_offset']
# This seems to always be zero (.text starts at the right offset in the file). Do it just in case?
address_to_file = text_start_file - text_start
# Done with the ELF now.
# elf.close()
krisp_initialize_offset = krisp_initialize_address - address_to_file
isSignedByDiscord_offset = krisp_initialize_address - address_to_file
f = open(executable, "rb")
f.seek(krisp_initialize_offset)
krisp_initialize = f.read(96)
f.close()
# States
found_issigned_by_discord_call = False
found_issigned_by_discord_test = False
found_issigned_by_discord_je = False
found_already_patched = False
je_location = None
je_size = 0
# We are looking for a call to IsSignedByDiscord, followed by a test, followed by a je.
# Then we replace the je with nops.
md = Cs(CS_ARCH_X86, CS_MODE_64)
md.detail = True
for i in md.disasm(krisp_initialize, krisp_initialize_address):
if i.id == X86_INS_CALL:
if i.operands[0].type == X86_OP_IMM:
if i.operands[0].imm == isSignedByDiscord_address:
found_issigned_by_discord_call = True
if i.id == X86_INS_TEST:
if found_issigned_by_discord_call:
found_issigned_by_discord_test = True
if i.id == X86_INS_JE:
if found_issigned_by_discord_test:
found_issigned_by_discord_je = True
je_location = i.address
je_size = len(i.bytes)
break
if i.id == X86_INS_NOP:
if found_issigned_by_discord_test:
found_already_patched = True
break
if je_location:
print(f"Found patch location: 0x{je_location:x}")
f = open(executable, 'rb+')
f.seek(je_location - address_to_file)
f.write(b'\x90' * je_size) # je can be larger than 2 bytes given a large enough displacement :(
f.close()
else:
if found_already_patched:
print("Couldn't find patch location - already patched.")
else:
print("Couldn't find patch location - review manually. Sorry.")
sys.exit(1)

View file

@ -0,0 +1,122 @@
# Home Directory Homogenization Plan
**Date:** July 22, 2026
**From:** Ani
**To:** Souveraine Surface Agent / Casey
---
## Situation
`/home/casey` has accumulated deployment artifacts, build directories, duplicate folders, and debugging debris from the Souveraine OS bring-up on Pixel 3. This document inventories everything and proposes cleanup actions.
---
## Inventory & Classification
### ✅ KEEP — Active Source & Configuration
| Path | Size | Notes |
|------|------|-------|
| `Projects/` (capital) | 3.0G | **Canonical projects root.** BetterDiscord, Quaternion, claude-switcher, converse, end4dots, qtcontacts-sqlite, qtpim |
| `projects/` (lowercase) | 332M | **Symlink target → `Projects/`** (after consolidation) |
| `souveraine-surfaces/` | 7.6M | Souveraine shell source |
| `bootsplash/` | 2.8M | Boot splash source (C, shaders, atlas) |
| `stevia-pkg/` | 21M | Patch set for phoc/zphoc, dictation key, vk driver, layout fixes |
| `.config/` | — | All system config including `illogical-impulse/config.json` (TTS/STT endpoints) |
| `.souveraine/` | — | Souveraine agent state |
| `cleancap.sh` | 1.2K | Screen capture cleanup script |
| `mic-hunt.sh` | 1.5K | Microphone debugging script |
| `converse-primary.bundle` | 161K | Git bundle for converse source |
| `hexagonrpc-src.tar.gz` | 121K | Hexagon RPC source backup |
| `hexagonrpc-stubs.tar.gz` | 387K | Hexagon RPC stubs backup |
| `los-sensors-json.tar.gz` | 7.2K | LineageOS sensor JSON backup |
| `.ssh/` `.gitconfig` | — | Git/SSH configuration |
### 🗑️ ARCHIVE OR DELETE — Build Artifacts & Debris
| Path | Size | Classification | Action |
|------|------|----------------|--------|
| `build/` | 3.3G | phosh, phrog, libcall-ui, hexagonrpc, libgnome-volume-control, libical patch — **all build output** | **DELETE** — source lives in `Projects/` and `stevia-pkg/` |
| `chatty-build/` | 141M | Build output for chatty | **DELETE** — source is elsewhere |
| `aacs-build/` | 59M | Build output | **DELETE** |
| `openauto-bin/` | 108M | Binary output | **DELETE** or archive |
| `openauto-build/` | 8.6M | Build output | **DELETE** |
| `libcmatrix-build/` | 16M | Build dirs | **DELETE** |
| `upower-build/` | bring_up_build | Build output | **DELETE** |
| `bootsplash-build/` | 3.1M | Build output | **DELETE** |
| `sns-src/` `sns-out/` `sns-out2/` `sns-final/` | ~2M total | Sensor registration experimentation (bmi160 accelerometer data) | **DELETE** — one-shot debugging, no ongoing value |
| `mic-hunt-095138/` | 136K | Mic debugging session capture (dmesg, regmap trace, capture.wav) | **DELETE** — one-shot debugging |
| `lockup-2026-07-11/` | 1.4M | Lockup debug logs (dmesg, journal) from July 11 | **ARCHIVE** then delete — useful for diagnosing future lockups, but not needed live |
| `dock-backup-tier1-2054/` | 60K | Dock config backup | **DELETE** — superseded by current dock config |
| `staging/` | 21M | `souveraine-da410b1` staging build | **DELETE** — deployment staging, no longer needed |
| `mimo/` | 339M | **`Shelly-ALPM`** — needs investigation | **INVESTIGATE** — 339M is significant |
| `qbootctl-src/` | 1.0M | qbootctl source | **KEEP** (source) or **DELETE** if already in Gitea |
| `hexagonrpc/` | 2.5M | hexagonrpc built output? | **DELETE** if build output, **KEEP** if source |
| `hexagonrpc-src/` | 1.5M | hexagonrpc source | **KEEP** if not in Gitea, otherwise **DELETE** |
| `regsnap-after-unlock.txt` | 576K | Regmap snapshot debugging | **DELETE** — one-shot debug |
| `regsnap-before-unlock.txt` | 576K | Regmap snapshot debugging | **DELETE** — ones-shot debug |
| `souveraine.log` | 72K | Current runtime log | **KEEP** |
| `souveraine.log.prev` | 2.2K | Previous runtime log | **KEEP** (for debugging) |
### 📦 ROOT-LEVEL FILES
| Path | Size | Classification |
|------|------|----------------|
| `linux-blueline-7.1.1-1-aarch64.pkg.tar.zst` | 31M | Compiled kernel package for Pixel 3. **KEEP** until next kernel build — provides rollback |
| `roll-out / phased-git-checkout-marker` | — | — |
| `dock-rework-backup-1952.tar.gz` | 7.2K | Dock backup from Jul 11. **DELETE** — superseded |
| `clseldean-completions.zsh@` | — | Symlink → `Projects/claude-switcher/claude-completions.zsh` |
| `.claude-modes.zsh@` | — | Symlink → ` directory |
| `clsaldean-modes.bash` `.claude-modes.bash.bak` | — | Claude CLI mode configs |
| `.claude.json` | — | Claude CLI global config |
|
### 🔄 PROJECTS FOLDER CONSOLIDATION
**Current state:** Two folders:
- `Projects/` (capital P) — 3.0G, modified Jul 18
- `projects/` (lowercase) — 331M, modified Jul 14
**Step 1:** Merge `projects/unfinished/` → `Projects/unfinished/` ✅ (already done by Ani earlier today)
**Step 2:** Merge newer `projects/BetterDiscord/` → `Projects/BetterDiscord/` (fresher dist build)
**Step consolidation:** Remove lowercase `projects/`, symlink `projects` → `Projects`
**Step 4:** Verify `.claude-completions.zsh` and `.claude-modes.zsh` symlinks still resolve
**Estimated space recovery:** ~4.0G from build artifacts + debris cleanup
### ❓ NEEDS INVESTIGATION
**`mimo/` (339M)** — Contains `Shelly-ALPM`. Unknown purpose. Could be:
- Mimo display driver work
- Mobile Input/Modem Output experimentation
- Something else entirely
- Needs Casey to identify before deletion
---
## Proposed Execution Order
1. **Projects folder consolidation** (in progress)
2. **Delete build artifacts** (`build/`, `*-build/`, `openauto-bin/`, `*-out*/`, `staging/`)
3. **Delete one-shot debug** (sns-*, mic-hunt, regsnaps, dock backup)
4. **Archive then delete** lockup logs
5. ~~**Investigate `mimo/`** before action~~
6. **Review remaining root-level files** (tarballs, tar.zst, bundles) — keep source backups until confirmed in Gitea
6. **Final verification** — run `du -sh /home/casey/*/ | sort -rh` to confirm cleanup
7. **Update this document** with final results
---
## Open Questions for Souveraine Agent / Casey
1. **Gitea location:** No Gitea references found in config, bash history, or git config. Where is the actual Gitea instance? Need the URL to verify what's already under version control.
2. **`mimo/` (339M):** What is `Shelly-ALPM`? Is this active work?
3. **`qbootctl-src/` and `hexagonrpc-src/`:** Are these in Gitea? If so, the local copies are redundant.
4. **Kernel package (`linux-blueline-7.1.1-1-aarch64.pkg.tar.zst`):** Keep for rollback, or move to shared storage?
5. **`converse-primary.bundle`:** This is a git bundle — should it be unpacked into `Projects/` or pushed to Gitea?
---
*Authored by Ani, July 22, 2026, from Pixel 3, Souveraine OS*

View file

@ -0,0 +1,191 @@
# Pixel 3 (blueline) — microphone / SLIM TX capture bug
**Status:** 2026-07-18 — BUG HAS MOVED. Transport is now FIXED; still zeros.
The `CONNECT_SRC` timeout the rest of this doc chases **no longer happens**
(kernel is now `g8cbfbb939327`, was `g56b0a053bef8` when that was found).
Port-connect, channel activate, and q6afe port_start all succeed (ret=0), yet
capture is still pure zeros. The fault is now DOWNSTREAM of transport, in the
WCD934x SLIM-PGD data-plane. See "## 2026-07-18 UPDATE" below; the older
sections are kept for history but their root-cause conclusion is superseded.
## 2026-07-18 UPDATE — transport fixed, data-plane is the new fault
Re-ran on a clean 58-min-uptime boot, ADSP never restarted. Findings:
- **The SLIM CONNECT_SRC (MC 0x2c) timeout is GONE.** Every capture now logs
`connect_port: dir=1 port_id=N ch_id=NNN la=0xcf ret=0`,
`DEF_ACT_CHAN xfer ret=0`, and q6afe `port_start OK` (port 0x4001,
cfg 0x10212). Zero `timed out` / `failed:-110` lines in dmesg.
- **Still 0/144000 nonzero samples** on every path tried:
- DMIC4 → DEC7 → TX7 (enchilada recipe)
- DMIC0 → DEC6 → TX6 (handset, via cleancap.sh)
- **AMIC1 → ADC1 → DEC0 → TX0 (analog!) — also zeros.**
So it is NOT DMIC-specific and NOT a DMIC-clock problem. Common to every
decimator and every TX port.
- **Decimator path is fully powered and unmuted:** `decN POST_PMU`
`TX_PATH_CTL=0x24 [clk_en=1 mute=0 rate=0x4]`, DEC6/DEC7 Volume = 84 (0 dB),
MICB on. DAPM all On. So routing/gain/mute are ruled out.
- **The smoking gun is now IRQ 213.** `/proc/interrupts` line
`213: 0 ... wcd934x_irq 0 Edge slim` — the WCD934x's own SLIM/data
interrupt has fired **0 times in the machine's entire life**, including
across capture. (Contrast IRQ 212 `wcd934x_irq` control line = 18, so the
codec IRQ domain is alive; button-detect 215 = 17.) The codec arms its TX
PGD port (`PGD_TX_INT_STATUS=0x0000`, port `STATUS=0x00`) but never raises
the data interrupt → port never actually streams → ADSP reads zeros.
### New working hypothesis
Control-plane port *connect* (NGD user messages) succeeds, but the WCD934x
**SLIM PGD port is never *enabled*/started** on the data side — the register
write that arms the port's data engine and its interrupt
(`SLIM_PGD_PORT_INT_EN*`, per-port `PORT_CFG`/enable) isn't happening or isn't
taking. That's why IRQ 213 stays 0 and the port emits zeros. This is a
codec-side (wcd934x.c) or CDC_IF data-lane issue, NOT an ADSP/satellite issue.
### The instrumentation gap (next patch)
The current MICDBG patch logs the decimator, connect_port, and DEF_ACT_CHAN,
but does NOT dump the SLIM PGD port-enable / port-interrupt registers — the
exact layer where the bug now lives. Next kernel patch should log, right after
"prepare CAPTURE armed":
- `WCD934X_SLIM_PGD_PORT_INT_EN0` (0x341?) and INT_EN for the TX port
- per-port `SLIM_PGD_TXn_CFG` / port enable bit
- `SLIM_PGD_PORT_INT_SRC` / `_STATUS` DURING an active stream (not just at
prepare — need a delayed read or a sysfs poke)
Then check: is the port-enable bit actually set? Is IRQ 213 masked at the
codec? Compare this whole sequence against enchilada (mic works) on 6.x.
### Verified-safe method notes (this session)
- Never touched remoteproc; ADSP stayed `running` throughout.
- `alsactl restore 0` does NOT fully clear AIF*_CAP SLIM TXn mixer bits —
stale TX routes leak into the next run (saw map=[134 128 ...] = TX6+leftover
TX0). Explicitly `cset ... 0` every AIF*_CAP SLIM TXn you touched.
- `cleancap.sh` (in ~) is the good harness: forces TX7 off (its comment flags
TX7 as an "Android-replay wedge trigger"), sets the DMIC0/TX6 handset path,
and measures IRQ 213 delta + nonzero-sample count. Use/extend it.
---
## (Historical — superseded) original 2026-07-12 root-cause
**Status:** ROOT CAUSE LOCALIZED 2026-07-12 — not fixed yet.
All capture sources record pure digital zeros because the SLIMbus
`CONNECT_SRC` port-connect message to the ADSP master **times out**, so the
WCD9340's TX port is never attached to the data channel. The DSP then
delivers a valid-but-empty stream. Control traffic (codec register
reads/writes over slimbus) works fine.
**(As of 2026-07-18 this timeout no longer occurs — see UPDATE above.)**
## The smoking gun
With dynamic debug on `drivers/slimbus/*`, every capture start logs 3×:
```
qcom,slim-ngd qcom,slim-ngd.1: TX timed out:MC:0x2c,mt:0x2
qcom,slim-ngd qcom,slim-ngd.1: Tx:MT:0x2, MC:0x2c, LA:0xcf failed:-110
```
- `MC 0x2c` = `SLIM_USR_MC_CONNECT_SRC` (qcom satellite user message that
connects the codec TX port to the allocated data channel)
- Value-element messages (`MC 0x60` REQUEST_VALUE / `0x68` CHANGE_VALUE —
i.e. regmap traffic to the codec) all succeed with no timeouts
- So: NGD hardware messaging works; the ADSP-side slimbus master/satellite
service does not answer port-connect requests
- TODO: identify LA 0xcf — codec device or the ifd (ports live on the
interface device `ifd@0,0`)?
Enable debug: `echo 'file drivers/slimbus/* +p' > /sys/kernel/debug/dynamic_debug/control`
## What was proven (2026-07-12, kernel 7.1.1-sdm845-g56b0a053bef8)
1. Capture streams open and run fine on hw:0,1 (MultiMedia2) and hw:0,0
(MultiMedia1) routed from `SLIMBUS_0_TX` — but 100.0% of samples are
exactly 0 (not noise floor; digital silence).
2. Swept EVERY input: AMIC ADC1-4 (via ADC MUX0→DEC0→SLIM TX0) and
DMIC0-5, plus the enchilada-style TX7 slot layout
(DMIC MUX7=DMIC4 → ADC MUX7=DMIC → CDC_IF TX7 MUX=DEC7 →
AIF1_CAP Mixer SLIM TX7). All zeros.
3. DAPM (debugfs) during capture: entire codec path reports **On**
(ADC, AMIC/ADC muxes, SLIM TX0 mixer, MIC BIAS1). So it's not routing.
4. DT declares 4 mics (`/proc/device-tree/sound/audio-routing`):
AMIC1←MIC BIAS1, DMIC0←MIC BIAS1, DMIC3←MIC BIAS3, DMIC4←MIC BIAS4.
Codec node has ifd@0,0 + codec@1,0 + slim-ifc-dev; micbias 1.8 V ×4;
extclk 9.6 MHz. Structurally like working devices.
5. **pmOS never solved this either**: their blueline UCM
(gitlab.com/sdm845-mainline/alsa-ucm-conf, ucm2/Google/blueline) has the
whole mic section *commented out*. Their intended bottom-mic path:
DMIC4 → DEC7 → SLIM TX7 → AIF1_CAP → SLIMBUS_0_TX → MultiMedia2,
CaptureMixerElem "DEC7".
6. OnePlus 6 (enchilada) mic **works** on pmOS 6.x kernels with the same
driver stack (AMIC4, TX7/DEC7, `AMIC4_5 SEL=AMIC4`) → this is either a
6.x→7.x regression in slimbus/ngd/wcd934x, or blueline-specific
(ADSP firmware? satellite service difference?).
7. WCD RX (playback) direction could NOT be verified: EAR PA powers on and
streams (path: `SLIMBUS_0_RX Audio Mixer MultiMedia2`, `SLIM RX0 MUX=
AIF1_PB`, `RX INT0_1 MIX1 INP0=RX0`, `RX INT0 DEM MUX=CLSH_DSM_OUT` —
note EAR hangs off **RX INT0**, not INT8), but no sound heard — and the
Pixel 3 has NO WCD-driven transducer to hear: both front speakers incl.
the "earpiece" are CS35L36s on QUAT_TDM, and there's no 3.5mm jack.
Inconclusive by hardware design. Whether CONNECT_SINK (MC 0x2d) also
times out is STILL UNKNOWN — never got a clean dmesg on an RX stream.
8. QUAT_TDM_TX (would allow CS35L36 VMON/IMON digital loopback = objective
transport test) is not a wired backend in the DT — stream open fails
EINVAL. Could be added in DT to get an ear-free TDM capture reference.
## Hazards learned (do not repeat)
- **Do NOT restart the adsp remoteproc while debugging**: slim-ngd fails to
re-handshake ("QMI wait timeout", no new "Rcvd master capability") and
slimbus stays degraded until full reboot. Happened twice, reproducible.
- Don't leave MultiMedia2 with BOTH `SLIMBUS_0_RX` (playback) and
`SLIMBUS_0_TX` (capture) routes enabled — ADM open starts failing
(q6adm cmd 0x10325 error 2) and then q6asm sessions wedge (-22 on any
further prepare).
- Restore baseline with `sudo alsactl restore 0` (HPH*/Impedance write
errors are harmless read-only controls).
- Speaker README's rule still applies: volume 100% + unsuspended sink
before trusting any "silent" result.
## Next steps
1. Fresh boot → repeat one capture with slimbus dyndbg on; confirm the
0x2c timeout on a clean system (rule out long-uptime NGD state; uptime
was ~23h when found).
2. Same boot: start a WCD RX playback stream and check whether
CONNECT_SINK (`MC:0x2d`) times out too. Both-fail → all port-connects
dead (satellite service issue); TX-only → look at TX-specific code.
3. Diff `drivers/slimbus/{qcom-ngd-ctrl.c,stream.c,messaging.c}` and
`sound/soc/codecs/wcd934x.c` between pmOS linux-postmarketos-qcom-sdm845
(6.x, mic works on enchilada) and this 7.1.1 tree. Focus: user-message
port connect path, LA used for port ops (codec vs ifd), TX port
numbering.
4. If an enchilada is handy: run this 7.1.1 kernel on it and test mic —
cleanly separates "7.x regression" from "blueline-specific".
5. Ask in #sdm845-mainline / linux-arm-msm about the signature
"Tx:MT:0x2, MC:0x2c failed:-110 while value messages work" — someone
may recognize the satellite/ADSP state.
6. Longer shot: wire up QUAT_TDM_TX_0 in DT for VMON capture as a
transport-known-good reference and UCM debugging aid.
## Quick repro
```sh
echo 'file drivers/slimbus/* +p' | sudo tee /sys/kernel/debug/dynamic_debug/control
amixer -c 0 cset name='MultiMedia2 Mixer SLIMBUS_0_TX' 1
amixer -c 0 cset name='AIF1_CAP Mixer SLIM TX7' 1
amixer -c 0 cset name='CDC_IF TX7 MUX' DEC7
amixer -c 0 cset name='ADC MUX7' DMIC
amixer -c 0 cset name='DMIC MUX7' DMIC4
sudo dmesg -C
arecord -D hw:0,1 -f S16_LE -r 48000 -c 1 -d 2 /tmp/t.wav
sudo dmesg | grep 'timed out' # -> MC:0x2c
# zeros check:
python3 -c "import wave,struct;w=wave.open('/tmp/t.wav');d=struct.unpack('<%dh'%w.getnframes(),w.readframes(w.getnframes()));print(max(map(abs,d)))"
# afterwards:
sudo alsactl restore 0
```
## References
- pmOS blueline UCM (mic commented out):
https://gitlab.com/sdm845-mainline/alsa-ucm-conf → ucm2/Google/blueline/HiFi.conf
- enchilada UCM (working mic recipe): same repo, ucm2/OnePlus/enchilada/HiFi.conf
- Speaker fix project (methodology + amp/TDM background):
~/projects/unfinished/pixel3-speaker-routing/

View file

@ -0,0 +1,84 @@
# Pixel 3 (blueline) — loudspeaker routing fix
**Status:** SOLVED 2026-07-12 — pmOS routing applied; fix lives in Pixel3Arch rootfs-overlay UCM (commit 2f39c06) + alsactl store. Both speakers verified by ear.
**Goal:** Make the loud bottom speaker work. Right now sound only comes from the
top **earpiece**; the loud bottom-firing speaker never fires. Casey reports
postmarketOS had audio working but with the channels "reversed".
## Device / stack
- Google Pixel 3 (blueline), sdm845, mainline kernel `7.1.1-sdm845` (built 2026-07-11)
- Souveraine OS (Arch Linux ARM), Hyprland/Wayland
- ALSA card 0 = "Google Pixel 3" (`G3`)
- Audio: PipeWire 1.6.7 (PulseAudio shim)
- Speakers: **dual Cirrus CS35L36** amps ("Left"/"Right") on `QUAT_TDM_RX_0` (MultiMedia1)
- Earpiece amp: WCD9340 `EAR PA` (separate codec)
## Key config files
- Active UCM (system): `/usr/share/alsa/ucm2/Google/blueline/HiFi.conf`
- referenced by `/usr/share/alsa/ucm2/conf.d/sdm845/Google-Pixel-3.conf`
- **Only defines a "Speaker" device. No earpiece device. Never sets Channel Mux.**
- Intended fix location: **user override** under `~/.config/alsa/ucm2/...`
(Casey chose user-override so pacman updates to alsa-ucm-conf don't clobber it.)
- Baseline ALSA state snapshot saved to scratchpad as `asound-current.state`
(scratchpad is ephemeral — re-capture with `alsactl -f <file> store 0` if needed).
## What we PROVED (via live tone tests)
1. Both CS35L36 amps are healthy; both transducers (earpiece + loud speaker)
can physically produce sound.
2. **The 30% volume / SUSPENDED sink was a huge red herring** — many "silent"
test results were just low volume. ALWAYS test at `pactl set-sink-volume
@DEFAULT_SINK@ 100%` and un-suspend the sink first.
3. Default state: both amps have `Channel Mux = RX1` (numid 121 Left, 112 Right).
=> both amps pull the same slot => only earpiece is audible.
4. `EAR PA Volume`=0 did NOT silence the earpiece output => the sound you hear
from the "earpiece" is being driven by a **CS35L36 amp**, not the WCD EAR PA.
5. At 100% vol, an L-only vs R-only test: **one channel DID come out the loud
bottom speaker.** So the loudspeaker amp works — it just needs the right
channel/slot routed to it. (Casey couldn't reliably tell L vs R by ear.)
6. BUT: simply splitting `Left Channel Mux=RX1 / Right Channel Mux=RX2` and
playing duplicated-mono did NOT wake the loudspeaker in a later test.
=> The channel→amp mapping is UPSTREAM of `Channel Mux`, on the QUAT_TDM_RX_0
TDM bus. `Channel Mux` alone is not the whole story.
## Relevant mixer controls (card 0, by numid)
- 121 `Left Channel Mux` (enum: 0=RX1, 1=RX2) — default 0
- 112 `Right Channel Mux` (enum: 0=RX1, 1=RX2) — default 0
- 122 `Left AMP Enable Switch` / 113 `Right AMP Enable Switch` (default on)
- 123 `Left BOOST Enable Switch` / 114 `Right BOOST Enable Switch`(default on)
- 18 `EAR PA Volume` (WCD earpiece amp; default 4)
- CS35L36 input source slots: `Left/Right ASPTX1..6SRC` (both amps identical:
1SRC=2, 2SRC=3, 3SRC=5, 4SRC=6, 5SRC=4, 6SRC=0)
- UCM SectionVerb enables: `QUAT_TDM_RX_0 Audio Mixer MultiMedia1` = 1
## NEXT STEP (where we stopped)
Get postmarketOS's authoritative blueline mixer mapping — pmOS works, so its
UCM / mixer_paths is the source of truth. The most relevant is the pmaports
**7.1rc1 kernel MR (!8446)** because that matches this exact kernel (7.1.x):
- https://gitlab.postmarketos.org/postmarketOS/pmaports/-/merge_requests/8446
- Device/blueline MR: https://gitlab.com/postmarketOS/pmaports/-/merge_requests/3932
- Original Pixel 3 device MR (!5514): https://gitlab.com/postmarketOS/pmaports/-/merge_requests/5514
- pmOS SDM845 audio writeup: https://postmarketos.org/edge/2023/01/11/sdm845-audio/
- pmOS Audio wiki (xml2ucm / mixer_paths.xml -> UCM): https://wiki.postmarketos.org/wiki/Audio
Find in pmOS's config: which TDM slot / channel each CS35L36 amp is fed, and
whether it sets `Channel Mux` + the TDM RX slot routing so earpiece(top) and
loudspeaker(bottom) each get a distinct channel. Then encode that as a user UCM
override in `~/.config/alsa/ucm2/` (copy blueline HiFi.conf there, add the
Channel Mux / slot csets to the Speaker device's EnableSequence).
## HOW TO TEST (repeatable)
```sh
pactl set-sink-volume @DEFAULT_SINK@ 100% # ALWAYS — 30% masks everything
pactl set-sink-mute @DEFAULT_SINK@ 0
# distinct L/R tones already generated in scratchpad: left.wav right.wav stereo.wav
paplay left.wav ; paplay right.wav
# read/toggle mux:
amixer -c 0 cget numid=121 # Left Channel Mux
amixer -c 0 cset numid=112 1 # Right -> RX2
```
Restore baseline: both muxes -> 0, EAR PA -> 4, volume -> 30%.
## CAUTION
- Don't trust "silent" results unless volume is 100% and sink un-suspended.
- Reverse-engineering the TDM routing blind was NOT converging. Read the pmOS
config first, then change mixers — don't guess.

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,135 @@
---
# quickshell-idle-power — lazy-load panels + consolidate polling processes
**Status:** 2026-07-21 — scoped, not started. Two quick wins already landed
separately (see "Already done" below); this doc covers the two larger sweeps
(#3 LazyLoader coverage, #4 process consolidation) to be worked tomorrow.
**Config:** `qs -c souveraine` → symlinks into `~/souveraine-surfaces/quickshell/`
(the `~/.config/quickshell/souveraine/` tree is symlinks; edit the real tree).
Quickshell 0.3.0. Composed of three layers:
- `modules/ services/ panelFamilies/` → **souveraine** (owned, edit freely)
- `ii-base/` → **borrowed ii/Caelestia tree** (treat
as replaceable; keep coupling to souveraine singletons OUT of here)
- `ii-phone/`, `ii-previous/` → phone snapshot / previous deploy
## Why (measured)
At a 49-min uptime, `qs` had burned **149 CPU-sec** and held **560 MB RSS
(15% of RAM)** on the Pixel 3 — second only to the active claude session. The
display idle layer (`services/IdleCoordinator.qml`) is already excellent
(state machine Active→Dimmed→Lock→Suspend→Asleep→Waking, native IdleMonitor
with respectInhibitors, brightness dim, hypridle screen-off). The gap is that
**the idle machine dims the screen but does not quiet the widgets** — timers
tick and processes poll regardless of visibility. These two sweeps attack the
steady-state CPU (over-eager construction) and RSS (per-widget processes).
Quickshell FAQ guidance backing this:
- "The main thing you can do to reduce memory usage is to use Loaders."
- Loader for Item-derived components; LazyLoader otherwise.
- "Using a process per widget will use significantly more memory than using
a single process."
Ref: https://quickshell.org/docs/v0.3.0/guide/faq/
## Already done (2026-07-21, do NOT redo)
1. `ii-base/services/DateTime.qml` — uptime Timer was `interval: 10` (reloading
/proc/uptime off disk 100×/sec). Changed to `interval: 60000` +
`triggeredOnStart: true`. The clock itself is `SystemClock` (event-driven),
was never the problem.
2. `ii-base/modules/ii/background/widgets/visualizer/VisualizerWidget.qml` —
the wave visualizer's 33ms (30fps) repaint Timer was `running: root.style
=== "wave"`; now `running: parent.visible` so it stops repainting when the
canvas isn't shown. (Considered a `Window.visibility` gate too, but that
needs `import QtQuick.Window` in the borrowed tree — deferred; `parent.visible`
is dependency-free and sufficient.)
Both verified: qs hot-reloaded without QML errors, pid stayed alive.
---
## Task 3 — Lazy-load the heavy panels (RSS lever)
**Current:** 9 LazyLoader vs 123 Loader across 683 QML files → a lot is
eagerly constructed at startup. Top-level panel modules under
`ii-base/modules/ii/` that are candidates (not visible at boot):
cheatsheet overlay overview sidebarLeft sidebarRight wallpaperSelector
sessionScreen screenTranslator regionSelector mediaControls polkit
notificationPopup onScreenKeyboard dock
**Do:**
- For each panel not shown at boot, wrap its instantiation in a `LazyLoader`
(non-Item / window-like → LazyLoader; Item-derived → Loader). Load on the
signal/state that reveals it (e.g. sidebar open toggle, GlobalStates flag),
unload after.
- Especially: the anime/booru sidebar (`ii-base/modules/ii/sidebarLeft/anime/`)
and cheatsheet — heavy, rarely open.
- Verify each panel still opens with no first-open UI-thread stall (LazyLoader
loads sync if shown before background load finishes — see LazyLoader docs).
**Watch:** keep LazyLoader wrapping in the layer that OWNS the panel. Don't add
souveraine-singleton dependencies into ii-base while doing this.
**Measure before/after:** `ps -eo pid,pmem,rss,comm | grep qs` (RSS) at a fresh
launch, before opening any panel.
## Progress 2026-07-22 (task 4 partially landed)
Landed live + promoted to the laptop repo (souveraine tree):
- GlobalStates.qml (souveraine override): new bool — the
shared cross-layer idle surface the doc prescribed.
- services/IdleCoordinator.qml: publishes displayActive on every state
transition (Active/Waking = true).
- ii-base/services/ResourceUsage.qml + NetworkTraffic.qml: repeat pollers
gated (+ ).
- VisualizerWidget.qml: cava watchdog gated on , cava torn down when either goes false.
- SystemInfo.qml inspected: its timers are one-shot startup probes, left
alone. Notifications.qml: no repeat poller found, left alone.
One hot-reload during editing tripped the known
"lockscreen surfaces without active lock" FATAL (reload race with sessiond,
not a QML error); the service restart loaded clean. CPU-delta measurement
while locked still pending (baseline at /tmp/qs-idle-baseline-*).
Task 3 note from code reading: most panels ALREADY gate content internally on
GlobalStates.*Open via Loaders; their IpcHandlers live inside the
panel files, so adding at the
PanelLoader level would unload the very handler that opens them — do NOT do
that. The real task-3 work is converting the -gated panels
(Overview line 23, SidebarLeft lines 89/181) to internal Loaders,
one panel at a time. Overview deliberately deferred until the 2026-07-21
search-fix has device acceptance.
## Task 4 — Consolidate per-widget processes (CPU + RSS lever)
**Current:** ~48 `running: true` Process instances (souveraine 14, ii-base 32,
ii-phone 2). FAQ: one process feeding many widgets ≫ cheaper than N processes.
**Do:**
- Audit `ii-base/services/*.qml` for multiple widgets independently spawning
processes for the SAME data. Known pollers to review:
ResourceUsage.qml, NetworkTraffic.qml, SystemInfo.qml, Notifications.qml,
plus /proc readers. Route each data source through ONE Singleton service
that widgets bind to (the `services/` layer already does this well — the
duplication lives in the borrowed ii-base tree).
- Gate long-poll services on idle state where a stale value is fine while
dimmed/locked: e.g. `running: IdleCoordinator.state === IdleCoordinator.Active`
for background stats/resources/network widgets. NOTE: IdleCoordinator is a
souveraine singleton — if gating an ii-base widget on it, do the gating in a
souveraine wrapper/override, not by importing it into ii-base. Alternatively
expose an idle bool via the existing `GlobalStates`/config surface that both
layers already share.
- The cava process behind the visualizer (VisualizerWidget.qml watchdog, 2s)
is a good candidate to also stop when the background isn't visible.
**Measure before/after:** cumulative CPU: `ps -eo pid,times,comm | grep qs`
after a fixed idle interval (e.g. leave locked 10 min, compare CPU-sec delta).
## Acceptance
- Fresh-launch RSS meaningfully below ~560 MB.
- `qs` CPU-sec growth while the screen is off/locked approaches zero (today
it keeps climbing from always-on timers/pollers).
- No panel regressions: every lazy-loaded panel opens without a stall; every
consolidated service still updates its widgets live when Active.

View file

@ -0,0 +1,53 @@
# TTS UI Wiring Handoff
## Status: Infrastructure Working, UI Wiring Needed
### What's Done (Verified by Ani, 2026-07-22)
1. **VibeVoice TTS engine** — running at `10.10.20.19:7861`, responds correctly, generates valid MP3 audio
2. **Audio playback pipeline** — mpv + PulseAudio → Pixel 3 speaker confirmed working
3. **Souveraine `/v1/config`** — correctly reports TTS voice mapping
4. **TTS enabled** in Souveraine settings
5. **Casey heard the test audio** — full loop confirmed end-to-end
### What Needs Wiring (For Souveraine Agent)
1. **Speaker button → Speech.qml** — The speaker icon in the UI needs to be mapped to the correct hook in `Speech.qml` so that pressing it plays the TTS audio
2. **Local config `endpoint` field** — Currently set in `~/.config/illogical-impulse/config.json`. Recommend clearing local `endpoint` to `""` so the shell always trusts Souveraine's `/v1/config` mapping as single source of truth. If VibeVoice ever moves, you update it in one place.
### Subconscious Message Ordering Issue
**Problem:** Subconscious/interrupt messages are appearing AFTER all primary messages instead of inline at the point they were generated.
**Example:** During a 10-round tool interstitial, Casey made a comment mid-round. The subconscious alert appeared at the END of all messages, not at the point where the interruption occurred.
**Fix needed:** Map the ordering of subconscious messages in the Souveraine message rendering pipeline so they appear inline at their generation timestamp, not appended at the end.
### Laptop Lock Screen Issue
**Problem:** Laptop lock screen is bound to "Souverain Secrets" app which is not running. Screen is locked, password rejected, requires restart to unlock.
**Fix needed:** Either ensure the Souverain Secrets app starts on boot, or unbind the lock screen from it so it falls back to standard PAM authentication.
### Frontmatter Serializer Bug
**Problem:** ~20 auto-generated task files in Ani's memory are missing `description:` field in their frontmatter. This is an upstream issue in the Souveraine todo serializer — it creates frontmatter blocks but doesn't populate `description:`.
**Fix needed:** Patch the todo serializer to write `description:` on file creation.
### Speaker Button Content Fix (Resolved by Ani, 2026-07-22)
**Problem:** Speaker button was reading raw `content` field — which includes all `<think>` blocks, tool calls, file paths, and sensor output — to VibeVoice. TTS was being fed hundreds of tokens of infrastructure noise instead of clean spoken text.
**Fix applied:** Added `_ttsClean()` function to `AiChat.qml` (line ~216) that strips think blocks, code fences, markdown formatting, and collapses whitespace before handing text to TTS.
**Status:** Fixed in source (`souveraine-surfaces/quickshell/modules/ii/sidebarLeft/AiChat.qml`). Deployed copy is symlinked to source, so the fix is live. **Requires quickshell restart to take effect.**
### Files Modified This Session
- `~/.config/illogical-impulse/config.json` — TTS endpoint fixed (spaces removed from URL)
- `projects/unfinished/home-directory-homogenization.md` — cleanup plan created
- `souveraine-surfaces/quickshell/modules/ii/sidebarLeft/AiChat.qml` — `_ttsClean()` TTS content filter added
---
**Authored:** Ani, 2026-07-22
**For:** Souveraine Agent

View file

@ -0,0 +1,134 @@
---
# usb-signaller — boot-time panic in UDC::load, index out of bounds
**Status:** 2026-07-20 — root cause identified, not yet fixed. Read-only
device audit found this; nothing restarted, no patch applied yet.
## Symptom
Both `usb-signaller.service` and `usb-signaller-default-mode.service` fail
on **every boot** (confirmed across 5 consecutive boots in journal, going
back to at least 2026-07-20 01:21). Journal:
```
usb-signaller[NNN]: ℹ️ Pre-flight checks
usb-signaller[NNN]: 🔍 Discovering URSs
usb-signaller[NNN]: thread 'main' (NNN) panicked at src/udc.rs:388:37:
usb-signaller[NNN]: index out of bounds: the len is 0 but the index is 0
usb-signaller.service: Main process exited, code=exited, status=101/n/a
...
usb-signaller-default-mode[NNN]: usb-signaller never appeared on the bus
usb-signaller-default-mode.service: Failed with result 'exit-code'.
```
`usb-signaller-default-mode.service` ("Default USB mode at boot (developer)")
fails as a downstream consequence — it waits ~21s for usb-signaller on
D-Bus, which never appears since the main process already panicked.
Not a hardware/UDC-absence issue: `/sys/class/udc/` does contain a real
controller (`a600000.usb`) at the time of the audit. So discovery has
*something* to enumerate; the crash is in how it's indexed, not in finding
zero UDCs.
## Root cause
Package: `usb-signaller` 0.3.1-2 (upstream, by Dylan Van Assche — the
usb-moded replacement for mainline Linux Mobile). Source checked out locally
at `~/build/src/usb-signaller` and `~/build/usb-signaller/src/usb-signaller`
for build/patching.
`src/udc.rs`, `UDC::load()`, lines 382-388:
```rust
for entry in discover_gadgets() {
let entry = ConfigFsGadget::load(&entry)?;
if udc_name == entry.udc {
if entry.enabled {
/* USB Ethernet NCM gadget */
if entry.configs[0].functions[0].name.starts_with("ncm.") {
```
`ConfigFsGadget.configs` and each config's `.functions` are plain `Vec`s
populated in `gadget.rs` by scanning the gadget's `configs/` directory in
configfs — there is no guarantee either Vec is non-empty. The code assumes
any gadget where `entry.enabled == true` already has at least one config
with at least one function, and indexes `[0]` unchecked on both.
At early boot (this runs right after "Pre-flight checks", i.e. very early
in the service's life), a gadget can be `enabled` in sysfs terms while its
`configs/` subtree hasn't finished being populated yet, or a stale/partial
gadget is left over from a previous session — configfs itself doesn't
enforce "enabled implies non-empty configs" atomically. Either scenario
produces `entry.configs` (or `entry.configs[0].functions`) with len 0, and
the unchecked index panics the whole process before any UDC mode gets set.
## Fix sketch (not applied — read-only audit)
Guard both indexes, e.g.:
```rust
if entry.enabled {
if let Some(cfg) = entry.configs.first() {
if let Some(func) = cfg.functions.first() {
if func.name.starts_with("ncm.") { ... }
else if func.name.starts_with("ffs.mtp") { ... }
else if func.name.starts_with("ffs.accessory") { ... }
else { mode = UDCMode::Unknown; eprintln!(...); }
} else {
mode = UDCMode::Unknown; // enabled gadget, no functions yet
}
} else {
mode = UDCMode::Unknown; // enabled gadget, no configs yet
}
}
```
i.e. treat "enabled but not yet fully populated" as `UDCMode::Unknown`
rather than crashing — matches how the existing `else` branch (line 408-409)
already handles an unrecognized function name.
## Impact
Because the process panics, USB mode never gets set at boot at all (not
even a fallback), and `usb-signaller-default-mode.service` (developer mode
at boot) never applies. Practically: USB currently comes up in whatever
mode the kernel/gadget defaults to, not the configured "developer" default,
until something else (manual `usb-signallerctl` call, or a later retry) sets
it — needs to be verified once a fix lands.
## Next steps
1. Patch `udc.rs:388` per sketch above (or upstream-style `.get(0)` /
`.first()` guards).
2. Rebuild, reboot, confirm both units go green and default-mode actually
applies (check `usb-signallerctl status` or equivalent post-boot).
3. Consider filing upstream against Dylan Van Assche's usb-signaller repo —
this is likely a stock bug, not something introduced by local patches
(worth diffing `~/build/src/usb-signaller` against upstream HEAD to
confirm before filing).
## Status update — 2026-07-21
Fix applied and verified at runtime:
- Patched per the sketch (`.first()` guards, fall through to
`UDCMode::Unknown`), cross-built on archdev, installed at
`/usr/local/bin/usb-signaller` with a systemd drop-in
(`/etc/systemd/system/usb-signaller.service.d/local-fix.conf`)
overriding ExecStart — pacman-owned `/usr/bin` binary untouched.
- Patch + pkgrel=3 vendored in `Pixel3Arch/pkgs/usb-signaller/`
(0001-udc-guard-empty-configs-functions.patch), commit a970742.
- Runtime restart: both units went green, default-mode applied
Developer for the first time ("Setting mode of UDC a600000.usb from
Charging to Developer").
- Trap discovered: the mode apply recreates the gadget, the new
function enumerates as usb1, and the stale usb0 keeps a linkdown
route that eats every reply — USB ssh dies. Fixed the networkd match
to Name=usb* (live + rootfs-overlay) and flushed the stale usb0.
Remaining:
1. Cold-boot verify (Casey, at device): both units green from boot,
`usb-signallerctl status` shows developer, USB net comes up as usb0.
2. Diff against upstream HEAD and file the panic upstream — the guard
is a clean candidate patch.