From walkeros
Author, simulate, validate, and test walkerOS step examples for sources, transformers, and destinations in flows, spanning lifecycle from creation to CI integration.
npx claudepluginhub elbwalker/walkerosThis skill uses the workspace's default tool permissions.
Step examples are structured `{ in, out }` pairs (with optional `mapping` for
Creates isolated Git worktrees for feature branches with prioritized directory selection, gitignore safety checks, auto project setup for Node/Python/Rust/Go, and baseline verification.
Executes implementation plans in current session by dispatching fresh subagents per independent task, with two-stage reviews: spec compliance then code quality.
Dispatches parallel agents to independently tackle 2+ tasks like separate test failures or subsystems without shared state or dependencies.
Step examples are structured { in, out } pairs (with optional mapping for
destinations) that define the expected input/output behavior of each step in a
flow. They serve as:
it.each testingwalkeros push --simulateEvery source, transformer, and destination can ship step examples alongside its code.
Each step in a flow sits at a boundary between arbitrary external formats and the walkerOS event model:
Source Transformer Destination
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ in: arbitrary │ │ in: walkerOS │ │ in: walkerOS │
│ (HTTP req, DOM) │ │ Event │ │ Event │
│ │ │ │ │ │ │ │ │
│ ▼ │ │ ▼ │ │ ▼ │
│ out: walkerOS │ │ out: walkerOS │ │ out: arbitrary │
│ Event │ │ Event | false │ │ (gtag, API) │
└─────────────────┘ └─────────────────┘ └─────────────────┘
arbitrary ──────────── walkerOS.Event ──────────── arbitrary
false to filter)in/out/trigger FormatEvery step example is an object with in (input), out (array of observable
effects), and optional trigger (how to invoke):
type StepEffect = readonly [callable: string, ...args: unknown[]];
type StepOut = readonly StepEffect[];
interface StepExample {
in: unknown; // Platform-specific input
out: StepOut; // Array of effect tuples — see shape rules below
trigger?: { type?: string; options?: unknown }; // How to invoke
mapping?: unknown; // Destination mapping rule
command?: 'config' | 'consent' | 'user' | 'run'; // Route to walker command
}
out is always an array of effect tuples, even for a single effect. Each
tuple is [callable, ...args]. The first element is the callable's public name
(the SDK function users would write). Remaining elements are the arguments.
| Component type | Callable | Notes |
|---|---|---|
| Destination (SDK function) | 'gtag', 'fbq', 'ttq.track' | Public SDK name. Dotted paths render literally. |
| Destination (method on global) | 'analytics.track', 'dataLayer.push' | Method notation. |
| Destination (HTTP) | 'fetch', 'sendServer' | The actual call users would make. NOT env.* prop names — those are internal plumbing. |
| Source | 'elb' | The walker public push API. |
| Transformer | 'return' | Reserved keyword. Renders as return <value> (no parens). |
'return' is reserved. Don't use it for anything else.
Empty out: [] means the step produced no observable effect (filtered input,
transformer passthrough, validator rejection). Reserved only for cases where the
destination/source deliberately emits nothing.
in is an HTTP request shape, out is a tuple of the elb() call the source
makes:
export const checkoutPost: Flow.StepExample = {
trigger: { type: 'POST' },
in: {
method: 'POST',
path: '/collect',
body: { name: 'order complete', data: { id: 'ORD-123', total: 149.97 } },
},
out: [
['elb', { name: 'order complete', data: { id: 'ORD-123', total: 149.97 } }],
],
};
in is an HTML string, out is a tuple of the elb() call:
export const clickEvent: Flow.StepExample = {
trigger: { type: 'click', options: 'button' },
in: '<button data-elb="cta" data-elb-cta="label:Sign Up" data-elbaction="click:click">Sign Up</button>',
out: [
[
'elb',
{
name: 'cta click',
data: { label: 'Sign Up' },
trigger: 'click',
entity: 'cta',
action: 'click',
},
],
],
};
in is a walkerOS event, out is a ['return', value] tuple. An empty array
means the transformer passed the event through unchanged:
export const step = {
orderPasses: {
in: { name: 'order complete', data: { id: 'ORD-123' } },
out: [['return', { name: 'order complete', data: { id: 'ORD-123' } }]],
},
debugFiltered: {
in: { name: 'debug test', data: { message: 'noise' } },
out: [['return', false]], // Transformer rejects
},
passthrough: {
in: { name: 'page view' },
out: [], // No modification
},
};
in is a walkerOS event, mapping is the mapping rule that transforms it,
out is an array of call tuples — one per observable effect the destination
produces. Multi-call events (e.g., GA4 + Ads + GTM for a single walker event)
flatten into a single array in execution order:
import type { Flow } from '@walkeros/core';
import { getEvent } from '@walkeros/core';
export const purchase: Flow.StepExample = {
in: getEvent('order complete', { timestamp: 1700000000 }),
mapping: {
name: 'Purchase',
data: { map: { value: 'data.total', currency: { value: 'EUR' } } },
},
out: [
[
'fbq',
'track',
'Purchase',
{ value: 555, currency: 'EUR' },
{ eventID: '1700000000-gr0up-1' },
],
],
};
Multi-tool example (one walker event produces GA4, Ads, GTM in order):
out: [
['gtag', 'event', 'purchase', { transaction_id: 'o1', value: 555 }],
['gtag', 'event', 'conversion', { send_to: 'AW-123', value: 555 }],
['dataLayer.push', { event: 'purchase', ecommerce: { ... } }],
],
Each export is a self-contained Flow.StepExample — no intermediate variables,
no all aggregation. The mapping field ties the mapping rule to the example
so tests can register it dynamically:
{ [event.entity]: { [event.action]: example.mapping } }.
Consumers iterate all examples via Object.entries(examples.step) —
export * as step exposes every named export directly.
Some destinations need to respond to walker commands (walker consent,
walker user, walker run), not events. Set the command field to route in
through elb('walker <command>', in) instead of pushing it as an event:
export const consentGranted: Flow.StepExample = {
command: 'consent',
in: { marketing: true, functional: true },
out: [
'consent',
'update',
{ ad_storage: 'granted', analytics_storage: 'granted' },
],
};
Supported commands: config, consent, user, run.
command is absent (default), in is pushed as a regular event via
elb(in).command is set, mapping is not applied — commands don't flow
through event mapping.out format is destination-specific. For gtag it's
[action, subAction, params] matching gtag(...) calls.checkoutPost,
debugFiltered, pageViewBasic)context: {}, nested: [],
user: {}, etc.)examples/step.ts -- follow the existing dev.ts structuretitle (2-5 words) and
description (one short sentence, 10-25 words) for every example that should
appear in docs. Mark test-only fixtures with public: false.// src/examples/step.ts — only Flow.StepExample exports, nothing else
import type { Flow } from '@walkeros/core';
import { getEvent } from '@walkeros/core';
export const purchase: Flow.StepExample = {
in: getEvent('order complete', { timestamp: 1700000000 }),
mapping: { name: 'Purchase', data: { map: { value: 'data.total' } } },
out: ['track', 'Purchase', { value: 555 }, { eventID: '1700000000-gr0up-1' }],
};
// src/examples/index.ts
export * as env from './env';
export * as step from './step';
The file exports only Flow.StepExample objects. No intermediate variables,
no all, no config. Consumers iterate via Object.entries(examples.step).
Every Flow.StepExample accepts three optional metadata fields that control how
it surfaces in docs and MCP output:
title?: string — overrides the default camelCase-to-spaced heading in
website docs. Keep it short (2-5 words), human-readable.description?: string — one short sentence (10-25 words) rendered above
each example in docs and surfaced in MCP flow_examples output. Explains what
the example demonstrates, not how.public?: boolean — defaults to true. When false, the example is
excluded from the website docs render and from default MCP flow_examples
output. It still runs in tests and remains callable via CLI/MCP
flow_simulate.export const purchase: Flow.StepExample = {
title: 'Purchase',
description:
'A completed order mapped to the Meta Pixel Purchase standard event.',
in: getEvent('order complete', { timestamp: 1700000000 }),
mapping: { name: 'Purchase', data: { map: { value: 'data.total' } } },
out: [['fbq', 'track', 'Purchase', { value: 555, currency: 'EUR' }]],
};
// Internal fixture — runs in tests, hidden from docs and default MCP output.
export const debugFiltered: Flow.StepExample = {
public: false,
in: { name: 'debug test', data: { message: 'noise' } },
out: [['return', false]],
};
public: falseMark public: false when:
out: [['return', false]]) that only
proves filtering works.Keep public (omit the field) when:
Source step examples can include a trigger field for simulation:
{
in: '<button data-elb="cta">Sign Up</button>',
trigger: { type: 'click' },
out: { name: 'cta click', data: { label: 'Sign Up' } }
}
When simulating via CLI or MCP, the step example maps to SourceInput:
in -> content (the actual source input)trigger -> trigger (which mechanism to fire)Destination and transformer examples don't use trigger.
Use the --step flag to target a specific step, then provide the event as
SourceInput ({ content, trigger? }):
# Simulate a source step with trigger metadata
walkeros push flow.json --simulate source.browser --event '{"content":"<html>...","trigger":{"type":"click"}}'
The MCP flow_examples tool returns trigger metadata alongside in/out,
and mapping for destination examples, giving full visibility into how input
events are transformed to vendor-specific output.
Cross-step example validation is included automatically when validating a flow:
# Validate flow config including step example compatibility
walkeros validate flow.json
Flow validation checks that:
out types match transformer in typesout types match destination in typesThe primary use of step examples is automated testing with it.each.
Sources export createTrigger from their examples. It follows the unified
Trigger.CreateFn interface: receives Collector.InitConfig, lazily starts the
flow, and returns a trigger function that simulates real-world invocations.
Each package implements createTrigger differently:
fetch() requestsUse a spy destination to capture events:
import type { Destination, WalkerOS } from '@walkeros/core';
import { sourceExpress } from '@walkeros/server-source-express';
import { examples } from '@walkeros/server-source-express/dev';
describe('Step Examples', () => {
it.each(Object.entries(examples.step))('%s', async (name, example) => {
const events: WalkerOS.Event[] = [];
const spy: Destination.Instance = {
type: 'spy',
config: { init: true },
push: jest.fn((event) => {
events.push(JSON.parse(JSON.stringify(event)));
}),
};
const instance = await examples.createTrigger({
consent: { functional: true },
sources: {
express: { code: sourceExpress, config: { settings: { port: 0 } } },
},
destinations: { spy: { code: spy } },
});
await instance.trigger(example.trigger?.type)(example.in);
const found = events.find((e) => e.name === example.out.name);
expect(found).toBeDefined();
});
});
Browser source note: The browser source's elbLayer processes events via a detached promise chain (fire-and-forget). For interactive triggers (click, submit), poll until events arrive:
while (!events.length) await Promise.resolve();
CMP sources push walker consent commands, not regular events. Assert on
collector consent state instead of using a spy destination:
const instance = await examples.createTrigger({
consent: {},
sources: {
usercentrics: {
code: sourceUsercentrics,
config: { settings: {} },
},
},
});
await instance.trigger(
example.trigger?.type,
example.trigger?.options,
)(example.in);
// Yield for detached elb('walker consent') chain
while (!Object.keys(instance.flow!.collector.config.consent || {}).length)
await Promise.resolve();
expect(instance.flow!.collector.config.consent).toEqual(
expect.objectContaining(expected),
);
Server function handlers (fetch, AWS Lambda, GCP CloudFunction) don't own
servers. Their createTrigger accesses the source instance from
collector.sources after startFlow and calls source.push() with
platform-native types:
function findSource(collector) {
for (const source of Object.values(collector.sources || {})) {
if (source.type === 'fetch') return source;
}
}
// In trigger:
const source = findSource(flow.collector);
const response = await source.push(request);
For destinations, use startFlow + elb() to run events through the real
collector pipeline. This verifies the full flow including mapping:
import { startFlow } from '@walkeros/collector';
import { clone } from '@walkeros/core';
import { examples } from './dev';
describe('Step Examples', () => {
it.each(Object.entries(examples.step))('%s', async (name, example) => {
const event = example.in as WalkerOS.Event;
const mapping = example.mapping as Rule | undefined;
const mockFn = jest.fn();
const env = clone(examples.env.push);
env.window.fbq = mockFn;
const dest = jest.requireActual('.').default;
const { elb } = await startFlow({ tagging: 2 });
// Build mapping config from event entity/action
const mappingConfig = mapping
? { [event.entity]: { [event.action]: mapping } }
: undefined;
elb(
'walker destination',
{ ...dest, env },
{
settings: { pixelId: '1234567890' },
mapping: mappingConfig,
},
);
await elb(event);
expect(mockFn).toHaveBeenLastCalledWith(...(example.out as unknown[]));
});
});
For transformers that can return false:
import { examples } from '../dev';
describe('transformer', () => {
it.each(Object.entries(examples.step))(
'%s',
async (name, { in: input, out: expected }) => {
const result = await transformer.push(input, context);
if (expected === false) {
expect(result).toBe(false);
} else {
expect(result).toEqual(expected);
}
},
);
});
When adding step examples to a package or flow:
src/examples/step.ts with Flow.StepExample exportssrc/examples/index.tsfalse (filtered) caseit.each test using step examplesnpm run buildnpm run testtitle and description to every public example.public: false.