# Work

The data behind agent work: plans, approvals, runs, diffs, logs and tests.

- Registry name: `work` (`@candor/work`)
- 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/work

## Install

```bash
npx candor-ui add work
```

Run `npx candor-ui@latest init` once first (https://candor.themethodic.com/get-started.md). Without the CLI: `npx shadcn@latest add @candor/work`.
It brings in: chat, status.

## Types

### PlanStep

```ts
{ id: string title: string status: RunStatus /** What the step does, in a sentence. */ summary?: string | undefined agent?: AgentIdentity | undefined /** Steps this one waits for. */ dependsOn?: string[] | undefined /** The things it touches, when known. */ touches?: EntityRef[] | undefined /** Required when status is "failed". */ failure?: string | undefined duration?: string | undefined /** * Ho
```

### PlanStepDrift

A step where the run left the approved plan: `"added"` (not in the plan that was approved), `"skipped"` (approved, but the run didn't do it) or `"changed"` (done differently from what was approved).

```ts
{ kind: "added" | "skipped" | "changed" /** Why, in a sentence: "The billing table had no country for 212 customers." */ note?: string | undefined /** For `"changed"`: the step as it was approved, in a few words ("Backfill from billing country"). */ from?: string | undefined }
```

### PlanAutonomyOption

What accepting a plan can allow the agent to do without asking. Two have built-in words in `Plan`: `"askBeforeEdits"` and `"runWithoutAsking"`; your own need `label` and `detail`.

```ts
{ /** Passed to `onAccept` when the person picks it. */ id: string label?: string | undefined /** What the agent may do without asking, in a sentence. */ detail?: string | undefined /** Why this option can't be picked here, e.g. "Your organisation asks before every edit." */ locked?: string | undefined }
```

### ClarifyingQuestion

```ts
| { id: string kind: "choice" prompt: string options: { id: string; label: string; detail?: string | undefined }[] multiple?: boolean | undefined recommended?: string | undefined /** A short label shown above the question: "Library", "Approach". */ tag?: string | undefined /** Adds an "Other" option that takes the person's own words, sent as the answer's `text`. */ other?: boolean | undefined } | 
```

### ClarifyingAnswer

`choice` holds the chosen option ids; `text` the person's own words (a text question, or "Other").

```ts
{ questionId: string; choice?: string[] | undefined; text?: string | undefined }
```

### ReviewLevel

How much a person reviews before the agents act.

```ts
"R1" | "R2" | "R3" | "R4" | "R5"
```

### ApprovalState

```ts
| "pending" // waiting for someone eligible to decide | "submitting" // a decision is being recorded | "approved" | "rejected" // by a person | "rejected-by-system" // its facts changed (the target was removed, the environment drifted) | "withdrawn" // by the requester | "expired" // its time (createdAt to expiresAt) ran out without a decision | "failed"
```

### ApprovalImpact

```ts
{ /** The things the change touches (tables, settings, files). */ components: EntityRef[] /** Records affected; "unknown" when it can't be measured before running. */ rows: number | "unknown" }
```

### Rollback

```ts
{ available: true; how: string } | { available: false; reason: string }
```

### ApprovalValueChange

One value the change sets, before and after: a role, a limit, a setting. `from: null` means it had no value ("Not set"); `from` left out means the app didn't record what it was, which is shown as that, never guessed.

```ts
{ label: string; from?: string | null | undefined; to: string | null }
```

### ApprovalRequest

```ts
{ id: string title: string summary: string requester: { name: string } | { agent: AgentIdentity } /** Who an agent asked on behalf of, when an agent asked for a person. */ onBehalfOf?: { name: string } | undefined /** The environment's name, e.g. "Shop". */ environment: string /** What tells same-named ones apart, e.g. "dev-2". */ detail?: string | undefined production: boolean /** sha256 of the c
```

### DiffLine

```ts
{ kind: "add" | "remove" | "context"; text: string; oldNo?: number | undefined; newNo?: number | undefined }
```

### DiffHunk

```ts
{ header?: string | undefined; lines: DiffLine[] }
```

### FileDiff

```ts
{ path: string /** What the file is, e.g. a field or a layout. */ entity?: EntityRef | undefined change: "create" | "update" | "delete" /** e.g. "xml", "ts", "python"; shown as a label. */ language?: string | undefined hunks: DiffHunk[] }
```

### BlastRadiusComponent

```ts
{ entity: EntityRef change: "create" | "update" | "delete" | "read" /** Line counts when a diff exists; null when it doesn't. */ diff: { added: number; removed: number } | null }
```

### BlastRadiusScope

```ts
| { kind: "measured" components: BlastRadiusComponent[] /** Kinds of thing the agent couldn't read, so couldn't rule out. */ unread: string[] } | { kind: "unknown"; reason: string }
```

### LogSeverity

```ts
"debug" | "info" | "warn" | "error"
```

### LogItem

```ts
| { kind: "line" id: string at?: string | undefined severity: LogSeverity message: string /** Who wrote it: an agent's name, a tool, the environment, "system". */ actor?: string | undefined stepId?: string | undefined } /** Lines that should be here but aren't, and why. Never silently skipped. */ | { kind: "gap"; id: string; missing: number | null; reason: string }
```

### TestCase

```ts
{ id: string className: string method: string outcome: "pass" | "fail" | "skip" duration?: string | undefined message?: string | undefined stack?: string | undefined }
```

### TestRun

```ts
{ status: RunStatus cases: TestCase[] /** Code coverage across the environment, 0–1. Absent until the run measured it. */ coverage?: number | undefined /** What production deploys require, 0–1. */ coverageRequired?: number | undefined duration?: string | undefined }
```

### RunNode

```ts
{ id: string agent: AgentIdentity task: string status: RunStatus duration?: string | undefined /** The agent's own check of its work, when it ran one. */ verdict?: { outcome: "pass" | "fail" | "unsure"; note: string } | undefined children?: RunNode[] | undefined }
```

### BackgroundTask

```ts
{ id: string title: string status: RunStatus agent?: AgentIdentity | undefined /** "Step 3 of 5", only when the service knows the total. */ progress?: { done: number; total: number } | undefined startedAt: string /** Set when the task needs the person. */ needs?: string | undefined /** When the app last heard anything from it. Running and silent for longer than the tray's `staleAfter`, it says "No
```

### ResultColumn

```ts
{ key: string; label: string; numeric?: boolean | undefined }
```

### ResultTable

```ts
{ columns: ResultColumn[] rows: Record<string, string | number | null>[] /** Rows the query matched; may exceed rows shown. */ total?: number | undefined query?: string | undefined }
```

### FileFormat

A format a file downloads as, e.g. `{ id: "pdf", label: "PDF" }`.

```ts
{ id: string; label: string }
```

### Deliverable

A file the work produced. Downloads go through your handler, never a link from model output.

```ts
{ id: string name: string /** In bytes. */ size?: number | undefined /** e.g. "application/pdf": picks the icon. */ mediaType?: string | undefined /** The formats it downloads as (Word, PDF); a single plain download when left out. */ formats?: FileFormat[] | undefined /** Where it stands, in your words: "Checked", "Not yet checked". */ state?: string | undefined /** "attention" for a state to look
```

### Artifact

A document or file to read in the artifact panel.

```ts
{ id: string title: string /** The document as markdown text, read into safe blocks (headings, lists, emphasis, code), never HTML. */ text?: string | undefined /** The document as typed blocks, when you have them already; used instead of `text`. */ blocks?: MessageBlock[] | undefined /** A file with no text to show: its icon, size and, for an image, the preview your app serves. */ file?: Attachmen
```

### AgentActivityEntry

One agent, what it is doing now and its state.

```ts
{ agent: AgentIdentity /** What it is doing now, in plain words; left out when it isn't working. */ doing?: string | undefined status: RunStatus /** Your word for the status ("Standing by"); defaults to the status's own label. */ statusLabel?: string | undefined /** When it started what it is doing. */ since?: string | undefined /** When the app last heard from it. Running and silent past `staleAf
```

### ReceiptLine

One line of a result's receipt: what it cost, how long it took, what checked it. Values are your data, as given.

```ts
{ label: string; value: string; note?: string | undefined }
```

### DecisionChoice

One way to answer a decision, and what it leads to.

```ts
{ id: string; label: string; detail?: string | undefined }
```

### Decision

A question with two answers that the work stops for (raise a limit or finish with what's done; keep or change): the facts to decide on, what carries on meanwhile, and the two choices.

```ts
{ id: string question: string /** What the person needs to decide, as label and value: "Spent so far" · "$1.62 of $2". */ facts?: { label: string; value: string }[] | undefined /** What carries on while it waits for an answer, in a sentence. */ continues?: string | undefined /** When it needs an answer by. */ deadline?: string | undefined /** What happens if nobody answers by `deadline`, in a sent
```

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