docs: retire Phase Rho from the plan, record it as landed

This commit is contained in:
2026-08-02 15:04:19 -04:00
parent b400384a4f
commit 51b13304ee
2 changed files with 61 additions and 369 deletions
+6 -369
View File
@@ -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,