Skip to content

Route supersession through a first-class Action.SUPERSEDE #854

Description

@bencap

Supersession is authorized ad hoc at each call site. For calibrations, validate_superseded_score_calibration checks Action.READ, which passes for any published calibration. For score sets, create_score_set passes owner_or_contributor to fetch_score_set_by_urn, where the narrowing collapses against only_published. Neither spelling is the rule we want, and neither is reachable from the front end, so no supersede affordance can be rendered.

Action.UPDATE is not a substitute. _handle_update_action denies updates on published calibrations to everyone except admins, and only published calibrations may be superseded, so requiring UPDATE would make supersession admin-only.

Proposed behavior

Add SUPERSEDE to the Action enum and a _handle_supersede_action for both score_set and score_calibration.

Calibration predicate — permitted if any of:

  • an active admin role
  • entity.created_by_id == user_data.user.id
  • entity.investigator_provided and the user is a contributor to the score set

user_is_contributor_to_score_set keeps its existing definition in lib/permissions/score_calibration.py: the user's ORCID is in entity.score_set.contributors, or the user is the score set's created_by or modified_by.

Score set predicate — admin, or owner, or contributor.

A score set contributor may not supersede a community calibration on their own score set. Ranking is the team's tool for an unwanted community contribution; declaring their own calibration its successor asserts a scientific succession that isn't theirs to make.

Business invariants stay out of the permission handler and remain in the validators, since they hold regardless of who is asking:

  • the target is published (private is False) — a private calibration has no permitted superseder, including its owner and admins; edit it instead
  • the target is not already superseded
  • the target is on the same score set as the new calibration

Document one asymmetry in the handler's docstring: investigator_provided is frozen at creation while contributor status is evaluated per request, so adding someone to a score set grants them supersede rights over every investigator-provided calibration already on it, and removing them leaves their own calibrations superseded-able through the owner leg.

Acceptance criteria

  • permissions/user-is-permitted/{model_name}/{urn}/{action} answers supersede for score_set and score_calibration
  • validate_superseded_score_calibration asserts Action.SUPERSEDE instead of Action.READ
  • create_score_set asserts Action.SUPERSEDE on the superseded score set rather than relying on fetch_score_set_by_urn's owner_or_contributor argument
  • a non-contributor is denied supersession of a published calibration and of a published score set
  • a score set contributor is permitted on an investigator-provided calibration and denied on a community one
  • test_can_create_superseding_score_calibration_in_public_score_set_as_non_contributor is updated to assert denial

Metadata

Metadata

Assignees

No one assigned

    Labels

    app: backendTask implementation touches the backend

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions