fix(display): Stage 0 display continuity, revocation, and kiosk recovery #92

Open
ekklesia-bot wants to merge 7 commits from stage0/display-runtime-continuity into main
Member

Stage 0 (Signage production readiness): the platform half. Signage's side follows in a separate PR that pins this one.

Defects fixed

Defect (reproduced on main with the display-device harness) Root cause Fix
A browser online event tore down and remounted a running display (connectivity-blip: 1 remount + offline badge per blip) DisplayApp bumped attempt, which reset state to loading and re-keyed the frame Re-resolve the descriptor in the background; remount only if descriptor or principal changed; keep the frame on transient errors (ADR 0043 requires re-resolution, not a remount)
A device revoked while its display was open kept playing and polled 403s forever (revoke scenario failed) DisplayUiFrame never wired onAuthorizationResponse; nothing re-checked authorization A refused (401/403) module request triggers a throttled revalidation; a denied descriptor returns the screen to pairing (ADR 0036)
A kiosk that boots before its server stops on "could not be reached" until someone presses Retry No automatic retry when the network is up but the API is not Backoff retry from failed/unavailable states (2 s → 60 s)
A module upgraded on the server is never picked up by a running display Nothing re-resolved the descriptor while running Background re-resolution every 5 min; the same answer disturbs nothing
Offline indicator flashed for any blip and said "waiting for Ekklesia" on a lobby wall No grace period; hard-coded platform branding 10 s grace; neutral wording
Revoke acted on one click No confirmation Shared in-product Dialog explaining the consequence (DESIGN §21.4)
The harness's revoke scenario left the kiosk revoked, so every later display check failed by construction (why Signage could never run it) Scenario stopped at the pairing screen It now re-pairs the screen and asserts it resumes and keeps its Signage roster entry

Also

  • CLAUDE.md updated: separate module repositories, parent ROADMAP.md/DESIGN.md authoritative, ownership/branding/token/dark-mode/no-native-dialog rules, displays transitional until Stage 1, branch-per-PR, no push to main, no release tags. It previously described in-repo modules/* and manifest v7.
  • deploy/display-kiosk.md: reproducible kiosk setup (single launcher, persistent profile, systemd unit, pairing and the fixed 180-day credential, outage behaviour, offline cold-start limit).

No ADR is changed or superseded: the behaviour now matches ADR 0036 and ADR 0043 as written. No contract, schema, migration, or credential format changes; existing paired displays are unaffected.

Verification

  • pnpm ci:verify passes (shell 229 tests, API 511, root 152); compose/deploy config valid.
  • New tests: surfaces/shell/tests/display-app.test.tsx (8 continuity/revocation/retry cases, all failing on main), surfaces/shell/tests/device-panel.test.tsx.
  • PR CI on the runner: all 8 jobs green at bae4ff3 (verify, infrastructure/migration smoke, package release/publication, image build, both deploy topologies).
  • Display-device harness, split topology, with the Signage Stage 0 package: pair, restart, two-pages, connectivity-blip, session-blip, stream-failure, caching-probe, stream-recycle, revoke (re-pair) all pass, then API restart, container recreation, disable/enable, and remove/reinstall: Zayin/Signage#18 run 274.
  • Known: the publish-packages.yml workflow and docs/development/releases.md / docs/module-development/README.md still name the old git.riverofglorymn.org/.../River_of_Glory_Church registry, which now answers 502. The packages live at git.zayin.net/api/packages/Zayin/npm/. Not changed here (infrastructure; flagged for the maintainer before the next package release).

Deferred to Stage 1

Settings → Displays, global display names, the hard-coded platform name in the Dashboard shell header/login, responsive shell, theme editor.

🤖 Generated with Claude Code

Stage 0 (Signage production readiness): the platform half. Signage's side follows in a separate PR that pins this one. ## Defects fixed | Defect (reproduced on `main` with the display-device harness) | Root cause | Fix | |---|---|---| | A browser `online` event tore down and remounted a running display (`connectivity-blip`: 1 remount + offline badge per blip) | `DisplayApp` bumped `attempt`, which reset state to `loading` and re-keyed the frame | Re-resolve the descriptor in the background; remount only if descriptor or principal changed; keep the frame on transient errors (ADR 0043 requires re-resolution, not a remount) | | A device revoked while its display was open kept playing and polled 403s forever (`revoke` scenario failed) | `DisplayUiFrame` never wired `onAuthorizationResponse`; nothing re-checked authorization | A refused (401/403) module request triggers a throttled revalidation; a denied descriptor returns the screen to pairing (ADR 0036) | | A kiosk that boots before its server stops on "could not be reached" until someone presses Retry | No automatic retry when the network is up but the API is not | Backoff retry from failed/unavailable states (2 s → 60 s) | | A module upgraded on the server is never picked up by a running display | Nothing re-resolved the descriptor while running | Background re-resolution every 5 min; the same answer disturbs nothing | | Offline indicator flashed for any blip and said "waiting for Ekklesia" on a lobby wall | No grace period; hard-coded platform branding | 10 s grace; neutral wording | | Revoke acted on one click | No confirmation | Shared in-product `Dialog` explaining the consequence (DESIGN §21.4) | | The harness's `revoke` scenario left the kiosk revoked, so every later display check failed by construction (why Signage could never run it) | Scenario stopped at the pairing screen | It now re-pairs the screen and asserts it resumes and keeps its Signage roster entry | ## Also - `CLAUDE.md` updated: separate module repositories, parent `ROADMAP.md`/`DESIGN.md` authoritative, ownership/branding/token/dark-mode/no-native-dialog rules, displays transitional until Stage 1, branch-per-PR, no push to `main`, no release tags. It previously described in-repo `modules/*` and manifest v7. - `deploy/display-kiosk.md`: reproducible kiosk setup (single launcher, persistent profile, systemd unit, pairing and the fixed 180-day credential, outage behaviour, offline cold-start limit). No ADR is changed or superseded: the behaviour now matches ADR 0036 and ADR 0043 as written. No contract, schema, migration, or credential format changes; existing paired displays are unaffected. ## Verification - `pnpm ci:verify` passes (shell 229 tests, API 511, root 152); compose/deploy config valid. - New tests: `surfaces/shell/tests/display-app.test.tsx` (8 continuity/revocation/retry cases, all failing on `main`), `surfaces/shell/tests/device-panel.test.tsx`. - PR CI on the runner: all 8 jobs green at `bae4ff3` (verify, infrastructure/migration smoke, package release/publication, image build, both deploy topologies). - Display-device harness, split topology, with the Signage Stage 0 package: `pair`, `restart`, `two-pages`, `connectivity-blip`, `session-blip`, `stream-failure`, `caching-probe`, `stream-recycle`, `revoke` (re-pair) all pass, then API restart, container recreation, disable/enable, and remove/reinstall: Zayin/Signage#18 run 274. - Known: the `publish-packages.yml` workflow and `docs/development/releases.md` / `docs/module-development/README.md` still name the old `git.riverofglorymn.org/.../River_of_Glory_Church` registry, which now answers 502. The packages live at `git.zayin.net/api/packages/Zayin/npm/`. Not changed here (infrastructure; flagged for the maintainer before the next package release). ## Deferred to Stage 1 Settings → Displays, global display names, the hard-coded platform name in the Dashboard shell header/login, responsive shell, theme editor. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
ekklesia-bot added 5 commits 2026-10-13 02:47:27 +00:00
Core's agent guidance still described modules living in this monorepo and
manifest v7, and said nothing about the product rules that now govern every
change. Record that first-party modules live in their own Zayin repositories
and install as Module Packages, that the parent ROADMAP.md and DESIGN.md are
authoritative for product direction, the ownership, branding, token, dark-mode
and no-native-dialog rules, that display pairing is transitional until Stage 1,
and the branch-per-PR, no-push-to-main, and no-release-tag workflow.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Every browser 'online' event reset the display host to its loading state and
mounted the module again: a fresh viewer session, a fresh stage, and every
cached object URL released, on each Wi-Fi blip. And a device revoked while its
display was open never noticed; it kept playing cached content and polling
403s until someone reloaded the page.

The host now re-resolves its descriptor in the background (ADR 0043) and only
replaces the frame when the answer actually changed: a new version or
principal remounts, a refusal returns the screen to pairing (ADR 0036), and
any other failure keeps the running display. A module request refused with
401/403 prompts the same revalidation, throttled, so revocation is noticed
within one heartbeat. The offline indicator waits ten seconds before
appearing, so a momentary blip shows nothing, and no longer names the
platform on a lobby wall.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Revoke acted on a single click, yet it strands the screen until someone
re-pairs it in person. Explain the consequence in the shared in-product
Dialog first (DESIGN 21.4), with an obvious Cancel.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Stage 0 requires that kiosk setup can be reproduced from documentation, and
there was none. Cover the address, a single-launcher Chromium command with a
persistent profile, a systemd user unit, pairing and the fixed 180-day device
credential, what the screen does through each kind of outage, the known offline
cold-start limit, and why a second launcher produces duplicate windows.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
fix(display): retry a display that could not start, and re-resolve a running one periodically
CI / verify (pull_request) Successful in 1m7s
CI / infrastructure-smoke (pull_request) Successful in 38s
CI / package-release (pull_request) Successful in 50s
CI / migration-smoke (pull_request) Successful in 2m9s
CI / image-build (pull_request) Successful in 57s
CI / deploy-smoke (same-origin) (pull_request) Successful in 53s
CI / deploy-smoke (split) (pull_request) Successful in 2m35s
CI / package-publication (pull_request) Successful in 1m1s
9331a8f7df
A kiosk that booted before its server, as after a power cut, stopped on 'This
display could not be reached' until someone pressed Retry: with the network
up, no online event ever came. It now retries on its own, from two seconds
doubling to a minute. A running display also re-resolves its descriptor every
five minutes in the background, so a module upgraded on the server is picked
up without a reload, and the same answer disturbs nothing.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Zayin_Admin added 1 commit 2026-10-13 03:02:36 +00:00
test(deploy): re-pair the kiosk after the revoke scenario
CI / verify (pull_request) Successful in 1m16s
CI / infrastructure-smoke (pull_request) Successful in 37s
CI / package-release (pull_request) Successful in 49s
CI / migration-smoke (pull_request) Successful in 2m14s
CI / image-build (pull_request) Successful in 1m0s
CI / deploy-smoke (same-origin) (pull_request) Successful in 51s
CI / deploy-smoke (split) (pull_request) Successful in 2m35s
CI / package-publication (pull_request) Successful in 1m1s
bae4ff3c6a
The revoke scenario left the kiosk revoked, so any display check after it -
the post-recreation restart check in particular - failed by construction.
That is why Signage could never run it. Finish the scenario the way the
roadmap states the requirement: a revoked screen shows a fresh code, is paired
again, resumes without a reload, and for Signage keeps its existing roster
entry rather than appearing as a new screen.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Zayin_Admin added 1 commit 2026-10-13 03:30:19 +00:00
docs(deploy): describe the in-use credential renewal in the kiosk guide
CI / verify (pull_request) Successful in 1m28s
CI / infrastructure-smoke (pull_request) Successful in 40s
CI / package-release (pull_request) Successful in 47s
CI / image-build (pull_request) Successful in 59s
CI / migration-smoke (pull_request) Successful in 2m8s
CI / deploy-smoke (same-origin) (pull_request) Successful in 49s
CI / deploy-smoke (split) (pull_request) Successful in 2m38s
CI / package-publication (pull_request) Successful in 1m1s
5e9433d96b
ADR 0056 (Zayin/Ekklesia#94) replaces the fixed 180 days from pairing with
180 days from last use, renewed while the screen is in use; the guide no
longer tells operators to track pairing dates.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
All checks were successful
CI / verify (pull_request) Successful in 1m28s
Required
Details
CI / infrastructure-smoke (pull_request) Successful in 40s
Required
Details
CI / package-release (pull_request) Successful in 47s
Required
Details
CI / image-build (pull_request) Successful in 59s
Required
Details
CI / migration-smoke (pull_request) Successful in 2m8s
Required
Details
CI / deploy-smoke (same-origin) (pull_request) Successful in 49s
Required
Details
CI / deploy-smoke (split) (pull_request) Successful in 2m38s
Required
Details
CI / package-publication (pull_request) Successful in 1m1s
Required
Details
This pull request doesn't have enough required approvals yet. 0 of 1 official approvals granted.
You are not authorized to merge this pull request.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin stage0/display-runtime-continuity:stage0/display-runtime-continuity
git checkout stage0/display-runtime-continuity
Sign in to join this conversation.
No Reviewers
No labels
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Zayin/Ekklesia#92