Skip to content

DEVELOPMENT.md: require backward compatible support of new dandi-archive features - #1963

Open
yarikoptic-gitmate wants to merge 2 commits into
masterfrom
claude/zealous-turing-ahafkn
Open

yarikoptic-gitmate wants to merge 2 commits into
masterfrom
claude/zealous-turing-ahafkn

Conversation

@yarikoptic-gitmate

@yarikoptic-gitmate yarikoptic-gitmate commented Oct 15, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

This adds a "Compatibility with dandi-archive instances" section to DEVELOPMENT.md, plus a one-line pointer in CLAUDE.md. The section says:

  • Support for a new dandi-archive feature must be backward compatible with prior dandi-archive versions, in particular those deployed by the DANDI and EMBER instances (production and sandbox). Then dandi-cli can be released before dandi-archive releases or deploys the feature, and tested against sandbox instances that already have it. No synchronized releases of dandi-cli and dandi-archive are needed, and no synchronized deployments across all instances.
  • How to decide whether a server supports a feature:
    1. by server_info["version"] if the releasing dandi-archive version is known (forks may report versions that cannot be compared);
    2. otherwise, and most likely, by one-time sensing, with the result remembered.
  • Rules for the conditional, following current practice:
    • a comment that refers to the dandi-archive issue/PR and says when the fallback can be removed;
    • log the fallback at DEBUG or INFO;
    • test both paths;
    • remove the fallback once all known instances support the feature.
  • Examples in the code, each linked to its lines in the 0.81.0 release.

This first step is documentation only, describing how this is done today. A follow-up PR will propose a registry of such conditionals (see "Next steps").

Ongoing: BULK_DELETE (WiP)

Historical precedents

Lockstep (2021–2023, before LINC and EMBER). Server changes were followed right away by a client release, and older clients broke:

Server-driven gates in /api/info/:

Sensing and fallbacks in the client:

Coordinated deployment instead of a fallback:

CI gap:

Existing conditionals: removable now?

Links are to the 0.81.0 release. The verdicts are based on the source of the branches the instances deploy: upstream master, aplbrain/dandi-archive prod for EMBER, and lincbrain/linc-archive release for LINC. I could not query /api/info/ on the live servers from the environment where this was prepared, so please check before acting on it.

Code Accommodates Verdict
_get_instance(), /server-info → /api/info/ (dandi/utils.py) GUI URLs (Netlify proxy) vs bare API hosts keep
allowed_schema_versions fallback in check_schema_version() (dandi/dandiapi.py) servers older than dandi-archive 0.19.0 keep: LINC lacks the field. The TODO about the field name (dandi/dandi-archive#2624) can go, since dandi/dandi-archive#2625 settled the name.
strict x-amz-tagging check, commented out (dandi/files/zarr.py) servers before 0.12.14 EMBER is past it; LINC is not, so keep
old "Zarr already exists" text in mkzarr (dandi/files/zarr.py) DRF before the upgrade in dandi-archive 1.0.0 keep: EMBER and LINC still send it, and upstream still raises it on a race
dandi-staging check in get_instance() (dandi/utils.py) the removed instance id candidate: unreachable for string ids, because known_instances[dandi_id] raises KeyError first
retry of downloads on HTTP 400 (dandi/download.py) Girder (#87) candidate: Girder support was removed in 2021, unless S3 400s merit a retry

To verify: if LINC still serves schema 0.6.9, check_schema_version() raises on dandi upload to LINC for every dandischema that dandi-cli allows. 0.6.9 is not in ALLOWED_TARGET_SCHEMAS, and it is in a different MAJOR.MINOR band than client schemas 0.7.0 and 0.8.0.

How other projects handle this

  • DataLad and git-annex: AnnexRepo._check_version_kludges() computes named, version-dependent flags once and caches them. It has a "maybe" state for development snapshots. Flags are removed in batches when the minimum version is raised (fa58b48). A cron-only CI job tests the minimum git-annex version.
  • Matrix: /versions returns spec versions plus unstable_features flags. Flags are dropped once a feature is in the spec, and an absent flag means unsupported. matrix-js-sdk's featureSupportResolver is a registry mapping each feature to its unstable flag(s) and the stable spec version, which is close to the registry proposed under "Next steps".
  • Nextcloud: the OCS capabilities endpoint lists versioned per-app capabilities. The desktop client's bulkUpload() reads dav.bulkupload and can be forced on or off with NEXTCLOUD_BULK_UPLOAD=0/1. This is the closest analogue to BULK_DELETE plus a kill switch.
  • Docker: the client negotiates the API version lazily, once per client. DOCKER_API_VERSION pins it and is meant only for when you must, or for debugging.
  • git protocol v2: servers advertise capabilities, and the agent (version) string "MUST NOT be used to programmatically assume the presence or absence of particular features". The protocol.version knob mattered in practice: v2 was made the default in 2.26, demoted in 2.27 for rough edges, and restored in 2.29.
  • OCI distribution: a client MUST fall back to the tag scheme when the referrers API returns 404. oras-go caches a tri-state support flag per repository. It tells a "repository unknown" 404 apart from "API unsupported", which is the same ambiguity Use the bulk asset deletion endpoint for all asset deletion #1960 faces.
  • Kubernetes: kubectl caches discovery per host with a 6 h TTL. It falls back to client-side validation when the server lacks fieldValidation. Both feature gates and the skew policy define when old paths can go.
  • BioBlend and Galaxy: BioBlend branches on /api/version and runs a weekly CI matrix over about 10 Galaxy releases.
  • OpenStack microversions: server-side version pinning is too heavy for us. The Nova spec also warns that a per-deployment extension list became "a poor man's versioning mechanism", so a features list should be curated and pruned.
  • Enforcing removal:
    • pip's deprecated(gone_in=...) turns into an error once pip reaches that version;
    • the deprecation package has fail_if_not_removed;
    • Django uses grep-able RemovedInDjangoXXWarning comments;
    • Chromium flags have an expiry milestone.

Next steps (follow-up PRs, not here)

  • dandi-cli registry of server features. Each entry has a CODENAME, the dandi-archive PR, base_version (the version the feature is developed on, so older servers need no sensing), optional released_version, and a sensing function. Results are resolved once per server, keyed by API URL rather than per DandiAPIClient.
  • DANDI_DEVEL_DISABLE_SERVER_FEATURES=BULK_DELETE,... to force the fallback when a feature turns out to be buggy.
  • A scheduled (e.g. weekly) test over known_instances that reports which features each instance supports. It fails when a fallback is supported everywhere and can be removed. Keep in mind that scheduled workflows are disabled after 60 days of repository inactivity.
  • dandi-archive: a features entry in /api/info/ generated from code. Keep old endpoints while clients migrate (Only support bulk asset deletion dandi-archive#2930).
  • CI against the dandi-archive versions the instances deploy, not only the latest image.

🤖 Generated with Claude Code

https://claude.ai/code/session_014sJtxzEPbSxycxetp4DquU

…ive features

dandi-cli works with several independently deployed dandi-archive
instances (DANDI and EMBER production and sandbox, LINC, ...), which get
new features at different times.  Document that support for a new
dandi-archive feature must keep working against prior dandi-archive
versions, in particular those deployed by the DANDI and EMBER
instances, so that dandi-cli can be released before dandi-archive and
tested against sandbox instances, without synchronized releases and
deployments.

Describe how to decide whether a server supports a feature (server
version, one-time sensing, or a future "features" list in /api/info/),
and ask to give each feature a CODENAME (e.g. BULK_DELETE), test both
code paths, and remove the fallback once all known instances support
the feature.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014sJtxzEPbSxycxetp4DquU
@yarikoptic-gitmate yarikoptic-gitmate added the documentation Changes only affect the documentation label Oct 15, 2026 — with Claude
@codecov

codecov Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 90.82%. Comparing base (0d5f96e) to head (2939a09).

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #1963      +/-   ##
==========================================
+ Coverage   90.76%   90.82%   +0.06%     
==========================================
  Files          92       92              
  Lines       14159    14159              
==========================================
+ Hits        12851    12860       +9     
+ Misses       1308     1299       -9     
Flag Coverage Δ
unittests 90.82% <ø> (+0.06%) ⬆️

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

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

@yarikoptic yarikoptic left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

keep it more factual to the moment and with better refs to the code for humans

Comment thread DEVELOPMENT.md Outdated
Comment thread DEVELOPMENT.md Outdated
….0 code

Per review: drop the not yet existing "features" list in /api/info/ and
the CODENAME convention, describing instead how such conditionals are
annotated now (a comment referring to the dandi-archive issue/PR and
saying when the fallback can be removed).  List the examples one per
line, each linked to its code in the 0.81.0 release.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014sJtxzEPbSxycxetp4DquU
Comment thread DEVELOPMENT.md
fail the first time. Simply re-run `git commit` — the second attempt should
succeed. Investigate further only if it still fails.

## Compatibility with dandi-archive instances

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@waxlamp @jjnesbitt @naglepuff and @dandi/dandi-archive at large: I am initiating these guidelines so far within dandi-cli but hope we could distill a solution which we would implement between both dandi-archive and dandi-cli in a more robust fashion. See this PR description (alert: claude-composed but quite informative! yet to be fully curated but looked sensible, might be worth migrating parts into doc for the next stage if we decide to proceed on "centralized" solution) for more history/pointers etc than this brief addition to DEVELOPMENT.md provides. Also please review this text snippet on either anything to change to reflect our agreement from the recent Zoom session on Tue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Changes only affect the documentation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants