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>
This commit is contained in:
Ben Stull
2026-05-24 04:31:11 -07:00
commit 779ba6db59
42 changed files with 10385 additions and 0 deletions
+153
View File
@@ -0,0 +1,153 @@
# Build notes
The slicing plan for the v1 build, the current state of the codebase,
and the next slice's brief.
## The slicing plan
Eight slices carry §§115 of [`SPEC.md`](../SPEC.md) end-to-end. The
build does not extend the spec; spec corrections during the build are
rare and surgical and live in the appropriate numbered section per
§19.3's working agreement.
1. **Repository scaffolding + propose-to-super-draft vertical.** The
chokepoint that every Git operation flows through (§1 bot wrapper),
the §4 cache machinery (webhook + reconciler), the §5 schema, Gitea
OAuth + user provisioning, the minimal §7 catalog, and one
end-to-end vertical: propose → idea PR → merge → super-draft view.
2. **The active-RFC view per §8 in full.** Editor, branch creation,
per-branch chat with AI participation (the §18 `<change>` protocol),
the change-card panel, accept/decline/edit, manual-edit flushes,
sub-threads, flags, DiffView.
3. **The PR flow per §10.** Open, review surface (diff + compressed
chat), the §10.3 seen-cursor, §10.4 review threads, merge,
post-merge, §10.9 conflict resolution.
4. **Super-draft body editing per §9.5 + §9.6.** Meta-repo edit
branches as the unit of work; everything from §8 inherits.
5. **Graduation per §13.** The dialog, the five-step transactional
sequence, rollback, the pre-graduation history affordance.
6. **Notifications per §15.** Last, because every other surface
produces signals the inbox receives — notification correctness
depends on the producers being in place first.
7. **The §14 chrome.** Landing page polish, the `/philosophy` route,
the persistent About link.
8. **Hardening.** End-to-end tests, dev/prod deployment shape,
the §12 30/90 branch-hygiene timers.
## State of the codebase
### Slice 1 — shipped
The repository scaffolding (`backend/`, `frontend/`, `scripts/`,
`docs/`), the §5 schema as numbered migrations under
`backend/migrations/`, the §1 bot wrapper (`app/bot.py`) that is the
single chokepoint every Git write flows through, Gitea OAuth and the
§6.1 user-provisioning row in `users`, the §4.1 webhook receiver and
the §4.1 periodic reconciler (both writing to the cache; user actions
never do), the §7 left pane (catalog list, search, sort, state-filter
chips, pending-ideas disclosure), and one end-to-end vertical: propose
→ idea PR opens → owner merges → super-draft appears in the catalog →
super-draft view renders the body.
The §17 endpoints exercised so far:
| Method | Path | § |
| ------ | -------------------------------------- | ------- |
| GET | `/api/auth/me` | §6 |
| GET | `/api/rfcs` | §7, §17 |
| GET | `/api/rfcs/{slug}` | §17 |
| GET | `/api/proposals` | §17 |
| GET | `/api/proposals/{pr_number}` | §17 |
| POST | `/api/rfcs/propose` | §9.1 |
| POST | `/api/proposals/{pr_number}/merge` | §9.3 |
| POST | `/api/proposals/{pr_number}/decline` | §9.3 |
| POST | `/api/proposals/{pr_number}/withdraw` | §9.3 |
| POST | `/api/webhooks/gitea` | §4.1 |
| GET | `/auth/login` / `/auth/callback` / `/auth/logout` | §18 |
### What's deferred from slice 1
These were on the §9.1 spec but pushed to Slice 2 because they belong
with surfaces that haven't been built yet:
- The propose modal's **AI-suggested tags** (§9.1) — the AI surface
lands with Slice 2's chat wiring. The tag chip input works manually
in the meantime.
- The propose modal's **AI-drafted PR description** (§9.2) — same
reason. The PR description is the pitch text for now.
- The decline ceremony's **two-step composer-then-preview dialog**
(§9.3) — the single-step required-comment input is in place; the
preview-and-confirm beat is the kind of UX polish that the §19.2
topic "pending-idea view's interaction design (remainder)" should
pick up alongside the merge-confirmation ceremony.
- The §9.3 **pre-merge chat thread on a pending-idea view** and the
migration of those threads to the super-draft on merge — depends
on Slice 2's chat infrastructure.
These are deferred in the build's working sense — surfaces exist in
the spec, but they share infrastructure that's wired in a later slice
and would otherwise have to be wired twice.
## Environment notes
- **Python 3.13.** Earlier 3.11+ should also work; 3.13 is what the
build session ran on.
- **Node 20+** for the frontend.
- **Local Gitea on port 3000.** Anything that exposes the Gitea v1
REST API works. If you tunnel Gitea elsewhere (e.g. a container,
a Codespace), re-run `scripts/seed_meta_repo.py` so the webhook
re-registers against the right `APP_URL`.
## Conventions
- **Bot writes only via `app/bot.py`.** If a module wants to call
`app/gitea.py`'s write methods directly, the spec is right and
the module is wrong — the wrapper is the chokepoint that makes
the §6.5 `On-behalf-of:` trailer and the §6 authorization both
consistent.
- **Cache writes only from `app/cache.py`.** User actions trigger
Git operations via the bot; the cache learns about them when the
webhook arrives (or the next reconciler sweep), and never before.
This invariant is what makes §4's "Git is truth" claim hold
operationally.
- **Spec corrections during the build are rare and surgical.** When
running code reveals the spec was wrong at a structural level (per
§19.3's working agreement), the correction lands in the appropriate
numbered section with a brief note explaining what running code
revealed. Spec extensions during the build are not in scope —
they accumulate in §19.2.
- **§16 stays deferred.** Body full-text search, per-RFC model
picker, funder role, persistent accepted-change markup, slug
renames — these are not shipped in any slice. They earn their own
topic sessions when use surfaces evidence they matter.
## Next slice
**Slice 2: the active-RFC view per §8.**
The active-RFC view inherits the three-column shape (§8.1), opens
on `main` in discuss mode by default (§8.2), supports the §8.3
discuss-vs-contribute mode flip on non-main branches, hosts §8.4's
per-branch chat with AI participation (§18's `<change>` protocol
parsing into `changes` rows per §8.6), the §8.8 change-card panel
with §8.9's accept / decline / edit-before-accept resolution, the
§8.10 tracked-change markup and DiffView toggle, the §8.11 manual-
edit flushes, the §8.12 range and paragraph sub-threads, the §8.13
flag affordance, and the §8.14 discuss-mode buffer.
The carryover assets that belong to Slice 2 are in the prototype
under `/Users/benstull/projects/wiggleverse/rfc-app-prototype/`:
- `frontend/src/components/Editor.jsx`, `ChatPanel.jsx`,
`ChangePanel.jsx`, `PromptBar.jsx`, `SelectionTooltip.jsx`,
`DiffView.jsx`, `ModelPicker.jsx` — Tiptap config, the
`<change>` parser, the selection-quote machinery, the
model-picker UX.
- `backend/providers.py`, `backend/chat.py` — the multi-provider
abstraction and the SSE-streaming chat layer.
These are §18 carryovers; reuse the working code rather than
rewriting. The prototype's *data model* and *permission shape* do
not carry; this codebase's `threads`, `thread_messages`, `changes`,
`changes.thread_id`, the §6 four-role model, and the per-branch
chat thread are the canonical shape for Slice 2 to wire against.