Skip to content

Enhance dataset version history and Zarr resolution alignment - #17

Merged
TheGreatAlgo merged 10 commits into
mainfrom
dns-migration
Aug 7, 2026
Merged

Enhance dataset version history and Zarr resolution alignment#17
TheGreatAlgo merged 10 commits into
mainfrom
dns-migration

Conversation

@eloramirez1356

@eloramirez1356 eloramirez1356 commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator

This pull request introduces a robust, unified interface for dataset version history and provenance, integrating support for STAC-advertised version services and expanding the metadata returned when resolving datasets. The main changes include a new ceramic_api.py module for version/provenance access, substantial enhancements to dataset resolution to return richer metadata, and new async methods in the client for version history. Existing resolution code is refactored to support these richer results, and documentation is updated to demonstrate the new features.

Dataset version/provenance support:

  • Added a new dclimate_client_py/ceramic_api.py module that provides a Python interface for listing dataset versions, resolving exact releases, fetching citations, and building gateway URLs. This supports both Hydrogen and Tritium Ceramic APIs and allows STAC-aware applications to discover and use the correct version service automatically.
  • Extended the DatasetMetadata type and dataset resolution results to include fields such as versions_api, provenance_api, citation_api, stream_id, commit_id, version_label, is_citable, and retention_class for richer metadata. [1] [2]

Client and API changes:

  • Added an async method list_dataset_versions() to dClimateClient, which uses the STAC-advertised version service to list dataset releases, selecting the appropriate backend automatically.
  • Refactored dataset resolution in both STAC server and catalog code to return a new ResolvedDatasetDetails object with extended metadata, and updated all call sites accordingly. [1] [2] [3] [4] [5] [6] [7] [8]

Documentation:

  • Updated the README.md with a new section and example for using the version history interface, highlighting how STAC-aware clients can now discover and use the correct version service without hardcoding URLs.

Minor code cleanup:

  • Removed the unused hydrogen_endpoint variable from datasets.py.
  • Ensured the correct zarr group is used when loading datasets by defaulting to the resolved metadata if not specified.

These changes make it much easier for downstream users and applications to access dataset version history, provenance, and citation data in a backend-agnostic and future-proof way.

Summary by CodeRabbit

  • New Features

    • Added dataset version history support, including listing versions and retrieving an exact version by commit ID.
    • Added citation and provenance details to dataset metadata.
    • Added multiresolution dataset loading with explicit resolution or Zarr-group selection.
    • Improved STAC discovery of version services and available dataset resolutions.
  • Bug Fixes

    • Added clearer errors for missing, unavailable, or conflicting resolution selections.
    • Preserved compatibility with legacy dataset loading paths.
  • Documentation

    • Expanded the README with version-history and multiresolution loading guidance.

@eloramirez1356 eloramirez1356 self-assigned this Aug 5, 2026
@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The client now resolves detailed dataset metadata from STAC, supports explicit multiresolution loading, and exposes Ceramic version and citation APIs. Legacy CID-only resolver APIs remain available.

Changes

Dataset versioning and multiresolution loading

Layer / File(s) Summary
Ceramic version and citation API
dclimate_client_py/ceramic_api.py, tests/test_ceramic_api.py
Adds typed version, citation, and verification models; URL and dataset-based retrieval functions; anchored-version selection; and gateway URL construction.
STAC release metadata resolution
dclimate_client_py/stac_server.py, dclimate_client_py/stac_catalog.py, tests/test_stac_version_discovery.py, tests/test_stac_server_async.py
STAC resolvers return release metadata and Zarr resolution choices. Compatibility wrappers still return ResolvedDataset.
Explicit multiresolution loading
dclimate_client_py/dclimate_client.py, dclimate_client_py/ipfs_retrieval.py, dclimate_client_py/dclimate_zarr_errors.py, dclimate_client_py/__init__.py, tests/test_ipfs_retrieval.py
Dataset loading validates resolution and group selections. Ambiguous Zarr stores raise MultiresolutionSelectionRequiredError with available groups.
Client version-listing integration
dclimate_client_py/dclimate_client.py, dclimate_client_py/datasets.py, README.md, tests/test_stac_version_discovery.py, tests/test_review_fu_httpx.py
The client adds filtered list_dataset_versions() and exact get_dataset_version() methods using STAC-discovered service URLs. Metadata and documentation describe version history and multiresolution selection.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant dClimateClient
  participant STACResolver
  participant CeramicAPI
  participant ZarrStore
  dClimateClient->>STACResolver: resolve dataset details
  STACResolver-->>dClimateClient: return CID, versions URL, and Zarr resolutions
  dClimateClient->>ZarrStore: select and open resolution
  dClimateClient->>CeramicAPI: list versions or retrieve commit
  CeramicAPI-->>dClimateClient: return typed version data
Loading

Possibly related PRs

Suggested reviewers: 0xswego, thegreatalgo

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 32.31% 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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main changes: dataset version history and Zarr resolution support.
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 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch dns-migration

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

@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: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
dclimate_client_py/dclimate_client.py (1)

378-440: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Populate the new DatasetMetadata fields.

resolved contains the STAC release metadata, but the metadata dictionary returned by load_dataset does not copy it. Callers therefore cannot access versions_api, provenance, citation, stream, commit, label, citability, or retention data after loading a dataset.

Add these fields to the STAC metadata dictionary and add a regression test.

Proposed fix
         metadata = {
             ...
+            "versions_api": resolved.versions_api,
+            "provenance_api": resolved.provenance_api,
+            "citation_api": resolved.citation_api,
+            "stream_id": resolved.stream_id,
+            "commit_id": resolved.commit_id,
+            "version_label": resolved.version_label,
+            "is_citable": resolved.is_citable,
+            "retention_class": resolved.retention_class,
         }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@dclimate_client_py/dclimate_client.py` around lines 378 - 440, Update the
metadata construction in the load_dataset flow after resolving the dataset,
using the fields on ResolvedDatasetDetails to populate versions_api, provenance,
citation, stream, commit, label, citability, and retention in the returned STAC
metadata dictionary. Preserve existing metadata and add a regression test
verifying these fields remain accessible after loading a dataset.
dclimate_client_py/stac_server.py (1)

262-283: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Correct the resolver return-type migration notes.

Both functions now return ResolvedDatasetDetails, but their migration notes still state ResolvedDataset. Update the notes to match the public API.

  • dclimate_client_py/stac_server.py#L262-L283: replace ResolvedDataset in the migration note with ResolvedDatasetDetails.
  • dclimate_client_py/stac_catalog.py#L294-L322: replace ResolvedDataset in the migration note with ResolvedDatasetDetails.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@dclimate_client_py/stac_server.py` around lines 262 - 283, Update the
migration notes for resolve_dataset_from_stac_server in
dclimate_client_py/stac_server.py (lines 262-283) and its corresponding resolver
in dclimate_client_py/stac_catalog.py (lines 294-322) to refer to
ResolvedDatasetDetails instead of ResolvedDataset; no implementation changes are
needed.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@dclimate_client_py/ceramic_api.py`:
- Around line 26-79: Run Ruff formatting on the ceramic_api.py file and commit
the resulting formatting-only changes, preserving the existing VerificationInfo
and DatasetVersion behavior.

---

Outside diff comments:
In `@dclimate_client_py/dclimate_client.py`:
- Around line 378-440: Update the metadata construction in the load_dataset flow
after resolving the dataset, using the fields on ResolvedDatasetDetails to
populate versions_api, provenance, citation, stream, commit, label, citability,
and retention in the returned STAC metadata dictionary. Preserve existing
metadata and add a regression test verifying these fields remain accessible
after loading a dataset.

In `@dclimate_client_py/stac_server.py`:
- Around line 262-283: Update the migration notes for
resolve_dataset_from_stac_server in dclimate_client_py/stac_server.py (lines
262-283) and its corresponding resolver in dclimate_client_py/stac_catalog.py
(lines 294-322) to refer to ResolvedDatasetDetails instead of ResolvedDataset;
no implementation changes are needed.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 49d85d0b-85d8-42f8-a7ac-831bf0e3ef67

📥 Commits

Reviewing files that changed from the base of the PR and between 26f4997 and 3f12e32.

📒 Files selected for processing (9)
  • README.md
  • dclimate_client_py/ceramic_api.py
  • dclimate_client_py/datasets.py
  • dclimate_client_py/dclimate_client.py
  • dclimate_client_py/stac_catalog.py
  • dclimate_client_py/stac_server.py
  • tests/test_ceramic_api.py
  • tests/test_review_fu_httpx.py
  • tests/test_stac_version_discovery.py

Comment thread dclimate_client_py/ceramic_api.py
@codecov-commenter

codecov-commenter commented Aug 5, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 90.37801% with 28 lines in your changes missing coverage. Please review.
✅ Project coverage is 77.59%. Comparing base (b08e049) to head (4bfbf86).
⚠️ Report is 11 commits behind head on main.

Files with missing lines Patch % Lines
dclimate_client_py/dclimate_client.py 78.82% 8 Missing and 10 partials ⚠️
dclimate_client_py/ceramic_api.py 95.08% 4 Missing and 2 partials ⚠️
dclimate_client_py/stac_catalog.py 83.33% 1 Missing and 1 partial ⚠️
dclimate_client_py/ipfs_retrieval.py 88.88% 1 Missing ⚠️
dclimate_client_py/stac_server.py 97.87% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main      #17      +/-   ##
==========================================
+ Coverage   75.56%   77.59%   +2.03%     
==========================================
  Files          16       17       +1     
  Lines        2050     2312     +262     
  Branches      374      407      +33     
==========================================
+ Hits         1549     1794     +245     
- Misses        350      358       +8     
- Partials      151      160       +9     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@da-code-reviewer da-code-reviewer 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.

Codex Automated Review

Found 3 actionable correctness issues.
Posted 3 inline comment(s).

Comment thread dclimate_client_py/dclimate_client.py Outdated
ipfs_cid=resolved.cid,
kubo_cas=self._kubo_cas,
zarr_group=zarr_group,
zarr_group=zarr_group or resolved.zarr_group,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

MEDIUM
ResolvedDatasetDetails now carries the release-service fields, but the metadata dictionary built immediately below never copies them. Consequently load_dataset() omits versions_api, provenance/citation URLs, commit metadata, and retention data despite adding them to DatasetMetadata. Populate the STAC metadata dictionary from resolved.

return await asyncio.to_thread(
resolve_dataset_from_stac,
catalog=self._stac_catalog,
collection=resolved_collection,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

LOW
This fallback passes the collection directly to the catalog resolver, omitting the unique suffix-alias normalization used by load_dataset(). Thus list_dataset_versions(collection="era5", ...) fails when only ecmwf_era5 exists even though loading the same collection succeeds. Apply the same list_available_datasets()/unique-suffix resolution here.

Comment thread dclimate_client_py/ceramic_api.py Outdated
anchored_versions = filter_anchored_versions(listing.versions)
if not anchored_versions:
raise ValueError(f"No anchored versions found for dataset '{dataset}'")
anchored_versions.sort(

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

MEDIUM
The versions endpoint already returns releases in Ceramic event order, but this re-sorts them using snapshot-controlled timestamps and arbitrary string tie-breakers. Missing, equal, or skewed timestamps can therefore make an older commit appear latest. Preserve the service ordering and select the last anchored entry instead.

@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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (5)
dclimate_client_py/dclimate_client.py (5)

609-652: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Reuse unqualified-collection resolution on the catalog fallback.

When organization is omitted, _resolve_dataset_details passes collection directly to resolve_dataset_from_stac. The existing load_dataset path searches for a unique <organization>_<collection> catalog ID at Lines [418-429]. If the STAC server is unavailable and the catalog contains noaa_aigfs while the caller passes aigfs, the version APIs can fail even though dataset loading succeeds. Share the collection-normalization logic between both paths. Add a fallback test for an unqualified collection.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@dclimate_client_py/dclimate_client.py` around lines 609 - 652, Share the
collection-normalization logic used by load_dataset with
_resolve_dataset_details so the catalog fallback resolves an unqualified
collection to its unique organization-prefixed catalog ID when organization is
omitted. Preserve explicitly qualified collections and organization-provided
behavior. Add a fallback test covering an unqualified collection such as aigfs
resolving to noaa_aigfs when the STAC server is unavailable.

445-445: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Preserve an explicitly supplied zarr_group.

When zarr_group="", this expression selects resolved.zarr_group. The loader normalizes "" to None, which represents the root group. Use an is None check.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@dclimate_client_py/dclimate_client.py` at line 445, Update the zarr_group
selection to distinguish an explicitly supplied empty string from None: retain
the caller’s zarr_group whenever it is not None, including "", and fall back to
resolved.zarr_group only when it is None.

677-683: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Server-side Request Forgery (CWE-918): Server-Side Request Forgery (SSRF)

Exploitability: Moderate

Validate versions_api before making the request. STAC properties supply this URL without validation, and _request_json follows redirects. Restrict the scheme and destination host, and revalidate redirect targets for both version endpoints at dclimate_client_py/dclimate_client.py:677-683 and 705-709.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@dclimate_client_py/dclimate_client.py` around lines 677 - 683, Update the
version-fetching flows around list_versions_from_url and the corresponding
endpoint near the second version request to validate details.versions_api before
requesting it and revalidate every redirect target in _request_json. Allow only
the approved URL scheme and destination host, rejecting unsafe initial or
redirected targets for both version endpoints.

677-683: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Security Misconfiguration (CWE-319): Cleartext Transmission of Sensitive Information

Reachability: External · Exploitability: Moderate

Require HTTPS for every version-request hop.

Validate details.versions_api before both version requests. Reject non-HTTPS URLs and revalidate redirect targets, or disable automatic redirects. _request_json currently performs no scheme validation and enables redirects, allowing HTTP requests or HTTPS-to-HTTP downgrades.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@dclimate_client_py/dclimate_client.py` around lines 677 - 683, Update the
version-request flow around list_versions_from_url and _request_json to enforce
HTTPS for the initial details.versions_api URL and every redirect target. Either
validate each redirect before following it or disable automatic redirects and
handle them with scheme validation; reject any HTTP or other non-HTTPS URL
before making the request.

383-389: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Propagate STAC release metadata through load_dataset. DatasetMetadata declares versions_api, provenance_api, citation_api, stream_id, commit_id, version_label, is_citable, and retention_class, but the STAC branch omits them. Copy these fields from resolved and add a regression test for a STAC-resolved release.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@dclimate_client_py/dclimate_client.py` around lines 383 - 389, Update the
STAC resolution branch in load_dataset to populate DatasetMetadata with
versions_api, provenance_api, citation_api, stream_id, commit_id, version_label,
is_citable, and retention_class from resolved, matching the non-STAC path. Add a
regression test covering a STAC-resolved release and asserting these metadata
fields are preserved.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@dclimate_client_py/dclimate_client.py`:
- Around line 609-652: Share the collection-normalization logic used by
load_dataset with _resolve_dataset_details so the catalog fallback resolves an
unqualified collection to its unique organization-prefixed catalog ID when
organization is omitted. Preserve explicitly qualified collections and
organization-provided behavior. Add a fallback test covering an unqualified
collection such as aigfs resolving to noaa_aigfs when the STAC server is
unavailable.
- Line 445: Update the zarr_group selection to distinguish an explicitly
supplied empty string from None: retain the caller’s zarr_group whenever it is
not None, including "", and fall back to resolved.zarr_group only when it is
None.
- Around line 677-683: Update the version-fetching flows around
list_versions_from_url and the corresponding endpoint near the second version
request to validate details.versions_api before requesting it and revalidate
every redirect target in _request_json. Allow only the approved URL scheme and
destination host, rejecting unsafe initial or redirected targets for both
version endpoints.
- Around line 677-683: Update the version-request flow around
list_versions_from_url and _request_json to enforce HTTPS for the initial
details.versions_api URL and every redirect target. Either validate each
redirect before following it or disable automatic redirects and handle them with
scheme validation; reject any HTTP or other non-HTTPS URL before making the
request.
- Around line 383-389: Update the STAC resolution branch in load_dataset to
populate DatasetMetadata with versions_api, provenance_api, citation_api,
stream_id, commit_id, version_label, is_citable, and retention_class from
resolved, matching the non-STAC path. Add a regression test covering a
STAC-resolved release and asserting these metadata fields are preserved.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ea86d1f9-444c-4e72-8e6e-cae1b4ba9dc4

📥 Commits

Reviewing files that changed from the base of the PR and between 3f12e32 and dead504.

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (4)
  • README.md
  • dclimate_client_py/dclimate_client.py
  • pyproject.toml
  • tests/test_stac_version_discovery.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • tests/test_stac_version_discovery.py
  • README.md

@da-code-reviewer da-code-reviewer 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.

Codex Automated Review

Found two actionable correctness/reliability issues.
Posted 2 inline comment(s).

] # How the dataset was loaded
organization: Optional[str]
zarr_group: Optional[str]
versions_api: Optional[str]

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

MEDIUM
load_dataset() never copies the newly resolved version/provenance fields into its returned metadata dictionary. Even when STAC advertises these values, callers receive none of the fields declared here. Populate the STAC metadata from resolved, preserving values such as is_citable=False.

return await asyncio.to_thread(
resolve_dataset_from_stac,
catalog=self._stac_catalog,
collection=resolved_collection,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

MEDIUM
This fallback omits load_dataset()'s unique suffix resolution for unprefixed collections (for example, era5ecmwf_era5). Thus both version methods can fail during STAC-server fallback even though loading the same dataset succeeds. Apply the same collection normalization before calling the catalog resolver.

@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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
dclimate_client_py/dclimate_client.py (1)

459-464: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Preserve an explicit root Zarr group.

Line 462 treats "" as absent and replaces it with resolved.zarr_group. An empty string can explicitly select the Zarr root group. Check for None instead.

Proposed fix
-            zarr_group=zarr_group or resolved.zarr_group,
+            zarr_group=zarr_group if zarr_group is not None else resolved.zarr_group,
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@dclimate_client_py/dclimate_client.py` around lines 459 - 464, Update the
zarr_group argument in the _load_dataset_from_ipfs_cid call to fall back to
resolved.zarr_group only when zarr_group is None, preserving an explicit empty
string as the root Zarr group.
tests/test_stac_server_async.py (1)

35-35: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Do not catch BaseException during fixture cleanup.

BaseException catches KeyboardInterrupt, SystemExit, and task cancellation. Catch Exception so these control-flow exceptions propagate immediately. This also resolves Ruff BLE001.

Proposed fix
-        except BaseException as error:
+        except Exception as error:
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/test_stac_server_async.py` at line 35, Update the fixture cleanup
handler around the existing `except BaseException as error` clause to catch
`Exception` instead, preserving the current cleanup behavior while allowing
KeyboardInterrupt, SystemExit, and cancellation to propagate.

Source: Linters/SAST tools

dclimate_client_py/stac_server.py (1)

516-517: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Correct the migration note.

Line 516 states that this function returns ResolvedDataset. Its signature and return value use ResolvedDatasetDetails. Update the note so callers see the correct public contract.

Proposed fix
-    Changed in 0.6: returns ResolvedDataset; variant='' is treated as an
+    Changed in 0.6: returns ResolvedDatasetDetails; variant='' is treated as an
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@dclimate_client_py/stac_server.py` around lines 516 - 517, Correct the
migration note for the affected function by replacing the incorrect
ResolvedDataset type with ResolvedDatasetDetails, matching the function
signature and returned value. Preserve the existing variant behavior
description.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@dclimate_client_py/dclimate_client.py`:
- Around line 459-464: Update the zarr_group argument in the
_load_dataset_from_ipfs_cid call to fall back to resolved.zarr_group only when
zarr_group is None, preserving an explicit empty string as the root Zarr group.

In `@dclimate_client_py/stac_server.py`:
- Around line 516-517: Correct the migration note for the affected function by
replacing the incorrect ResolvedDataset type with ResolvedDatasetDetails,
matching the function signature and returned value. Preserve the existing
variant behavior description.

In `@tests/test_stac_server_async.py`:
- Line 35: Update the fixture cleanup handler around the existing `except
BaseException as error` clause to catch `Exception` instead, preserving the
current cleanup behavior while allowing KeyboardInterrupt, SystemExit, and
cancellation to propagate.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: aa501a6a-8622-480d-8c20-525ebb9865ab

📥 Commits

Reviewing files that changed from the base of the PR and between 4c62473 and 6df81af.

📒 Files selected for processing (5)
  • README.md
  • dclimate_client_py/dclimate_client.py
  • dclimate_client_py/stac_server.py
  • tests/test_stac_server_async.py
  • tests/test_stac_version_discovery.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • tests/test_stac_version_discovery.py
  • README.md

@da-code-reviewer da-code-reviewer 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.

Codex Automated Review

Found 3 actionable issues.
Posted 3 inline comment(s).

return await asyncio.to_thread(
resolve_dataset_from_stac,
catalog=self._stac_catalog,
collection=resolved_collection,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

MEDIUM
Preserve shorthand collection resolution on fallback. Unlike load_dataset, this helper never maps an unprefixed collection such as era5 to a unique catalog key such as ecmwf_era5. When the STAC API is unavailable, both new version methods therefore fail for collection names that normal dataset loading accepts. Apply the same suffix normalization before resolving the catalog.

Comment thread dclimate_client_py/ceramic_api.py Outdated
anchored_versions = filter_anchored_versions(listing.versions)
if not anchored_versions:
raise ValueError(f"No anchored versions found for dataset '{dataset}'")
anchored_versions.sort(

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

MEDIUM
Keep the service's authoritative version order. The versions endpoint returns commits in Ceramic event order, but this sort replaces that order with snapshot timestamps and an arbitrary commit-ID tie-breaker. Equal, corrected, or backfilled timestamps can make get_latest_anchored_version() return an older commit. Preserve the listing order and select the last anchored entry.

dataset=dataset,
variant=variant,
server_url=self._stac_server_url,
client=self._get_stac_http_client(),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

LOW
Close the STAC client for context-free version calls. The new methods do not require entering the async context—and the added README example constructs a bare client—but this creates a pooled AsyncClient whose only close path is __aexit__. Such calls leave the connection pool and sockets unclosed. Require context-managed use, provide an explicit aclose(), or use a temporary client here.

@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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
dclimate_client_py/stac_catalog.py (1)

295-301: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Correct the stale return-type migration note.

Line 305 states that this function returns ResolvedDataset. The function now returns ResolvedDatasetDetails. This gives API consumers incorrect migration guidance.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@dclimate_client_py/stac_catalog.py` around lines 295 - 301, Update the
migration note associated with resolve_dataset_from_stac to state that the
function returns ResolvedDatasetDetails instead of ResolvedDataset, keeping the
guidance consistent with the function’s declared return type.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@dclimate_client_py/stac_catalog.py`:
- Around line 295-301: Update the migration note associated with
resolve_dataset_from_stac to state that the function returns
ResolvedDatasetDetails instead of ResolvedDataset, keeping the guidance
consistent with the function’s declared return type.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: bf291694-7e88-4132-92e5-d51148f872c1

📥 Commits

Reviewing files that changed from the base of the PR and between 6df81af and 813ea6b.

📒 Files selected for processing (11)
  • README.md
  • dclimate_client_py/__init__.py
  • dclimate_client_py/datasets.py
  • dclimate_client_py/dclimate_client.py
  • dclimate_client_py/dclimate_zarr_errors.py
  • dclimate_client_py/ipfs_retrieval.py
  • dclimate_client_py/stac_catalog.py
  • dclimate_client_py/stac_server.py
  • tests/test_ipfs_retrieval.py
  • tests/test_stac_server_async.py
  • tests/test_stac_version_discovery.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • dclimate_client_py/datasets.py

@da-code-reviewer da-code-reviewer 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.

Codex Automated Review

Found two actionable reliability/correctness issues.
Posted 1 inline comment(s).

Findings that could not be placed inline:

  • dclimate_client_py/dclimate_client.py:563 [medium] Preserve the resolved version metadata. ResolvedDatasetDetails now carries the advertised version, provenance, citation, commit, and retention fields, but load_dataset() only copies resolution into its returned metadata. Consequently every STAC load discards the newly added DatasetMetadata fields. Populate them from resolved when constructing metadata.

try:
return await aresolve_dataset_from_stac_server(
collection=resolved_collection,
dataset=dataset,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

MEDIUM
Provide cleanup for version-only clients. The new version methods can be called without entering the async context (as shown in the README), but this line creates and retains an httpx.AsyncClient that is only closed by __aexit__. Repeated short-lived clients therefore leak connection pools until garbage collection/process exit. Require context-manager use, expose an aclose() path, or use a temporary client for these calls.

@da-code-reviewer da-code-reviewer 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.

Codex Automated Review

Found 3 actionable correctness regressions.
Posted 3 inline comment(s).

Comment thread dclimate_client_py/dclimate_client.py Outdated
organization: typing.Optional[str] = None,
cid: typing.Optional[str] = None,
return_xarray: bool = False,
resolution: typing.Optional[str] = None,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

MEDIUM
Preserve existing positional arguments. Inserting resolution before the existing zarr_group and shard_read_mode parameters reinterprets previously valid positional calls; for example, a positional "0" group is now treated as a human-readable resolution and rejected. Append the new parameter after existing parameters or provide compatibility handling.

return await asyncio.to_thread(
resolve_dataset_from_stac,
catalog=self._stac_catalog,
collection=resolved_collection,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

MEDIUM
Normalize shorthand collections on catalog fallback. Unlike load_dataset, this path passes an unprefixed collection such as era5 directly to the new-layout catalog resolver, which requires ecmwf_era5 unless organization is supplied. Both version methods therefore fail during STAC-server outages for shorthand names that normal loading accepts. Reuse the unique-suffix normalization before resolving.

Comment thread dclimate_client_py/ceramic_api.py Outdated
anchored_versions = filter_anchored_versions(listing.versions)
if not anchored_versions:
raise ValueError(f"No anchored versions found for dataset '{dataset}'")
anchored_versions.sort(

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

MEDIUM
Preserve Ceramic event order when selecting the latest version. The service returns versions in authoritative event-state order, while dataset timestamps may be corrected or backfilled and need not be monotonic. Re-sorting by timestamp and commit ID can therefore return an older commit as the latest anchored release; filter the response in place and select its final anchored entry.

@da-code-reviewer da-code-reviewer 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.

Codex Automated Review

Found three actionable correctness regressions.
Posted 3 inline comment(s).

Comment thread dclimate_client_py/dclimate_client.py Outdated
organization: typing.Optional[str] = None,
cid: typing.Optional[str] = None,
return_xarray: bool = False,
resolution: typing.Optional[str] = None,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

MEDIUM
Preserve existing positional arguments. Inserting resolution before the existing zarr_group and shard_read_mode parameters reinterprets valid positional calls; a positional "0" group is now treated as a resolution and rejected. Make resolution keyword-only after the existing parameters or add compatibility handling.

return await asyncio.to_thread(
resolve_dataset_from_stac,
catalog=self._stac_catalog,
collection=resolved_collection,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

MEDIUM
Normalize shorthand collections on catalog fallback. Unlike load_dataset(), this path passes an unprefixed collection such as era5 directly to the new-layout catalog resolver. If the STAC lookup fails, both version methods reject shorthand names that normal loading resolves to ecmwf_era5. Reuse the unique-suffix normalization before resolving.

Comment thread dclimate_client_py/ceramic_api.py Outdated
anchored_versions = filter_anchored_versions(listing.versions)
if not anchored_versions:
raise ValueError(f"No anchored versions found for dataset '{dataset}'")
anchored_versions.sort(

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

MEDIUM
Preserve Ceramic event order when selecting the latest version. The versions endpoint returns history ordered by Ceramic event state, while timestamp is snapshot content and need not be monotonic. Re-sorting can therefore select an older commit as the latest anchored release. Filter in place and return the final anchored entry.

@da-code-reviewer da-code-reviewer 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.

Codex Automated Review

No high-confidence actionable issues found.
No inline issues were posted.

@TheGreatAlgo
TheGreatAlgo merged commit dbd8d03 into main Aug 7, 2026
4 checks passed

@da-code-reviewer da-code-reviewer 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.

Codex Automated Review

No high-confidence actionable issues found.
No inline issues were posted.

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.

3 participants