Files
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

83 lines
4.0 KiB
SQL

-- §4: the metadata cache. Reconstructible from Gitea at any time by the
-- §4.1 reconciler; never written from user actions, only from webhook
-- handlers and reconciler sweeps. Body content is cached for main-branch
-- reads (§4 #3); branch bodies are not.
--
-- These tables are not in §5's "canonical app tables" list because they
-- are cache, not truth — but they are required for the left-pane render
-- path and for serving super-draft and main-branch bodies without a
-- Gitea round-trip on every navigation.
-- One row per meta-repo rfcs/<slug>.md entry. Mirrors §2.1 frontmatter
-- plus the cached body for super-draft preview (graduated entries have
-- frontmatter-only bodies per §13.3 step 3, but the field is reused).
CREATE TABLE cached_rfcs (
slug TEXT PRIMARY KEY,
title TEXT NOT NULL,
state TEXT NOT NULL CHECK (state IN ('super-draft', 'active', 'withdrawn')),
rfc_id TEXT, -- 'RFC-NNNN' or NULL until graduated
repo TEXT, -- 'org/repo' or NULL until graduated
proposed_by TEXT, -- gitea login or email
proposed_at TEXT,
graduated_at TEXT,
graduated_by TEXT,
owners_json TEXT NOT NULL DEFAULT '[]',
arbiters_json TEXT NOT NULL DEFAULT '[]',
tags_json TEXT NOT NULL DEFAULT '[]',
body TEXT, -- super-draft body or main RFC.md body
body_sha TEXT, -- the commit sha the body was fetched at
last_main_commit_at TEXT, -- §7.1's "Recently active" sort
last_entry_commit_at TEXT, -- last meta-repo commit touching this entry
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
);
CREATE INDEX idx_cached_rfcs_state ON cached_rfcs (state);
CREATE INDEX idx_cached_rfcs_last_active ON cached_rfcs (
COALESCE(last_main_commit_at, last_entry_commit_at) DESC
);
-- One row per branch the bot knows about on either a per-RFC repo
-- (rfc_slug, state='active'') or on the meta repo as a super-draft edit
-- branch (rfc_slug, state='super-draft', branch_name like 'edit/<slug>/...').
-- §11.5: closed branches stay; deleted branches keep their metadata row
-- per §12 ("branch removed from Gitea, row remains").
CREATE TABLE cached_branches (
id INTEGER PRIMARY KEY AUTOINCREMENT,
rfc_slug TEXT NOT NULL,
branch_name TEXT NOT NULL,
head_sha TEXT,
state TEXT NOT NULL DEFAULT 'open' CHECK (state IN ('open', 'closed', 'deleted')),
pinned INTEGER NOT NULL DEFAULT 0,
created_at TEXT NOT NULL DEFAULT (datetime('now')),
last_commit_at TEXT,
closed_at TEXT,
UNIQUE (rfc_slug, branch_name)
);
CREATE INDEX idx_cached_branches_rfc ON cached_branches (rfc_slug, state);
-- One row per PR the bot knows about. Includes meta-repo idea PRs (rfc_slug
-- carries the proposed slug, see §5 super-draft scoping note) and meta-repo
-- body-edit PRs and per-RFC-repo PRs. The pr_kind disambiguates.
CREATE TABLE cached_prs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
rfc_slug TEXT NOT NULL,
pr_kind TEXT NOT NULL CHECK (pr_kind IN ('idea', 'meta_body_edit', 'meta_metadata', 'meta_claim', 'rfc_branch')),
repo TEXT NOT NULL, -- 'org/repo' the PR lives on
pr_number INTEGER NOT NULL,
title TEXT NOT NULL,
description TEXT,
state TEXT NOT NULL CHECK (state IN ('open', 'merged', 'closed', 'withdrawn')),
opened_by TEXT, -- gitea login (resolved from On-behalf-of trailer where present)
opened_at TEXT,
merged_at TEXT,
closed_at TEXT,
head_branch TEXT,
base_branch TEXT NOT NULL DEFAULT 'main',
head_sha TEXT,
UNIQUE (repo, pr_number)
);
CREATE INDEX idx_cached_prs_rfc ON cached_prs (rfc_slug, state);
CREATE INDEX idx_cached_prs_kind ON cached_prs (pr_kind, state);