# Handoff — 2026-08-07, the day her face went on the glass

Everything below is pushed. Nothing is half-applied. The one thing that is
**not** durable is the phone's binaries — see "The phone is running test
installs" at the end, and fix that first if a `-Syu` is coming.

## Read these before touching any of it

`docs/tasks/59-her-face-on-the-glass.md` (rewritten today — the five bugs in
the way, and why each presented as something else), `docs/tasks/60-the-
multitasking-view.md`, `docs/DUMP-bugs-2026-08-06.md` (items 3/4/5 closed, 7
retracted, 8/9/10 new).

## What is on the phone and working

She draws. She is **furniture on home**, not an app: no zone, no dock entry, no
overview card, no frame, her own size, hung from the top of the zone with the
room below deliberately left free for what she shares. She takes touch only on
her own silhouette, so the home screen underneath still works. She follows a
finger **anywhere on the panel**. Holding her records and sends through the
existing STT into the sidebar. Double-tapping her dismisses her; locking the
phone sends her away and she stays away. The clock and the quote fade out when
she is up.

Also landed: multitasking cards draw; zones mint correctly; the dial records
the screen on a hold; layer-surface popups exist at all; move mode can be left;
the dock is bigger and its scale is a setting.

## The five that cost the most, kept short

1. **The rig drew nothing because of one missing DOM element.** `live2d.js` is
   the reference's whole bundle, not just Cubism, and it reads `#live_talk`
   every frame. Missing, it throws on the first update — after the model and
   all four textures have loaded. Everything else reads healthy.
2. **`--app-id` was a no-op.** `set_prgname` after GTK made the surface. The
   compositor saw `souveraine-web`; window rules could only match the title.
3. **Claim the GL context *after* sizing the canvas.** Before it, the context is
   born 300x150, `gl.viewport` stays there, and she renders as a thumbnail in
   the bottom-left corner of a full-size buffer.
4. **Multitasking cards were `width: 0`** — a Column↔loader binding cycle with
   no search widget to break it. Every other signal read healthy.
5. **`Feeds::publish` dropped subscribers whose channel was `Full`**, so a fast
   stream unsubscribed its own reader inside the first burst. An external probe
   that read faster kept working, which is what made it look like a shell bug.

## Traps this session re-learned the hard way

- **`pgrep -f` / `pkill -f` match your own command string.** Killed the SSH
  session and a kitty more than once. Bracket the pattern *and* make sure the
  unbracketed form does not also appear later in the same command.
- **A duplicate signal handler fails the whole QML module** and takes the shell
  into a restart loop. `onCanceled` already existed on the rail; adding a second
  is `Property value set multiple times`.
- **Quickshell's `Socket` emits `onConnectionStateChanged`,** not
  `onConnectedChanged`. A handler for the wrong one is silent.
- **Check `locked` in viewtop's `state` before diagnosing a capture failure.**
  A session left locked by a test refuses every capture, correctly, and it
  looks exactly like a broken capture path.
- **`GlobalStates.screenLocked` drifts.** `session lock` answered
  `already-locked` on a phone in use while logind said `LockedHint=no`. Gate on
  `screenLockSecure`, the compositor's ack.
- **A greetd restart leaves the phone locked** and only the PIN opens it —
  `unlock` is deliberately not a verb anything but a human can call. Do not
  leave it locked and walk away.
- **`scene` intent `drop`** clears a stuck window grab in one call. It is the
  unstick for a phone frozen in move mode. The intent is a bare string
  (`{"op":"scene","intent":"drop"}`), not a map.

## What is open, honestly

- **The pill strands a pose** (DUMP 8). My cancel-path theory was **wrong** —
  `onCanceled` already handles it. Cause unfound. Casey wants no backing-off
  state at all: gate the partial slide on speed and length, then commit.
- **The swipe into multitasking is awkward** (TASK-60). Two named suspects, both
  needing to be watched in motion.
- **Side panels glitch on the way out** (DUMP 9) — keyboard half fixed, visual
  glitch uncharacterised. **The terminal cannot keep its keyboard** (DUMP 10);
  the two are mirror images and probably one focus-handoff bug.
- **Dock appstacks** — the popup fix is committed and **unverified on hardware**.
  Layer-surface popups were never composited *or* hit-tested, which is why
  tapping a stack did nothing. Needs a finger on it.
- **Holding her to talk is the wrong affordance.** Casey: *"holding onto her has
  a different connotation."* It works; it should become explicit controls.
- **Her states are wired but thin.** Nine postures collapse onto two levers —
  `#live_talk` and the bundle's head/body hit test. That is all the rig has.
- **VPN through the state machine** — Casey's call: its own session, and it
  probably wants a settings surface for SSIDs. Do not start it as a side quest,
  and remember §12: an `Action`, never a daemon (`wiuf-vpn-gate` was 652
  recycles in 90 minutes).

## The rig is the next big thing, and it is a pipeline

Not a repaint. Strip the parts out by name with their exact UV footprints,
rebuild by compositing them back at those footprints, and let agents swap
components the way they swap outfits — the rebuild half is what makes swapping
real. V1 of the *look* is the face only: shrink the eyes, slim the face with
transparency, because Histoire reads as a little girl and not a woman. Wings go
holographic. Outfit is fine for now.

**Ceiling:** parts are paint, silhouette is mesh. Twin-tails and the pose need a
new rig, and that is a Cubism 2 → 4/5 change — a different loader, which also
replaces the character layer that reads `#live_talk`.

Assets: `~/Projects/live2d-editing` (4 × 1024², palette-indexed, convert to RGBA
first). Style reference: `~/Projects/AniAvatar`.

## The phone is running test installs

`/usr/bin/viewtop` and `/usr/bin/souveraine-web` are **hand-installed**, and the
QML lives in `/home/casey/souveraine-surfaces`. Originals parked at
`/usr/local/bin/viewtop.prev-r88-package` and `souveraine-web.prev-r363`. A
`pacman -Syu` reverts the binaries. This is `souveraine/CLAUDE.md` failure mode
#4 and it should be closed by building the packages, not by copying again.

Build loop that works: edit on the laptop → `scp` the changed file to archdev
(`/home/casey/souveraine-viewtop`, `/home/casey/souveraine`) → **checksum it**,
because another session may be in the same tree → `VIEWTOP_AARCH64_SYSROOT=
/home/casey/aarch64-sysroot ./scripts/build-cross.sh --release` → base64 down,
scp up, `install -m755`. `~/souveraine` needs `~/tuie` beside it (symlinked to
`~/Projects/tuie`).

## Cross-repo state

- `souveraine` → `primary` @ `630d691`
- `souveraine-viewtop` → `main` @ `62ec16b`
- `SouveraineOS` → `main` @ (this commit)
- smithay fork → `souveraine` @ `645961c7`, pinned; the `colour-transform`
  branch was folded in and **deleted**, and the local `[patch]` that pointed at
  `/home/casey/smithay-colour` is gone — the tree builds from the fork now.
- Dead branches removed: `public`, `main`, `detached-reinit-backup`,
  `experimental/session-trust-phase1` (souveraine), `public-main`,
  `codeberg-publish-tmp` (Pixel3Arch). Nothing of value was stranded on any of
  them — all merges and dead codeberg snapshots. `archive/*` refs kept.
