Artifact format¶
Library package version is 0.2.0. Durable artifact format is frozen at
FormatVersion { major: 0, minor: 2 } (antecedent_io::STABLE_FORMAT).
Container¶
See ADR 0002 / 0017:
magic (CAUSAL\0\0) | container_version (u32 LE = 1)
| manifest_len (u32 LE) | canonical CBOR manifest
| repeated: section_len (u32 LE) | section bytes
Manifest fields: format_version, minimum_reader_version, artifact_kind,
library_version, artifact_id, sections[], provenance.
Section compression¶
Sections may use Zstandard (compression = "zstd"). The length prefix and
BLAKE3 checksum cover on-wire (possibly compressed) bytes.
uncompressed_size is the logical payload length. Auto compression applies when
the logical size is ≥ 4 KiB and compression improves size (≈95% ratio).
Arrow IPC sections (application/vnd.apache.arrow.file) use
CompressPolicy::Never so they remain mmap-eligible.
Selective read / mmap / shared writes¶
| API | Behavior |
|---|---|
EncodedArtifact::read_from |
Full materialization (compat) |
EncodedArtifact::read_selective |
Stream-hash + discard unselected sections; no retained payload |
ArtifactReader::open_seek |
Index section offsets; seek-skip without allocating payloads |
MappedArtifactReader::open_path |
memmap2 map; load_section_mapped for uncompressed views |
SectionBytes.data: Arc<[u8]> |
Shared logical buffers; Never-compress write avoids clone |
decode_posterior_meta_from_seek / _from_path |
Metadata without loading draws |
Compressed sections cannot be mapped as logical views (IoError::MappedCompressed);
use load_section to decompress into owned bytes.
Supported kinds¶
| Kind | Sections |
|---|---|
schema_graph |
schema (CBOR SchemaWire v2), dag (CBOR DagWire) |
analysis_trace |
analysis.trace (CBOR) |
causal_posterior |
posterior.meta (CBOR), posterior.draws (f64 LE col-major) |
model_bundle |
required: bundle.header, schema, dag, mechanisms; optional: contrast, query, analysis.trace, identification, estimate, refutations, plans, provenance, posterior, discovery |
Migration¶
antecedent_io::migrate_artifact / read_and_migrate / migrate_from_seek accept
source formats 0.1 and 0.2. Format 0.1 skinny SchemaWireV01 { variable_names }
is rewritten to full SchemaWire with Continuous defaults. Unknown format
versions fail with IoError::UnsupportedFormat. Breaking changes require
migration from at least the previous two stable versions.
Graph interchange (non-artifact)¶
DOT, JSON, GML, and NetworkX-compatible (node_link_data / adjacency_data)
codecs live alongside wire types for string-graph import/export.
Supported graph classes: DAG, CPDAG, PAG, and ADMG
(DagWire / CpdagWire / PagWire / AdmgWire, plus matching *_from_* /
*_to_* APIs). CPDAG undirected edges use DOT -- / GML undirected 1 /
NetworkX undirected=true. ADMG bidirected edges use DOT dir=both / GML
bidirected 1 / NetworkX bidirected=true. PAG circle/conflict marks use
mark_a / mark_b attributes.
Interactive / artifact-first (discovery off the estimate click)¶
Discovery is evidence and is not on the interactive estimate path. Preferred spreadsheet / dashboard spine:
discover_* once → AcceptedGraph (versioned) / JSON hold
→ many analyze(graph=..., latency="interactive") or PreparedAnalysis
→ rediscover only on explicit AcceptedGraph.rediscover / refresh
Static graphs: Dag / Cpdag / Pag / Admg. Temporal completions: TemporalDag
(and reviewed TemporalCpdag / TemporalPag) from PCMCI / PCMCI+ / LPCMCI —
estimate-only pulse clicks never re-run discovery.
One-shot analyze(..., discovery=...) remains a script convenience
(Standard/Report or no latency tier). Combining discovery= with
latency="interactive" raises CausalUnsupportedError (Rust:
refuse_discovery_under_interactive). See python/examples/discover_then_estimate.py
and parity/discovery.toml capability discovery.ux.artifact_first.