# Build the "Shared review queue" pattern: `ReviewQueue`

## Stack (edit this block to match your project)
- React 18+ with TypeScript: one function component, typed props, no `any`.
- Tailwind CSS, using your design system's tokens if you have them.
- Presentational only: data comes in through props, decisions go out through callbacks. No fetching or global state inside.
- If your codebase already has conventions (naming, shared components, tokens), follow them over this spec.

## What the pattern is for
Agent output waiting on people, with assignment, priority and age visible to the team.

Use it when: Volume operations where humans review a stream of agent work.

## What to build
A team inbox of agent output waiting for a human decision, showing owner, priority, age and deadline, with a detail pane for the draft, the agent's own check and the decision. Team figures on decision time and edit rate sit in view so rubber-stamping shows up early. It renders as a full-page inbox or a split view.

### Props
- `items`: Array<{ id: string; kind: string; title: string; assignee?: { id: string; name: string }; priority: "high" | "normal" | "low"; createdAt: Date; dueAt?: Date; draft: ReactNode; agentCheck: string; confidence?: number }>.
- `currentUserId`: string.
- `filter`: "all" | "mine" | "unassigned" and `onFilterChange(f)`.
- `selectedId?`: string and `onSelect(id: string)`.
- `stats?`: { decidedToday: number; medianDecisionMs: number; editRate: number; fastApprovals?: number }, computed by the backend.
- `status`: "loading" | "ready" | "error".
- `onApprove(id, { viewedMs })`, `onEdit(id)`, `onSendBack(id, reason)` and `onAssign(id, userId)`: callbacks; `viewedMs` is how long the detail was open. The component never applies a decision itself.

### States it must render
- List: rows with kind, title, assignee or "Unassigned", priority and age; a header like "6 waiting, oldest 3h 10m"; filter tabs with counts.
- Over deadline: the age shows "over SLA" in amber text.
- Detail: the draft, the agent's check ("Matched against the payments log"), the assignee line, and Approve, Edit, Send back.
- Someone else's item: decision buttons disabled with "Assigned to" and the teammate's name, and an Assign to me action.
- Team figures: "31 decided today, median 1m 40s, 18% edited"; a sudden run of very fast approvals is flagged in amber.
- Empty view and loading: "Nothing left in this view" with other tabs suggested; skeleton rows while loading.

### Interactions
- There is no select-all or bulk approve; Approve exists only in the detail pane, after the draft is shown.
- Approving an unassigned item claims it for the current user in the same action.
- Send back requires a short reason before calling `onSendBack`.
- Up and Down arrows (or J and K) move through the list; Enter opens the detail.

### Edge cases
- An item is claimed by someone else while open: switch to the read-only state without losing the viewer's place.
- Ages refresh each minute without reordering the list under the pointer.
- Edit rate at 0% over a full day, or median decision time under a few seconds, is shown as a warning, not a success.
- A decided item leaves the list with a short Undo, and focus moves to the next item.

## Must avoid
Queues that only grow, teaching reviewers to batch-approve without reading.

## Engineering constraint
Track per-item decision time and correction rate; they are your early rubber-stamp detector.

## Accessibility
- The list is a `listbox` (or a list of links) with each option labelled by kind, title, priority and age.
- Announce decisions and assignment changes politely; don't announce age updates.

## House rules
- Real `<button>`/`<input>` elements, full keyboard use, visible focus; announce status changes with `aria-live="polite"`; respect `prefers-reduced-motion`.
- All user-facing text comes from props (or a `labels` prop) so it can be translated.
- One consistent accent marks anything the AI made or is doing; amber means "needs a person", red means risky, green means done. Never rely on colour alone.
- Fits a 360 px wide container without horizontal scrolling.

## Acceptance checks
- [ ] Every state above can be rendered by passing props, with no internal fake data.
- [ ] Every interaction and edge case above behaves as described.
- [ ] Nothing in "Must avoid" can happen with this component.

## Deliver
1. The component file with its exported prop types.
2. A short usage example that renders each state.
