Materialization¶
This page covers filesystem byte recovery and input staging. It is not about choosing whether tabular artifacts should be ingested into DuckDB, except where ingested artifacts provide a fallback byte source.
Consist has three related filesystem recovery and staging stories:
- historical output recovery, which starts with
hydrate_run_outputs(...)orhydrate_run_outputs_to_destinations(...) - single-artifact recovery, which starts with
materialize_artifact(...) - canonical input staging, which starts with
ExecutionOptions(input_materialization="requested", input_paths={...})onrun(...)andScenarioContext.run(...)
For layout-preserving historical output recovery, start with
hydrate_run_outputs(...). When a consumer needs exact, unrelated paths,
use hydrate_run_outputs_to_destinations(...).
It is the clearest API for restart, archive-mirror recovery, and cross-workspace reuse because it answers the practical questions in one call:
- Which keys did I ask for?
- Where did each one land?
- Can I use the returned artifact right now?
materialize_run_outputs(...) performs the same underlying copy/export work
but returns the older aggregate
MaterializationResult.
Keep it for compatibility or summary-style reporting; use
hydrate_run_outputs(...) for new workflows.
Exact destinations¶
hydrate_run_outputs_to_destinations(...) makes the caller-provided mapping
the complete requested-output set. It supports ordinary artifact files and
directories, including unrelated destinations:
hydrated = tracker.hydrate_run_outputs_to_destinations(
"prior_run_id",
destinations_by_key={
"persons": tracker.run_dir / "tool_inputs" / "persons.csv",
"skims": tracker.run_dir / "shared" / "skims.zarr",
},
)
The caller selects the historical run and is responsible for verifying that an
external tool consumes these staged paths. Results are keyed and can be partial
in warning mode, so inspect each status before invoking that tool. Every
destination must be inside the tracker's run directory or a configured mount
root unless allow_external_paths=True. Persisted OutputSet hydration is
deferred from this slice. Newly logged Zarr outputs are immutable directory
artifacts automatically; other directory trees require
log_output(..., artifact_kind="directory"). Newly logged Shapefile outputs
are immutable file bundles automatically: Consist records the .shp, .shx,
and .dbf files plus every same-stem regular sidecar. Zarr and Shapefile
artifacts retain their native drivers for consist.load(...).
For strict archive recovery, pair db_fallback="never" with an explicit
source_root; Consist uses only that archive root and atomically publishes a
verified tree. A Shapefile destination is a clean bundle-root directory, not a
shared .shp parent: the returned item's path is that root and entry_path
is the loadable .shp inside it. Legacy Zarr and Shapefile rows without their
respective persisted manifests cannot use exact archive or hydration and must
be re-logged under this contract.
Use materialize_artifact(...) when you already have one artifact object and
need Consist to recover its bytes into a target workspace root. It is the
artifact-centric primitive beside the run-output-centric
hydrate_run_outputs(...).
For input-side workflows, prefer requested input materialization on the run
surface. It uses the same canonical artifact recovery rules, but keeps the
operation attached to normal execution and cache-hit behavior. Reach for
stage_artifact(...) and stage_inputs(...) only when you need the same
staging behavior outside a run lifecycle.
Recovery Ordering¶
When Consist rematerializes a historical output or stages a resolved artifact, it probes recovery sources in this order:
- A per-call
source_root=...override - The historical source derived from the producing run directory, historical mount snapshot, or recorded artifact mount root
- Ordered
artifact.meta["recovery_roots"] - DuckDB export fallback for ingested CSV/Parquet artifacts
container_uri remains the canonical logical location throughout. Recovery
roots are advisory byte sources, not a second canonical path.
Archive Workflow¶
For long-lived workflows that move outputs into an archive or iteration-specific
root, prefer recording recovery roots once instead of passing source_root=...
on every restart:
archive_root = Path("/archive/pilates/iteration_004")
run_id = tracker.current_consist.run.id
archive = tracker.archive_current_run_outputs(
archive_root,
mode="copy",
)
hydrated = tracker.hydrate_run_outputs(
run_id,
keys=["persons"],
target_root=tracker.run_dir / "restored_workspace",
)
archived_persons_path = archive.paths["persons"]
next_inputs = archive.outputs["persons"]
archive_current_run_outputs(...) and archive_run_outputs(...) return an
ArchivedOutputs mapping. It still behaves like a read-only Mapping[str,
Path] for the archived bytes. Its explicit .paths property exposes that
same read-only path mapping, while .outputs gives you refreshed artifacts
that already carry the new recovery root. Pass those refreshed artifacts
directly into a later inputs={...} mapping when you want the next run to
consume the archive.
For a manifest-backed OutputSet, archive_run_outputs(...) archives the
logical set as one recovery unit: every declared member is copied or moved into
the parent artifact's URI-relative archive root, and the persisted manifest is
archived at its own URI-relative location. The parent, members, and manifest
receive the recovery root only after all of those bytes have been validated and
published. Thus archive.paths["set_key"] is the archived set root and
archive.outputs["set_key"] is the refreshed parent artifact for downstream
reuse. archive_run_output_files(...) remains a regular-file-only report API
and reports OutputSets as unsupported.
When a selected immutable directory artifact is nested inside an OutputSet root, it reuses the set publication only when its persisted directory manifest exactly matches that OutputSet manifest subtree. This preserves strict set validation; unrepresented files or directories fail archival instead of being silently accepted.
When an application needs an auditable, no-replacement archive pass for files
only, use tracker.archive_run_output_files(...) instead:
report = tracker.archive_run_output_files(
"prior_run_id",
Path("/archive/pilates/iteration_004"),
keys=["persons", "households"],
preserve_existing=True,
verify=True,
append=True,
)
if not report.complete:
for key, result in report.items():
if not result.metadata_committed:
print(key, result.copy_status, result.verification_status, result.message)
ArchivedRunOutputFilesReport is a read-only mapping of selected output keys
to immutable per-key results. Each result includes the source artifact, known
source and target paths, copy and verification statuses, metadata-commit state,
and a message. The helper supports regular file artifacts only; directories,
output sets, symlinks, and artifacts lacking a full-file hash are reported per
key rather than copied. It never replaces archive bytes. A rerun can retain a
previously verified target and retry only the recovery-root metadata commit.
report.complete means every selected key met the requested verification policy
and committed metadata in this call; it is not a durable workflow-state claim.
Use the lower-level helpers when you want to manage archival yourself:
tracker.set_artifact_recovery_roots(...)records one or more archive roots without copying bytes or verifying the archive-side files.tracker.register_artifact_recovery_copy(...)verifies an externally copied artifact file before recording the recovery root.tracker.register_run_output_recovery_copies(...)applies verified external-copy adoption to all or selected outputs for a run.tracker.archive_current_run_outputs(...)archives the outputs of the active run without manually extracting the run ID first.tracker.archive_artifact(...)copies or moves a single artifact into an archive root and records that root.tracker.archive_run_outputs(...)applies the same pattern to all or a selected subset of outputs for a run, including manifest-backed OutputSets.tracker.archive_run_output_files(...)copies or conservatively retains selected regular files, verifies them when requested, and bulk-registers recovery roots while retaining per-key outcomes for retries.
For HDF5 containers, verified recovery-copy adoption respects the parent
container policy. Parent H5 files with
container_recovery_unit="parent_file" can register recovery roots; child
h5_table artifacts whose parent declares
child_recovery_policy="descriptive_only" return
blocked_by_container_policy.
Recommended Workflow¶
Hydrate only the outputs you need into a fresh workspace root:
from pathlib import Path
hydrated = tracker.hydrate_run_outputs(
"prior_run_id",
keys=["persons", "households"],
target_root=tracker.run_dir / "restored_workspace",
source_root=Path("/archive/outputs_mirror"), # optional
)
Relative target_root values are resolved from the process working directory.
Use tracker.run_dir / ... or an absolute mount-backed path when you want the
restore destination to stay inside the tracker's allowed roots.
Inspect the keyed results and reuse the detached artifacts directly:
persons = hydrated["persons"]
print(persons.status)
print(persons.path)
if persons.resolvable:
path = persons.artifact.as_path()
print(path)
In this flow:
hydrated["persons"].statustells you what happened for that key.hydrated["persons"].pathtells you where Consist expected or created the destination.hydrated["persons"].artifactis a detached artifact view. WhenresolvableisTrue,artifact.as_path()points at the hydrated destination in the new workspace. The detached artifact also preservesartifact.hash, Consist's canonical artifact fingerprint surface.
Requested Input Staging¶
For new workflow code, input-side staging usually belongs on the run surface:
from pathlib import Path
from consist import ExecutionOptions
result = tracker.run(
fn=run_tool,
inputs={"config_path": Path("./configs/baseline.yaml")},
outputs=["report"],
execution_options=ExecutionOptions(
input_binding="paths",
input_materialization="requested",
input_paths={
"config_path": tracker.run_dir / "workspace" / "config.yaml"
},
),
)
This keeps the canonical input artifact unchanged for identity and lineage while ensuring the callable sees a real local file at the requested path.
Use the low-level helpers when you already have resolved artifacts and need to stage them manually:
staged = tracker.stage_inputs(
{"config_path": artifact},
destinations_by_key={
"config_path": tracker.run_dir / "workspace" / "config.yaml"
},
)
Staging Status Meanings¶
| Status | Meaning |
|---|---|
staged |
Copied bytes to the requested destination |
preserved_existing |
Destination already existed with matching content and was reused |
missing_source |
No readable source bytes were found for the canonical artifact |
failed |
Staging was attempted but failed due to a collision, policy check, or copy error |
Status Meanings¶
| Status | Meaning |
|---|---|
materialized_from_filesystem |
Copied from historical cold bytes on disk |
materialized_from_db |
Exported from DuckDB for an ingested CSV/Parquet artifact |
preserved_existing |
Destination already existed and was reused |
skipped_unmapped |
No safe historical relative-path mapping was available |
missing_source |
Historical bytes were unavailable and no DB fallback applied |
failed |
Recovery was attempted but failed due to a collision, policy check, or copy/export error |
When To Use materialize_run_outputs(...)¶
Use materialize_run_outputs(...) only when you intentionally want the older
aggregate summary buckets:
materialized_from_filesystemmaterialized_from_dbskipped_existingskipped_unmappedskipped_missing_sourcefailed
If you are writing new restart or recovery logic, prefer
hydrate_run_outputs(...).
Reference¶
This page keeps the story focused on the recommended workflow. For full API signatures and attribute details, use the generated reference below.
Historical output materialization and hydration helpers.
This module exposes two related recovery layers for prior run outputs:
materialize_run_outputs(...)performs the physical copy/export work and returns the legacy aggregateMaterializationResultsummary.hydrate_run_outputs(...)performs the same recovery work but returns a key-indexedHydratedRunOutputsResultwith detached artifacts, resolved paths, and per-key statuses that are immediately usable in a new workspace.
The keyed hydration result is the higher-level API. The aggregate
MaterializationResult remains the compatibility wrapper for callers that
only need summary buckets.
MaterializedArtifact
dataclass
¶
Outcome for materializing one artifact into a target root.
Attributes:
| Name | Type | Description |
|---|---|---|
artifact |
Artifact
|
Detached artifact associated with the result. |
key |
str
|
Artifact key. |
path |
Path | None
|
Usable materialized or preserved destination, when available. |
source_path |
Path | None
|
Filesystem source used for materialization. |
target_path |
Path | None
|
Intended destination even when materialization did not succeed. |
status |
MaterializedArtifactStatus
|
Per-artifact materialization outcome. |
message |
str | None
|
Optional diagnostic for a non-happy-path outcome. |
resolvable |
bool
|
Whether the detached artifact can be used at |
StagedInput
dataclass
¶
Per-key outcome for canonical input staging.
Attributes:
| Name | Type | Description |
|---|---|---|
key |
str
|
Input key requested by the caller. |
artifact |
Artifact
|
Detached copy of the input artifact. When |
path |
Path | None
|
Explicit staging destination, when one was known. |
status |
StagingStatus
|
One of:
|
message |
str | None
|
Optional warning or error detail for |
resolvable |
bool
|
Whether the returned detached artifact is immediately usable from the staged destination. |
StagedInputsResult
dataclass
¶
Bases: Mapping[str, StagedInput]
Key-indexed result for canonical input staging.
This mapping preserves the caller's requested key order and answers the practical questions for each requested input: what destination was used, whether the staged artifact is immediately usable, and whether staging copied bytes or reused an existing local path.
ArtifactRecoveryCopyRegistration
dataclass
¶
Outcome for adopting one externally copied artifact recovery location.
Attributes:
| Name | Type | Description |
|---|---|---|
artifact |
Artifact
|
Artifact represented by the recovery copy. |
key |
str | None
|
Artifact key when available. |
artifact_id |
str
|
Persistent artifact identifier, or an empty string when unavailable. |
recovery_root |
Path
|
Root supplied for recovery-copy registration. |
expected_path |
Path | None
|
URI-relative path expected beneath |
status |
RecoveryCopyStatus
|
Verification or metadata-persistence outcome. |
message |
str | None
|
Optional diagnostic for a blocked or failed result. |
metadata_updated |
bool
|
Whether recovery-root metadata was committed. |
verification_succeeded |
bool
|
Whether the copy satisfied the requested byte-verification policy. |
Notes
registered means Consist verified the existing copy according to the
requested policy and persisted the recovery root. Other statuses are
advisory blockers; identity is unchanged and metadata was not updated.
RunOutputRecoveryCopiesRegistration
dataclass
¶
Bases: Mapping[str, ArtifactRecoveryCopyRegistration]
Key-indexed result for adopting externally copied run-output bytes.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
outputs
|
dict[str, ArtifactRecoveryCopyRegistration]
|
Per-output registration outcomes, keyed by run-output key. |
dict()
|
blocked
property
¶
Return outputs that were not registered successfully.
complete
property
¶
Return whether every selected output was registered successfully.
registered
property
¶
Return outputs whose verified recovery roots were committed.
summary
property
¶
Return deterministic status counts for the registration results.
ArchivedRunOutputFile
dataclass
¶
Outcome for archiving one regular run-output file.
Attributes:
| Name | Type | Description |
|---|---|---|
artifact |
Artifact
|
Output artifact selected for archival. |
key |
str
|
Run-output key. |
source_path |
Path | None
|
Source path discovered for a copy attempt. |
target_path |
Path | None
|
Canonical path below the requested recovery root. |
copy_status |
ArchiveRunOutputFileStatus
|
Copy or existing-target outcome. |
verification_status |
ArchiveRunOutputVerificationStatus
|
Outcome of the requested verification policy. |
metadata_committed |
bool
|
Whether recovery-root metadata was persisted for this output. |
message |
str | None
|
Optional diagnostic for a non-happy-path outcome. |
ArchivedRunOutputFilesReport
dataclass
¶
Bases: Mapping[str, ArchivedRunOutputFile]
Key-indexed report for Tracker.archive_run_output_files(...).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
outputs
|
dict[str, ArchivedRunOutputFile]
|
Per-output archival outcomes, keyed by requested output key. |
dict()
|
Notes
complete is a result for this invocation only; it does not represent a
durable archive-workflow state.
HydratedRunOutput
dataclass
¶
Per-key outcome for hydrate_run_outputs(...).
Attributes:
| Name | Type | Description |
|---|---|---|
key |
str
|
Output key requested by the caller. |
artifact |
Artifact
|
Detached copy of the historical artifact record. When |
path |
Path | None
|
Destination path under the requested target root, when one is known. This can be populated even for non-resolvable outcomes to show the intended restore location. |
status |
HydrationStatus
|
One of:
|
message |
str | None
|
Optional warning or error detail for |
resolvable |
bool
|
Whether the returned detached artifact is immediately usable from the
hydrated workspace. When |
HydratedRunOutputsResult
dataclass
¶
Bases: Mapping[str, HydratedRunOutput]
Key-indexed recovery result for historical run-output hydration.
This mapping is returned by hydrate_run_outputs(...) and preserves the
caller's requested key order. Each value carries the detached artifact,
destination path, and per-key status.
New users should generally start with this result because it answers the three practical questions in one object: which keys were requested, what path each key resolved to, and whether the returned artifact is immediately usable in the current workspace.
Examples:
>>> hydrated = tracker.hydrate_run_outputs(
... "prior_run",
... keys=["persons", "households"],
... target_root="restored",
... )
>>> hydrated["persons"].status
'materialized_from_filesystem'
>>> hydrated.paths["persons"].name
'persons.parquet'
complete
property
¶
Return whether no output is unmapped, missing, or failed.
failed_keys
property
¶
Return keys whose hydration status is "failed".
paths
property
¶
Return resolvable output destinations keyed by output key.
resolvable
property
¶
Return hydration outcomes with immediately usable artifacts.
summary
property
¶
Return deterministic per-status hydration counts.
MaterializationResult
dataclass
¶
Aggregate summary for historical run-output materialization.
This is the compatibility result returned by
Tracker.materialize_run_outputs(...) and the top-level
consist.materialize_run_outputs(...) helper. It groups outcomes into
summary buckets rather than returning one object per requested key.
For new recovery flows, prefer HydratedRunOutputsResult because it
preserves per-key status, detached artifacts, and directly usable paths.
Attributes:
| Name | Type | Description |
|---|---|---|
materialized_from_filesystem |
dict[str, str]
|
Keys restored by copying bytes from historical filesystem locations. Values are the destination paths. |
materialized_from_db |
dict[str, str]
|
Keys reconstructed from DuckDB for ingested CSV/Parquet artifacts. Values are the destination paths. |
skipped_existing |
list[str]
|
Keys whose destination already existed and was preserved. |
skipped_unmapped |
list[str]
|
Keys that could not be mapped back to a historical relative path. |
skipped_missing_source |
list[str]
|
Keys whose historical bytes were unavailable and could not be recovered. |
failed |
list[tuple[str, str]]
|
Keys that failed during recovery, paired with the failure message. |
complete
property
¶
Return whether every requested output was materialized or retained.
has_failures
property
¶
Return whether any output encountered a materialization failure.
materialized
property
¶
Return all successfully materialized key-to-path entries.
summary
property
¶
Return deterministic aggregate materialization counts.
materialize_artifact(tracker, artifact, *, target_root, source_root, allowed_base, preserve_existing, on_missing, validate_content_hash)
¶
Materialize one artifact into a target root using recovery metadata.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tracker
|
Tracker
|
Tracker used for source discovery and policy checks. |
required |
artifact
|
Artifact
|
Artifact to materialize. |
required |
target_root
|
Path
|
Root below which its URI-relative path is recreated. |
required |
source_root
|
Path | None
|
Optional source root tried before recorded recovery locations. |
required |
allowed_base
|
Path, sequence of Path, or None
|
Allowed destination root or roots; |
required |
preserve_existing
|
bool
|
Whether an existing compatible destination can be retained. |
required |
on_missing
|
('warn', 'raise')
|
Policy for unavailable bytes and materialization failures. |
"warn"
|
validate_content_hash
|
('never', 'if-present', 'always')
|
Content-validation policy for source and destination bytes. |
"never"
|
Returns:
| Type | Description |
|---|---|
MaterializedArtifact
|
Detached artifact, source/target paths, and a recovery outcome. |
stage_artifact(tracker, artifact, destination, *, mode='copy', overwrite=False, validate_content_hash='if-present', allow_external_paths=None)
¶
Stage one artifact to a requested filesystem destination.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tracker
|
Tracker
|
Tracker used to resolve canonical paths, allowed roots, and recovery fallbacks. |
required |
artifact
|
Artifact
|
Canonical input artifact to stage. |
required |
destination
|
Path
|
Explicit local destination where the staged copy should exist. |
required |
mode
|
('copy', 'hardlink', 'symlink')
|
Staging mode. In v1, only |
"copy"
|
overwrite
|
bool
|
Whether to replace an existing non-identical destination. |
False
|
validate_content_hash
|
('never', 'if-present', 'always')
|
Whether to validate staged file bytes against |
"never"
|
allow_external_paths
|
bool | None
|
Override for the tracker's external-path policy. |
None
|
Returns:
| Type | Description |
|---|---|
StagedInput
|
Detached artifact view plus the per-key staging outcome. |
stage_inputs(tracker, inputs_by_key, destinations_by_key, *, mode='copy', overwrite=False, validate_content_hash='if-present', allow_external_paths=None)
¶
Stage multiple keyed artifacts to their requested destinations.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tracker
|
Tracker
|
Tracker used to resolve canonical paths, allowed roots, and recovery fallbacks. |
required |
inputs_by_key
|
Mapping[str, Artifact]
|
Resolved input artifact mapping, typically the same key space later
used for |
required |
destinations_by_key
|
Mapping[str, Path]
|
Explicit staging destinations keyed by input name. |
required |
mode
|
('copy', 'hardlink', 'symlink')
|
Staging mode. In v1, only |
"copy"
|
overwrite
|
bool
|
Whether to replace an existing non-identical destination. |
False
|
validate_content_hash
|
('never', 'if-present', 'always')
|
Whether to validate staged file bytes against |
"never"
|
allow_external_paths
|
bool | None
|
Override for the tracker's external-path policy. |
None
|
Returns:
| Type | Description |
|---|---|
StagedInputsResult
|
Key-indexed staging outcomes in the caller's requested key order. |