# src/shell/bank_ops — promptless bank-mutation verbs

## Scope

The single home for bank-mutation logic: create/rename/delete/evacuate/activate/
transfer/remove a sample or bank, plus the undo-batched persist that follows a
mutation. No prompts, no message boxes, no panel-state reads — this is the verb
seam that `shell/panel/panel_bank_ops` (menu/prompt UX) and `shell/actions/
bank_actions` (bindable-action UX) both consume as thin skins, so the mutation
logic has exactly one home. The in-model enforcement of bank rules (pool
privileges, uniqueness, etc.) lives in `core/model` (`bank_book`) — this directory
calls into that model, it does not reimplement its rules.

## Invariants

- **Pool privileges are inviolable.** The pool always exists, is un-deletable, is
  un-renamable (fixed id + fixed display name "Pool"), and a project can never be
  left with zero banks. No verb in this directory may delete or rename the pool,
  or evacuate it (the pool is evacuation's destination, not a source).
- **Delete drops members; evacuate returns them.** Deleting a named bank drops its
  member index entries only — files are never deleted by a bank op (file lifecycle
  stays owned by the capture/prune path). Evacuate moves all of a bank's members
  back to the pool (index-only, same destination-collapse-by-hash as move), leaving
  the bank empty. A plain delete of a non-empty bank orphans those members out of
  every index until prune reclaims them — an accepted, designed window, not a bug.
- **Movement is index-only.** Moving/copying a sample between banks removes/adds
  the index entry only; the underlying file never moves on disk. No bank operation
  writes, moves, or deletes a file.
- **One bank operation is one Ctrl-Z.** Every bank index verb wraps its mutation in
  a batched REAPER undo point (`Undo_BeginBlock2`/`EndBlock2`, `UNDO_STATE_MISCCFG`)
  via `persistBankOp` — this is enforced in this directory, not left to callers.

## Modules

- `bank_ops` (`shell/bank_ops`) — the promptless bank-mutation verb seam (Q-W6 lift out of `panel_bank_ops`): `bankOpCreate`/`Rename`/`Delete`/`Evacuate`/`Activate`/`Transfer`/`Remove` + `persistBankOp` (the undo-batched ext-state persist), each taking a `ReaSamplerSession&` and returning whether the model accepted the mutation — no prompts, no message boxes, no panel-state reads. `shell/panel/panel_bank_ops` (menu/prompt UX) and `shell/actions/bank_actions` (bindable-action UX) both consume these as thin skins, so the mutation logic has exactly one home.

## Gotchas

- The pool-privilege rules and uniqueness rules are enforced in the pure
  `core/model` `bank_book` layer, not re-checked here defensively — if a mutation
  looks like it should be rejected but isn't, the bug is more likely in `bank_book`
  than in this seam. Reference `core/model`, do not duplicate its rules here.
