Skip to content

Development and command reference

Use GNU Make from the repository root. Rust 1.96.1 and the matching formatter, Clippy, and WASM targets are pinned in rust-toolchain.toml. make lint also uses the pinned nightly-2026-07-16 Clippy toolchain to check coverage-only code paths; install that nightly with its clippy component before running the full lint command. Documentation uses Python 3.12.10 in CI and hash-locked MkDocs dependencies.

On hosts without the x86-64 Linux target installed, including ARM hosts, prepare the target before running make lint-x86-simd:

rustup target add x86_64-unknown-linux-gnu --toolchain 1.96.1

Common commands

Task Command Effect
Help make help Lists contributor commands without running Cargo metadata
Build make build Compiles the locked default-feature crate
Format / fix make fmt / make fmt-fix Checks / rewrites Rust formatting
Contracts make verify Legal, fixture, capability, roadmap, coverage-origin, and release-tool checks
Lint make lint Rust formatting plus strict debug/release, coverage-hook, and JPEG benchmark Clippy
Tests make test Rustdoc, all-feature tests, strict WASI Clippy, and feature/target lanes
Package example make example Executes the self-contained public PNG example
Coverage make coverage Measures the full source denominator and enforces alpha floors
Selected coverage make coverage-selected COVERAGE_CASES='decode:webp:<id>' Runs exact matrix cases and retains the report, command log, and local execution evidence outside target/
Parity coverage make coverage-parity Runs every active decode/encode parity row and reports planned rows without running unit tests or fault-contract rows
Full matrix coverage make coverage-matrix Runs active decode/encode parity and fault-contract rows, reports planned rows, and does not run ordinary unit tests
Complete coverage make coverage-complete Requires all four metrics to reach 100%
Package inspection make package-verify Builds, extracts, and tests the distributable archive
Documentation setup make docs-setup Creates an isolated environment and installs the hashed lock
Site build / preview make docs-build / make docs-serve Builds checked HTML / serves localhost:8000
JPEG oracle setup make bench-setup Downloads, verifies, and builds pinned TurboJPEG under target
JPEG comparison make bench Runs all 20 configurations for encode and decode, five rounds each
Full local CI make ci Runs validation, tests, coverage, and packaging

Setup and initial toolchain/dependency builds may need network access. Documentation builds do not execute benchmarks. Stop the local preview with Ctrl-C. Benchmark output directories must be new or empty; use BENCH_OUTPUT=target/benchmarks/jpeg/run-2 for a repeat.

Parity inputs and outputs

The maintained manifest, complete encoded inputs, and pinned Pillow oracle define observable behavior. Generate references using the documented canonical macOS ARM64 environment. Compare formats, modes, metadata, frames, pixels, palettes, deterministic output bytes, and structured error behavior where the contract requires them.

Do not replace exact comparisons with prefix checks, dimensions, or hashes chosen to match the Rust output. Add the corresponding public input when changing codec behavior. Preserve planned and not-applicable outcomes.

tests/fixtures/coverage_matrix.json also keeps an append-only mode_decode_cases index, with at least one active Pillow-backed decode row for each format/header-mode/decoded-mode pair. Add extra representative rows when distinct source layouts normalize to the same public mode pair, such as RGB-carrier TIFF layouts. Header and decoded-transfer modes are independent expectations and may differ when Pillow reports different modes for the source header and pixel transfer. A case can set frame_index to represent a decoded animation or multipage frame; without it, the case uses the row's ordinary decoded image. Exact Pillow-backed sequence frame modes also contribute to the required pair set. When a decoder adds a new pair or a carrier alias needs an explicit row, append it to that index. The parity harness checks both identities independently, that indexed rows remain active, and that every successful mode pair is listed. Every indexed decode row reruns token-aware still decode and its max-pixel token-policy path. Both must preserve the exact Pillow result and ordinary transfer identity, alongside owned and borrowed encoded-source header inspection. Indexed sequence-frame cases rerun token-aware, work-budget, and max-pixel token-policy sequence decode, checking every exact frame's decoded transfer mode and raster identity. GIF frame references can be rendered RGB while the public sequence retains indexed source samples; the harness checks the retained indexed identity and compares the separately rendered RGB pixels exactly.

The matrix also keeps an append-only mode_encode_cases index with one active Pillow-reference-backed encoder row for each format/source-mode/decoded-mode pair. When an encoder adds a new pair, append a representative row; the parity harness checks that each listed case retains its byte and pixel references and that no successful mode pair is omitted. Every indexed encode case also checks its encoded header and decoded-transfer identity, even when other cases in the same format share the internal raster mode.

Successful encode rows also exercise append-only sink delivery. The harness starts with a sentinel prefix and leaves OutputSink::checkpoint() at its default None; it checks that the prefix remains intact, appended bytes match both the Pillow byte reference and the in-memory encoder, and the reported byte count equals the appended length. One-frame TIFF rows use the sequence sink API; other one-frame rows use the still-image API, and multi-frame rows use the sequence API.

Some oracle outputs are checked in because clean CI source checkouts consume them. They are reproducible through maintained generators, but regeneration requires the exact source assets and pinned oracle. In particular, AV1's index and five sidecars form one input set. Do not delete them just because they are generated. AVIF provenance describes how to regenerate them.

Coverage and focused work

Use Coverage MCP for source observations with the exact selected fixture IDs and source revision. Report all four aggregate totals (lines, branches, functions, and regions). Selected-case reports do not establish full coverage, and missing observations are not automatically regressions.

Successful still-image AVIF decode parity rows also require inspect, inspect_basic, and inspect_with_policy to agree. They compare source and color metadata through the public accessors against decode output, including normalized auxiliary-item relationships. Sequence metadata has separate parity coverage because its decoded frame source can include sequence-only item data.

For focused exploration, pass one or more exact manifest keys to make coverage-selected, separated by spaces:

make coverage-selected COVERAGE_CASES='decode:webp:lossy_vp8 encode:png:enc_rgb'

Use make coverage-parity to execute the complete active decode/encode matrix through the parity harness. Planned rows are retained in the report as not executed; fault-contract rows and ordinary unit tests are excluded. The command also retains its LLVM report, log, and local evidence receipt under .coverage-mcp/local-evidence/selected/, using the same source and fixture identity checks as focused selected coverage.

Use make coverage-matrix to include all declared fault-contract rows with the decode/encode parity matrix in one retained coverage report. Planned rows remain reported as not executed, and ordinary unit tests are not run. Fault-contract rows exercise deterministic target-only failures and are reported separately from Pillow parity results within the retained evidence.

Full coverage uses target/cargo-llvm-cov/full; selected coverage uses target/cargo-llvm-cov/selected. Each is isolated from ordinary and feature-matrix builds. Full runs use Cargo's host layout and selected runs pass an explicit --target; sharing their cache lets LLVM-Cov collect old executables from both layouts and change the reported denominator. Existing cache contents are retained. Set COVERAGE_TARGET_DIR to relocate their parent, or set COVERAGE_FULL_TARGET_DIR and COVERAGE_SELECTED_TARGET_DIR individually. Keep each cache dedicated to its invocation layout. Direct script calls default to the selected cache and honor an explicit CARGO_TARGET_DIR. Local receipts record the resolved Cargo output directory.

The runner rejects malformed, duplicate, unknown, and planned-only selections, then delegates canonical validation and execution to the existing Rust matrix harness. By default it writes the LLVM report, captured command log, and local evidence receipt together under a unique .coverage-mcp/local-evidence/selected/<run-id>/ directory. It prints each repository-relative path when the run finishes. These artifacts are outside target/, so target cleanup leaves them in place. They are local, ignored evidence; keep a separate copy if you need them beyond this checkout's lifetime.

Set SELECTED_COVERAGE_REPORT to choose another report path. The report, log, and receipt remain adjacent. The runner accepts paths inside the repository, rejects paths that resolve outside it, and refuses to overwrite an existing report, log, or receipt. The receipt records the Git revision and worktree state, relevant source and fixture hashes, toolchain, target, feature set, exact command arguments, per-case results, and all four report totals. It is local evidence, not a registered Coverage MCP source/build receipt.

After a focused run, pass its printed repository-relative report path to Coverage MCP coverage_gaps and record the returned status and limitations. The current interface exposes coverage_gaps and coverage_compare; it has no local CLI bridge or action to register the receipt. MCP may therefore report source=unverified and tests=unknown. Use coverage_compare with scope selected_tests to compare two selected reports; this reports observed coordinates without combining their totals. Use scope incremental only when the selected report and a resolvable full exploration baseline have compatible source/build identity, instrumentation, denominator, and known test results. If the baseline is missing, stale, or incompatible, record that limitation without claiming aggregate gain or union. Selected reports do not enforce full-suite floors. This command stays opt-in and is not part of push, release, or full-coverage CI jobs.

Run make coverage occasionally to refresh a full exploration baseline, not for every selected increment. Give that report its own directory outside target/:

mkdir -p .coverage-mcp/local-evidence/full
run_dir="$(mktemp -d .coverage-mcp/local-evidence/full/run.XXXXXX)"
COVERAGE_REPORT="$run_dir/coverage.json" make coverage

This full run still measures the complete source denominator and enforces the configured alpha floors. It is not required for each focused change.

The alpha floors are 59% lines, 46% branches, 52% functions, and 58% regions. No source exclusions are added to reach those floors. Complete coverage retains 100%; every executed test must pass. The evidence guide preserves the distinction between old claim-ledger measurements and the release report.

Optional native coverage exporter

The pinned Rust LLVM coverage reader can select a hash-zero unused mapping before a real mapping with the same function name. Historical exports of the same profile and object set depended on object order. The optional native patch recognizes complete unused mappings, prefers real mappings even with zero executions, and retains unmatched unused source regions. It changes mapping selection in LLVM itself; it does not rewrite report counters or omit sources.

The supported setup recipe is native macOS ARM64 with nightly-2026-07-16 (Rust commit d0babd8b6b05ef9bb65d42f928cef4129d64cf65, LLVM 22.1.8). Independent source review and native builds passed on that host. A fresh seven-executable campaign retained all 110 repository sources and produced identical JSON and LCOV for 18 order labels (17 distinct object orders). All 64 restored positive functions matched their exact merged-profile tuples; 397 unused-only functions stayed at zero. A genuine raw-profile subset also preserved real zero-count branch mappings with two and seven branches. Positive foreign-hash unmatched-unused retention has source-policy proof only: no natural runtime control occurred. The descriptor records conditional admission for this pinned native platform. A separately bound maintained v3 recipe built from the prepared source, passed real no-op/concurrent reuse, and matched the audited native exports in all 17 distinct object orders. The integrated canonical coverage command also passed all executed tests and the alpha floors. Construction and command checks are recorded separately in the evidence guide; the descriptor records the producer pin and its admission scope. The first-source-fetch path remains source-reviewed and unexercised. This is an opt-in contributor tool; another platform and an upstream fixed release have not been verified.

Install Git, CMake, Ninja and the pinned Rust toolchain with llvm-tools-preview through the normal contributor setup, then run:

make coverage-exporter-setup
make coverage-with-exporter
make coverage-with-exporter COVERAGE_EXPORTER_TARGET=coverage-complete

The first setup fetches the exact Rust LLVM source commit, checks the source and patch hashes, and builds only llvm-cov with two jobs. LLVM is governed by its upstream license. Rust runtime dependencies and installed toolchain files remain unchanged. COVERAGE_EXPORTER_SOURCE=/absolute/path/to/pinned/llvm-project reuses a prepared clean checkout or the exact reviewed patched checkout without another download. COVERAGE_EXPORTER_PREFIX=/absolute/path/to/exporter selects an isolated build directory; its default is under the removable target/ tree. Setup verifies the descriptor, source, native executable, build configuration and coupled profile tool on every reuse. Changed or stale bindings fail.

The explicit coverage target uses the documented LLVM_COV and LLVM_PROFDATA overrides only for its recursive Make invocation. Conflicting caller overrides, custom LLVM flags and unsupported toolchain or cross-target settings are rejected. The setup also rejects custom native build flags or a CMake toolchain file instead of changing caller settings. The optional alias accepts report names with spaces or apostrophes. It rejects dollar signs, backticks, double quotes, backslashes and CR/LF before running a coverage command: the unchanged inner Make recipes cannot represent those characters literally. Normal make coverage keeps the normal Cargo command, alpha floors, complete source denominator and test-failure policy. The strict command still requires 100% for all four metrics. Run setup separately before using the explicit target; a missing or invalid receipt never triggers a silent download.

Submit a change

Run the narrow failing lane first, then the relevant full contract. Explain the first observed divergence and how the pinned oracle behaves. Keep durable implementation nuance beside the relevant source, with public guidance here when it changes contributor procedure. Follow Contributing.

Run make test-feature-matrix to repeat the complete native, WASI, and WASM feature lanes independently.