Files
rfc-app/README.md
T
Ben Stull 779ba6db59 Slice 1: scaffolding + propose-to-super-draft vertical
Brings the §1 bot wrapper, the §4 cache (webhook + reconciler), the
§5 schema (six numbered migrations), Gitea OAuth + §6 user
provisioning, the §7 catalog left pane, and the propose-to-merge
vertical: propose modal opens an idea PR against the meta repo, an
owner merges from the pending-idea view, the cache picks it up via
webhook or reconciler sweep, and the catalog renders the new
super-draft.

Per §1 the bot is the only Git writer; every commit, branch
creation, and PR merge carries the §6.5 On-behalf-of: trailer and
an `actions` audit row. Per §4 the cache is never written from a
user action — it's webhook+reconciler only.

Covered by `backend/tests/test_propose_vertical.py` against an
in-process Gitea simulator.

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

200 lines
7.2 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`.
## 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.