From 960c5166eed020bedfbcbd94d01e879e16c9796c Mon Sep 17 00:00:00 2001 From: daniel-c-harvey Date: Thu, 23 Jul 2026 20:24:31 -0400 Subject: [PATCH] docs: record D2-W3-A landed in COMPLETED.md planLaneMinting pure decision, applyMintPlan shell, reconcileManagedLanes on load, persist via ViewModeModel view_state. CTest 14/14; DAW verify pending. (PLAN.md D2-W3-B narrowing already landed via a concurrent commit.) --- COMPLETED.md | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/COMPLETED.md b/COMPLETED.md index f8041e7..deef77d 100644 --- a/COMPLETED.md +++ b/COMPLETED.md @@ -547,3 +547,70 @@ projects with no stored key fall back to Off / 2 s. (Off mode, 2 s stored length) — graceful for projects saved before this feature shipped. - `kProjExtTailKey = "tail_setting"` is forever-stable (changing it would orphan saved choices, falling back to the default — graceful but lossy). + +--- + +## D2-W3-A — lane minting + item→lane assignment + persist round-trip +**Goal:** The functional core that makes item-lanes appear: a pure `planLaneMinting` +decision (which tracks hold >1 mode's content, which managed lane each item lands on) +plus the shell apply path in `view.cpp` — enables fixed-lane mode, mints one managed +`reasampler:`-named lane per involved mode, assigns each item (including +pre-existing) to its mode's lane, and drives per-lane play state, all under one undo +block, triggered off the auto-tag detection tick. Reconciles the lane-ownership index +from durable lane names on project load before active-mode visibility is reapplied. +The lane-ownership index persists inside the `"reasampler"` `view_state` blob (rides +in `ViewModeModel::serialize()` / `deserialize()`). +**Verify:** CTest green (14/14). Pure decision unit-tested in `view_mode_model_tests`. +**DAW verification pending** (Daniel testing on dev): two behaviors are +REAPER-runtime-only — whether lane names stick when written on the same tick the track +flips to fixed-lane mode, and whether the leftover empty default lane 0 is silent. +**Depends on:** D2-W2. + +- [x] Pure `planLaneMinting` decision (`view_mode_model.{h,cpp}`): for each reported + track, collect the distinct modes of managed-eligible items; if < 2 modes, no split + (D1 whole-track parking still separates stances); if ≥ 2 modes, emit one + `TrackSplit`, one `LaneMint` per involved mode (durable key = `laneNameForMode(mode)`, + owned by that mode), and one `LaneAssign` per managed-eligible item — including + pre-existing items, so a track that just gained a second mode retroactively lanes all + its content. Manual-lane items (`onManualLane = true`) are exempt at the source: + never counted, never reassigned, never minted-over. +- [x] Shell apply path `applyMintPlan` in `view.cpp`: enables `I_FREEMODE` = fixed + lanes, grows `I_NUMFIXEDLANES` (never shrinks — user's manual lanes are never + deleted), stamps each managed lane's durable name via `P_LANENAME`, records + ownership in the model (`lanes().setManaged`), assigns each item to its mode's lane + via `I_FIXEDLANE` resolved from the durable key. Returns `changed` so the Undo block + is only kept when state actually changed (idempotent re-runs produce no undo point). +- [x] Per-lane play state driven immediately after minting: `planToggle` lane ops + applied via `applyLaneOps` so the freshly-minted lanes take the correct + `C_LANEPLAYS` state for the active mode without a full `applyMode` re-run (which + would re-park/restore whole tracks — not correct for a minting tick). +- [x] `mintManagedLanes` entry point in `view.cpp`: reads live track/item picture via + `readLaneTracks`, calls `planLaneMinting`, wraps the apply in one Undo block labelled + `"ReaSampler: separate cross-mode content into lanes"`, calls `UpdateTimeline()` + + `UpdateArrange()` after a fixed-lane mode change. +- [x] `reconcileManagedLanes` in `view.cpp`: on project load, reads every fixed-lane + track's `P_LANENAME` values; for each name carrying the managed prefix, records the + lane as managed-for-its-mode in the ownership index — pure read of REAPER state, no + lane created or renamed. Called from `main.cpp`'s load path before `applyMode`. +- [x] Lane-ownership index persists via `ViewModeModel::serialize()` / + `deserialize()` — the `LaneOwnershipIndex` is a member of `ViewModeModel` and + round-trips inside the `"reasampler"` `view_state` key alongside modes, membership, + snapshots, and active mode. No new persistence key required. +- [x] Detection tick integration: `mintManagedLanes` is called from the `bank_panel` + timer after the auto-tag pass, so a newly-tagged multi-mode track is split into lanes + on the same tick the content is detected. + +**Notes/decisions:** +- **Single-mode-track rule:** a track carrying content of only ONE mode is not split — + D1's whole-track parking continues to separate its stance from the other mode without + lane overhead. The lane-split only engages when a track genuinely holds ≥ 2 modes' + content. +- **Manual-lane invariant upheld at the source:** `planLaneMinting` never receives + manual-lane items as split candidates. The shell's `readLaneTracks` marks items on + manual lanes `onManualLane = true`; the pure decision skips them entirely. Managed + lanes are always appended (tail ordinals), never overwriting a user's existing lanes. +- **Idempotency:** re-reporting an already-split track produces the same plan; the + shell's ensure/assign writes are no-ops when state already matches. The Undo block is + closed with no label (discarded by REAPER) when the plan is non-empty but every write + was already satisfied, so no phantom undo points accumulate. +- **Review passed** with no Critical or Major findings.