docs: plan report/block/hide, decide M8's open items, plan demo content

- website/USER_SAFETY.md (new): report, block and hide on the website and
  in the app, S1-S5 decided by the org lead, five questions open.
- android/PLAN.md: M8's three open items decided (keep the rating and add
  report/block/hide; conservative data safety; UnifiedPush on specialUse
  with restart on boot), the order to the cutover, and M21 (Teams and the
  team forum in the app, planned, not scheduled).
- website/DEMO_MODE.md: DM32 and section 11, the demo content plan (two
  seed passes; moderation, Teams/forum/reports, event history, Rust stats).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
This commit is contained in:
2026-10-29 07:27:21 -05:00
parent 6aeb8afa52
commit df3d43f355
4 changed files with 278 additions and 13 deletions

View File

@@ -1003,20 +1003,46 @@ push, and Play (M6–M8) follow the designed app.
for 14 days in a row** before it can apply for production. runicgateway.com `/beta`
collects them.
**Open, each waiting for a decision:**
**The three open questions, decided (org lead, 2026-10-29):**
- **Content rating.** The questionnaire answered "Social or Communication: moderated chat;
users or user-generated content can be reported". The app shows other players' chat (the
Rust feed) but has no report or block action, and Play's user-generated-content policy
expects both where that is declared. Either retake the questionnaire, or add report/block
to the app.
- **Data safety's premise.** The generated notes answer "not collected" almost everywhere,
because the app talks only to a deployment the user chose. The notes call it the answer
most likely to be questioned in review. Declaring user IDs and messages as collected,
encrypted in transit and needed for the app to work, is the conservative alternative.
Rust feed) but had no report or block action, and Play's user-generated-content policy
expects both where that is declared. **Decided: keep the answer and make it true** with
report, block and hide, on the website and in the app. The plan of record is
[`website/USER_SAFETY.md`](../website/USER_SAFETY.md): a block lives on the account and
applies everywhere; a member can hide an item for themself, and staff can hide it for
everyone. In the app it goes on chat lines. The team forum in the app is planned as M21.
- **Data safety's premise.** The generated notes answered "not collected" almost everywhere,
because the app talks only to a deployment the user chose. **Decided: the conservative,
more accurate answers.** Each kind of data the app sends to the deployment is declared
collected, not shared, encrypted in transit and required for the app to work. That is
what Play counts as collection, since the data leaves the device, whoever runs the server.
runicgateway.com's generator (`scripts/playDataSafety.mjs`, from `src/data/collection.mjs`)
changes to say so after an audit of what the app actually transmits. The published privacy
page keeps saying that *we* receive nothing, which stays true.
- **The push foreground service.** Play asks for a declaration (and a video) for the
`dataSync` service that holds the ntfy connection. Google reserves `dataSync` for transfers,
and Android 15 limits it to six hours a day. This brings forward §13's "FCM flavor for
Play?" question.
`dataSync` service that holds the ntfy connection. Google reserves `dataSync` for
transfers, and Android 15 limits it to six hours a day. **Decided: keep UnifiedPush.** The
service changes to the `specialUse` type with a `PROPERTY_SPECIAL_USE_FGS_SUBTYPE` reason,
as ntfy does on Play; `specialUse` has no daily limit. **Push also restarts after a
reboot** when the member had it on and is signed in, which `specialUse` allows from
`BOOT_COMPLETED` and `dataSync` does not. This also fixes a crash: `PushService` does not
handle `onTimeout`, so on Android 15+ with target 36 (`edge` since step 1) the six-hour
cut-off would crash the app. The declaration's video is recorded on the emulator (turn push
on, the ongoing notification, a push arriving), and the org lead uploads it. §13's "FCM
flavor for Play?" is therefore answered **no**.
**The order from here** (org lead, 2026-10-29):
1. **Push** (`edge`): `specialUse` and the restart on boot. One PR.
2. **Data safety** (runicgateway.com): the audit and the generator. One PR.
3. **Report, block and hide** across `website`, `Module-Rust`, the kit and this app
(`USER_SAFETY.md` §7).
4. **The cutover**, `edge` → `main`, once 1-3 have landed, so the first Play bundle carries
all of it. **Before it merges, the org lead enrolls the existing keystore as the app-signing
key** in Play Console (App integrity, "use my own key"). `PLAY_SERVICE_ACCOUNT_JSON` is
already set, so the release run will call Play, and the signing choice is permanent. On the
draft app, CI's commit is refused as expected; the first bundle is then uploaded by hand
(step 2 above).
10. **M9 — Native SSO login** (post-v1, additive; independent of M8): in-app "Sign in with Google /
Discord" via the **Mobile SSO Authorization Bridge** (§4.2). **Backend-first**, mirroring M7's
split:
@@ -1828,9 +1854,39 @@ push, and Play (M6–M8) follow the designed app.
just as wrong. A maintenance error kind, read from that body, is the likely fix. It is
outside M20 and waits for a decision.
22. **M21 — Teams and the team forum in the app** (post-v1; planned 2026-10-29, **not scheduled**).
The org lead asked for it with M8's user-safety decision (`../website/USER_SAFETY.md` S4). It
revives the deferred *Platform Teams in the app* below, whose revival trigger (Team pages,
roster and forums on the website) has long since been met.
**Scope:**
- **A Teams entry** in the menu: the member's own Teams (`GET /player/teams`) and the public
list (`GET /public/teams`), then a Team's page (`/public/teams/:slug`, its members and
activity).
- **The forum**, for a Team the member can read (`GET /player/teams/:slug/access`): threads
(`…/forum/threads`), a thread's posts (`…/forum/threads/:id`), reply and new thread
(`POST …/threads`, `…/threads/:id/posts`), and editing their own post (`PATCH …/posts/:id`).
Leaders get pin, lock and hide through the existing `…/moderate` routes.
- **Report, hide for me and block** on every thread and post, as on chat lines
(`USER_SAFETY.md` §4).
- **Deep links** for the four Team push streams, which today open the app and no more, and the
per-Team mute list (`GET|PUT /auth/me/notifications/teams`; its DTO field has no default,
see the trap noted below).
- **Uploads: view only** to begin with. Sending a file from the phone is a later decision,
because uploads carry the operator's liability acknowledgement (`TEAMS.md` §5.5).
**What it changes for Play:** members can then post from the app, so the data-safety
declaration gains "user-generated content: collected" and the content rating stays as
answered. runicgateway.com's `collection.mjs` gains the row in the same change.
**Before it starts:** USER_SAFETY.md's build order is done (the report/block/hide routes M21
reuses), and the questions it will raise (offline drafts, uploads, the order of the Teams tab
in the menu) go to the org lead first.
### Deferred (not a milestone)
- **Platform Teams in the app** — **deferred 2026-08-17, no app work scheduled.** The website is
- **Platform Teams in the app** — **revived as M21 on 2026-10-29** (above); the text below is the
2026-08-17 deferral, kept for its reasoning. **Deferred 2026-08-17, no app work scheduled.** The website is
building Teams as a core platform primitive (pages, roster, activity feed, forums, notifications —
[`../website/TEAMS.md`](../website/TEAMS.md)), and the app has no story for any of it. Deferred
deliberately rather than scheduled: this milestone list is already behind, and adding a large new
@@ -2147,7 +2203,8 @@ it is a UX convenience, not a v1 requirement — deferred at M3, descoped at M6;
No backend publish token — **decided** (the content-free-tickle design does not require one; optional
`NTFY_PUBLISH_TOKEN` is honored if ever set).
- FCM flavor: build it for the Play release or ship Play on UnifiedPush too? Decide at M8. (The M7
Part 2 `PushTransport` seam keeps this swap cheap.)
Part 2 `PushTransport` seam keeps this swap cheap.) **Decided at M8 (2026-10-29): UnifiedPush on
Play too**, with the service moved to the `specialUse` type.
- Deep-link / share targets for wiki pages and posts (share/open-in-app). *Notification-tap* deep-links
are **resolved** for M7 Part 2 (stream→screen map, work item 7).
- iOS: none planned (this is the Android-only choice); revisit only if cross-platform is later