| name | usecase-vitest |
| description | Human-led API session that authors and debugs a vitest integration test for one of the 5 fixed AtomicFi use-cases, then optionally fans out to MDX (Docusaurus cookbook) and Bruno (collection) generator agents in parallel. Triple purpose — QA pass, in-session bug fixing, and demo-ready output. Use when the user wants to walk through `setup-platform`, `onboard`, `transact`, `respond-to-aml-document-request`, or `add-documents`. |
| when_to_use | ["QA walkthrough of a business use-case end-to-end against the live API","Authoring or extending a vitest integration test in `integration-tests/tests/`","Preparing a live demo (the vitest run IS the demo tape)","Investigating and fixing bugs discovered during the walkthrough (default behavior)"] |
| related_artifacts | ["integration-tests/tests/cookbook/<slug>.test.ts","integration-tests/recordings/<slug>/*.jsonl","api-docs/docs/cookbook/<slug>.mdx (generated by vitest-to-mdx agent)","bruno/<slug>/ (generated by vitest-to-bruno agent)"] |
Use-Case Vitest Walkthrough
Drive a use-case end-to-end against the live API by authoring an integration test and debugging it in-session. When green, fan out to the MDX and Bruno generator agents — they consume the test file plus its recorded request/response payloads and produce the cookbook page and Bruno collection.
This is the only interactive step in the pipeline. The downstream agents are pure transforms.
The 5 fixed use-cases
$ARGUMENTS MUST be one of these slugs. If missing, ask which.
| Slug | Business outcome |
|---|
setup-platform | Platform tenant exists, platform admin can sign in, API keys minted |
onboard | A customer / account holder / counterparty is onboarded, screened, KYC-approved |
transact | Payment account funded, ledger entries posted, transaction visible |
respond-to-aml-document-request | An open AML/KYC document request is satisfied and resolved |
add-documents | Documents attached to an entity (account holder, counterparty, legal entity) |
Prerequisite chain: setup-platform → standalone. onboard requires setup-platform. transact / respond-to-aml-document-request / add-documents require onboard. State is shared across runs via integration-tests/vitest-state/<runId>/.
Architecture (mirrors work/alvera-ai/platform-sdk/integration-tests/)
integration-tests/
package.json
vitest.config.ts # singleFork, fileParallelism: false, ManifestSequencer
vitest.sequencer.ts # reads tests/_order.json
vitest.setup.ts
src/
api.ts # raw fetch wrapper (no SDK in this repo)
env.ts # baseUrl, rootEmail, rootPassword
state.ts # loadBase / loadSpec / saveSpec — per-runId state slices
recording.ts # recordingFetch — wraps fetch, writes recordings/<slug>/<NN>.jsonl
tests/
_order.json # ["setup-platform","onboard","transact",...]
setup-platform.test.ts
onboard.test.ts
transact.test.ts
respond-to-aml-document-request.test.ts
add-documents.test.ts
vitest-state/
base.state.json # creds + names (gitignored, never mutated by tests)
<runId>/
<slug>.state.json # IDs and tokens written by each spec
recordings/
<slug>/
01-create-tenant.jsonl
02-create-user.jsonl
... # one jsonl per `it("§N — ...")` — handoff to mdx/bruno agents
State pattern (per platform-sdk):
beforeAll calls loadBase(slug) + loadSpec(slug, '<spec>').
- Each
it("§N — ...") short-circuits via ctx.skip() if its output already exists in state — re-runs are idempotent.
saveSpec is called at the end of each it() after a successful step.
Recording pattern (specific to this skill):
recordingFetch(stepLabel, init) writes one {step, request, response, status, durationMs, timestamp} JSON line per call to recordings/<slug>/<NN>-<step>.jsonl.
- Step number =
§N index. The agents read these to build MDX code blocks and Bruno requests.
- Recordings are committed (they're documentation source-of-truth).
vitest-state/ is gitignored.
CRITICAL RULES
- WAIT for explicit human instructions before each new step. Don't anticipate.
- NEVER invent endpoints. Source from human, OpenAPI spec, or
lib/atomic_fi_api/routes.ex.
- NEVER skip a broken step silently. Stop and use the Investigate-and-Fix protocol.
- NEVER file a GitHub issue unless explicitly asked. Default is to fix in-session.
- One
it("§N — ...") per state-machine transition. Mirrors platform-sdk Pattern ①.
- Use
recordingFetch for every API call so the agents have the artifacts they need.
- All commits GPG-signed (
git commit -S) per ~/.claude/CLAUDE.md. No Co-Authored-By trailers — disrespectful per user prefs.
Phase 1: Initialization
1.1 Validate slug
Reject anything not in the 5-slug list with the valid options.
1.2 Bootstrap integration-tests/ if missing
test -d integration-tests && test -f integration-tests/vitest.config.ts
If missing, scaffold it modeled on work/alvera-ai/platform-sdk/integration-tests/:
package.json with vitest, tsx, types
vitest.config.ts — pool: 'forks', singleFork: true, fileParallelism: false, sequencer: ManifestSequencer
vitest.sequencer.ts reading tests/_order.json
src/api.ts, src/env.ts, src/state.ts, src/recording.ts
tests/_order.json seeded with the 5 slugs in dependency order
dev.env with BASE_URL=http://localhost:4000, ROOT_EMAIL=admin@dev.local, ROOT_PASSWORD=devpassword
- Add
integration-tests/vitest-state/ and integration-tests/node_modules/ to .gitignore
- Commit the bootstrap separately:
chore: bootstrap integration-tests harness
Ask the human before bootstrapping.
1.3 Check existing artifacts
ls integration-tests/tests/cookbook/<slug>.test.ts 2>/dev/null
ls integration-tests/recordings/<slug>/ 2>/dev/null
- Test exists → ask: "Extend or rewrite?"
- Test missing → start fresh
1.4 Open session log (gitignored)
.walkthrough-sessions/<slug>-<YYYY-MM-DD>.md. On first run ever, add .walkthrough-sessions/ to .gitignore.
# Vitest Session — <slug>
Date: <YYYY-MM-DD>
Branch: <git branch --show-current>
## Bugs found and fixed
(none yet)
## Steps completed
(none yet)
1.5 Boot environment
- App reachable:
curl -sf http://localhost:4000/api/info
- DB seeded: ask before running
mix ecto.reset
- Tidewave MCP available
cd integration-tests && pnpm install if node_modules missing
Phase 2: Authentication step (always §1)
The first it() of every spec is authentication. For setup-platform, that's the platform admin session. For other slugs, that's loading the API key from setup-platform's state slice and verifying it.
it('§1 — platform admin signs in', async (ctx) => {
if (s.rootBearer) {
try { await api.sessions.verify(s.rootBearer); ctx.skip(); return } catch {}
}
const res = await recordingFetch('01-sign-in', {
method: 'POST',
url: '/api/sessions',
body: { email: config.rootEmail, password: config.rootPassword },
})
expect(res.status).toBe(201)
s.rootBearer = res.body.session_token
saveSpec(SLUG, SPEC, s)
})
Auth values live in dev.env and base.state.json — never inlined in test files.
Phase 3: The Walkthrough
For each step the human dictates:
- Add an
it("§N — <transition>") block to the test file.
- Wrap the API call in
recordingFetch(stepLabel, ...) — this is what the downstream agents consume.
- Add idempotency short-circuit — check the state slice; skip if already done.
- Add assertions on status + key fields.
- Save state slice at the end of the
it().
- Run just this spec:
cd integration-tests && pnpm vitest run tests/cookbook/<slug>.test.ts
- If green → append session log: "§N: PASSED". Ask for the next step.
- If broken → Phase 5 (Investigate-and-Fix). Never skip.
Recording file format
recordings/<slug>/<NN>-<step>.jsonl — one line per recordingFetch call:
{"step":"§3","label":"create-account-holder","request":{"method":"POST","url":"/api/account-holders","headers":{"x-api-key":"<redacted>","content-type":"application/json"},"body":{"holder_type":"individual","legal_entity_id":"...","status":"pending"}},"response":{"status":201,"body":{"data":{"id":"...","holder_type":"individual"}}},"durationMs":42,"timestamp":"2026-04-30T12:34:56Z"}
Sensitive headers (x-api-key, session tokens) are redacted to <redacted> at recording time. The agents will substitute Bruno env vars ({{apiKey}}) when generating outputs.
Phase 4: Conventions worth keeping (from platform-sdk)
- Single fork, no parallelism — state files thread between specs in dependency order.
vitest.config.ts enforces this.
UUID_RE constant for asserting UUID-shaped fields.
§N JSDoc cross-references at top of every test file pointing back to the cookbook section it produces.
ctx.skip() short-circuit — the spec is its own state machine; re-runs are cheap and idempotent.
- No mocks, ever (matches CLAUDE.md). Real API, real DB.
Phase 5: Investigate-and-Fix Protocol (default when stuck)
Stop and present:
Stuck at §N: <what failed, expected vs. actual>
A) Investigate & fix now (default)
I'll check tidewave logs, query DB, inspect source, propose a fix,
apply it, re-run §N, and log everything.
B) File a GitHub issue instead
Only if you tell me to.
Path A (default)
- Reproduce — capture the exact recording entry. Save request + response to session log.
- Investigate via tidewave:
get_logs — last 50 lines, look for stack traces / changeset errors
execute_sql_query — verify DB state (was the row inserted? FK valid? RLS scope right?)
project_eval — inspect changeset / struct / module behavior
- Identify root cause — one paragraph in plain English.
- Propose the fix — describe before editing. Wait for ack.
- Apply the fix — edit Elixir source. Honor CLAUDE.md (no mocks, struct-passing controllers, RLS-aware contexts).
- Pre-commit checklist:
mix format
mix credo --strict
mix test <relevant test file>
- Re-run the failed
it() — confirm green.
- Commit (GPG signed):
git commit -S -m "fix: <concise description>
<one-paragraph why>"
- Update session log:
### Bug: <symptom>
- Repro: §N — <step label>
- Root cause: <one paragraph>
- Fix: <files changed> — commit <sha>
- Verified: §N now PASSED
- Continue from §N+1.
Path B (only when explicitly asked)
Standard gh issue create flow with screenshots/recordings attached. Then continue.
Phase 6: Wrap Up — Fan Out to Agents
When the human says "done" / "wrap" / "finish" — or when all it() blocks pass — gate-check:
cd integration-tests && pnpm vitest run tests/cookbook/<slug>.test.ts
Must be all-green. If reds remain, back to Phase 5. Do not spawn the agents on a red test.
6.1 Commit the test + recordings (separate from any fix commits)
git add integration-tests/tests/cookbook/<slug>.test.ts \
integration-tests/tests/_order.json \
integration-tests/recordings/<slug>/
git commit -S -m "test: add <slug> integration walkthrough
- Vitest spec: integration-tests/tests/cookbook/<slug>.test.ts (N transitions, all green)
- Recordings: integration-tests/recordings/<slug>/*.jsonl
- Verified end-to-end against local API"
6.2 Offer the fan-out
Ask the human:
Vitest is green. Generate downstream artifacts now?
[a] MDX cookbook page (api-docs/docs/cookbook/<slug>.mdx)
[b] Bruno collection (bruno/<slug>/)
[c] Both in parallel (recommended)
[d] Skip for now
If [c] — spawn vitest-to-mdx and vitest-to-bruno in a single message with two Agent tool calls (parallel).
If [a] or [b] — spawn just that one.
If [d] — close the session log and stop.
6.3 React component (future)
Do not offer to spawn vitest-to-react automatically — it's a stub. If the human asks for it, spawn it; otherwise leave it for later.
6.4 Close session log
## Session summary
- Steps completed: N
- Bugs fixed: M (commits: <sha>, <sha>)
- Outstanding: <list, or "none">
- Demo-ready: yes / no (if no, why)
- Agents spawned: vitest-to-mdx, vitest-to-bruno
Session log file is never committed (gitignored).
Tool Reference
| Tool | When to use |
|---|
Bash (pnpm vitest) | Run the spec under construction |
Bash (curl) | Quick out-of-band probes; not a substitute for recordingFetch inside the test |
Read / Edit / Write | Edit test file, recordings, session log, source fixes |
Agent (vitest-to-mdx) | Phase 6 fan-out — generate cookbook page |
Agent (vitest-to-bruno) | Phase 6 fan-out — generate Bruno collection |
Agent (vitest-to-react) | Future — only on explicit ask |
Tidewave: get_logs | First stop when a request fails |
Tidewave: execute_sql_query | Verify DB state |
Tidewave: project_eval | Inspect changesets / structs |
Quality Checklist (before Phase 6 fan-out)