pikopod scenario check drives its own requests against a throwaway engine, so your application is never in the loop. A mode is the other half: it arms the scenario’s standing conditions on the sandbox pikopod up is serving, and then your own tests, your own app, Postman, or plain curl meet the failure.
http://127.0.0.1:4600/examplepay. Every GET /charges is delayed, and your timeout handling is what gets exercised.
What a mode arms
The scenario’s standing conditions: the faults it injects and the state it seeds. Its request steps are not run; those are yours to make. A scenario whose first step is a request has no standing state and is refused with the reason.Scoped, or global
A mode is set on the sandbox and stays until cleared. What each test sees is partitioned by one request header,X-Pikopod-Scope. Three things are partitioned: fault times and per windows, the request journal, and the recordings cursor. Two workers that each send their own scope get their own 503, 503, 201 under the same idempotency key, and mode verify --scope judges one partition:
mode verify judges only that partition. A scoped client never consumes an unscoped window, and the reverse. The Jest and pytest helpers send the header per test with scoped().
The header is stripped before the request is journaled, so a scope token never appears in pikopod requests output or in a recording. A sandbox keeps at most 256 live scopes; when a 257th arrives, the scope idle longest is forgotten, with its journal and windows, and the control plane counts the eviction. A scope longer than 128 characters is refused with 400.
clear leaves the standing state and clears every fault on the sandbox, in every scope.
Verify what your tests sent
A mode arms the failure.verify asks whether your client did what the scenario expects once it met that failure. It reads the sandbox’s request journal and judges it against the scenario’s verification steps: request order and spacing, webhook counts, stored state. It never arms, seeds, or resets anything.
retry_storm refuses the first two creates under one idempotency key with 503 and answers the third, so a client that retries correctly sends three. Then:
503 fails, and the summary says which expectation the journal never met and which journaled request came closest:
--format junit or --format githubactions turns the verdict into a build artifact, one testsuite for the mode and one testcase per verification step, the same shapes scenario check writes:
0 when every step passed, 1 when the client fell short, 2 when no mode is set or the mode’s scenario has no verification step. Spacing between requests is judged on wall-clock time, because your client made them in real time. Clear the mode between suites, or give each suite its own scope; otherwise the next suite is judged against the last one’s requests too.
From a test
The Go packagepkg/sandboxtest does the same inside a test, with no pikopod up: New, Mode, run your code, Verify. See Use it from a test.
From a coding agent
The same operations are MCP tools:set_mode, mode_verify, clear_mode, and get_requests to read the journal itself. See MCP tools.