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

# Simulations

> Build the evidence for an improvement: estimate, run, poll, promote

Simulations are how you **make evidence** for a proposed fix — run a candidate change against a curated dataset, measure the outcome delta, and (if it looks good) promote it into the improvement queue for a human to approve.

Authenticated by **PAT `simulate` scope**. Simulations are **paid** — they run LLM calls against your dataset and bill against your workspace LLM keys.

## Budget is required

A `budget` is **required** on every run (no default — a runaway spend would be silent). Two shapes:

* `budgetUsd: <n>` — hard USD cap.
* `budgetMinutes: <n>` + `confirmUsdCeiling: <n>` — time cap, plus an expected-cost ceiling the SDK confirms before starting.

The SDK has no human in the loop, so a bare `budgetMinutes` (with no cost ceiling) is refused — you'd have no way to abort a runaway.

## Verbs

Simulations are long-running: `run` returns a `roundId` immediately and you poll for progress.

```ts theme={null}
// Estimate cost / duration for a config, without running
const est = await nora.simulations.estimate({ /* config */ });

// Start a run — returns immediately
const run = await nora.simulations.run("lin_1", {
  kind: "consistency",              // simulation kind
  budgetUsd: 5,                     // or: budgetMinutes: 30, confirmUsdCeiling: 5
});
run.roundId;                         // use this to poll

// Poll for progress + results
await nora.simulations.get(run.roundId);
await nora.simulations.remaining(run.roundId);   // just the remaining-work count

// Re-rank results without re-running (free — no new LLM calls)
await nora.simulations.requery(run.roundId);

// Promote a good result into an improvement proposal
await nora.simulations.promote(run.roundId);
```

### `estimate`

No LLM calls. Server returns projected cost and duration for the config you'd pass to `run`. Use this to gate — "if the projection exceeds \$X, don't run."

### `run(lineageId, opts)`

Kicks off a simulation. `lineageId` identifies which change lineage you're testing. Returns `{ roundId }` immediately.

### `get(roundId)`

Full round state — progress, per-case verdicts, aggregate delta, provider errors if any. Poll until done.

### `remaining(roundId)`

Just the count of remaining cases. Lighter than `get` — right for a progress bar.

### `requery(roundId)`

Re-rank the same case results with a different scoring config. **Free** — no new LLM calls, just a re-scoring pass on the stored outputs. Use this to see how sensitive the "did this help?" answer is to your scoring choice.

### `promote(roundId)`

Take a completed round and file it as an improvement proposal. It then flows into the [improvement queue](/sdk/improvements) for a human to approve — the simulation is *evidence*, approval is what ships.

## Common patterns

### Estimate → gate → run

```ts theme={null}
const est = await nora.simulations.estimate({ /* config */ });

if (est.projectedUsd > 10) {
  throw new Error(`Simulation projected $${est.projectedUsd} — over $10 ceiling, refusing`);
}

const run = await nora.simulations.run("lin_1", {
  kind: "consistency",
  budgetUsd: Math.ceil(est.projectedUsd * 1.5),   // 50% headroom
});
```

### Poll with a progress bar

```ts theme={null}
const run = await nora.simulations.run("lin_1", { kind: "consistency", budgetUsd: 5 });

for (;;) {
  const remaining = await nora.simulations.remaining(run.roundId);
  ui.progress({ done: total - remaining.count, total });
  if (remaining.count === 0) break;
  await sleep(5000);
}

const result = await nora.simulations.get(run.roundId);
if (result.delta > 0.10) {
  await nora.simulations.promote(run.roundId);  // → improvement queue
}
```

### Time budget with confirmation

If you only care about wall time, pair `budgetMinutes` with a cost ceiling the SDK checks against `estimate` before starting:

```ts theme={null}
await nora.simulations.run("lin_1", {
  kind: "consistency",
  budgetMinutes: 30,
  confirmUsdCeiling: 5,   // refuse to start if estimate > $5
});
```

## Where simulations sit in the loop

```
Failures → Signals → Clusters → [Simulations = evidence] → Improvements → Approve → Version
                                       ↑ SDK exposes                 ↑ SDK exposes
```

The SDK surfaces `simulations` (the evidence generator) and `improvements` (the decision queue). Making the initial improvement candidates from clusters is what the loop's internals do — not an SDK verb. Once a candidate exists, you can re-simulate it here to refresh the evidence, then `promote` back into the queue.
