Skip to content

Upgrade from miura-nexus 0.61.4 to 0.65.x

Use this page if you still run the 0.61.4 client and want the 0.65 release. You can upgrade in one step—you do not need to install 0.62 or 0.63 along the way.

Most workflows that only read collections, upload local files, or download results need little or no code change. If you author recipes, import from connectors, or read published tables from a run handle, start with the table below.

Upgrade at a glance

Recipe authors on a backend that requires a case-grouping binding must follow Recipe execution on binding-required backends.

get, list_items, download, and local upload without case grouping stay as they are. Bumping the package does not migrate collection data or force you to rewrite recipes you already run successfully.

Existing usage Impact Required action
Basic collection reads / download None None
Local upload / plan without case grouping None None
Read/write existing collections None None
Create or run a new recipe on a binding-required backend Breaking Upgrade to 0.65.x; create an admitted case binding and pass it as cases= to deploy_recipe
Run an existing recipe with a persisted admitted binding None None
RunHandle.catalog_asset() / catalog_publication() Breaking Use catalog_asset() on the target collection
Connector discovery APIs (ConnectorDiscovery*) Breaking Rename discovery → inventory; provide source_prefix; set transfer_binding_id before apply()
Early 0.62 / 0.63 case-grouping / with_case_grouping Breaking Bind with cases= from upload.cases / plan.cases
Reusing AsyncNexus after close() Behavior Fix client lifecycle (async with or new client)
Assuming UploadResult.total_size_bytes is always an int Behavior Handle None
get_or_create_recipe when the stored executable differs Behavior Catch RecipeSpecConflictError or use deploy_recipe
Nested optional() schema folders Behavior Re-check if you relied on old child-required semantics
Tests matching “Not authenticated” on permission denials Behavior Update string matchers only

If every row marked Breaking or Behavior does not apply to your code, no source changes are expected for the common 0.61.4 collection workflows above.


Before you upgrade

  1. Scan the impact matrix and note which rows apply.
  2. Complete every applicable item in the migration checklist.
  3. Know your rollback posture before you install.
  4. Install 0.65.x, then verify.

Rollback

Client-only rollback to miura-nexus==0.61.4 is not viable for recipe authoring on a backend that requires a case-grouping binding. 0.61.4 can still read and write ordinary collection data, but it cannot create or send an admitted binding for a new recipe execution.

For other workflows, rollback is expected to be safe without a data migration provided you have not started using APIs introduced after 0.61.4 (case grouping, deploy_recipe revisions with case pins, connector inventory apply, collection description writes, inventory summary).

Once you create data or recipes that only newer workflows understand (for example recipes bound with admitted case membership, or connector imports via the inventory apply path), a client downgrade may still read older resources but will not drive those newer workflows. Prefer validating on an isolated test project first if you plan to adopt those features.


Migration checklist

Only perform an item if the corresponding API appears in your code. When every applicable box is done, the source migration is complete.

  • Replace run.catalog_asset() / run.catalog_publication() with collection.catalog_asset() on the target collection.
  • Rename connector discovery types and fields to inventory (ConnectorInventoryScope, inventory_scope, …).
  • For connector datasources, provide the now-required source_prefix and ensure max_keys is within the supported 1–20000 range.
  • Set transfer_binding_id before connector apply(); leave any optional transfer override unset unless you intentionally set one for a single run.
  • Prefer async with AsyncNexus(...) — API calls after close() now raise.
  • Allow UploadResult.total_size_bytes is None (unknown size, not zero).
  • Catch RecipeSpecConflictError from get_or_create_recipe, or switch to deploy_recipe.
  • If your backend requires case-grouping bindings, do not retain 0.61.4 for recipe authoring. Upgrade to 0.65.x, create an admitted case result, and pass its AdmittedCases as cases= to deploy_recipe.
  • If you used early 0.62 / 0.63 case-grouping helpers: bind with cases= (upload.cases / plan.cases); drop with_case_grouping / update(case_grouping=...).

Optional product adoption (CaseStructure, case grouping, connector import, miura.sdk facade) is not required to finish this migration — see New capabilities through 0.65.x after you verify.


Install 0.65.x

Install the latest 0.65 release from the same package index you already use:

python -m pip install --upgrade "miura-nexus>=0.65,<0.66"
# extras as before, e.g. python -m pip install --upgrade "miura-nexus[pandas,sigma]>=0.65,<0.66"

Breaking changes

Recipe execution on binding-required backends

Breaking platform compatibility change — affects 0.61.4 recipe authors.

Deployments that require admitted case membership expect every recipe execution to resolve a case-grouping binding. A 0.61.4 client has no API for creating an admitted case result or sending that binding. Creating a recipe with create_recipe() or get_or_create_recipe() can succeed, but executing it fails with HTTP 400:

case_grouping_binding is required for recipe execute

Not affected: collection reads, downloads, local uploads, and reads of already published catalog assets. An existing recipe with a persisted admitted binding can continue to run.

What to do: upgrade the client and use the admitted binding flow:

grouping = CaseGrouping.root_path("/", 1)
upload = await source.upload(datasource, case_grouping=grouping)
recipe = await project.deploy_recipe("transform", spec, cases=upload.cases)
run = await project.run_recipe(recipe)

The backend has a deprecated legacy-tag fallback for a pre-created binding, but that is not a complete 0.61.4 authoring workflow and should not be used as a long-term compatibility contract.

Catalog asset from the collection, not the run

Breaking change — only if you use RunHandle.catalog_asset() or RunHandle.catalog_publication().

Not affected: code that already uses collection-level catalog_asset() / catalog_assets.

What changed: Run-scoped shortcuts are removed. Resolve published tables from the target collection.

run = await project.run_recipe("my-recipe")
await run.wait()
asset = await run.catalog_asset()
run = await project.run_recipe("my-recipe")
await run.wait()
asset = await target_collection.catalog_asset()
# or: target_collection.catalog_assets …

What to do: Point at the collection that owns the published table. See Published tables.

Connector discovery → inventory

Breaking change — only if your code constructs connector datasources (or imports ConnectorDiscoveryScope / ConnectorDiscoveryJobRef).

Not affected: callers that never used connector datasources.

0.61.4 0.65.x
ConnectorDiscoveryScope ConnectorInventoryScope
ConnectorDiscoveryJobRef ConnectorInventoryJobRef
discovery_scope / discovery_job inventory_scope / inventory_job
discovery_job_id inventory_job_id
ManifestProvenance.CONNECTOR_DISCOVERY CONNECTOR_INVENTORY
optional source_prefix required source_prefix
soft max_keys include_glob default **/*, max_keys in 1–20000

What to do:

  • Rename imports and fields.
  • Always set source_prefix; keep max_keys between 1 and 20000 if you set it.
  • Set transfer_binding_id on ConnectorDataSource before apply().
  • For a normal import, leave optional transfer overrides unset. Set an override only when you intentionally redirect a single run.
  • Use async collections for connector import — the sync API rejects connector datasources.

Guide: Import files from a remote connector.

Preview case-grouping APIs (cases=)

Breaking change — only if you wrote against early 0.62 / 0.63 wheels or docs.

Not affected: 0.61.4 callers only when they do not author recipes on a binding-required backend. Those callers must upgrade as described above; this section otherwise only concerns early 0.62 / 0.63 preview APIs.

What changed: Membership is bound on deploy/update with cases= (AdmittedCases from upload.cases / plan.cases, or an admitted CaseGroupingResult). recipe.update(case_grouping=...) and with_case_grouping helpers are gone. Raw CaseGrouping, upload, or plan objects are rejected at the binding site. RecipeSpec factories still take grouping intent on case_grouping= (authoring, not the admitted pin).

What to do:

upload = await collection.upload(datasource, schema=schema, case_grouping=grouping)
recipe = await project.deploy_recipe("my-recipe", spec, cases=upload.cases)
await recipe.update(cases=plan.cases)  # rebind later

Important behavior changes

These APIs remain available, but their behavior or returned values changed.

Closed clients can no longer be reused

Who is affected: Code that calls an AsyncNexus client after close().

What changed: Such calls now raise RuntimeError. Previously a new HTTP session was opened implicitly.

What to do: Use async with AsyncNexus(...) or create a new client.

Upload size may be unknown

Who is affected: Code that formats or arithmetic on UploadResult.total_size_bytes without a null check.

What changed: The field is int | None. For connector apply(), None means unmeasured (not zero). Local uploads still always count.

What to do:

if result.total_size_bytes is not None:
    print(f"{result.total_size_bytes} bytes")

get_or_create_recipe rejects executable drift

Who is affected: Idempotent bootstrap that assumed “same name ⇒ return whatever exists” even when the requested executable spec differed.

What changed: Mismatch raises RecipeSpecConflictError; the stored recipe is left unchanged.

What to do: Catch RecipeSpecConflictError, or prefer project.deploy_recipe(name, spec, cases=...) for create / no-op / revise.

Permission errors show the real denial

Who is affected: Tests or logs that match the old permission_denied: Not authenticated string on grant failures.

What changed: Permission denials no longer look like missing API keys when your credentials are fine and the resource simply denied the action. API-key hints are reserved for genuine authentication failures.

What to do: Update those matchers only. Everyday callers need no change.

Token refresh defaults

Who is affected: Environments where token refresh was wired to a non-default auth client without an explicit override.

What changed: Refresh-token exchanges default to the nexus-hub-atlas Keycloak client. nexus-core is not the default while that profile is not generally available in production.

What to do: Hub Atlas users need no change. To use Core, set MIURA_INTERNAL_KEYCLOAK_CLIENT_ID=nexus-core. If authentication starts failing after the upgrade, set the token-refresh client to the client that issued your API key.

Optional schema parents skip required descendants

Who is affected: Schemas that nest required members under optional() folders and expected child requirements to fire when the parent is absent.

What changed: optional() is per-node. Absent optional parents skip required descendants.

What to do: Re-check those schemas if you relied on the old interpretation.


Verify the upgrade

Run the paths you actually depend on:

  • Authentication succeeds
  • Read an existing collection (get / list_items)
  • Upload one representative local datasource
  • Run one existing recipe and wait for completion
  • Resolve its published output via collection catalog_asset(), if applicable
  • Run connector plan/apply, if applicable
  • Download one representative result

If these pass, the 0.61.4 → 0.65.x migration is complete.

Everything below is optional modernization — stop here if your only goal is a green bump.


New capabilities through 0.65.x

Nothing in this section is required for a successful upgrade.

Authoring from miura.sdk

from miura.sdk import CaseStructure, File, Number, RecipeSpec, ResourceMap, Schema, capture, one_or_more

Recipe helpers: miura.sdk.recipe. Optional Sigma: miura.sdk.sigma (plain import miura.sdk does not require the [sigma] extra).

CaseStructure, case grouping, and CaseSet

from miura.sdk import CaseStructure, File, Number, capture, one_or_more

case_id = capture("case_id", Number(min_digits=6, max_digits=6))
structure = CaseStructure.folder(
    path="sim_" + case_id,
    members={"mesh": File("mesh.stl"), "field": File("field.xdmf")},
)
schema = structure.schema(count=one_or_more())
case_grouping = structure.case_grouping()

upload = await collection.upload(
    datasource,
    schema=schema,
    case_grouping=case_grouping,
)
# upload.cases → AdmittedCases pin for deploy

Also: collection.plan(..., case_grouping=), case_set(...), probe_case_grouping(), live CaseSet reads. Guides: Describe simulations, Case discovery.

Deploy → run recipe lifecycle

from miura.sdk import RecipeInputs

recipe = await project.deploy_recipe("convert", spec, cases=upload.cases)
run = await recipe.run(inputs=RecipeInputs.PINNED)  # or "latest"
  • deploy_recipe creates, no-ops, or revises (create_recipe remains create-only).
  • inputs="pinned" (default) vs inputs="latest" for membership at run time.
  • project.available_writers / available_readers; pass writers as plain strings (writer="parquet_tabular").

Guide: Transform cases with a recipe.

Connector plan() → apply()

Same review-before-run flow as local upload. See Import files from a remote connector.

Connector plans can also opt into case grouping:

from miura.nexus.case_grouping import CaseGrouping

case_grouping = CaseGrouping.root_path("/", depth=1)
plan = await collection.plan(
    connector_datasource,
    case_grouping=case_grouping,
)
recipe = await project.deploy_recipe("transform", spec, cases=plan.cases)

Check plan.validation_gates and plan.is_admissible before applying the plan. If connector-origin grouping is not enabled by the deployment, the client raises ValueError rather than silently ignoring the request.

Collection description and inventory summary

  • Optional Markdown description CRUD (CollectionDescription on miura.sdk).
  • Collection inventory summary (None when unavailable).

Sigma factories and pipeline input manifests

RecipeSpec.from_sigma_converter / from_sigma_dataset_generator, plus attach_pipeline_input_manifest() for attaching a pipeline input manifest to a recipe data_ref.


Reliability fixes

Usually no code change — same APIs, more correct behavior:

  • Uploads tolerate brief finalize retries instead of failing immediately.
  • Large or slow local uploads are more stable.
  • Staging uploads no longer send unsigned headers that object storage rejects.
  • Downloads find the right object when the storage key and item URI diverge.
  • HTTP 422 responses include field-level validation detail.
  • Uploads without case grouping are not blocked when grouping was skipped as a precondition.

Update only if you asserted exact old error strings.


Documentation changes

Getting Started now leads with CaseStructure.folder and deploy → run. Runnable examples are listed under Example scripts. Update bookmarks or CI that still pointed at the old tree.