Files
rfc-app/CHANGELOG.md
T
Ben Stull 21fcbc92d4 Release 0.3.0: private-beta gate + anonymous read mode
Adds an email allowlist (toggleable per deployment) that restricts
OAuth sign-in to listed emails while keeping read paths public.
Anonymous visitors now see the full app shell in read-only mode
instead of the §14.1 landing wall. Empty allowlist = gate off, so
deployments that don't enable it behave exactly as 0.2.3.

Also fixes single-finger scroll on /philosophy and other .chrome-pane
views on iOS Safari (.app: 100vh → 100dvh).

Renames deploy/nginx/rfc.wiggleverse.org.conf →
ohm.wiggleverse.org.conf to match the deployed-domain rename
(rfc.wiggleverse.org deprovisioned 2026-05-27).

See CHANGELOG.md for full details + upgrade steps.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 20:58:58 -07:00

240 lines
11 KiB
Markdown

# Changelog
The binding policy for what each kind of version bump means and what
the changelog has to carry is in [`SPEC.md` §20](./SPEC.md). The
practical recipe downstream deployments follow when reading this file
is in [`docs/DEPLOYMENTS.md`](./docs/DEPLOYMENTS.md).
The canonical version is the `VERSION` file at the repo root;
`frontend/package.json#version` mirrors it. Deployments pin to a
specific framework version in their own `.rfc-app-version` file
(per §20.5).
While the framework is pre-1.0, minor-version bumps may introduce
breaking changes; the entry below each such bump documents the
upgrade steps a deployment must apply.
Upgrade-steps blocks use the [RFC 2119](https://www.rfc-editor.org/rfc/rfc2119)
/ [RFC 8174](https://www.rfc-editor.org/rfc/rfc8174) normative-
language convention per `SPEC.md` §20.4. **MUST** / **SHALL** steps
are required; **SHOULD** steps are recommended (the framework's
default and tested path); **MAY** steps are optional. Upgrades that
skip versions are the composition of each intervening adjacent
release's steps in order — no A-to-B path is pre-computed beyond
that.
## 0.3.0 — 2026-05-27
**Minor — operator action required if a deployment wants to enable the
private-beta gate; no action required to stay open.** This release adds
an email allowlist that, when populated, restricts OAuth sign-in to the
listed emails while keeping all read paths public. Anonymous visitors
now see the full app (catalog, RFC bodies, public branch conversations)
in read-only mode instead of the §14.1 landing-page wall.
### Added
- **`allowed_emails` table** (`backend/migrations/011_allowlist.sql`).
Empty list = gate off (any successful OAuth provisions a user, as
before). Any rows present = gate on (only listed emails, plus
users already grandfathered by `gitea_id`, may sign in).
- **Admin → Allowlist tab** at `/admin/allowlist`. Add/remove emails,
see who added each row and when. Status banner shows whether the
gate is currently active.
- **`/beta-pending` page** shown after a rejected OAuth callback. Free-
text invite-contact line is configurable via the new
`VITE_BETA_CONTACT` env var (optional; falls back to a generic line).
- **Beta chips** next to the Discuss/Contribute mode toggle, the Sign
in link, and the header Sign-in button so anonymous viewers see
immediately what is gated.
- **Anonymous read mode** in the React app: the §14.1 Landing page is
preserved at `/welcome` for deployments that want to link to it, but
the default route now renders the full app shell with write
affordances hidden behind a sign-in CTA.
### Changed
- **`/auth/callback`** now consults `auth.is_allowed_sign_in()` after
fetching the Gitea profile. Rejected sign-ins clear the OAuth state
and redirect to `/beta-pending`; the session is not populated.
- **`Catalog`** receives a `viewer` prop. Anonymous viewers see "Sign
in to propose (Beta)" instead of "+ Propose New RFC".
- **`PhilosophyWithSidebar`** now reads `authenticated` from the
current viewer instead of hardcoded `true`.
### Fixed
- **Single-finger scroll on the `/philosophy` page** (and any other
`.chrome-pane`-hosted view: `/admin/*`, `/settings/notifications`)
was broken on iOS Safari. The `.app` container used `height: 100vh`,
which on iOS measures the URL-bar-hidden ("largest") viewport — so
`.app` overflowed what's actually visible. Combined with the
`body { overflow: hidden }` in `index.css`, this meant single-finger
touches on the visible area were consumed by the (blocked) page-
level scroll attempt rather than reaching the nested `.chrome-pane`
scroll. Two-finger touches bypassed the page-level layer and
one-finger then worked once the URL bar had collapsed. Switched
`.app` to `height: 100dvh` (dynamic viewport — adjusts as the URL
bar shows/hides), with `100vh` retained as a fallback for browsers
predating iOS 15.4 / Chrome 108.
### Upgrade steps (from 0.2.3)
1. The deployment **MUST** rebuild the frontend with the new
`VITE_BETA_CONTACT` env var optionally set in `frontend/.env` (it
is OK to leave it blank — the `/beta-pending` page falls back to
a generic line).
2. The deployment **MUST** restart the backend so migration
`011_allowlist.sql` runs. No data loss; the new table starts
empty, which keeps the gate off and preserves existing behavior.
3. To **enable** the private-beta gate, the deployment operator
**SHOULD** sign in once (so their `users` row exists and they
grandfather in by `gitea_id`), then open `/admin/allowlist` and
add the first invited email. The first row added turns the gate
on for any user not yet in `users`.
4. To **stay open**, do nothing — leave `allowed_emails` empty and
the deployment behaves exactly as 0.2.3.
5. The deployment **MAY** customise its `/beta-pending` contact line
by setting `VITE_BETA_CONTACT` (an email, a URL, or a short
instruction) before the frontend build. Unset is fine.
## 0.2.3 — 2026-05-26
**Patch — no operator action required.** Rebuild and restart per the
routine deploy steps; no `.env` changes, no schema changes, no
behavior changes a deployment would notice in steady state beyond
the new endpoint below.
### Added
- **`GET /api/health`** — an unauthenticated probe returning JSON
`{version, status}` for ops tooling. `version` is the running
framework version (the contents of `VERSION` per §20.1, read at
process start and cached as a module-level constant in
`backend/app/health.py`); `status` is `"ok"` with HTTP 200 in
v1. The `"degraded"` / HTTP 503 path stays reserved in the
response shape so a later release can wire real degradation
conditions without breaking the contract. The version-match
check is the structural value — a deploy-control-panel polling
the endpoint after `systemctl restart` catches the failure mode
where a restart did not pick up the new code. See `SPEC.md` §17
and the §19.2 settlement.
### Upgrade steps (from 0.2.2)
1. The deployment **MAY** configure its monitoring (Pingdom,
Healthchecks.io, the flotilla deploy control panel, etc.) to
probe `/api/health` and compare the returned `version` against
the tag last deployed. The endpoint is unauthenticated by
design — no PII in the payload, no session required.
## 0.2.2 — 2026-05-26
**Patch — no operator action required.** Rebuild the frontend and
restart per the routine deploy steps; no `.env` changes, no schema
changes, no behavior changes a deployment would notice in steady
state beyond the fix below.
### Fixed
- **Mermaid blocks rendered as raw code on `/philosophy`.**
`frontend/src/components/Philosophy.jsx` parsed PHILOSOPHY.md with
the bare `marked` import and `dangerouslySetInnerHTML`, so
```` ```mermaid ```` fences fell through as `<pre>` blocks rather
than rendered diagrams. Swapped to `MarkdownPreview`, which already
carries the lazy mermaid loader + SVG render path used by the RFC
body view, so the philosophy surface now renders mermaid the same
way RFC bodies do. Pre-existing gap, surfaced when a deployment
authored a mermaid block in its `PHILOSOPHY_PATH` override.
## 0.2.1 — 2026-05-26
**Patch — no operator action required.** Rebuild the frontend and
restart per the routine deploy steps; no `.env` changes, no schema
changes, no behavior changes a deployment would notice in steady
state.
### Fixed
- **PR view blank page (React #310).** `frontend/src/components/PRView.jsx`
declared its `threadsByKind` `useMemo` *after* the early-return
guard for the loading state (`if (!pr) return …`). On first mount
`pr` was null so the early return fired and 14 hooks were called;
on the second render `pr` was populated and execution reached the
`useMemo`, calling 15 hooks — violating the Rules of Hooks and
unmounting the page subtree (blank screen). The `useMemo` is now
declared above the early returns with optional-chaining on `pr`,
keeping the hook count stable between the loading and loaded
renders. Pre-existing bug in the post-Contribute-rewrite PR view;
surfaced in production by 0.2.0's graduation merge race fix
enabling the workflow that opens this code path.
## 0.2.0 — 2026-05-26
**Breaking config change.** The frontend now requires
`VITE_APP_NAME` to be set at build time. `npm run build` fails with a
clear message if it is missing. There is no default; every deployment
names itself.
### Upgrade steps (from 0.1.0)
1. The deployment **MUST** create a `frontend/.env` file before
building. See `frontend/.env.example` for the contract.
2. The deployment **MUST** set `VITE_APP_NAME` in `frontend/.env` to
the user-visible name it wants to ship — the string used as the
browser tab title, the header brand, and the landing H1. The
build will fail loudly if this is unset or blank.
3. The deployment **MUST** rebuild the frontend (`npm install && npm
run build`) and redeploy the resulting bundle. The previous
bundle does not read `VITE_APP_NAME`.
4. The deployment **SHOULD** verify the name appears correctly in
the browser tab and the header after redeploy before bumping its
`.rfc-app-version` pin to `0.2.0`.
5. The deployment **MAY**, in the same upgrade, take the opportunity
to retire any local overrides it was using to brand the
pre-0.2.0 hardcoded strings — those overrides are now obsolete.
6. The deployment **MUST NOT** continue to expect graduation step 4
to fail on Gitea's "Please try again later" race; that path is
now handled by `wait_for_mergeable` + bounded retry. Any local
workaround retrying graduation at the deployment level is now
redundant and **SHOULD** be removed.
### Fixed
- **Graduation merge race.** §13.3 step 4 (`merge_pr`) used to call
Gitea's merge endpoint the instant step 3 returned, which races
Gitea's background mergeability computation and produces the
`405 "Please try again later"` response. Step 4 now waits for the
`mergeable` field to become non-null (bounded by a 30s timeout) and
retries the merge call up to three times on the transient response.
See `backend/app/gitea.py#wait_for_mergeable` and
`backend/app/bot.py#_merge_with_retry`.
### Changed
- `frontend/src/App.jsx` header brand reads `VITE_APP_NAME` instead of
a hardcoded string.
- `frontend/src/components/Landing.jsx` H1 reads `VITE_APP_NAME`
instead of a hardcoded string. The pitch / deck / attribution copy
in this file is still deployment-specific and tracked as a follow-up
for extraction into deployment config.
- `frontend/index.html` `<title>` is rewritten at build time via Vite's
HTML transform.
### Added
- `frontend/.env.example` documenting the new required variable.
- Top-level `VERSION` file and this `CHANGELOG.md` as the canonical
release log.
- `SPEC.md` §20 (versioning and downstream deployments) as the
binding policy for the framework/deployment relationship.
- `docs/DEPLOYMENTS.md` as the practical guide for building on
rfc-app and upgrading existing deployments to new versions.
- `CLAUDE.md` at the repo root capturing the separation-of-concerns
rule for working sessions.
## 0.1.0 — v1 build
Initial release. See `docs/DEV.md` for the slicing plan and build
history.