minerva 0.2.0

Causal ordering for distributed systems
//! Retirement-evidence and orphan-classification counterexamples.

extern crate alloc;

use alloc::vec;
use alloc::vec::Vec;
use core::num::NonZeroUsize;

use crate::metis::tests::support::dot as d;

use super::super::fabric::Fabric;
use super::super::replica::Replica;
use super::super::{Note, act, fleet_of};

/// A retirement acknowledgement counts only when its carried cut is
/// delivered.
///
/// The honest meet ("every member has applied this removal") is not enough
/// under a reordering fabric: a write that anchors through the removed
/// element is minted while the element is still visible at its writer, so
/// it precedes that writer's acknowledgement causally, but the
/// acknowledgement note can outrun the weave in flight. A receiver that
/// counted it early would excise a tombstone whose anchoring child it has
/// not yet delivered, and the child would arrive dangling there while it
/// places everywhere else: divergence. The gate (count an acknowledgement
/// only once the acker's floor is delivered here) makes the hazard
/// unreachable, because a writer's own mints are always inside its own
/// floor.
#[test]
fn a_retirement_acknowledgement_counts_only_when_its_cut_is_delivered() {
    const ROSTER: [u32; 3] = [1, 2, 3];
    let depth = NonZeroUsize::new(2).expect("positive");
    let mut one = Replica::new(1, &ROSTER, depth);
    let mut two = Replica::new(2, &ROSTER, depth);
    let mut three = Replica::new(3, &ROSTER, depth);

    // Station 1 weaves T; everyone delivers it.
    let mut outbox_one = Vec::new();
    let _ = one.insert_visible(0, &mut outbox_one);
    let mut round = outbox_one;
    while !round.is_empty() {
        let mut next = Vec::new();
        for note in &round {
            for replica in [&mut one, &mut two, &mut three] {
                let mut out = Vec::new();
                replica.handle(note, &mut out);
                next.extend(out);
            }
        }
        round = next;
    }

    // Station 2 weaves X anchored through T (T is visible at 2), then
    // deletes T. The weave reaches station 1 only; the delete, the reports,
    // and every acknowledgement flood everywhere.
    let mut outbox_two = Vec::new();
    let _ = two.insert_visible(1, &mut outbox_two);
    let weave_x = outbox_two
        .iter()
        .find(|note| matches!(note, Note::Old { .. }))
        .cloned()
        .expect("the weave note");
    let mut relay = Vec::new();
    one.handle(&weave_x, &mut relay);

    let mut outbox_delete = Vec::new();
    let deleted = two.delete_visible(0, &mut outbox_delete);
    assert_eq!(deleted, Some(d(1, 1)), "station 2 deletes T");

    let mut round: Vec<Note> = Vec::new();
    round.extend(
        outbox_two
            .iter()
            .filter(|note| !matches!(note, Note::Old { .. }))
            .cloned(),
    );
    round.extend(relay);
    round.extend(outbox_delete);
    while !round.is_empty() {
        let mut next = Vec::new();
        for note in &round {
            for replica in [&mut one, &mut two, &mut three] {
                let mut out = Vec::new();
                replica.handle(note, &mut out);
                next.extend(out);
            }
        }
        round = next;
    }

    // Station 3 holds acknowledgements from 1 and 2 whose cuts cover the
    // undelivered X, so they are parked and its meet stays bottom: nothing
    // excises. Without the gate, the meet would cover T, T is sterile at 3
    // (X is absent), and 3 would excise the tombstone X still anchors.
    assert_eq!(
        three.condense(),
        0,
        "parked acknowledgements withhold the witness"
    );
    // Station 1 holds every acknowledgement, but X anchors T there:
    // sterility keeps the tombstone regardless of the witness.
    assert_eq!(one.condense(), 0, "a fertile tombstone never excises");

    // X lands at 3; the parked acknowledgements count on the same handle,
    // and the fleet reads one order.
    let mut flood = vec![weave_x];
    while let Some(note) = flood.pop() {
        for replica in [&mut one, &mut two, &mut three] {
            let mut out = Vec::new();
            replica.handle(&note, &mut out);
            flood.extend(out);
        }
    }
    assert_eq!(one.effective_order(), two.effective_order());
    assert_eq!(one.effective_order(), three.effective_order());
    assert_eq!(one.effective_order().len(), 1, "X alone is visible");
    assert_eq!(
        three.condense(),
        0,
        "a complete witness still never excises a fertile tombstone"
    );

    // Deleting X sterilizes the chain: every replica excises X then T
    // transitively, and the skeletons agree byte for byte.
    let mut outbox_delete_x = Vec::new();
    let deleted = two.delete_visible(0, &mut outbox_delete_x);
    assert_eq!(deleted, Some(d(2, 1)), "station 2 deletes X");
    let mut flood = outbox_delete_x;
    while let Some(note) = flood.pop() {
        for replica in [&mut one, &mut two, &mut three] {
            let mut out = Vec::new();
            replica.handle(&note, &mut out);
            flood.extend(out);
        }
    }
    for replica in [&mut one, &mut two, &mut three] {
        assert_eq!(replica.condense(), 2, "X tip-first, then T");
    }
    assert_eq!(one.text().store().to_bytes(), two.text().store().to_bytes());
    assert_eq!(
        one.text().store().to_bytes(),
        three.text().store().to_bytes()
    );
}

/// Retirement evidence is generation-scoped: a carried meet licenses
/// excising a recurring numeral.
///
/// The re-foundation compacts each station's survivors into `1..=n`, so
/// the next plane recurs small dot numerals. A tracker carried across the
/// seal still holds the old generation's applied sets, and its meet names
/// dots that in the new plane belong to different, possibly freshly
/// deleted elements the new generation's own round has not vouched for:
/// one replica excises early, a peer's in-flight anchor strands, exactly
/// the over-claim the witness exists to refuse. The fleet therefore
/// rebuilds the tracker at every seal, and a fresh generation's meet
/// starts empty.
#[test]
fn retirement_evidence_is_generation_scoped() {
    const ROSTER: [u32; 3] = [1, 2, 3];
    let mut fleet = fleet_of(&ROSTER, NonZeroUsize::new(2).expect("positive"));
    let mut fabric = Fabric::new(0xF1EE_7005, &ROSTER, 0);

    // Generation 1: station 1 weaves (1, 1) and (1, 2), deletes (1, 2),
    // and the whole round settles, so every carried applied-set names
    // (1, 2).
    let _ = act(&mut fabric, &mut fleet, 1, |replica, out| {
        replica.insert_visible(0, out)
    });
    let _ = act(&mut fabric, &mut fleet, 1, |replica, out| {
        replica.insert_visible(1, out)
    });
    fabric.drain(&mut fleet);
    let deleted = act(&mut fabric, &mut fleet, 1, |replica, out| {
        replica.delete_visible(1, out)
    });
    assert_eq!(deleted, Some(d(1, 2)));
    fabric.drain(&mut fleet);

    // Cross the boundary; station 1's lone survivor compacts to (1, 1).
    let _ = act(&mut fabric, &mut fleet, 2, |replica, out| {
        replica.try_declare(out)
    })
    .expect("the settled watermark licenses the declaration");
    fabric.drain(&mut fleet);
    for replica in fleet.values() {
        assert_eq!(replica.generation(), 2);
    }

    // The new plane recurs the numeral: station 1's next mint is (1, 2)
    // again, a different element in a different dot space.
    let minted = act(&mut fabric, &mut fleet, 1, |replica, out| {
        replica.insert_visible(1, out)
    });
    assert_eq!(minted, d(1, 2), "the compacted plane recurs the numeral");
    fabric.drain(&mut fleet);

    // Station 1 deletes it, and ONLY the delete reaches station 3: no
    // generation-2 acknowledgement of (1, 2) has been counted anywhere.
    let replica = fleet.get_mut(&1).expect("roster member");
    let mut outbox = Vec::new();
    let deleted = replica.delete_visible(1, &mut outbox);
    assert_eq!(deleted, Some(d(1, 2)));
    let delete_note = outbox
        .iter()
        .find(|note| matches!(note, Note::Old { .. }))
        .cloned()
        .expect("the delete note");
    let mut sink = Vec::new();
    fleet
        .get_mut(&3)
        .expect("roster member")
        .handle(&delete_note, &mut sink);

    // The tombstone is invisible and sterile at 3, and the old meet named
    // this numeral; only the generation scoping keeps it unexcisable until
    // the new round vouches.
    assert_eq!(
        fleet.get_mut(&3).expect("roster member").condense(),
        0,
        "a fresh generation's meet starts empty"
    );
}

/// An outrun testimony is not an orphan.
///
/// Under the reordering fabric a movement testimony can arrive before its
/// target's weave, so a locally absent locus can mean "not delivered yet"
/// rather than "excised": retiring on absence alone would erase a valid
/// in-flight move everywhere, convergent but lossy (the review-caught
/// hazard). Orphanhood therefore requires causal-removal evidence: the
/// target's dot in the text context (context never forgets, so a covered
/// weave arrives already superseded and the move could never show again)
/// beside the missing locus. The outrun testimony survives the hygiene
/// pass and resolves once its weave lands.
#[test]
fn an_outrun_testimony_is_not_an_orphan() {
    const ROSTER: [u32; 3] = [1, 2, 3];
    let depth = NonZeroUsize::new(2).expect("positive");
    let mut one = Replica::new(1, &ROSTER, depth);
    let mut two = Replica::new(2, &ROSTER, depth);
    let mut three = Replica::new(3, &ROSTER, depth);

    // Station 1 weaves A everywhere, then weaves B and moves it to the
    // front. The move note reaches station 3; B's weave does not.
    let mut round = Vec::new();
    let _ = one.insert_visible(0, &mut round);
    while !round.is_empty() {
        let mut next = Vec::new();
        for note in &round {
            for replica in [&mut one, &mut two, &mut three] {
                let mut out = Vec::new();
                replica.handle(note, &mut out);
                next.extend(out);
            }
        }
        round = next;
    }
    let mut outbox = Vec::new();
    let woven = one.insert_visible(1, &mut outbox);
    assert_eq!(woven, d(1, 2), "B is station 1's second weave");
    let weave_b = outbox
        .iter()
        .find(|note| matches!(note, Note::Old { .. }))
        .cloned()
        .expect("the weave note");
    let mut outbox_move = Vec::new();
    let moved = one.move_visible(1, None, &mut outbox_move);
    assert_eq!(moved, Some(d(1, 3)), "the testimony dot");
    let move_note = outbox_move
        .iter()
        .find(|note| matches!(note, Note::Old { .. }))
        .cloned()
        .expect("the move note");
    let mut sink = Vec::new();
    three.handle(&move_note, &mut sink);

    // Station 3 holds the testimony but not the weave: no locus, and no
    // context evidence of removal. The hygiene pass must leave it alone.
    assert_eq!(
        three.retire_orphans(&mut Vec::new()),
        None,
        "a missing locus without removal evidence is an outrun, not an orphan"
    );

    // The weave lands and the move floods on; everything resolves
    // identically everywhere.
    let mut flood = vec![weave_b, move_note];
    flood.extend(sink);
    while let Some(note) = flood.pop() {
        for replica in [&mut one, &mut two, &mut three] {
            let mut out = Vec::new();
            replica.handle(&note, &mut out);
            flood.extend(out);
        }
    }
    assert_eq!(one.effective_order(), three.effective_order());
    assert_eq!(two.effective_order(), three.effective_order());
    assert_eq!(
        three.effective_order().first(),
        Some(&d(1, 2)),
        "the outrun move survived the hygiene pass and resolved"
    );
}