Skip to content

Allow Deprecation of Calibrations via Superseding #721

@bencap

Description

@bencap

Summary

Calibrations currently have no mechanism for deprecation. Score sets support deprecation by creating a new score set that supersedes the old one — setting superseded_score_set_urn on creation, which links the two via a replaces_id FK. This issue tracks adding equivalent superseding behavior to calibrations, including UI support for navigating the deprecation chain.

Problem

When a calibration needs to be updated or replaced, there is no way to mark the old calibration as deprecated or link it to its replacement. Researchers viewing a score set may encounter stale calibrations with no indication that a newer version exists. This is inconsistent with the score set lifecycle model, which explicitly supports superseding. Additionally, there is no way for a user to discover the history of a calibration — whether it was replaced, or what it replaced.

Proposed Behavior

A calibration may be superseded by another calibration belonging to the same score set. When a new calibration is created, an optional supersededCalibrationUrn field in the request body links it to the calibration it replaces. Once the superseding calibration is published:

  • The superseded calibration is hidden from the default calibration list returned on the score set.
  • The superseded calibration remains accessible directly by URN.
  • Both supersededCalibration and supersedingCalibration are included in the full calibration response so consumers can follow the deprecation chain.
  • The UI surfaces the deprecation chain, allowing users to navigate from a superseded calibration to its replacement and vice versa.

If the superseding calibration has not yet been published, both calibrations remain visible — matching the existing behavior for score sets.

Acceptance Criteria

  • ScoreCalibration model has a nullable superseded_calibration_id FK column (replaces_id) pointing to another ScoreCalibration, with a corresponding superseded_calibration relationship and superseding_calibration back-reference.
  • A new Alembic migration adds the replaces_id column to the calibrations table.
  • ScoreCalibrationCreate (or equivalent input view model) accepts an optional superseded_calibration_urn string field, validated against MAVEDB_CALIBRATION_URN_RE.
  • Validation rejects attempts to supersede a private (unpublished) calibration with an appropriate error message, matching the behavior for score sets.
  • The user must have READ permission on the superseded calibration to reference it.
  • SavedScoreCalibration (or equivalent response view model) includes superseded_calibration: Optional[ShorterScoreCalibration] and superseding_calibration: Optional[ShorterScoreCalibration].
  • The calibration list returned with a score set filters out calibrations that have been superseded by a published calibration, matching the filter logic used for score sets.
  • If the requesting user lacks READ permission on the superseding calibration, superseding_calibration is masked to None in the response.
  • The UI displays a deprecation notice on a superseded calibration and provides a link or navigation element to the superseding calibration.
  • The UI displays a "supersedes" notice on a superseding calibration and provides a link or navigation element back to the superseded calibration.
  • Users can follow the full deprecation chain in the UI (e.g., A → B → C) without needing to manually look up URNs.
  • Tests cover: creating a superseding calibration, verifying the superseded calibration is hidden once superseding is published, verifying it is still visible when superseding is unpublished, and verifying permission masking.

Implementation Notes

  • The deprecation model for score sets lives on ScoreSet as superseded_score_set_id (replaces_id in the DB) with ORM relationships superseded_score_set and supersigning_score_set. Mirror this pattern on ScoreCalibration.
  • Superseding validation for score sets is done in the ScoreSetCreate view model via a @field_validator on superseded_score_set_urn. Apply the same approach in the calibration input view model.
  • Score set superseding validation enforces that the target must be a published URN (not a tmp: URN). Apply the same rule: only a published calibration URN may be superseded.
  • The visibility filter for score sets uses an or_ on supersigning_score_set.id.is_(None) / supersigning_score_set.published_date.is_(None). Apply equivalent join-and-filter logic when building calibration queries.
  • Calibrations do not have their own router today — they are managed through the score sets router. The superseding logic should be wired in at the point where calibrations are created or updated within that router.
  • A calibration URN regex (MAVEDB_CALIBRATION_URN_RE) may need to be added to src/mavedb/lib/validation/urn_re.py if it does not already exist.
  • The ShorterScoreCalibration response type (analogous to ShorterScoreSet) needs to be defined if it does not exist, containing at minimum the URN and title, so the UI can render links without fetching full calibration objects.
  • The UI deprecation chain navigation should match how score set superseding is rendered — coordinate with the frontend on the expected shape of supersededCalibration and supersedingCalibration in the API response.

Metadata

Metadata

Assignees

No one assigned

    Labels

    app: backendTask implementation touches the backendapp: databaseTask implementation requires database changesapp: frontendTask implementation touches the frontendtype: featureNew feature

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions