phase: 09_wall_remote_page
Build and Push Containers / test (push) Successful in 45s
Build and Push Containers / build-and-push-app (push) Successful in 13s

**Phase 09 — final verification pass (all 3 tasks already in `complete/`; phase work was in the working tree, verified, nothing needed fixing)**

**Gates** — `uv run ruff check . && uv run ruff format --check .` → clean; `uv run pyright` → 0 errors; `uv run pytest` → **360 passed, 5 skipped** (only the realdisplay rung), coverage 96% total / 95% app.py; `MIRROR_REALDISPLAY=1 uv run pytest -m realdisplay` → 4 passed, 1 skipped (documented host limitation: no audio source on the real X11 path); `uv run pytest tests/e2e/test_remote_control.py` → 8/8 passed; `node --check` on every `static/*.js` → all pass.

**Completion criteria** — all met: (1) "Show on wall" stages on the display, asserted on display DOM + `/api/kiosk` ✓; (2) Back/Fullscreen + untouched second controller converge (TestConvergence) ✓; (3) `/api/streams` reports `viewers: 0` / `watching: 0` with only `/remote` open (TestACostsNothing) ✓; (4) followers count displays only — 0/1/1 with a second controller (TestFollowers) ✓; (5) controller works with no display: `followers: 0`, state stored (protocol test + e2e) ✓; (6) e2e + full `-m e2e` + realdisplay all green ✓; (7) README + SECURITY.md updated, Silverblue default now `/kiosk` (installer, conf, gnome script, checker) ✓; (8) suite 100% green, coverage >90% on new code ✓; (9) PLAN.md untouched; AGENTS.md changed only for a stale statement ✓.

**Documentation** — README.md reviewed in full against code (page table, quickstart, env-var table vs `config.py`/`debug.py`, presets, keys, deployment, kiosk section): no inconsistencies found. AGENTS.md: fixed the one stale statement introduced by this phase ("Four pages" → "Five pages"); no other staleness, nothing added.

**Decisions** — no repo changes were required; the one realdisplay skip is a pre-documented environment fact, not a failure.

**Next pending phase** — none; `todo/` held only phase 09.
This commit is contained in:
2026-11-03 23:50:43 -05:00
parent cd29156a7d
commit d0077e0db4
36 changed files with 1354 additions and 43 deletions
+19 -3
View File
@@ -10,6 +10,7 @@ your network:
| **Share** | `/mirror` (also `/share`) | Picks a window, screen, or tab via the OS-native picker and mirrors it. |
| **Watch** | `/watch` | Shows every screen currently being mirrored; click a tile to take the stage. |
| **Kiosk** | `/kiosk` | A chrome-free wall for an unattended display: no header, no counts — the net pill is the only chrome, and the shared layout state (below) arranges it. |
| **Remote** | `/remote` | A controller for the shared wall, from a laptop or a phone: lists the live mirrors and the wall's state and sends the decisions (show this, back to the wall, full screen) — no video, no bandwidth. |
| **Index** | `/` | Signpost + live count of mirrors. |
Any number of people can mirror at the same time and any number of people can
@@ -32,6 +33,7 @@ Then, from another machine on the LAN:
http://<this-machine>:8055/mirror # to share
http://<this-machine>:8055/watch # to watch
http://<this-machine>:8055/kiosk # the unattended wall (kiosk display)
http://<this-machine>:8055/remote # drive the wall from this device
```
Linux is the primary target. On Linux the native picker is X11/Wayland's own
@@ -78,6 +80,20 @@ same `arrange` intent, so a controller and a display converge on one state
(`m` is local-only: a display never becomes audible because another machine
asked).
### Driving an unattended wall
The shared state exists for a box with no keyboard: point the box at `/kiosk`,
open `/remote` on a phone, and press **Show on wall** — the box stages that
mirror, and **Back to the wall** and **Full screen** do the same for their
decisions. When a keyboard is plugged into the box, the wall keys still work
there, and a keypress performs the change *and* publishes the same `arrange`
intent, so a key on the box is seen by every controller — a controller and a
display converge on one state. Fullscreen is **requested, not promised**: a
remote command carries no user activation, so the browser may refuse the
request; the in-page stage fills the viewport anyway (and the kiosk box's
window manager forces the window fullscreen). The state lives in the server's
memory only: a restart returns the wall to a wall.
## Configuration
Environment variables (all optional; a `.env` file is loaded if present):
@@ -180,9 +196,9 @@ from this side:
A wall display is a Fedora Silverblue box running the locked-down GNOME Kiosk
session, auto-logged in as `kiosk`, with Firefox in `--kiosk` mode pointed at
the wall. It is set up and repaired entirely over SSH — no keyboard, no console,
no monitor. The display page is `/kiosk`; the installer default still opens
`/watch` and an existing `kiosk.conf` URL wins, so moving a box is the
one-line re-point in [the runbook](docs/kiosk-silverblue.md#two-endpoints-watch-and-kiosk). A brand-new box layers the kiosk packages once first
no monitor. The display page is `/kiosk`, and it is the installer default
too; an existing `kiosk.conf` URL wins over the default, so moving a box is
the one-line re-point in [the runbook](docs/kiosk-silverblue.md#two-endpoints-watch-and-kiosk). A brand-new box layers the kiosk packages once first
(`rpm-ostree install gnome-kiosk gnome-kiosk-script-session`, then a reboot);
after that these two commands are both the setup and the repair path: