Wanderland

Wanderland Core: Archetype System

The engine is a VM. The archetype is the combustion sequence. The user config is the fuel. Crossings are the exhaust.

The Core Loop

The engine does one thing:

Every slot is a boundary. Every boundary creates potential, transforms it, and produces an output crossing. The slot sequence is the combustion — potential in, crossings out.

Archetypes

An archetype is a YAML file shipped inside the gem. It defines a slot sequence — named positions, each binding a boundary to user config via !UserConfig thunks.

The terminal slot determines the runtime shape:

Archetype Terminal behavior Runtime
engine rack_server — blocks on HTTP Long-running service
scenario shape_validate — checks expected shape One-shot, exits
cli Returns result One-shot, exits

Everything before the terminal slot is identical: load boundaries, configure resolvers, mount storage, seed data. The archetype IS the program.

Scenario Archetype

A scenario is not a special class. It is a config that boots through the scenario archetype:

# lib/wanderland/archetypes/scenario.yml
name: scenario
description: One-shot boundary execution with validation

slots:
  - name: boundaries
    boundary: boot_load_boundaries
    args:
      target: !UserConfig boundary_path

  - name: resolvers
    boundary: boot_configure_resolvers
    args:
      config:
        resolvers: !UserConfig resolvers

  - name: storage
    boundary: boot_mount_storage
    args:
      config:
        storage: !UserConfig storage

  - name: seed
    boundary: seed_storage
    args:
      records: !UserConfig records

  - name: execute
    boundary: !UserConfig operation
    args: !UserConfig input

  - name: validate
    boundary: shape_validate
    args:
      expected: !UserConfig expected

The user YAML provides the fuel:

archetype: scenario
boundary_path: ../lib/wanderland/boundaries
resolvers:
  Fixture:
    fixture_dirs: [fixtures]
storage:
  mounts:
    ":test:":
      driver: sqlite
      path: ":memory:"

records:
  - to_addr: ":test:overlay:01"
    from_addr: ":boundaries:spring"
    type_addr: ":types:tick"
    payload: { shape: circle, color: red }
    at: "2026-04-09T10:00:00Z"

operation: overlay_ops
input:
  action: compose
expected:
  payload:
    shape: star
    color: red

No Scenario class. No load_all. No hydrate_context!. No run. No verify. Just YAML through the engine.

The rspec spec becomes:

Dir.glob("scenarios/**/*.yml").each do |path|
  it File.basename(path) do
    result = Wanderland.boot(path)
    expect(result).to be_passing
  end
end

Engine Archetype

The HTTP service is the same pattern:

# lib/wanderland/archetypes/engine.yml
name: engine
slots:
  - name: boundaries
    boundary: boot_load_boundaries
    args:
      target: !UserConfig boundary_path
  - name: resolvers
    boundary: boot_configure_resolvers
    args:
      config:
        resolvers: !UserConfig resolvers
  - name: storage
    boundary: boot_mount_storage
    args:
      config:
        storage: !UserConfig storage
  - name: triggers
    boundary: boot_mount_triggers
    args:
      config:
        triggers: !UserConfig triggers
  - name: routes
    boundary: boot_mount_routes
    args: {}

Same boot, different terminal behavior. The boot_mount_routes slot creates the Rack app. config.ru calls run Wanderland.boot("config.yml") and blocks on traffic.

The Combustion Model

Every level of the system is the same loop:

Create potential → burn through boundaries → produce crossings. Same engine. Different fuel.

Tracing

Every boundary execution opens a trace span. The span tree IS the combustion record:

boot (root span)
  ├── boot_load_boundaries (span)
  ├── boot_configure_resolvers (span)
  ├── boot_mount_storage (span)
  ├── seed_storage (span)
  ├── overlay_ops (span)
  │   ├── Storage.append × 3
  │   └── Storage.overlay
  └── shape_validate (span)

No separate trace mechanism. The span tree is built by Boundary.execute opening spans. Storage operations record on the current span. The trace writes to storage at the engine's boot address.

What Dissolves

Input Adapters (Modes)

The engine has three input adapters. The adapter determines how fuel arrives. The archetype determines what happens with it. The mode can be explicit (--http, --test) or inferred from the archetype.

Mode Adapter Fuel source Terminal behavior
--http Rack path + verb + params from HTTP request Long-running, blocks on traffic
--test YAML fixed input shape from scenario file One-shot, validate shape and exit
(default) CLI command + args from argv One-shot, execute and exit

All three run the same engine. The adapter is just how !UserConfig gets populated:

The CLI Entry Point

wanderland [--mode] [source] [args]
  -> resolve source (file path, command name, or stdin)
  -> determine mode (from flag, archetype declaration, or inference)
  -> load archetype
  -> hydrate !UserConfig from source + args
  -> run slot sequence
  -> terminal slot determines runtime behavior

Command Registration

CLI commands are crossings in storage. Registering a command writes a crossing:

wanderland register --command deploy --for deploy-spec.yml
to_addr:   ":commands:deploy"
from_addr: ":sessions:graeme"
type_addr: ":types:command"
payload:
  archetype: cli
  spec: deploy-spec.yml

Running a command reads the crossing, loads the spec, hydrates the archetype:

wanderland deploy --target prod
# or via shim:
deploy --target prod

The shim is a symlink to wanderland — the binary name is passed as the command. deploy -> wanderland deploy -> look up :commands:deploy -> run.

Mode Inference

If no mode flag is given:

Cross-Mode Operation

wanderland --http scenario.yml boots a scenario as an HTTP service. The operation becomes a POST endpoint. The expected shape becomes the response validator. Same YAML, different adapter.

wanderland --test config.yml boots a service config but runs it as a one-shot health check. Hit /health, validate the shape, exit.

The mode is orthogonal to the archetype. Any config can run in any mode. The adapter just changes how fuel arrives and how the terminal slot behaves.

Commands as Crossings

The storage is the command registry:

The CLI is the engine with argv as fuel. Everything is a crossing.