Lands roadmap item #12: the RFC's owner can invite specific users by
email to one of two per-RFC roles — contributor (open PRs and join
discussion) or discussant (join discussion only). Non-invited users
keep the v0.6.0 anonymous-read contract: they can read but cannot
write/discuss that RFC. The platform-level grant (v0.8.0 / item #6)
is unchanged; this release adds a per-RFC membership layer beneath
it.
Migration 018_rfc_invitations.sql adds rfc_invitations (the
lifecycle row with the email token and 30-day expiry) and
rfc_collaborators (the accepted-invitation substrate the write
gate consults). FK-cascaded against cached_rfcs and users per §5.
New endpoints (in backend/app/api_invitations.py):
POST /api/rfcs/{slug}/invitations (owner)
GET /api/rfcs/{slug}/invitations (owner)
POST /api/rfcs/{slug}/invitations/{id}/revoke (owner)
GET /api/invitations/accept?token=… (signed-in)
POST /api/invitations/accept (signed-in)
The discussion / branch / open-pr write surfaces compose new
auth.can_discuss_rfc and auth.can_contribute_to_rfc predicates
after the existing require_contributor check. A super-draft with
no frontmatter owners yet falls through to the platform-granted
contract — the gate engages only once an owner exists.
The email path reuses the existing SMTP plumbing (EmailConfig.from_env)
the v0.7.0 OTC and v0.5.0 notification mailers share — transactional
envelope, no preferences honored, no unsubscribe footer.
The §17 admin user-management surface (item #7, v0.9.0) is extended
additively: GET /api/admin/users carries a new per-user
rfc_invitations array naming each accepted per-RFC collaboration
with inviter / RFC / role / timestamp. The platform-grant decision
keeps its context without restructuring the existing shape.
Frontend additions: InvitationsModal.jsx (owner-only RFC view
header strip affordance), AcceptInvitation.jsx (the
/invitations/accept landing page), five api.js helpers.
237 backend tests pass (18 new in test_rfc_invitations_vertical.py,
three older tests updated to opt into the per-RFC contributor
contract via the new grant_rfc_collaborator test seam). Frontend
build clean.
No new env vars. No new overlay keys. Migration auto-applied on
backend start by the existing db.run_migrations() sweep.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The §13.3 transactional sequence flips a super-draft to active —
five steps with paired undoes, an in-process orchestrator fed by
an asyncio.Queue, the §17 SSE endpoint streaming step transitions
to the dialog. Each step is a new bot primitive that logs an
`actions` row, bracketed by `graduate_start` / `graduate_complete`
for the linkable audit sequence. Rollback runs the undoes in
reverse from the last completed step; merge_pr has no undo by
design per §13.5.
The §9.8 precondition gate is enforced server-side at the top of
POST /graduate so the §13.3 rollback complexity does not grow.
The §13.4 chat migration is a database semantic no-op — the
(slug, branch_name='main') threads keep their identity, only the
interpretation changes. The §9.8 pre-graduation history surfaces
via a new _is_meta_target(rfc, branch) dispatch helper and lands
as pre_graduation_history on /main.
§13.1 claim flow landed alongside since it's the prerequisite for
non-admin graduation — bot.open_claim_pr plus broadening
api_prs._require_pr to accept meta_claim.
45/45 tests green; ten new integration tests cover the validator,
the §9.8 precondition refusal, happy path with audit verification,
mid-sequence rollback at steps 2 and 3, concurrent refusal,
chat-survives-without-data-movement, pre-graduation history, and
the §13.1 claim PR cycle.
SPEC.md §19.1 rewritten for Slice 6 (notifications); §19.2 grew
four candidates surfaced during the slice.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>