Twins

Guides

Custom scenarios

A scenario is a starting state plus an expectation. Twins give you three scenario mechanisms today — all executable, none hand-waved — and each is verified the same way the twins themselves are: by running against real seeded state.

1 · Describe a world in natural language

The blank-harness path turns a plain-language description into a seeded, executable starter world (tables, tools, tasks) on the hosted runtime:

Prompt-seeded world
$ curl -sS -X POST https://blobfish.ai/api/v1/twin/environments \
    -H "X-API-Key: $BLOBFISH_API_KEY" -H "Content-Type: application/json" \
    -d '{"prompt":"An ops desk mid-incident: two overdue orders, one angry ticket, an empty on-call rotation"}'

Generation runs asynchronously (the response is honestly 202 building); poll the listing until it is ready. To test against real vendor twins rather than a synthetic starter world, pass services instead — the prompt path seeds a scenario-shaped company; the services path mounts the branded twins.

2 · Run a twin’s own scenario suite

Every mounted service twin carries executable scenarios — read scenarios assert state-hash invariance, write scenarios assert a required state diff. List them, then execute them live:

Per-service scenarios
# declared suite
$ curl -sS https://blobfish.ai/api/v1/sandbox/worlds/<world_id>/services/slack/scenarios \
    -H "X-API-Key: $BLOBFISH_API_KEY"

# execute it against live state (run=true)
$ curl -sS "https://blobfish.ai/api/v1/sandbox/worlds/<world_id>/services/slack/scenarios?run=true" \
    -H "X-API-Key: $BLOBFISH_API_KEY"

Scenario evidence is tri-state on purpose: passed / failed / not_run — absent evidence is recorded as absent, never published as a pass.

3 · Fork state per scenario

Sessions are the isolation primitive: each scenario (or CI shard) gets a copy-on-write fork of the seeded baseline, mutates it freely, and resets it in one call — the shared baseline never moves.

Session-per-scenario
$ ./bf-twin session new                 # export BLOBFISH_TWIN_SESSION=sess_…
$ ./bf-twin slack call chat.postMessage --json '{"token":"xoxb-test","channel":"C0000001","text":"scenario A"}'
$ ./bf-twin slack reset                 # this fork only — baseline untouched

Determinism contract

  1. Seeds are stable per twin (<service>-seed-1): identical starting rows on every run, addressable ids your assertions can pin.
  2. Writes return row-level diffs; /state returns the tables — assert on state, not just response bodies.
  3. Reset restores the seed baseline exactly — including for the shared world when you own it.

Roadmap: saved scenario records

A named, reusable scenario registry — save a seeded configuration once, load it into any future environment by id — is on the roadmap and not yet served; today you compose the three mechanisms above. The environment console labels roadmap items exactly the same way (“coming soon”, never a fake control).

Next

Twin reference — what each twin seeds and guards · Quickstart — key to environment in a minute.