503689bf1abc124932c6f984bf33270ce37ad287
4 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
503689bf1a |
feat(projects): M2 — project-scoped authorization + the §22.7 resolver (§22)
Builds the three-tier authorization resolver on M1's schema spine: the most-permissive union of deployment role (§6.1), project role (§22.6), and per-RFC authority (§6.3/§12), with the §22.5 visibility gate subtractive on top (§22.7). Pure app-layer — no migration (M1 shipped the tables), no behavior change on the public default project. Verifiable on the single default project by flipping its visibility and granting/revoking roles. - app/auth.py: the §22.7 resolver primitives — project_visibility, project_member_role, project_of_rfc, is_project_superuser, can_read_project, effective write/discuss standing (can_contribute_in_project / can_discuss_in_project), require_project_readable, visible_project_ids. The three per-RFC capability helpers (can_discuss_rfc / can_contribute_to_rfc / can_invite_to_rfc) now compose all three tiers, so the ~20 call sites inherit M2 unchanged. - Read pass: the §22.5 visibility gate (404 to non-members) threaded into every RFC-resolution helper across api.py / api_branches / api_prs / api_graduation / api_discussion / api_contributions / api_invitations, plus the catalog + proposals listings filtered by visible_project_ids. - Write pass: the deep gates (branch read/contribute/owner, PR merge/withdraw/ edit, graduation, discussion resolve) fold in project_admin via is_project_superuser; the "any-contributor" branch mode routes through can_contribute_in_project; propose + claim gate on project contribute standing. Deployment-level surfaces (admin idea-PR merge/decline, account notification-mute) stay deployment-scoped. - Operator decisions: implicit-on-public (a granted deployment contributor keeps its pre-M2 write baseline on a public project, no membership row, so the N=1 case stays whole) and preserve-curation (that baseline does not override per-RFC owner curation — only an explicit project grant or a deployment owner/admin does), keeping the v0.16.0 per-RFC invite contract intact on public. - tests: test_multi_project_authz_vertical.py — 9 vertical assertions on the resolver tiers, public-unchanged regression, the gated 404 read gate, the gated contribution gate, union + subtractive visibility, and revocation. Full suite 390 passed. - docs: mark Part C M2 "(landed)" with the two operator decisions. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> |
||
|
|
e794523079 |
v0.26.0: auto-link referenced RFCs in PR text + comments (roadmap #28 Part 1)
Part 1 of item #28: references to existing accepted RFCs inside PR descriptions and comment text now render as inline links to the referenced RFC. Parts 2 (offer-to-create) and 3 (offer-to-contribute- to-pending) are deliberately deferred — Part 1 ships first as the easy win, per the roadmap row. Shipped in parallel with the v0.25.0 security-hardening session; this took the next free version slot (0.26.0) per the roadmap's "claims the next available version number" rule. Expect a top-of-file CHANGELOG/VERSION merge with 0.25.0 — distinct concerns, trivial to resolve. Backend: - rfc_links.py (new): builds a term index from the live accepted (state='active') RFC corpus and segments plain text into text / rfc-link segments. Conservative matching — links only rfc_id tokens (RFC-0001), multi-word titles (Open Human Model), and hyphenated slugs (open-human-model); a single common-word title/slug is NOT linked (would turn every prose "human" into a link). Case-insensitive, word-boundary-anchored, longest-match-wins, self-reference suppressed. - api_prs.py get_pr(): enriches the PR description (description_segments) and every PR comment (text_segments). - api_discussion.py: enriches PR-less discussion comments (text_segments). Read-time, not submit-time: the roadmap says "at submit time" but the intent it names is "not as live compose preview", which read-time honors. Chosen for correctness (links track the live active set — newly-accepted RFCs start linking, withdrawn ones stop), zero migration, and cheapness (small cache-resident corpus). Recorded as a §19.3-rule-2 note in the session transcript. Frontend: - LinkedText.jsx (new): maps backend segments onto React text nodes + anchors. No dangerouslySetInnerHTML — XSS-safe by construction, independent of any HTML-sanitization layer. Falls back to raw text when segments are absent. - PRView.jsx: description + PR conversation comment bodies render via LinkedText. - RFCDiscussionPanel.jsx: discussion comment bodies render via LinkedText. - App.css: .rfc-autolink (subtle accent + dotted underline, tokenized). Tests: 12 new (test_rfc_links_vertical.py) — 9 scanner units + 3 end-to-end (PR description / review comment / discussion comment all surface *_segments; self-reference suppression). Full suite 363 green; frontend builds clean. No upgrade steps: additive, no migration, no secret, no config. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
ee4925b6ac |
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
(
|
||
|
|
c92730a737 |
Release 0.5.0: PR-less per-RFC discussion (contribution still requires PR)
Roadmap item #3. An RFC's main view now carries a discussion surface distinct from PR comments and from branch chat. The substrate is the existing threads/thread_messages tables — rows with branch_name IS NULL scope to the RFC's main view; the schema already permitted that shape, v0.5.0 is the first build to write it. Five new endpoints under /api/rfcs/<slug>/discussion/..., a new RFCDiscussionPanel right-column component used when branchParam === main, SPEC §10.10 settling discussion-vs-contribution, and §17 listing the new routes. Notification routing reuses the existing chat_message_in_participated_thread / chat_reply_to_my_message event kinds with branch_name=null on the fan-out row; a distinct event_kind is a §19.2 candidate. Anonymous viewers can read; writes require contributor — v0.6.0's item #4 will harden adjacent gates. No schema migration; minor bump, no operator action required beyond rebuild and restart. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> |