Files
rfc-app/docs/DEV.md
T
Ben Stull 3bc8fe92af Slice 2: the §8 active-RFC view in full
Per the §19.1 brief: the three-column shape (§8.1) opens on main
in discuss mode (§8.2), supports the §8.3 discuss-vs-contribute
flip on non-main branches, hosts §8.4's per-branch chat with AI
participation (§18's <change> protocol → §8.14 changes rows), the
§8.8 change-card panel with §8.9 accept/decline/edit-before-accept,
the §8.10 tracked-change markup + DiffView toggle, the §8.11
manual-edit flushes with the stale-change mechanic, the §8.12
range and paragraph sub-threads, the §8.13 flag affordance, and
the §8.14 discuss-mode buffer.

Backend: bot.py grew per-RFC-repo write ops (cut_branch_from_main,
commit_accepted_change with the structured original/proposed/reason
body and Change-Id + Source-Message-Id + On-behalf-of trailers,
commit_manual_flush, ensure_rfc_repo_seed). cache.py grew
refresh_rfc_repo and the webhook dispatches on repository.full_name.
providers.py and chat.py port the §18 carryovers — multi-provider
LLM abstraction and SSE-streaming chat against the §5 threads /
thread_messages / changes schema. api_branches.py mounts the §17
branches/<branch>/* and threads/<thread_id>/* routes with the §6
/ §11 permission checks inline.

Frontend: RFCView.jsx rebuilt as the §8 surface; Editor.jsx,
ChatPanel.jsx, ChangePanel.jsx, PromptBar.jsx, SelectionTooltip.jsx,
DiffView.jsx, ModelPicker.jsx, modelStyles.js lifted from the
prototype and adapted to the canonical schema.

Covered by `backend/tests/test_rfc_view_vertical.py` — eleven new
integration tests against an extended FakeGitea (PUT contents,
POST orgs/{org}/repos, seed_rfc_repo): 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. The 5
Slice 1 tests continue to pass alongside.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-24 04:35:14 -07:00

12 KiB
Raw Blame History

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 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.

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 <change> blocks, and materializes changes rows per §8.14). The §17 endpoints owned by Slice 2 — the branches/<branch>/* and threads/<thread_id>/* 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/<branch> 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 <login>-draft-<hex> (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/<slug>/prs/<n>.

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.