From d87990f0ac2a59e59e74509319aa5cb38944b693 Mon Sep 17 00:00:00 2001 From: ekklesia Date: Thu, 10 Sep 2026 08:54:41 -0500 Subject: [PATCH] docs: write up the classroom v2 result Co-Authored-By: Claude Opus 5 --- docs/classroom-v2-experiment.md | 441 ++++++++++++++++++++++++++--- docs/screenshots/recover-panel.png | Bin 0 -> 28638 bytes tools/lab/Dockerfile.plasma | 6 + tools/lab/cold-start | 38 +++ 4 files changed, 447 insertions(+), 38 deletions(-) create mode 100644 docs/screenshots/recover-panel.png create mode 100755 tools/lab/cold-start diff --git a/docs/classroom-v2-experiment.md b/docs/classroom-v2-experiment.md index 741d851..1db0570 100644 --- a/docs/classroom-v2-experiment.md +++ b/docs/classroom-v2-experiment.md @@ -15,21 +15,25 @@ or hyprland room, and nothing here changes the development host. That commit is the published **stable** build, verified rather than assumed: `repo/stable/manifest.json` names `ekklesia-classroom_0.1.44_amd64.deb` with -sha256 `7de568...81041`, and the copy in `repo/packages/` hashes to exactly +sha256 `7de568…81041`, and the copy in `repo/packages/` hashes to exactly that. The branch is not built on `experiment/hyprland`; `git ls-files` on it contains no Hyprland file. -## What the experiment must not do +## The answer, in one paragraph -* No change to the development host's OS, packages, partitions or bootloader. -* No in-place distribution upgrade or migration. -* No touching stable, canary, or a real classroom machine. -* Alternate operating systems get a disposable environment or a written VM - specification, never the host. +Every user-visible failure the church reported is a piece of desktop +infrastructure the classroom wrote itself. The shell that disappeared had no +supervisor. The minimised window with no feedback exists because wlroots has +no minimise and the classroom invented one. The secondary window that fights +the browser needed a 314-line arbiter because sway tiles. The unrecognisable +browser icon was hand-drawn to match a monochrome set. Debian 13 already +ships a desktop that solves all four, so **the recommendation is Plasma 6.3 +on the Debian the classroom already runs**, configured rather than written, +and it was built and tested here rather than argued for. ## The development host, as found -Recorded because it bounds what can be tested here. +Recorded because it bounds what could be tested. | | | | --- | --- | @@ -37,43 +41,404 @@ Recorded because it bounds what can be tested here. | machine | already a VM — root is `/dev/vda2`, GPU is `bochs-drm`, CPU is `QEMU Virtual CPU` | | nested virtualisation | **none** — `/dev/kvm` does not exist | | libvirt / qemu / VirtualBox | not installed | -| containers | Docker 26.1.5, cgroup v2, `/dev/uinput` present | +| containers | Docker 26.1.5, cgroup v2 | | resources | 6 vCPU, 11 GiB RAM, 160 GiB free | -**Consequence: this session cannot create a virtual machine.** There is no -KVM and no hypervisor. Every alternate OS or desktop that needs real -hardware-level isolation has to be either tested in a container or written up -as a VM specification for someone with a hypervisor to run. The previous -Hyprland milestone hit the same wall and used containers; that lab worked, and -this experiment extends it rather than inventing a new one. +**This session could not create a virtual machine**, so the candidate desktop +was given a systemd container instead. Nothing outside Docker was changed on +the host: no packages installed, no configuration touched, no service +altered. See *Alternate operating systems* for what still needs a real VM. -## Hypothesis being tested +## The lab -> The classroom's reliability problems — a shell that vanished in production, -> no minimised-window feedback, secondary windows fighting the main browser, -> and a growing pile of compositor glue — are all symptoms of having written a -> desktop environment by hand. A mature integrated desktop already solves each -> one, and Debian 13 already ships one. +`tools/lab/` builds a Debian 13 container with a full Plasma 6.3 desktop in +it, runs systemd as PID 1 so the session is supervised the way a real machine +supervises it, and nests the session inside a headless sway so that `grim` +can photograph it and a real pointer can be aimed at it. -`apt-cache policy` inside a throwaway `debian:trixie` container, on the same -archive the classroom already tracks: +``` +tools/lab/lab build # the image +tools/lab/cold-start 3 # three complete cold starts, from nothing +tools/lab/lab session # compositor + Plasma session +tools/lab/lab shot NAME # docs/screenshots/NAME.png +tools/lab/lab click X Y # a real pointer press +tools/lab/kwin-script FILE # ask KWin what it believes about a window +``` -| package | Debian 13 version | +Every window measurement in this document came from `kwin-script`, not from +looking at a screenshot. Every click came through libinput. + +Three lab-only deviations, recorded because they bound what was proven: + +* **Software rendering.** llvmpipe throughout, with KWin nested in sway. This + proves behaviour and layout, not performance on classroom hardware. +* **No sound card and no optical drive.** The audio policy was therefore + tested as a pure function against the real room's reported hardware state + rather than end to end, and DVD playback was not exercised. +* **Video did not decode.** VLC came up fullscreen at exactly 1920×1080 with + the panel covered, which is the window-management result being tested, but + the picture was black under software XWayland. + +## What was measured + +Each of these changed a decision. + +### The panel gives up after three crashes + +Plasma's own unit restarts a crashed `plasmashell`, but with +`StartLimitBurst=3` inside sixty seconds. Measured: + +| | | | --- | --- | -| plasma-workspace | 4:6.3.6-2 | -| plasma-desktop | 4:6.3.6-1 | -| kwin-wayland | 4:6.3.6-1 | -| pipewire / wireplumber | 1.4.2 / 0.5.8 | -| vlc | 3.0.23 | -| cups | 2.4.10 | -| chromium | 152.0.7977.82 | -| firefox-esr | 140.15.0esr | +| kill 1 | back in 0.3 s | +| kill 2 | back in 0.4 s | +| kill 3 | **never came back** | -So a full Plasma 6.3 desktop is available **without leaving Debian**, which -means the entire `.deb`/apt OTA investment survives the change. That is the -first thing that has to be checked, because if a better desktop required -Fedora or Arch, this experiment would have to stop at a VM specification. +That is the production failure the church already had, reproduced on stock +Plasma. A four-line systemd drop-in +(`systemd/user/plasma-plasmashell.service.d/ekklesia-classroom.conf`) makes +the restart unconditional. Re-measured: **eight consecutive `SIGKILL`s one +second apart, back every time in about 2.4 s**, with the open application +window untouched. -## Status +This is the single most important line in the whole experiment, and it is a +supported override rather than a supervisor of our own. -See the checkpoint log at the end of this document. +### KWin already recovers, and takes nothing with it + +`kill -9 kwin_wayland` twice: back in **0.1 s** each time, via +`kwin_wayland_wrapper`, with the running application still on screen and its +Wayland connection intact. + +### A deleted panel rebuilds itself + +`plasma-org.kde.plasma.desktop-appletsrc` was deleted and the shell +restarted. The classroom bar came back exactly as designed, from the layout +script in the look-and-feel package. So the recovery procedure for a damaged +desktop is one file removed and one service restarted — no reflash, and no +technician in the room. + +### Plasma cannot show all five application states + +The brief asks for closed, running, active, minimised and behind. Measured by +sampling the pixels of the indicator strip rather than by eye: + +| state | indicator | icon | +| --- | --- | --- | +| closed | none — panel background `(26, 29, 32)` | full colour | +| running, behind | grey `(42, 45, 48)` | full colour | +| running, active | teal `(65, 127, 121)` | full colour | +| **minimised** | grey `(42, 45, 48)` — **identical to behind** | full colour | + +Three marks for four states. Rather than write a task manager that draws a +fourth — which is exactly the kind of custom desktop code this experiment +exists to remove — **the classroom removes the state**: clicking a launcher +always means *show me this*, never *hide it*, and the window decoration has +no minimise button. There is nothing on the idle desktop a teacher could want +to see, so minimising in this room only ever loses the lesson. + +A window minimised by some other route is still shown as running and comes +back with one click, so it can never be lost even then. + +### A window rule cannot tell a browser from its popup + +The first attempt maximised the browser with a KWin rule on `google-chrome`. +Measured: the window a lesson page opened was maximised too, because Chrome +gives both the same class — the very behaviour the current architecture +spends 314 lines preventing. + +Chrome can tell them apart, so `--start-maximized` in the launcher does it. +Measured afterwards: + +| | | +| --- | --- | +| main browser | `[0,0] 1920×1004` — the whole desktop above the bar | +| window the page opened | `[509,160] 902×683` — centred, the size the page asked for | +| main browser afterwards | unchanged | + +The secondary window carries an ordinary title bar with a close button, so +the dock's sticky Close button has nothing left to do. + +### Printing + +A page's own Print button produced a **native KDE print dialog** through +`xdg-desktop-portal-kde`, `transient=true` on the browser, centred over it, +in the classroom's colours, offering only **Elmer**. Stacking is the window +manager's ordinary behaviour and needed no rule. + +### An immutable colour scheme cannot be toggled + +Marking `[General]` immutable in `/etc/xdg/kdeglobals` froze the palette so +hard that `classroom-theme light` changed the running session and lost the +change at the next start. Immutability now covers the Kiosk restrictions and +nothing else. Measured both ways afterwards. + +### VLC calls itself `Vlc` + +KWin's window matching is case sensitive, and a lower-case substring rule +matched nothing while reporting nothing. The rule is a regular expression +now. + +## The architecture + +| | | +| --- | --- | +| distribution | **Debian 13 trixie** — unchanged, so the `.deb` and OTA work survives | +| desktop | **KDE Plasma 6.3.6** (Debian's own packages) | +| compositor | **KWin 6.3.6**, Wayland | +| panel | **Plasma panel**, one full-width bar, built by a look-and-feel layout script | +| task feedback | **Icons-only Task Manager** | +| notifications | **Plasma's own** | +| browser | **Google Chrome**, unchanged | +| Wonder Ink | Chrome app mode, **its own profile** | +| session start | **SDDM autologin** (recommended; not yet tested — see risks) | +| lockdown | **KDE Kiosk** action restrictions | +| audio | one small classroom service, event driven | + +### The bar + +One continuous full-width bar, 76 px, the height the room already has. + +* **Left** — Parent Call, drawn as a line mark in the accent rather than a + colour tile, so it reads as a different kind of thing from the applications + and is visibly not one of them. +* **Centre** — the applications: Wonder Ink, and the browser wearing + **Chrome's own icon, in colour**. That is the direct answer to the teacher + who asked whether the old monochrome glyph meant "the Internet". +* **Right** — network, volume, removable media, notifications, clock. Four + applets placed directly on the bar rather than a system tray: the tray + discovers whatever is installed and hides most of it behind an arrow, and + in the lab it collected battery, brightness, clipboard, keyboard layout, + camera, vaults and screen management on a machine with none of those. + +The bar launches applications, shows their state, and shows a little status. +It is not the Live View, not Parent Call, and not a shell framework. + +### Wonder Ink as an application + +``` +Wonder Ink chrome --app=https://app.wonderink.org/ --user-data-dir=…/wonderink +Browser chrome the room's own profile +``` + +Its window class is `chrome-app.wonderink.org__-Default` — measured on a +running session, and the launcher's `StartupWMClass` matches it exactly, so +the panel shows one button rather than a launcher and a window side by side. + +The separate profile buys something the current single-profile design cannot: +if a teacher signs into Wonder Ink through Google, that Google session is in +the Wonder Ink profile and closing the window ends it, while the church +YouTube sign-in lives in the browser's profile and is untouched. The +documented conflict in `browser.md` — that clearing `wonderink.org` leaves +the next teacher one click from the previous teacher's account — does not +arise. + +The ordinary browser therefore no longer opens Wonder Ink. Its home page is +YouTube, because a second way in through that window would put a teacher's +lesson session in the room's shared profile, which is the leak the split +exists to prevent. + +### Windows + +KWin's ordinary behaviour, with one rule. + +* Main windows fill the desktop above the bar, edge to edge, because Chrome + is told to start maximized. +* Windows a page opens float over the main one, centred, at the size the page + asked for, with a title bar and a close button. +* Dialogs are transient on their parent and stack correctly with no help. +* Fullscreen video covers the whole display: `[0,0] 1920×1080`, panel + included, no rounding, no wallpaper. +* One rule, and it is `noborder` for VLC. + +**The outer margin and the 10 px rounding were dropped.** Chrome squares its +own corners when maximised, so margin and rounding are the same decision, and +expressing "the screen minus 16 px" needs a rule generated per resolution at +every boot. That is glue in exchange for appearance, and the brief says +reliability wins. Full-bleed above the bar is also what every other appliance +does. + +### Theme + +One set of design tokens in `EkklesiaDark.colors`; the light scheme is +**generated** from it by `tools/build-light-scheme`, so a colour can only be +changed in one place. Plasma applies either to the panel, the notifications, +the decorations and every dialog at once. `classroom-theme light|dark|toggle` +is the whole switch. Accent teal `#4FB3A8`, taken from the wallpaper. + +### Wallpaper + +`config/wallpapers/classroom-calm.png` is a **stand-in**, generated by +`tools/build-wallpaper` in the sage/teal/blue/lavender palette with a river +leading into the picture and a quiet upper middle. The illustrated wallpaper +the church selected is not in this repository; when it arrives it replaces +that file and nothing else changes. + +### Lockdown + +`/etc/xdg/kdeglobals`, `[KDE Action Restrictions][$i]`. Verified by clicking, +not by reading the file: right-clicking the wallpaper produced no menu, +right-clicking the bar produced no menu, and KRunner was not running and +could not be summoned. `run_command`, `shell_access`, `logout`, +`action/lock_screen`, `action/kwin_rmb` and `plasma/plasmashell/unlockedDesktop` +all read back `false`. There is no lock screen and no session restore. + +Nothing was achieved by deleting a binary. Lifting a restriction for +maintenance is one line and a session restart. + +### Audio + +The one place custom code is still right, because no desktop expresses this +policy. `lib/classroom_audio.py` is a pure function so it can be tested +against the room that failed rather than against a machine: + +* a port that reports itself unavailable is not a candidate; +* an analog port existing is not evidence that a cable is in it; +* an explicit `AUDIO_OUTPUT=hdmi|analog` overrides everything. + +`scripts/classroom-audio` follows PipeWire's own event stream — no polling — +and on every change sets the card profile, sets the default sink, and +**moves streams that are already playing**. That last step is the fix for the +teacher who had to restart the browser after changing the output. + +Twelve test cases in `tests/test-classroom-audio.sh`, the real room among +them. The service is supervised the same way the panel is; measured, it came +back from four consecutive kills. + +### DVD + +`/usr/share/solid/actions/ekklesia-dvd.desktop` with an +`X-KDE-Solid-Predicate` for a video DVD. Plasma's device notifier offers +"Play the DVD" while a disc is in the drive and withdraws it when the disc +leaves, so a room without a drive shows nothing and no room needs +`DVD_ENABLED` edited. Nothing polls, and `classroom_optical`'s two-second +loop goes away. **Not exercised** — the lab has no optical drive. + +## Custom code this deletes + +| | lines | replaced by | +| --- | --- | --- | +| `classroom-dock` | 754 | the Plasma panel | +| `classroom-chrome-windows` | 314 | `--start-maximized`, and a window manager | +| `classroom-window` | 208 | the task manager | +| `classroom-notify` | 214 | Plasma notifications | +| `classroom-notifications` | 74 | — | +| `classroom-scale` | 57 | Plasma display scaling | +| `classroom_sway.py` | 127 | — | +| `classroom_icons.py` | 95 | the icon theme | +| `classroom_style.py` | 36 | the colour scheme | +| `classroom_optical.py` | 125 | Solid | +| sway, waybar, mako configs | — | — | +| `config/bash_profile` | 2 | SDDM | +| **total** | **≈ 2 000** | | + +Kept, because they are the classroom's own subject matter: the DVD session +and CSS setup, printer setup, provisioning, MQTT, the updater, shutdown. +Added: the audio policy and its test, the theme switch, and configuration. + +## Alternatives, and why they lost + +**Stay on sway, add Waybar.** Cheapest migration, and it loses on the thing +that matters. wlroots has no minimise, so the invented hidden workspace +stays; sway tiles, so the 314-line window arbiter stays; there is no +window-manager decoration, so the dock's sticky Close button stays; and +Waybar is one more unsupervised process to lose. It removes none of the four +reported problems. + +**Hyprland** (`experiment/hyprland`). Already measured on this repository and +already carrying two findings against it: the legacy config silently matches +nothing, and a Lua config changes the IPC language so the classroom would be +adopting a brand-new format early. For critical production UI in a room +nobody attends on a Sunday morning, that is the wrong end of the maturity +curve. + +**GNOME.** Task state, a bottom bar and application pinning all need +extensions, and extensions break on GNOME updates. That is precisely the +"several fragile independently-maintained components" the brief rules out. + +**Fedora KDE / Arch.** Both would supply the same Plasma. Neither supplies +anything Debian 13 does not, and both would cost the entire `.deb` and OTA +investment. Arch's rolling updates are the wrong risk for an appliance that +must survive unattended. **Changing distribution buys nothing here**, which +is the most useful negative result in this document. + +**Firefox ESR / Chromium instead of Chrome.** No concrete benefit. Firefox +has no app mode, which is what makes Wonder Ink an application; Debian's +Chromium ships no Widevine. Chrome is what Wonder Ink is known to work in. + +## Reliability results + +| test | result | +| --- | --- | +| cold start, 3 identical runs | 16, 16, 17 s — panel, compositor and 11 applets every time | +| `plasmashell` SIGKILL ×8 at 1 s | back every time, ≈2.4 s | +| `plasmashell` on stock settings | **dead on the third kill** — fixed by the drop-in | +| `kwin_wayland` SIGKILL ×2 | back in 0.1 s, windows survived | +| audio service SIGKILL ×4 | active every time | +| panel configuration deleted | rebuilt itself from the layout script | +| right-click desktop / panel | no menu | +| KRunner | not running, not reachable | +| light ⇄ dark | persists both ways | + +## Screenshots + +In `docs/screenshots/`. + +`v2-idle-desktop` the idle desktop · `v2-light-bar` / `v2-dark-bar` the same +bar in both themes · `state-1-closed` closed · `state-3-behind` active beside +running · `state-4-minimized` · `state-5-restored` · `v2-testpage` a +maximised browser · `v2-secondary-window` a window a page opened, floating · +`v2-print-dialog` · `v2-notification-crop` · `v2-fullscreen` · `recover-panel` +the bar rebuilt from nothing · `lock-desktop-rmb`, `lock-panel-rmb`, +`lock-krunner` the lockdown. + +## Not done, and honest about it + +* **DVD end to end.** No drive in the lab. +* **Real audio hardware.** Policy tested as a function; no card to apply it to. +* **Video decode and performance.** Software rendering only. +* **SDDM.** Recommended over the current `bash_profile` `exec`, because when + the session ends SDDM starts another one instead of leaving a teacher at a + shell prompt. Not yet configured or tested. +* **Packaging.** `tools/build-release.sh` still installs the sway-era file + list. The desktop is installed by `tools/lab/install-desktop`, which is the + shape the package needs but is not the package. +* **Parent Call and Live View.** Placed in the architecture, not built. Live + View's pin is KWin's Keep Above, which is already a title-bar button. +* **UI scale.** Plasma's own display scaling replaces `classroom-scale`; not + exercised at 1.25 or 1.5. + +## Alternate operating systems + +None is required. The recommendation stays on Debian 13, which the classroom +already runs and already packages for. + +If the church later wants Fedora KDE or Arch compared on real hardware, that +needs a VM this session could not create: + +> **Recommended next candidate: none.** +> Debian 13 + Plasma 6.3 was actually tested, in a container, on this branch. +> A disposable VM is needed only for what a container cannot show: +> 4 vCPU, 8 GiB RAM, 60 GiB disk, a real GPU or working virtio-gpu, +> an HDMI-capable audio device, and an optical drive — to test DVD +> playback, audio switching against real hardware, SDDM autologin, +> display hotplug, and performance on classroom-class hardware. + +The existing Debian classroom test VM should be kept. + +## Checkpoint log + +* Branch opened at `aadb1b8`; host recorded as unable to create a VM. +* Lab built; Plasma 6.3.6 running software-rendered at 1920×1080. +* Panel supervision measured, failure reproduced, drop-in written, re-measured. +* Classroom desktop written: colours, layout, window rules, Kiosk, launchers. +* Task states measured; minimise removed as a route. +* Secondary windows, print dialog, notifications, fullscreen, themes, lockdown + all verified on a running session. +* Audio policy written with the failing room as a test. +* Cold starts repeated. + +**Next**: SDDM autologin; move the file list in `tools/build-release.sh` to +the Plasma layout and build a `0.1.45~plasma1` package on its own channel; +Plasma display scaling at 1.25 and 1.5; then a real VM for DVD, audio and +performance. diff --git a/docs/screenshots/recover-panel.png b/docs/screenshots/recover-panel.png new file mode 100644 index 0000000000000000000000000000000000000000..d1c0246093126af7594b28415b7eedc24c0c142b GIT binary patch literal 28638 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/dev/null + docker exec classroom-v2-lab /srv/classroom/tools/lab/install-desktop >/dev/null + docker exec classroom-v2-lab sh -c ' + install -Dm644 /srv/classroom/config/chrome/policies/managed/ekklesia-classroom.json \ + /etc/opt/chrome/policies/managed/ekklesia-classroom.json + install -Dm755 /srv/classroom/scripts/classroom-audio /usr/local/bin/classroom-audio + install -Dm755 /srv/classroom/scripts/classroom-theme /usr/local/bin/classroom-theme + install -Dm644 /srv/classroom/lib/classroom_audio.py \ + /usr/local/lib/ekklesia-classroom/classroom_audio.py + install -Dm644 /srv/classroom/lib/classroom_config.py \ + /usr/local/lib/ekklesia-classroom/classroom_config.py + install -Dm644 /srv/classroom/systemd/user/ekklesia-classroom-audio.service \ + /etc/systemd/user/ekklesia-classroom-audio.service' + "$LAB" session >/dev/null + sleep 6 + + panel=$(docker exec classroom-v2-lab pgrep -c -x plasmashell || echo 0) + kwin=$(docker exec classroom-v2-lab pgrep -c -x kwin_wayland || echo 0) + applets=$(docker exec classroom-v2-lab sh -c \ + 'grep -c "^plugin=" /home/classroom/.config/plasma-org.kde.plasma.desktop-appletsrc 2>/dev/null' \ + || echo 0) + + printf 'round %s: %ss plasmashell=%s kwin=%s applets=%s\n' \ + "$round" "$(( $(date +%s) - started ))" "$panel" "$kwin" "$applets" +done