minerva 0.2.0

Causal ordering for distributed systems
= Day one: your first replicated document
:toc: macro

This guide builds one replicated document for two people on two machines.
It uses no server, lock, or coordinator. Concurrent edits at one position
remain present. The example uses approximately one hundred lines of Minerva's
public API.

Each listing comes from xref:../tests/day_one.rs[`tests/day_one.rs`]. That file
is an integration test. It imports `minerva::` as an external crate, so the
example can use only public APIs. A synchronization test checks that each
listing is an exact excerpt. The test fails if the code and guide differ.
Read the xref:consumer-guide.adoc[consumer guide] before you design your own
integration.

toc::[]

== The cast, and the one boundary to understand first

A participant has a *station* identifier and a `Composer<Rhapsody>`. Each
participant in this example also owns a `Clock`, so the example requires a
different nonzero `u32` for each participant. `Clock::new` refuses station
zero. The broader `metis` station domain permits zero. A deployment that uses
that domain without a `Clock` can select its station policy at the authenticated
boundary.

`Composer<Rhapsody>` is the write facade for a causal pair. The pair contains a
sequence store and a context that records all observed identities. Each write
mints a *dot*: a station and a nonzero counter. A dot is the permanent identity
of that element.

The caller must preserve dot uniqueness. Assign one station identifier to each
participant. Do not reuse an identifier with fresh state. After a restart,
restore the saved pair through `Composer::adopt`. If a participant starts with
empty state, it can mint a counter that it used before. The consumer guide owns
the durability recipes.

Minerva stores identities and order. It does not store application meaning.
The caller owns the payload for each dot, such as a glyph, block, or row. Each
author in this example has a dot-keyed glyph map. Synchronization transfers
payloads explicitly. The authors do not share memory.

[source,rust]
----
struct Author {
    composer: Composer<Rhapsody>,
    clock: Clock<TickCounter>,
    glyphs: BTreeMap<Dot, char>,
}
----

The `TickCounter` clock is deterministic. The order is a pure function of the
write history, so the example is reproducible.

== Act 1: typing is minting

Ada uses station 1 and types `hi`. Each write gets the next dot, places it after
the previous element, and assigns a clock rank.

[source,rust]
----
let visible = self.composer.state().store().order();
let anchor = visible
    .last()
    .map_or(Anchor::Origin, |&dot| Anchor::After(dot.into()));
let rank = self.clock.now(0u16);
let (dot, _delta) = self.composer.compose(|assigned| {
    let mut rhapsody = Rhapsody::new();
    assert!(rhapsody.weave(assigned, Locus { anchor, rank }));
    (rhapsody, DotSet::new())
});
----

`order()` returns the store's linearization. The read walks the anchor tree.
Each element has a side relative to its anchor. Concurrent elements can have
the same anchor. The public sibling rule orders them by descending rank and
then by ascending dot. The caller cannot change this rule at run time.

== Act 2: joining is asking what you are owed

Bea uses station 2. She gives her context to Ada, absorbs the state that Ada
owes, and copies the payloads for those dots.

[source,rust]
----
let mine = self.composer.state().context().clone();
let owed = other.composer.owed_to(&mine);
let _ = self.composer.absorb(other_station, &owed);
for (&dot, &glyph) in &other.glyphs {
    let _ = self.glyphs.entry(dot).or_insert(glyph);
}
----

This exchange has no Minerva session, handshake, or server. `owed_to` and
`absorb` exchange the ordering state. The caller owns transport and I/O.

The final two lines exchange payloads. Payloads use globally unique dots as
keys and are immutable in this example. Therefore, the payload merge is a set
union. Minerva does not own that policy.

== Act 3 and 4: the same place, at the same time

Ada types `a` after the `i`. Bea, not having heard, types `!` after the
same `i`. Then they exchange:

[source,rust]
----
ada.pull_from(&bea, 2);
bea.pull_from(&ada, 1);
assert_eq!(
    ada.composer.state().store().order(),
    bea.composer.state().store().order(),
    "one document, whatever order the network chose",
);
----

Both edits remain present. Minerva does not apply a last-writer-wins policy.
Both replicas return the same interleaving because `a` and `!` are siblings at
one anchor. The sibling rule needs no arbiter. The canonical wire frame also
makes equal states byte-identical. The test checks this property with
`to_bytes()`.

== Act 5: erasing is observing, not destroying

Ada erases her typo. This is an *observed-remove*. It supersedes only the
identity that Ada observed, so it cannot remove Bea's concurrent `!`. The
removal synchronizes like any other change.

[source,rust]
----
let mut superseded = DotSet::new();
assert!(superseded.insert(dot));
let _ = self.composer.retract(superseded);
----

The erased element becomes an *order tombstone*. A tombstone can retain the
position of anchored descendants. Another replica can also be unaware of the
removal. This tombstone is a childless leaf, but Minerva still does not remove
it without evidence. Garbage collection is a separate witnessed operation.

== Act 6: forgetting requires a witness

Garbage collection requires a `Retired` witness. The witness states that each
replica applied the removal. It also states that no concurrent write can still
use the dot as an anchor. Without this condition, a write can become present
but unreachable after anchor removal.

This sequential simulation can inspect the claim directly. Both authors are
settled. No data is in flight. No writes occur between the scan and the two
`condense` calls.

A production fleet uses the fixed-roster `Retirement` tracker. The receiver
counts an acknowledgement only after it delivers the acknowledger's cut. The
tracker computes the roster meet. One of two caller disciplines must then make
the claim safe against later writes. The caller can apply the excision to the
full fleet before writes resume. The caller can instead use the oath-plane
writer discipline, which permits local condensation from local evidence. See
rulings R-41 and R-42 in
https://github.com/OwlRoute/minerva/blob/main/owner-comments.adoc[`owner-comments.adoc`]
(repository only; not shipped in the crate archive).

[source,rust]
----
let before = author.composer.state().store().order();
let excised = author.composer.condense(&retired);
assert_eq!(excised, 1, "exactly the witnessed tombstone is excised");
let store = author.composer.state().store();
assert!(store.locus(a).is_none(), "the locus is gone");
assert_eq!(store.skeleton_len(), 3);
assert_eq!(store.order(), before, "condense never changes the reading");
----

The skeleton decreases from four loci to three at each replica. The erased
locus is absent. The visible order does not change.

The API does not accept a bare set as retirement evidence. `Retired::trust` is
the audited escape for caller-supplied evidence. Its name makes that trust
decision visible in review.

== What you just used, and what you deliberately did not

The example used one `Rhapsody` store, one `Composer` facade, two exchange
reads, one removal write, and one witnessed fold. It did not use epochs,
movement, marks, budgets, or wire codecs. Epochs support history compaction
across sealed boundaries. Movement records a reorder as separate testimony.
The other mechanisms also use typed refusals and caller-owned policy.

Next, read these documents in order:

. xref:consumer-guide.adoc[Consumer guide] for supported APIs and production
  recipes.
. xref:what-is-proven.adoc[What is proven] for assurance scope.
. xref:glossary.adoc[Glossary] for project terms.