Vision
delta-explain is a metadata-level diagnostic tool for Delta Lake. It makes file elimination visible without executing queries.
The positioning, stated once and meant literally: delta-explain is production-usable as a conservative Delta metadata diagnostic and CI guardrail, not yet a fully production-grade general-purpose Delta observability product. The "not yet" items below are the long-term roadmap; a capability moves across that line through a release note and updated docs, never silently.
This document outlines the planned evolution.
Principles
These apply across all releases:
- Not an optimizer. No query planner simulation, no execution time prediction.
- Metadata only. Reads the transaction log and file statistics. Never touches data files.
- Value comes from the semantic model, not from feature count. Each release should deepen understanding of why pruning works or fails, not just count files.
v0.1: Soft launch (shipped April 2026)
The tool works end-to-end for the common case: given a predicate and a Delta table, it shows how many files are eliminated by partition pruning and data skipping.
What works:
- Partition pruning and data skipping phases, reported separately
- SQL predicates: comparisons, AND/OR/NOT, IN, BETWEEN, IS [NOT] NULL
- Per-file verbose output showing kept/dropped with reason
- JSON output for programmatic consumption
- CI assertions (
--min-pruning,--assert-stats) - Local and cloud storage (S3, Azure, GCS)
v0.2: Confidence and classification (shipped May 2026)
Released as the v0.2.0 → v0.2.3 series. The tool now explains why pruning worked or failed, not just the file counts.
- Confidence model: each result tagged as
exact,conservative, orincompletedepending on stats completeness and predicate separability - Predicate classification: each clause explicitly labeled as
partition_safe,stats_safe, orunsplittable, with coded notes explaining why - Stable JSON schema (v0.1.0): versioned, documented, with analysis/notes/assertions blocks
- Expanded test fixtures: tables with partial stats; OR-mixed predicates classified as
unsplittableand covered by the existing canonical fixture - Distribution: crates.io, Docker (multi-arch), pre-built binaries for six targets,
.debpackages, Homebrew tap, Scoop bucket
The architecture and design rationale behind v0.2 are written up in detail in the companion deep-dive: delta-explain: Making Delta Lake Pruning Visible.
v0.3: Checkpoint support and type hardening (shipped July 2026)
Works reliably on production tables that have been checkpointed and vacuumed. The whole analysis rides on the kernel's log replay, which reads checkpoint Parquet natively: one source of truth for counts, per-file statistics, and assertions.
- Checkpoint coverage: per-file statistics come from the stats payload the kernel carries on each scan row, JSON commits and checkpoint Parquet alike, in both layouts (
add.statsJSON and structuredstats_parsed). No more[no stats]on long-lived tables, no more--assert-statsfalse positives. - Partition columns on checkpoint-only logs: the
metaDataaction in the JSON commits is the primary source; when log cleanup has removed it, partition columns fall back to the kernel-replayedpartitionValueskeys, preserving the two-phase attribution. - delta-kernel-rs 0.24,
object_store0.13. - Internal architecture: lib/bin split;
scan,attribution,gates,render, anderrormodules; the attribution arithmetic and the CI gates are pure and unit-tested.
Shipped in the same v0.3.0 release: type hardening and the production sprint. Date and timestamp coercion was the gate to real tables: most production tables are partitioned or clustered by date, and a predicate that cannot be typed cannot prune.
- Type coercions:
DATE,TIMESTAMP(UTC-normalized),TIMESTAMP_NTZ(wall-clock, offsets rejected as ambiguous),DECIMAL(exact scale), and narrow integers, resolved against the Delta schema;DATE '...'/TIMESTAMP '...'literal forms accepted - Time travel:
--at-version <N> - Alongside:
--profile(AWS shared config), the composite GitHub Action, and the first differential harness
v0.4: Predicate analysis, production tables, the public contract (shipped July 2026)
One release for the whole cycle. Goal: reduce false negatives for common patterns without becoming an optimizer, be honest on tables that look like production, and make the contract externally verifiable. The substrate comes first: an owned minimal predicate AST, produced by a small converter from sqlparser (one parse, two interpreters: kernel lowering and classification). Owning SQL lexing forever is the wrong trade, so sqlparser stays as the front end; the converter is the only module coupled to it, and everything outside the pruning language collapses into an explainable Unsupported leaf at that boundary. The rewrites below operate on the owned AST.
- Light normalization: flatten nested ANDs, push negations down to the leaves (De Morgan, three-valued-logic safe)
- OR factoring: factor conjuncts common to every OR branch out of the OR, so
(col = 'A' AND x) OR (col = 'A' AND y)exposescol = 'A'as a partition-safe top-level conjunct (a single-column OR likecol = 'A' OR col = 'B'already classifies as partition-safe) - Unsplittable explanations: for each unsplittable fragment, explain why it couldn't be classified (mixed columns, function calls, etc.), and degrade unsupported expressions to a conservative keep-all with a diagnostic warning instead of a fatal error
That is the complexity ceiling for the predicate analyzer: anything beyond crosses into optimizer territory. Also in v0.4: IS [NOT] DISTINCT FROM (the kernel's native null-safe comparison), and NULL literals typed from the column.
Production-table honesty (formerly the v0.5 plan, delivered in the same cycle):
- Detect and declare protocol features: deletion vectors (record counts overcount and the report says so), column mapping (physical vs logical names), liquid clustering. Declared in a
table_featuresJSON block with warnings; the numbers are never silently compensated. - Per-file machine-readable output:
--verbose --format jsonemits afiles[]array (kept,pruned_by, sizes, partition values), capped by--limit; the differential harness consumes it instead of scraping text. - Scale, measured: 200k files in ~1.6 s / ~320 MB, linear; numbers and the ~1M-file memory ceiling documented in the README.
- Exotic log shapes, proven: log compaction, classic multi-part checkpoints, and V2 UUID-named checkpoints with sidecars are covered by the test matrix (the kernel handled them; now that is a guarantee, not an assumption).
And the public contract, stabilized:
docs/semantics.md: what the tool guarantees, what it deliberately does not do, degradation rules, exit codes.- A formal JSON Schema (
schemas/report-v0.2.schema.json) validated by the integration suite against every emitted document shape, plus the field-by-field reference indocs/json-schema.md. RELEASE.mdand a repeatable three-minute quickstart (examples/quickstart/).
The road that led here
Every remaining professionalization item has landed: the "validated against" story (docs/validation.md, including the declared managed-table boundary), CI test jobs on macOS and Windows behind the required gate, reproducible benchmark tooling (examples/gen_scale_log.rs, three log shapes), supply-chain checks (dependabot plus cargo-audit, which found and fixed three real advisories on its first run), and the example notebooks re-verified end to end against MinIO and real GCS.
After 0.4.0 the plan is deliberate: a stabilization period of bug fixing and validation only, no new surface. The roadmap below resumes after that.
v0.5 - v0.6: Diagnostic layer and the report viewer (shipped July 2026)
Goal, delivered: shift from "file counter" to "pruning advisor", and make a report readable without a terminal.
--explain-why: a deterministic diagnostic engine (ADR 0007) that turns the report into advice - which fragment blocked pruning and what to change - with stable codes (NO_PARTITION_FILTER,WEAK_DATA_SKIPPING,STATS_ABSENT,UNSUPPORTED_FRAGMENT). No ML, nothing predicted: the why is as trustworthy as the numbers it explains, and gate-able in CI. JSON gains an additiveexplainarray (schema_version0.4.0).- The report viewer (
viewer/): a self-contained HTML page that renders a--format json --verbosereport - pruning funnel, predicate analysis, gates, diagnoses, and a filterable per-file table that stays usable at 200k files. A client of the JSON contract (no new analysis, no CLI change), air-gapped and CI-artifact safe.
Still planned in this track:
--engine-profile: emulate a specific engine's pruning strength (max|datafusion|spark|kernel) so a CI gate asserts what your engine will do, not the metadata's theoretical best. The known divergences (IN-list strategies) are documented in the README today; this makes them selectable.
Later: Fidelity and coverage (planned)
Moving declared boundaries across the line, one release note at a time:
- Deletion vectors: compensate, not just declare. The DV descriptor carries
cardinality;numRecords - cardinalityis the live record count. Cheap, but it changes a reported number's meaning, so it ships with a schema note. - Batch mode (
--predicates <file>): one log replay, N reports; also the natural place for task-level parallelism, which stays out of the single-invocation core by design. - One log replay per invocation: today a predicate costs up to three metadata scans; on remote tables that is the real latency. Redundancy elimination before any parallelism.
LIKE: shipped in v0.5.0, ahead of this list - prefix patterns rewrite to a lexicographic range and prune on both axes, and any-shapeLIKEon partition columns is evaluated exactly against the literal partition values (partition_exact).- JSON schema 1.0: declared only after 0.2 has soaked unchanged under real consumers for a few months.
Adoption track
- AWS shared-config profiles: shipped in v0.3.0 (
--profile; SSO andcredential_processproduce an actionable error pointing ataws configure export-credentials) - GitHub Action: shipped in v0.3.0 as a composite action at the repo root, one-line
uses:with CLI-mirroring inputs and step outputs - Next: full SSO resolution if demand shows up, and richer Action outputs
Trust track
- Differential testing: the same predicates over the same tables through a reference engine, asserting the survivor set covers every file with matching rows. The harness in
examples/differential(MinIO + Spark 4.1) runs a 29-predicate matrix over two tables - a syntheticuserstable and ataxitable written by Spark from real NYC TLC data - including normalized forms (De Morgan, factored ORs),LIKEin both its rewritten and partition-evaluated shapes (the latter checked against Spark's ownLIKEon real partition values), and null-safe comparisons: sound on all, exact on many. It consumes the JSONfiles[]contract instead of scraping text (the "validated against" documentation shipped indocs/validation.md). A scheduled weekly Validation workflow now runs the harness and an Azurite leg for the az:// path (a GCS emulator leg is not possible today: object_store has no emulator support for gs://, so GCS coverage stays a manual verification against a real bucket). Next: fixtures written by third-party engines (Trino, Flink).
Ongoing: the kernel track
delta-explain builds on delta-kernel-rs, so some improvements arrive by adopting a newer kernel rather than writing code here:
- Void schema type: tables containing a
voidcolumn are unreadable today; support landed on kernel main after 0.24 and ships with the next release. - thrift advisory (issue #9): drops out of the dependency tree once a kernel release moves to parquet 59+.
- Public partition-columns accessor: if the kernel exposes one, it replaces the checkpoint-only fallback and also covers the empty-table edge.
- Public accessor for system metadata domains:
delta.clusteringis read from the JSON commits today because the kernel rejects system domains on its public API; an accessor would also close the checkpoint-only clustering blind spot. - Data skipping on the kernel's binary
In: today it evaluates conservatively; if a kernel release adds skipping, our IN-as-OR expansion becomes an optimization choice instead of a necessity.
Future: Compare mode
- Same predicate across two tables (flat vs partitioned, before vs after compaction)
- Side-by-side output with delta highlighted
Library surface: the Python half is shipped
pip install delta-explain ships platform wheels containing the compiled CLI plus a thin Python module (explain() -> Report): a pruning check as a function call in a notebook or a pytest fixture, with gate failures as outcomes and runtime errors as exceptions - the CLI's exit-code contract in Python types. Deliberately a client of the CLI-and-JSON contract, not a second API: in-process PyO3 bindings (the polars model) and a stable Rust facade remain future work, taken up only if demand shows, and the explain() signature is designed to survive that swap unchanged.