# Handoff card

A run passed from one person to another while it's in flight: who from, who to, the run and where it is, the note, and what does and doesn't transfer. The recipient accepts or declines; until then it stays with the sender, unless `pendingNote` says what happens instead (say, the sender has already stepped back).

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

## Install

```bash
npx candor-ui add handoff-card
```

Run `npx candor-ui@latest init` once first (https://candor.themethodic.com/get-started.md). Without the CLI: `npx shadcn@latest add @candor/handoff-card`.
It brings in: button, locale, relative-time, status, status-dot, utils.
npm packages: lucide-react@^1.67.0.

## Example

```tsx
"use client"

import * as React from "react"
import { HandoffCard } from "@/components/handoff-card"

const ago = (m: number) => new Date(Date.now() - m * 60_000).toISOString()

export default function Example() {
  const [state, setState] = React.useState<"pending" | "accepted" | "declined">("pending")
  return (
    <HandoffCard
      className="max-w-xl"
      run="Add region to customers"
      status="waiting"
      from="Sam"
      to="Priya"
      note="Backfill is paused at batch 12. Staging looks good; Production needs an approver."
      at={ago(20)}
      moves={["The thread and its history", "The pending approval request", "Notifications for this run"]}
      stays={["Sam’s drafts in other threads", "Sam’s role on Production"]}
      state={state}
      onAccept={() => setState("accepted")}
      onDecline={() => setState("declined")}
    />
  )
}
```

## <HandoffCard>

Props:

- `run: string`: The run or thread being handed over.
- `status: RunStatus`: Where the run is now.
- `from: string`: Who hands it over.
- `to: string`: Who it goes to: a person or a team.
- `note?: string | undefined`: The sender's note for the recipient.
- `at: string`: When it was handed over (ISO).
- `moves: string[]`: What transfers, as the service defines it.
- `stays: string[]`: What doesn't.
- `state: "pending" | "accepted" | "declined"`: Whether the recipient has taken it over yet.
- `pendingNote?: string | undefined`: What happens while it waits, in place of "Stays with <from> until <to> accepts." E.g. "The agent has stopped replying; it waits for Support."
- `onAccept?: (() => void) | undefined`: Take it over. Without it, there's no button.
- `onDecline?: (() => void) | undefined`: Turn it down. Without it, there's no button.
- `className?: string | undefined`

## Strings (`handoffCard`)

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

- `handoff`: Handoff: {run}
- `to`: to
- `note`: “{note}”
- `movesTo`: Moves to {to}
- `doesntMove`: Doesn’t move
- `staysWith`: Stays with {from} until {to} accepts.
- `decline`: Decline
- `takeOver`: Take it over
- `accepted`: {to} took it over.
- `declined`: {to} declined; it’s still with {from}.

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