Files
wtclaude 1313e748ae
All checks were successful
PR checks / checks (pull_request) Successful in 1m5s
feat(beta): phase 5 — the app page and the closed-beta signup
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>
2026-08-24 03:50:51 -05:00

48 lines
2.3 KiB
JSON

{
"$comment": [
"PLAN.md §7: the stock brand, baked into the image at /app/brand-default and always",
"complete. The bind mount at /app/brand may be empty, partial or full; every field and",
"every asset resolves against the mount first and these defaults second, per key, so an",
"empty mount produces exactly this.",
"",
"This file is also the ONLY place in the repository allowed to contain an email address",
"(D13). The org lead has no mailbox at the domain and chose to publish the existing",
"address rather than delay the beta; what makes that reversible is that moving to",
"privacy@ / security@ later is an edit to the mounted copy of this file and a container",
"restart — no rebuild, no PR. scripts/checkFacts.mjs fails the build if an address",
"appears anywhere else in the source, because the promise only survives while that",
"stays true."
],
"siteName": "Runic Gateway",
"tagline": "Put your game server on the web without putting it on the internet.",
"contactEmail": "whitlocktech@gmail.com",
"discordInvite": "https://discord.gg/t2Jav8yT4g",
"giteaOrg": "https://gitea.whitlocktech.com/RunicGateway",
"$comment_demo": [
"§15 / D12. A public demo instance is planned and out of scope today. The homepage's",
"'See it running' slot and /features/'s per-capability affordance render only when this",
"is a non-empty URL, so the site gains a working demo by way of one line in a mounted",
"file — no rebuild, consistent with §7."
],
"demoUrl": "",
"$comment_beta": [
"PLAN.md §8 / D27. The Google Play closed-test opt-in URL. Empty until the track",
"exists, and /beta renders a waiting state rather than a broken link while it is.",
"",
"It is safe to publish once it is filled in, and that is the whole reason the beta can",
"work with a site that sends no email (D7): the opt-in link only works for addresses",
"already on the tester list, so anyone else who opens it is refused. Google does not",
"notify testers on the email-list path either — Discord carries the announcement.",
"",
"/beta is one of the two routes that render per request, so unlike every other field",
"here this one is read from the mounted copy on the NEXT REQUEST rather than at the",
"next restart. Paste the URL in and reload the page."
],
"betaOptInUrl": ""
}