Files
rfc-app/CHANGELOG.md
T
Ben Stull c92730a737 Release 0.5.0: PR-less per-RFC discussion (contribution still requires PR)
Roadmap item #3. An RFC's main view now carries a discussion surface
distinct from PR comments and from branch chat. The substrate is the
existing threads/thread_messages tables — rows with branch_name IS NULL
scope to the RFC's main view; the schema already permitted that shape,
v0.5.0 is the first build to write it. Five new endpoints under
/api/rfcs/<slug>/discussion/..., a new RFCDiscussionPanel right-column
component used when branchParam === main, SPEC §10.10 settling
discussion-vs-contribution, and §17 listing the new routes. Notification
routing reuses the existing chat_message_in_participated_thread /
chat_reply_to_my_message event kinds with branch_name=null on the
fan-out row; a distinct event_kind is a §19.2 candidate. Anonymous
viewers can read; writes require contributor — v0.6.0's item #4 will
harden adjacent gates. No schema migration; minor bump, no operator
action required beyond rebuild and restart.

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

18 KiB

Changelog

The binding policy for what each kind of version bump means and what the changelog has to carry is in SPEC.md §20. The practical recipe downstream deployments follow when reading this file is in 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 / RFC 8174 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.5.0 — 2026-05-27

Minor — no operator action required. This release wires the PR-less per-RFC discussion surface (roadmap item #3, SPEC §10.10). An RFC's main view now carries a discussion panel distinct from PR comments and branch chat; contribution — proposing edits the document will land — still requires opening a PR via the §10.1 affordance. The substrate is the existing threads / thread_messages tables; rows with threads.branch_name IS NULL scope to the RFC's main view. No schema migration is required.

Added

  • PR-less discussion endpoints (backend/app/api_discussion.py) mounted at /api/rfcs/<slug>/discussion/...:
    • GET /api/rfcs/<slug>/discussion/threads — list threads where threads.branch_name IS NULL. Anonymous-readable per the v0.3.0 contract; the default whole-doc chat thread is materialized lazily on first read.
    • POST /api/rfcs/<slug>/discussion/threads — open a new discussion thread (thread_kind='chat', anchor_kind='whole-doc', branch_name=NULL). Body: optional label, optional first message. Requires contributor role.
    • GET /api/rfcs/<slug>/discussion/threads/<thread_id>/messages — read messages on a discussion thread. Anonymous-readable.
    • POST /api/rfcs/<slug>/discussion/threads/<thread_id>/messages — post a message. Body: text, optional quote. Requires contributor role.
    • POST /api/rfcs/<slug>/discussion/threads/<thread_id>/resolve — resolve a discussion thread. Permission per §10.10: thread creator, RFC owner / arbiter, or app admin / owner.
  • RFCDiscussionPanel.jsx — the right-column surface on the RFC view when the viewer is on main. Composer requires sign-in; Cmd/Ctrl+Enter sends. Multiple threads surface as pill-shaped tabs above the message feed. A "New thread" affordance opens a fresh thread on the same RFC.
  • SPEC §10.10 PR-less discussion vs. contribution — settles the distinction between discussion (RFC-scoped, no PR, both anonymous-readable and contributor-writeable) and contribution (still requires a PR via §10.1). Also extends §5's threads table commentary so the null-branch interpretation is documented as actively used rather than reserved scaffold.
  • SPEC §17 — lists the five new discussion/... endpoints in the illustrative table.

Changed

  • backend/app/chat.py_fan_out_chat now passes branch_name straight through to the notify chokepoint instead of coercing None to "main". The notifications row carries the null through, which preserves the §15.7 reconciler's keying on (rfc_slug, branch_name) for the eventual discussion-side chat-seen advance (deferred to a §19.2 candidate). Existing branch-scoped chat continues to pass non-null branch names; the change is invisible to that path.
  • backend/app/notify.pyfan_out_chat_message's branch_name parameter is now typed str | None to match the v0.5.0 PR-less shape. Routing rules are unchanged; the inbox prose renders identically whether the chat lives on a branch or on the RFC's discussion surface, which is the right honest signal.
  • RFCView.jsx — the right-column panel is now conditional: when branchParam === 'main', render RFCDiscussionPanel (the new PR-less surface); otherwise render the existing ChatPanel (branch chat unchanged).

§19.2 candidates surfaced

  • PR-less discussion: range / paragraph anchors (the data model permits them; the UI is the deferred part).
  • PR-less discussion: distinct notification event_kinds (open_rfc_discussion_thread, etc.) if usage shows contributors want to filter discussion-vs-branch in the §15.2 inbox.
  • PR-less discussion: chat-seen cursor closing the §15.7 reconciliation loop for the new surface.
  • PR-less discussion: AI participant invocation (discussion-only, no <change> block side-effects).
  • PR-less discussion: anonymous-read polish to match the v0.6.0 write-gate hardening (item #4).

Upgrade steps (from 0.4.0)

  1. The deployment MUST rebuild the frontend (npm install && npm run build) so RFCDiscussionPanel.jsx ships in the bundle. No new env vars; existing frontend/.env is sufficient.
  2. The deployment MUST restart the backend so the new api_discussion router mounts. No schema migration runs — the threads table already supports branch_name IS NULL per §5, and the v0.5.0 build is the first to write rows in that shape.
  3. The deployment MAY announce the new surface to its contributors: the RFC view's main page now carries a discussion panel below the document. Existing branch chat and PR review surfaces are unchanged.
  4. The deployment SHOULD NOT expect a hardening of the anonymous-write gate in v0.5.0 — that lands in v0.6.0 (item #4). v0.5.0's write paths already refuse anonymous posts, so no pre-emptive operator action is needed.

0.4.0 — 2026-05-27

Minor — no operator action required beyond rebuild + restart. The proposer of a new RFC is now the implicit first owner of its super- draft entry, set automatically at propose time from the session user. The §13.1 claim flow remains available for additional owners. No schema changes, no env-var changes, no API-shape changes; only newly proposed RFCs receive the auto-owner — existing super-drafts whose owners: is empty are unaffected and can still be claimed via §13.1 as before. This is a §19.3 rule-2 spec correction: SPEC.md §9.1, §9.2, and §13.1 are updated to reflect the new shape.

Changed

  • POST /api/rfcs/propose (backend/app/api.py) now sets Entry.owners = [user.gitea_login] when constructing the new super-draft entry, instead of owners=[]. The session's gitea_login is the canonical source; the endpoint never accepted an owner field from the request payload and still doesn't.
  • SPEC.md §9.1 narrowed: the "no proposed-owner or working- group fields" sentence becomes a proposer-owner-auto / working- group-deferred split, with a §19.3 rule-2 note.
  • SPEC.md §9.2 frontmatter shape: owners: []owners: [<proposer.gitea_login>], with a §19.3 rule-2 note.
  • SPEC.md §13.1 reframed: claim flow is now a graduation-time broadening for additional owners, not a precondition for the proposer's own RFC. The §13.1 / §13.2 / §13.3 graduation pipeline itself is unchanged — the "at least one owner" precondition for graduation still holds and is now satisfied by default.

Upgrade steps (from 0.3.0)

  1. The deployment MUST rebuild and restart per the routine deploy steps. No .env changes, no schema/migration changes, no API-shape changes.
  2. Operators SHOULD note that newly proposed RFCs after the upgrade carry the proposer in owners: automatically. Existing super-drafts with empty owners: are not migrated; they remain claimable via the §13.1 flow exactly as before. No deployment- side data action is required.
  3. Deployments MAY communicate the UX shift to active proposers (the "Claim ownership" affordance no longer applies to your own newly proposed RFC), but the affordance simply hides on RFCs the viewer already owns, so no operator-side action is required.

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.