feat(legal): phase 6 — the privacy policy and the terms
All checks were successful
PR checks / checks (pull_request) Successful in 55s
All checks were successful
PR checks / checks (pull_request) Successful in 55s
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>
This commit is contained in:
@@ -41,6 +41,8 @@
|
||||
* cannot start, which is exactly what §1 forbids.
|
||||
*/
|
||||
|
||||
import { legal } from './legal.mjs';
|
||||
|
||||
/**
|
||||
* Google Play's closed-testing rules, as verified in the Play Console documentation on
|
||||
* **2026-08-24**.
|
||||
@@ -111,8 +113,12 @@ function readInt(name, fallback) {
|
||||
|
||||
/**
|
||||
* A label for the batch a row was written in. Stored in no column — see the header.
|
||||
*
|
||||
* Suffixed rather than re-dated when phase 6 added the age clause on the same day the
|
||||
* original wording was written: two different sentences must not share a label, and the
|
||||
* date is what an operator groups a CSV by.
|
||||
*/
|
||||
export const CONSENT_VERSION = '2026-08-24';
|
||||
export const CONSENT_VERSION = '2026-08-24b';
|
||||
|
||||
/**
|
||||
* The exact sentence beside the checkbox, and the exact sentence written to `consent_text`.
|
||||
@@ -121,12 +127,22 @@ export const CONSENT_VERSION = '2026-08-24';
|
||||
* the address is kept until the beta ends or removal is asked for, it is pasted into Play
|
||||
* because that is the only way Play accepts testers, and nothing is mailed to it because
|
||||
* the site cannot send mail at all (D7).
|
||||
*
|
||||
* Phase 6 added the age (D31), and it goes FIRST because it is the only clause the person
|
||||
* ticking the box is asserting rather than acknowledging — everything after it is a
|
||||
* description of what we do. `legal.minimumAge` is interpolated rather than typed, because
|
||||
* /privacy and /terms state the same number and the Data Safety notes answer a question
|
||||
* about it; four surfaces, one source.
|
||||
*
|
||||
* Editing this string is a real act: `consent_text` stores the wording rather than a
|
||||
* version, so rows written from here on carry the new sentence and older rows keep the one
|
||||
* they were given. That is the property that makes the column worth having.
|
||||
*/
|
||||
export const CONSENT_TEXT =
|
||||
'I understand my email address will be stored so it can be added to the Google Play ' +
|
||||
'closed test, that it will be shared with Google Play for that purpose only, that ' +
|
||||
'Runic Gateway sends no email of any kind, and that I can ask for it to be deleted at ' +
|
||||
'any time.';
|
||||
`I am ${legal.minimumAge} or older. I understand my email address will be stored so it ` +
|
||||
'can be added to the Google Play closed test, that it will be shared with Google Play ' +
|
||||
'for that purpose only, that Runic Gateway sends no email of any kind, and that I can ' +
|
||||
'ask for it to be deleted at any time.';
|
||||
|
||||
/**
|
||||
* What a tester needs, rendered as the page's eligibility list.
|
||||
@@ -136,6 +152,14 @@ export const CONSENT_TEXT =
|
||||
* a deployment, and a client with no server is not a product with a missing feature.
|
||||
*/
|
||||
export const requirements = [
|
||||
{
|
||||
title: `Being ${legal.minimumAge} or older`,
|
||||
body:
|
||||
'The beta is for adults. Nothing verifies it and nothing pretends to — ticking the ' +
|
||||
'box on the form is the whole of it — but it is the condition the list is collected ' +
|
||||
'under, and it is why the form needs no parental consent machinery it could not ' +
|
||||
'honestly operate.',
|
||||
},
|
||||
{
|
||||
title: 'An Android device on 10 or newer',
|
||||
body:
|
||||
|
||||
Reference in New Issue
Block a user