Scenarios
#What Scenarios Are
Scenarios are seed scripts that put the dev database into a specific state. Instead of manually creating data through the app every time you want to test something, you run a scenario and get a repeatable starting point.
A scenario is an async function that uses the same db.push() calls as methods. If you can write a method, you can write a scenario.
#What Scenarios Don't Touch
Scenarios seed database tables. Nothing else. They do not touch file stores or data sources, and that's deliberate: both are durable, shared across dev and prod, and have no per-release copy to reset. There is nothing for a scenario to truncate.
So don't reach for a scenario to put documents into a data source, and don't write clear()-style reset helpers for one. To get a test corpus in place, add it once from the CLI:
mindstudio-prod datasources add --source policies --wait fixtures/*.pdf
Re-running that is free: documents are content-addressed, so an unchanged corpus transfers nothing and embeds nothing. That's what makes it safe to keep in a setup script alongside your scenarios.
#Defining Scenarios
In mindstudio.json:
{
"scenarios": [
{
"id": "ap-overdue-invoices",
"name": "AP: Overdue Invoices",
"description": "AP user with two invoices past due date.",
"path": "dist/methods/.scenarios/apOverdueInvoices.ts",
"export": "apOverdueInvoices",
"roles": ["ap"]
},
{
"id": "empty-requester",
"name": "Empty Requester",
"description": "Brand new user, no data.",
"path": "dist/methods/.scenarios/emptyRequester.ts",
"export": "emptyRequester",
"roles": ["requester"]
}
]
}| Field | Description |
|---|---|
id | Kebab-case identifier |
name | Display name (shown in CLI and dev panel) |
description | What state this scenario creates |
path | Path to the TypeScript file |
export | Named export (the async function) |
roles | Roles assigned to the dev test user after seeding (requires app auth) |
#Writing a Scenario
Scenarios live at dist/methods/.scenarios/. They're inside the methods package scope, so @mindstudio-ai/agent resolves normally and table imports are relative.
#Full Example
// dist/methods/.scenarios/apOverdueInvoices.ts import { db } from '@mindstudio-ai/agent'; import { Vendors } from '../src/tables/vendors'; import { PurchaseOrders } from '../src/tables/purchase-orders'; import { Invoices } from '../src/tables/invoices'; export async function apOverdueInvoices() { const vendor = await Vendors.push({ name: 'Acme Corp', contactEmail: 'billing@acme.com', status: 'approved', }); const po = await PurchaseOrders.push({ vendorId: vendor.id, requestedBy: 'user-requester-1', totalAmountCents: 500000, status: 'active', }); await Invoices.push([ { poId: po.id, invoiceNumber: 'INV-001', amountCents: 150000, dueDate: db.ago(db.days(5)), status: 'pending_review', }, { poId: po.id, invoiceNumber: 'INV-002', amountCents: 100000, dueDate: db.ago(db.days(2)), status: 'approved', }, ]); }
#Empty Scenario
// dist/methods/.scenarios/emptyRequester.ts export async function emptyRequester() { // No data — the truncate clears everything. // This scenario exists so you can switch to 0 state // without manually deleting records. }
#Composable Helpers
Shared setup code goes in dist/methods/.scenarios/_helpers/:
// _helpers/createTestVendor.ts export async function createTestVendor(overrides = {}) { return Vendors.push({ name: 'Test Vendor', contactEmail: 'test@example.com', status: 'approved', ...overrides, }); }
#Execution Flow
Running a scenario is three steps:
#1. Truncate
POST /_internal/v2/apps/{appId}/dev/manage/reset
Body: { "mode": "truncate" }Deletes all rows from all tables, preserving schema and IDs, so the seed function starts from empty.
#2. Execute the Seed
POST /_internal/v2/apps/{appId}/dev/manage/token
→ { "authorizationToken": "~~internal_identity_token~~..." }The CLI gets a fresh callback token scoped to the dev release, then transpiles and executes the scenario file in a child process with CALLBACK_TOKEN set. The SDK's db.push() calls route through the token to the correct dev database.
#3. Assign roles to the test user
POST /_internal/v2/apps/{appId}/dev/create-auth-session
Body: { "email": "remy@mindstudio.ai", "roles": ["ap"] }Assigns the scenario's roles to the dev test user, a real write to that user's row (requires app auth). Signing in as the test account now shows the app from the AP user's perspective.
#CLI Integration
#TUI Mode
Select "Run Scenario" from the actions menu:
Select Scenario ❯ AP: Overdue Invoices — AP user with two invoices past due Empty Requester — Brand new user, no data Admin: Busy Org — Admin with lots of pending approvals Back
After running:
✓ Scenario "AP: Overdue Invoices" applied Reset database, seeded data, test user roles: ap Sign in as the test account to see the new state.
#Headless Mode
JSON events on stdout:
{"event":"scenario-start","id":"ap-overdue-invoices","name":"AP: Overdue Invoices"}
{"event":"scenario-reset"}
{"event":"scenario-seeded","duration":234}
{"event":"scenario-complete","roles":["ap"]}The C&C server (in the sandbox) triggers scenarios via control messages to the tunnel.
#Why Scenarios Matter
- Living documentation. Each scenario is an executable description of an app state. "What does the AP dashboard look like with overdue invoices?" Run it and see.
- Deterministic. The same scenario always produces the same state: no accumulated test data, no "it worked on my machine."
- Demo-ready. Scenarios double as demo data for stakeholders.
- Visual regression. Screenshot each scenario and diff against the previous run to catch UI regressions across roles and data states.