Previous behavior: load_composition_groups() always re-expanded each matrix spec through execution-time binary validation. Rebuilding build/c/codebase-memory-mcp therefore made a completed campaign fail with candidates[3].binary_sha256 does not match, even though its immutable run records and inputs were intact.
Accept exactly one matrix_spec or plan source per composition campaign. Immutable plans pass through validate_plan() without reopening candidate executables, while matrix_spec retains strict live expansion and SHA validation. Record each source kind, absolute path, and SHA-256 in composition provenance.
Files: scripts/summarize-benchmark-results.py; tests/test_summarize_benchmark_results.py.
Verification: uv run python -m unittest tests.test_benchmark_incremental_speed tests.test_benchmark_campaign tests.test_summarize_benchmark_results (94 passed); bash scripts/check-source-safety.sh; git diff --check.
Signed-off-by: Andrew Hundt <ATHundt@gmail.com>