b57b4ffc4b
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>
218 lines
7.8 KiB
Markdown
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.
|