lib

Core libraries for Radroots
git clone https://radroots.dev/git/lib.git
Log | Files | Refs | README

commit 29ed561227adba60c2b1428e472df9f0de931f87
parent 8c70b0f4f0e7307bcf3ebf61a4e81b2cfc5b219f
Author: triesap <tyson@radroots.org>
Date:   Tue, 28 Jul 2026 10:05:01 +0000

event_store: cover incremental reconciliation replay

- replay a newer addressable head after baseline
- ignore an older non-winning arrival
- reduce mixed deletion request references
- verify suppression transition and retraction authority

Diffstat:
Mcrates/event_store/src/nip09/reconciliation_v1.rs | 101+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 101 insertions(+), 0 deletions(-)

diff --git a/crates/event_store/src/nip09/reconciliation_v1.rs b/crates/event_store/src/nip09/reconciliation_v1.rs @@ -4689,6 +4689,107 @@ INSERT INTO radroots_event_store_owned_child_probe(id, parent_id) VALUES (1, 999 assert_request_index_matches_protocol(&target, &reversed, &RequestIndex::new(&reversed)); } + #[test] + fn expected_transition_history_replays_incremental_heads_and_requests() { + fn reconciled( + seq: i64, + observed_at_ms: i64, + envelope: RadrootsEventEnvelope, + ) -> ReconciledEvent { + let ingest = ingest_for_test(envelope, observed_at_ms); + let admission = EventAdmission::for_profile( + ReconciliationProfile::Nip09V1RegistryV7, + ingest.verified_event(), + ) + .expect("fixture admission"); + ReconciledEvent { + seq, + inserted_at_ms: observed_at_ms, + verified_event: ingest.verified_event().clone(), + admission, + } + } + + let author = fixture_author(); + let identifier = "incremental-history"; + let baseline = signed_event( + TARGET_CREATED_AT, + KIND_LIST_SET_RELAY, + vec![vec!["d".to_owned(), identifier.to_owned()]], + "{}", + ); + let replacement = signed_event( + TARGET_CREATED_AT + 10, + KIND_LIST_SET_RELAY, + vec![vec!["d".to_owned(), identifier.to_owned()]], + "{}", + ); + let replacement_id = replacement.id_str().to_owned(); + let older = signed_event( + TARGET_CREATED_AT - 1, + KIND_LIST_SET_RELAY, + vec![vec!["d".to_owned(), identifier.to_owned()]], + "{}", + ); + let replaceable = signed_event(TARGET_CREATED_AT + 20, 0, Vec::new(), "{}"); + let replaceable_id = replaceable.id_str().to_owned(); + let deletion = signed_event( + TARGET_CREATED_AT + 30, + KIND_DELETION_REQUEST, + vec![ + vec!["e".to_owned(), "0".repeat(64)], + vec!["e".to_owned(), replacement_id.clone()], + vec!["e".to_owned(), replaceable_id], + vec!["a".to_owned(), coordinate(author.as_str(), identifier)], + ], + "remove current head", + ); + let deletion_id = deletion.id_str().to_owned(); + let events = vec![ + reconciled(1, 1_000, baseline), + reconciled(2, 2_000, replacement), + reconciled(3, 3_000, older), + reconciled(4, 4_000, replaceable), + reconciled(5, 5_000, deletion), + ]; + let source = SourceState { + generation: RadrootsEventStoreSourceGeneration::from_bytes([0x71; 32]), + profile: ReconciliationProfile::Nip09V1RegistryV7, + raw_event_count: 5, + raw_tag_count: 7, + raw_high_water_seq: 5, + last_transition_seq: 3, + transition_floor_seq: 0, + baseline_raw_event_count: 1, + baseline_raw_tag_count: 1, + baseline_raw_high_water_seq: 1, + }; + + let transitions = + expected_transition_history(&source, &events).expect("incremental history"); + assert_eq!(transitions.len(), 3, "{transitions:#?}"); + assert_eq!(transitions[0].origin, "baseline"); + assert_eq!(transitions[0].raw_head_decision, "baseline_rebuild"); + assert_eq!(transitions[1].origin, "incremental"); + assert_eq!(transitions[1].raw_head_decision, "applied"); + assert_eq!(transitions[1].raw_head_event_id, replacement_id); + assert_eq!(transitions[2].origin, "incremental"); + assert_eq!(transitions[2].raw_head_decision, "not_head_selected"); + assert_eq!(transitions[2].visibility, "suppressed"); + assert_eq!( + transitions[2].event_reference_request_id.as_deref(), + Some(deletion_id.as_str()) + ); + assert_eq!( + transitions[2].address_reference_request_id.as_deref(), + Some(deletion_id.as_str()) + ); + assert_eq!( + transitions[2].retracted_event_id.as_deref(), + Some(replacement_id.as_str()) + ); + } + type RawEventRows = Vec<( i64, String,