Release 0.16.0: owner-only invite for per-RFC contribution + discussion (+ #21 Part C Amplitude wiring)
Wave 5 / Track B. Roadmap item #12. Folds in #21 Part C Amplitude
wiring inline (operator ask: "best practices from the very get-go").
RFC owners can invite specific users to one of two per-RFC roles:
contributor (open PRs + join discussion) or discussant (discussion
only). Non-invited users keep the v0.6.0 anonymous-read contract.
The per-RFC write gate layers on top of the existing
require_contributor gate; a super-draft with no owners yet falls
through to the platform-granted contract, preserving the
v0.6.0/v0.7.0/v0.8.0 contracts in their domains.
Backend: migration 018_rfc_invitations.sql (auto-applied — two
tables: rfc_invitations + rfc_collaborators); api_invitations.py
with five endpoints + transactional email; auth.py helpers
(is_rfc_owner / is_rfc_collaborator / can_discuss_rfc /
can_contribute_to_rfc / can_invite_to_rfc); api_discussion +
api_branches + api_prs gate composition; api_admin.py additive
rfc_invitations[] per user. 237 backend tests pass (18 new in
test_rfc_invitations_vertical.py).
Frontend: InvitationsModal.jsx (owner surface), AcceptInvitation.jsx
(/invitations/accept route), api.js helpers, RFCView.jsx
Invitations button, App.jsx route registration.
Amplitude wiring (inline, #21 Part C):
- INVITATION_SENT from InvitationsModal { rfc_slug, role_in_rfc }
- INVITATION_ACCEPTED from AcceptInvitation { rfc_slug, role_in_rfc }
- identify() BEFORE the accept event with properties: invited_at
(setOnce), last_invited_to_rfc, last_invite_role_in_rfc,
claim_method: 'rfc-invite'
- EVENTS taxonomy extended with INVITATION_SENT + INVITATION_ACCEPTED
No new secrets, no new overlay keys, no operator gesture beyond the
v0.15.0 overlay-set + restart. Frontend build verified green.
Subagent ν shipped the feature on feature/v0.16.0-owner-invite
(a51beec). Driver-side integration squash-merged into main,
hand-resolved VERSION + package.json + CHANGELOG (strict-descending
0.16.0 → 0.15.0), and added the inline Amplitude wiring.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -43,6 +43,7 @@ import RFCDiscussionPanel from './RFCDiscussionPanel.jsx'
|
||||
import ChangePanel, { diffWords } from './ChangePanel.jsx'
|
||||
import PRModal from './PRModal.jsx'
|
||||
import GraduateDialog from './GraduateDialog.jsx'
|
||||
import InvitationsModal from './InvitationsModal.jsx'
|
||||
import { claimOwnership } from '../api'
|
||||
import { EVENTS, track } from '../lib/analytics'
|
||||
|
||||
@@ -148,6 +149,11 @@ export default function RFCView({ viewer }) {
|
||||
const [showMetadataPane, setShowMetadataPane] = useState(false)
|
||||
const [showGraduateDialog, setShowGraduateDialog] = useState(false)
|
||||
const [claimError, setClaimError] = useState(null)
|
||||
// v0.16.0 (item #12): the per-RFC invitations modal. Visible only to
|
||||
// RFC owners (frontmatter) and platform admin/owner — the backend
|
||||
// gates the underlying endpoints regardless, so a leaked toggle
|
||||
// can't actually leak anything.
|
||||
const [showInvitationsModal, setShowInvitationsModal] = useState(false)
|
||||
|
||||
// Load main view + branch view whenever slug/branch changes.
|
||||
useEffect(() => {
|
||||
@@ -633,6 +639,20 @@ export default function RFCView({ viewer }) {
|
||||
Graduate to RFC repo
|
||||
</button>
|
||||
)}
|
||||
{/* v0.16.0 (item #12): owner-only invitations affordance.
|
||||
Shown when the viewer is named in the RFC's frontmatter
|
||||
`owners` list or holds a platform admin/owner role.
|
||||
Available on both super-drafts and active RFCs. */}
|
||||
{viewer && (viewer.role === 'owner' || viewer.role === 'admin' || (entry?.owners || []).includes(viewer.gitea_login)) && (
|
||||
<button
|
||||
type="button"
|
||||
className="btn-link"
|
||||
onClick={() => setShowInvitationsModal(true)}
|
||||
title="Invite collaborators to this RFC"
|
||||
>
|
||||
Invitations
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{claimError && (
|
||||
@@ -865,6 +885,14 @@ export default function RFCView({ viewer }) {
|
||||
/>
|
||||
)}
|
||||
|
||||
{showInvitationsModal && (
|
||||
<InvitationsModal
|
||||
slug={slug}
|
||||
rfcTitle={entry?.title}
|
||||
onClose={() => setShowInvitationsModal(false)}
|
||||
/>
|
||||
)}
|
||||
|
||||
{showMetadataPane && (
|
||||
<MetadataPaneModal
|
||||
slug={slug}
|
||||
|
||||
Reference in New Issue
Block a user