# Build the "Workspace scoping" pattern: `ScopeSelector`

## 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
Explicit boundaries — this project, this team, this dataset — for what an agent may recall or search.

Use it when: Multi-client and multi-team environments where leakage between contexts is a real hazard.

## What to build
A control that shows and sets which sources an agent may search for the current task (this client, the shared handbook, private notes), with presets and a report of what the retrieval layer filtered out. It renders as a popover under the composer or a panel beside a draft.

### Props
- `sources`: Array<{ id: string; name: string; description: string; kind: "project" | "client" | "company" | "private" | "web" | "app"; included: boolean; locked?: { reason: string } }>.
- `presets`: Array<{ id: string; label: string; sourceIds: string[] }> with `activePresetId?`: string, e.g. "This client" and "Everything I can access".
- `surface`: "internal" | "external", whether output goes to someone outside the team; drives warnings.
- `status`: "applied" | "pending" | "applying" | "error", controlled by the parent.
- `filtered?`: { count: number; sourceNames: string[] }, reported by the retrieval layer after a search.
- `appliesTo`: string, plain words for the boundary, e.g. "Applies to this reply".
- `onChange(sourceIds: string[])` and `onPresetChange(id: string)`: callbacks. The component only requests a scope; enforcement happens in retrieval, never in the prompt.

### States it must render
- Applied: every source listed with "Searched" or "Blocked" written beside it.
- Pending: "Changes apply to the next search" with Apply, until the parent confirms.
- Filtered report: "9 matches in another client's files and your private notes were filtered out before the agent saw them" (using real names from `filtered`).
- Widened on an external surface: choosing a broad preset shows an amber warning listing the private and other-client sources it would add.
- Applying and error: controls disabled with a busy indicator; on error the previous scope is shown as still in force.

### Interactions
- Checkbox per source; preset radios set several at once and show "Custom" when edited by hand.
- Apply calls `onChange` with the selected ids; locked sources can't be toggled and show their reason.
- The filtered report can expand to list the blocked sources by name, not their content.

### Edge cases
- No source selected: Apply is disabled with "Select at least one source".
- The default is the narrowest preset for the task, never "Everything I can access".
- Private sources on an external surface are locked out unless the parent allows them.
- A source loses permission mid-session: it flips to Blocked with "Access removed".

## Must avoid
Personal context bleeding into a client-facing surface because scoping was implicit.

## Engineering constraint
Enforce scope in the retrieval layer with permission filters, not in the prompt.

## Accessibility
- Sources are checkboxes in a `fieldset` whose legend states the boundary; Searched and Blocked are text, not colour.
- Announce the filtered count once after each search.

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