Residue batch on Cut 10's fourth fix batch: seal path_for, fill N1's gaps, pin the gate's other axis

S3: Mind::path_for bypassed the grammar door and the daemon's own tests
constructed stores through it directly. It is now private; a test-only
Mind::store_path_for validates through require_grammatical_slug first,
gated behind cfg(test) here and a test-support feature dev-dependents
opt into (huginn-daemon's Cargo.toml). The four daemon test call sites
and one huginn-mind test call site now go through the door.

S2: N1's door, require_grammatical_slug, had no mutation entry for two
of its three callers (open_with and require_instance survived deleting
it; open already had N1's own escape test). Added a test per caller and
a mutation entry for each of the three deletions (N1open, N1openwith,
N1requireinstance) in tools/eureka-cut10-mutations.psd1.

S4: a Unicode hyphen (U+2010) folded to ASCII '-' before the grammar
check survived. Added a fixture and mutation entry (S4fold) pinning
that no such fold runs before Slug::validate_slug.

S1: the size-gate fixtures varied operation and runtime id together
(4/16, 4/16, 10/19), so a formula tracking one and dropping the other
still passed. Added a fixture varying them independently (10/16 and
4/19) and two mutation entries (N4c, N4d) for the two formulas that
each drop one term.

Verified: cargo test -p huginn-mind --lib (65/65), cargo test -p
huginn-daemon --lib (20/20, one more than before: the new
independent-axis gate test). Mutation suites through the Eureka
harness: Cut 8 67/67 killed, Cut 9 38/38 killed, Cut 10 77/77 killed
(71 before this batch, +6: N1open, N1openwith, N1requireinstance,
S4fold, N4c, N4d). M0 green in every suite.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Meta Vens 2026-09-22 08:21:16 +02:00
commit 65c46aa59d
7 changed files with 255 additions and 7 deletions

View file

@ -30,3 +30,9 @@ signal-hook = "0.3"
[dev-dependencies]
tempfile = "3"
# Test-only: `Mind::store_path_for`, the grammar-checked door onto a raw
# store's path. The daemon's own store-integrity tests reach a store directly
# (to break a row on purpose) and must go through the same door `open` does
# rather than reconstructing the path by hand (S3, residue batch).
huginn-mind = { path = "../huginn-mind", features = ["test-support"] }

View file

@ -626,7 +626,7 @@ pub(crate) mod tests {
use huginn_mind::store::{CacheBackingStore, DatabaseEntry};
let root = tempfile::tempdir().unwrap();
let path = Mind::<OwnedRedbMessagePackBackingStore>::path_for(root.path(), &slug(INSTANCE));
let path = Mind::<OwnedRedbMessagePackBackingStore>::store_path_for(root.path(), &slug(INSTANCE)).unwrap();
{
let mut daemon = Daemon::open(root.path(), &slug(INSTANCE)).unwrap();
let outcome = daemon.handle(HuginnMindRequest::Admit(batch(INSTANCE, vec![identity(INSTANCE)])), now());
@ -678,7 +678,7 @@ pub(crate) mod tests {
assert_eq!(writes.len(), 1, "a cut spec alone derives nothing");
let cut_spec_id = writes[0].id.0.clone();
drop(daemon);
(cut_spec_id, OwnedRedbMessagePackBackingStore::new(&Mind::<OwnedRedbMessagePackBackingStore>::path_for(root, &slug(INSTANCE))).unwrap())
(cut_spec_id, OwnedRedbMessagePackBackingStore::new(&Mind::<OwnedRedbMessagePackBackingStore>::store_path_for(root, &slug(INSTANCE)).unwrap()).unwrap())
}
let cut_spec_type = PipelineKind::CutSpec.type_id();
@ -812,7 +812,7 @@ pub(crate) mod tests {
fn the_daemon_refuses_loudly_when_it_cannot_open_the_mind_and_binds_nothing() {
let root = tempfile::tempdir().unwrap();
let held = Daemon::open(root.path(), &slug(INSTANCE)).unwrap();
let path = Mind::<OwnedRedbMessagePackBackingStore>::path_for(root.path(), &slug(INSTANCE));
let path = Mind::<OwnedRedbMessagePackBackingStore>::store_path_for(root.path(), &slug(INSTANCE)).unwrap();
assert_eq!(
Daemon::open(root.path(), &slug(INSTANCE)).err(),
Some(MindRefusal::MindAlreadyOwned { path: path.display().to_string() })
@ -822,7 +822,7 @@ pub(crate) mod tests {
let mut planted = Daemon::open(root.path(), &slug(INSTANCE)).unwrap();
planted.handle(HuginnMindRequest::Admit(batch(INSTANCE, vec![identity(INSTANCE)])), now());
drop(planted);
let moved = Mind::<OwnedRedbMessagePackBackingStore>::path_for(root.path(), &slug(OTHER));
let moved = Mind::<OwnedRedbMessagePackBackingStore>::store_path_for(root.path(), &slug(OTHER)).unwrap();
std::fs::create_dir_all(moved.parent().unwrap()).unwrap();
std::fs::copy(&path, &moved).unwrap();
assert_eq!(

View file

@ -873,6 +873,37 @@ mod tests {
}
}
/// Residue S1: every fixture up to this one, including the pair above,
/// moves `operation` and the runtime id together -- both short (`view`,
/// 4 bytes / `huginn-yggdrasil`, 16) or both long (`open_items`, 10 /
/// `huginn-thought-cage`, 19) -- so a formula that tracks only one of the
/// two and ignores the other still lands on the right answer at both
/// points by coincidence. This pins a point where they move apart: a long
/// operation with the short runtime id, and a short operation with the
/// long one, so a gate that dropped either term is wrong at one of them.
#[test]
fn the_gates_boundary_holds_when_operation_and_runtime_id_vary_independently() {
for (operation, runtime_id) in [("open_items", "huginn-yggdrasil"), ("view", "huginn-thought-cage")] {
let at = sized_exactly_full("m-g", operation, runtime_id, MAX_RESPONSE_BYTES);
let over = sized_exactly_full("m-g", operation, runtime_id, MAX_RESPONSE_BYTES + 1);
assert_eq!(
within_window(at.clone(), "m-g", operation, runtime_id),
at,
"{operation}/{runtime_id}: exactly the limit passes"
);
let refused = within_window(over.clone(), "m-g", operation, runtime_id);
let (correlation, answered) = decode_response(&refused).unwrap();
assert_eq!(correlation, "m-g");
let HuginnMindResponse::Refused(MindRefusal::ResponseTooLarge { bytes, limit }) =
answered.expect("a refusal is an answer, not an envelope failure")
else {
panic!("{operation}/{runtime_id}: one byte over the limit was not refused: {refused:?}");
};
assert_eq!((bytes, limit), (MAX_RESPONSE_BYTES + 1, MAX_RESPONSE_BYTES), "{operation}/{runtime_id}");
}
}
/// One cut spec's own reference, by its cut label.
fn spec_ref(daemon: &mut Daemon<OwnedRedbMessagePackBackingStore, NoIndex>, cut: &str) -> PipelineRef {
let query = PipelineQuery {

View file

@ -23,3 +23,11 @@ sha2 = "0.10"
[dev-dependencies]
serde_json = "1"
tempfile = "3"
# `test-support` exposes `Mind::store_path_for`, a grammar-checked path
# accessor tests in other crates need to reach a raw store directly. It buys
# no runtime surface: gated `#[cfg(any(test, feature = "test-support"))]`, so
# it compiles only under `cargo test` in this crate or when a dependent's own
# dev-dependencies enable it (see huginn-daemon/Cargo.toml).
[features]
test-support = []

View file

@ -752,7 +752,10 @@ mod tests {
let root = tempfile::tempdir().unwrap();
let mut yggdrasil = Mind::open(root.path(), &slug(INSTANCE)).unwrap();
let mut thought_cage = Mind::open(root.path(), &slug(OTHER_INSTANCE)).unwrap();
assert_ne!(Mind::path_for(root.path(), &slug(INSTANCE)), Mind::path_for(root.path(), &slug(OTHER_INSTANCE)));
assert_ne!(
Mind::store_path_for(root.path(), &slug(INSTANCE)).unwrap(),
Mind::store_path_for(root.path(), &slug(OTHER_INSTANCE)).unwrap()
);
committed(admit(&mut yggdrasil, vec![instance(INSTANCE), stewardship(INSTANCE, REPO)]));
committed(admit(&mut thought_cage, vec![instance(OTHER_INSTANCE)]));
let key = format!("{INSTANCE}:stewardship:GameCult_-Epiphany.n1");

View file

@ -100,11 +100,26 @@ pub struct Mind<S: MindStore> {
impl Mind<OwnedRedbMessagePackBackingStore> {
/// `<state_root>/minds/<instance>/mind.redb`. Derived for convenience;
/// the `instance` document inside is the identity.
pub fn path_for(state_root: &Path, instance: &Slug) -> PathBuf {
/// the `instance` document inside is the identity. Private: every caller
/// that can reach a raw path must go through the grammar door first, so
/// this is never exposed on its own. `open` calls it only after
/// `require_grammatical_slug` has already passed.
fn path_for(state_root: &Path, instance: &Slug) -> PathBuf {
state_root.join("minds").join(&instance.0).join("mind.redb")
}
/// Test-only door onto the same path: validates through
/// `require_grammatical_slug` first, exactly as `open` does, so a test
/// that must reach the raw store directly (to break a row on purpose)
/// cannot bypass the grammar the way the old public `path_for` did. Built
/// only for `cfg(test)` in this crate and for `huginn-daemon`'s own tests
/// via the `test-support` feature; never compiled into a release binary.
#[cfg(any(test, feature = "test-support"))]
pub fn store_path_for(state_root: &Path, instance: &Slug) -> Result<PathBuf, MindRefusal> {
require_grammatical_slug("instance", instance)?;
Ok(Self::path_for(state_root, instance))
}
/// Opens the instance's mind under the state root, taking the store's
/// exclusive lock for the mind's lifetime. A second owner of the same
/// path, in this process or another, is `MindAlreadyOwned`. An empty
@ -592,4 +607,59 @@ mod tests {
assert!(!outer.path().join("escaped").join("mind.redb").exists(), "no store escaped the state root");
assert!(!inner.join("minds").exists(), "no store was created under the state root either");
}
/// S2: `open_with` is the door `open` shares with every store already in
/// hand (planted directly, or opened by a test through `store_path_for`),
/// so it must run `require_grammatical_slug` too, not only `open`'s own
/// path-joining caller. A store need not hold anything for this to
/// refuse: the declared name is checked before the store is even pulled.
#[test]
fn open_with_refuses_a_declared_instance_outside_the_grammar() {
let store = MemoryStore::new();
let bad = Slug("not a slug!".into());
assert_eq!(
Mind::open_with(store.clone(), &bad).err(),
Some(MindRefusal::Document(epiphany_pipeline::PipelineRefusal::InvalidFormat {
field: "instance".into(),
value: bad.0.clone(),
}))
);
assert_eq!(store.pull_count(), 0, "refused before the store was ever pulled");
}
/// S2: `require_instance` is A1 for admission and the daemon's reads
/// alike, and it runs the same grammar door on the *declared* name before
/// comparing it to the mind's own. A declared name outside the grammar is
/// `InvalidFormat { field: "declared" }`, never folded into a comparison
/// or silently treated as merely foreign.
#[test]
fn require_instance_refuses_a_declared_name_outside_the_grammar() {
let mind = crate::fixtures::seeded();
let bad = Slug("not a slug!".into());
assert_eq!(
mind.require_instance(&bad).err(),
Some(MindRefusal::Document(epiphany_pipeline::PipelineRefusal::InvalidFormat {
field: "declared".into(),
value: bad.0.clone(),
}))
);
}
/// S4: a Unicode hyphen (U+2010) is not ASCII `-`, so a grammatical
/// `Slug` never contains one and a name carrying it is refused by the
/// leaf's own grammar as written -- nothing here may fold it to plain
/// `-` before asking `Slug::validate_slug`, the way `S3wide`'s fullwidth
/// mutant folds a fullwidth letter to its ASCII form.
#[test]
fn a_unicode_hyphen_is_not_folded_before_the_grammar_check() {
let declared = Slug(format!("ab{}cd", '\u{2010}'));
assert!(!declared.0.is_ascii(), "U+2010 is not ASCII, unlike plain '-'");
assert_eq!(
Mind::open_with(MemoryStore::new(), &declared).err(),
Some(MindRefusal::Document(epiphany_pipeline::PipelineRefusal::InvalidFormat {
field: "instance".into(),
value: declared.0.clone(),
}))
);
}
}

View file

@ -232,6 +232,88 @@ fn require_grammatical_slug(field: &str, declared: &Slug) -> Result<(), MindRefu
})
})
}
'@
}
# Residue S2: N1's door, `require_grammatical_slug`, had no mutation
# entry for two of its three callers. `open`'s deletion is covered by
# N1's own escape test; `open_with`'s survived all 81 tests before
# this batch added one that names it; `require_instance`'s is covered
# the same way, since neither caller's test constructs an ungrammatical
# name the other way.
@{
Id = 'N1open'
Rule = 'N1: `open` checks the declared name''s grammar before it ever joins a path from it.'
Test = 'mind::tests::a_slug_outside_the_grammar_cannot_escape_the_state_root'
Command = 'cargo test -p huginn-mind --lib'
File = 'crates/huginn-mind/src/mind.rs'
Old = @'
pub fn open(state_root: &Path, instance: &Slug) -> Result<Self, MindRefusal> {
require_grammatical_slug("instance", instance)?;
let path = Self::path_for(state_root, instance);
'@
New = @'
pub fn open(state_root: &Path, instance: &Slug) -> Result<Self, MindRefusal> {
let path = Self::path_for(state_root, instance);
'@
}
@{
Id = 'N1openwith'
Rule = 'N1: `open_with` checks the declared name''s grammar too, for every store already in hand and not only for one `open` derived a path for.'
Test = 'mind::tests::open_with_refuses_a_declared_instance_outside_the_grammar'
Command = 'cargo test -p huginn-mind --lib'
File = 'crates/huginn-mind/src/mind.rs'
Old = @'
pub fn open_with(store: S, instance: &Slug) -> Result<Self, MindRefusal> {
require_grammatical_slug("instance", instance)?;
let raw = store.pull_all().map_err(unavailable)?;
'@
New = @'
pub fn open_with(store: S, instance: &Slug) -> Result<Self, MindRefusal> {
let raw = store.pull_all().map_err(unavailable)?;
'@
}
@{
Id = 'N1requireinstance'
Rule = 'N1: `require_instance` checks the declared name''s grammar before comparing it to the mind''s own, so an ungrammatical name is refused by name rather than merely being foreign.'
Test = 'mind::tests::require_instance_refuses_a_declared_name_outside_the_grammar'
Command = 'cargo test -p huginn-mind --lib'
File = 'crates/huginn-mind/src/mind.rs'
Old = @'
pub fn require_instance(&self, declared: &Slug) -> Result<(), MindRefusal> {
require_grammatical_slug("declared", declared)?;
if declared != self.instance() {
'@
New = @'
pub fn require_instance(&self, declared: &Slug) -> Result<(), MindRefusal> {
if declared != self.instance() {
'@
}
@{
Id = 'S4fold'
Rule = 'Residue S4: the grammar check runs on the declared bytes as given: nothing folds a Unicode hyphen (U+2010) to plain ASCII `-` before asking Slug::validate_slug whether the name is grammatical.'
Test = 'mind::tests::a_unicode_hyphen_is_not_folded_before_the_grammar_check'
Command = 'cargo test -p huginn-mind --lib'
File = 'crates/huginn-mind/src/mind.rs'
Old = @'
fn require_grammatical_slug(field: &str, declared: &Slug) -> Result<(), MindRefusal> {
declared.validate_slug().map_err(|_| {
MindRefusal::Document(epiphany_pipeline::PipelineRefusal::InvalidFormat {
field: field.into(),
value: declared.0.clone(),
})
})
}
'@
New = @'
fn require_grammatical_slug(field: &str, declared: &Slug) -> Result<(), MindRefusal> {
let folded = Slug(declared.0.chars().map(|c| if c == '\u{2010}' { '-' } else { c }).collect());
folded.validate_slug().map_err(|_| {
MindRefusal::Document(epiphany_pipeline::PipelineRefusal::InvalidFormat {
field: field.into(),
value: declared.0.clone(),
})
})
}
'@
}
@{
@ -451,6 +533,54 @@ fn require_grammatical_slug(field: &str, declared: &Slug) -> Result<(), MindRefu
}
_ => bytes.len() as u64,
},
'@
}
@{
Id = 'N4c'
Rule = 'Residue S1: what is measured holds for the operation and the runtime id independently, not merely for the two moving together as N4a and N4b probed them; a formula that adds a term for the operation''s length but drops the runtime id -- `228+id+(id>31)+(op.len()-4)*3/2` -- is wrong once a fixture holds the operation long and the runtime id short.'
Test = 'serve::tests::the_gates_boundary_holds_when_operation_and_runtime_id_vary_independently'
Command = 'cargo test -p huginn-daemon --lib'
File = 'crates/huginn-daemon/src/serve.rs'
Old = @'
let encoded = match encode_cultnet_message_to_vec(&reply, CultNetWireContract::CultNetSchemaV0) {
Ok(bytes) => bytes.len() as u64,
'@
New = @'
let encoded = match encode_cultnet_message_to_vec(&reply, CultNetWireContract::CultNetSchemaV0) {
Ok(bytes) => match &reply {
CultNetMessage::OperationResponse { payload, .. } => {
payload.len() as u64
+ 228
+ message_id.len() as u64
+ (message_id.len() > 31) as u64
+ (operation.len() as u64).saturating_sub(4) * 3 / 2
}
_ => bytes.len() as u64,
},
'@
}
@{
Id = 'N4d'
Rule = 'Residue S1: the same, the other way round; a formula that adds a term for the runtime id but drops the operation -- `228+id+(id>31)+3*(rt.len()-16)` -- is wrong once a fixture holds the runtime id long and the operation short.'
Test = 'serve::tests::the_gates_boundary_holds_when_operation_and_runtime_id_vary_independently'
Command = 'cargo test -p huginn-daemon --lib'
File = 'crates/huginn-daemon/src/serve.rs'
Old = @'
let encoded = match encode_cultnet_message_to_vec(&reply, CultNetWireContract::CultNetSchemaV0) {
Ok(bytes) => bytes.len() as u64,
'@
New = @'
let encoded = match encode_cultnet_message_to_vec(&reply, CultNetWireContract::CultNetSchemaV0) {
Ok(bytes) => match &reply {
CultNetMessage::OperationResponse { payload, .. } => {
payload.len() as u64
+ 228
+ message_id.len() as u64
+ (message_id.len() > 31) as u64
+ 3 * (runtime_id.len() as u64).saturating_sub(16)
}
_ => bytes.len() as u64,
},
'@
}
@{