Release 0.2.0: graduation merge race fix + VITE_APP_NAME + versioning infrastructure
- Fix §13.3 step 4 race against Gitea's async mergeability computation: gitea.wait_for_mergeable polls the mergeable field before calling merge, and bot._merge_with_retry rides out the transient 'Please try again later' response. - Frontend now requires VITE_APP_NAME at build time (frontend/vite.config.js fails the build if unset). Header brand, landing H1, and browser title read the deployment's configured name. - Establish framework versioning: VERSION (canonical) + frontend/package.json#version mirror; CHANGELOG.md as the release log with RFC 2119 / 8174 normative-language upgrade-steps; SPEC.md §20 as the binding policy; CLAUDE.md capturing the separation-of-concerns rule; docs/DEPLOYMENTS.md as the downstream operating manual; docs/QUICKSTART-VISION.md staking the future one-command-deploy capability. - Surgical fix to SPEC.md opening so the spec no longer asserts OHM is the corpus the framework produces. - SPEC.md §19.2 gains the 'Deployment-supplied subject framing' candidate that drives the 0.3.0 release.
This commit is contained in:
@@ -0,0 +1,253 @@
|
||||
# Deployments — building on rfc-app and keeping in sync
|
||||
|
||||
This document is the operating manual for downstream projects that
|
||||
host an instance of the RFC framework. It is the practical
|
||||
counterpart to [`SPEC.md` §20](../SPEC.md), which carries the
|
||||
binding policy. If anything here disagrees with §20, §20 wins and
|
||||
the discrepancy is a bug in this doc.
|
||||
|
||||
The audience is the operator of a downstream deployment — someone
|
||||
who wants to run their own corpus on top of rfc-app and keep that
|
||||
deployment current as the framework releases new versions.
|
||||
|
||||
## What is a "deployment"?
|
||||
|
||||
A deployment is a running instance of the framework, hosting a
|
||||
specific corpus, branded and configured for a specific community.
|
||||
Each deployment has:
|
||||
|
||||
- **A content repo** (separate from this one). Typically holds the
|
||||
initial RFC markdown files, a `README.md` describing what the
|
||||
deployment is, a `CONTRIBUTING.md`, and a `.rfc-app-version`
|
||||
pin. The Open Human Model (`ohm-rfc`) is one such repo.
|
||||
- **A Gitea instance** the framework talks to. Hosts the meta repo
|
||||
and the per-RFC repositories the graduation flow creates.
|
||||
- **A server** running the rfc-app backend + frontend bundle,
|
||||
pointed at the Gitea instance, with `frontend/.env` and
|
||||
`backend/.env` configured for this deployment.
|
||||
- **A pin** — the `.rfc-app-version` file in the deployment's
|
||||
content repo, declaring which framework version is currently in
|
||||
flight.
|
||||
|
||||
The framework repo (this one) is generic across deployments. The
|
||||
deployment supplies the values, the content, and the operational
|
||||
choices. The framework supplies the substrate.
|
||||
|
||||
## Starting a new deployment
|
||||
|
||||
The shortest path from scratch:
|
||||
|
||||
1. **Create the deployment's content repo.** Anywhere git lives —
|
||||
GitHub, Gitea, GitLab. It will hold the initial RFC markdown
|
||||
files and the deployment's public-facing README. Add a
|
||||
`.rfc-app-version` file with the framework version you intend to
|
||||
pin to (the current `VERSION` from this repo, or any prior
|
||||
version if you have a reason to pin behind).
|
||||
|
||||
2. **Stand up Gitea.** Anything that exposes the Gitea REST API
|
||||
works; `deploy/RUNBOOK.md` in this repo describes the bring-up.
|
||||
Create the bot service account and the OAuth2 application.
|
||||
|
||||
3. **Check out rfc-app at the version your deployment pins to.**
|
||||
`git checkout` the tag (or commit) matching the
|
||||
`.rfc-app-version` you chose. The pin is a contract — your
|
||||
deployment's behavior is defined by *that* version's code, not
|
||||
`main`.
|
||||
|
||||
4. **Configure the backend.** Copy `backend/.env.example` to
|
||||
`backend/.env` and fill in the Gitea URL, bot token, OAuth
|
||||
client credentials, `GITEA_ORG`, `META_REPO`, OWNER login, and
|
||||
any optional providers (SMTP, LLM keys). The `.env.example` is
|
||||
the contract for what the framework expects at this version;
|
||||
read every entry.
|
||||
|
||||
5. **Configure the frontend.** Copy `frontend/.env.example` to
|
||||
`frontend/.env` and fill it in. At minimum, every release
|
||||
requires `VITE_APP_NAME` (the user-visible name of your
|
||||
deployment — what it gets called in the browser tab, the header
|
||||
brand, and the landing H1). The framework ships no defaults for
|
||||
any deployment-identifying value; if a required variable is
|
||||
missing, the build fails loudly.
|
||||
|
||||
6. **Build and run.** `cd frontend && npm install && npm run
|
||||
build`, then start the backend per `deploy/RUNBOOK.md`. The
|
||||
first sign-in is the OWNER login from `backend/.env`.
|
||||
|
||||
7. **Seed the meta repo.** `python scripts/seed_meta_repo.py`
|
||||
creates the meta repo on Gitea with the initial README,
|
||||
CONTRIBUTING, and `.gitignore` per §1–§2. This step is one-time
|
||||
per deployment.
|
||||
|
||||
8. **Bring in your content.** Open proposal PRs against the meta
|
||||
repo to add your initial RFCs, or — if you're migrating an
|
||||
existing corpus — write directly into the meta repo's `rfcs/`
|
||||
directory at the version of `.rfc/metadata.yaml` your pinned
|
||||
framework version expects.
|
||||
|
||||
At this point the deployment is running. The content repo holds
|
||||
the canonical text and the `.rfc-app-version` pin; the server runs
|
||||
the framework at that pin against your Gitea; the framework knows
|
||||
nothing about your specific deployment beyond what your `.env`
|
||||
files told it.
|
||||
|
||||
## The two-repo working pattern
|
||||
|
||||
Once a deployment is live, day-to-day changes split across the
|
||||
framework repo and the deployment repo. The triage that decides
|
||||
which side a given change lands on:
|
||||
|
||||
A change is **deployment-side** when it is about a value, a piece
|
||||
of content, or an operational choice specific to this instance.
|
||||
Examples: changing the display name; updating an RFC's body;
|
||||
adding a new RFC; rotating Gitea credentials; tightening SMTP
|
||||
settings; changing who has the OWNER role.
|
||||
|
||||
A change is **framework-side** when it is about behavior, UI
|
||||
logic, schema, or any piece of code that would matter the same way
|
||||
to a different deployment. Examples: fixing a bug; adding a new
|
||||
feature; making a previously hardcoded value configurable; tuning
|
||||
a retry; correcting a race condition; updating a dependency that
|
||||
affects observable behavior.
|
||||
|
||||
The most common case is **mixed**: the framework needs to make
|
||||
something configurable, and the deployment needs to supply the
|
||||
value. The change pattern is then: framework adds the env var (or
|
||||
config knob) and the loud-failure path, ships a new minor or
|
||||
patch; the deployment upgrades to the new version and supplies
|
||||
the value in its `.env`.
|
||||
|
||||
When picking up a change, name it in deployment terms first ("in
|
||||
*my deployment* I want X"), then triage. The phrasing keeps the
|
||||
work honest — every framework change is in service of something
|
||||
someone wants from their deployment.
|
||||
|
||||
## Upgrading to a new framework version
|
||||
|
||||
The upgrade contract is in [`SPEC.md` §20.5](../SPEC.md), and the
|
||||
normative language used in upgrade-steps blocks is fixed in §20.4.
|
||||
The mechanics in practice:
|
||||
|
||||
1. **Decide the target version.** Read `CHANGELOG.md` from the
|
||||
current pin up through `main`. Pick the target — usually the
|
||||
latest, but pinning behind is legitimate if a release introduces
|
||||
something the deployment isn't ready for.
|
||||
|
||||
2. **Read every intervening release's upgrade steps.** Each release
|
||||
header in `CHANGELOG.md` includes an "upgrade steps" block when
|
||||
there is operator action required. Read them in order; the
|
||||
composition is your upgrade plan. **Cross-version upgrades are
|
||||
not pre-computed** (see §20.4) — going from 0.1.0 to 0.5.0 means
|
||||
walking the upgrade-steps for 0.2.0, then 0.3.0, then 0.4.0,
|
||||
then 0.5.0 in that order, applying them as a single plan.
|
||||
|
||||
3. **Triage the plan by normative language.** Each step is worded
|
||||
per RFC 2119 / 8174:
|
||||
- **MUST / SHALL / REQUIRED** steps are non-negotiable. Skip one
|
||||
and the deployment is out of contract on the new version.
|
||||
Either it will fail to build or start (the framework's
|
||||
§20.6 commitment to loud failures), or it will misbehave
|
||||
subtly later. Treat these as the binding part of your plan.
|
||||
- **SHOULD / RECOMMENDED** steps are the framework's tested
|
||||
default. You can deviate when you have a reason, but
|
||||
subsequent releases assume you took the recommended path; a
|
||||
deviation now may complicate a future upgrade.
|
||||
- **MAY / OPTIONAL** steps are affordances. Take them or skip
|
||||
them based on your needs; the framework's behavior doesn't
|
||||
depend on the choice.
|
||||
- **MUST NOT / SHOULD NOT** steps remove or discourage prior
|
||||
behavior. Check whether your deployment was relying on the
|
||||
called-out behavior; if so, that's the thing to change.
|
||||
|
||||
4. **Apply the upgrade steps in order against the deployment's
|
||||
environment.** New env vars get added to `frontend/.env` or
|
||||
`backend/.env`. Renamed vars get renamed. Removed vars get
|
||||
removed. Migration scripts (if any are referenced) get run.
|
||||
|
||||
5. **Check out the framework at the target version.** `git
|
||||
fetch && git checkout <tag>`.
|
||||
|
||||
6. **Rebuild.** `npm install && npm run build` for the frontend;
|
||||
restart the backend.
|
||||
|
||||
7. **Smoke-test.** Sign in, check the brand reflects your
|
||||
`VITE_APP_NAME`, exercise whatever surface the release touched.
|
||||
Walk through the SHOULD steps that involve verification.
|
||||
|
||||
8. **Update the pin.** Change `.rfc-app-version` in the deployment
|
||||
repo to the target version. Commit. This is the deployment
|
||||
promising its users that *this* is the framework version
|
||||
currently in flight.
|
||||
|
||||
If a step fails, stop. Do not bump the pin. The framework's
|
||||
obligation per §20.6 is that a faithful upgrade does not regress
|
||||
the deployment — failure is a bug to report against the framework
|
||||
release, not something to paper over.
|
||||
|
||||
### Composing across multiple versions
|
||||
|
||||
Worked example: a deployment pinned to `0.1.0` wants to move to
|
||||
some future `0.4.0`. The plan is the concatenation of three
|
||||
adjacent upgrade-steps blocks from `CHANGELOG.md`:
|
||||
|
||||
```
|
||||
0.1.0 → 0.2.0: MUST set VITE_APP_NAME, MUST rebuild, SHOULD verify
|
||||
0.2.0 → 0.3.0: (whatever the 0.3.0 entry's steps are)
|
||||
0.3.0 → 0.4.0: (whatever the 0.4.0 entry's steps are)
|
||||
```
|
||||
|
||||
The operator concatenates these into one plan, applies them in
|
||||
order, rebuilds once at the end against `0.4.0`'s checkout, and
|
||||
bumps the pin to `0.4.0`. No path-specific instructions are
|
||||
needed because each adjacent step is unambiguous in isolation.
|
||||
|
||||
This is why §20.4 binds the normative-language convention: the
|
||||
composition only works mechanically if each adjacent step's
|
||||
strength is clear from its wording.
|
||||
|
||||
## Versioning, briefly
|
||||
|
||||
[`SPEC.md` §20.2](../SPEC.md) is the binding rule. Summary:
|
||||
|
||||
- **Patch** (`0.x.0 → 0.x.1`): redeploy and you're done. No env
|
||||
changes, no behavior surprises.
|
||||
- **Minor** (`0.x.0 → 0.(x+1).0`): read the changelog. New optional
|
||||
config, new features, and — while pre-1.0 — breaking changes
|
||||
that the changelog spells out.
|
||||
- **Major** (`0.x.0 → 1.0.0`, then `1.0.0 → 2.0.0`): always read
|
||||
the changelog. Breaking by definition; upgrade steps required.
|
||||
|
||||
The framework is pre-1.0 today. Expect minor bumps to occasionally
|
||||
require changes to your `.env` files until the first stable cut.
|
||||
|
||||
## The deployment's repo, at a glance
|
||||
|
||||
A deployment's content repo typically holds:
|
||||
|
||||
- The RFC markdown files (one per concept the deployment defines).
|
||||
- A `README.md` describing what the deployment is, public-facing.
|
||||
- A `CONTRIBUTING.md` describing how to participate.
|
||||
- `.rfc-app-version` — the framework pin (single line, SemVer).
|
||||
- A `CLAUDE.md` describing how to work on the deployment, the
|
||||
two-repo iteration pattern, and the deployment-specific values
|
||||
(typically without secrets — those live on the server only).
|
||||
|
||||
The deployment repo does **not** hold:
|
||||
|
||||
- Any of the framework's code. The framework lives in its own repo;
|
||||
you build against it at the pinned version.
|
||||
- Secrets. `.env` files belong on the server, never in git.
|
||||
- Forked copies of framework files. If you find yourself wanting to
|
||||
edit a framework file, the right move is to file a change against
|
||||
the framework, get a release, and pin to it.
|
||||
|
||||
## When something goes wrong
|
||||
|
||||
If a framework behavior is wrong for your deployment, file it as a
|
||||
framework change request: a clear statement of what you wanted, what
|
||||
happened instead, and (if you can) what the right separation of
|
||||
concerns is between framework and deployment.
|
||||
|
||||
If a framework upgrade breaks your deployment after you followed the
|
||||
changelog faithfully, that's a §20.6 violation — file it against the
|
||||
framework release. The pin lets you roll back to the prior version
|
||||
while the framework fixes the regression.
|
||||
Reference in New Issue
Block a user