feat(beta): phase 5 — the app page and the closed-beta signup
All checks were successful
PR checks / checks (pull_request) Successful in 1m5s
All checks were successful
PR checks / checks (pull_request) Successful in 1m5s
Builds `/app/` and `/beta/`, the SQLite signup store, the rate limiting and the export CLI of PLAN.md §8, and adds this repository's first test suite. Four decisions of record, D26–D29 (§8, "How phase 5 built the app and the beta"): - D26 — the screenshot slot ships empty, reserved for phase 9. §10 promised `/app/` "the 14 existing screenshots"; they are a July trusted-device smoke test against an unseeded dev instance, captured before the theming work, and five of the fourteen are two-factor prompts. Shipping them would break D4. Phase 9 already builds the rig, so it gains an emulator pass. - D27 — the public demo is the tester target. `ConnectScreen.kt` gates the whole app on a validated deployment address, so a tester needs somewhere to point it. The beta therefore waits on the demo VM, and the page says so. - D28 — `/beta` handles its own POST; there is no `/api/beta-signup`. An endpoint cannot report a validation error without JavaScript. §6's diagram is amended. - D29 — the APK and the beta get equal billing, and the APK link is off: `androidApk.serviceable` is false because the published v0.5.0 build does not work. The panel stays and states that plainly rather than being removed. Three mechanisms the plan did not anticipate: - `liveBrand()` — a server-rendered page never passes through the boot rewrite, so `/beta` reads the mounted brand.json itself. Pasting the Play opt-in URL in takes effect on the next request rather than the next restart. - `checkLinks.mjs` derives on-demand routes from `prerender = false` in the source. A PLANNED_ROUTES entry would have been wrong: its reverse check fires when a route has been built, and an on-demand route never produces a file, so the entry could never rot out. - `npm test` — the five existing checks all read built output, and none of this logic appears there. A honeypot can stop working and leave the build identical. Also: `checkFacts.mjs` gains the APK assets and `minSdk`, and learns that RFC 2606 reserved domains are not contact addresses; the D13 rule is otherwise unchanged. Verified end to end against the built server: every outcome renders with no JavaScript, cross-origin POSTs are refused, a mounted opt-in URL appears without a restart, and the export CLI round-trips. Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -127,7 +127,11 @@ export const notBuilt = [
|
||||
},
|
||||
{
|
||||
id: 'public-demo',
|
||||
scope: ['features'],
|
||||
// Phase 5 added `app` and `beta`, and that is not tidying. D27 makes the demo the
|
||||
// deployment a beta tester connects to, so on those two pages this stopped being a
|
||||
// thing the site lacks and became the thing the beta is waiting for. An absence that
|
||||
// blocks a call to action has to be on the page carrying that call to action.
|
||||
scope: ['features', 'app', 'beta'],
|
||||
title: 'A public demo you can click through',
|
||||
body:
|
||||
'Planned and out of scope today: a virtual machine running the whole stack including ' +
|
||||
@@ -138,6 +142,46 @@ export const notBuilt = [
|
||||
'The machine being stood up. The site is already built to gain it by way of one line ' +
|
||||
'in a configuration file, rather than a rebuild.',
|
||||
},
|
||||
|
||||
/* ---------------------------------------------------------------------------------------
|
||||
THE ANDROID CLIENT (phase 5)
|
||||
|
||||
These are about the app rather than the platform, and they live here rather than in a
|
||||
second list on `/app/` for the reason this file exists at all: two lists of absences
|
||||
drift, and the one that drifts is always the one nobody is looking at. The `scope` tag
|
||||
is what keeps them off the pages they would be noise on.
|
||||
--------------------------------------------------------------------------------------- */
|
||||
{
|
||||
id: 'ios-app',
|
||||
scope: ['app'],
|
||||
title: 'An iOS app',
|
||||
body:
|
||||
'Android only. There is no iOS build, no cross-platform layer waiting to grow one, ' +
|
||||
'and no work in progress — the app is native Kotlin and Compose, so a second ' +
|
||||
'platform would be a second app rather than another build target.',
|
||||
resolvedBy: 'Nothing planned. A deployment is a website first, and that works on any phone.',
|
||||
},
|
||||
{
|
||||
id: 'play-listing',
|
||||
scope: ['app', 'beta'],
|
||||
title: 'A listing on Google Play',
|
||||
body:
|
||||
'The app is not published. A developer account exists; the closed test is the next ' +
|
||||
'step, and production access cannot even be requested until a run of testers has ' +
|
||||
'been opted in continuously — which is what the beta is for, and why the beta is not ' +
|
||||
'a formality.',
|
||||
resolvedBy: 'The closed test running its course, and then a production review.',
|
||||
link: { href: '/beta/', label: 'The closed beta' },
|
||||
},
|
||||
{
|
||||
id: 'app-offline',
|
||||
scope: ['app'],
|
||||
title: 'Reading anything offline',
|
||||
body:
|
||||
'Every screen is a live read against the deployment. Nothing is cached for offline ' +
|
||||
'use, so the app with no signal is an app with no content.',
|
||||
resolvedBy: 'Somebody asking for it. Nobody has.',
|
||||
},
|
||||
];
|
||||
|
||||
/** The entries a given page renders, in file order. */
|
||||
|
||||
Reference in New Issue
Block a user