# src/core/view — pure Design View mode model

## Scope

Pure, REAPER-free Design View model: mode/track membership, folder-derived
visibility, snapshot-based park/restore planning, new-content (GUID) detection,
and the managed/manual lane-identity convention that underlies per-item mode
separation (fixed lanes). Does **not** include: the actual DAW-side flag
application (hide, CPU-park, per-FX offline, restore via `B_SHOWINTCP` /
`B_SHOWINMIXER` / `B_MAINSEND` / `I_FXEN`) or the never-touch-master/mute/solo
enforcement — those live in `shell/view`.

## Invariants

Design View — additive phase spec (settled decisions; Two-canvas sub-phase,
settled 2026-07-23):

- **Membership.** Default = Arrange; every untagged leaf belongs to it. Leaves
  opt in to Design (or any mode). No track appears in two modes at once except
  via an explicit show-both toggle or parent/folder derivation.
- **Parents are derived, never tagged.** A parent/folder track is visible in
  mode M if either any descendant leaf is visible in M, or the parent belongs to
  M by its own membership; an untagged parent is an Arrange member by default. A
  parent is never parked in any mode it is visible in. Master track is always
  visible and never touched.
- **N-mode model, two-mode UI.** The data model carries arbitrarily many modes;
  the UI ships Arrange + Design. A mode is (stable id, display name, ordinal).
- **Persistence.** Membership index + last-active mode + per-track flag
  snapshots ride in the existing `"reasampler"` project ext-state namespace and
  travel with the `.rpp`.
- **Non-destructive restore (enforced in the pure layer).** For every flag the
  tool drives, snapshot the prior value before parking; on toggle-back, restore
  from the snapshot, never to a hardcoded default. Round-trip (snapshot → park →
  restore) returns every driven flag to its captured value — this is the
  phase's trust anchor, the analog of the capture null test.
- **GUID-keyed, reorder-safe.** Membership keys on track GUID (`GetTrackGUID`),
  never track index; tolerates unknown/stale GUIDs (pruned on reconcile via
  `ViewModeModel::reconcile(liveGuids)`).
- **Relative/portable state only** in the persisted view section (GUID strings,
  mode ids — no absolute paths, no index positions).
- **Show-both semantics.** A per-track "pin visible across modes" flag that
  re-enables processing whenever shown. A show-both leaf appears in every
  mode's visible set and is never parked — its driven flags stay at
  snapshot/restored values. Stored on the membership record; persists;
  togglable per selection.
- **Non-goals / guardrails:** No literal second canvas — a second window or
  duplicated project stays rejected; subproject/second-project-file approaches
  and overloading item `D_POSITION` with mode semantics (timebase-offset
  regions) are rejected paths (the latter collides with the capture null test).
  Every leaf is managed: an untagged leaf is an Arrange member and, when the
  active mode is not Arrange, is fully parked and snapshot-restored exactly
  like a tagged leaf; show-both is the only way to opt a leaf out of parking.
  Restore from snapshot, never to a default: no hardcoded "on" restores.

**Two-canvas sub-phase (Phase D2/E) — settled and landed parts only:**

- **Mechanism: fixed item lanes.** Map mode → lane; toggle drives per-lane
  play/show so only the active mode's lane is present. Items keep their real
  position and real track — nothing is moved in time or deleted.
- **Membership: adoption rule for new items; active mode for new tracks.** New
  tracks are tagged to the active mode at creation. New items follow an
  adoption rule: if the item's track has pre-existing managed-eligible content
  spanning exactly one mode, the item adopts that mode; the active-mode
  fallback applies only when the track is empty or already spans multiple
  modes. Items in manual lanes are excluded from the prior-mode computation and
  are not auto-tagged at all. Membership is exclusive per item except via
  show-both.
- **Managed vs. manual lanes — indexed and distinct.** The tool maintains a
  lane-ownership index — per (track GUID, lane): managed (which mode owns it)
  vs. manual (user-minted, outside the mode system). Mode operations touch only
  managed lanes; manual lanes are never shown, hidden, silenced, or re-laned by
  a toggle, and their `C_LANEPLAYS` stays exactly as the user set it. The
  ownership index rides in `"reasampler"` `view_state` alongside the membership
  index, GUID-keyed and portable. **New invariant — mode operations touch only
  managed lanes:** "the 'which lanes may this toggle touch' decision is a pure
  query over the ownership index; only reading REAPER's live lane state is
  shell."
- **REAPER floor: v7** for this sub-phase (fixed lanes shipped in v7); no
  version-gate branch — below v7 the sub-phase is simply unavailable.
- Precision invariants (null test, bit-identical repeats, non-destructive,
  exact bounds, relative-paths-only) are entirely unaffected by this
  sub-phase — no capture path changes; lane assignment and `C_LANEPLAYS` are
  reversible flags, never a destructive operation.

## Modules

- `view_mode_model` — Design View mode system: mode registry, GUID-keyed membership, folder-tree-aware visibility derivation, snapshot-based park/restore planner, JSON round-trip.
- `view_tree` — pure `I_FOLDERDEPTH`→FolderTree helper for the Design View shell.
- `mode_switch` — REAPER-free segment layout + hit-test for the bank_panel's Design View mode switch.
- `guid_diff` — the pure, REAPER-free core of the D2 Wave-2 new-content detection: `newGuids(previous, current)` computes the GUIDs present in `current` but absent from `previous` (empty GUIDs ignored); `GuidBaseline` tracks the live GUID set across polls for one project, implementing the first-poll-after-open guard (the first `observe()` after construction/`reset()` records a baseline and reports nothing new, so pre-existing content is never mass-tagged) and re-arms via `reset()` on a detected project switch so detection never diffs across two unrelated projects.
- `lane_keys` — the pure, REAPER-free convention mapping a fixed lane's durable REAPER name (`P_LANENAME:n`) to the opaque lane-key `view_mode_model` keys by, plus the managed/manual heuristic both the toggle-apply path and the new-content/auto-tag exemption path share: `kManagedLanePrefix` ("reasampler:") stamps every lane the tool mints; `isManagedLaneName`/`managedLaneKey`/`laneNameForMode`/`modeIdFromLaneName` round-trip a lane name ↔ its owning mode id; `isOnManualLane` is the single predicate governing which lanes a toggle may drive and which items are exempt from auto-tag.

## Gotchas

- REAPER exposes no durable per-lane GUID — the only lane identity is the
  ordinal `I_FIXEDLANE`, which REAPER renumbers on reorder/delete. Lane
  identity must ride on the durable `P_LANENAME` (`lane_keys`), never the raw
  ordinal, or a reorder will silently corrupt managed/manual ownership.
- `kManagedLanePrefix` ("reasampler:") is stable-forever like an action-id
  string — changing it strands the ownership of every already-minted lane in
  every already-saved project.
- `guid_diff::GuidBaseline` must have `reset()` called on every detected
  project switch, or the next `observe()` will diff across two unrelated
  projects and mass-tag (or miss) content.
