Slice 3: the PR flow
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+106
-30
@@ -119,6 +119,73 @@ 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.
|
||||
|
||||
### Slice 3 — shipped
|
||||
|
||||
The §10 PR flow in full. The bot wrapper grew per-RFC-repo PR
|
||||
operations — `open_branch_pr` (with the §10.9 `Supersedes:` trailer
|
||||
hook), `merge_branch_pr` (no-fast-forward via Gitea's `style='merge'`,
|
||||
the `On-behalf-of:` trailer carrying the merging user per §6.5),
|
||||
`withdraw_branch_pr`, `cut_resolution_branch`, and
|
||||
`commit_replay_change` for the §10.9 per-accept replay onto fresh
|
||||
main. The §4 cache learned about per-RFC PRs via the existing
|
||||
`refresh_rfc_repo` sweep, plus a `_parse_supersedes` pass that bumps
|
||||
an original PR's state to closed and records the supersession the
|
||||
moment the resolution PR's merge arrives — whether via webhook or
|
||||
the reconciler. The §17 endpoints owned by Slice 3 — the
|
||||
`branches/<branch>/{pr-draft,open-pr}` and the `prs/<n>/*` family —
|
||||
live in `backend/app/api_prs.py`, mounted alongside Slices 1 and 2's
|
||||
routes via `api.make_router`. The migration in `007_pr_flow.sql`
|
||||
adds `superseded_by_pr_number` and `merge_commit_sha` columns to
|
||||
`cached_prs` plus the `pr_resolution_branches` join table that
|
||||
records resolution-branch parentage so the cache can supersede the
|
||||
original on the resolution PR's merge.
|
||||
|
||||
On the frontend, the `Open PR` affordance landed on `RFCView.jsx`'s
|
||||
branch view (gated on the branch having commits ahead of main and no
|
||||
already-open PR), opening a new `PRModal.jsx` that fetches the AI
|
||||
draft via `/pr-draft`, lets the contributor edit, and surfaces the
|
||||
§11.3 universal-public confirmation inline when the source branch is
|
||||
private. The `PRView.jsx` sibling to `RFCView.jsx` is mounted at
|
||||
`/rfc/:slug/pr/:prNumber` and renders the §10.3 three-column shape:
|
||||
catalog left (App chrome), a unified/split diff in the center
|
||||
computed from main and branch RFC.md bodies, and a compressed
|
||||
conversation surface on the right that interleaves chat / flag /
|
||||
review threads with visual distinction per §10.4. The per-user
|
||||
seen-cursor advances on every visit; new commits and new messages
|
||||
since the cursor surface with an accent. The merge button is
|
||||
arbiter-gated per §6.3; withdraw is contributor-or-arbiter per §10.8;
|
||||
the §10.9 `Start resolution branch` affordance fires from the
|
||||
conflict banner when the live Gitea pull reports the PR as
|
||||
unmergeable, and the new resolution branch opens in the §8 editor for
|
||||
the contributor to re-anchor stale changes before opening the
|
||||
resolution PR.
|
||||
|
||||
The §17 endpoints exercised in Slice 3:
|
||||
|
||||
| Method | Path | § |
|
||||
| ------ | ----------------------------------------------- | ------- |
|
||||
| POST | `/api/rfcs/{slug}/branches/{branch}/pr-draft` | §10.2 |
|
||||
| POST | `/api/rfcs/{slug}/branches/{branch}/open-pr` | §10.1 |
|
||||
| GET | `/api/rfcs/{slug}/prs/{n}` | §10.3 |
|
||||
| POST | `/api/rfcs/{slug}/prs/{n}/seen` | §10.3 |
|
||||
| POST | `/api/rfcs/{slug}/prs/{n}/review` | §10.4 |
|
||||
| POST | `/api/rfcs/{slug}/prs/{n}/merge` | §10.5 |
|
||||
| POST | `/api/rfcs/{slug}/prs/{n}/withdraw` | §10.8 |
|
||||
| POST | `/api/rfcs/{slug}/prs/{n}/description` | §10.2 |
|
||||
| POST | `/api/rfcs/{slug}/prs/{n}/resolution-branch` | §10.9 |
|
||||
|
||||
Slice 3 ships covered by `backend/tests/test_pr_flow_vertical.py` —
|
||||
nine integration tests against an extended FakeGitea that grew PR
|
||||
mergeability via base-snapshot tracking, no-fast-forward merge
|
||||
behavior, and a `mergeable` field on PR responses. The tests cover
|
||||
opening (with the §11.3 visibility flip and the §10.9 one-PR-per-
|
||||
branch refusal), the AI draft, the three-column payload shape,
|
||||
seen-cursor advance with stale-tab protection, review-thread
|
||||
posting, arbiter-only merge, contributor withdraw with the
|
||||
`withdrawn` state distinct from generic `closed`, anonymous read
|
||||
of a public PR, and the full §10.9 conflict-replay path including
|
||||
the auto-close of the original PR on the resolution PR's merge.
|
||||
|
||||
### What's deferred from Slice 2
|
||||
|
||||
These were in the §8 spec but lean on infrastructure later slices
|
||||
@@ -192,41 +259,50 @@ spec:
|
||||
|
||||
## Next slice
|
||||
|
||||
**Slice 3: the PR flow per §10.**
|
||||
**Slice 4: super-draft body editing per §9.5 + §9.6.**
|
||||
|
||||
§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 §8 within-branch surface and the §10 bridge to main now ship for
|
||||
active RFCs; the same mechanics still need to reach super-draft
|
||||
entries on the meta repo. Slice 4's unit of work is the meta-repo
|
||||
edit branch — `edit/<slug>/<auto-name>` per §9.5 — and the
|
||||
structural claim is that almost everything from §8 falls out
|
||||
unchanged once `<slug>` resolves to a super-draft entry and
|
||||
`<branch>` names a meta-repo branch rather than a per-RFC-repo
|
||||
branch (see the §5 super-draft scoping note).
|
||||
|
||||
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`.
|
||||
What Slice 4 owns specifically:
|
||||
|
||||
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>`.
|
||||
- §9.5's `Start Contributing` on a super-draft cutting an
|
||||
`edit/<slug>/<auto-name>` branch on the meta repo via the bot,
|
||||
re-anchoring pending `changes` rows from `main` to the new branch
|
||||
the way `promote-to-branch` does for active RFCs.
|
||||
- §9.6's chat-and-threads surface scoped to the super-draft and to
|
||||
edit branches, sharing the §5 `threads`/`thread_messages` shape.
|
||||
- §9.7's visibility and contribute grants on edit branches — the
|
||||
same `branch_visibility` / `branch_contribute_grants` machinery
|
||||
that Slice 2 wired, now keyed on the meta repo.
|
||||
- The metadata pane from §9.5 — title and tag edits as small
|
||||
meta-repo PRs via `POST /api/rfcs/{slug}/metadata`. Slug renames
|
||||
remain deferred per §9.5 / §19.2.
|
||||
- The §17 routing collapse the spec calls for: the
|
||||
`branches/<branch>/...` endpoint family already exists; Slice 4's
|
||||
job is the dispatch in `api_branches.py` that recognizes a
|
||||
super-draft slug and routes to the meta repo on every read and
|
||||
write. `RFCView.jsx`'s super-draft placeholder is replaced by the
|
||||
full editor surface.
|
||||
|
||||
What Slice 4 does NOT own: the §10 PR flow against the meta repo's
|
||||
super-draft edits is structurally identical to the active-RFC PR
|
||||
flow Slice 3 just shipped, and falls out from the same dispatch.
|
||||
The graduation flow from §13 stays deferred to Slice 5.
|
||||
|
||||
The carryovers Slice 4 inherits — none new from the prototype;
|
||||
every §8 / §10 surface already exists. The work is dispatch glue
|
||||
plus a small number of routes that need the meta-repo path
|
||||
(`branches/edit/<slug>/<auto-name>` cuts).
|
||||
|
||||
The next build session should read `SPEC.md`, `README.md`, and
|
||||
`docs/DEV.md` and pick up Slice 3 cleanly without re-briefing. The
|
||||
`docs/DEV.md` and pick up Slice 4 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
|
||||
|
||||
Reference in New Issue
Block a user