How a value is resolved: pin → tape → synth
When ProdBreak needs an out-of-control value, it looks for a source in this order and takes the first that matches:A pin you set
An explicit value you pinned for this result (most-specific match wins). This is the surface you
use day to day — the rest of this page.
A tape (roadmap)
A sequenced answer for the “same call, evolving result” case — e.g. a running total that changes
across repeated calls. Order-dependent by nature, so it’s post-MVP; reach for a pin first.
What synth gives you
A synth value is deterministic and shape-correct — it matches the field’s declared schema (type, format, enum), and it’s keyed by(world, operation, field) so the same field comes back the
same value on every run. That’s what lets a whole suite pass without a single pin: nothing is
random, nothing flakes, and you only pin the fields an assertion actually depends on.
Synth fills, it never lies. Some fields are declared empty-by-default when an empty value is
the honest representation (a not-yet-produced result) — ProdBreak leaves those null rather than
inventing data, and never randomly nulls a field. Synth invents a plausible leaf value; it never
invents history or causality. (How faithful is it? →)
Pin by result, never by channel
You pin against a result key — anoperation plus a field_path — never against “the webhook” or
“the GET”. There is no verb that names a channel.
GET, and the list cannot disagree — a divergent fixture is literally inexpressible.
Match specificity is the scoping knob
The most-specific matching pin wins. That single mechanism handles both “give everyone the same value” and “give this one user a different value”:Author in the vocabulary you think in
Developers think “when the webhook fires, return $1000.” That’s fine — the webhook firing is the producing transition. Theon(...) sugar accepts exactly that phrasing and compiles to the same
result-keyed pin:
The resolution trace: the mock teaches its own surface
After any call, ask why a field came back the way it did. The trace names every exogenous field, where its value came from (a pin you set, or synth), and the match that fired:Pins cover the common case. For the rarer “same request, evolving answer” (a running total over
repeated identical calls), there’s a sequenced primitive — but it’s order-dependent by nature and
post-MVP. Reach for a pin first; narrow it by input or credential when calls differ.