Skip to content

Couple Diagnostic.version into the regression CI coupling gate#770

Merged
lewisjared merged 6 commits into
mainfrom
feat/diagnostic-version-gate
Jun 30, 2026
Merged

Couple Diagnostic.version into the regression CI coupling gate#770
lewisjared merged 6 commits into
mainfrom
feat/diagnostic-version-gate

Conversation

@lewisjared

@lewisjared lewisjared commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Description

Couples the per-diagnostic, author-declared Diagnostic.version into the regression CI coupling gate as an authoritative "results changed" staleness signal, alongside the existing per-case test_case_version.

Why. A replay re-runs only the extraction code (build_execution_result) against the cached native baseline, so it is structurally blind to execution-affecting changes — the recipe, the pinned ESMValTool commit, update_recipe, or data requirements. Path-based extraction_changed detection cannot tell an execution change from an extraction change within a provider package, so such a change currently routes to a green replay that validates nothing about the new computation. Diagnostic.version is the author's existing, explicit declaration that a diagnostic's results changed (it already drives live-solver recomputation), so the gate now treats it as authoritative.

What changed.

  • Manifest schema 1 -> 2 with a new required diagnostic_version field, recorded at mint time.
  • decide_coupling FAILs a case when the in-code Diagnostic.version exceeds the manifest's recorded value (stale baseline — re-mint required) or falls below it (illegal downgrade). A cross-PR diagnostic_version decrease FAILs only when the committed bundle is unchanged, so a legitimate revert that lowers the version and re-mints the baseline keeps a green path.
  • mint (including --from-replay) stamps diag.version; run/build preserve the recorded value. A version bump without a re-mint therefore FAILs the gate, forcing the baseline to be regenerated.
  • New ref test-cases migrate-manifests verb backfills diagnostic_version across all existing baselines (idempotent, iterates the same _iter_test_cases the gate uses). Applied here to all 84 committed manifests (23 carry version 2: every ilamb case and the pmp annual_cycle cases).
  • The coarse path-based extraction_changed replay backstop is retained — it and the author-declared signal are complementary.

test_case_version remains a separate, independently-monotonic per-case axis; the two are not collapsed here.

This change was produced through a Planner/Architect/Critic consensus review; the design rationale (including the downgrade carve-out and the rejected --allow-downgrade alternative) is captured in the commit body and the updated docs/background/regression-baselines.md.

Verification. make mypy clean (141 files); 87 gate/manifest unit tests, 149 CLI/integration tests pass; ref test-cases ci-gate reports no spurious failures (native-bearing cases route to replay as expected for a core-code change).

Checklist

Please confirm that this pull request has done the following:

  • Tests added
  • Documentation added (where applicable)
  • Changelog item added to changelog/

Summary by CodeRabbit

  • New Features
    • Added diagnostic_version support to regression test-case manifests, with manifest schema v2.
    • Introduced a one-shot ref test-cases migrate-manifests command to backfill and update existing manifests.
  • Bug Fixes
    • Strengthened the CI coupling gate to fail when the in-code diagnostic version is stale versus the recorded baseline.
    • Legitimate reverts remain supported via authorised diagnostic version decreases and re-minting.
  • Documentation
    • Updated regression baseline and coupling-gate guidance for the new versioning semantics.
  • Tests
    • Expanded unit, integration, and CLI coverage for schema v2 validation, manifest migration, and gate decisions.

The coupling gate previously read only test_case_version, so an
execution-affecting change (recipe, ESMValTool commit, update_recipe)
could slip through as a green replay against a stale committed bundle
because replay never re-runs execution.

Couple the per-diagnostic, author-declared Diagnostic.version into the
gate as an authoritative staleness signal:

- Manifest schema 1 -> 2 with a required diagnostic_version field.
- decide_coupling FAILs a case when the in-code Diagnostic.version
  exceeds the manifest's recorded value (stale baseline) or falls below
  it (illegal downgrade); a cross-PR decrease FAILs only when the
  committed bundle is unchanged, so a legitimate revert-plus-remint has
  a green path.
- mint (incl. --from-replay) stamps diag.version; run/build preserve the
  recorded value, so a bump without a re-mint FAILs the gate.
- New 'ref test-cases migrate-manifests' verb backfills diagnostic_version
  across all existing baselines (idempotent, iterator-driven).

test_case_version remains a separate, independently-monotonic per-case
axis.
@coderabbitai

coderabbitai Bot commented Jun 30, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 5f1f7b24-51e5-4851-85ea-2ba51dcede19

📥 Commits

Reviewing files that changed from the base of the PR and between d5355b9 and 132b095.

📒 Files selected for processing (4)
  • docs/background/regression-baselines.md
  • packages/climate-ref-core/src/climate_ref_core/regression/gate.py
  • packages/climate-ref/src/climate_ref/cli/test_cases/migrate.py
  • packages/climate-ref/tests/unit/cli/test_test_cases.py
🚧 Files skipped from review as they are similar to previous changes (4)
  • packages/climate-ref/src/climate_ref/cli/test_cases/migrate.py
  • packages/climate-ref-core/src/climate_ref_core/regression/gate.py
  • docs/background/regression-baselines.md
  • packages/climate-ref/tests/unit/cli/test_test_cases.py

📝 Walkthrough

Walkthrough

Adds diagnostic_version to manifest schema v2, extends coupling decisions to compare code and recorded diagnostic versions, threads the field through CLI manifest writes and migration, and updates tests, committed manifests, and docs.

Changes

diagnostic_version schema, gate, and migration

Layer / File(s) Summary
Manifest schema v2 and loading
packages/climate-ref-core/src/climate_ref_core/regression/manifest.py
Bumps SCHEMA_VERSION to 2, adds diagnostic_version: int to Manifest, validates it on load, includes it in dumps, and extends seed_v1 to accept it.
Coupling gate staleness and revert checks
packages/climate-ref-core/src/climate_ref_core/regression/gate.py
Adds code_diagnostic_version: int | None = None to decide_coupling, fails on diagnostic-version mismatches, and allows decreases when the committed bundle changes with an authorised revert.
CLI manifest writing and gate wiring
packages/climate-ref/src/climate_ref/cli/test_cases/_common.py, baselines.py, run.py, ci_gate.py
Threads diagnostic_version through manifest writers and passes code_diagnostic_version=diag.version into the CI gate call.
migrate-manifests CLI command
packages/climate-ref/src/climate_ref/cli/test_cases/migrate.py, __init__.py
Adds migrate-manifests, which rewrites committed manifests to SCHEMA_VERSION, stamps diagnostic_version, and registers the command module.
Unit tests for manifest and gate
packages/climate-ref-core/tests/unit/regression/test_gate.py, test_manifest.py
Updates manifest helpers and adds gate/manifest coverage for diagnostic_version round-trips, validation, stale-version failures, and revert behaviour.
Integration and CLI unit tests
packages/climate-ref/tests/integration/test_native_roundtrip.py, packages/climate-ref/tests/unit/cli/test_test_cases.py
Updates schema-v2 manifest constructions, adds migration coverage, and extends CLI tests for diagnostic-version stamping and gate outcomes.
Committed test-data manifest migration
packages/climate-ref-esmvaltool/tests/test-data/*/manifest.json, packages/climate-ref-ilamb/tests/test-data/*/manifest.json, packages/climate-ref-pmp/tests/test-data/*/manifest.json, packages/climate-ref-example/tests/test-data/*/manifest.json
Updates committed test-data manifests to schema 2 with diagnostic_version added.
Documentation and changelog
docs/background/regression-baselines.md, changelog/770.feature.md
Updates the regression-baselines docs and changelog entry for schema v2, diagnostic versioning, gate behaviour, and migration commands.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 26.32% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately summarizes the main change to the regression CI coupling gate.
Description check ✅ Passed The description matches the template with Description and Checklist sections, and all required checklist items are completed.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/diagnostic-version-gate

Comment @coderabbitai help to get the list of available commands.

…on-gate

* origin/main:
  feat(example): extract model/reference/bias time series as CMEC series
  refactor(example): rename diagnostic to global-sst-bias and drop version guard
  test: close NetCDF datasets in surface-temperature end-to-end tests
  feat(example): use ocean SST (tos) and add comparison figures
  test(example): mint regression baselines for surface-temperature-bias
  fix: address review feedback on surface temperature diagnostic
  test: update solver regression snapshots for new example diagnostic
  feat(example): add model-vs-obs surface temperature diagnostic and 5-minute quickstart

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: f6cf6b14-e599-4f73-bd13-4d925e564837

📥 Commits

Reviewing files that changed from the base of the PR and between 7207ab9 and ae8479e.

📒 Files selected for processing (98)
  • changelog/770.feature.md
  • docs/background/regression-baselines.md
  • packages/climate-ref-core/src/climate_ref_core/regression/gate.py
  • packages/climate-ref-core/src/climate_ref_core/regression/manifest.py
  • packages/climate-ref-core/tests/unit/regression/test_gate.py
  • packages/climate-ref-core/tests/unit/regression/test_manifest.py
  • packages/climate-ref-esmvaltool/tests/test-data/climate-at-global-warming-levels/cmip6/manifest.json
  • packages/climate-ref-esmvaltool/tests/test-data/climate-drivers-for-fire/cmip6/manifest.json
  • packages/climate-ref-esmvaltool/tests/test-data/climate-drivers-for-fire/cmip7/manifest.json
  • packages/climate-ref-esmvaltool/tests/test-data/cloud-radiative-effects/cmip6/manifest.json
  • packages/climate-ref-esmvaltool/tests/test-data/cloud-scatterplots-cli-ta/cmip6/manifest.json
  • packages/climate-ref-esmvaltool/tests/test-data/cloud-scatterplots-clivi-lwcre/cmip6/manifest.json
  • packages/climate-ref-esmvaltool/tests/test-data/cloud-scatterplots-clivi-lwcre/cmip7/manifest.json
  • packages/climate-ref-esmvaltool/tests/test-data/cloud-scatterplots-clt-swcre/cmip6/manifest.json
  • packages/climate-ref-esmvaltool/tests/test-data/cloud-scatterplots-clt-swcre/cmip7/manifest.json
  • packages/climate-ref-esmvaltool/tests/test-data/cloud-scatterplots-clwvi-pr/cmip6/manifest.json
  • packages/climate-ref-esmvaltool/tests/test-data/cloud-scatterplots-clwvi-pr/cmip7/manifest.json
  • packages/climate-ref-esmvaltool/tests/test-data/enso-basic-climatology/cmip6/manifest.json
  • packages/climate-ref-esmvaltool/tests/test-data/enso-characteristics/cmip6/manifest.json
  • packages/climate-ref-esmvaltool/tests/test-data/equilibrium-climate-sensitivity/cmip6/manifest.json
  • packages/climate-ref-esmvaltool/tests/test-data/global-mean-timeseries/cmip6/manifest.json
  • packages/climate-ref-esmvaltool/tests/test-data/global-mean-timeseries/cmip7/manifest.json
  • packages/climate-ref-esmvaltool/tests/test-data/ozone-annual-cycle/cmip6/manifest.json
  • packages/climate-ref-esmvaltool/tests/test-data/ozone-annual-cycle/cmip7/manifest.json
  • packages/climate-ref-esmvaltool/tests/test-data/ozone-lat-time/cmip6/manifest.json
  • packages/climate-ref-esmvaltool/tests/test-data/ozone-lat-time/cmip7/manifest.json
  • packages/climate-ref-esmvaltool/tests/test-data/ozone-nh-mar/cmip6/manifest.json
  • packages/climate-ref-esmvaltool/tests/test-data/ozone-nh-mar/cmip7/manifest.json
  • packages/climate-ref-esmvaltool/tests/test-data/ozone-sh-oct/cmip6/manifest.json
  • packages/climate-ref-esmvaltool/tests/test-data/ozone-sh-oct/cmip7/manifest.json
  • packages/climate-ref-esmvaltool/tests/test-data/ozone-zonal/cmip6/manifest.json
  • packages/climate-ref-esmvaltool/tests/test-data/regional-historical-annual-cycle/cmip6/manifest.json
  • packages/climate-ref-esmvaltool/tests/test-data/regional-historical-annual-cycle/cmip7/manifest.json
  • packages/climate-ref-esmvaltool/tests/test-data/regional-historical-timeseries/cmip6/manifest.json
  • packages/climate-ref-esmvaltool/tests/test-data/regional-historical-timeseries/cmip7/manifest.json
  • packages/climate-ref-esmvaltool/tests/test-data/regional-historical-trend/cmip6/manifest.json
  • packages/climate-ref-esmvaltool/tests/test-data/regional-historical-trend/cmip7/manifest.json
  • packages/climate-ref-esmvaltool/tests/test-data/sea-ice-area-basic/cmip6/manifest.json
  • packages/climate-ref-esmvaltool/tests/test-data/sea-ice-area-basic/cmip7/manifest.json
  • packages/climate-ref-esmvaltool/tests/test-data/sea-ice-sensitivity/cmip6/manifest.json
  • packages/climate-ref-esmvaltool/tests/test-data/sea-ice-sensitivity/cmip7/manifest.json
  • packages/climate-ref-esmvaltool/tests/test-data/transient-climate-response-emissions/cmip6/manifest.json
  • packages/climate-ref-esmvaltool/tests/test-data/transient-climate-response-emissions/cmip7/manifest.json
  • packages/climate-ref-esmvaltool/tests/test-data/transient-climate-response/cmip6/manifest.json
  • packages/climate-ref-esmvaltool/tests/test-data/transient-climate-response/cmip7/manifest.json
  • packages/climate-ref-esmvaltool/tests/test-data/zero-emission-commitment/cmip6-1pctCO2-parent/manifest.json
  • packages/climate-ref-esmvaltool/tests/test-data/zero-emission-commitment/cmip6-esm-1pctCO2-parent/manifest.json
  • packages/climate-ref-example/tests/test-data/global-mean-timeseries/cmip7/manifest.json
  • packages/climate-ref-example/tests/test-data/global-mean-timeseries/default/manifest.json
  • packages/climate-ref-ilamb/tests/test-data/burntfractionall-gfed/cmip6/manifest.json
  • packages/climate-ref-ilamb/tests/test-data/burntfractionall-gfed/cmip7/manifest.json
  • packages/climate-ref-ilamb/tests/test-data/csoil-hwsd2/cmip6/manifest.json
  • packages/climate-ref-ilamb/tests/test-data/csoil-hwsd2/cmip7/manifest.json
  • packages/climate-ref-ilamb/tests/test-data/emp-gleamgpcp2.3/cmip6/manifest.json
  • packages/climate-ref-ilamb/tests/test-data/gpp-fluxnet2015/cmip6/manifest.json
  • packages/climate-ref-ilamb/tests/test-data/gpp-wecann/cmip6/manifest.json
  • packages/climate-ref-ilamb/tests/test-data/lai-avh15c1/cmip6/manifest.json
  • packages/climate-ref-ilamb/tests/test-data/lai-avh15c1/cmip7/manifest.json
  • packages/climate-ref-ilamb/tests/test-data/mrro-lora/cmip6/manifest.json
  • packages/climate-ref-ilamb/tests/test-data/mrro-lora/cmip7/manifest.json
  • packages/climate-ref-ilamb/tests/test-data/mrsos-wangmao/cmip6/manifest.json
  • packages/climate-ref-ilamb/tests/test-data/nbp-hoffman/cmip6/manifest.json
  • packages/climate-ref-ilamb/tests/test-data/nbp-hoffman/cmip7/manifest.json
  • packages/climate-ref-ilamb/tests/test-data/snc-esacci/cmip6/manifest.json
  • packages/climate-ref-ilamb/tests/test-data/snc-esacci/cmip7/manifest.json
  • packages/climate-ref-ilamb/tests/test-data/so-woa2023-surface/cmip6/manifest.json
  • packages/climate-ref-ilamb/tests/test-data/so-woa2023-surface/cmip7/manifest.json
  • packages/climate-ref-ilamb/tests/test-data/thetao-woa2023-surface/cmip6/manifest.json
  • packages/climate-ref-ilamb/tests/test-data/thetao-woa2023-surface/cmip7/manifest.json
  • packages/climate-ref-pmp/tests/test-data/annual-cycle/cmip6-pr/manifest.json
  • packages/climate-ref-pmp/tests/test-data/annual-cycle/cmip6-ts/manifest.json
  • packages/climate-ref-pmp/tests/test-data/annual-cycle/cmip7-ts/manifest.json
  • packages/climate-ref-pmp/tests/test-data/enso_proc/cmip6/manifest.json
  • packages/climate-ref-pmp/tests/test-data/enso_proc/cmip7/manifest.json
  • packages/climate-ref-pmp/tests/test-data/enso_tel/cmip6/manifest.json
  • packages/climate-ref-pmp/tests/test-data/enso_tel/cmip7/manifest.json
  • packages/climate-ref-pmp/tests/test-data/extratropical-modes-of-variability-nam/cmip6/manifest.json
  • packages/climate-ref-pmp/tests/test-data/extratropical-modes-of-variability-nam/cmip7/manifest.json
  • packages/climate-ref-pmp/tests/test-data/extratropical-modes-of-variability-nao/cmip6/manifest.json
  • packages/climate-ref-pmp/tests/test-data/extratropical-modes-of-variability-nao/cmip7/manifest.json
  • packages/climate-ref-pmp/tests/test-data/extratropical-modes-of-variability-npgo/cmip6/manifest.json
  • packages/climate-ref-pmp/tests/test-data/extratropical-modes-of-variability-npgo/cmip7/manifest.json
  • packages/climate-ref-pmp/tests/test-data/extratropical-modes-of-variability-npo/cmip6/manifest.json
  • packages/climate-ref-pmp/tests/test-data/extratropical-modes-of-variability-npo/cmip7/manifest.json
  • packages/climate-ref-pmp/tests/test-data/extratropical-modes-of-variability-pdo/cmip6/manifest.json
  • packages/climate-ref-pmp/tests/test-data/extratropical-modes-of-variability-pdo/cmip7/manifest.json
  • packages/climate-ref-pmp/tests/test-data/extratropical-modes-of-variability-pna/cmip6/manifest.json
  • packages/climate-ref-pmp/tests/test-data/extratropical-modes-of-variability-pna/cmip7/manifest.json
  • packages/climate-ref-pmp/tests/test-data/extratropical-modes-of-variability-sam/cmip6/manifest.json
  • packages/climate-ref-pmp/tests/test-data/extratropical-modes-of-variability-sam/cmip7/manifest.json
  • packages/climate-ref/src/climate_ref/cli/test_cases/__init__.py
  • packages/climate-ref/src/climate_ref/cli/test_cases/_common.py
  • packages/climate-ref/src/climate_ref/cli/test_cases/baselines.py
  • packages/climate-ref/src/climate_ref/cli/test_cases/ci_gate.py
  • packages/climate-ref/src/climate_ref/cli/test_cases/migrate.py
  • packages/climate-ref/src/climate_ref/cli/test_cases/run.py
  • packages/climate-ref/tests/integration/test_native_roundtrip.py
  • packages/climate-ref/tests/unit/cli/test_test_cases.py

Comment thread packages/climate-ref/src/climate_ref/cli/test_cases/migrate.py Outdated
Comment thread packages/climate-ref/tests/integration/test_native_roundtrip.py
@codecov

codecov Bot commented Jun 30, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 93.22034% with 4 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
...mate-ref/src/climate_ref/cli/test_cases/migrate.py 91.89% 2 Missing and 1 partial ⚠️
...te-ref/src/climate_ref/cli/test_cases/baselines.py 50.00% 1 Missing ⚠️
Flag Coverage Δ
core 92.30% <93.22%> (+<0.01%) ⬆️
providers 86.29% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
...e-ref-core/src/climate_ref_core/regression/gate.py 100.00% <100.00%> (ø)
...f-core/src/climate_ref_core/regression/manifest.py 100.00% <100.00%> (ø)
...ate-ref/src/climate_ref/cli/test_cases/__init__.py 100.00% <100.00%> (ø)
...mate-ref/src/climate_ref/cli/test_cases/_common.py 84.12% <100.00%> (ø)
...mate-ref/src/climate_ref/cli/test_cases/ci_gate.py 91.12% <ø> (ø)
.../climate-ref/src/climate_ref/cli/test_cases/run.py 86.84% <100.00%> (ø)
...te-ref/src/climate_ref/cli/test_cases/baselines.py 71.61% <50.00%> (ø)
...mate-ref/src/climate_ref/cli/test_cases/migrate.py 91.89% <91.89%> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Only backfill diagnostic_version when absent so re-running after an authorised version bump cannot re-stamp a stale bundle as current and subvert the gate's staleness check. Add a regression test for the preserve path and a ci-gate test that drives the real schema-1 base-manifest seeding fallback.
Tighten the new decide_coupling comments and the regression-baselines
prose, reflow to semantic line breaks, and drop em dashes from the added
comments, docstrings, and FAIL messages. No behaviour change.
@lewisjared lewisjared merged commit ebf4aea into main Jun 30, 2026
28 checks passed
@lewisjared lewisjared deleted the feat/diagnostic-version-gate branch June 30, 2026 10:36
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