# 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 §§1–15 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 `` 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. ### Slice 2 — shipped The §8 active-RFC view in full. The bot wrapper grew per-RFC-repo write operations — branch cut from main, accept-change commit with the structured `original`/`proposed`/`reason` body and trailers, manual-edit flush, and a `ensure_rfc_repo_seed` seam Slice 5's graduation will eventually replace. The §4 cache now mirrors per-RFC repos via a new `refresh_rfc_repo` path; the webhook receiver dispatches on `repository.full_name` so per-RFC events refresh just that repo, and the reconciler sweeps every active entry. The §18 carryovers landed as `backend/app/providers.py` (the multi-provider abstraction, unchanged from the prototype) and `backend/app/chat.py` (an adapter that runs the provider's streaming interface against `thread_messages` rows, parses `` blocks, and materializes `changes` rows per §8.14). The §17 endpoints owned by Slice 2 — the `branches//*` and `threads//*` families — live in `backend/app/api_branches.py`, mounted alongside Slice 1's routes via `api.make_router`. On the frontend, `RFCView.jsx` was rebuilt as the §8 three-column surface; `Editor.jsx`, `ChatPanel.jsx`, `ChangePanel.jsx`, `PromptBar.jsx`, `SelectionTooltip.jsx`, `DiffView.jsx`, `ModelPicker.jsx`, and `modelStyles.js` were lifted from the prototype and adapted to the canonical `threads` / `thread_messages` / `changes` shape rather than the prototype's global session_id. The §18 carryovers explicitly preserved: SSE streaming with base64-encoded chunks, Tiptap + ProseMirror plugin for the paragraph-margin gutter accent, the prompt-bar selection-quote machinery, the model picker. 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 | | GET | `/api/models` | §18 | | GET | `/api/rfcs/{slug}/main` | §8.1, §8.2, §17 | | GET | `/api/rfcs/{slug}/branches/{branch}` | §8.4, §17 | | POST | `/api/rfcs/{slug}/branches/main/promote-to-branch` | §8.14, §17 | | POST | `/api/rfcs/{slug}/branches/{branch}/changes/{id}/accept` | §8.9, §17 | | POST | `/api/rfcs/{slug}/branches/{branch}/changes/{id}/decline` | §8.9, §17 | | POST | `/api/rfcs/{slug}/branches/{branch}/changes/{id}/reask` | §8.11, §17 | | POST | `/api/rfcs/{slug}/branches/{branch}/manual-flush` | §8.11, §17 | | POST | `/api/rfcs/{slug}/branches/{branch}/visibility` | §11.1, §17 | | POST | `/api/rfcs/{slug}/branches/{branch}/grants` | §6.4, §17 | | DELETE | `/api/rfcs/{slug}/branches/{branch}/grants/{login}` | §6.4 | | GET | `/api/rfcs/{slug}/branches/{branch}/threads` | §8.12, §17 | | POST | `/api/rfcs/{slug}/branches/{branch}/threads` | §8.12, §8.13 | | GET | `/api/rfcs/{slug}/branches/{branch}/threads/{id}/messages` | §8.12 | | POST | `/api/rfcs/{slug}/branches/{branch}/threads/{id}/messages` | §8.12 | | POST | `/api/rfcs/{slug}/branches/{branch}/threads/{id}/resolve` | §8.12 | | POST | `/api/rfcs/{slug}/branches/{branch}/threads/{id}/chat` | §18 | Slice 2 ships covered by `backend/tests/test_rfc_view_vertical.py` — the FakeGitea simulator from Slice 1 grew per-RFC-repo support (PUT contents, POST `orgs/{org}/repos`, `seed_rfc_repo`), and a new test file walks the §8 vertical end-to-end: main-view read, promote-to- branch, accept (with and without edit-before-accept), decline, manual flush + system message, flag creation, visibility flip, anonymous read-but-no-contribute, stale-change refusal, and the chat-streaming path with a fake provider injected. ### What's deferred from Slice 2 These were in the §8 spec but lean on infrastructure later slices build, so they were scoped out of this slice without altering the spec: - **Super-draft body editing on the meta repo (§9.5).** The `branches/` machinery is structurally general enough that meta-repo edit branches fall out of it once Slice 4 wires the super-draft view's "Start Contributing" gesture to cut against the meta repo. The Slice 2 RFCView renders a placeholder for super-draft entries pointing at Slice 4. - **The §10.4 review threads on PRs.** `thread_kind='review'` is in the schema and the threads endpoints honor it generically, but the PR-page surface where review threads anchor to diff hunks lands with Slice 3. - **DiffView's full reconstruction from `changes` history.** Slice 2 renders the editor's current HTML (which carries the session-local tracked-change markup from the accepts that happened in this session) into DiffView; rebuilding the full accepted-change markup from `changes` for a returning contributor needs a render pipeline DiffView doesn't yet own. The current behavior matches §8.10's "session-local" framing exactly; the §19.2 "persistent accepted-change markup" topic is the durable extension when evidence demands it. - **The §10.6 PR-side commit / chat reconciliation.** Manual-edit flushes drop a system-author message into branch chat per §10.6 in Slice 2, but the PR-side seen-cursor that uses the marker ships with Slice 3. - **Branch-name path conversion for slashes.** The auto-generated branch name in Slice 2 is `-draft-` (no slash) so the FastAPI `{branch}` path segment matches without `{branch:path}`. Users can still rename to a slashed name, but the routes will 404 on read; the proper fix is `{branch:path}` everywhere, which lands cleanly when Slice 3 makes the same change to the PR routes (PR numbers don't have this problem, but resolving the routing shape once across both surfaces is the right hop). ## 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 3: the PR flow per §10.** §8 settled the within-branch surface; §10 settles the bridge between a branch and main. The work covers the `Open PR` affordance from §10.1 (with the §11.3 universal-public confirmation when the branch is private), the §10.2 AI-drafted creation modal (title + description from the diff plus the branch chat), the §10.3 review page (three-column, diff in the center, compressed conversation right, per-user seen-cursor accenting new hunks and new messages), the §10.4 `thread_kind='review'` threads anchored to diff hunks inline in branch chat, §10.5 merge (no-fast-forward, preserving the per-acceptance commits), §10.6 update-after-open (commits and chat arriving on the open PR, the manual-flush system message that already lands per Slice 2), §10.7 post-merge (`Merged` banner, chat read-only, 90-day deletion timer starts), §10.8 withdraw, and §10.9 conflict-replay with the resolution-branch path. The shared seen-cursor mechanism in §15.7 (the `pr_seen` and `branch_chat_seen` cursors are in the schema already; Slice 3 wires the advance-on-view reconciler). The carryovers Slice 3 inherits — none new from the prototype; the prototype's `PRModal.jsx` had a one-shot PR-creation flow that the spec's §10 expanded considerably. The `backend/app/bot.py` operations Slice 3 needs are: `open_pr`, `merge_pr` (style='merge' to preserve the per-accepted-change commit nodes per §10.5), `close_pr` (for withdraw), and the resolution-branch replay sequence from §10.9 — which is structurally a `cut_branch_from_main` plus a series of `commit_accepted_change` calls plus an `open_pr`. The frontend needs a `PRView.jsx` sibling to `RFCView.jsx` that inherits the §8.1 three-column shape but renders the diff instead of the editor. The route is `/rfc//prs/`. The next build session should read `SPEC.md`, `README.md`, and `docs/DEV.md` and pick up Slice 3 cleanly without re-briefing. The working agreement in §19.3 continues to apply: implement the slice, correct the spec only where running code reveals it was wrong at a structural level, accumulate new candidate topics in §19.2, do not extend the spec beyond what the slice requires.