Decision log - UnitBuilds-CC/Merkles-World GitHub Wiki

Decision log

Authoritative version with full context, consequences and re-open triggers: docs/decisions.md. This page is the skimmable index: 29 ADRs, ADR-001โ€ฆADR-029, no gaps.

Statuses: locked = implement as written ยท locked-contingent = decided, but depends on a spike result or an open question ยท proposed = our recommendation, needs sign-off.

A decision is changed by amending its ADR, never by quietly coding around it.

Foundation

ADR Decision Status Rejected instead
001 Two-phase project: domain-agnostic engine runtime first, then the game as Mod #1 locked one codebase with game rules compiled in; generic engine + separate private mod API
002 Rust for the engine core locked C++ (memory-safety bug classes), Java (the trap Minecraft later rewrote out of), Go (GC again, weaker GPU ergonomics)
003 WebGPU + WGSL as the rendering API and shader language locked hand-written Vulkan (2โ€“3 months of low-level debugging), GLSL/HLSL native toolchains (no headless validation)
004 Single-player first; multiplayer consensus deferred locked building co-op into the prototype
016 64-bit Hierarchical Double-Origin Coordinate System (HDCS): i64 global, f32 camera-relative locked f64 world coords (precision drift), f32 everywhere (breaks hashing past ~10,000 units), 3ร—i64 per delta (192 bits, a WASM ABI liability)
017 Determinism boundary: simulation in Rust/WASM, the GPU never authoritative locked โ€” highest priority GPU-authoritative world state
025 Contract-first agent swarm: read-only contracts/, Architect + CI-gatekeeper roles locked free-for-all file ownership, human review of every line

State & the ledger

ADR Decision Status Rejected instead
005 Stateless view-time evaluation: S = f(pos, epoch) โŠ• M_local locked-contingent (on S7/S8) a per-tick global hash chain (O(N) replay)
006 Localized octree Merkle roots, one per region locked one global root per tick
007 Sleep/epoch compaction of the delta mask, LSM-tree analogy locked-contingent compact every N minutes; never compact
008 Slab allocator with bump-pointer reset โ†’ O(1) edits and O(1) mass free locked per-edit heap allocation; copy-on-write chunk arrays
009 Tiered slabs: L1 VRAM hot / L2 RAM cold, LRU eviction + background micro-checkpoints locked forcing a sleep to free memory; unbounded VRAM growth
015 FlatBuffers + bitpacked u64 deltas for runtime/ledger data locked Cap'n Proto; JSON at runtime
028 BLAKE3-256 as the ledger digest locked 2026-10-11 โ€” signed off: 32-byte digests with domain-separated leaf/branch/event-tag/entity-state prefixes. Locks SPEC-04's hashing half and makes BLAKE3 the first external dependency the workspace takes. Not settled by it: the Merkle tree shape (arity, child order, empty-node rule) โ€” crates/ledger's first job, and it had to become a contract with published test vectors before any root was canonical. Settled 2026-10-12 by ADR-030: A-10 frozen, crates/ledger landed as the workspace's first crate with behaviour, and fifteen vectors are published in doc 03 SHA-256 (slow, no benefit here); non-crypto hashes
029 Resolve SPEC-09 by taking the slab out of the record address: 16-bit region slot + 36-bit intra-region position (three 12-bit local offsets) + 12-bit payload = exactly one u64. Slabs become append-only record buffers, so their geometry is an allocation choice, not an addressing constraint locked 2026-10-11 โ€” signed off; resolves SPEC-09, freezes A-01 and gives A-03 / A-08 a byte layout widen the record to 84 โ†’ 96/128 bits (worse per-edit storage, abandons ADR-015); keep 8 bytes with the 16 bits indexing an unbounded per-region sparse edit list; shrink the slab to โ‰ค2ยนโถ voxels so 16 bits fits
030 The Merkle tree shape: octree arity 8 over a derived depth of 12 (36 / 3 from ADR-029's field), child order x_bit | y_bit<<1 | z_bit<<2 with x the low bit, level counted as height above the voxel leaves, one derived empty rule (empty(0) = H(kind 2, 0, โˆ…), empty(l) = H(kind 1, l, [empty(lโˆ’1); 8])) with separate kinds for an empty region and an empty epoch, ordered collections as binary lists with an odd trailing item paired with itself, fixed-width little-endian integers in every preimage, preimage layout DOMAIN โ€– kind โ€– level โ€– payload, and base terrain committed intensionally (seed โ€– rule id โ€– version, never 2ยณโถ voxels) locked 2026-10-12 โ€” signed off after crates/ledger computed the vectors and CI run 38181630859 printed EPOCH_0 91b97a52โ€ฆ byte-identically on x86-64 Linux, x86-64 Windows and ARM64 macOS. Freezes artifact A-10; a rule change from here is a shape break that moves every root and every published vector. Not settled by it: OQ-45 (what the 12 payload bits mean โ€” hashed opaquely, which is why the shape could freeze first), OQ-27 (the base rule set: fixing it moves GENESIS), and OQ-44, which is now live โ€” node_parts takes bytes, so nothing types a float out of a preimage chained epoch roots (a contract change to A-03 dressed as a tree decision); hashing the base field voxel by voxel (2ยณโถ leaves โ€” the storage model the design escapes); one flat Morton index as the octree key (docs/02 forbids it, and it makes a disagreeing peer's address wrong); carry-forward for an odd list item (lets a truncated list agree with a longer one); a per-level EMPTY_i table (13 hand-published values to keep in sync, against one rule that derives them)

Rendering & content

ADR Decision Status Rejected instead
011 Assets are code: SDF raymarching, no meshes, no textures locked mesh + PNG pipeline
012 Shape/material node grammar interpreted by one master shader locked one hand-written shader per entity; a visual node editor at runtime
024 Spatial BVH mapped to slabs for SDF culling locked evaluating every SDF for every ray
026 Reuse an existing GPU runtime (wgpu); do not hand-write low-level plumbing locked 2026-10-11 โ€” signed off: wgpu is the substrate and the raymarcher is ours, written as a WGSL compute pipeline. Answers OQ-26's choice half; spike S9 still owes the cost half hand-written Vulkan; adopting a mesh-based voxel engine (Luanti/Minetest โ€” rejected as a mesh renderer against ADR-011); Bevy only if its plumbing pays for itself
014 Universal authoring formats compiled on save; RON canonical, JSON accepted locked JSON at runtime; binary-only authoring

Simulation, logic, physics

ADR Decision Status Rejected instead
010 Circuits as compiled graph topologies evaluated analytically f(I, t) locked-contingent (on OQ-02) block-by-block CA redstone (breaks lazy evaluation); "no electricity feature"
013 WASM (Component Model + WIT) as the modding and logic ABI locked Lua/JS embedding; native DLL plugins
018 Physics by analytic SDF gradient queries (N = โˆ‡SDF(p)) locked hidden mesh colliders; VRAM readback to CPU
019 Animation by domain deformation / time-warping locked bone rigs and skinned meshes
020 Audio occlusion by SDF sphere tracing from source to ear locked pre-baked sound zones; mesh raycasts

Multiplayer

ADR Decision Status Rejected instead
021 Transmit signed intent streams (ActionTx), never world state locked authoritative server streaming chunk bytes
022 WebRTC datachannels / QUIC with split reliable + unreliable channels locked one reliable channel for everything
023 Desync detection by 32-byte epoch root exchange + Merkle bisection repair locked-contingent (on S3) per-action server validation; whole-world checksum
027 Epoch-scoped transaction pool (quorum-or-timer commit) proposed per-player epochs (worlds diverge)

Reading order

If you only read three: ADR-017 (what the GPU may and may not decide โ€” the whole verification story depends on it, and its acceptance test is the Bitwise Determinism Suite, spike S3 / gate P1.5, see Roadmap & status), ADR-005 + ADR-006 (why the world is a function with local roots, not a hash chain), ADR-011 + ADR-012 (why there is no art pipeline, which is what makes the mod story and the agent swarm possible).