Blitz impact CI is a workspace execution layer that answers two questions before running a command:

  1. Has this exact task state already passed?
  2. If the workspace is dirty, is this project/task still covered by the latest clean baseline because the current diff does not impact it?

The task state combines:

  • workspace project composition
  • the selected Mix task and command arguments
  • command executable, cwd, and environment
  • project file fingerprints
  • local dependency fingerprints
  • mix.exs and mix.lock content hashes
  • Elixir, OTP, and Blitz versions

If the exact task state has a latest passed result, Blitz skips the command. If the state is new, failed most recently, or --force is used, Blitz runs it and persists the result.

Multi-stage callers should use Blitz.MixWorkspace.Impact.run_many!/3. It builds the workspace snapshot once, applies each requested task against that snapshot, and writes a clean baseline plus a clean-pipeline manifest after a successful clean run. A later identical clean run can skip from that manifest before project fingerprinting, which is the fastest path for repeated mix ci checks. Dirty runs use the clean baseline to skip unimpacted project/task pairs.

Commands

Run a single configured workspace task through the impact planner:

mix blitz.workspace.impact test
mix blitz.workspace.impact compile --dry-run
mix blitz.workspace.impact docs --base main --head HEAD
mix blitz.workspace.impact test --force
mix blitz.test_state.prune

Task arguments belong after --:

mix blitz.workspace.impact test --dry-run -- --seed 0

Decisions

Each planned command receives one decision:

  • run: no passed result exists for the exact task state, or the file change impacts the task and no exact passed result exists.
  • skip: an exact passed task-state index exists, or the latest clean baseline covers an unimpacted dirty project/task state.
  • force_run: --force was requested.

Decision maps include coverage_source so callers and tests can distinguish :exact_task_state, :clean_baseline, :impacted, :force, and :missing_clean_baseline without parsing dry-run output.

Dry-runs print every decision and a selected/skipped/total summary without writing the store. Each line includes the machine-readable coverage source and whether a matching clean baseline was available:

Blitz impact dry-run
skip test apps/core 8df1c94a915a source=clean_baseline baseline=true clean baseline passed; not impacted
run docs apps/web f64f661313f2 source=impacted baseline=true changed apps/web/README.md
Blitz impact summary: selected=1 skipped=1 total=2

Impact Rules

Git changes decide which tasks are considered impacted. Task-state hashes decide whether impacted work has already been proven.

Current file classification:

  • mix.exs: all known tasks
  • mix.lock: compile, test, dialyzer, and docs; deps_get is not selected only because lockfile output changed
  • Markdown, docs/, or guides/: docs
  • .formatter.exs: format
  • .credo.exs: credo
  • files under test/: format, test, credo
  • .ex or .exs: format, compile, test, credo, dialyzer, docs
  • unknown files: all known tasks

Dependency-affecting source changes expand through reverse local dependency closure for compile, test, dialyzer, and docs. deps_get uses dependency declaration state instead, so it stays skipped for source-only edits and reruns when mix.exs or dependency declarations change.

Workspace invalidators are treated conservatively. By default, root mix.exs, build_support/dependency_resolver.exs, and build_support/workspace_contract.exs select every configured project/task when changed. These files are also fingerprinted into the workspace task state, so a previously passed exact state cannot hide a changed resolver or workspace contract.

Seven-Stage CI

Blitz does not hardcode a universal CI pipeline. A downstream repo wires its own pipeline by calling the impact runner for the task families it cares about. A typical Mix monorepo pipeline is:

[
  {:deps_get, []},
  {:format, ["--check-formatted"]},
  {:compile, []},
  {:test, []},
  {:credo, ["--strict"]},
  {:dialyzer, []},
  {:docs, []}
]

Downstream aliases can keep mix ci as the public command while delegating to a repo-local task that invokes Blitz.MixWorkspace.Impact.run_many!/3.