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:
@@ -2405,63 +2405,109 @@ surface. With Topic 13 folded in, the structural surface is
|
||||
complete. What follows is no longer "topics that block specifying
|
||||
v1" but "topics to address during or shortly after the v1 build."
|
||||
|
||||
### 19.1 Next slice: the active-RFC view in full
|
||||
### 19.1 Next slice: the PR flow
|
||||
|
||||
Slice 1 of the build has landed. The repository scaffolding
|
||||
(`backend/`, `frontend/`, `scripts/`, `docs/`) is in place; the §5
|
||||
canonical app tables exist as numbered SQLite migrations with the
|
||||
§4 cache mirror beside them; the §1 bot wrapper is the single
|
||||
chokepoint every Git write flows through, with the §6.5
|
||||
`On-behalf-of:` trailer applied uniformly and an `actions` row
|
||||
recorded; Gitea OAuth provisions a `users` row on first sign-in
|
||||
with role resolved from `OWNER_GITEA_LOGIN`; the §4.1 webhook
|
||||
receiver and the periodic reconciler both write to the cache and
|
||||
neither user actions nor the API do; the §7 left pane (catalog
|
||||
with search, sort, state-filter chips, pending-ideas disclosure,
|
||||
"+ Propose New RFC" button) renders against `GET /api/rfcs` and
|
||||
`GET /api/proposals`; and the end-to-end propose-to-super-draft
|
||||
vertical works: propose modal opens the idea PR, owner merges from
|
||||
the pending-idea view, webhook (or reconciler sweep) updates the
|
||||
cache, the catalog crossfades the super-draft in, and the
|
||||
super-draft view renders the body. The vertical is covered by
|
||||
integration tests against an in-process Gitea simulator.
|
||||
Slice 2 of the build has landed. The §8 active-RFC view is wired
|
||||
end-to-end against the local Gitea: the three-column shape (§8.1)
|
||||
inherits the §7 catalog on the left, hosts a Tiptap editor in the
|
||||
center with a breadcrumb dropdown listing main + open branches +
|
||||
open PRs, and surfaces per-branch chat plus the change-card panel
|
||||
on the right. Opening an active RFC lands on `main` in discuss mode
|
||||
per §8.2; `Start Contributing` on main calls the §17
|
||||
promote-to-branch endpoint to cut a new branch via the bot and
|
||||
re-anchors any pending `main`-scoped `changes` rows to it (§8.14).
|
||||
On a non-main branch the §8.3 discuss-vs-contribute toggle flips
|
||||
the editor between read-only and edit-enabled. The §18 carryovers
|
||||
landed in `backend/app/providers.py` and `backend/app/chat.py` and
|
||||
on the frontend as `Editor.jsx`, `ChatPanel.jsx`, `ChangePanel.jsx`,
|
||||
`PromptBar.jsx`, `SelectionTooltip.jsx`, `DiffView.jsx`, and
|
||||
`ModelPicker.jsx`. AI chat parses `<change>` blocks per §18 into
|
||||
`changes` rows with `state='pending'` per §8.14; accept runs the
|
||||
bot's per-accepted-change commit (§8.6) with the structured body
|
||||
and `Change-Id`, `Source-Message-Id`, and `On-behalf-of:` trailers;
|
||||
decline persists the row as evidence per §8.9; edit-before-accept
|
||||
preserves the AI's original text under an `AI proposed:` section
|
||||
of the commit body per §8.9. Manual edits flush as one commit per
|
||||
window with a system-author chat message landing per §10.6. The
|
||||
§8.10 tracked-change markup is session-local in the editor; DiffView
|
||||
is the read-only render of the same accepted changes. The §8.11
|
||||
stale-change machinery sets `changes.stale_since` when a manual
|
||||
edit changes the document such that a pending AI proposal's
|
||||
`original` no longer locates; the re-ask and force-apply paths are
|
||||
wired. The §8.12 range threads (via the selection tooltip) and the
|
||||
§8.13 flag threads (via the selection tooltip's flag tab) materialize
|
||||
as `threads` rows scoped to the branch; the chat feed renders them
|
||||
inline with the whole-doc default thread. The §11.1 visibility and
|
||||
§6.4 contribute grants are wired with the branch-creator /
|
||||
arbiter / admin set per §11.1, §11.2, §6.3. The §4 cache grew a
|
||||
`refresh_rfc_repo` path that the webhook dispatches per
|
||||
`repository.full_name` and the reconciler sweeps for every active
|
||||
entry. The vertical is covered by `backend/tests/test_rfc_view_vertical.py`
|
||||
— eleven integration tests against an extended FakeGitea that
|
||||
supports per-RFC repos.
|
||||
|
||||
Several §9 affordances that depend on infrastructure that has not
|
||||
yet been built were deferred from Slice 1 to Slice 2 — they are
|
||||
not new candidate topics, only delivery sequencing:
|
||||
Several §8 / §10 affordances were deferred from Slice 2 to later
|
||||
slices — they're not new candidate topics, only delivery sequencing:
|
||||
|
||||
- The §9.1 propose modal's AI-suggested tags and the §9.2
|
||||
AI-drafted PR description — the AI surface lands with chat.
|
||||
- The §9.3 two-step composer-then-preview decline dialog —
|
||||
shipped as a single-step required-comment input in Slice 1, with
|
||||
the preview-and-confirm ceremony pulled into the existing §19.2
|
||||
"pending-idea view's interaction design (remainder)" topic
|
||||
alongside the merge-confirmation ceremony.
|
||||
- The §9.3 pre-merge chat thread on a pending-idea view and its
|
||||
migration to the super-draft on merge — depends on the per-RFC
|
||||
/ per-branch chat infrastructure Slice 2 builds.
|
||||
- **Super-draft body editing on meta-repo edit branches (§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" to cut against the
|
||||
meta repo. The Slice 2 RFCView renders a placeholder for
|
||||
super-draft entries pointing at Slice 4.
|
||||
- **PR-anchored review threads (§10.4).** `thread_kind='review'` is
|
||||
in the §5 schema and the threads endpoints honor it generically,
|
||||
but the PR-page surface that anchors review threads to diff
|
||||
hunks lands with Slice 3.
|
||||
- **DiffView's full reconstruction from `changes` history.** Slice 2
|
||||
's DiffView renders the editor's current HTML, which carries the
|
||||
session-local tracked-change markup from accepts done in the
|
||||
current session. Rebuilding the markup for accepted changes
|
||||
earlier in branch history is the §19.2 "persistent
|
||||
accepted-change markup" topic; the §8.10 framing already commits
|
||||
the markup to session-local scope and points returning
|
||||
contributors at DiffView, which is the durable artifact.
|
||||
- **The §10.6 PR-side seen-cursor reconciliation.** Manual-edit
|
||||
flushes drop a system-author message per §10.6 in Slice 2, but
|
||||
the per-PR seen-cursor that uses the marker ships with Slice 3.
|
||||
|
||||
**Slice 2 is the active-RFC view per §8 in full.** The 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 (the §18 `<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. The carryover assets — the Tiptap
|
||||
configuration, the SelectionTooltip, the `<change>` parser, the
|
||||
prompt-bar selection-quote machinery, the multi-provider LLM
|
||||
abstraction, the SSE streaming — are present in working form in
|
||||
the prototype at
|
||||
`/Users/benstull/projects/wiggleverse/rfc-app-prototype/` and
|
||||
should be lifted directly per §18.
|
||||
**Slice 3 is the PR flow per §10 in full.** Open a PR via the
|
||||
§10.1 affordance on a branch (with the §11.3 universal-public
|
||||
confirmation when the branch is private); the §10.2 AI-drafted
|
||||
creation modal pulls title and description from the diff plus the
|
||||
branch chat. The §10.3 PR review page inherits the §8.1
|
||||
three-column shape — catalog left, diff (unified or split) in the
|
||||
center, the compressed conversation plus the inline review-comment
|
||||
surface (§10.4) on the right. The §10.3 per-user seen-cursor
|
||||
mechanism accents new hunks and new conversation messages on the
|
||||
next visit. The §10.4 review comments materialize as
|
||||
`thread_kind='review'`, `anchor_kind='range'` threads anchored to
|
||||
the post-PR document state, surfaced inline with the AI chat
|
||||
visually distinguished. §10.5 merge writes a no-fast-forward merge
|
||||
commit preserving the per-accepted-change commit nodes from §8.6
|
||||
as individual reachable commits in main's history. §10.6 update-
|
||||
after-open re-renders the diff as new commits arrive (which they
|
||||
already do — Slice 2's manual-flush and accept-change paths both
|
||||
push immediately). §10.7 post-merge renders the PR read-only with
|
||||
a `Merged` banner and starts §12's 90-day deletion timer. §10.8
|
||||
withdraw closes the PR with the same read-only treatment. §10.9
|
||||
conflict-replay cuts a fresh resolution branch off main's tip,
|
||||
replays the source branch's diff (running the AI participant
|
||||
against unambiguous conflicts and surfacing the rest to the
|
||||
contributor), and opens a new PR with the original auto-closing on
|
||||
merge.
|
||||
|
||||
The carryover assets Slice 3 inherits: none new from the prototype
|
||||
beyond what Slice 2 already lifted. The prototype's `PRModal.jsx`
|
||||
was a one-shot submission flow; §10's PR creation modal is its
|
||||
descendant but the spec broadened the surface considerably. The
|
||||
seen-cursor advances are pure schema work — `pr_seen` and
|
||||
`branch_chat_seen` are in the §5 schema; Slice 3 wires the
|
||||
advance-on-view reconciler from §15.7.
|
||||
|
||||
The next build session should read `SPEC.md`, `README.md`, and
|
||||
`docs/DEV.md` and pick up Slice 2 cleanly without re-briefing.
|
||||
The working agreement in §19.3 carries forward: implement the
|
||||
`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.
|
||||
@@ -2570,6 +2616,41 @@ binding.
|
||||
topic once the cost of "the cache thinks the bot proposed
|
||||
everything pre-app" becomes concrete. Touches §4.1 (the
|
||||
reconciler's job description) and §15.9 (the attribution rule).
|
||||
- **Branch-name path routing.** Slice 2's `branches/<branch>`
|
||||
endpoints use FastAPI's default `{branch}` path-segment matcher,
|
||||
which refuses slashes. The Slice 2 auto-generated branch name
|
||||
steered around this with `<login>-draft-<hex>`, but a user who
|
||||
renames to a slashed name will 404 on read. The fix is to convert
|
||||
every `branches/<branch>` route to `{branch:path}` with the
|
||||
understanding that ordering matters (more-specific routes like
|
||||
`branches/main/promote-to-branch` must register first). Surfaced
|
||||
by Slice 2's testing; defer-able until a user actually wants a
|
||||
slashed branch name.
|
||||
- **Markdown round-trip fidelity in the editor.** Slice 2's manual-
|
||||
flush converts the Tiptap document to text via `editor.getText()`,
|
||||
which discards markdown structure on round-trip (lists become
|
||||
flat lines, headings lose their `#`, links collapse to their text
|
||||
content). A faithful HTML-to-markdown serializer — or switching
|
||||
the on-disk format to a structured one the editor owns natively
|
||||
— earns its own session once usage shows where the loss bites.
|
||||
Touches §8.6 (commit unit) and §8.11 (the manual-edit card's
|
||||
diff fidelity).
|
||||
- **The chat feed's per-thread filter affordances.** §8.12 commits a
|
||||
top-of-chat disclosure that lists open threads with anchor previews
|
||||
and per-thread filter affordances. Slice 2 wired the disclosure
|
||||
counts; the filter that collapses the feed down to a single
|
||||
thread, and the per-thread "Reply" affordance that posts back into
|
||||
a specific thread from the unified feed, are the natural follow-on.
|
||||
Small scope, defer-able until the feed grows busy enough to
|
||||
warrant.
|
||||
- **Cross-branch source-message labelling.** §8.14's data-model rule
|
||||
permits a `changes` row whose `source_message_id` points at a
|
||||
message in main's chat — the row's `branch_name` was mutated from
|
||||
`main` to the new branch on promote-to-branch, but the message
|
||||
reference stays. Slice 2's frontend doesn't yet label these as
|
||||
"from a conversation on main" in the change panel; a small visual
|
||||
treatment is the natural follow-on. Surfaced by §8.14's data path
|
||||
going through Slice 2 for the first time.
|
||||
- **Body full-text search.** When the time comes.
|
||||
|
||||
Topic 13 (notifications) is settled and folded into §5 (the
|
||||
|
||||
Reference in New Issue
Block a user