Step 01 — Cluster and log

Goal

Define the three core types — Op, LogEntry, Node — and the container Cluster that holds three nodes. No replication yet; the leader appends to its own log only.

Tasks

  1. Define OpKind as Put | Del and Op { kind, k: i64, v: i64 }.
  2. Define LogEntry { term: u64, index: u64, op: Op }.
  3. Define Node { id: u8, term: u64, commit_index: u64, last_applied: u64, log: Vec<LogEntry>, kv: Map<i64,i64> }.
  4. Implement Node::append requiring entry.index == log.len() + 1, with idempotent re-acceptance of an already-present index (used later by catch_up).
  5. Implement Node::apply_committed: while last_applied < commit_index, apply log[last_applied] to kv and increment.
  6. Implement Node::encode with the canonical wire format from CONCEPTS.md.
  7. Implement Cluster::new with three nodes (ids 0, 1, 2) all marked up, and Cluster::encode_snapshot = concat of all three encodings.

Acceptance

  • snapshot_layout_smoke test passes in all three languages.
  • An empty cluster's snapshot has length 3 × (8+1+8+8+4+4) = 99 bytes.

Pitfalls

  • Go map iteration order is undefined — sort keys before encoding.
  • std::map (ordered) in C++, NOT std::unordered_map.
  • All multi-byte ints are little-endian.
  • v for a Del op encodes as 0.