> ## Documentation Index
> Fetch the complete documentation index at: https://docs.prodbreak.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Setting values & the trace

> Set out-of-control values by result, not by channel — and let the sandbox teach you its own surface.

A **pin** sets an [exogenous](/concepts/exogenous) value. But you don't have to pin everything — a
field you don't set falls back to a synthesized default, so a suite runs green with zero setup. You
pin only the values your test actually cares about.

## 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:

<Steps>
  <Step title="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.
  </Step>

  <Step title="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.
  </Step>

  <Step title="Synth — the default fallback">
    If you pinned nothing, ProdBreak fills the field with a **synthesized** value. It's the floor
    every out-of-control field lands on.
  </Step>
</Steps>

### 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.

```ts theme={null}
// no pin set for output.due_date → synth fills it, identically every run
const run = await client.taskRuns.create({ taskId: "task_rent" });
// run.output.due_date is shape-correct and stable; pin it only if a test asserts on it
```

<Note>
  **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? →](/fidelity))
</Note>

## Pin by result, never by channel

You pin against a **result key** — an `operation` plus a `field_path` — never against "the webhook" or
"the GET". There is no verb that names a channel.

```ts theme={null}
await sandbox.exogenous.pin(
  { operation: "runTask", credential_id: "cred_alice" }, // optional narrowing
  "output.balance",
  1432.18,
);
```

This is the data-side mirror of [causes-not-effects](/concepts/causes-not-effects): *pin the result,
let every channel render it.* Because the value lives in exactly one place, the webhook payload, the
`GET`, and the list **cannot disagree** — a divergent fixture is literally inexpressible.

```
output.balance := 1432.18   ◄ one place the value lives
   ├─ webhook renders it → 1432.18
   ├─ GET renders it     → 1432.18
   └─ list renders it    → 1432.18
```

## 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":

```ts theme={null}
// everyone gets 1432.18...
await sandbox.exogenous.pin({ operation: "fetchBalance" }, "output.balance", 1432.18);
// ...except Alice, whose more-specific pin wins for her calls
await sandbox.exogenous.pin({ operation: "fetchBalance", credential_id: "cred_alice" }, "output.balance", 0);
```

## 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. The `on(...)` sugar accepts exactly that phrasing and compiles to the same
result-keyed pin:

```ts theme={null}
await sandbox.exogenous.on("task_run.completed", "output.balance", 1000);
```

## 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:

```ts theme={null}
const trace = await sandbox.lastResolutionTrace();
// {
//   operation: "runTask",
//   outcome: "normal",
//   exogenous: [
//     { field_path: "output.balance",  source: "pin",   value: 1432.18 },
//     { field_path: "output.due_date", source: "synth",  key: "world+runTask+trun_8a2+output.due_date" }
//   ]
// }
```

<Tip>
  You rarely need to read pack docs first. Run the call, read the trace — it tells you *"this response
  had one exogenous field, `output.balance`, served by synth; pin it with…"* The override surface is
  discoverable at runtime.
</Tip>

<Note>
  **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.
</Note>
