moonreplaykit

Deterministic state-machine replay, checkpoint validation and divergence diagnostics for MoonBit.

deterministic-replay
event-journal
state-machine
divergence
checkpoint
moon add LAOBIAO656/moonreplaykit@0.2.0
Download zip
Version
0.2.0
License
Apache-2.0
Last updated
last month
Downloads
11
README

#MoonReplayKit

Deterministic state-machine replay, checkpoint validation, branch comparison, divergence diagnostics, and portable replay evidence for MoonBit.

#Highlights

  • Generic append-only hash-linked journals and deterministic reducers.
  • Full replay and checkpoint resume with state and journal anchors.
  • Branch and reducer-version divergence diagnostics.
  • Invariant failure localization and shortest reproducing prefixes.
  • Merkle-style evidence roots over event and transition-state fingerprints.
  • Logarithmic inclusion proofs that verify without the full state history.
  • First evidence divergence lookup for equal-length replay trees.
  • Backend-neutral core with no I/O, clock, randomness, or third-party runtime.

#Evidence Example

let report = @moonreplaykit.replay(
journal,
initial_state,
payload_fingerprint,
state_fingerprint,
reducer,
)

let tree = @moonreplaykit.build_replay_evidence(
journal,
report,
payload_fingerprint,
).unwrap()

let proof = @moonreplaykit.replay_inclusion_proof(
journal,
report,
tree,
sequence,
).unwrap()

assert_true(@moonreplaykit.verify_replay_inclusion(proof))

The built-in fingerprint is deterministic and intended for replay integrity, comparison, and regression evidence. It is not a cryptographic signature.

See the repository README and docs/ for design rationale and full examples.

#
ChainIssue

pub(all) enum ChainIssue {
UnexpectedSequence(Int, Int, Int)
BrokenPreviousHash(Int, Int, Int)
InvalidEventHash(Int, Int, Int)
} derive(Eq,
Debug
)

Why a journal failed structural validation.

#
ChainValidation

pub(all) enum ChainValidation {
Valid(Int, Int)
Invalid(ChainIssue)
} derive(Eq,
Debug
)

Structural validation result for an event journal.

#
Checkpoint

pub(all) struct Checkpoint[S] {
sequence : Int
state : S
state_hash : Int
journal_hash : Int
}

A state snapshot anchored to a verified journal boundary.

#
Divergence

pub(all) struct Divergence {
sequence : Int
kind : DivergenceKind
left_hash : Int
right_hash : Int
message : String
} derive(Eq,
Debug
)

A stable, serializable explanation of a replay divergence.

#
DivergenceKind

pub(all) enum DivergenceKind {
StateMismatch
StatusMismatch
LengthMismatch
EventMismatch
} derive(Eq,
Debug
)

The first point where two executions no longer agree.

#
InvariantResult

pub(all) enum InvariantResult {
Holds(Int)
Violated(Int, Int, String)
ReplayFailed(ReplayStatus)
} derive(Eq,
Debug
)

Result of checking an invariant over every replayed prefix.

#
Journal

pub struct Journal[P] {
records : Array[JournalEvent[P]]
}

In-memory append-only journal.

Persistence adapters can reconstruct a journal with from_events and then call validate before allowing replay.

#
Journal::append

fn[P] Journal::append(self : Journal[P], kind : String, payload : P, correlation_id : String, payload_fingerprint : (P) -> String) -> JournalEvent[P]

Appends one event and returns its complete journal record.

#
Journal::events

fn[P] Journal::events(self : Journal[P]) -> Array[JournalEvent[P]]

Returns a copy suitable for persistence or diagnostics.

#
Journal::from_events

fn[P] Journal::from_events(events : Array[JournalEvent[P]]) -> Journal[P]

Reconstructs a journal from externally loaded events.

The caller must validate the returned journal before replay.

#
Journal::get

fn[P] Journal::get(self : Journal[P], index : Int) -> JournalEvent[P]?

Returns an event by zero-based index.

#
Journal::hash_at

fn[P] Journal::hash_at(self : Journal[P], sequence : Int) -> Int?

Returns the hash-chain anchor at a sequence boundary.

#
Journal::length

fn[P] Journal::length(self : Journal[P]) -> Int

Returns the number of events in the journal.

#
Journal::new

fn[P] Journal::new() -> Journal[P]

Creates an empty event journal.

#
Journal::prefix

fn[P] Journal::prefix(self : Journal[P], count : Int) -> Journal[P]

Returns a journal containing the first count events.

The returned journal preserves the original hash evidence and should still be validated before replay.

#
Journal::tail_hash

fn[P] Journal::tail_hash(self : Journal[P]) -> Int

Returns the current hash-chain tail, or zero for an empty journal.

#
Journal::validate

fn[P] Journal::validate(self : Journal[P], payload_fingerprint : (P) -> String) -> ChainValidation

Validates sequence continuity and the complete event hash chain.

#
JournalEvent

pub(all) struct JournalEvent[P] {
sequence : Int
kind : String
payload : P
correlation_id : String
previous_hash : Int
hash : Int
}

A journal event together with the evidence required to verify its position.

#
MigrationReport

pub(all) struct MigrationReport[S] {
compatible : Bool
old_report : ReplayReport[S]
new_report : ReplayReport[S]
first_divergence : Divergence?
}

Result of comparing two reducer versions against the same journal.

#
ReplayBranch

pub struct ReplayBranch[P] {
name : String
base_sequence : Int
journal : Journal[P]
}

A named alternative history forked from a journal boundary.

#
ReplayBranch::append

fn[P] ReplayBranch::append(self : ReplayBranch[P], kind : String, payload : P, correlation_id : String, payload_fingerprint : (P) -> String) -> JournalEvent[P]

Appends one event to the alternative history.

#
ReplayBranch::base_sequence

fn[P] ReplayBranch::base_sequence(self : ReplayBranch[P]) -> Int

Returns the source sequence where the branch was created.

#
ReplayBranch::fork

fn[P] ReplayBranch::fork(name : String, source : Journal[P], sequence : Int, payload_fingerprint : (P) -> String) -> ReplayBranch[P]?

Forks a branch from the first sequence events of a source journal.

Returns None when the source is invalid or the boundary is out of range.

#
ReplayBranch::journal

fn[P] ReplayBranch::journal(self : ReplayBranch[P]) -> Journal[P]

Returns a copy of the branch journal.

#
ReplayBranch::name

fn[P] ReplayBranch::name(self : ReplayBranch[P]) -> String

Returns the branch name.

#
ReplayEvidenceTree

pub(all) struct ReplayEvidenceTree {
event_count : Int
root : Int
levels : Array[Array[Int]]
} derive(Eq,
Debug
)

Merkle-style deterministic commitment over event and state evidence.

This is an integrity and comparison structure, not a cryptographic signature.

#
ReplayInclusionProof

pub(all) struct ReplayInclusionProof {
sequence : Int
event_hash : Int
state_hash : Int
leaf_hash : Int
siblings : Array[Int]
sibling_on_left : Array[Bool]
root : Int
} derive(Eq,
Debug
)

Portable logarithmic proof that one transition belongs to an evidence root.

#
ReplayReport

pub(all) struct ReplayReport[S] {
status : ReplayStatus
initial_sequence : Int
applied_events : Int
final_sequence : Int
final_state : S
final_state_hash : Int
journal_tail_hash : Int
state_hashes : Array[Int]
}

Evidence produced by a replay.

#
ReplayStatus

pub(all) enum ReplayStatus {
Completed
Rejected(Int, String)
InvalidJournal(ChainIssue)
InvalidCheckpoint(String)
} derive(Eq,
Debug
)

Replay completion status.

#
Transition

pub(all) enum Transition[S] {
Accepted(S)
Rejected(String)
}

The result of applying one event to an application state.

#
build_replay_evidence

fn[P, S] build_replay_evidence(journal : Journal[P], report : ReplayReport[S], payload_fingerprint : (P) -> String) -> ReplayEvidenceTree?

Builds a deterministic commitment over every event and resulting state.

The journal chain, replay boundary, state evidence length, and tail anchor must all agree. Invalid or partial reports return None.

#
checkpoint_from_report

fn[S] checkpoint_from_report(report : ReplayReport[S]) -> Checkpoint[S]?

Creates a checkpoint from the final verified boundary of a replay report.

#
compare_histories

fn[P] compare_histories(left : Journal[P], right : Journal[P], payload_fingerprint : (P) -> String) -> Divergence?

Locates the first structural difference between two event histories.

#
compare_reducer_versions

fn[P, S] compare_reducer_versions(journal : Journal[P], initial_state : S, payload_fingerprint : (P) -> String, state_fingerprint : (S) -> String, old_reducer : (S, JournalEvent[P]) -> Transition[S], new_reducer : (S, JournalEvent[P]) -> Transition[S]) -> MigrationReport[S]

Replays one journal through old and new reducer versions.

Compatibility means both versions completed and every state boundary has the same fingerprint.

#
compare_reports

fn[S] compare_reports(left : ReplayReport[S], right : ReplayReport[S]) -> Divergence?

Compares the transition evidence from two replay reports.

#
divergence_kind_name

fn divergence_kind_name(kind : DivergenceKind) -> String

Returns a stable machine-readable label for a divergence kind.

#
event_fingerprint

fn event_fingerprint(previous_hash : Int, sequence : Int, kind : String, correlation_id : String, payload_fingerprint : String) -> Int

Combines journal metadata and an application-provided payload fingerprint.

#
event_to_json

fn[P] event_to_json(event : JournalEvent[P], payload_json : (P) -> String) -> String

Encodes one journal event. payload_json must return a valid JSON value.

#
events_by_correlation

fn[P] events_by_correlation(journal : Journal[P], correlation_id : String) -> Array[JournalEvent[P]]

Returns events carrying a given correlation identifier.

#
events_by_kind

fn[P] events_by_kind(journal : Journal[P], kind : String) -> Array[JournalEvent[P]]

Returns events with an exact application event kind.

#
events_in_range

fn[P] events_in_range(journal : Journal[P], first_sequence : Int, last_sequence : Int) -> Array[JournalEvent[P]]

Returns events in an inclusive sequence range.

This is an investigation view, not a replayable journal. Original sequence and hash evidence remain attached to every returned event.

#
find_first_invariant_failure

fn[P, S] find_first_invariant_failure(journal : Journal[P], initial_state : S, payload_fingerprint : (P) -> String, state_fingerprint : (S) -> String, reducer : (S, JournalEvent[P]) -> Transition[S], invariant : (S) -> String?) -> InvariantResult

Checks an invariant at the initial state and after every accepted event.

The invariant returns None when the state is valid or a stable diagnostic message when it is violated. The first violation is therefore the shortest replay prefix that reproduces the failure.

#
fingerprint_text

fn fingerprint_text(text : String) -> Int

Stable non-cryptographic fingerprint used for deterministic replay evidence.

This function is deliberately identical on every MoonBit backend. It is intended for change detection and replay comparison, not for security.

#
first_evidence_divergence

fn first_evidence_divergence(left : ReplayEvidenceTree, right : ReplayEvidenceTree) -> Int?

Locates the first differing one-based transition.

Equal-length trees are searched by descending only into the first unequal subtree. A length mismatch returns the first missing transition boundary.

#
journal_to_json

fn[P] journal_to_json(journal : Journal[P], payload_json : (P) -> String) -> String

Encodes a journal as a stable JSON array.

#
json_escape

fn json_escape(value : String) -> String

Escapes a string for use as a JSON string value.

#
minimal_failing_prefix

fn[P, S] minimal_failing_prefix(journal : Journal[P], initial_state : S, payload_fingerprint : (P) -> String, state_fingerprint : (S) -> String, reducer : (S, JournalEvent[P]) -> Transition[S], invariant : (S) -> String?) -> Journal[P]?

Returns the shortest journal prefix that reproduces an invariant failure.

#
replay

fn[P, S] replay(journal : Journal[P], initial_state : S, payload_fingerprint : (P) -> String, state_fingerprint : (S) -> String, reducer : (S, JournalEvent[P]) -> Transition[S]) -> ReplayReport[S]

Replays a complete validated journal from an initial state.

payload_fingerprint and state_fingerprint must be deterministic. reducer must not depend on time, randomness, I/O, or mutable global state.

#
replay_evidence_to_json

fn replay_evidence_to_json(tree : ReplayEvidenceTree) -> String

Encodes the portable summary of a replay evidence tree.

#
replay_from_checkpoint

fn[P, S] replay_from_checkpoint(journal : Journal[P], checkpoint : Checkpoint[S], payload_fingerprint : (P) -> String, state_fingerprint : (S) -> String, reducer : (S, JournalEvent[P]) -> Transition[S]) -> ReplayReport[S]

Continues a validated journal from an anchored checkpoint.

#
replay_inclusion_proof

fn[P, S] replay_inclusion_proof(journal : Journal[P], report : ReplayReport[S], tree : ReplayEvidenceTree, sequence : Int) -> ReplayInclusionProof?

Creates an inclusion proof for a one-based event sequence.

#
replay_inclusion_proof_to_json

fn replay_inclusion_proof_to_json(proof : ReplayInclusionProof) -> String

Encodes an inclusion proof without application state serialization.

#
replay_report_to_json

fn[S] replay_report_to_json(report : ReplayReport[S], state_json : (S) -> String) -> String

Encodes the summary and final state of a replay report.

state_json must return a valid JSON value.

#
replay_status_name

fn replay_status_name(status : ReplayStatus) -> String

Returns a stable machine-readable label for a replay status.

#
verify_determinism

fn[P, S] verify_determinism(journal : Journal[P], initial_state : S, payload_fingerprint : (P) -> String, state_fingerprint : (S) -> String, reducer : (S, JournalEvent[P]) -> Transition[S]) -> Bool

Runs the same replay twice and checks the complete transition evidence.

This catches reducers that accidentally read time, randomness, or mutable ambient state even when their final states happen to match.

#
verify_replay_inclusion

fn verify_replay_inclusion(proof : ReplayInclusionProof) -> Bool

Verifies a portable inclusion proof without the full journal or state list.

Powered by MoonBit

Site sourceReport issuePackagesBuild queueSkillsStatistics

© 2026 mooncakes.io