Changelog

0.2.8 (2026-05-07)

Features

  • Add xnatctl resource refresh URI [--options ...] for targeted catalog refresh against /data/services/refresh/catalog. Options accept any of checksum, delete, append, populateStats. Replaces the need to drop to api post for single scan/resource catalog refreshes.

  • Add --project/-P to xnatctl resource upload so label-based session resolution works for scan-level and session-level resource uploads. Default falls back to the active profile’s default_project.

  • Allow xnatctl config show -p NAME to filter the output to a single profile (and its details). Unknown profile names error with the available list. Unfiltered behavior is unchanged.

  • Add HierarchyService for shared path building, response-envelope parsing (ResultSet.Result and items[]), and resolution of ProjectRef / SubjectRef / ExperimentRef / ScanRef / ResourceRef. Services and CLI commands now route through it instead of open-coding paths and parsing.

  • Bundle pydicom and pynetdicom in the standalone PyInstaller binary so xnatctl dicom utilities work out of the box after install.sh.

Bug fixes

  • Fix session show and scan list returning “Session not found” or “No results” for sessions reachable via subject-scoped paths. HierarchyService.resolve_experiment now falls back to listing /data/projects/{P}/experiments?columns=ID,label,subject_ID,xsiType with client-side label/ID match when the direct project-experiment endpoint fails, and tries /data/experiments/{ID} for accession-ID shaped references that don’t resolve in the active project.

  • Narrow _inspect_experiment exception swallowing in cli/scan.py to ResourceNotFoundError only; transient network and auth errors now surface to the user instead of silently producing “No results”.

  • Fix xnatctl api put -f FILE and api post -f FILE corrupting binary files with UnicodeDecodeError. The body is now read in binary, with a UTF-8 + JSON probe before falling back to raw bytes. JSON and plain-text paths are unchanged on the wire.

  • Fix session upload --mode gradual --prearchive silently uploading to direct-archive: the CLI wrapper now threads direct_archive through to the gradual-DICOM service. Consolidate prearchive routing through a single archive_destination_params() helper that uses dest=/prearchive/projects/{p} instead of Direct-Archive=false. Help text now warns that --prearchive is best-effort against projects with auto-archive enabled.

  • Fix subject merge silently destroying experiments. Replace the global PUT /data/experiments/{id}?xnat:experimentData/subject_ID=... shortcut with the scoped PUT /data/projects/{p}/subjects/{target_id}/experiments/{id} that the XNAT web UI uses, plus per-experiment post-PUT verification before the source-subject delete. SubjectService.delete() now refuses to delete subjects with experiments still attached unless force=True.

  • De-flake tests/test_archive_poller_zero_vs_error on slower CI runners by waiting on the actual observable (zero_scan_cycles >= 1) rather than the racy proxy.

  • Various resource-listing and transfer edge-case fixes carried into the HierarchyService refactor.

Documentation

  • Document that xnatctl resource upload PUTs files directly to the resource catalog and bypasses XNAT project-level DICOM anonymization scripts and pipelines. Use xnatctl session upload / xnatctl session upload-exam when anonymization is required.

Refactors

  • Extract cli/common.py context helpers (get_profile, default_project_from_context, require_project_from_context, resolve_workers_from_context) and adopt them across CLI commands.

  • Migrate services and CLI from open-coded ResultSet.Result / items[] parsing to HierarchyService.extract_rows / extract_first_item. Session.get() now merges meta["xsi:type"] into the model when the field-level value is missing (fixes non-imaging session xsiType). Behavior note: a session label that cannot be resolved in a project now raises ResourceNotFoundError (was ValueError).

0.2.7 (2026-04-20)

Features

  • Add xnatctl dicom modify for batch in-place DICOM tag editing across single files or directories, with repeatable --tag KEYWORD=VALUE, recursive search, --backup, --dry-run, atomic writes, and JSON/table output modes.

Bug fixes

  • Switch prearchive archiving to XNAT’s archive service API (/data/services/archive) for both prearchive archive and transfer-side prearchive resolution.

  • Surface archive-service failures during transfer instead of letting them degrade into silent archive wait timeouts; blocking fallback paths now record per-experiment archive errors consistently.

  • Encode archive-service path segments for project, timestamp, session, subject, and experiment values to avoid malformed archive and prearchive-delete requests.

0.2.6 (2026-03-24)

CLI simplification

  • Consolidate --unzip/--cleanup into --extract/--no-extract on session and scan download commands. Hidden backward-compat aliases preserved.

  • Consolidate --gradual/--archive-format into --mode {tar|zip|gradual} on session upload. Hidden backward-compat aliases preserved.

  • Collapse --wait-for-archive/--wait-timeout/--wait-interval into single --wait SECONDS flag on session upload-exam (0 = skip, default: 900).

  • Eliminate --parallel/--no-parallel toggle; unify to --workers across all commands that use @parallel_options (scan delete, admin refresh-catalogs, project transfer).

  • Reserve -P for --project everywhere (removed from api --params and pipeline --param).

  • Reserve -w for --workers everywhere (removed from pipeline --wait and pipeline --watch).

  • Normalize -E to --experiment on session upload and upload-exam (hidden --session alias preserved for backward compatibility).

  • Add profile operational defaults: workers, overwrite, direct_archive, archive_mode, extract. CLI flags override profile values.

  • Hide advanced flags from --help (still accepted): --username, --password, --zip-to-tar, --ignore-unparsable, --misc-label, --calling-aet, --name, --session-resources, --dest-url, --dest-user, --dest-pass.

  • Standardize destructive UX: apply @confirm_destructive (adds --dry-run) to prearchive delete, pipeline cancel, config remove-profile.

Bug fixes

  • Upload: validate DICOM magic bytes for extensionless files (fixes non-DICOM files like ps being uploaded via gradual-DICOM).

  • Upload: add --direct-archive flag to upload-exam (was previously missing; gradual-DICOM uploads now pass Direct-Archive query parameter).

0.2.5 (2026)

Bug fixes

  • Auto-refresh XNAT session token on HTTP 401 during large gradual-DICOM uploads, preventing mass failures when the 15-minute session TTL expires

  • Add manual PyPI publish workflow (workflow_dispatch) as fallback

  • Fix CI alls-green check treating skipped release jobs as failures

0.2.4 (2026)

Bug fixes

  • Fix auth login and top-level whoami so they resolve the authenticated user from dedicated current-user endpoints instead of treating /data/user as a whoami endpoint

0.2.3 (2026)

Bug fixes

  • Ignore non-DICOM sidecar files such as .txt and .pdf during gradual DICOM uploads from directories, ZIP archives, and explicit file lists

  • Refactor transfer scan sync to use a two-phase download-then-upload flow

  • Strip the session label attribute from transfer XML overlay uploads to prevent destination-side HTTP 400 errors

  • Add transfer debug logging around DICOM import and XML overlay failures

0.2.2 (2026)

Bug fixes

  • Reconcile experiments deleted from destination during incremental transfer

  • Save experiment ID mappings for future reconciliation

  • Preserve special characters (colons, brackets) in api get/put/post/delete query parameter keys

  • Resolve xsiType for non-imaging sessions in session show scan listing

0.2.1 (2026)

Features

  • Pipelined transfer: overlap DICOM uploads with server-side archiving via background poller thread

  • max_pending_archives config field to throttle concurrent server-side import jobs

Bug fixes

  • Reconcile previously-synced subjects deleted from destination

  • Use folderName (not name) for prearchive archive requests in wait_for_archive

  • Add exception guard around wait_for_archive poll loop for transient HTTP error resilience

  • Flatten ZIP hierarchy for non-DICOM resource uploads

  • Ensure experiment is created when all DICOM uploads fail but DICOM was expected

  • Resolve xsiType correctly for non-imaging sessions in scan list

0.2.0 (2026)

Features

  • project transfer command for cross-instance project synchronisation

  • Transfer orchestrator with per-scan pipeline, retry, and verification

  • Transfer executor with DICOM-zip import and non-DICOM resource repack

  • Discovery service for subjects, experiments, and scans

  • Filter engine for xsiType, scan type, and resource label filtering

  • XML metadata overlay to preserve session/scan metadata after DICOM import

  • Prearchive resolution (READY/CONFLICT) during archive wait

  • Scan resource caching across DICOM and non-DICOM transfer phases

  • Deferred experiment creation (skip pre-create when DICOM import will create)

  • Dest-profile CLI helper for dual-instance configuration

Bug fixes

  • Handle XNAT timestamps with fractional seconds and missing last_modified

  • Reject multiple --resource values in scan download

Docs

  • Add project transfer command documentation

  • Update session downloading guide for multi-resource support

0.1.3 (2026)

  • Fix server version endpoint (use /xapi/siteConfig/buildInfo/version)

  • Build Linux binary on manylinux_2_28 for RHEL 8+/AlmaLinux 9 compatibility

0.1.2 (2026)

Bug fixes

  • Handle 409 Conflict when creating a resource that already exists (session upload-exam --attach-only)

  • Tolerate missing resource IDs and non-numeric counts in resource responses

  • Skip experiment lookup when session has no resources

  • Scope gradual upload clients and reject duplicate files

  • Validate exam root directory and sort scan classification

  • Use files input for codecov-action v5

Features

  • session upload-exam command for uploading scanner exam-root directories (DICOM + top-level resources)

  • Wait for archived session before attaching resources in upload-exam

  • Gradual DICOM upload from explicit file lists

  • Exam-root classification for mapping directory structure to XNAT resources

  • CI: harden security (SHA-pinned actions, minimal permissions, persist-credentials: false)

  • CI: cross-platform test matrix (Ubuntu 3.11/3.12/3.13, macOS 3.12, Windows 3.12)

  • CI: macOS arm64 binary build

  • CI: uv caching, mypy caching, alls-green gate job

  • Batch upload helper script with YAML-driven folder-to-label contract (scripts/upload_from_folders.py)

Docs

  • Add DICOM utilities page documenting xnatctl[dicom] commands

  • Add administration page (catalog refresh, user management, audit log)

  • Rewrite all user-facing documentation for beginner-friendly onboarding

  • Add shell completion setup instructions

  • Document session upload-exam and upload method comparison

Refactoring

  • Rename admin user add-to-groups to admin user add

0.1.1 (2026)

  • Fix Windows binary build (venv activation in CI workflow)

  • Improve Windows installation docs with PATH setup instructions

  • Clarify that install script is Linux/macOS only

0.1.0 (2026)

  • Uniform -E/-P options across all session and scan commands

  • -E/--experiment accepts ID or label (label requires -P or profile default_project)

  • default_project profile setting now used as automatic -P fallback

  • Consistent metavar=ID_OR_LABEL and help text on all -E options

  • PyPI trusted publishing via OIDC (stable releases to PyPI, prereleases to TestPyPI)

  • Multi-platform standalone binaries: Linux, macOS, Windows via PyInstaller

  • install.sh auto-detects OS and architecture

  • CLI integration test suite: 150 tests covering all commands

  • Service layer unit test suite: 138 tests covering all services

0.0.2 (2025)

  • Add sequential retry mechanism for failed uploads

  • Implement thread-local HTTP client for gradual-DICOM uploads

  • Enhance 400 error logging

0.0.1 (2025)

  • Initial release

  • Core CLI commands: project, subject, session, scan, resource, prearchive, pipeline, admin, api

  • Profile-based YAML configuration

  • httpx-based HTTP client with retry logic

  • Parallel download and upload support

  • JSON and table output formats