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.)
This commit is contained in:
@@ -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:<mode>`-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.
|
||||
|
||||
Reference in New Issue
Block a user