docs: archive M11 completion (drag-out) to COMPLETED; reconcile CLAUDE.md

Native OS drag-out landed (copy-only, prune stays sole deleter).
Milestone 11 complete; M9 slots is the last M-numbered milestone.
This commit is contained in:
2026-07-26 20:11:51 -04:00
parent 1b9e65d583
commit ec05af35ef
3 changed files with 29 additions and 13 deletions
+5 -2
View File
@@ -4,7 +4,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
## Repo identity and current state ## Repo identity and current state
The CMake project and binary are now named `reaper_reasampler`. This is **ReaSampler** — a per-project audio sample-bank capture tool. The MPE modules (`mpe_model`, `mpe_view`) have been removed. M0M8 are complete (bank_model, peaks, capture offline+realtime, persist, bank_panel, insert, capture action family, RealtimeRecordBackend, tail T1+T2+T1-followons). Phase B multi-bank (B1B5, B-cap), Phase D1/D2 Design View (D1D5, D2-W1W3-B), Phase V versioning/beta-channel (V1/V3, V4), M10 provenance + re-capture from source, Phase R Reclaim (R1R3: prune-reconcile core, dry-run shell, guarded deletion + action + panel button), and M11 wave 1 (batch capture, action-button strip + keybinding labels, conform-on-insert verified-extant) have all landed. M9 slots and M11 native-OS-drag-out remain. The *discipline* — pure REAPER-free testable core split from REAPER-facing shells — is **preserved** throughout. The CMake project and binary are now named `reaper_reasampler`. This is **ReaSampler** — a per-project audio sample-bank capture tool. The MPE modules (`mpe_model`, `mpe_view`) have been removed. M0M8 are complete (bank_model, peaks, capture offline+realtime, persist, bank_panel, insert, capture action family, RealtimeRecordBackend, tail T1+T2+T1-followons). Phase B multi-bank (B1B5, B-cap), Phase D1/D2 Design View (D1D5, D2-W1W3-B), Phase V versioning/beta-channel (V1/V3, V4), M10 provenance + re-capture from source, Phase R Reclaim (R1R3: prune-reconcile core, dry-run shell, guarded deletion + action + panel button), and M11 in full (batch capture, action-button strip + keybinding labels, conform-on-insert verified-extant, native OS drag-out) have all landed. M9 slots remain. The *discipline* — pure REAPER-free testable core split from REAPER-facing shells — is **preserved** throughout.
CONTEXT.md is the authoritative spec and build roadmap. Read it first for any non-trivial task. Every REAPER API name cited there is correct-by-intent; verify argument order, types, and flag values against `vendor/reaper-sdk/sdk/reaper_plugin_functions.h` before use. CONTEXT.md is the authoritative spec and build roadmap. Read it first for any non-trivial task. Every REAPER API name cited there is correct-by-intent; verify argument order, types, and flag values against `vendor/reaper-sdk/sdk/reaper_plugin_functions.h` before use.
@@ -41,6 +41,7 @@ Key targets (see CMakeLists.txt for the full list):
| `prune_button_tests` | executable | Pure unit tests for `prune_button` — no REAPER, no DAW. | | `prune_button_tests` | executable | Pure unit tests for `prune_button` — no REAPER, no DAW. |
| `batch_capture_tests` | executable | Pure unit tests for `batch_capture` — no REAPER, no DAW. | | `batch_capture_tests` | executable | Pure unit tests for `batch_capture` — no REAPER, no DAW. |
| `action_buttons_tests` | executable | Pure unit tests for `action_buttons` — no REAPER, no DAW. | | `action_buttons_tests` | executable | Pure unit tests for `action_buttons` — no REAPER, no DAW. |
| `drag_out_tests` | executable | Pure unit tests for `drag_out` — no REAPER, no DAW. |
| `reaper_reasampler` | loadable module | The actual extension binary (`.dll` / `.dylib` / `.so`). | | `reaper_reasampler` | loadable module | The actual extension binary (`.dll` / `.dylib` / `.so`). |
### Beta channel build (Phase V, V4) ### Beta channel build (Phase V, V4)
@@ -81,15 +82,17 @@ There is no hot-reload. Copy the built binary into REAPER's `UserPlugins/` folde
- `prune_button` — Phase R pure layout/hit-test for the `bank_panel` footer Prune button: `computePruneButton` (right-anchored, suppressed gracefully when footer is too narrow) + `hitTestPruneButton`. Mirror of `mode_switch` / `tab_strip`. - `prune_button` — Phase R pure layout/hit-test for the `bank_panel` footer Prune button: `computePruneButton` (right-anchored, suppressed gracefully when footer is too narrow) + `hitTestPruneButton`. Mirror of `mode_switch` / `tab_strip`.
- `batch_capture` — M11 pure batch-capture planner: maps a list of source ranges to ordinal capture units, drives per-unit capture via a shared `captureAndIndexOne` helper, and aggregates mixed results. No REAPER types at the boundary. - `batch_capture` — M11 pure batch-capture planner: maps a list of source ranges to ordinal capture units, drives per-unit capture via a shared `captureAndIndexOne` helper, and aggregates mixed results. No REAPER types at the boundary.
- `action_buttons` — M11 pure action-button strip layout/hit-test: divides a strip rect into N action buttons, min-width overflow-hiding, label formatting with "(unbound)" fallback, `ActionButtonRect` struct. Mirror of `mode_switch` / `bank_grid`. - `action_buttons` — M11 pure action-button strip layout/hit-test: divides a strip rect into N action buttons, min-width overflow-hiding, label formatting with "(unbound)" fallback, `ActionButtonRect` struct. Mirror of `mode_switch` / `bank_grid`.
- `drag_out` — M11 pure OS drag-out module: gesture-boundary decision (internal drag becomes OS-bound when the pointer leaves the panel client rect), path-list assembly with dedupe and missing-file skip. No REAPER types at the boundary.
**REAPER-facing shells:** **REAPER-facing shells:**
- `capture``ICaptureBackend` interface; `OfflineRenderBackend` (deterministic default) and `RealtimeRecordBackend`. Input: `CaptureRequest`. Output: finished file + populated `Sample` handed to `bank_model`. - `capture``ICaptureBackend` interface; `OfflineRenderBackend` (deterministic default) and `RealtimeRecordBackend`. Input: `CaptureRequest`. Output: finished file + populated `Sample` handed to `bank_model`.
- `insert` — placement via `InsertMedia`; conform-to-project-tempo is an explicit opt-in flag, never silent stretching. - `insert` — placement via `InsertMedia`; conform-to-project-tempo is an explicit opt-in flag, never silent stretching.
- `bank_panel` — docked LICE-drawn grid: thumbnails, audition, multi-select, keyboard navigation; a 28px action-button strip (M11) between the split body and tail footer that fires capture/insert/re-capture actions via `NamedCommandLookup` + `Main_OnCommand` and shows live keybinding labels via `kbd_getTextFromCmd`. - `bank_panel` — docked LICE-drawn grid: thumbnails, audition, multi-select, keyboard navigation; a 28px action-button strip (M11) between the split body and tail footer that fires capture/insert/re-capture actions via `NamedCommandLookup` + `Main_OnCommand` and shows live keybinding labels via `kbd_getTextFromCmd`; OS drag-out hook (M11) initiating an OS-level copy drag when the pointer leaves the panel client rect, via `drag_out` + `drag_out_win`.
- `persist` — project ext state (`SetProjExtState` / `GetProjExtState`, namespace `"reasampler"`) ↔ `BankBook` JSON (`"banks"` key) + `ViewModeModel` JSON (`"view_state"` key) + `TailSetting` JSON (`"tail_setting"` key) + `OwnedManifest` JSON (`"owned_files"` key) + writing-version stamp (`"version"` key, written via `stampVersion()` on every `saveToActiveProject()`); project-relative path resolution. A `projectconfig` hook (`BeginLoadProjectState(isUndo)`) triggers a deferred session reload on undo/redo so Ctrl-Z/redo visibly restores book/view/tail/manifest in-session. Hosts `ReaSamplerSession::pruneDryRun()` (read-only orphan enumeration via M4 project-relative resolution) and `pruneOrphanSet()` (full-set query for the R3 delete path); supplies `referencedPaths()` + `owned().paths()` to the `prune_reconcile` pure core. - `persist` — project ext state (`SetProjExtState` / `GetProjExtState`, namespace `"reasampler"`) ↔ `BankBook` JSON (`"banks"` key) + `ViewModeModel` JSON (`"view_state"` key) + `TailSetting` JSON (`"tail_setting"` key) + `OwnedManifest` JSON (`"owned_files"` key) + writing-version stamp (`"version"` key, written via `stampVersion()` on every `saveToActiveProject()`); project-relative path resolution. A `projectconfig` hook (`BeginLoadProjectState(isUndo)`) triggers a deferred session reload on undo/redo so Ctrl-Z/redo visibly restores book/view/tail/manifest in-session. Hosts `ReaSamplerSession::pruneDryRun()` (read-only orphan enumeration via M4 project-relative resolution) and `pruneOrphanSet()` (full-set query for the R3 delete path); supplies `referencedPaths()` + `owned().paths()` to the `prune_reconcile` pure core.
- `view` — Design View shell: reads the folder tree via `view_tree`, snapshots flag values before parking, drives hide + CPU-park on inactive-mode leaves (`B_SHOWINTCP`/`B_SHOWINMIXER`/`B_MAINSEND`/`I_FXEN` + per-FX offline) and derived visibility on parents; restores from snapshot. Never touches master or `B_MUTE`/`I_SOLO`. - `view` — Design View shell: reads the folder tree via `view_tree`, snapshots flag values before parking, drives hide + CPU-park on inactive-mode leaves (`B_SHOWINTCP`/`B_SHOWINMIXER`/`B_MAINSEND`/`I_FXEN` + per-FX offline) and derived visibility on parents; restores from snapshot. Never touches master or `B_MUTE`/`I_SOLO`.
- `track_guid` — shared `MediaTrack*` → canonical GUID-string formatter; single source of truth for membership keys used by both the view shell and the actions layer. - `track_guid` — shared `MediaTrack*` → canonical GUID-string formatter; single source of truth for membership keys used by both the view shell and the actions layer.
- `provenance_shell` — FX-chain identity queries via `TrackFX_*` / `TakeFX_*` APIs; collects source-item paths and parent-detection inputs to feed the pure `provenance` fingerprint builder. Stamps `Sample.provenance` on capture when every resolving source item maps by exact normalized path (case-folded on Windows) to exactly one bank sample; ambiguous/mixed cases record nothing conservatively. - `provenance_shell` — FX-chain identity queries via `TrackFX_*` / `TakeFX_*` APIs; collects source-item paths and parent-detection inputs to feed the pure `provenance` fingerprint builder. Stamps `Sample.provenance` on capture when every resolving source item maps by exact normalized path (case-folded on Windows) to exactly one bank sample; ambiguous/mixed cases record nothing conservatively.
- `drag_out_win` — M11 OS drag-out shell: Windows OLE `DoDragDrop` / `CF_HDROP`, copy-only structurally (`DROPEFFECT_MOVE` not offered, no source-deletion path); macOS/Linux via `SWELL_InitiateDragDropOfFileList` (copy-semantics caveat documented — SWELL does not expose a drop-effect query). Driven by the `drag_out` pure module.
- `actions` — registers the capture/placement/slot action family, the Design View action family (toggle active mode, activate Arrange/Design, tag/untag selected tracks, show-both), the multi-bank action family (create/rename/reorder/delete bank, evacuate, activate, move/copy/remove selected samples), and the Phase R prune action (`BANK_PRUNE_FOLDER` — dry-run-first, confirm-with-manifest, then `pruneDeletePlan`-guarded deletion; **the ONLY file-deletion authority in the system**); routes each to the modules above via the `command_id`/`gaccel`/`hookcommand` contract. Every bank index verb wraps its mutation in a batched REAPER undo point (`Undo_BeginBlock2`/`EndBlock2`, `UNDO_STATE_MISCCFG`) so one bank operation is one Ctrl-Z. The prune action writes no ext state and opens no undo point (file deletion is not REAPER-undoable). - `actions` — registers the capture/placement/slot action family, the Design View action family (toggle active mode, activate Arrange/Design, tag/untag selected tracks, show-both), the multi-bank action family (create/rename/reorder/delete bank, evacuate, activate, move/copy/remove selected samples), and the Phase R prune action (`BANK_PRUNE_FOLDER` — dry-run-first, confirm-with-manifest, then `pruneDeletePlan`-guarded deletion; **the ONLY file-deletion authority in the system**); routes each to the modules above via the `command_id`/`gaccel`/`hookcommand` contract. Every bank index verb wraps its mutation in a batched REAPER undo point (`Undo_BeginBlock2`/`EndBlock2`, `UNDO_STATE_MISCCFG`) so one bank operation is one Ctrl-Z. The prune action writes no ext state and opens no undo point (file deletion is not REAPER-undoable).
## REAPER extension contract (src/main.cpp) ## REAPER extension contract (src/main.cpp)
+24 -1
View File
@@ -1196,7 +1196,30 @@ invariants.
mute-after-capture workflow; a mute action would be redundant with the dual-canvas mute-after-capture workflow; a mute action would be redundant with the dual-canvas
architecture. Mirror of the null-test cut precedent ("Cut (fixed by Daniel)"). architecture. Mirror of the null-test cut precedent ("Cut (fixed by Daniel)").
**Remaining in M11:** Native OS drag-out (not built in wave 1 — still in PLAN.md). ## Milestone 11 — polish (wave 2 / completion)
**Goal:** Native OS drag-out — the final M11 polish item. CONTEXT.md Build order 11,
§Non-goals (drag-out deferred to last).
**Verify (gates 24/24 both configs):** Drag-out places a valid file in the OS target
without regressing the precision invariants; copy-only semantics throughout (no source
deletion on drop); internal move/copy drag unchanged.
- [x] **`drag_out` pure module** (gesture-boundary decision): internal drag becomes
OS-bound when the pointer leaves the panel client rect; path-list assembly with
dedupe and missing-file skip. No REAPER types at the boundary. New CTest target
`drag_out_tests`.
- [x] **`drag_out_win` shell** (Windows): OLE `DoDragDrop` / `CF_HDROP`. Copy-only
structurally — `DROPEFFECT_MOVE` is not offered and no source-deletion path exists;
prune remains the sole file-deleter. macOS/Linux via `SWELL_InitiateDragDropOfFileList`
with a documented copy-semantics caveat (SWELL does not expose a drop-effect query).
- [x] `bank_panel` additive hook only — internal move/copy drag unchanged.
**Notes/decisions:**
- Copy-only is structural, not a policy flag: `DROPEFFECT_MOVE` is never offered on
Windows, so the OS never signals a move. The SWELL path cannot query drop-effect;
copy semantics are documented as a known caveat for macOS/Linux.
- Prune remains the sole authority for deleting files off disk; drag-out does not
remove the source file or any bank index entry.
- This wave completes **Milestone 11** in full.
## Phase R forks — settled 2026-07-24 ## Phase R forks — settled 2026-07-24
- **Fork R-C — deletion mechanism.** Settled: **trash-preferred, unlink fallback.** - **Fork R-C — deletion mechanism.** Settled: **trash-preferred, unlink fallback.**
-10
View File
@@ -23,16 +23,6 @@ state persists via the index.
- [ ] Slot model + slot↔sample assignment. - [ ] Slot model + slot↔sample assignment.
- [ ] "Capture to slot N" / "insert slot N" actions, MIDI-bindable. - [ ] "Capture to slot N" / "insert slot N" actions, MIDI-bindable.
## Milestone 11 — polish
**Goal:** Native OS drag-out (remaining polish item). CONTEXT.md Build order 11,
§Non-goals (drag-out deferred to last).
**Verify (in DAW):** Drag-out places a valid file in the OS target without
regressing the precision invariants.
- [ ] Native OS drag-out (deferred final; `InsertMedia` path must already work).
---
## Open questions to resolve during build ## Open questions to resolve during build
Carried from CONTEXT.md §Open questions — keep visible until each is closed by a Carried from CONTEXT.md §Open questions — keep visible until each is closed by a
landed milestone. landed milestone.