Watch
1
0
Fork
You've already forked SouveraineOS
0

START-HERE: the page to open when something is broken

Doctrine explains why the system is shaped the way it is and is the wrong
thing to open when the screen is black. This is for that: USB
unreachability (with the ARP test that proves the phone is fine), the
black-screen regression and its mechanism, what to do when a Hyprland
binding stops existing, where to build, the three different ways things
reach the phone and why an overlay-only fix never arrives, and the five
traps that have each cost a session.

Every repo's CLAUDE.md now points here before it points at doctrine.
This commit is contained in:
Fimeg 2026-08-02 19:13:40 -04:00
commit 9e2e193af3
2 changed files with 212 additions and 0 deletions

208
START-HERE.md Normal file
View file

@ -0,0 +1,208 @@
# 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.**
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 | hand-copied to `/usr/local/bin` | Not packaged yet — TASK-25. |
| 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`.
Away from home the phone has no wifi — the SSIDs are pinned to the home network —
so USB is the only path, which is why §1 matters.
---
## 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.