From 076eebee2421f218e3e2b255ecdba2b1324901e6 Mon Sep 17 00:00:00 2001 From: wtclaude Date: Mon, 26 Oct 2026 06:30:10 -0500 Subject: [PATCH] =?UTF-8?q?docs(website):=20demo=20mode=20step=204=20built?= =?UTF-8?q?=20=E2=80=94=20DM19,=20DM20=20(the=20app)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - DM19: the app shows a refusal as one snackbar with the server's sentence; lines that blame the role say the demo sentence; ErrorKind.DEMO_LOCKED for load screens. §3.1 rewritten to match. - DM20: the app shows the banner only; its strip names the site's sign-in page for the passwords (§6). - §10 step 4 built as Android-app#61. - android/PLAN.md §7: the demo's refusal and strip. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY --- android/PLAN.md | 5 +++++ website/DEMO_MODE.md | 19 +++++++++++++++---- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/android/PLAN.md b/android/PLAN.md index ead4bab..4e42500 100644 --- a/android/PLAN.md +++ b/android/PLAN.md @@ -776,6 +776,11 @@ Mirrors the website's "degrade gracefully" invariant: shard as **offline**, keep the rest of the app usable. - **Site maintenance** (`/public/status` = maintenance) → show the maintenance page; public shard widgets may still render (they're not maintenance-gated server-side). +- **The public demo's refusal** (`403` with `code: "DEMO_LOCKED"`, `../website/DEMO_MODE.md` §3.1) → + one app-wide snackbar with the server's sentence, spotted by an interceptor on the main client; + `HttpError` carries the body's `code`, `ErrorKind.DEMO_LOCKED` covers load screens (no Retry), and + a screen whose `403` line blames the role says the demo sentence instead (DM19). Home shows the + demo's strip from `/public/settings`' `demo` block (DM20). - **Offline caching is not a v1 requirement** (decided). The app assumes connectivity and shows clean loading/error/retry states; it does **not** ship a Room cache in v1. Cached read-only content can be added later without reworking the repository layer (its typed results already isolate the UI from the diff --git a/website/DEMO_MODE.md b/website/DEMO_MODE.md index 2322ede..b05ee83 100644 --- a/website/DEMO_MODE.md +++ b/website/DEMO_MODE.md @@ -47,8 +47,11 @@ answers: ``` The web client shows the message as a toast wherever an action fails with that code. The Android -app maps the code to a new error kind with the same wording; today it would show the generic -"Something went wrong", which is misleading for a reviewer. +app does the same with a snackbar (DM19): an interceptor on its one HTTP client spots the code on +any `403` and reports the server's sentence, so a module's own wording (a refused console command) +reaches the screen as written. The screen still shows its own failure line, except where that line +blames the role ("You don't have permission for that action", on the admin screens), which says the +demo sentence instead. Load screens get a `DEMO_LOCKED` error kind, with no Retry button. Reads are never locked, with one exception: the bridge's config (§3.5). Every page can be opened and every form filled in; only the save is refused. That keeps the demo useful as a tour of the admin panel. @@ -256,7 +259,8 @@ address with (§5), and the static logins cover every role. The seed writes both into one settings row, `demo_state`, which `PUT /admin/settings` cannot touch (§3.3). The web client shows a thin strip on every page: "Demo site — anything you change is wiped at 14:00 UTC. Sign-ins: demo-admin / demo-player … — passwords on the sign-in page". The app shows the same -strip on Home. Both read `resetsAt`, so neither needs to know the schedule. The time is said in UTC, +strip on Home, but it does not list the passwords: its strip names the site's sign-in page instead +("passwords at demo.runicgateway.com/account/login"), DM20. Both read `resetsAt`, so neither needs to know the schedule. The time is said in UTC, not converted, so it matches the schedule published on `/beta`. **The toast.** Every web request goes through one request primitive (`client/src/api/client.js`), @@ -422,6 +426,8 @@ down. | DM16 | (build) Settings are open, so `player_registration` can be switched on. Lock registration? | Yes: **`POST /auth/register`** is a Tier A lock. Invites stay open (§3.3) | | DM17 | (build) The server cannot tell which mobile session is the caller's own. What does the other-sessions lock do? | **`DELETE /auth/me/sessions/:id` is locked outright on a demo account**; `POST /auth/mobile/logout` stays open (§3.3) | | DM18 | (build, step 2) Saving an event is core's route, and core never asks a module about a step's params. How is a denied command refused at save? | **A new core hook:** an event action's optional `demoRefuses(params)`, asked at create, save and publish on the demo, refusing `403 DEMO_LOCKED` (§3.4). `MODULE_API` 1.19.0; Module-Rust then needs `^1.19.0` | +| DM19 | (build, step 4) Each app screen writes its own failure line, and the admin screens turn every `403` into "You don't have permission for that action". How does the app show a refusal? | **One app-wide snackbar** with the server's sentence, like the web's toast, and the screens whose `403` line blames the role say the demo sentence instead. `ErrorKind.DEMO_LOCKED` for load screens (§3.1) | +| DM20 | (build, step 4) Should the app's sign-in screen list the logins, as the web's does? | **No, the banner only.** The Home strip names the site's sign-in page for the passwords; Google's reviewer gets `play-review`'s from the Play Console (§6) | DNS and HTTPS for `demo.runicgateway.com` come from an external reverse proxy, which the org lead is setting up. The app sits behind it on plain HTTP; core already trusts one proxy hop for @@ -465,7 +471,12 @@ through that proxy. also covers `onDemoSeed` (DM15), and chapter 5 covers `demoRefuses` (DM18). Both hooks were decided after this list was written. Module-Rust's own pin moved to the same sha in `Module-Rust#63`. -4. **App** (`Android-app`): the `DEMO_LOCKED` error kind and the banner on Home. One PR. +4. **App** (`Android-app`): the `DEMO_LOCKED` error kind and the banner on Home. One PR. **Built + 2026-10-26** as `Android-app#61`, into `edge`, with the snackbar (DM19) and the banner only (DM20). + The refusal is read in `core/result/DemoLocked.kt` and spotted by `core/net/DemoLockInterceptor`; + the strip is `ui/demo/DemoBanner`. Checked on an emulator against `website` `main` with + `DEMO_MODE=true` and `seed:demo`: the strip, a shared login's username change refused, and the + admin site-mode switch refused with the demo sentence in place of the permission line. 5. **Deployment** (`website`): `deploy/demo/`, `demo-wipe.yml` (with §7.5's bundle step) and `demo-baseline.yml`; the first deploy on `demo-site`; setting up the default set and capturing the baseline. Then a walk of every static login, every Tier A lock and the upload lock, in the