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
+90
View File
@@ -0,0 +1,90 @@
import { useEffect, useState } from 'react'
import { Routes, Route, Link, useNavigate } from 'react-router-dom'
import { getMe } from './api'
import Catalog from './components/Catalog.jsx'
import RFCView from './components/RFCView.jsx'
import ProposalView from './components/ProposalView.jsx'
import ProposeModal from './components/ProposeModal.jsx'
import Landing from './components/Landing.jsx'
import './App.css'
export default function App() {
const [me, setMe] = useState(null)
const [loading, setLoading] = useState(true)
const [proposeOpen, setProposeOpen] = useState(false)
const [catalogVersion, setCatalogVersion] = useState(0)
const navigate = useNavigate()
useEffect(() => {
getMe()
.then(setMe)
.catch(() => setMe({ authenticated: false }))
.finally(() => setLoading(false))
}, [])
if (loading) {
return <div className="boot">Loading</div>
}
if (!me?.authenticated) {
return <Landing />
}
return (
<div className="app">
<header className="app-header">
<div className="app-brand">
<Link to="/">Wiggleverse RFCs</Link>
</div>
<div className="header-right">
<span className="user-name">{me.user.display_name}</span>
<span className={`user-role-badge role-${me.user.role}`}>{me.user.role}</span>
<a className="btn-link" href="/auth/logout">Sign out</a>
</div>
</header>
<div className="app-body">
<Catalog
onProposeRFC={() => setProposeOpen(true)}
version={catalogVersion}
/>
<main className="main-pane">
<Routes>
<Route path="/" element={<Welcome viewer={me.user} />} />
<Route path="/rfc/:slug" element={<RFCView />} />
<Route path="/proposals/:prNumber" element={<ProposalView viewer={me.user} onChange={() => setCatalogVersion(v => v + 1)} />} />
</Routes>
</main>
</div>
{proposeOpen && (
<ProposeModal
onClose={() => setProposeOpen(false)}
onSubmitted={({ pr_number }) => {
setProposeOpen(false)
setCatalogVersion(v => v + 1)
navigate(`/proposals/${pr_number}`)
}}
/>
)}
</div>
)
}
function Welcome({ viewer }) {
return (
<div className="welcome">
<h1>Welcome, {viewer.display_name}.</h1>
<p>
The catalog on the left lists every super-draft and active RFC in the
framework. Open one to read the canonical body, or use{' '}
<strong>Propose New RFC</strong> at the bottom of the catalog to open an
idea PR against the meta repository.
</p>
<p>
Slice 1 of the build is in place: propose idea PR owner merges
super-draft appears in the catalog super-draft view renders. The
revision flow, per-branch chat, AI participation, and the PR surface
land in subsequent slices.
</p>
</div>
)
}