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:
53
src/data/legal.mjs
Normal file
53
src/data/legal.mjs
Normal file
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* legal.mjs — the handful of values the legal pages and the signup form must agree on.
|
||||
* PLAN.md §9, built in phase 6.
|
||||
*
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* WHY THESE THREE THINGS ARE HERE AND NOT IN THE PAGES
|
||||
* ---------------------------------------------------------------------------------------
|
||||
* Each is stated in more than one place and would be wrong in exactly one of them:
|
||||
*
|
||||
* `minimumAge` /terms says it, /privacy repeats it, the consent sentence beside the
|
||||
* signup checkbox commits somebody to it, and the Play Data Safety notes
|
||||
* answer a question about it. Four surfaces, one number (D31).
|
||||
* `lastUpdated` A legal page with no date is a legal page nobody can reason about, and
|
||||
* two pages with different dates invites the reader to work out which one
|
||||
* is stale. They changed together; they say so together.
|
||||
* `licence` Quoted on /terms and in the footer.
|
||||
*
|
||||
* The contact address is deliberately NOT here. It is a `brand.json` field read through
|
||||
* `src/lib/brand.mjs` (D13), so that changing the published address stays a file copy on a
|
||||
* mount rather than an edit to the source — and `checkFacts.mjs` fails the build if one is
|
||||
* typed into any file under `src/`.
|
||||
*/
|
||||
|
||||
export const legal = {
|
||||
/**
|
||||
* The date the legal pages last changed, in the format they render it.
|
||||
*
|
||||
* Bump it in the same commit that changes what either page says. It is not generated
|
||||
* from git: a build timestamp would move on every rebuild and tell a reader nothing,
|
||||
* and a commit date would move when a stylesheet changed.
|
||||
*/
|
||||
lastUpdated: '2026-08-24',
|
||||
|
||||
/**
|
||||
* The minimum age to sign up for the beta. The org lead's decision, 2026-08-24 (D31).
|
||||
*
|
||||
* Eighteen, chosen over thirteen and sixteen: it is above the children's-consent
|
||||
* threshold in every EEA state, so consent works as a basis with no parental-consent
|
||||
* machinery — which this form has no way to obtain and no way to verify. It is the
|
||||
* simplest thing to state truthfully for a beta that needs twelve people.
|
||||
*
|
||||
* A number rather than a sentence because four surfaces render it. What the site can
|
||||
* actually enforce is a statement, not a check, and every one of those surfaces is
|
||||
* written to say so plainly rather than implying verification that does not happen.
|
||||
*/
|
||||
minimumAge: 18,
|
||||
|
||||
/** The licence, quoted on /terms and in the footer. */
|
||||
licence: {
|
||||
id: 'GPL-3.0-or-later',
|
||||
url: 'https://www.gnu.org/licenses/gpl-3.0.html',
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user