Wanderland

Wanderland Core: Overlay Operators

Extending the storage overlay from antiparticle-only to full operator-based composition. Interceptors, pokes, and any corrective edit use the same merge operators that crossings uses for its collapse. The overlay reads all records at an address and composes them: seed + operations = current state.

The Model

All data is either a map (k/v) or a stream (ordered sequence). Every mutation is an operation record at the same to_addr as the seed. The overlay reads all records, identifies the seed, applies operations in at order, and returns the composed result.

seed record (the original crossing)
  + operation 1 (splice by interceptor A)
  + operation 2 (snip by interceptor B)
  + operation 3 (append by trigger C)
  - antiparticle (cancels operation 1)
  = current state

Same address, different type_addr values. The seed is any non-operator record. Operations have type_addr values like :types:splice, :types:snip, :types:poke. Antiparticles cancel any record.

Operation Types

Inherited from the crossings operator system, adapted for the storage overlay:

Map Operations (k/v data)

Operation type_addr Payload Effect
$set` | `:types:poke` | `{ path: "result.shape", value: "star", op: "$set" } Replace value at path
$merge` | `:types:poke` | `{ path: "result", value: { extra: true }, op: "$merge" } Deep merge at path
$inc` | `:types:poke` | `{ path: "result.score", value: 1, op: "$inc" } Increment counter at path
$min` / `$max :types:poke { path: "result.best", value: 5, op: "$min" } Keep min/max
snip :types:snip { path: "result.temporary" } Remove key at path

Stream Operations (ordered sequences)

Operation type_addr Payload Effect
$append` | `:types:poke` | `{ path: "result.items", value: ["new"], op: "$append" } Append to array
$prepend` | `:types:poke` | `{ path: "result.items", value: ["first"], op: "$prepend" } Prepend to array
$union` | `:types:poke` | `{ path: "result.tags", value: ["new"], op: "$union" } Set union
$intersection` | `:types:poke` | `{ path: "result.allowed", value: ["a","b"], op: "$intersection" } Set intersection
splice :types:splice { path: "stream", index: 3, delete: 1, insert: [token] } Array splice at index

Control Operations

Operation type_addr Payload Effect
antiparticle :types:antiparticle { cancels: { at: "...", type: "..." } } Cancel a record
annotate :types:annotate { path: "_meta.warnings", value: [...], op: "$append" } Add metadata (additive only)

Every Operation Is a Signed Crossing

Each operation is a full crossing record — to_addr, from_addr, type_addr, payload, at, sig. The from_addr is the interceptor/boundary that wrote it. The sig proves who made the edit. The trace links it to the crossing that triggered it.

# Seed: original boundary crossing
to_addr:  ":games:abc123:runs:001:vine_right:A2"
from_addr: ":boundaries:vine_right"
type_addr: ":types:tick"
payload:   { shape: "circle", color: "red" }
at:        "2026-04-09T10:00:00Z"
sig:       "aaa"

# Operation: interceptor annotates with timing
to_addr:  ":games:abc123:runs:001:vine_right:A2"
from_addr: ":interceptors:request_timer"
type_addr: ":types:poke"
payload:
  op: "$merge"
  path: "_meta"
  value: { elapsed_ms: 3, measured_by: "request_timer" }
at:        "2026-04-09T10:00:00.003Z"
sig:       "bbb"
trace:     "aaa"

# Operation: interceptor flags a warning
to_addr:  ":games:abc123:runs:001:vine_right:A2"
from_addr: ":interceptors:result_validator"
type_addr: ":types:poke"
payload:
  op: "$append"
  path: "_warnings"
  value: ["shape value not in expected set"]
at:        "2026-04-09T10:00:00.005Z"
sig:       "ccc"
trace:     "aaa"

Overlay Read

The overlay method becomes:

def overlay(addr)
  records = at(addr)
  records = apply_antiparticles(records)
  seed, operations = partition_seed_and_ops(records)
  return seed if operations.empty?
  apply_operations(seed, operations)
end

def partition_seed_and_ops(records)
  ops = records.select { |r| operator_type?(r["type_addr"]) }
  seeds = records - ops
  # Seed is the most recent non-operation record
  seed = seeds.last
  [seed, ops.sort_by { |o| o["at"] }]
end

def apply_operations(seed, operations)
  result = deep_dup(seed)
  operations.each do |op|
    payload = op["payload"]
    path = payload["path"]
    operator = payload["op"]
    value = payload["value"]

    case op["type_addr"]
    when ":types:poke"
      apply_merge_op(result, path, operator, value)
    when ":types:snip"
      apply_snip(result, path)
    when ":types:splice"
      apply_splice(result, path, payload)
    when ":types:annotate"
      apply_merge_op(result, path, "$append", value)
    end
  end
  result
end

CRDT Properties

The operators inherited from crossings have well-defined CRDT semantics:

Operator CRDT Model Commutativity
$set LWW Register No (order matters, at resolves)
$append Grow-only sequence Yes (both appends produce same result regardless of order)
$union G-Set Yes (set union is commutative)
$inc G-Counter Yes (increment is commutative)
$merge OR-Map Partially (key-level, not value-level)
$min` / `$max Min/Max register Yes
splice Positional edit No (index-dependent, requires OT for concurrent edits)

For map data (k/v), most operations are commutative — two interceptors writing to different paths produce the same result regardless of order. For stream data (positional edits), order matters — splice at index 3 means different things if another splice shifted the indices. This is where OT semantics apply.

Multiplayer

Because operations are positional edits on an immutable seed, multiple actors can write operations concurrently:

The multiplayer semantics come from the CRDT/OT properties of the operators, not from any locking mechanism. The append-only store guarantees that all operations are visible to all readers. The overlay composes them deterministically.

Relationship to Remark Bridge

The remark bridge already implements this model for markdown documents:

The wanderland-core overlay generalizes this to all data in the store. Documents, crossings, game state, interceptor edits — all composed the same way. The operator vocabulary is shared. The overlay method is the universal read.

Implementation Path