# Build the "Silent enrichment with reveal" pattern: `EnrichedRecord`

## 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
AI-added fields and tags applied automatically, marked so users can see and undo the machine's edit.

Use it when: Data entry, CRM hygiene, tagging and triage where the win is nobody having to ask.

## What to build
A record view (contact, ticket, row) where fields the AI filled in automatically are marked, each with its source and time on reveal and a one-click undo. Values a person entered are never overwritten; the AI's disagreement appears as a suggestion beside them. It renders as a record detail panel or form.

### Props
- `fields`: Array<{ key: string; label: string; value: string | null; origin: "person" | "ai" | "import"; provenance?: { source: string; sourceUrl?: string; at: Date; confidence?: number; previousValue?: string | null }; suggestion?: { value: string; source: string } }>, values with per-field provenance.
- `revealedKey?`: string and `onReveal(key | null)`: which field's provenance is open.
- `onUndo(key)`: revert an AI-filled field to its previous value.
- `onAcceptSuggestion(key)` and `onDismissSuggestion(key)`: decide on a suggestion shown beside a person-entered value.
- `onEdit(key, value)`: a person edits a field, which turns its origin to "person".
- `showOnlyAi?`: boolean, filter to AI-filled fields for review. The component never writes values itself.

### States it must render
- Default: every field shows its value; AI-filled fields carry a small AI mark and lighter label, "Filled by AI".
- Revealed: the field shows source, time, confidence and the previous value ("was empty"), with Undo and a link to the source.
- Suggestion beside a person value: the person's value stays as is, with "AI suggests 25 (from billing)" and Accept / Dismiss.
- Undone: the field returns to its previous value with "AI value removed" and an option to restore it until the panel closes.
- Review filter: only AI-filled fields listed with a count, e.g. "6 fields filled by AI today".

### Interactions
- Clicking the AI mark calls `onReveal`; clicking it again or pressing Escape closes it.
- Undo calls `onUndo` and keeps focus on the field.
- Editing an AI-filled field drops the AI mark and records it as the person's value via `onEdit`.
- Accepting a suggestion calls `onAcceptSuggestion` and then marks the field as AI-filled with the person's acceptance noted.

### Edge cases
- AI data conflicts with a person-entered value: never replace it; render a suggestion, as above.
- Provenance missing for an AI value: mark it "Filled by AI, source unknown" rather than hiding the mark.
- A field re-enriched several times: the reveal shows the latest source and offers "History" if more than one change exists.
- Low-confidence values: show them as suggestions in empty fields rather than applied values when below the threshold you pass in.

## Must avoid
Overwriting user-entered values with model guesses and leaving no trace of what changed.

## Engineering constraint
Store provenance per field with the source and timestamp; never overwrite human values without a diff.

## Accessibility
- The AI mark is a button named "Filled by AI from <source>, show details"; the field's accessible description includes "filled by AI".
- After Undo, announce "<label> reverted to <previous value>".

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