Skip to content

Scope review authority by category reviewer groups - #137

Merged
TrueMessenger merged 1 commit into
mainfrom
feat/scoped-review-authority
Jul 8, 2026
Merged

TrueMessenger merged 1 commit into
mainfrom
feat/scoped-review-authority

Conversation

@TrueMessenger

Copy link
Copy Markdown
Owner

What

Adds per-category review scoping so an alliance can restrict who reviews which submissions (e.g. a "Super Capital Admin" group reviews titan-doctrine submissions while a blackops reviewer cannot see or decide them), while keeping group membership (visibility) distinct from review authority.

Model

  • DoctrineCategory.reviewer_groups — a blank M2M to Group. Members of these groups who also hold a Fit Check review permission may review submissions for that category's doctrines. Empty list = unscoped (any reviewer). Migration 0041.

Semantics

A submission is reviewable by a plain reviewer if its doctrine is None, its doctrine has no categories, ≥1 of its categories has empty reviewer_groups (unscoped), or the user's groups intersect ≥1 category's reviewer_groups. Doctrine managers and superusers bypass everything. Both review permissions (review_submissions, secure_group_management) are treated identically. With nothing configured, behaviour is byte-for-byte the same as before.

Enforcement

Scope is applied across the whole review path:

  • Queue base queryset + its doctrine-filter dropdown
  • decide (403 when out of scope), bulk approve, bulk delete
  • Submission-detail non-owner access + the decision panel flag
  • New-submission reviewer ping fan-out (notify_reviewers_new_submission)
  • Reviewer digest per-reviewer counts (send_review_digest) — NotificationSettings gating and per-user mute preserved

Visibility narrowing

_is_manager now means doctrine managers and superusers only. A plain review permission is no longer a see-everything bypass: a reviewer sees member-visible content plus the categories they are scoped to review (authority grants sight). This flows through the visible_to querysets and the category-chip helpers via a shared visible_category_ids fold. An unscoped category still lets any reviewer decide its submissions even when they cannot open the doctrine page (authority ≠ visibility).

New helpers (_has_review_perm, reviewable_category_ids, can_review_submission, FitSubmissionQuerySet.reviewable_by) centralise the rules; the duplicated _can_review helper is removed.

UI

Category edit form/template and the admin gain the Reviewer groups field, with help text making the membership-vs-authority distinction explicit.

Tests

New fitcheck/tests/test_review_scoping.py covers: unscoped-install invariants (queue/decisions/notifications/digest unchanged), scoped authority across every surface, doctrine in two categories (one unscoped opens it to all), doctrine-less submissions, the reviewer visibility narrowing (in-scope sees, out-of-scope 403, manager sees all), and the pilot self-view. Full suite green (833 tests); check and makemigrations --check clean.

Add DoctrineCategory.reviewer_groups (blank M2M to Group): members of those
groups who also hold a Fit Check review permission may review submissions for
that category's doctrines. An empty list is unscoped - any reviewer may act -
so an install that configures nothing keeps the original behaviour exactly.
Reviewer authority is kept distinct from category visibility (Selected/Required
groups): being able to review a category is separate from being admitted to see
it.

The scope is enforced across the whole review path: the queue base queryset and
its doctrine dropdown, individual and bulk approve/reject actions, the decide
guard (403 out of scope), submission detail access and its decision panel, the
new-submission reviewer ping fan-out, and the reviewer digest's per-reviewer
counts.

Narrow the manager bypass: _is_manager now means doctrine managers and
superusers only. A plain review permission no longer reveals every doctrine,
fit, and category - a reviewer sees member-visible content plus the categories
they are scoped to review (authority grants sight). This flows through the
visible_to querysets and the category-chip helpers via the shared
visible_category_ids fold. New helpers _has_review_perm, reviewable_category_ids,
can_review_submission, and FitSubmissionQuerySet.reviewable_by centralise the
rules; _can_review is removed in favour of the shared helpers.

Add the Reviewer groups field to the category edit form/template and the admin,
and cover the whole behaviour in test_review_scoping.py (unscoped-install
invariants, scoped authority across every surface, multi-category and
doctrine-less openings, and the reviewer visibility narrowing). Migration 0041.
@TrueMessenger
TrueMessenger merged commit fc2031c into main Jul 8, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant