The engine is a VM. The archetype is the combustion sequence. The user config is the fuel. Crossings are the exhaust.
The engine runs two slot sequences.
The boot sequence is fixed. runtime.yml lists the slots: load boundaries, configure resolvers, mount storage, mount adapters, capture env, register injections, mount routes. Sites do not pick this sequence — it runs the same way for every boot.
The per-request sequence is the route's chain. A route declares it in one of two ways. Inline: a chain: field listing boundaries. Or via an archetype: a named YAML template registered by a pack, with !UserConfig knobs the user fills with their own implementation choices.
Each slot binds a boundary. Each boundary produces a crossing. Same engine, two different fuel sources.
A work archetype is a YAML file shipped by a pack. It defines the structural chain for a route: the sequence of boundaries that always run plus !UserConfig knobs where the user names a specific implementation.
A pack registers its archetypes at gem-load:
Wanderland::Archetypes.register(
"infrastructure_deployment",
File.expand_path("archetypes/infrastructure_deployment.yml", __dir__)
)
The user picks one in config:
archetype: infrastructure_deployment # default for every route
provisioner: cfn_provisioner # fills the provisioner knob
Or per route:
routes:
/deploy:
archetype: infrastructure_deployment
provisioner: cdk_provisioner
Route-level overrides top-level. A route without an archetype uses its chain: field directly. An unknown archetype name fails at boot with the registered list.
An archetype that ships from a deployment pack defines the chain shape and exposes the knobs the user has to fill:
# wanderland-pipelines-pack/lib/.../archetypes/infrastructure_deployment.yml
name: infrastructure_deployment
slots:
- name: start_run
boundary: start_run
args:
system: !UserConfig system
environment: !UserConfig environment
- name: provision
boundary: !UserConfig provisioner
args:
template: !UserConfig template
parameters: !UserConfig parameters
- name: publish
boundary: publish_stack
- name: complete
boundary: complete_run
Three of the slots name fixed boundaries (start_run, publish_stack, complete_run). One — provision — names its boundary as a !UserConfig thunk. The user's config decides which provisioner runs:
archetype: infrastructure_deployment
provisioner: cfn_provisioner
template: stacks/web.yml
parameters: { instance_type: t3.medium }
!UserConfig thunks resolve at mount time. UserConfig.data is scoped to the route — config domain merged with the route's own spec — so the same archetype produces different chains per route when the route fills knobs differently.
The pack ships the boundary classes (cfn_provisioner, cdk_provisioner, start_run, etc.). The pack's entry file globs its boundary directory and requires each file; the existing boundary :name, ... DSL self-registers.
The boot init chain lives at lib/wanderland/runtime.yml. It is the same for every site:
name: runtime
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: adapters
boundary: boot_mount_adapters
args:
config:
adapters: !UserConfig adapters
- name: triggers
boundary: boot_mount_triggers
args:
config:
triggers: !UserConfig triggers
- name: env
boundary: env_snapshot
args:
env_allowlist: !UserConfig env_allowlist
- name: denials
boundary: context_from_shape
args:
under: denials
shape: !UserConfig denials
capabilities: [denials]
- name: core_injections
boundary: register_injections
args:
injections:
- { boundary: enforce_denials, position: interleave }
- { boundary: verify_route, position: last }
- { boundary: trace_emit, position: last }
- { boundary: format, position: last }
- { boundary: seal, position: last }
- name: user_injections
boundary: register_injections
args:
injections: !UserConfig injections
- name: archetype
boundary: boot_mount_archetype
args: {}
The terminal slot is boot_mount_archetype. It mounts core routes, then walks the user's routes. For each route it picks an archetype:
archetype: field, if set.archetype: field, if set.chain: field is the chain.When an archetype is named, the registered YAML loads, slots resolve through TemplateEngine with UserConfig.data scoped to (config domain ⊕ route spec), and the resolved slot list becomes the route's chain.
An unknown archetype name fails at boot. The error lists the archetypes registered through Wanderland::Archetypes so the operator can see which packs contributed what.
Every level of the system is the same loop:
Create potential → burn through boundaries → produce crossings. Same engine. Different fuel.
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.
Three artifacts carry the model.
runtime.yml lives in wanderland-core, next to runtime.rb. One file, one boot sequence. Sites do not pick from a menu of boot archetypes.
Wanderland::Archetypes is a registry: register(name, path), lookup(name), load(name), registered. Packs register their archetype YAMLs from their gem entry file. Site-local archetypes can register the same way from config.ru or a Rakefile.
boot_mount_archetype is the terminal boundary in the boot chain. It owns the per-route resolution: route override → top-level default → chain field → fail loud.
A pack that contributes archetypes follows two conventions. Boundaries: glob lib/<pack>/boundaries/**/*.rb and require each — the boundary :name, ... DSL self-registers when the file loads. Archetypes: glob lib/<pack>/archetypes/*.yml and call Wanderland::Archetypes.register(name, path) for each. No new DSL. The pack's entry file is the single integration point.
wanderland.dev
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:
!UserConfig per route!UserConfig at boot!UserConfig at invocationwanderland [--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
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
:commands:deploy from storage!UserConfigThe shim is a symlink to wanderland — the binary name is passed as the command. deploy -> wanderland deploy -> look up :commands:deploy -> run.
If no mode flag is given:
mode: http -> Rack adaptermode: test -> YAML adapter, validate on exitmode: cli or no mode -> CLI adapter.yml with expected: key -> infer test modewanderland --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.
The storage is the command registry:
:commands:name:commands:name:commands:The CLI is the engine with argv as fuel. Everything is a crossing.