AssociationEntitySelect, the name dropdown for the note, session, chat, or document a panel is bound to. Use when a surface must show that name and let the user rename, switch, unlink, or add new ('add a note switcher', 'show the session name'). NOT for count cards or row lists (use canonical-associations).
Scanned 10/3/2026
npx -y skills add armanisadeghi/ai-matrx --skill association-entity-select --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Association Entity Select?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/armanisadeghi-association-entity-select)More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.
---
name: association-entity-select
description: "AssociationEntitySelect, the name dropdown for the note, session, chat, or document a panel is bound to. Use when a surface must show that name and let the user rename, switch, unlink, or add new ('add a note switcher', 'show the session name'). NOT for count cards or row lists (use canonical-associations)."
---
# AssociationEntitySelect — the canonical name dropdown
> **W5 SWAP NOTICE (2026-08-29):** the association/category UI + hooks + service
> implementations now ship in **`@ai-matrx/associations`** (`/react` for faces +
> hooks, `/core` for the headless services/store). Paths in this document that
> point at `features/scopes/components/associations/**`,
> `features/scopes/components/Category*`, or `features/scopes/redux/**`
> association/category fragments refer to DELETED files — import from
> `@ai-matrx/associations/react` (hooks also re-exported under
> `features/scopes/hooks/`), and see `features/scopes/host/` for the host
> binding. The rules and contracts described remain in force.
**One control, five jobs, per (token, container):** display the active entity's real name (registry icon) · inline rename (click the name) · switch via an **always-visible** dropdown (searchable past 5 items) · per-row unlink (non-active rows, edge only — never deletes the entity) · trailing **"+ New \<Entity\>"** that creates + associates + activates (typed search text doubles as the new name).
Component: `features/scopes/components/associations/AssociationEntitySelect.tsx`. Redux-free; everything flows through an **adapter**. Docs: `features/scopes/FEATURE.md` §"Association cards + list".
## The CREATE-then-ASSOCIATE contract (applies to EVERY association surface)
Two component classes, one law each:
1. **Associate-existing** (pickers, "Add" panels): the ONLY job is the edge — it must be written and verified (`AssociationWriteResult.ok`), and failure must toast. An "attach" that silently no-ops is the bug class.
2. **Create-then-associate** (upload file → attach, "+ New Note", new doc → attach): the item is created FIRST (durable row — it exists in the user's library regardless of what happens next), the edge is written SECOND (idempotent, retry-once is safe). **Every terminal outcome is loud, and a created-but-unlinked item is reported WITH its location** ("Uploaded to your Files (War Room folder) but couldn't attach — use Add file to retry"). The user gesture must never end in silence: cancellation, zero-result, create-failure, and attach-failure each get their own message. A created item that vanishes without a trace is the exact bug this contract kills.
References: `ThreadResourcesTab.handleFilesSelected` (upload → attach, all outcomes loud) and `useAssociationEntitySelectAdapter.createAndAttach` (create → attach with retry + created-but-unlinked toast).
## Rules
- **Never rebuild any face of this** — no bespoke note/session switchers, no standalone rename spans next to a separate dropdown, no "+ New X" menu items wired by hand. If a toolbar shows an entity's name, this component owns that name.
- **The dropdown never hides.** `items.length === 1` (or 0) still renders the chevron — "add another" must always be reachable. That gap is the bug this component exists to kill.
- Unlink ≠ delete: `detach` removes the association edge only. The active row never shows the X (switch first).
- Registry-driven: icon/labels come from `getEntityInfo(token)`. The token needs a `titleColumn` in `ENTITY_OVERLAY` (`features/scopes/registry/entityRegistry.ts`) for generic create/rename.
- **Not this control:** count cards (`AssociationCard`), row lists (`AssociationList`), and cross-type attach (`UniversalAssociationPicker`) — those faces are the `canonical-associations` skill.
## Plain container → default adapter
```tsx
import { AssociationEntitySelect } from "@/features/scopes/components/associations/AssociationEntitySelect";
import { useAssociationEntitySelectAdapter } from "@/features/scopes/hooks/useAssociationEntitySelect";
const adapter = useAssociationEntitySelectAdapter({
token: "note",
container: { type: "project", id: projectId, orgId },
activeId, // optional (controlled); omit → first attached row
onActiveChange, // optional
createColumns: {}, // NOT NULL columns the registry conventions can't know
});
<AssociationEntitySelect token="note" adapter={adapter} />
```
Reads via `useContainerLinks` + `useEntityTitles`; creates via `createEntityRow` + `attach`; renames via `renameEntityRow`. Both row writes live in `features/scopes/service/entityRows.ts` (registry titleColumn + owner/org conventions, loud errors, primes `primeEntityTitle` so no surface renders stale).
## Bespoke lifecycle → implement `AssociationEntitySelectAdapter`
When the surface has its own active semantics or create pipeline, implement the interface (exported from the component file): `{ loading, items, activeId, setActive, createAndAttach, rename, detach? }`.
**Reference implementation:** `features/war-room/hooks/useThreadEntitySelect.ts` — `useThreadNoteSelectAdapter` (is_active edge metadata, notes autosave rename via `notesApi.update` + `upsertNoteFromServer`) and `useThreadAudioSessionSelectAdapter` (`studio_sessions` titles + `updateSessionThunk` rename). Consumers: `ThreadNotesTab` / `ThreadAudioTab` / `ThreadAgentTab`.
Adapter contract details:
- `createAndAttach(title)` must create the row, write the association, AND make it active; return the new id or null. **Optional** — omit it when creation isn't name-driven and pass the component's **`createSlot`** instead: a custom footer (ReactNode or `(close) => ReactNode`) replacing the name-input creator. Reference: the war-room Chat tab passes the canonical `AgentListDropdown` — "+ New Chat" = pick an AGENT, which mints a conversation (`startThreadConversation`); the label shows the agent's name until the server auto-labels the conversation after its first turn (`useThreadConversationSelectAdapter`'s label chain).
- `rename(id, title)` returns false on failure (the component toasts + keeps the editor open). Call `primeEntityTitle(token, id, title)` on success.
- Items carry real titles — positional fallbacks (`Note 2`, `Recording 3`) only for unhydrated rows.
## Props worth knowing
`renameActivation` ("click" | "doubleClick", default click) · `align` · `emptyLabel` · `showIcon` / `iconClassName` · `className` / `labelClassName`. Toolbar-dense by default (h-6, text-xs).
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!