The app now sends reports, blocks and hides (USER_SAFETY.md step 4,
Android-app#64). A new app row, app-safety, declares them: App activity ->
Other user-generated content (the report's note) and Other actions (the
report, block and hide themselves), collected, not shared, required. Other
actions moves from "not collected" to collected in PLAY_DATA_SAFETY.md.
The deployment row for posted content now says a report keeps a copy of
what it reported, and that blocks and hides are kept on the account and
deleted with it (user_blocks and user_hidden_items cascade).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
Step 5 of docs/website/ACCOUNT_DELETION.md (§4.5). The page explains how
to delete an account on a community's site and in the app, what goes,
what stays and for how long, and who to ask when you cannot sign in. It
cannot delete anything, because we hold no account.
- The facts are one deployment row in collection.mjs (deletion.goes,
.stays with a keep each, .staff), in the website's own words. /privacy
section 3 renders it as prose, the page renders its lists, and
PLAY_DATA_SAFETY.md prints them under the console's deletion questions.
- The app rows that a deletion touches (session, trust token, push,
inbox snapshot) say so. The Data Safety notes now answer "yes" to
account deletion and name the URL. lastUpdated is 2026-10-30.
- D85: linked from the footer's legal bar, /privacy sections 2 and 3,
and /app/'s "Two ways to get it".
- D86: someone who cannot sign in is sent to their community's operator,
never to us.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
Play counts data that leaves the device as collected, whoever runs the
server it lands on. The generated Data Safety notes answered "not
collected" because we run no server, which is true of /privacy and the
wrong answer to the form (org lead, 2026-10-29, Android PLAN.md M8).
An audit of the app's edge (every body, query and header in data/api/)
found more leaving the phone than section 2 listed. New rows: the
User-Agent, game-account links and creation, the Contact form, staff
posts and moderation, notification read state and preferences, search
text. The sign-in and trusted-device rows now say what is sent (the
credentials, the device's make and model).
- PlayMapping: a list of console types, plus required and purposes,
checked against PLAY_DATA_TYPES / PLAY_PURPOSES (the first version
filed "Other user-generated content" under Messages).
- The generator answers per data type, lists every type not collected,
and answers the form's security questions.
- The premise test now asserts what is still true: nothing is shared.
- legal.lastUpdated moves to 2026-10-29 with /privacy.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
PLAN.md §16 phase 17, built on the org lead's answers of 2026-10-22,
recorded as D75-D79 (count of record 79).
- D75: core marketing pages describe what a game module adds in core's
words. "Game intelligence" became "What a game module adds"; each
game's own list moved to src/data/games/ and renders only on the new
/modules/uo/ and /modules/rust/. /modules/ is a card per game.
- The page-side capability check is per game (assertGameCoverage), the
item phase 13 moved here.
- D76: the homepage keeps its game screenshot, labelled. screens.mjs
entries may carry `game` (D67); Screenshot.astro and the app grid
print the game's name and mark the figure data-game.
- D77: /app/ describes the Rust screens (released the same day).
- D78: /runicnpc/, the standalone page, is in the header and footer;
the narrow header was re-measured so five links keep two rows.
- D79: checkTerms.mjs --built reads every built page's visible text
after the build; game routes and data-game elements allow their
game's words. Proven against ten plants. Still the twelfth check.
- Re-read fixes: /architecture/'s "two machines is also the maximum",
Discord "guild" on /integrations/, and collection.mjs against what
Steam sign-in and the Rust module keep (PLAY_DATA_SAFETY.md
regenerated; the Play answers are unchanged).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E14m6SuuY6i1vASFeGDBeY
Engagement Phase 12a. The site had pages for where a message goes (Notifications and
email) and what it says (Message templates), and nothing at all for what makes one get
sent -- the four Engagement screens the workstream built.
New page: Engagement rules. Rules, Audiences, the trigger catalog and the send log on
one page, sitting between the two it joins up. Templates already has its own page and
Suppressions is in Troubleshooting, so neither is repeated here.
Two things it exists to state plainly:
* Every rule ships disabled, including the ones a module brings. "Installed" is not
"on", and an upgrade whose Team mail went quiet is the same fact.
* The ceiling is a TREE, not a ladder. The tempting reading -- a staff-only event
could obviously also go to one person -- is wrong, and the example is the argument:
"one person" for cheat detection is the player it was detected on.
Troubleshooting gains the symptom that page answers ("nothing is sent for one
particular event"): the rule is off, the rule is dormant, its own cooldown held it, or
the audience is empty.
Privacy: two rows the engagement work makes necessary, and one sentence it made false.
* app-content claimed "Nothing is cached for offline use". Phase 8 shipped a DataStore
snapshot of the inbox, so it was untrue -- and that row feeds the generated Play Data
Safety answers, which is a store-review matter rather than a doc nit. The snapshot now
has its own row and its own Play mapping (Messages / Other in-app messages; not
collected by us, stored on the device), and app-content's claim is narrowed to
everything else.
* deploy-engagement, for the deployment scope: an address is now used for more than
getting into an account, there is a delivery log holding a one-way hash of it, and
there is a suppression list. Its retention line says what is true rather than what a
reader assumes -- none of these tables has a retention sweep.
PLAY_DATA_SAFETY.md regenerated from the inventory; legal.lastUpdated moved with the page
it dates.
Verified: the whole `verify` chain green -- checkSidebar (plannedSidebar moved with the
live tree), checkFacts 19/19, checkQuickstart 59, checkReference 22, checkLinks 2605,
checkA11y, checkCsp, playDataSafety --check, 42 + 7 tests. Read in a browser as well, in
the served build.
AI-assisted: written with Claude Code.
Co-Authored-By: Claude <noreply@anthropic.com>
PLAN.md §9. Builds /privacy and /terms, links them from the footer on every page,
and generates the Play Data Safety notes from the same inventory the policy renders.
Four decisions taken by the org lead before either page was written, recorded in
§9 under "How phase 6 built the legal pages":
D30 DNS-only records, so the reverse proxy on the host keeps the only access
log. Described qualitatively — the retention belongs to the proxy, and a
policy that quotes a number the deployment does not enforce is worse than
one that does not.
D31 Eighteen or older. Above the children's-consent threshold everywhere in the
EEA, so consent works with no parental-consent machinery this form could not
honestly operate. Four surfaces render it from src/data/legal.mjs, and every
one says plainly that nothing verifies it.
D32 No governing-law clause. Nothing of value is contracted for here.
D33 PLAY_DATA_SAFETY.md is generated from src/data/collection.mjs and checked in
CI, so the published policy and the answers given to Google cannot drift.
/privacy is three separately-scoped sections because "we" means three different
parties: this site (one form, no cookies, no third-party requests), the Android app
(we operate no server it talks to — the rows are what the DEVICE holds), and a
self-hosted deployment (the operator is the controller, not us). Every row names the
file it was read out of, because a policy is the document most likely to be written
from a template and least likely to be re-read against the software.
/terms governs only what we run: this site, the beta list, and the APK we publish.
The software is governed by its licence, and a community's deployment by that
community — a terms page claiming authority over every install of a GPL program is
the thing a generated template gets wrong.
Also here:
- the age clause changed CONSENT_TEXT, so CONSENT_VERSION gained a suffix; rows
written from now on carry the new sentence and older rows keep theirs
- PLANNED_ROUTES is now empty — these were its last two entries, and its reverse
check is what forced the deletion; the list stays for phases 7 and 8
- test/legal.test.mjs asserts the structural promises no build check can see,
including that every mapped Play row still answers "not collected, not shared"
- --check normalises line endings: the repo has no .gitattributes and Windows
checkouts are CRLF, so a byte comparison would fail for every Windows developer
while passing in CI
Verified: npm run verify green end to end (tokens, brand, data safety, astro check,
36 tests, build, 214 links, 19 facts), both pages walked in a browser, and neither
overflows at 390px. One defect the checks could not see and a look could: the
retention line was being pushed to the foot of the tallest card in its row, opening
a void in the middle of the short ones.
Co-Authored-By: Claude <noreply@anthropic.com>