# Run recovery

A run that stopped. Shows the steps up to where it broke, what went wrong at that step, and how to carry on: retry from the broken step (earlier work is kept), skip it and go on (when the app allows that), or stop. A loop the agent couldn't leave is named as a loop, with how many times it repeated, rather than reported as a generic failure. A retry or skip that changes something can ask first: `confirm` says what will happen, and the action runs only once the person confirms.

- Registry name: `run-recovery` (`@candor/run-recovery`)
- Tier: Pro. Installing it needs a Pro, Premium or Team licence key: the person runs `npx candor-ui key <key>` themselves; never ask for the key in chat or write it to a file.
- Docs: https://candor.themethodic.com/docs/run-recovery

## Install

```bash
npx candor-ui add run-recovery
```

Run `npx candor-ui@latest init` once first (https://candor.themethodic.com/get-started.md). Without the CLI: `npx shadcn@latest add @candor/run-recovery`.
It brings in: button, locale, step-list, work, utils.
npm packages: lucide-react@^1.67.0.

## Example

```tsx
"use client"

import type { PlanStep } from "@/lib/work"
import { RunRecovery } from "@/components/run-recovery"

const steps: PlanStep[] = [
  { id: "s1", title: "Add the region column", status: "succeeded", duration: "14s" },
  { id: "s2", title: "Show it on the customer page", status: "succeeded", duration: "9s" },
  { id: "s3", title: "Backfill from billing country", status: "failed", failure: "Row lock on customers, batch 12" },
  { id: "s4", title: "Test on Staging", status: "idle" },
]

export default function Example() {
  return (
    <RunRecovery
      className="max-w-xl"
      title="Add region to customers"
      steps={steps}
      failedStepId="s3"
      reason="The backfill hit the same row lock on batch 12 four times. Another job may be writing customers."
      loop={{ times: 4 }}
      onRetryFromStep={() => {}}
      onSkip={() => {}}
      onStop={() => {}}
      confirm={{ skip: "Skipping leaves region empty for customers not yet backfilled." }}
    />
  )
}
```

## <RunRecovery>

Props:

- `title: string`: What the run was doing.
- `steps: PlanStep[]`: Every step of the run, in order.
- `failedStepId: string`: The step it stopped at.
- `reason: string`: What went wrong at that step, in plain words.
- `loop?: { times: number } | undefined`: Set when the run was stopped for repeating itself.
- `onRetryFromStep?: ((id: string) => void) | undefined`: Retry from the failed step; earlier finished steps are kept. Without it, there's no retry button.
- `onSkip?: ((id: string) => void) | undefined`: Skip the failed step and go on with the next one. Without it, there's no skip button.
- `skipNote?: string | undefined`: What skipping means for this run, shown beside the actions. Defaults to saying the step's work won't be done.
- `onStop?: (() => void) | undefined`: Stop the run where it is. Without it, there's no stop button.
- `confirm?: { retry?: string | undefined; skip?: string | undefined } | undefined`: Ask before a retry or skip that changes something: what it will do, in plain words, shown with a Confirm button. Leave one out to act at once.
- `className?: string | undefined`

## Strings (`runRecovery`)

The fixed words, English by default. Override any of them under `runRecovery` in `<LocaleProvider strings={{ runRecovery: { … } }}>`; the table is exported as `runRecoveryStrings`.

- `label`: Stopped run: {title}
- `stoppedLooping`: { one: "Stopped after repeating step {step} {count} time without progress.", other: "Stopped after repeating step {step} {count} times without progress.", }
- `stoppedAt`: Stopped at step {step} of {total}.
- `kept`: { one: "{count} finished step is kept.", other: "{count} finished steps are kept." }
- `confirmLabel`: Confirm this change
- `cancel`: Cancel
- `confirmRetry`: Confirm retry from step {step}
- `confirmSkip`: Confirm skipping step {step}
- `skipNote`: Skipping goes on to the next step without “{step}”; its work won’t be done.
- `stop`: Stop here
- `skip`: Skip step {step}
- `retry`: Retry from step {step}

## Rules for using Candor UI

- Components take plain typed data. Never pass model output as HTML or render it as a link; messages are typed blocks.
- A control that can't be used says why: wrap it in `<Locked reason="…">`, don't just disable it.
- Don't show a value you don't have: leave it out (it renders as —) rather than guess. An estimate is labelled as one.
- Nothing is pre-selected in a decision a person makes, and the agent is named from your app's data, never from model output.
- Your app's words go in `LocaleProvider` (fixed strings) and `TermsProvider` (nouns); entity kinds and agent roles in `EntityKindsProvider` and `AgentRolesProvider`.
