# Build the "Forget and expire" pattern: `RetentionControls`

## 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
Controls for temporary context, session-only mode, and automatic expiry of stale memory.

Use it when: Sensitive work, one-off tasks, and regulated data-retention regimes.

## What to build
A settings panel for what an assistant keeps: a temporary-mode switch, a list of memories with their expiry rules, and a Forget action that shows deletion reaching every store and then proves nothing comes back. It renders in a memory or workspace settings screen.

### Props
- `temporaryMode`: { on: boolean; description: string } and `onTemporaryChange(on: boolean)`, the session-only switch and what it means in plain words.
- `memories`: Array<{ id: string; text: string; source: string; expiry: { kind: "date" | "event" | "never"; label: string; at?: Date }; sensitive?: boolean }>.
- `deletion?`: { memoryId: string; stores: Array<{ id: string; label: string; count: number; unit: string; state: "pending" | "running" | "done" | "failed"; error?: string }>; verification: { state: "pending" | "running" | "passed" | "failed"; query?: string; resultCount?: number } }, progress reported by the backend.
- `expired?`: Array<{ id: string; text: string; expiredAt: Date; storesCleared: string[] }>, recent automatic expiries with receipts.
- `status`: "idle" | "forgetting" | "error", controlled by the parent.
- `onForget(id)`, `onRetryStore(memoryId, storeId)` and `onExpiryChange?(id, expiry)`: callbacks. The component never deletes anything itself.

### States it must render
- Idle: each memory with its source and expiry label ("Auto-forgets in 29 days", "Kept until you remove it"); sensitive ones flagged.
- Temporary on: the switch shows on with its description, e.g. "New chats create no memories and are deleted after 24 hours".
- Forgetting: the memory is marked as going and a checklist shows each store (list, search index, cache, logs) with counts moving from pending to done.
- Verified: "Nothing comes back: a search for the card number returns 0 results", then the entry leaves the list with a receipt.
- Partly forgotten: a failed store keeps the entry listed as "Partly forgotten" with the failing store named and Retry.
- Expired receipts and empty: past expiries show which stores were cleared; an empty list says what will appear here.

### Interactions
- Forget calls `onForget`; other Forget buttons stay usable but queue behind the running one.
- The temporary switch calls `onTemporaryChange` and updates its description at once.
- Retry on a failed store calls `onRetryStore`; changing an expiry rule calls `onExpiryChange`.

### Edge cases
- The entry is removed from the list only when every store reports done and verification passes; removing the row early is never allowed.
- Verification finds a result: show "Still found in search index" as an error, not success.
- An event-based expiry whose date has passed but hasn't been processed shows "Due to expire", not "Expired".
- Turning on temporary mode mid-conversation states whether it applies to the current chat or only new ones, from `description`.

## Must avoid
A forget action that removes the UI entry while the embedding stays in the index.

## Engineering constraint
Deletion must propagate to vector stores, caches and logs; prove it in your retention tests.

## Accessibility
- The store checklist is an ordered list whose step states are written in text, not only icons.
- Announce the outcome once: "Forgotten from 4 stores and verified" or the failure.

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