Refactor steward permissions from per-action rows to role+scope model#564
Open
Refactor steward permissions from per-action rows to role+scope model#564
Conversation
Replace StewardPermission (one row per steward × contribution type × action)
with StewardAssignment, a role + scope model. Two roles: full_review
(accept, reject, request_more_info, propose) and propose (propose only).
Scope is one of a category, a contribution type, or global (both null).
Multiple assignments per steward are additive.
Data migration collapses existing permissions per steward: global full set
first, then category-level, then per-type, then propose-only with the same
collapsing. Partial action combinations are dropped with a summary print
for manual review. The old StewardPermission model is deleted in a follow
up migration.
Frontend response shapes are preserved so no Svelte changes are required:
/stewards/ still returns role + permitted_categories, /my-permissions/
still returns { ct_id: [actions] }. Django admin gains a StewardAssignment
inline with a dual-scope guard matching the DB CheckConstraint.
## Implementation Notes
- backend/stewards/models.py: Remove StewardPermission; add StewardAssignment with CheckConstraint (scopes mutually exclusive) and UniqueConstraint on (steward, role, scope_category, scope_type).
- backend/stewards/migrations/0010_stewardassignment.py: New model.
- backend/stewards/migrations/0011_migrate_permissions_to_assignments.py: Data migration collapsing StewardPermission rows into StewardAssignment rows.
- backend/stewards/migrations/0012_delete_stewardpermission.py: Drop old model.
- backend/contributions/permissions.py: Rewrite steward_has_permission and steward_permitted_type_ids to query StewardAssignment; full_review implies propose, encoded in code.
- backend/contributions/views.py: Rewrite /my-permissions/; short-circuits when steward has a global full_review assignment.
- backend/stewards/views.py: Rewrite /stewards/ list; role label derived from assignments, permitted_categories built via scope_category or scope_type.category_id.
- backend/stewards/admin.py: Replace StewardPermissionInline/Admin with StewardAssignmentInline/Admin; form clean() enforces single scope.
- backend/contributions/management/commands/review_submissions.py: AI steward creation uses a single global full_review StewardAssignment.
- backend/contributions/tests/test_steward_permissions.py, test_calibration_data.py: Update setUp to create StewardAssignment.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Replaces
StewardPermission(one row per steward × contribution type × action, 672 rows for 4 stewards) withStewardAssignment, a role + scope model. Two roles:full_review(accept/reject/request_more_info/propose) andpropose(propose only). Scope is a category, a contribution type, or global. Multiple assignments per steward are additive. The data migration collapses existing permissions per steward (global → category → per-type, then propose-only with the same collapsing), dropping partial action combinations with a print summary; verified against live data with zero mismatches (672 grants → 40 assignments). Frontend response shapes for/stewards/and/my-permissions/are preserved so no Svelte changes are required.Test plan
python manage.py migrateon a DB copy; confirmStewardPermissiontable is dropped andStewardAssignmentrow count matches the collapse summaryStewardAssignmentin Django admin with bothscope_categoryandscope_typeset; confirm form validation blocks it/stewards/list shows expected role label andpermitted_categories/my-permissions/returns{ct_id: [actions]}covering every permitted type