use headwater_census::census;
use headwater_census::shelves::Taxonomy;
use headwater_census::walk::Corpus;
use headwater_check::paint::ColorMode;
use headwater_check::{Cache, Context, Date, Declared, Detail, Register, Run, Shape};
use headwater_graph::anchors::Resolvers;
use headwater_graph::declarations::Declarations;
use headwater_graph::{Config, Graph};
use std::path::{Path, PathBuf};
const LOCK: &str = "sha256:0000000000000000000000000000000000000000000000000000000000000000";
const RULES: &str = "sha256:rules-0000000000000000000000000000000000000000000000000000000000";
const RULES_AFTER_A_DROPPED_RULE: &str =
"sha256:rules-1111111111111111111111111111111111111111111111111111111111";
const TODAY: &str = "2026-08-12";
fn at(date: &str) -> Context {
Context::at(Date::parse(date).expect("a date"))
}
fn fixtures_dir() -> PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR")).join("fixtures")
}
fn corpus_for(case: &str) -> PathBuf {
let root = Path::new(env!("CARGO_TARGET_TMPDIR")).join(case);
match std::fs::remove_dir_all(&root) {
Ok(()) => {}
Err(error) if error.kind() == std::io::ErrorKind::NotFound => {}
Err(error) => panic!("{} will not clear: {error}", root.display()),
}
copy(&fixtures_dir().join("check"), &root.join("check"));
root
}
fn copy(from: &Path, to: &Path) {
std::fs::create_dir_all(to).expect("the copy directory");
for entry in std::fs::read_dir(from).expect("the fixture tree reads") {
let entry = entry.expect("an entry");
let target = to.join(entry.file_name());
match entry.file_type().expect("a file type").is_dir() {
true => copy(&entry.path(), &target),
false => {
std::fs::copy(entry.path(), target).expect("a fixture copies");
}
}
}
}
fn run_over(root: &Path, cache: &mut Cache) -> Run {
run_at(root, &at(TODAY), cache)
}
fn run_at(root: &Path, ctx: &Context, cache: &mut Cache) -> Run {
let source = std::fs::read_to_string(fixtures_dir().join("check.taxonomy.yml"))
.expect("the fixture taxonomy");
let loaded = headwater_yaml::load(&source).expect("it loads");
let declared = loaded.value.as_map().expect("a mapping");
let lock = headwater_hash::hex(source.as_bytes());
let corpus = Corpus::new(root, "check");
let taxonomy = Taxonomy::read(declared).expect("the taxonomy reads");
let declarations = Declarations::read(declared).expect("the declarations read");
let register = Register::read(declared).expect("the register reads");
let shape = Shape::read(declared).expect("the shape reads");
let taken = census::take(&corpus, &taxonomy);
let config = Config::default();
let graph = Graph::build(
&taken,
&declarations,
&Resolvers::over(&corpus),
&corpus,
&config,
);
headwater_check::run(
&taken,
&graph,
&Declared {
lock: &lock,
taxonomy: &taxonomy,
shape: &shape,
relations: &declarations,
config: &config,
register: ®ister,
observations: &headwater_check::Observations::empty(),
pin: None,
harvests: &[],
adoption: None,
source: "engine/crates/check/tests/cache.rs",
},
&headwater_check::claim::Claims::at(root),
ctx,
cache,
)
}
#[test]
fn a_cached_run_and_a_run_with_no_cache_write_the_same_report() {
let root = corpus_for("differential");
let without = run_over(&root, &mut Cache::disabled());
let mut cold = Cache::at(&root, LOCK, RULES);
let first = run_over(&root, &mut cold);
cold.write(&root).expect("the cache writes");
let mut warm = Cache::at(&root, LOCK, RULES);
let second = run_over(&root, &mut warm);
assert_eq!(
without.render(Detail::EveryInstance, ColorMode::Plain),
first.render(Detail::EveryInstance, ColorMode::Plain)
);
assert_eq!(
without.render(Detail::EveryInstance, ColorMode::Plain),
second.render(Detail::EveryInstance, ColorMode::Plain)
);
assert_eq!(without.cache.hits, 0, "a disabled cache served something");
assert_eq!(first.cache.hits, 0, "an empty cache served something");
assert!(first.cache.misses > 0, "{:?}", first.cache);
assert_eq!(
second.cache.hits, first.cache.misses,
"the warm run did not serve every verdict the cold run stored: {:?}",
second.cache
);
assert_eq!(second.cache.misses, 0, "{:?}", second.cache);
assert_eq!(without.cache.unkeyed, without.instances.len());
assert_eq!(
second.cache.unkeyed,
second
.instances
.iter()
.filter(|instance| !instance.ran())
.count()
);
assert!(second.cache.unkeyed > 0, "the fixture tree skips nothing");
assert!(Cache::path(&root).is_file(), "no cache file was written");
}
#[test]
fn an_edited_document_is_evaluated_again() {
const EDITED: &str = "check/evaluations/gamma.md";
let root = corpus_for("edited");
let mut cold = Cache::at(&root, LOCK, RULES);
let before = run_over(&root, &mut cold);
cold.write(&root).expect("the cache writes");
let path = root.join(EDITED);
let source = std::fs::read_to_string(&path).expect("the fixture reads");
let mut edited = String::new();
for line in source.lines().filter(|line| !line.starts_with("doc_type:")) {
edited.push_str(line);
edited.push('\n');
}
assert_ne!(source, edited, "the fixture no longer restates its kind");
std::fs::write(&path, &edited).expect("the copy writes");
let mut warm = Cache::at(&root, LOCK, RULES);
let after = run_over(&root, &mut warm);
assert_ne!(
before.render(Detail::EveryInstance, ColorMode::Plain),
after.render(Detail::EveryInstance, ColorMode::Plain),
"the edit changed nothing, so this test proves nothing"
);
assert_eq!(
after.render(Detail::EveryInstance, ColorMode::Plain),
run_over(&root, &mut Cache::disabled()).render(Detail::EveryInstance, ColorMode::Plain),
"the cached run reported a verdict over the document as it was"
);
let touched = after
.instances
.iter()
.filter(|instance| instance.ran() && instance.paths().contains(&EDITED))
.count();
assert!(touched > 1, "the edited document is read by one instance");
assert_eq!(after.cache.misses, touched, "{:?}", after.cache);
assert!(after.cache.hits > touched, "{:?}", after.cache);
}
#[test]
fn a_duplicate_settled_in_the_other_file_is_not_served_stale() {
const FIRST: &str = "check/spec/05-no-summary.md";
const SECOND: &str = "check/spec/06-retired.md";
let root = corpus_for("duplicate");
rewrite(
&root.join(SECOND),
"id: SPEC-FIX-retired",
"id: SPEC-FIX-no-summary",
);
let untouched = std::fs::read(root.join(SECOND)).expect("the second claimant");
let mut cold = Cache::at(&root, LOCK, RULES);
let planted = run_over(&root, &mut cold);
cold.write(&root).expect("the cache writes");
let claimed = |run: &Run| {
run.findings
.iter()
.filter(|finding| finding.rule == headwater_check::duplicate::RULE)
.count()
};
assert_eq!(claimed(&planted), 2, "one collision, one finding per file");
rewrite(
&root.join(FIRST),
"id: SPEC-FIX-no-summary",
"id: SPEC-FIX-summary-less",
);
assert_eq!(
untouched,
std::fs::read(root.join(SECOND)).expect("the second claimant"),
"the repair edited the file the finding was reported against, which is \
the case this test is not about"
);
let mut warm = Cache::at(&root, LOCK, RULES);
let settled = run_over(&root, &mut warm);
assert_eq!(
claimed(&settled),
0,
"the cache served a duplicate that the other claimant had already \
settled: {:#?}",
settled
.findings
.iter()
.filter(|finding| finding.rule == headwater_check::duplicate::RULE)
.collect::<Vec<_>>()
);
assert_eq!(
settled.render(Detail::EveryInstance, ColorMode::Plain),
run_over(&root, &mut Cache::disabled()).render(Detail::EveryInstance, ColorMode::Plain),
"the cached run reported a verdict over the corpus as it was"
);
assert!(settled.cache.hits > 0, "{:?}", settled.cache);
}
const GOVERNING: &str = "---
id: SPEC-FIX-governing
doc_type: design_spec
status: current
status_since: 2026-01-05
summary: One edge onto a path of the repository, which is a target outside the corpus.
relations:
governs:
- src/service.rs
---
# Governing
The `governs` entry names a path rather than an identifier, so its target is an
external anchor and the source tree is what decides whether it binds.
";
const GOVERNED: &str = "src/service.rs";
const GOVERNING_PATH: &str = "check/spec/18-governing.md";
#[test]
fn a_moved_anchor_target_is_not_served_from_the_entry_before_it() {
let root = corpus_for("anchor");
let governed = root.join(GOVERNED);
std::fs::create_dir_all(governed.parent().expect("the anchor has a directory"))
.expect("the anchor's directory");
std::fs::write(&governed, "// the file a `governs` edge names\n").expect("the anchor writes");
std::fs::write(root.join(GOVERNING_PATH), GOVERNING).expect("the document writes");
fn unresolved(run: &Run) -> Vec<&headwater_check::Finding> {
run.findings
.iter()
.filter(|finding| {
finding.rule == headwater_check::target::RULE && finding.path == GOVERNING_PATH
})
.collect()
}
let mut cold = Cache::at(&root, LOCK, RULES);
let bound = run_over(&root, &mut cold);
cold.write(&root).expect("the cache writes");
assert_eq!(
unresolved(&bound).len(),
0,
"the anchor did not bind on the first run, so this test proves nothing: {:#?}",
unresolved(&bound)
);
std::fs::remove_file(&governed).expect("the anchor's target goes");
let mut warm = Cache::at(&root, LOCK, RULES);
let gone = run_over(&root, &mut warm);
assert_eq!(
unresolved(&gone).len(),
1,
"the cache served a verdict about an anchor whose target is no longer there"
);
assert_eq!(
gone.render(Detail::EveryInstance, ColorMode::Plain),
run_over(&root, &mut Cache::disabled()).render(Detail::EveryInstance, ColorMode::Plain),
"the cached run reported a verdict over the tree as it was"
);
assert!(gone.cache.hits > 0, "{:?}", gone.cache);
assert_eq!(gone.cache.misses, 3, "{:?}", gone.cache);
assert_eq!(
gone.cache.unkeyed, bound.cache.unkeyed,
"an instance lost its key rather than changing it: {:?}",
gone.cache
);
}
fn rewrite(path: &Path, from: &str, to: &str) {
let source = std::fs::read_to_string(path).expect("the fixture reads");
let edited = source.replace(from, to);
assert_ne!(source, edited, "{} does not carry {from}", path.display());
std::fs::write(path, edited).expect("the copy writes");
}
#[test]
fn a_clock_that_moved_is_not_served_from_the_entry_before_it() {
let root = corpus_for("clock");
let mut cold = Cache::at(&root, LOCK, RULES);
let inside = run_at(&root, &at("2026-08-12"), &mut cold);
cold.write(&root).expect("the cache writes");
let mut warm = Cache::at(&root, LOCK, RULES);
let outside = run_at(&root, &at("2026-09-30"), &mut warm);
assert_ne!(
inside.render(Detail::EveryInstance, ColorMode::Plain),
outside.render(Detail::EveryInstance, ColorMode::Plain),
"the window did not close, so this test proves nothing"
);
assert_eq!(
outside.render(Detail::EveryInstance, ColorMode::Plain),
run_at(&root, &at("2026-09-30"), &mut Cache::disabled())
.render(Detail::EveryInstance, ColorMode::Plain),
"the cached run served a verdict reached on another day"
);
assert!(outside.cache.hits > 0, "{:?}", outside.cache);
assert_eq!(
outside.cache.misses,
outside
.instances
.iter()
.filter(
|instance| instance.rule == headwater_check::participation::RULE && instance.ran()
)
.count(),
"{:?}",
outside.cache
);
}
#[test]
fn a_lock_that_moved_serves_nothing() {
let root = corpus_for("relocked");
let mut cold = Cache::at(&root, LOCK, RULES);
run_over(&root, &mut cold);
cold.write(&root).expect("the cache writes");
let mut relocked = Cache::at(&root, "sha256:something-else", RULES);
let after = run_over(&root, &mut relocked);
assert_eq!(after.cache.hits, 0, "{:?}", after.cache);
assert!(after.cache.misses > 0, "{:?}", after.cache);
relocked.write(&root).expect("the cache writes");
let text = std::fs::read_to_string(Cache::path(&root)).expect("the cache reads");
assert_eq!(text.lines().count(), after.cache.misses + 1, "{text}");
}
#[test]
fn an_engine_upgrade_that_drops_a_rule_serves_nothing() {
let root = corpus_for("upgraded");
let mut cold = Cache::at(&root, LOCK, RULES);
run_over(&root, &mut cold);
cold.write(&root).expect("the cache writes");
let mut upgraded = Cache::at(&root, LOCK, RULES_AFTER_A_DROPPED_RULE);
let after = run_over(&root, &mut upgraded);
assert_eq!(after.cache.hits, 0, "{:?}", after.cache);
assert!(after.cache.misses > 0, "{:?}", after.cache);
}
#[test]
fn a_damaged_cache_file_costs_one_run_and_nothing_else() {
let root = corpus_for("damaged");
let mut cold = Cache::at(&root, LOCK, RULES);
let expected = run_over(&root, &mut cold);
cold.write(&root).expect("the cache writes");
for damage in ["", "headwater check cache 99\n", "not a cache at all\n"] {
std::fs::write(Cache::path(&root), damage).expect("the cache writes");
let mut cache = Cache::at(&root, LOCK, RULES);
let run = run_over(&root, &mut cache);
assert_eq!(
expected.render(Detail::EveryInstance, ColorMode::Plain),
run.render(Detail::EveryInstance, ColorMode::Plain)
);
assert_eq!(run.cache.hits, 0, "{damage:?} served an entry");
}
}