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>
This commit is contained in:
+123
-43
@@ -49,6 +49,34 @@ 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 | § |
|
||||
@@ -64,29 +92,70 @@ The §17 endpoints exercised so far:
|
||||
| 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 |
|
||||
|
||||
### What's deferred from slice 1
|
||||
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.
|
||||
|
||||
These were on the §9.1 spec but pushed to Slice 2 because they belong
|
||||
with surfaces that haven't been built yet:
|
||||
### What's deferred from Slice 2
|
||||
|
||||
- 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 were in the §8 spec but lean on infrastructure later slices
|
||||
build, so they were scoped out of this slice without altering the
|
||||
spec:
|
||||
|
||||
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.
|
||||
- **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
|
||||
|
||||
@@ -123,31 +192,42 @@ and would otherwise have to be wired twice.
|
||||
|
||||
## Next slice
|
||||
|
||||
**Slice 2: the active-RFC view per §8.**
|
||||
**Slice 3: the PR flow per §10.**
|
||||
|
||||
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.
|
||||
§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 carryover assets that belong to Slice 2 are in the prototype
|
||||
under `/Users/benstull/projects/wiggleverse/rfc-app-prototype/`:
|
||||
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`.
|
||||
|
||||
- `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.
|
||||
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>`.
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
Reference in New Issue
Block a user