Files
Classroom_Desktop/docs/classroom-v2-experiment.md
T
2026-09-10 13:10:25 -05:00

44 KiB
Raw Blame History

Classroom v2 architecture experiment

An open re-evaluation of the whole Ekklesia Classroom desktop stack, with reliability as the first requirement. Nothing here reaches a stable, canary or hyprland room, and nothing here changes the development host.

Base

branch experiment/classroom-v2
base commit aadb1b881977b0eb25e1ca10e060977bed31a48f
base subject fix: keep the system print dialog in front of the window that rendered it
VERSION at base 0.1.44

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 that. The branch is not built on experiment/hyprland; git ls-files on it contains no Hyprland file.

The answer, in one paragraph

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 could be tested.

OS Debian 13 trixie (13.5), kernel 6.12.90
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
resources 6 vCPU, 11 GiB RAM, 160 GiB free

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.

The lab

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.

To resume: the lab is left running, built from the package, with Parent Call's test rig not started — so the classroom's broker settings are off and nothing can reach anything. tools/lab/parent-call-rig up brings the coordinator back, and it re-establishes and re-verifies the block on the church's networks before it will start.

tools/lab/lab build          # the image
tools/lab/from-package       # a fresh machine, the .deb, and nothing else
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
tools/lab/recovery-check     # every recovery property, measured
tools/lab/parent-call-rig    # the real coordinator, safely walled off

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:

kill 1 back in 0.3 s
kill 2 back in 0.4 s
kill 3 never came back

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 SIGKILLs one second apart, back every time in about 2.4 s, with the open application window untouched.

This is the single most important line in the whole experiment, and it is a supported override rather than a supervisor of our own.

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.

Two packages cannot own one file

dpkg -i refused the first package outright: plasma-workspace already owns /etc/xdg/plasmanotifyrc, and kdeglobals, kwinrc and the rest are the same story. The room's settings live in /etc/ekklesia-classroom/xdg/ instead, with /etc/environment.d/90-ekklesia-classroom.conf putting that directory in front of /etc/xdg in XDG_CONFIG_DIRS. That is KConfig's own cascade rather than a fight over a path, and it was checked at runtime: systemctl --user show-environment carries it, and kreadconfig6 in the session reads the classroom's values.

The postinst then failed on a chmod for a command that had been removed. It walks what the package actually installed now.

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

How much of the DVD player survives this change was measured, not assumed, because the room's disc playback works and breaking it for tidiness would be a bad trade.

  • Its loading cover and control bar are gtk-layer-shell surfaces. KWin implements zwlr_layer_shell_v1 — the Plasma panel is one — and a probe overlay came up full-screen under it. So the DVD surfaces keep working and python3-gi and gir1.2-gtklayershell-0.1 stay in the package. Removing them broke classroom-dvd toggle immediately, which is how this was found.
  • Its stylesheet was the dock's. The dock is gone; the template survives as config/overlay/style.css.in, renamed to say what still uses it.
  • classroom-dvd calls classroom-window, which speaks sway's IPC. Measured with no sway running: it exits 0 and does nothing, so it degrades rather than hanging — but the DVD player's focus handling is not yet ported, and that is the largest piece of work this change leaves.
  • One behavioural difference: under sway the loading cover hid the dock; under KWin the panel draws above it, so the bar stays visible while a disc spins up. Fullscreen VLC still covers everything, which is the state that matters.

/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.

Parent Call

The coordinator already existed and its contract is frozen. It was read from /home/media/docker/parent-call/README.md and its source on the control computer, not guessed, and nothing here changes it.

What the teacher does

Click the mark on the left of the bar. A small card appears in the middle of the screen, above whatever they were reading. It already knows the room. They type a child's name and press Call parent. The card says what happened, in the coordinator's own words, and closes itself four seconds after a call is on its way.

Four steps, one of which is typing a name. The room is never asked for: this machine knows which room it is, and offering a list is a chance to pick the wrong one.

The mark

Three were put on a real panel and judged at the size the bar draws them.

tried why it lost
a bell sat inches from Plasma's own notification bell in the same bar
call-outgoing, a handset with an arrow read as a telephone feature, not as fetching somebody
an adult, a child, and one speech bubble kept

Breeze has people and it has telephones and the two never meet, so the kept mark is drawn rather than picked — on Breeze's 22-unit grid, with its ColorScheme-Text class, so it recolours with the session and needs no second file for the light theme. docs/screenshots/pc-icon-vs-notifications.png is it beside the notification bell.

It is carried by Quick Launch, on the left, apart from the applications. Two other widgets were tried first and both failed in ways worth writing down:

  • the Icon widget copies the desktop entry into the user's home and uses the copy, so an update can never change the icon or the command — it kept showing a bell out of a package that no longer contained one;
  • a task manager reads the entry live, which fixes that, but a task manager also shows every running window, so the auxiliary corner filled up with the lesson's tasks.

The states a teacher sees

Every word comes back from the coordinator in the message field. The classroom never composes one for a call, because the coordinator is the only thing that knows what happened.

coordinator status on screen what the card does
submitted Sending waits
waiting_approval Waiting for approval waits, amber
sending Sending to sanctuary waits
sent_to_sanctuary Sent to sanctuary green, closes after 4s
rejected Not approved red, offers Try again
expired Expired before it was sent red, offers Try again
unavailable Parent Call is unavailable red, no way to send
error Could not reach the sanctuary red, offers Try again

Nothing says "displayed". A ProPresenter call that succeeded means the message was accepted, not that anyone saw it, and Sent to sanctuary is the strongest claim the room is entitled to make.

Before a name can be typed at all, the card reads the two retained topics and refuses in four different ways: no broker (Cannot reach the church network), no coordinator (The sanctuary system is not running), mode off (Parent Call is unavailable, the coordinator's own wording), and no retained mode at all (Parent Call has not been set up yet). A teacher is never left believing a call went out.

How it is built

Quick Launch  ->  classroom-parent-call   a separate process
                     |
                     +-- classroom_parent_call   topics, payloads, states
                     +-- paho, one short-lived clean session

Not a Plasma applet. A broker library, a network timeout and a text entry have no business inside the only way a teacher has of getting anywhere. Measured: killing the card leaves the panel untouched, and clicking the mark again starts a new one.

Three properties the contract asks for and this keeps:

  • the status topic is subscribed before the request is published, so an answer cannot arrive before anyone is listening;
  • the session is clean and nothing is retained, so a call from last Sunday cannot replay into this one;
  • listening stops the moment a status is terminal.

Bounded everywhere: 5s to reach the broker, 1.5s for retained state, 8s for the coordinator's first answer, and 210s overall — its own REQUEST_TTL is 180. Silence is reported as No answer from the sanctuary, which is not the same as a refusal and is not shown as one.

Sixty tests cover the contract with no broker at all (tests/test-classroom-parent-call.sh).

Testing it without touching the sanctuary

tools/lab/parent-call-rig runs the real coordinator — copied from the control computer — against a broker on loopback and a ProPresenter that is a mock, and it rejects the church's own networks from inside the container before it will start. That guard is not a convention; the rig exits if either address is still reachable. It was needed: 10.40.0.2:1883 is reachable from this development host.

Verified end to end on the packaged install: request accepted, queued, approved by an operator, delivered as Name=Emma, Room=Nursery, and Sent to sanctuary shown. Also off, rejected, and the broker stopped.

DVD

It is an application now

DVD used to sit in the status row beside the clock, as though a disc were a machine reading. It is in the row with Wonder Ink and the browser, with Breeze's optical-disc mark, and Plasma gives it the same closed / running / active treatment as the others.

DVD_ENABLED is gone. classroom-desktop-layout asks the machine for /dev/sr* and removes the launcher from the layout on a room that has no drive. A computer that moves between rooms takes its drive with it, and nobody remembers to edit a file when it does.

Disc presence is not shown on the launcher. Doing that needs either polling or rewriting the panel while it runs, and rewriting the panel while it runs was measured to destroy it. The launcher tracks the drive, not the disc; a room with a drive and no disc gets a notification saying so when it is pressed.

The panel is rebuilt from the package at every login

Plasma keeps whatever panel it first built. An update could therefore change the bar in the package and never change it in a room — which is exactly what happened to an icon that outlived the file it came from.

classroom-desktop-reset removes the panel configuration before plasmashell starts, so the bar is built from the layout every time. The desktop is stateless: there is nothing in a teacher's home an update cannot reach, and a damaged panel is repaired by the next login. Nothing is lost, because Kiosk policy means a teacher never had anything in that file.

Editing the live configuration instead was tried first and destroyed the panel — the shell owns that file and rewrites it, so an edit races with it. That is why this runs Before=plasma-plasmashell.service.

The last of sway

The DVD player was the only thing still speaking a compositor the room no longer runs. It asked sway's tree whether VLC's window had really gone fullscreen, because VLC reports playing while its window is still the black startup one the loading cover exists to hide.

VLC's own status was measured as a replacement and is not one. It reports <fullscreen>true</fullscreen> in the same 250ms poll as <state>playing</state>, because the flag is the option it was started with and not a report about its window. So the cover is held for a floor (MINIMUM_COVER_SECONDS) and still capped by the deadline that was already there, and the compositor is not asked anything at all.

Focus-or-launch went the same way: the session already takes an exclusive lock so a second one cannot start, and bringing an existing one to the front is the panel's job.

Gone with it: classroom-window, classroom_sway.py, and classroom_chrome.py, which turned out to be reachable from nothing. One mention of sway remains in the whole tree, and it is the upgrade step in postinst that removes the sway-era login shell from a room being upgraded. Nothing executes it, imports it, or asks it anything.

Still unproven, and it is the same gap as before: playback end to end. The lab has a drive with no readable disc and no libdvdcss, so VLC exits on --key-length=. The loading cover, the control bar and the session's own lifecycle all run; the picture does not.

Audio: how to test it on the hardware

Nothing below can be done in the lab: the container has no sound card, so the policy is tested as a function and applying it is not tested at all. classroom-audio show prints what it can see and what it would choose, and changes nothing — start there.

  1. Television on before the computer. Boot with the TV on and an input selected. Expected: sound comes out of the TV with nothing touched. Check with classroom-audio show; the chosen port should be an hdmi-output-*.
  2. Television off during boot, switched on afterwards. Boot with the TV off, wait for the desktop, then switch the TV on. Expected: within a couple of seconds the output moves to HDMI by itself. This is the case the room actually failed on, and the one worth watching.
  3. Browser already playing. Start a YouTube video, then switch the TV on or change the output. Expected: the sound follows without restarting Chrome. That is the whole point of moving existing streams; if Chrome still has to be restarted, the policy is not doing its job and journalctl --user -u ekklesia-classroom-audio will say how many streams it moved.
  4. Unplug and replug HDMI while something is playing. Expected: falls back, then returns.
  5. VLC already playing, same as 3.
  6. Overrides. AUDIO_OUTPUT=analog in /etc/ekklesia-classroom/device.conf, restart the user service, and confirm it obeys even with the TV on. Then hdmi, then back to auto.

What to capture if something is wrong: classroom-audio show, pactl list cards short, pactl list sinks short, and the service journal. The decision is a pure function, so a bad choice can be reproduced as a test case from what those print.

Custom code this deletes

Deleted on this branch across both milestones, and counted rather than estimated:

lines replaced by
scripts/classroom-dock 754 the Plasma panel
scripts/classroom-chrome-windows 314 --start-maximized, and a window manager
tests/test-classroom-chrome-windows.sh 280 —
config/dock/style.css.in 102 the colour scheme
scripts/classroom-notifications 74 Plasma notifications
config/sway/config 62 kwinrc
config/mako/config.in 48 plasmanotifyrc
config/waybar/* 55 the Plasma panel
config/bash_profile, getty autologin 6 SDDM
config/icons/browser.svg — Chrome's own icon
scripts/classroom-window 208 the panel, and a lock the DVD session already took
lib/classroom_sway.py 127 —
lib/classroom_chrome.py 66 nothing: it was reachable from nowhere
tests/test-classroom-window.sh 214 —
config/icons/* (dock set) — Breeze, and two marks the classroom draws
total ≈ 2 310

Rewritten rather than deleted: classroom-browser (41 → 30 lines, it no longer decides launch-or-focus-or-minimise), classroom-scale (sway output scale → kscreen-doctor), classroom-notify (found the session by looking for sway and refused to run without mako; now looks for the compositor and the shell).

Nothing in the classroom speaks to a compositor any more. classroom_icons.py, classroom_style.py and classroom_optical.py remain, reachable only from the DVD player and the MQTT notifier: the DVD overlay draws its own layer-shell surfaces and still needs the icon and stylesheet helpers the dock shared, and disc detection is still ours.

Added since: the audio policy and its tests, the theme switch, Parent Call and its contract module, the layout tailor, the panel reset, and configuration.

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.

The package was built and its contents checked: every /usr/local path any classroom command names is in it.

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.

Recovery, measured against the package

tools/lab/recovery-check, run after tools/lab/from-package, against the lab exactly as the .deb built it:

panel, 8 × SIGKILL one second apart back every time, 2.2–2.5s
compositor, 2 × SIGKILL back in 0.1s
audio policy, 3 × SIGKILL active every time
Parent Call killed panel untouched
browser killed panel untouched
afterwards panel, compositor, 10 applets, audio all standing

The drop-in that makes the panel restart unconditional is in the package, not only in a hand-edited session: /etc/systemd/user/plasma-plasmashell.service.d/.

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
memory, 3 samples over 6 minutes flat: plasmashell 399 MB, KWin 141 MB under llvmpipe
package install on a clean machine clean, and the session came up from it

The package

tools/build-release.sh builds it, and it was installed on a fresh machine and booted from rather than described. ekklesia-classroom_0.1.45 is 65 files: the desktop under /etc/ekklesia-classroom/xdg, a look and feel package, two colour schemes, the launchers, the Solid action, two supervised user services, and the classroom's own commands. Dependencies swap sway, swaybg, waybar, mako-notifier, python3-gi, gir1.2-gtklayershell-0.1 for plasma-desktop, plasma-workspace, kwin-wayland, plasma-nm, plasma-pa, sddm, and it Conflicts with the welcome tour, the software centre and the phone pairing daemon that plasma-desktop would otherwise recommend into a classroom.

Checked mechanically: every /usr/local path any classroom command names is in the package.

The OTA mechanism is untouched. This is an ordinary version on the ordinary channel structure, and an experimental build would go out as 0.1.45~plasma1 on a channel of its own, exactly as the Hyprland milestone did.

Screenshots

Twenty-nine, in docs/screenshots/, indexed in screenshots/README.md — including the two that record failures rather than successes.

Not done, and honest about it

  • DVD end to end. The overlay surfaces and disc detection were checked; playback was not, and the focus helper is not ported. See the DVD player above.
  • 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. classroom-scale now asks kscreen-doctor instead of the compositor, and 1.25 was applied and read back correctly. The look of it could not be judged: a nested session shrinks its own canvas to the logical size instead of keeping the display's mode, so everything got smaller rather than larger. On a real output it does the opposite. Judge this one on hardware.
  • Network states. plasma-nm is in the bar and live — it correctly showed a problem state throughout, because Docker configures the container's interface before NetworkManager sees it and NM reports limited connectivity for it. Online, connecting and offline transitions need a machine whose network NetworkManager actually owns.

Television control, and where CEC will fit

The devices arrive today. Nothing here guesses at their protocol and no CEC code has been written.

What the architecture needs to leave room for: the volume control in the bar today is the computer's volume, and once the television is under CEC control that is the wrong thing for a teacher to be adjusting. The teacher should be turning the television up, not the computer.

The shape that keeps Plasma out of the hardware:

panel button / notification        presentation only
        |
        v
ekklesia-classroom-tv.service      a small supervised user service
        |                          the same shape as the audio policy
        +-- backend: CEC over the adapter
        +-- interface: on, off, volume up, volume down, mute, input

Three properties to hold to:

  • Plasma is presentation. The panel may show state and send an action; it must not contain the protocol. That is why the audio policy is a service and not a plasmoid, and this should follow it.
  • The interface is verbs, not device commands. "Volume up" is the contract; whether that is a CEC user-control-pressed opcode or something else is the backend's business.
  • Supervised and failure-tolerant. A television that is off, an adapter that is unplugged, or a set that ignores CEC must degrade to nothing happening, never to a stuck classroom.

When the hardware has been exercised, the computer volume applet comes out of the bar and a television control goes in. Until then it stays, because a room with no sound control at all is worse.

Live View, and whether this desktop can carry it

Not implemented. What the KDE architecture already provides for it:

  • A small status widget on the idle desktop. A Plasma applet, or — preferring less custom desktop code — a notification when the sanctuary goes live plus a launcher in the bar.
  • A preview that expands. A separate application window, not something embedded in the panel. The panel may launch it and show that something is live; it must not be the player.
  • Pin above other applications. This is KWin's Keep Above, which is already a window state with a title-bar button, and a window rule can force it for the preview's class. Nothing has to be invented, which was the open question when this experiment started.
  • Unpin returns it to normal stacking. The same button.

The likely media path (MediaMTX, WHEP) is a property of the player application and does not touch the desktop architecture.

Unresolved risks

In the order I would worry about them.

  1. DVD playback has never run end to end on this desktop. The port off sway is done and nothing asks a compositor anything, but the lab has a drive with no readable disc and no libdvdcss, so VLC exits before a picture. A room that uses DVDs should not take this build until a real disc has played, the cover has dropped at the right moment, and eject has returned cleanly to the desktop.
  2. The loading cover is now timed, not observed. It used to wait for the window manager to confirm VLC was fullscreen. It waits for a floor instead, because VLC's own fullscreen flag was measured to be the option it was given rather than a report about its window. On real hardware the disc is far slower than the floor, so this should not show — but if the cover ever drops onto a black VLC window, this is why, and the fix is to raise MINIMUM_COVER_SECONDS.
  3. Parent Call has never had a real key pressed into it. Keyboard injection cannot reach the nested session in this lab at all — the same limit that stopped it reaching Chrome — and the one tool that tries disturbs the session enough to close the card. The name was typed through a lab-only --child argument. Type a name on the bench before a Sunday.
  4. SDDM is configured and untested. It cannot be exercised in a container, and it is the thing that starts the session. If the config is wrong the room boots to nothing. This is the first thing to check on a VM or a bench machine, and it is why the checklist below starts there.
  5. No real audio hardware has been through the policy. The decision is well tested; applying it is not. A wrong pactl set-card-profile on real hardware is a silent room.
  6. Plasma is a much larger surface than sway. More code, more services, more that can regress in a Debian point release. The mitigations are that all of it is supervised, that Debian 13 is frozen apart from security updates, and that the OTA channel structure already lets a build be held back. But it is a real trade and it should be named.
  7. Performance is unmeasured on classroom hardware. Everything here ran on llvmpipe, which says nothing useful about an old office machine with Intel graphics. Plasma with compositing is heavier than sway.
  8. Chrome remains a third-party repository. Unchanged from today, but still the one part of the stack Debian does not update.
  9. The wallpaper is a stand-in. The real illustration has not been seen against this palette, and is to be supplied separately.
  10. The disc launcher tracks the drive, not the disc. A room with a drive shows the DVD button whether or not anything is in it.

Would I trust it on a classroom computer, unattended, for a Sunday?

For a room that does not use DVDs: yes, after the checks below. The reasoning is not that Plasma is nicer. It is that the four failures the church actually reported are all failures of code we wrote, three of them are gone because the code is gone, and the fourth — the panel disappearing — went from reproducible on the third crash to survived eight kills and is now a supervised system service rather than a process started by a compositor and forgotten.

For a room that uses DVDs: not yet. Finish the focus port and play a real disc first.

Before any room:

  1. Boot a bench machine from the package and confirm SDDM autologins, and that killing the session brings it back rather than a shell.
  2. Put a television on HDMI, switch it on after the computer, and confirm sound arrives without touching anything, with the browser already open. The full procedure is under Audio: how to test it on the hardware.
  3. Press Parent Call and type a name with a real keyboard. Then do it with the coordinator's mode off, and with the broker stopped.
  4. Print to Elmer.
  5. Play a DVD, if that room uses them: cover, picture, controls, eject.
  6. Leave it running from Friday to Sunday.

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: eleven, identical.
  • Package built, and the file conflict and the stale postinst it exposed both fixed.
  • tools/lab/from-package run: fresh machine, .deb, session up, both applications launched, one button each.
  • Parent Call built against the coordinator's own frozen contract, and proven end to end on a rig that rejects the church's networks before it starts.
  • Parent Call's mark replaced twice and judged on a real panel.
  • The last of sway removed: the DVD player asks no compositor anything, and classroom-window, classroom_sway and classroom_chrome are gone.
  • DVD moved out of the status row into the applications, and the launcher now follows the machine's drive rather than DVD_ENABLED.
  • The panel is rebuilt from the package at every login, so an update can reach a room that has already run.
  • Recovery re-measured against the packaged build, not a hand-edited session.

Next, in order:

  1. On the bench, tonight. Type a name into Parent Call with a real keyboard. Switch a television on after the computer and listen. Play a disc. These three are the only things left that a container cannot answer, and each of them is a Sunday-morning path.
  2. Publish 0.1.45~plasma1 to a channel of its own, the way the Hyprland milestone did, so a stable or canary room can never be offered it.
  3. Bench-test SDDM: autologin, and that ending the session brings the classroom back rather than a shell.
  4. When the CEC adapters have been exercised, replace the computer volume applet with a television control, behind the service boundary in Television control, and where CEC will fit.
  5. Live View, when the rest is quiet.