From 4126c83eff1a296c7e2a5bf88bcdb3be4b0b4330 Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Sat, 10 Oct 2026 22:14:01 +0200 Subject: [PATCH] ghostlight import-key: pin whole-record NFC and the admit_resolved duplicate backstop Tests only, no production change. Both build. A late NFD sequence and a late format character in a long lore-shaped path kill an is_nfc-on-prefix mutant; a resolved effect carrying two subjects with one key kills a fresh declared set in admit_resolved. Co-Authored-By: Claude Opus 5.5 --- crates/ghostlight/src/import_key_tests.rs | 41 +++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/crates/ghostlight/src/import_key_tests.rs b/crates/ghostlight/src/import_key_tests.rs index 89adbc3..ba5d86d 100644 --- a/crates/ghostlight/src/import_key_tests.rs +++ b/crates/ghostlight/src/import_key_tests.rs @@ -580,6 +580,9 @@ fn a_record_key_is_one_canonical_spelling() { "Del\u{a0}ta", "Del\u{2003}ta", "Del\nta", + // The violation sits deep in a long, real-shaped path, past any prefix. + "Worldbuilding/Pre-Elysium/Factions/Powers/Minor/Cafe\u{301}", + "Worldbuilding/Pre-Elysium/Factions/Powers/Minor/Ewan\u{200b}Hart", ]; for (index, record) in refused.iter().enumerate() { let set = rejected(submit(&mut kernel, "bad", record)); @@ -660,3 +663,41 @@ fn a_loaded_state_with_a_bad_import_key_is_refused() { "a key with a format character must not load" ); } + +/// The admission backstop, reached with an effect resolution never produced: a +/// resolved patch whose two subjects carry one key. The control is the same +/// effect with distinct keys, admitted by the same function. +#[test] +fn admitting_a_resolved_effect_refuses_two_subjects_with_one_key() { + let (_dir, kernel) = keyed_world(); + let place = commons(&kernel); + let resolve = || { + crate::patch::resolve_patch( + &kernel.state, + CommandId::new(), + &patch_of(vec![ + mirror("first", Some(key("aetheria.ship", "Beta")), place), + mirror("second", Some(key("aetheria.ship", "Gamma")), place), + ]), + None, + None, + ) + .expect("two distinct keys resolve") + }; + let resolves_to = crate::resolution_revision(&kernel.state).unwrap(); + let mut control = kernel.state.clone(); + crate::admit_resolved(&mut control, &resolve(), resolves_to) + .expect("distinct keys are admitted"); + + let mut twinned = resolve(); + assert_eq!(twinned.subjects.len(), 2); + twinned.subjects[1].subject.import_key = twinned.subjects[0].subject.import_key.clone(); + let mut state = kernel.state.clone(); + let error = crate::admit_resolved(&mut state, &twinned, resolves_to) + .expect_err("one key on two resolved subjects is refused"); + assert!( + matches!(&error, KernelError::Invariant(why) if why.contains("import key")), + "{error:?}" + ); + assert!(!format!("{error:?}").contains("Beta"), "the refusal echoed the key"); +}