Files
rfc-app/README.md
T
Ben Stull b57b4ffc4b Add seed_test_rfc.py for §8 manual testing
Slice 5 will own the §13 graduation flow that creates per-RFC repos
from super-drafts; until then, the §8 active-RFC view needs a dev
shortcut to bring an `state: active` entry plus a per-RFC repo into
existence. The script:

- Creates `<org>/rfc-NNNN-<slug>` via bot.ensure_rfc_repo_seed,
  seeding RFC.md on main.
- Registers the §4.1 webhook on the per-RFC repo.
- Creates (or graduates) the meta-repo entry with state=active,
  id=RFC-NNNN, repo=<full>.

Requires the operator's gitea_login to have a `users` row (sign in
once via OAuth first); refuses to synthesize a fake user since the
§6 / §15.9 attribution surfaces read from it.

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

218 lines
7.8 KiB
Markdown

# RFC App
A single-process FastAPI + SQLite + React + Vite + Tiptap app that
materializes the Wiggleverse RFC framework specified in
[`SPEC.md`](./SPEC.md). The framework's mission lives in
[`PHILOSOPHY.md`](./PHILOSOPHY.md); the spec is the binding contract;
this README is how to bring the app up against a local Gitea instance
and exercise the slice the build session has shipped so far.
The implementation is in progress. See [`docs/DEV.md`](./docs/DEV.md)
for the slicing plan and the current state.
## What the app expects to talk to
- **A Gitea instance** at `GITEA_URL`. The instance hosts the meta
repository and (eventually) one repository per graduated RFC.
- **A bot service account** in that Gitea, with a personal access
token in `GITEA_BOT_TOKEN`. Per §1 the bot is the only writer in
the system — every commit, branch, and PR the app produces flows
through one wrapper that applies the §6.5 `On-behalf-of:` trailer
and records a row in the `actions` audit log.
- **An OAuth2 application** registered against that Gitea, with the
callback URL set to `{APP_URL}/auth/callback`. Real human users
authenticate via Gitea OAuth (the §18 carryover); the app reads
their Gitea profile, provisions a row in `users`, and layers §6's
app-owned permission model on top.
## Local bring-up
The shortest path from a clean checkout to a working app is:
### 1. Stand up a local Gitea
Anything that exposes the Gitea REST API works. The fastest path is
Docker:
```sh
docker run -d --name gitea \
-p 3000:3000 -p 222:22 \
-v gitea-data:/data \
gitea/gitea:1.21
```
Open `http://localhost:3000`, walk through the install wizard
(SQLite, default port), and create your owner-zero account.
### 2. Create the bot service account
In Gitea, sign in as your owner account and **Site Administration →
User Accounts → Create User Account**. Give it a name like `rfc-bot`
and an email. Then sign in as the bot, open **Settings → Applications
→ Generate New Token**, and grant it the `write:repository`,
`write:user`, and `write:admin` scopes (admin is needed because the
bot will create per-RFC repos on graduation; in v1 you can scope down
to `repo` and `org` if you want to defer admin until Slice 5).
Copy the token; you will paste it into `.env`.
### 3. Create the org that will host the meta repo
The seed script creates the meta repo *inside* an org. Create the org
(e.g. `wiggleverse`) in Gitea and add `rfc-bot` to it as an
**Owner**.
### 4. Register the OAuth2 application
In Gitea: **Site Administration → Integrations → OAuth2 Applications
→ Create**. Name it whatever you like, set the redirect URI to
`http://localhost:8000/auth/callback`. Copy the client id and client
secret — they go into `.env`.
### 5. Configure the app
```sh
cd backend
cp .env.example .env
$EDITOR .env # fill in every variable
```
Required values:
| Variable | What it is |
| -------------------------- | --------------------------------------------------------- |
| `GITEA_URL` | Base URL of the Gitea instance (no trailing slash). |
| `GITEA_BOT_USER` | The bot account's login. |
| `GITEA_BOT_TOKEN` | The bot account's access token. |
| `GITEA_ORG` | The org that owns the meta repo. |
| `META_REPO` | The meta repo's name (default `meta`). |
| `OAUTH_CLIENT_ID` | From the OAuth app you registered. |
| `OAUTH_CLIENT_SECRET` | Likewise. |
| `APP_URL` | The URL the app is reachable at locally. |
| `SECRET_KEY` | A long random string for cookie signing. |
| `OWNER_GITEA_LOGIN` | Your owner-zero Gitea login — gets the owner role on first sign-in. |
| `GITEA_WEBHOOK_SECRET` | A shared secret for the §4.1 webhook signature. |
The LLM-provider settings (`ENABLED_MODELS`, `ANTHROPIC_API_KEY`,
etc.) are not exercised by Slice 1 but are wired through `config.py`
so the next slice can pick them up.
### 6. Install dependencies
Backend:
```sh
cd backend
python3 -m venv .venv
.venv/bin/pip install -r requirements.txt
```
Frontend:
```sh
cd ../frontend
npm install
```
### 7. Seed the meta repo
The seed script creates `wiggleverse/meta` if it does not exist,
populates it with `PHILOSOPHY.md`, `README.md`, `CONTRIBUTING.md`,
the regenerate-index workflow placeholder, and an empty `rfcs/`
directory, and registers the Gitea webhook the app needs:
```sh
cd backend
.venv/bin/python ../scripts/seed_meta_repo.py
```
Re-running is safe — every step is upsert-shaped.
### 8. Run the app
In two terminals:
```sh
# Terminal 1 — backend
cd backend
.venv/bin/uvicorn app.main:app --reload --port 8000
```
```sh
# Terminal 2 — frontend
cd frontend
npm run dev
```
Open `http://localhost:5173`. Sign in with your owner-zero Gitea
account. The catalog should appear empty; the **+ Propose New RFC**
button at the bottom opens the propose modal.
## What slice 1 lets you do
End-to-end: propose a new RFC → an idea PR opens against the meta
repo → an owner merges from the pending-idea view → the super-draft
appears in the catalog → opening it renders the body.
This exercises the §4 cache (webhook + reconciler), the §6 permission
model (the owner-only merge button, the contributor-only propose
modal), the §1 bot wrapper (every Git write goes through it, every
commit and PR carries the `On-behalf-of:` trailer), and the §9
propose-merge-render path.
Out of scope for slice 1: the active-RFC view (§8), per-branch chat,
AI participation, the change-card panel, PRs against per-RFC repos,
graduation, notifications, the landing page's full polish. Those
slices are listed in [`docs/DEV.md`](./docs/DEV.md).
## Verifying it worked
After bring-up:
- `http://localhost:8000/docs` lists the API routes the build session
has wired so far.
- `sqlite3 backend/data/rfc-app.db .schema` shows the §5 schema.
- `gitea ls /api/v1/repos/wiggleverse/meta/contents/rfcs` after a
proposal merges should show one new `<slug>.md`.
## Seeding an active RFC for §8 testing
Slice 2 (the active-RFC view per §8) needs an entry whose `state` is
`active` and whose per-RFC repo exists. Slice 5's graduation flow
will land the proper path; until then, `scripts/seed_test_rfc.py` is
the dev shortcut. Sign in once via OAuth so a `users` row exists,
then:
```sh
cd backend && .venv/bin/python ../scripts/seed_test_rfc.py \
--slug open-human-model \
--title "Open Human Model"
```
The script creates `wiggleverse/rfc-NNNN-<slug>`, seeds `RFC.md` on
`main`, registers the webhook, and graduates the meta entry. The §8
surface at `/rfc/<slug>` then has something real to render.
## Troubleshooting
- **The catalog stays empty after a merge.** Check that the webhook
is reaching the app: the reconciler runs every five minutes and
will catch up, but a missing or misconfigured webhook is the most
common reason for sub-second freshness to fail. The seed script
registers the webhook for you; if you bring up Gitea on a different
host (e.g. a Codespace, a tunnel), re-run the seed against the new
`APP_URL`.
- **OAuth callback errors.** The redirect URI in Gitea has to match
`APP_URL` exactly, including the protocol and port.
- **The bot can't merge.** The bot needs Maintainer or Owner on the
meta repo (membership in `GITEA_ORG` as Owner gives it both).
## Where to read further
- [`SPEC.md`](./SPEC.md) — the binding contract. Every load-bearing
decision is there.
- [`PHILOSOPHY.md`](./PHILOSOPHY.md) — why this framework exists.
The spec's decisions answer to it.
- [`docs/DEV.md`](./docs/DEV.md) — the build's slicing plan, the
current state, and the next slice's brief.