docs: retire Phase Rho from the plan, record it as landed
This commit is contained in:
+6
-369
@@ -91,8 +91,9 @@ decision.**
|
||||
RULED**, same day (Daniel, 2026-08-02): **Ρ-F1** multi-track — *"refuse"*, one track per
|
||||
fire, a settled non-goal rather than a deferral; **Ρ-F2** the result track's mode —
|
||||
*"for this action which is not a capture, the result track should always go to
|
||||
arrange"*; **Ρ-F3** tail — *"follow panel tail settings."* The rulings are folded into
|
||||
the track below and indexed at `docs/product/render-in-place.md` §"Rulings". **Ρ-F2
|
||||
arrange"*; **Ρ-F3** tail — *"follow panel tail settings."* Ρ-W1-T1 has landed; see
|
||||
`docs/COMPLETED.md` for the full narrative, and the rulings remain indexed at
|
||||
`docs/product/render-in-place.md` §"Rulings". **Ρ-F2
|
||||
overrode the request's own original wording** ("stays in whatever mode was active") and
|
||||
is the only one of the three that changed the spec: the result track is now an Arrange
|
||||
member unconditionally, the A/B-on-the-bench behaviour mode-following would have enabled
|
||||
@@ -2891,372 +2892,6 @@ properties under test are structural and a large payload proves nothing extra.
|
||||
|
||||
---
|
||||
|
||||
## Phase Ρ — Render in place: a track's output to a new sibling, source to the bench
|
||||
|
||||
**Ships:** one bindable action that renders the selected track's output over the current
|
||||
range to a file outside the bank, drops that file as an item on a brand-new sibling track
|
||||
at the exact position it was rendered from, clones the source's colour and its name with an
|
||||
idempotent `Capture ` prefix, moves the source track into Design mode — where Design
|
||||
View's existing park hides it, takes it out of the mix, and puts its FX offline — and puts
|
||||
the result track into Arrange, unconditionally. The bank is never read, never written, and
|
||||
never notified.
|
||||
|
||||
**Consolidates: none of the seventeen.** Phase Ρ came from a direct request (Daniel,
|
||||
2026-08-02) and is scoped in `docs/product/render-in-place.md`. Daniel's framing, verbatim
|
||||
in substance: *similar to REAPER's "Render selected track time selection to new track
|
||||
(stereo) and mute original", except instead of muting the original, the source track
|
||||
stays/goes to design mode, and the resulting new sibling track — which gets the rendered
|
||||
audio item placed correctly in the timeline — stays in whatever mode was active when the
|
||||
action was run; the new track clones the source track colour and name with a Capture
|
||||
prefix; this must NOT put the rendered audio into the ReaSampler banks/pool.* **The
|
||||
mode-following clause in that framing was superseded by Daniel's own Ρ-F2 ruling the same
|
||||
day** — *"for this action which is not a capture, the result track should always go to
|
||||
arrange"* — and the quote is kept verbatim only as the record of the request. It
|
||||
**supersedes nothing** — a sweep of `docs/TODO.md` and `docs/TODO-1.0.md` for
|
||||
`render.in.place|render to new track|preserve.source` returns nothing.
|
||||
|
||||
### The framing answer — why this does not breach the load-bearing principle
|
||||
|
||||
Root `CLAUDE.md`'s rule — *capture and placement are separate acts; any code path that
|
||||
auto-inserts a capture into the timeline must be rejected in review* — is a rule **about
|
||||
the bank**, and Phase Ρ does not put the bank on either side of its verb. The full argument
|
||||
is `docs/product/render-in-place.md` §"The third verb"; the operative summary, which every
|
||||
review of this phase must apply:
|
||||
|
||||
| Verb | Source | Sink | Touches the bank |
|
||||
|---|---|---|---|
|
||||
| **Capture** (`RunCapture`, batch, realtime, bake, ingest) | arrange / instrument | bank | writes it |
|
||||
| **Placement** (`RunInsertSelected`, `performArrangeDrop`) | bank | arrange | reads it |
|
||||
| **Render in place** (this phase) | arrange | arrange | never |
|
||||
|
||||
What Ρ shares with capture is the **render** — `renderOffline`, `FxBypassGuard`, exact
|
||||
custom time bounds, `RENDER_ADDTOPROJ = 0`, the multi-track refusal — not the capture. A
|
||||
capture is a render *plus* a bank landing; Ρ takes the mechanism and declines the landing.
|
||||
|
||||
**Four boundary conditions, three of them structural, all review-rejectable:**
|
||||
|
||||
1. Ρ's shell never calls `session.bank()`, `session.book()`, `session.recordCreated()`, or
|
||||
`session.bumpBankGeneration()`. The `Sample` the backend returns is discarded, and on the
|
||||
`ProjectMedia` destination its `relativePath` is left **empty** — a Ρ `Sample` is inert
|
||||
by construction.
|
||||
2. **Ρ cannot express "write into the bank folder."** The destination reaches the backend as
|
||||
a two-valued enum, never a caller-supplied path string. A `renderDir` string on
|
||||
`CaptureRequest` instead of the enum **is** the drift, and is rejected on sight.
|
||||
3. Ρ's file is never recorded as owned, so prune (`(owned ∩ present) − referenced`) cannot
|
||||
reach it — and it lives outside the bank folder, so prune's enumeration never sees it
|
||||
either. Two independent layers. The tool deletes only what it owns; a render-in-place
|
||||
file belongs to the project.
|
||||
4. **Traffic is one-way.** Ρ may borrow capture's render; **capture may never borrow Ρ's
|
||||
placement.** No `place` flag on `CaptureActionDef`, no "capture and also place" action,
|
||||
ever.
|
||||
|
||||
### Phase-Ρ acceptance criteria
|
||||
|
||||
These bind the track in this phase, in addition to the plan-wide set above.
|
||||
|
||||
- **Placement is sample-exact and unsnapped.** The item lands at the render window's
|
||||
`startSeconds`, unrounded, with `SnapToGrid` deliberately **not** applied (unlike
|
||||
`performArrangeDrop`). Ρ's placement is the null test performed automatically — a render
|
||||
of a range re-inserted at its source position nulls to silence against the source — so a
|
||||
snapped or rounded placement is a phase failure, not a rough edge.
|
||||
- **No tempo conform, ever.** `computeInsertMode(InsertOptions{})` only; `insert_plan`
|
||||
already guarantees the &4 stretch-to-time-selection bit is never set. No conform variant
|
||||
of this action is offered.
|
||||
- **The bank path is byte-identical to today.** The `CaptureDestination` enum defaults to
|
||||
`Bank`; every existing capture entry point must produce exactly the file, path, hash, and
|
||||
index entry it produces now. If any capture test changes expectation, the seam is wrong.
|
||||
- **The new track is bare.** `InsertTrackInProject(proj, p, /*flags=*/0)` — flags&1 adds
|
||||
default envelopes/FX (SDK header 3954) and a default chain would process the render a
|
||||
second time.
|
||||
- **Both `I_FOLDERDEPTH` writes, or none.** The sibling-placement arithmetic is pure and
|
||||
unit-tested before any DAW work; a render that lands the new track at the wrong nesting
|
||||
level is audibly wrong in both directions (double-processed through a folder it re-enters,
|
||||
or bypassing the folder bus entirely).
|
||||
- **The Ρ-F2 ruling survives the auto-tag detector.** The result track and its placed
|
||||
item are tagged `kArrangeModeId` **explicitly** (a membership record, not an `untag()`
|
||||
to the Arrange default), and `panel_input::detectNewContent` drops added GUIDs that
|
||||
already carry a record. Without that filter the detector tags every new track to the
|
||||
active mode on its next tick and a Design-fired render silently becomes a Design
|
||||
member — the ruling reversed inside a second. This criterion **replaces** the
|
||||
tag-before-reapply ordering criterion the phase carried under mode-following: with the
|
||||
result track an Arrange member unconditionally, a Design reapply parking it is the
|
||||
correct outcome and the ordering is state hygiene, not behaviour.
|
||||
- **One undo block, `UNDO_STATE_ALL`,** opened before the track is created and closed after
|
||||
the mode reapply; the render sits outside it. `persistViewState` runs **after** the block
|
||||
closes — it may raise a Save-As dialog, which must not sit inside an open undo block
|
||||
(`design_view_actions::doMoveItems`' documented ordering).
|
||||
- **Every pure module gets a `<module>_tests` target.** All three pure additions land in
|
||||
existing `core/capture` modules that already have one.
|
||||
|
||||
**Performance posture.** Every surface is cold — one gesture, once. None of the named hot
|
||||
paths (peaks envelope compute, audition, the realtime-capture tick's single-pointer-test
|
||||
idle fast path, the instrument's `process()`) is touched.
|
||||
|
||||
**Concurrency.** Phase Ρ is extension-side. Γ lives in `core/instrument/` +
|
||||
`shell/instrument/`; Ε lands in the new `core/package/` + `shell/package/`. The pre-existing
|
||||
files Ρ edits are named in its track's surface boundary and intersect neither — including
|
||||
`shell/panel/panel_input.cpp`, which the Ρ-F2 ruling adds: no in-flight track in this plan
|
||||
touches that file (Phase Ψ's two named regions in it, the footer block and the drag-arm
|
||||
block, are both landed, and both are functions other than `detectNewContent`).
|
||||
|
||||
### Rulings — Daniel's, 2026-08-02. Nothing open.
|
||||
|
||||
All three forks this phase opened were ruled the day it was framed. Full statements, the
|
||||
counter-arguments that made each a fork, and the one unexercised alternative:
|
||||
`docs/product/render-in-place.md` §"Rulings".
|
||||
|
||||
- **Ρ-F1 — multi-track. RULED: refuse.** One selected track per fire, inherited from
|
||||
`isMultiTrackStemRender`. A **settled non-goal**, in the same register as the other
|
||||
entries under §"What Phase Ρ explicitly is NOT" — not deferred-with-a-plan. There is no
|
||||
per-track loop planned, no second wave holding one, and no seam to leave half-open for
|
||||
it. Matches the framing recommendation; nothing in the track changed.
|
||||
- **Ρ-F2 — the result track's mode. RULED: always Arrange.** *"For this action which is
|
||||
not a capture, the result track should always go to arrange."* **This overrode the
|
||||
framing and the request's own original wording** — the result track no longer follows
|
||||
the active mode. The source still goes to Design. Three consequences, all specced
|
||||
below: the result track and its item are tagged `kArrangeModeId` explicitly; firing
|
||||
from Design produces **no visible change** (the A/B-on-the-bench behaviour
|
||||
mode-following would have enabled **does not exist** and must not be cited as a
|
||||
benefit anywhere); and the panel's auto-tag detector needs a two-line
|
||||
explicit-tag-wins filter, or it re-tags the result track to Design on its next timer
|
||||
tick and silently reverses the ruling.
|
||||
- **Ρ-F3 — tail. RULED: follow the panel tail settings.** Matches the framing
|
||||
recommendation; nothing in the track changed. **Two consequences, accepted rather than
|
||||
caveated:** under Auto/Manual the placed item is **longer than the window it replaces**
|
||||
(correct for a decaying chain, wrong for a butt-joined section — the user's lever is
|
||||
the panel's own tail setting), and the exact-bounds gate is **inactive** in those two
|
||||
modes because it runs only under `TailMode::None`. Both are inherited from every other
|
||||
capture path, not introduced here. The item's **start** is exact in all three modes, so
|
||||
the null test holds in all three. The third option floated at framing (run the gate's
|
||||
start-alignment check regardless of tail mode) is **not ruled in** and is recorded in
|
||||
the product doc as an unexercised alternative.
|
||||
|
||||
---
|
||||
|
||||
### Ρ-W1 — The verb
|
||||
|
||||
**Depends on:** nothing.
|
||||
|
||||
**One track, deliberately.** The whole phase is roughly 350 lines: three small pure
|
||||
additions to existing `core/capture` modules, one bounded edit to the offline backend, one
|
||||
new shell TU, a two-line filter in `panel_input.cpp`, one `ActionTableRow`, four invariant
|
||||
amendments. Splitting it would create a
|
||||
merge dance across `core/capture` for no gain, and the pure half cannot be reviewed
|
||||
meaningfully apart from the caller that gives it meaning. **Named contingency:** if the
|
||||
sibling-placement arithmetic balloons in implementation, that function is the natural split
|
||||
point — it is the only piece with zero dependency on anything else in the track.
|
||||
|
||||
#### Ρ-W1-T1 — `render-in-place`
|
||||
|
||||
**Goal.** One bindable action: render the selected track's output over the current range to
|
||||
the project's recording path, place it on a new sibling track at the exact render position,
|
||||
clone colour and name, move the source to Design and the result track to Arrange —
|
||||
**always Arrange, whatever mode was active** (Ρ-F2).
|
||||
|
||||
**Spec:** `docs/product/render-in-place.md` — §"The render", §"Where the file goes",
|
||||
§"Placement", §"The new track", §"Mode transitions", §"Undo", §"The action".
|
||||
|
||||
**Surface boundary — owns:**
|
||||
- **New:** `src/shell/capture/render_in_place.{h,cpp}` (the action body) and its
|
||||
`CMakeLists.txt` entry. It lives in `shell/capture/` rather than `shell/actions/` because
|
||||
it composes `renderOffline` and `ResolveScopeSource` — that directory's `CLAUDE.md` places
|
||||
action *bodies* here and reserves `shell/actions` for skins over mutation logic owned
|
||||
elsewhere.
|
||||
- **Extends (existing modules, existing test targets):** `core/capture/track_topology`
|
||||
(`siblingPlacement`), `core/capture/capture_name` (`captureTrackName`),
|
||||
`core/capture/capture_paths` (`RenderPaths` + `deriveRenderPaths`, with `deriveBankPaths`
|
||||
re-expressed over it so the file-stem spelling keeps one owner —
|
||||
`bankRelativeForName` already depends on that).
|
||||
- **Edits, bounded:** `src/shell/capture/capture.{h,cpp}` — the `CaptureDestination` enum on
|
||||
`CaptureRequest`, the ~6-line destination branch at path derivation, and
|
||||
`CaptureResult::absolutePath`. `src/shell/panel/panel_input.cpp` — two lines inside
|
||||
`detectNewContent` only, dropping added GUIDs that already carry a membership record
|
||||
(`MembershipIndex::query(guid) != nullptr`) before the `autoTagNewContent` call. That
|
||||
edit exists **only because of the Ρ-F2 ruling**. `src/app/main.cpp` — one
|
||||
`ActionTableRow`.
|
||||
- **Does not own:** anything under `core/instrument/`, `shell/instrument/`, `core/package/`,
|
||||
`shell/package/`, `core/model/`, `core/tracking/`, `core/reclaim/`, or `shell/persist/`.
|
||||
No new directory. No new persisted state, no new ext-state key, no new wire version rung.
|
||||
|
||||
**Behavior.**
|
||||
- **Resolve** via `ResolveScopeSource(CaptureScope::Track, …)` — razor-else-time range,
|
||||
selected tracks, canonical GUIDs, source track name. No range → refuse with the reason
|
||||
`resolveRange` produced. **Item extent is not a fallback and must not become one.**
|
||||
- **Refuse multi-track by inheritance.** `renderOffline` fires `isMultiTrackStemRender`
|
||||
before touching anything, keyed on the render *source* (`SelectedTracks`), so Ρ adds no
|
||||
check of its own and gets `multiTrackRefusalMessage(CaptureScope::Track)` for free.
|
||||
- **Render** through `renderOffline(CaptureScope::Track, src.sourceTracks, req)` with
|
||||
`req.destination = ProjectMedia`, tail from `bankPanelTailSetting()`, `channelCount = 2`,
|
||||
`Float32`, `sampleRate = 0`, and the name from `captureNameFor(src.trackNames, 0,
|
||||
"capture")`. Inherits the FX-bypass guard, the render-selection guard, the `RENDER_*`
|
||||
snapshot/restore, `RENDER_ADDTOPROJ = 0`, the exact-bounds gate, the unsaved-project
|
||||
Save-As gate, and the lossless mono collapse — **all unchanged**.
|
||||
- **Destination resolves in the backend, after its own save gate**, so an unsaved project is
|
||||
still prompted before any path arithmetic runs. `ProjectMedia` → `GetProjectPathEx(proj,
|
||||
…)` (SDK header 2550; header 3102 names it as the way to get the *effective* recording
|
||||
path when `RECORD_PATH` is blank or relative). The relative-paths-only invariant is
|
||||
untouched — it binds the `BankIndex`, and Ρ writes to no index.
|
||||
- **Sibling placement** (pure, in `track_topology`): prefix-sum `I_FOLDERDEPTH` to absolute
|
||||
levels; `L = level[srcIdx]`; if `depth[srcIdx] >= 1` the insert position `p` is the first
|
||||
`j > srcIdx` with `level[j] == L` (else `count`), otherwise `p = srcIdx + 1`; then exactly
|
||||
two writes — `depth[p-1] = L - level[p-1]` and the new track's `depth = level[p] - L`
|
||||
(with `level[count] = 0`). Total delta sum preserved, so nothing downstream shifts. The
|
||||
five cases and their expected outcomes are tabulated in the product doc; a malformed
|
||||
project whose deltas do not sum to zero clamps rather than asserts.
|
||||
- **Create + dress:** `InsertTrackInProject(proj, p, 0)`, `GetTrack(proj, p)`;
|
||||
`SetMediaTrackInfo_Value(new, "I_CUSTOMCOLOR", (double)GetTrackColor(source))` — one line
|
||||
clones a colour and the absence of one, since `GetTrackColor` returns the value already
|
||||
OR'd with `0x1000000` and `0` means unset; `GetSetMediaTrackInfo_String(new, "P_NAME",
|
||||
buf, true)` with `captureTrackName(trackName(source))`.
|
||||
- **Name rule (pure, tested):** `"Capture " + sourceName`, **idempotent** — if the source
|
||||
name already begins with the prefix, the new name is the source name verbatim, so a second
|
||||
run yields `Capture MONEY`, never `Capture Capture MONEY`. An unnamed source yields
|
||||
`Capture Track N` (`trackName` uses `GetTrackName`, which already answers REAPER's own
|
||||
convention — the Ψ-W2-T1 precedent). A counter suffix is rejected: REAPER does not
|
||||
uniquify track names either, and it is a treadmill.
|
||||
- **Place:** snapshot the edit cursor → `SetOnlyTrackSelected(new)` →
|
||||
`SetEditCurPos(src.startSeconds, false, false)` → `InsertMedia(absolutePath,
|
||||
computeInsertMode(InsertOptions{}))` → restore the cursor. **Leave the new track selected,
|
||||
alone** — a deliberate divergence from the restore-the-selection convention every other
|
||||
placing path follows, because in the headline case the source is being parked out of sight
|
||||
in the same gesture and restoring the selection would leave the user selecting an
|
||||
invisible track.
|
||||
- **Modes (Ρ-F2, RULED — absolute, not mode-following):**
|
||||
`membership().tag(sourceGuid, kDesignModeId)` (covers stays *and* goes — `tag` replaces
|
||||
prior single-mode membership); `membership().tag(newTrackGuid, kArrangeModeId)` —
|
||||
**`kArrangeModeId` unconditionally, never `view.activeModeId()`**; `membership().tag(…,
|
||||
kArrangeModeId)` for **each item on the new track** (enumerate after `InsertMedia`; the
|
||||
track is brand new so those are exactly the items just placed — `item_read::itemGuid` is
|
||||
the existing GUID seam); then `mintManagedLanes(view, nullptr)`; then `applyMode(view,
|
||||
view.activeModeId(), nullptr)` — a reapply, never a switch, so it is not transport-gated
|
||||
and does not touch solo.
|
||||
- **Why explicit tags and not `untag()`:** an untagged GUID is an Arrange member by
|
||||
behaviour but carries no membership record, and the record is what the auto-tag
|
||||
detector's new filter keys on. Tag, don't untag. **This is the first explicit
|
||||
`kArrangeModeId` record in the tree** — the shipped *tag selected tracks → Arrange*
|
||||
action dispatches to `doUntag()`, i.e. Arrange-by-absence. The record is well-formed
|
||||
and behaviourally identical (`isMember` answers the same for both states, `untag()`
|
||||
still clears it); it costs one persisted entry. Unit-test the JSON round-trip;
|
||||
`[verify — DAW]` that such a project shows no view-behaviour difference.
|
||||
- **Fired from Design, the result track is parked and nothing is visible.** That is the
|
||||
ruled behaviour, not a bug: the result track is an Arrange member, so a Design reapply
|
||||
parks it, and it appears in the source's place on the next switch to Arrange. The
|
||||
tag-before-reapply ordering that mode-following made load-bearing is now state hygiene
|
||||
only.
|
||||
- **Selection stays absolute too.** The result track is left selected, alone, in both
|
||||
cases — even fired from Design, where that selects a parked track. Making selection
|
||||
conditional on the active mode would reintroduce exactly the mode-relative behaviour
|
||||
Ρ-F2 removed.
|
||||
- **Inherited and deliberately not fought:** show-both on the source is not cleared (it is
|
||||
the user's pin); a folder-parent source is not hidden by tagging, because parents are
|
||||
derived (`core/view/CLAUDE.md`) — the existing tag action behaves identically. **Do not
|
||||
invent a cascade that tags the children.**
|
||||
- **Feedback:** silent on success (the new track is the feedback), `ShowConsoleMsg` on every
|
||||
refusal — matching `RunInsertSelected`. **[propose at review]** whether the Design-fired
|
||||
path breaks that silence with a one-line `ShowConsoleMsg` naming the track it created:
|
||||
under Ρ-F2 that path produces no visible change, so a silent success is
|
||||
indistinguishable from a no-op. Recommendation: yes, one line and one string.
|
||||
- **Action:** suffix **`RENDER_TRACK_IN_PLACE`** — FOREVER-STABLE per channel, minted as a
|
||||
new `RENDER_*` verb family rather than a `CAPTURE_*` member, deliberately: the id is
|
||||
permanent and is the most durable statement the codebase makes about which pillar a
|
||||
feature belongs to. Phrase: **`"render selected track to a new track (source moves to
|
||||
Design)"`**. Main section only; one `ActionTableRow`; no `custom_action`/`hookcommand2`.
|
||||
|
||||
**Invariant amendments — deliverables of this track, not follow-ups** (the Phase Ψ
|
||||
precedent, where three such amendments were acceptance criteria of the tracks that broke
|
||||
them). A track that lands Ρ without these reads as an invariant breach in review.
|
||||
|
||||
1. `src/shell/capture/CLAUDE.md` §Invariants — *"`RunInsertSelected` is the one deliberate
|
||||
exception to capture-never-places."* Amend to state that this directory now hosts two
|
||||
placing paths and give the discriminator: `RunInsertSelected` places a *bank sample*;
|
||||
`render_in_place` places a render that never entered the bank. Neither is a capture
|
||||
placing itself.
|
||||
2. `src/shell/actions/CLAUDE.md` §Invariants — *"`arrange_drop_win` is the only
|
||||
timeline-placing shell in this directory."* Scope the sentence explicitly to that
|
||||
directory and cross-reference the third verb.
|
||||
3. Root `CLAUDE.md` §"The load-bearing principle" — **one sentence, not a rewrite**: a
|
||||
render that never enters the bank and never leaves it is a third verb outside the rule,
|
||||
with the two-way boundary named. The prohibition must not be softened; the exception must
|
||||
be named precisely.
|
||||
4. `src/core/view/CLAUDE.md` §Invariants — *"New tracks are tagged to the active mode at
|
||||
creation."* **Added by the Ρ-F2 ruling.** That rule now applies only to a GUID carrying
|
||||
no membership record: an explicit tag wins over the detector. Amend the sentence and
|
||||
state the reason in one clause — the detector classifies content the *user* made, not
|
||||
content the tool made and already classified. `src/shell/panel/CLAUDE.md` describes
|
||||
`panel_input` only as "the new-content auto-tag timer" and does not restate the rule, so
|
||||
it needs no amendment.
|
||||
|
||||
**Acceptance criteria.**
|
||||
- `siblingPlacement` is unit-tested over all five cases in the product doc's table (normal
|
||||
mid-folder, last-in-folder `-1`, last-in-two-folders `-2`, folder parent, last track in
|
||||
the project) plus a malformed non-zero-sum delta list, with no DAW.
|
||||
- `captureTrackName` is unit-tested for plain, already-prefixed (idempotence), empty, and
|
||||
`Track N` sources.
|
||||
- `deriveRenderPaths` round-trips the same stem spelling `deriveBankPaths` produces for the
|
||||
same inputs, and the existing `capture_paths` tests pass unchanged.
|
||||
- Every existing capture test passes with **no expectation change** — the proof that the
|
||||
destination seam is inert on the `Bank` path.
|
||||
- `render_in_place.cpp` contains no reference to `session.bank()`, `session.book()`,
|
||||
`recordCreated`, or `bumpBankGeneration` — checkable by grep, and the review gate for
|
||||
boundary condition 1.
|
||||
- The action registers, dispatches, and mirror-unregisters through the single
|
||||
`ActionTableRow` — no separate registration mechanism.
|
||||
- `render_in_place.cpp` contains no reference to `activeModeId` in the tagging path —
|
||||
checkable by grep, and the review gate for the Ρ-F2 ruling. The result track and its
|
||||
item are tagged `kArrangeModeId`; `activeModeId()` appears only in the `applyMode`
|
||||
reapply.
|
||||
- `detectNewContent` no longer auto-tags a GUID that already carries a membership record,
|
||||
and the existing `view_mode_model` / `guid_diff` tests pass unchanged (the filter is
|
||||
shell-side; `autoTagNewContent`'s pure contract does not move).
|
||||
- A membership index carrying an explicit `kArrangeModeId` record JSON-round-trips
|
||||
unchanged, and `isMember` answers identically for that record and for an absent one —
|
||||
one added `view_mode_model` test, no DAW.
|
||||
- All four invariant amendments are in the diff.
|
||||
|
||||
**DAW-verification obligation** (stated up front; nothing past the pure functions is
|
||||
unit-testable):
|
||||
- **The null test on Ρ's own output** — render a track over a range, polarity-invert the
|
||||
source against the new track, confirm silence. The phase's trust anchor.
|
||||
- **The three folder cases**, each confirming the new track's nesting level and that the
|
||||
render feeds (or correctly bypasses) the folder bus. `[verify — DAW]` whether
|
||||
`InsertTrackInProject` plus the two `I_FOLDERDEPTH` writes settle without an intermediate
|
||||
`TrackList_AdjustWindows(false)` (SDK header 7735; header 2721 notes some attribute writes
|
||||
need a manual panel update, and the `isMinor` semantics are undocumented).
|
||||
- **The collapsed-mono placement** — render a dead-centre source, confirm a mono item, and
|
||||
confirm it sums at the same level the stereo source did. This is root `CLAUDE.md`'s
|
||||
existing `[verify — DAW]` on mono-item-on-stereo-track summing, **promoted to
|
||||
load-bearing by this phase**: Ρ is the first path where a collapsed render is placed into
|
||||
the mix by the tool itself.
|
||||
- **Both mode transitions under the Ρ-F2 ruling** — fired from Arrange (source parks;
|
||||
result track visible and in the mix) and fired from Design (source stays on the bench;
|
||||
result track parked, then present in the source's place after a switch to Arrange).
|
||||
**In each case wait out at least one panel timer tick and re-check the membership** —
|
||||
that is the auto-tag-detector regression, and it is what catches a missing
|
||||
explicit-tag-wins filter or an untagged item, either of which silently reverses the
|
||||
ruling. The old ordering check (that the new track is never momentarily parked) no
|
||||
longer applies.
|
||||
- **Undo** — one Ctrl-Z removes track and item and reverts the folder-depth write; the file
|
||||
survives (REAPER's undo deletes no files, and prune cannot reach this one); the source
|
||||
stays tagged Design, whose way out is the existing *tag selected tracks → Arrange* action.
|
||||
All three residuals are inherited from the documented model-vs-undo split
|
||||
(`src/shell/view/CLAUDE.md` §Gotchas), not introduced here.
|
||||
- **Name and colour clone**, including a second run over an already-prefixed track (must not
|
||||
stack) and an unnamed source (must read `Capture Track N`).
|
||||
- **`GetProjectPathEx` against a project with a non-default recording path**, confirming the
|
||||
render lands where the project's media lives.
|
||||
|
||||
**Open questions.** **No [Daniel] questions — Ρ-F1, Ρ-F2 and Ρ-F3 are all RULED**
|
||||
(2026-08-02), so nothing here is gated. Two **[propose at review]** items, both
|
||||
recommendation-carrying. First, whether the Design-fired path emits a one-line
|
||||
`ShowConsoleMsg` (see the Feedback bullet) — recommendation: yes. Second, **[propose at
|
||||
review]** whether `render_in_place` should refuse when the source track is the master —
|
||||
`ResolveScopeSource` collects via `CountSelectedTracks`/`GetSelectedTrack`, which skip the
|
||||
master (SDK header), so a master-only selection already resolves to zero tracks and refuses
|
||||
with "nothing selected"; the recommendation is to leave that inherited behaviour alone
|
||||
rather than add a message for a case the existing path already handles correctly.
|
||||
|
||||
---
|
||||
|
||||
## Phase Λ — ReaSampler on Linux: both artifacts, shipped
|
||||
|
||||
**Ships:** `reaper_reasampler.so` and `reasampler_9000.vst3` built, installed and documented
|
||||
@@ -4219,7 +3854,8 @@ proof it exists to give.
|
||||
day it was framed, so the track here is not gated on a decision; see "Decision state"
|
||||
above. **Ρ-F2 was the one that moved the spec** — the result track goes to Arrange
|
||||
unconditionally rather than following the active mode, which is also the only reason the
|
||||
phase touches `panel_input.cpp` at all.
|
||||
phase touches `panel_input.cpp` at all. **Ρ-W1-T1 has landed**; see `docs/COMPLETED.md`
|
||||
for the full narrative.
|
||||
- **All of Phase Λ** (`pl-*`). **Thirteen pending tracks across seven waves** (Λ-W2…Λ-W8),
|
||||
plus Λ-W1's two audit tracks, which are landed. From a direct request (Daniel, 2026-08-02),
|
||||
not from `TODO-1.0.md`; the product reasoning lives in `docs/product/linux-readiness.md`
|
||||
@@ -4373,6 +4009,7 @@ Phase Epsilon — The bank package (none of the seventeen; a direct reque
|
||||
textually adjacent only — serialize T2 behind T1 if zero contention is wanted.
|
||||
|
||||
Phase Rho — Render in place (none of the seventeen; a direct request 2026-08-02)
|
||||
[LANDED — Ρ-W1-T1; see docs/COMPLETED.md; full detail section removed from this file]
|
||||
W1 The verb [ONE track, deliberately]
|
||||
T1 render-in-place ............. render selected track -> new sibling track,
|
||||
item placed at the exact render position,
|
||||
|
||||
Reference in New Issue
Block a user