ControlWriter::open refuses a store holding a record it does not read

The CultLib pin makes a pull carry unregistered records instead of refusing; the writer guards like the heartbeat store does.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
Eureka 2026-11-14 09:31:17 +00:00
commit 0c2d1e2cab
2 changed files with 21 additions and 1 deletions

View file

@ -36,7 +36,7 @@
use std::path::{Path, PathBuf};
use anyhow::{Context, Result};
use anyhow::{Context, Result, bail};
use chrono::{DateTime, Utc};
use cultcache_rs::{CultCache, CultCacheEnvelope, DatabaseEntry, SingleFileMessagePackBackingStore};
use eureka_pipeline::Slug;
@ -414,6 +414,10 @@ impl ControlWriter {
let mut cache = registered_cache()?;
cache.add_generic_backing_store(SingleFileMessagePackBackingStore::new(&path))?;
cache.pull_all_backing_stores()?;
// A pull carries a record no registered type claims instead of refusing it; the writer still must not write beside one.
if !cache.foreign_records().is_empty() {
bail!("{} holds a record this control store does not read: archive it, nothing migrates it", path.display());
}
Ok(Self { cache, path })
}

View file

@ -255,3 +255,19 @@ fn release(root: &Path, name: &str) {
ControlWriter::open(root.join(name).join("control.cc")).unwrap().set_brake(true, at(0), "op").unwrap();
release_swarm(root);
}
#[test]
fn a_writer_refuses_to_open_over_a_store_holding_a_record_it_does_not_read() {
let dir = tempfile::tempdir().unwrap();
let path = store_path(dir.path());
std::fs::create_dir_all(path.parent().unwrap()).unwrap();
let mut cache = CultCache::new();
cache.register_entry_type::<OldDial>().unwrap();
cache.add_generic_backing_store(SingleFileMessagePackBackingStore::new(&path)).unwrap();
cache.pull_all_backing_stores().unwrap();
cache.put("dial", &OldDial { heat: "1".into() }).unwrap();
let before = std::fs::read(&path).unwrap();
let refusal = ControlWriter::open(&path).err().expect("a foreign record refuses the open").to_string();
assert!(refusal.contains("does not read"), "{refusal}");
assert_eq!(std::fs::read(&path).unwrap(), before, "the refused open leaves the store bytes alone");
}