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

# Improvements & versions

> Embed the improvement queue in your product: surface fixes, let a human approve, ship the version

An **improvement** is not a cluster — clusters are grouped failures; an improvement is a **concrete fix proposal aimed at one cluster**, with a title, `before → after` diff, and a measured delta from simulation. The loop generates and measures these; the SDK is where your product **shows the queue and lets a human decide**.

Embedding this in your product means customers approve fixes from inside your admin surface — no jump to the Nora dashboard, but the single-approval gate is preserved (only the actor recorded in the audit changes).

Authenticated by **PAT `read`** for browsing, **PAT `approve`** for decisions.

## The `Improvement` shape

`improvements.list` / `improvements.get` return objects with:

* `id`, `kind`, `title`, `status`
* `addresses` — number of failures this improvement fixes
* `delta` — simulated improvement magnitude (`null` when not yet measured)
* `clusterKey` — the cluster this improvement targets
* `graphSlug` — the Flow slug the improvement applies to (`null` for workspace-scoped fixes)
* `targetAgentId` / `targetAgentName` — the agent being changed
* `diagnosis` — why (from the loop's classifier)
* `proposal` — the primary `before → after` (mirrors `changes[0]`)
* `changes[]` — full typed change-set `{ surface, target, before, after, rationale }`
* `compare` — 2-axis simulation result (before vs after)

## Browsing

```ts theme={null}
const queue = await nora.improvements.list("proposed");  // status filter; read scope
const one   = await nora.improvements.get("imp_1");       // full: diagnosis, changes, compare
```

Status values: `proposed` (waiting for a decision), `approved` (adopted → deployed), `rejected` (dismissed), `reverted` (a deployed fix rolled back via `revert`). New statuses can be introduced server-side — treat as an extensible string.

## Deciding

```ts theme={null}
await nora.improvements.approve("imp_1");                     // adopt → deploy new version
await nora.improvements.approve("imp_2", { edits: [0, 2] }); // adopt only these entries from changes[]
await nora.improvements.reject("imp_3");                      // dismiss (advisory)
```

**Approve is the load-bearing verb** — that's where a human decides. Reject is advisory (proposals need approval to deploy either way). `approve` requires the **`approve`** scope; `reject` accepts `read`.

`approve({ edits })` lets you accept only some entries from the multi-surface `changes[]` array — e.g. take the prompt change but skip the tool config change. Indices refer to the `changes` array order.

## Safety valves

```ts theme={null}
await nora.improvements.revert("imp_1");         // undo a deployed fix
const stale = await nora.improvements.staleFixes(); // fixes that decayed since deploy — candidates to re-experiment
```

`revert` is the post-deployment undo — it requires `approve` because it changes production. `staleFixes` returns fixes whose measured effect has degraded (the underlying failure pattern reappeared), so you can queue them for re-simulation.

## `versions`

Same DAG, different level. `versions` deals with the Flow version pointer directly — improvements ship *as* versions, but you can also roll the pointer without going through the improvement queue.

```ts theme={null}
const vers = await nora.versions.list();       // what shipped when, with authors and improvement refs
await nora.versions.rollback("ver_9");         // roll published pointer back — approve scope
```

Use `versions.rollback` when the pointer needs to move faster than an improvement `revert` would allow (e.g. a bad publish that had nothing to do with an improvement).

## Why `simulate` / `generate` aren't here

The improvement queue is where the loop's output lands. **Making** an improvement — running the failure classifier, generating candidates, measuring them in simulation — is what the loop's internals do, not what the SDK exposes. The SDK is the decision surface, not the generator.

If you want to make evidence on-demand (e.g. re-simulate a specific fix), that's [`simulations`](/sdk/simulations). Its `promote(roundId)` verb turns a simulation result into an improvement proposal that then flows back through this queue for approval.

## Common patterns

### Product-embedded approval

Show pending improvements in your own admin UI:

```ts theme={null}
const queue = await nora.improvements.list("proposed");

render(queue.map((imp) => ({
  title: imp.title,
  agent: imp.targetAgentName,
  fixes: imp.addresses,               // "fixes N failures"
  delta: imp.delta,                    // "+12% correctness"
  diff: imp.proposal,                  // {before, after}
  onApprove: () => nora.improvements.approve(imp.id),
  onReject:  () => nora.improvements.reject(imp.id),
})));
```

The audit records **your admin user** as the approver — same gate, different location.

### Cherry-pick multi-surface changes

An improvement often touches several surfaces (prompt + tool policy + a data source filter). Take only the safe subset:

```ts theme={null}
const imp = await nora.improvements.get("imp_1");
const safe = imp.changes
  .map((c, i) => ({ i, c }))
  .filter(({ c }) => c.surface === "prompt")     // e.g. accept only prompt edits this round
  .map(({ i }) => i);

await nora.improvements.approve("imp_1", { edits: safe });
```

### Watch for decay

Weekly:

```ts theme={null}
const stale = await nora.improvements.staleFixes();
if (stale.length) alertOncall({ staleFixes: stale });
```

Then re-simulate the fix (see [Simulations](/sdk/simulations)) and let the loop decide whether to re-promote or leave it.
