feat(view): D2 Wave 2 — apply managed-lane ops + timer-diff auto-tag new content

Drives fixed-lane C_LANEPLAYS for managed lanes only (name-keyed, ordinal-renumber
safe) in the view shell; diffs live track/item GUIDs on the panel timer to auto-tag
new content to the active mode, first-poll-guarded. Pure guid_diff + lane_keys
modules unit-tested; folds in Wave-1 polish.
This commit is contained in:
2026-07-23 17:32:22 -04:00
parent af5f7ee912
commit af34143107
11 changed files with 773 additions and 4 deletions
+61
View File
@@ -0,0 +1,61 @@
#pragma once
// lane_keys — the pure, REAPER-free convention that maps a REAPER fixed lane's
// durable NAME (P_LANENAME:n) to the opaque lane-key the pure view_mode_model uses,
// and the managed/manual heuristic that rides on it.
//
// PURE MODULE (CLAUDE.md §load-bearing split): NO REAPER types, NO SWELL. std only.
// Unit-tested outside the DAW. The shell (view.cpp) reads each lane's P_LANENAME:n
// string from REAPER and asks this module whether the lane is tool-managed and what
// its stable lane-key is; the shell never re-derives the prefix rule itself.
//
// -- Design point #2 (lane-identity robustness) resolution --------------------
//
// REAPER exposes no durable per-lane GUID. The only lane identity is the ordinal
// I_FIXEDLANE, which REAPER RENUMBERS when lanes are reordered or deleted — so keying
// the ownership index by raw ordinal would silently corrupt managed/manual ownership
// on any reorder. REAPER DOES expose a writable, durable lane NAME (P_LANENAME:n) that
// travels with the lane across renumber. So the tool names each lane it mints with a
// stable, prefixed identity ("reasampler:<mode>") and keys the ownership index by that
// NAME, not the ordinal. On each apply the shell walks the track's lanes by current
// ordinal, reads each name, and reconciles ordinal<->laneKey — so a C_LANEPLAYS:N
// write always targets the lane's CURRENT ordinal for a given durable key even after a
// reorder. A lane WITHOUT the prefix was not minted by the tool: it is manual and
// off-limits (the fixed-lane analog of "never touch mute/solo").
//
// -- Design point #1 (manual-lane exemption) resolution -----------------------
//
// The SAME prefix rule is the manual/managed heuristic for auto-tag: an item on a lane
// whose name lacks the "reasampler:" prefix is on a manual lane and is EXEMPT from
// auto-tag. isManagedLaneName is the single predicate both the toggle-apply path and
// the new-content detection path consult, so the boundary is defined in one place and
// unit-tested.
#include <optional>
#include <string>
namespace reasampler {
// The prefix the tool stamps on every lane NAME it mints. A lane name carrying this
// prefix is a managed lane the tool created; any other name (or an empty/unnamed lane)
// is a user-minted manual lane. Stable-forever: changing it would strand the ownership
// of every lane in every already-saved project, so treat it like an action id string.
inline constexpr const char* kManagedLanePrefix = "reasampler:";
// True iff `laneName` is a tool-minted managed-lane name (carries kManagedLanePrefix).
// This is the load-bearing managed/manual predicate for BOTH design points #1 and #2.
bool isManagedLaneName(const std::string& laneName);
// The opaque lane-key the pure model keys by, for a lane with REAPER name `laneName`.
// For a managed lane the key IS the durable name (stable across ordinal renumber). For
// a manual/unnamed lane there is no managed key: returns std::nullopt so the caller
// treats the lane as manual (never driven, items on it exempt from auto-tag).
std::optional<std::string> managedLaneKey(const std::string& laneName);
// The lane NAME the tool mints for the lane owned by `modeId` (kManagedLanePrefix +
// modeId). The inverse of managedLaneKey for a managed lane: managedLaneKey(
// laneNameForMode(m)) == kManagedLanePrefix + m. Exposed for the Wave-3 lane-minting
// path and for tests; the apply path in this wave only READS names, but the round-trip
// contract is asserted here so minting and reading cannot drift.
std::string laneNameForMode(const std::string& modeId);
} // namespace reasampler