Pin the reopened subject, the retaken repo, and six blind spots

Two tests for the rules the last commit landed.
`a_withdrawn_resolution_reopens_its_subject_and_stays_readable` withdraws a
closure, admits the next one at `n2`, reads the first back with its outcome
intact, refuses a third while `n2` stands, refuses the withdrawal of the
withdrawal, and has a ruling answer the reopened question -- the derived
resolution lands at `n2`, not `n1`.
`a_repo_is_stewarded_once_at_a_time_and_again_after_a_transfer` refuses a
second assignment while the first stands and a second claiming the taken
sequence, hands the repo away, finds nothing stewarding it, hands it back and
gets `n2` with the second `handed_on`, the first still readable by key.
Entries H40-H46 carry the mutants: the two sequence rules, the recursive
in-force, `AlreadyStewarded`, the Q19 cap, and `derive`'s two sequences.

Then the six places Soul found the code right and the suite blind, each a
fixture and an entry (H47-H51): a handed-away repo is not stewarded, so
`stewardship_of`'s in-force half is pinned; a document two batch documents
cite is pinned once rather than cancelled; what a derived write cites in the
image is pinned, which is H32's surviving half; a finding may cite the
vocabulary of the target landing beside it; `Answered` coherence is checked
against a ruling in the image as against one in the batch; a branch differing
only in case is a mismatch.

H32 is re-anchored rather than left to rot. Its A4 half is unreachable now: a
derived write takes the sequence after the batch's own records, so a derived
identity cannot collide with one the batch holds. What survives is that
derived writes are validated, staged and resolved like any other, and the
transfer test's strong read is what fails when they are not.

README stops claiming the two stub crates do anything.

H1-H51 all killed, M0 green. 33 tests, 0 warnings; `cargo check --workspace`
clean; `git diff --stat 4094e68 -- .voidbot` empty.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Meta Vens 2026-09-16 19:44:15 +02:00
commit fcb7fdd061
3 changed files with 306 additions and 9 deletions

View file

@ -29,8 +29,10 @@ edits `.cc` state. Huginn reads and writes minds.
A Cargo workspace of three crates:
- `crates/huginn-mind`: storage, identity, and admission of memory documents.
- `crates/huginn-daemon`: the CultNet surface and serve loop.
- `crates/eureka-state`: typed state for the Eureka pipeline.
- `crates/huginn-daemon`: a stub. It will carry the CultNet surface and serve
loop; today it holds neither.
- `crates/eureka-state`: a stub. It will carry typed state for the Eureka
pipeline; today it holds none.
`huginn-mind` is live: it opens one instance's store (an owned redb CultCache
at `<state_root>/minds/<instance>/mind.redb`, locked for the mind's lifetime),

View file

@ -1082,6 +1082,118 @@ mod tests {
);
}
/// Q17 B and Q19 A together: withdrawing a resolution reopens its subject
/// without erasing the record. The withdrawn closure is still readable by
/// key, the subject takes its next resolution at the next sequence, and
/// the chain stops at depth two -- a withdrawal cannot be withdrawn.
#[test]
fn a_withdrawn_resolution_reopens_its_subject_and_stays_readable() {
let mut mind = seeded();
committed(admit(&mut mind, vec![question("Q1", &["A", "B"], "A")]));
let subject = r(K::Question, &id("question", "Q1"));
let first = id("resolution", "question.Q1.n1");
committed(admit(&mut mind, vec![resolution(subject.clone(), withdrawn())]));
// The withdrawal of that closure: one nesting out, under the
// resolution it withdraws.
committed(admit(&mut mind, vec![resolution(r(K::Resolution, &first), withdrawn())]));
let withdrawal = id("resolution", "resolution.question.Q1.n1.n1");
assert!(mind.get(K::Resolution, &withdrawal).unwrap().is_some());
// The subject is open again, and its next record is `n2`. The first
// one is still there, outcome and all: this is a log, not an erasure.
committed(admit(&mut mind, vec![resolution_n(subject.clone(), 2, withdrawn())]));
let Some(D::Resolution(kept)) = mind.get(K::Resolution, &first).unwrap() else { panic!() };
assert_eq!(kept.outcome, withdrawn());
assert_eq!(kept.sequence, 1);
// `n2` stands, so a third is refused, and the withdrawal cannot
// itself be withdrawn to re-raise `n1` over it (Q19 A).
assert_eq!(
refusal(admit(&mut mind, vec![resolution_n(subject, 3, withdrawn())])),
MindRefusal::AlreadyResolved { subject: id("question", "Q1") }
);
assert_eq!(
refusal(admit(&mut mind, vec![resolution(r(K::Resolution, &withdrawal), withdrawn())])),
MindRefusal::IncompatibleResolution { subject_kind: K::Resolution, outcome: "Withdrawn".into() }
);
// And a ruling may answer a question whose first closure was
// withdrawn: the derived resolution takes the next sequence.
let mut mind = seeded();
committed(admit(&mut mind, vec![question("Q1", &["A", "B"], "A")]));
committed(admit(&mut mind, vec![resolution(r(K::Question, &id("question", "Q1")), withdrawn())]));
committed(admit(&mut mind, vec![resolution(r(K::Resolution, &first), withdrawn())]));
let mut answering = ruling("R1");
answering.answers = Some(s(&id("question", "Q1")));
answering.choice = Some(l("A"));
let (_, writes) = committed(admit(&mut mind, vec![D::Ruling(answering)]));
assert_eq!(writes, vec![
r(K::Ruling, &id("ruling", "R1")),
r(K::Resolution, &id("resolution", "question.Q1.n2")),
]);
}
/// Q18 A and Q20 A at admission: a repo is stewarded by one record at a
/// time on a mind, and a repo handed away and handed back is a second
/// record rather than a collision or an overwrite.
#[test]
fn a_repo_is_stewarded_once_at_a_time_and_again_after_a_transfer() {
let mut mind = seeded();
let first = format!("{INSTANCE}:stewardship:GameCult_-Epiphany.n1");
assert_eq!(
refusal(admit(&mut mind, vec![stewardship_n(INSTANCE, REPO, 2)])),
MindRefusal::AlreadyStewarded { repo: REPO.into() }
);
let D::Stewardship(mut retaken) = stewardship(INSTANCE, REPO) else { panic!() };
retaken.note = "assigned again".into();
assert_eq!(
refusal(admit(&mut mind, vec![D::Stewardship(retaken)])),
MindRefusal::StewardshipOutOfSequence { repo: REPO.into(), expected: 2, actual: 1 }
);
// Handing the repo away withdraws the assignment, and the withdrawal
// is a resolution of that record, sequence and all. The stewardship it
// reads to derive that is an image document, so it is pinned.
let (receipt_id, writes) = committed(admit(&mut mind, vec![hand_off(INSTANCE, OTHER_INSTANCE, REPO, &[])]));
let away = format!("{INSTANCE}:hand_off:{OTHER_INSTANCE}.GameCult_-Epiphany.{}", date().0);
assert_eq!(writes, vec![
r(K::HandOff, &away),
r(K::Resolution, &format!("{INSTANCE}:resolution:stewardship.GameCult_-Epiphany.n1.n1")),
]);
let receipt = mind.receipts().unwrap().into_iter().find(|receipt| receipt.receipt_id == receipt_id).unwrap();
let stewardship_envelope = mind.envelope(K::Stewardship, &first).unwrap().clone();
assert!(
receipt.strong_reads.contains(&DocumentVersion::from_envelope(&stewardship_envelope)),
"the derived withdrawal's own citation is pinned: {:?}",
receipt.strong_reads
);
// Nothing stewards the repo now, and `stewardship_of` says so: a new
// campaign over it is refused.
let D::Campaign(mut second) = campaign(&[REPO]) else { panic!() };
second.slug = slug("second");
assert_eq!(
refusal(admit(&mut mind, vec![D::Campaign(second)])),
MindRefusal::RepoNotStewarded { repo: REPO.into() }
);
// Handed back, the mind stewards it again, as `n2`. No field names a
// return: this is the same hand-off shape with the instances swapped.
let D::HandOff(mut back) = hand_off(OTHER_INSTANCE, INSTANCE, REPO, &[]) else { panic!() };
back.handed_on = epiphany_pipeline::Date("2026-09-17".into());
let (_, writes) = committed(admit(&mut mind, vec![D::HandOff(back)]));
let again = format!("{INSTANCE}:stewardship:GameCult_-Epiphany.n2");
assert_eq!(writes, vec![
r(K::HandOff, &format!("{OTHER_INSTANCE}:hand_off:{INSTANCE}.GameCult_-Epiphany.2026-09-17")),
r(K::Stewardship, &again),
]);
let Some(D::Stewardship(taken)) = mind.get(K::Stewardship, &again).unwrap() else { panic!() };
assert_eq!(taken.sequence, 2);
assert_eq!(taken.assigned_on, epiphany_pipeline::Date("2026-09-17".into()));
assert!(mind.envelope(K::Stewardship, &first).is_some(), "the first assignment is still readable by key");
}
/// A seeded mind with one document of every resolvable kind, and the
/// non-resolvable ones beside them.
fn world() -> Mind<MemoryStore> {
@ -1238,7 +1350,15 @@ mod tests {
answering.choice = Some(l("A"));
let wrong = resolution(r(K::Question, &id("question", "Q2")), ResolutionOutcome::Answered { by: r(K::Ruling, &id("ruling", "R1")) });
assert_eq!(
refusal(admit(&mut mind, vec![D::Ruling(answering), wrong])),
refusal(admit(&mut mind, vec![D::Ruling(answering.clone()), wrong.clone()])),
MindRefusal::IncompatibleResolution { subject_kind: K::Question, outcome: "Answered".into() }
);
// The coherence is checked whether the ruling is in the batch or in
// the image: a later batch citing the stored R1 is refused the same
// way, and the derived resolution of Q1 is not what saves it.
committed(admit(&mut mind, vec![D::Ruling(answering)]));
assert_eq!(
refusal(admit(&mut mind, vec![wrong])),
MindRefusal::IncompatibleResolution { subject_kind: K::Question, outcome: "Answered".into() }
);
}
@ -1306,6 +1426,17 @@ mod tests {
for envelope in &cited {
assert!(receipt.strong_reads.contains(&DocumentVersion::from_envelope(envelope)), "{}", envelope.key);
}
// Two batch documents citing one image document pin it once: the pins
// are a set, so a second citation neither duplicates the pin nor
// cancels it.
let mut twice = cut_spec("2", 1);
twice.rulings = vec![s(&id("ruling", "R1"))];
let D::Question(mut raised) = question("Q1", &["A", "B"], "A") else { panic!() };
raised.raised_in = Some(r(K::Ruling, &id("ruling", "R1")));
let (receipt_id, _) = committed(admit(&mut mind, vec![D::CutSpec(twice), D::Question(raised)]));
let receipt = mind.receipts().unwrap().into_iter().find(|receipt| receipt.receipt_id == receipt_id).unwrap();
assert_eq!(receipt.strong_reads, vec![DocumentVersion::from_envelope(&cited[0])], "cited twice, pinned once");
}
#[test]
@ -1351,6 +1482,11 @@ mod tests {
let mut report = cut_report("1", 1);
report.branch = s("another-branch");
assert_eq!(refusal(admit(&mut mind, vec![D::CutReport(report)])), MindRefusal::SpecMismatch { field: "branch".into() });
// A branch is compared as written. Git's refs are case-sensitive, and
// a report of `Codex/...` is a report of another branch or of none.
let mut report = cut_report("1", 1);
report.branch = s("CODEX/eureka-pipeline-state");
assert_eq!(refusal(admit(&mut mind, vec![D::CutReport(report)])), MindRefusal::SpecMismatch { field: "branch".into() });
let mut report = cut_report("1", 1);
report.repo = repo(OTHER_REPO);
assert_eq!(refusal(admit(&mut mind, vec![D::CutReport(report)])), MindRefusal::SpecMismatch { field: "repo".into() });
@ -1428,6 +1564,7 @@ mod tests {
unknown.invariants = vec![l("nope")];
assert_eq!(refusal(admit(&mut mind, vec![D::Finding(unknown)])), MindRefusal::UnknownInvariant { label: "nope".into() });
// A raw envelope without `range` cannot be built from the type, and
// it is the leaf's decode that refuses it, before any organ rule.
#[derive(serde::Serialize)]
@ -1469,10 +1606,20 @@ mod tests {
// The in-force target owns the invariant vocabulary. Supersede r1 with
// an r2 that drops its label, and the label is unknown again; r2's own
// label is what a finding may cite.
// label is what a finding may cite. The target is looked for in image
// and batch, like every other citation, so a finding may cite the
// vocabulary of the revision landing beside it: `other` is admitted in
// r2's own batch, before r2 is anywhere but here.
let mut fresh = finding("1", 1, "F4", FindingConfidence::Plausible);
fresh.invariants = vec![l("other")];
assert_eq!(
refusal(admit(&mut mind, vec![D::Finding(fresh.clone())])),
MindRefusal::UnknownInvariant { label: "other".into() }
);
committed(admit(&mut mind, vec![
target(2, &["other"]),
resolution(r(K::Target, &id("target", "r1")), superseded(&[r(K::Target, &id("target", "r2"))])),
D::Finding(fresh),
]));
assert_eq!(
refusal(admit(&mut mind, vec![D::Finding(finding("1", 1, "F2", FindingConfidence::Plausible))])),

View file

@ -1,6 +1,15 @@
# Cut 8 mutations, H1-H39: each entry restores one permissiveness the cut
# closed and names the test that must fail while it is applied. Run through
# Epiphany's harness from this repo:
# Cut 8 mutations, H1-H51: each entry restores one permissiveness the cut
# closed and names the test that must fail while it is applied. H40-H46 are
# the Cut 6d follow-up's rules (the two sequences, the recursive in-force, the
# single in-force stewardship, the Q19 cap, and the sequences `derive` sets);
# H47-H51 close the gaps Soul found where the code was right and the suite
# blind. Run through Epiphany's harness from this repo:
#
# H32 is re-anchored to the same follow-up: its A4 half is unreachable now
# that a derived write takes the sequence after the batch's own records, so a
# derived identity can no longer collide with one the batch holds. What it
# still pins is that derived writes are validated, staged and resolved like
# any other, which the transfer test's strong read shows.
#
# $env:CARGO_TARGET_DIR = 'C:\Users\Meta\.cargo-target-codex'
# powershell -File F:\Projects\Epiphany\tools\eureka-mutations.ps1 -Repo F:\Projects\Huginn `
@ -397,8 +406,8 @@ impl MindStore for OwnedRedbMessagePackBackingStore {
}
@{
Id = 'H32'
Rule = 'Derived writes pass A3-A7 like any other; a derived identity the batch already holds is A4''s refusal, not a store error.'
Test = 'admission::tests::a_hand_off_derives_this_minds_side_only'
Rule = 'Derived writes go through validation, A4 and A7 like any other: what a derived write cites in the image is pinned.'
Test = 'admission::tests::a_repo_is_stewarded_once_at_a_time_and_again_after_a_transfer'
File = 'crates/huginn-mind/src/admission.rs'
Old = @'
for document in derive(&docs, &mind) {
@ -486,5 +495,144 @@ impl MindStore for OwnedRedbMessagePackBackingStore {
'@
New = ' let _ = (ruling, subject_id);'
}
@{
Id = 'H40'
Rule = 'resolution: the sequence is the previous plus one over image and batch (Q17 B).'
Test = 'admission::tests::a_subject_with_a_resolution_in_force_refuses_another'
File = 'crates/huginn-mind/src/admission.rs'
Old = ' if resolution.sequence != expected {'
New = ' if false {'
}
@{
Id = 'H41'
Rule = 'In force is recursive: a withdrawn resolution no longer closes its subject.'
Test = 'admission::tests::a_withdrawn_resolution_reopens_its_subject_and_stays_readable'
File = 'crates/huginn-mind/src/admission.rs'
Old = @'
&& !own(resolution)
&& self.in_force(PipelineKind::Resolution, key)
'@
New = @'
&& !own(resolution)
&& { let _ = key; true }
'@
}
@{
Id = 'H42'
Rule = 'stewardship: the sequence is the previous plus one, as a resolution''s is (Q18 A, Q20 A).'
Test = 'admission::tests::a_repo_is_stewarded_once_at_a_time_and_again_after_a_transfer'
File = 'crates/huginn-mind/src/admission.rs'
Old = ' if stewardship.sequence != expected {'
New = ' if false {'
}
@{
Id = 'H43'
Rule = 'stewardship: at most one assignment of a repo is in force on a mind.'
Test = 'admission::tests::a_repo_is_stewarded_once_at_a_time_and_again_after_a_transfer'
File = 'crates/huginn-mind/src/admission.rs'
Old = @'
if docs.stewardships_of(mind, &stewardship.repo, None).iter().any(|(other, _)| *other != key) {
return Err(MindRefusal::AlreadyStewarded { repo: stewardship.repo.0.clone() });
}
'@
New = ''
}
@{
Id = 'H44'
Rule = 'Q19 A: a withdrawal cannot be withdrawn; the chain stops at depth two.'
Test = 'admission::tests::a_withdrawn_resolution_reopens_its_subject_and_stays_readable'
File = 'crates/huginn-mind/src/admission.rs'
Old = @'
if subject_kind == K::Resolution
&& let Some(D::Resolution(subject)) = docs.find(K::Resolution, subject_id)
&& subject.subject.kind == K::Resolution
{
return Err(incompatible());
}
'@
New = ''
}
@{
Id = 'H45'
Rule = 'derive: a derived resolution takes the subject''s next sequence, not always the first.'
Test = 'admission::tests::a_withdrawn_resolution_reopens_its_subject_and_stays_readable'
File = 'crates/huginn-mind/src/admission.rs'
Old = @'
sequence: docs.latest_resolution(&subject, None) + 1,
subject,
outcome: ResolutionOutcome::Answered
'@
New = @'
sequence: 1,
subject,
outcome: ResolutionOutcome::Answered
'@
}
@{
Id = 'H46'
Rule = 'derive: H45''s other half, a derived stewardship takes the repo''s next sequence on this mind.'
Test = 'admission::tests::a_repo_is_stewarded_once_at_a_time_and_again_after_a_transfer'
File = 'crates/huginn-mind/src/admission.rs'
Old = ' sequence: docs.latest_stewardship(mind, &hand_off.repo, None) + 1,'
New = ' sequence: 1,'
}
@{
Id = 'H47'
Rule = 'stewardship_of: only an in-force assignment counts, so a handed-away repo is not stewarded.'
Test = 'admission::tests::a_repo_is_stewarded_once_at_a_time_and_again_after_a_transfer'
File = 'crates/huginn-mind/src/admission.rs'
Old = @'
.filter(|(key, stewardship)| {
stewardship.instance == *mind
&& stewardship.repo == *repo
&& self.in_force_unless(PipelineKind::Stewardship, key, |resolution| {
matches!(&resolution.outcome, ResolutionOutcome::Withdrawn { reason } if Some(reason.0.as_str()) == hand_off_key)
})
})
'@
New = @'
.filter(|(_key, stewardship)| {
let _ = hand_off_key;
stewardship.instance == *mind && stewardship.repo == *repo
})
'@
}
@{
Id = 'H48'
Rule = 'A7: a document two batch documents cite is pinned once, not cancelled by the second citation.'
Test = 'admission::tests::every_cited_image_document_is_a_strong_read'
File = 'crates/huginn-mind/src/admission.rs'
Old = ' strong.insert((reference.kind.type_id().to_string(), reference.id));'
New = @'
let pin = (reference.kind.type_id().to_string(), reference.id);
if !strong.insert(pin.clone()) {
strong.remove(&pin);
}
'@
}
@{
Id = 'H49'
Rule = 'finding: the in-force target is looked for in image and batch, not the image alone.'
Test = 'admission::tests::a_finding_names_evidence_locations_and_known_invariants'
File = 'crates/huginn-mind/src/admission.rs'
Old = ' let target = docs.of_kind(K::Target).find_map(|(target_key, document)| match document {'
New = ' let target = docs.image.iter().map(|held| (held.key.as_str(), &held.document)).find_map(|(target_key, document)| match document {'
}
@{
Id = 'H50'
Rule = 'resolution: the Answered coherence check reads the ruling wherever it is, batch or image.'
Test = 'admission::tests::a_ruling_answering_a_question_derives_the_answered_resolution_atomically'
File = 'crates/huginn-mind/src/admission.rs'
Old = ' let Some(D::Ruling(ruling)) = docs.find(K::Ruling, &by.id.0) else { return Ok(()) };'
New = ' let Some(D::Ruling(ruling)) = docs.in_batch(K::Ruling, &by.id.0) else { return Ok(()) };'
}
@{
Id = 'H51'
Rule = 'cut_report: the branch is compared as written; git''s refs are case-sensitive.'
Test = 'admission::tests::a_cut_report_cites_an_in_force_spec_and_agrees_with_it'
File = 'crates/huginn-mind/src/admission.rs'
Old = ' if report.branch != spec.branch {'
New = ' if !report.branch.0.eq_ignore_ascii_case(&spec.branch.0) {'
}
)
}