One row per capability, one column per body, with a strict status vocabulary — "works" means exercised with evidence named, never that a device node appeared. blueline points at PAF/ rather than copying it; d10 carries its own state; m1 records what already transfers before anything is measured. The display pipe is corrected in the same change: ADP binds and scans out and the panel stays dark, so it is partial, not working.
229 lines
10 KiB
Markdown
229 lines
10 KiB
Markdown
# START HERE
|
|
|
|
**If you are picking up the phone and something is broken, this page is the
|
|
whole answer. Do not go reading doctrine first.**
|
|
|
|
> **Which phone?** There are two, and they are plugged in at the same time.
|
|
> This page is **blueline**, the Pixel 3 daily driver, at `172.16.42.1`.
|
|
> The **iPhone 7** is at `172.16.43.1`, boots with
|
|
> `~/Projects/hoolock/boot.sh`, and its whole state is
|
|
> [`devices/d10/`](devices/README.md). Never unplug one to make a command
|
|
> convenient. [`devices/`](devices/README.md) is the per-body support matrix.
|
|
|
|
Doctrine explains *why* the system is shaped the way it is and is worth reading
|
|
before you *design* something. It is the wrong thing to open when the screen is
|
|
black. This page is for that.
|
|
|
|
---
|
|
|
|
## 0. The thirty-second triage
|
|
|
|
Run this. It answers most of what a session usually spends an hour rediscovering.
|
|
|
|
```sh
|
|
Pixel3Arch/tools/phone-triage.sh
|
|
```
|
|
|
|
It checks, in order: can I reach the phone at all (USB, VPN, wifi), is the
|
|
compositor up, is the shell up, what does the panel think it is doing, is the
|
|
session class right, and is the lock/blank ordering holding. Every check prints
|
|
the fix next to the failure.
|
|
|
|
---
|
|
|
|
## 1. "The phone is unreachable over USB"
|
|
|
|
**It is almost certainly booted and fine.** This exact symptom has cost multiple
|
|
sessions.
|
|
|
|
Signature: `ip neigh` shows the phone **REACHABLE** (it answers ARP), and ICMP
|
|
and ssh both time out.
|
|
|
|
Cause: a cold boot leaves **two** gadgets — the initramfs one and the rootfs one
|
|
— so the phone ends with a dead `usb0` and a live `usb1`, and both hold
|
|
`172.16.42.1`. Replies egress the dead one and vanish.
|
|
|
|
**Fix, and read this part:** `IgnoreCarrierLoss=5s` in `40-usb0.network` has been
|
|
correct in the repo since 2026-08-01 and **was never on the device**, because
|
|
`rootfs-overlay/` only lands on a *flash* and this phone updates with pacman.
|
|
It is now a package:
|
|
|
|
```sh
|
|
sudo pacman -S blueline-usb-gadget # then reboot, or:
|
|
sudo ip addr flush dev usb0 # one-shot, on the phone
|
|
```
|
|
|
|
Until that package is installed on a given device, expect this every cold boot.
|
|
|
|
**Check which gadget you are talking to** — this tells you whether the phone
|
|
finished booting:
|
|
|
|
```sh
|
|
sudo dmesg | grep "SerialNumber: " | tail -3
|
|
# dbg-initramfs -> still in the initramfs (serial consoles on ttyACM0/1)
|
|
# google,blueline -> the real rootfs is up
|
|
```
|
|
|
|
**`error -71` on enumeration is a cable, not the phone.** `device descriptor
|
|
read/64, error -71` retrying forever is EPROTO — try another port, then another
|
|
cable, before touching anything else.
|
|
|
|
**Never warm-reboot blueline to fix a cable.** A warm reboot poisons
|
|
TZ/remoteproc state; recovery from *that* is a true cold boot. Power off fully.
|
|
|
|
---
|
|
|
|
## 2. "The screen is black after login"
|
|
|
|
Regression fixed 2026-08-02 in `souveraine-viewtop` `1fa1ea1`. If you see it
|
|
again, the mechanism is worth knowing because the shape recurs:
|
|
|
|
`disclosure_locked()` was `authority.locked || position.hides_session()`. When
|
|
the protocol unlocks, `position` clears — but the *authority* half stays true
|
|
until sessiond's poll catches up, and while it is true `frame_elements`
|
|
composites lock surfaces **and nothing else**. The client has just destroyed
|
|
those surfaces, so the scene renders **empty**.
|
|
|
|
The OR was harmless for months because nothing ever set `LockedHint`. Making
|
|
viewtop report it made the half true for the first time and the latent trap
|
|
fired. **The pattern: a dormant OR becomes a bug the day its other side starts
|
|
working.**
|
|
|
|
---
|
|
|
|
## 3. "A control that worked under Hyprland does nothing"
|
|
|
|
Ask first: **was it a `hyprland.lua` binding?** If yes, it did not survive, and
|
|
the fix is never to re-add a binding — it is to put the behaviour in the state
|
|
machine.
|
|
|
|
Everything in `overlays/hypr/hyprland.lua` is a checklist of things that either
|
|
have been ported or still need to be. Ported so far: power button, volume,
|
|
double-tap-to-wake, DPMS. Still bound to nothing: brightness keys, media keys
|
|
(`playerctl`), `osk-switch`.
|
|
|
|
The rule, from `DEVICE-STATE-MACHINE.md` §12: a small daemon that reads a signal
|
|
and calls a tool is the **eighth blind actor**. Behaviour enters as a
|
|
`sensor_input`/`button`/`input` verb and leaves as an `Action` from `tick()`,
|
|
through the one executor table.
|
|
|
|
---
|
|
|
|
## 4. Where to build, and where NOT to
|
|
|
|
| | |
|
|
|---|---|
|
|
| **Build host** | archdev, `ssh -i ~/.ssh/ani root@10.10.20.123`, run cargo as `casey` |
|
|
| **Never build on** | the phone (3.5 G RAM, daily driver) or the laptop |
|
|
| **viewtop mirror** | `/home/casey/souveraine-viewtop` — **not** `~/Projects/...`, that one is stale |
|
|
| **The git tree** | the laptop. archdev's mirrors are not git repos. |
|
|
|
|
The loop is **edit on the laptop → rsync UP → build → `cargo fmt` on archdev →
|
|
rsync DOWN**. Pull down before you push up and you silently revert your own work,
|
|
and a build started before the push verifies the *old* code while looking green.
|
|
Verify your change arrived (`grep -c <new symbol>`) before trusting a result.
|
|
|
|
viewtop's gates — and `cargo clippy --workspace` does **not** cover the render
|
|
path, which is the whole `kms` feature:
|
|
|
|
```sh
|
|
cargo fmt --all
|
|
cargo clippy --workspace --all-targets -- -D warnings
|
|
cargo clippy -p viewtop-compositor --features kms --all-targets -- -D warnings
|
|
cargo clippy -p viewtop-compositor --features nested --all-targets -- -D warnings
|
|
cargo test --workspace
|
|
./scripts/build-cross.sh
|
|
```
|
|
|
|
smithay's real signatures are on archdev at
|
|
`~/.cargo/git/checkouts/smithay-312425d48e59d8c8/812bd33`. Read them; the laptop
|
|
has no checkout.
|
|
|
|
---
|
|
|
|
## 5. How anything reaches the phone
|
|
|
|
**Three different paths, and picking the wrong one is why things silently never
|
|
update.**
|
|
|
|
| What | How it ships | Gotcha |
|
|
|---|---|---|
|
|
| `souveraine`, sessiond, machined, secrets | **pacman**, built by CI on push to `primary` | Needs the VPN up — the repo is at `10.10.20.120:4455` |
|
|
| Device packages (`pkgs/*`) | **pacman**, from Pixel3Arch CI | `pkgs/**` has no workflow for some paths — check |
|
|
| `rootfs-overlay/` | **flash only** | **This phone does not flash.** An overlay-only fix never arrives. This is TASK-28, and it is what broke USB for months. |
|
|
| viewtop | **pacman**, built by CI on push to `main` | Packaged since TASK-25 closed. `/usr/bin/viewtop` is owned by `souveraine-viewtop`; verify with `pacman -Qo /usr/bin/viewtop`, whose version carries the commit. **Do not hand-copy over it** — that is the "silently never updates" trap this table exists for. Installing does not restart it; the running compositor keeps the old binary until the session restarts. |
|
|
| QML shell | per-file `scp` | **Never `deploy.sh --phone`.** Check whether the live path is a symlink into `ii/` or a real override; patch every copy. |
|
|
|
|
**A green CI run on `primary` can ship nothing and look identical to one that
|
|
shipped.** Read the *jobs*, not the run status.
|
|
|
|
---
|
|
|
|
## 6. Reaching the phone
|
|
|
|
```sh
|
|
ssh -i ~/.ssh/ani casey@172.16.42.1 # USB tether
|
|
ssh -i ~/.ssh/ani casey@10.10.20.234 # home wifi, via VPN
|
|
```
|
|
|
|
**10.10.20/24 is reachable only through the VPN**, and it does not autoconnect
|
|
after a reboot. If gitea resolves to a Cloudflare address, the tunnel is down.
|
|
On the phone: `nmcli con up wiufph`. On the laptop: `wiufarch`.
|
|
|
|
**The phone does join networks that are not home.** This page used to say it
|
|
could not — that away from home the SSIDs are pinned and USB is the only path.
|
|
On 2026-08-02 it sat on a foreign SSID at `10.10.30.213` working perfectly while
|
|
that paragraph sent the reader hunting for a cable. Pass the address:
|
|
|
|
```sh
|
|
Pixel3Arch/tools/phone-triage.sh 10.10.30.213 # or export PHONE_HOST
|
|
```
|
|
|
|
Find it from the router, or on the phone with `ip -br a show wlan0`.
|
|
|
|
**A phone on a foreign network cannot reach the package repo**, since that lives
|
|
at `10.10.20.120:4455` behind the VPN. You do not need to bring `wiufph` up for
|
|
that — the laptop has the tunnel and the phone is on the local subnet, so fetch
|
|
on the laptop and push down: gitea release → `scp` → `pacman -U`. Leave the
|
|
phone's tunnel alone; it is the user's switch.
|
|
|
|
---
|
|
|
|
## 7. The five traps that have each cost a session or more
|
|
|
|
1. **Grepping the design docs.** They argue; the argument is the content.
|
|
`ls -lt docs/` and read what is newest. Sessions that grep produce confident,
|
|
wrong architecture — the single most reliable failure in this project.
|
|
2. **Verifying the actuator instead of the invariant.** Calling
|
|
`blueline-screen-toggle off` proves DPMS works and *nothing* about the
|
|
lock-then-blank ordering, because it bypasses `request_blank()` entirely.
|
|
Trigger a blank the way the machine does, then read the trail.
|
|
3. **The forensic trail is at `~/.local/state/souveraine/forensic.jsonl`**, not
|
|
the runtime dir. It moved. Looking in the old place shows an empty file and
|
|
invites the wrong conclusion.
|
|
4. **A greetd restart silently downgrades the session to `Class=greeter`**, and
|
|
logind then refuses `SetLockedHint` outright — after which every blank goes
|
|
out on a session nobody could confirm was locked. Fixed by a drop-in, but
|
|
check `loginctl show-session <seat0 session> -p Class` before believing
|
|
anything about locking.
|
|
5. **Two actors deciding one thing.** It has happened three times: hypridle vs
|
|
the idle rule, the compositor vs sessiond over the panel (a press woke the
|
|
phone and put it back to sleep), and the shell vs the compositor over
|
|
`LockedHint`. When something oscillates or fights, count the writers first.
|
|
|
|
---
|
|
|
|
## 8. Then, and only then: the design docs
|
|
|
|
Read these when you are *designing*, not when you are debugging.
|
|
|
|
- `SESSION-AUTHORITY-DOCTRINE.md` — **§13 first** (it inverts the default the
|
|
earlier sections imply), then §4, §9, §10.
|
|
- `DEVICE-STATE-MACHINE.md` — §1 (why one machine, not seven blind actors), §4
|
|
(evidence and confidence), §10 (source health), §12 (an Action, not a daemon).
|
|
- `LOCK-DPMS-LESSONS.md` — §1 is the lock-then-blank invariant and every way it
|
|
has been broken.
|
|
- `docs/tasks/README.md` — the live work. 43 (viewtop), 50 (her hand on the
|
|
glass), 51 (viewtop → daily driver), 52 (atmosphere).
|
|
|
|
`STATE.md` is the resume anchor and goes stale; check its date.
|