Blitz persists task-state evidence under .blitz/test_state_v1 by default. Set
BLITZ_TEST_STATE_DIR or pass --store-dir to place it on a CI cache volume.
The default store is intentionally compact:
indexes/task_states.ndjson: latest result and latest passed result per task-state hashbaselines/current.json: the latest clean passing baseline for dirty workspace reusemanifests/<hash>.json: clean-pipeline pass manifests forrun_many!
The task-state index handles exact dirty-state memoization. The clean baseline lets a dirty workspace skip unimpacted project/task states that were proven by the latest clean run. Pipeline manifests allow a clean workspace to skip a previously passed multi-stage CI pipeline before rebuilding every project fingerprint.
Exact dirty-state memoization is separate from clean-baseline coverage. A first dirty run from a fresh clean baseline must use the clean baseline for unimpacted work; exact dirty-state skips are valid only after that dirty command has passed once.
Append-only audit streams are opt-in:
BLITZ_TEST_STATE_RETENTION=audit mix blitz.workspace.impact test
Audit retention writes:
results.ndjson: append-only command result recordscommits/<commit>.ndjson: per-commit result streams when a commit is knownindexes/by_task_state/<hash>.json: one JSON index per task state
Compact retention is the default for local .blitz stores because stale history
is not needed to answer whether the current exact state may skip.
What Gets Hashed
Each task-state hash includes:
workspace_state_hash: project paths, task config, isolation config, parallelism config, Elixir version, OTP version, and Blitz versionproject_task_hash: project files relevant to the task, local dependency state, and dependency closure where applicablecommand_hash: executable, args, cwd, and command environment hash
Project fingerprints include mix.exs and mix.lock content hashes. Blitz also
parses mix.lock as local dependency metadata when possible, so Hex and Git
lock entries contribute to dependency state. If parsing fails, the file content
hash still participates in task hashes.
Does mix.lock Suffice?
A mix.lock content hash is sufficient for Hex and pinned Git dependency
resolution state because it changes when Mix writes a different resolved
dependency entry. It also captures resolved versions and checksums for each
project that owns its own lockfile.
It is not sufficient by itself for path dependencies. Path dependencies can
change without changing the caller's mix.lock, so Blitz also fingerprints
local path dependency source state when the path resolves inside the workspace.
That source state participates in dependency-sensitive execution tasks such as
compile, test, dialyzer, and docs. deps_get uses a narrower dependency
declaration hash built from mix.exs declarations and path dependency mix.exs
state, so ordinary path dependency source edits do not refetch dependencies.
It is also not sufficient for unpinned Git dependencies unless the lockfile has already been refreshed to the exact commit that was tested. The tested state must therefore include the lockfile content hash, and CI should run from a checked-in or otherwise preserved lockfile. When a Git dependency has no lockfile entry, Blitz treats that dependency as unsafe for reuse and gives dependency-sensitive task states non-reusable evidence rather than allowing deterministic skips.
When State Is Written
Blitz writes state after command execution:
- passing commands update the task-state index and
latest_passed_result_id - failing commands replace the task-state index with a failed latest result
- successful
run_many!pipelines write a clean-pipeline manifest when the workspace is clean - successful clean
run_many!pipelines writebaselines/current.json - dry-runs do not write
A later command skips only when latest_passed_result_id is present for the
exact task state. A newer failed result for the same state removes skip
eligibility until that state passes again.
Compact pruning runs after successful execution. It removes default-stale
artifacts such as append streams and commit streams, keeps the current clean
baseline, and run_many! also prunes task-state indexes down to the current
pipeline surface. You can run the cleanup manually:
Workspace invalidator files are part of the workspace state fingerprint. The
default invalidators are root mix.exs,
build_support/dependency_resolver.exs, and
build_support/workspace_contract.exs, plus any downstream paths provided in
:workspace_invalidators. If one of these files changes, old exact task-state
coverage no longer matches and the configured impact policy can rerun the
workspace conservatively.
mix blitz.test_state.prune
mix blitz.test_state.prune --store-dir /tmp/blitz-test-state
Memoization Semantics
The store is memoization of verified command outcomes. It is not a replacement for Mix compilation artifacts, Dialyzer PLTs, dependency caches, or docs output.
The skipped unit is:
workspace + project + task + command + env + dependency state + relevant filesAny change to that unit creates a different hash. Reverting to a previously tested state can skip immediately because the exact passed hash is already in the store.
For dependency-affecting files such as mix.exs, Blitz uses the current
workspace graph to select owner plus reverse local dependents for
dependency-sensitive tasks. Owner-only tasks such as format and credo remain
limited to the changed project unless policy expands them.