docs(product): S17 load-capture seam — settle on (B) component-state injection

Fold the decision into the existing phase-s S17 spec: (B) via
TrackFX_SetNamedConfigParm vst_chunk; (A) ext-state handshake rejected.
Flag the shared component-state blob format as an extension<->instrument
coordination dependency.
This commit is contained in:
2026-07-26 23:09:28 -04:00
parent 725f3e7d3c
commit 37cb290cbc
2 changed files with 72 additions and 47 deletions
+29 -15
View File
@@ -956,9 +956,25 @@ The current Phase S spec gives the instrument a *live-state read* seam (it reads
index + mapping from `"reasampler"` ext-state via the bridge) but **no entry point for an
external actor to say "instantiate playing *this specific* capture."** This wave is the
reason to add that seam. It is a Phase S dependency, not extension-side, and must land in
the instrument before drop-and-load's drop half can work end-to-end. See CONTEXT.md
§Phase S for the two candidate seam mechanisms (fresh-instance ext-state handshake vs.
VST3 `setState` preset injection) and the open question on which is chosen.
the instrument before drop-and-load's drop half can work end-to-end. **Mechanism (SETTLED
— (B) VST3 component-state injection):** after `TrackFX_AddByName`, the extension writes the
new instance's component state directly via `TrackFX_SetNamedConfigParm(track, fx,
"vst_chunk", <blob>)`**verified present** in `reaper_plugin_functions.h` (`bool
TrackFX_SetNamedConfigParm(MediaTrack*, int fx, const char* parmname, const char* value)`;
the header documents the `vst_chunk`/`vst_chunk_program` parms as the base64-encoded
VST-specific chunk write-path). Deterministic, no shared-state race, no cross-process
handshake. **Load-bearing caveat:** `vst_chunk` is the plugin's **own** base64-encoded
serialized chunk (its `getChunk`/`setChunk` FXP/FXB-style blob), **not** a raw VST3
`IComponent::setState` stream REAPER re-marshals — so the extension must construct exactly
ReaSampler 9000's own state-blob bytes with the target capture pre-selected. That makes the
**component-state blob format a shared cross-artifact contract** — still being defined in
Phase S — and a coordination dependency between the extension and the instrument: both must
agree on the exact byte layout the instrument's `setChunk` accepts. Rejected alternative —
**(A) fresh-instance ext-state handshake** (extension writes a "pending load" hint into
`"reasampler"` ext-state, the fresh instance claims + clears it on init): loosely coupled
through the existing bridge, but loses on the claim/clear race — "which instance claims
which hint" needs a stable key and a cross-process handshake to get right. See CONTEXT.md
§Phase S (drop-and-load) for the full seam decision.
**Coexistence with the M11 OS drag-out (disambiguation, load-bearing):** the two drag
modes are disambiguated by **where the pointer goes**, not by a mode toggle. Inside the
panel client rect → `Internal` (unchanged). Left the panel but still over REAPER's own
@@ -976,7 +992,9 @@ still works unchanged; no media item is ever inserted into the arrange; the inst
holds no private copy (it reads the one authoritative bank).
**Depends on:** M11 (`drag_out` gesture machinery — the mode it extends); **Phase S S4**
(a loadable, playing ReaSampler 9000 instance must exist) **AND the new load-capture seam
added inside ReaSampler 9000**. Composes with — but is distinct from — **S8** (ingest
added inside ReaSampler 9000 via (B) component-state injection — the shared component-state
blob contract must be defined so the extension can construct it and the instrument's
`setChunk` accept it**. Composes with — but is distinct from — **S8** (ingest
through the bank: capture/import/drop *into* the bank) and **S13** (drop-to-load *inside*
the editor). S17 is the third integration gesture: drop *onto a track's FX button* to
instantiate a player. Gated on the rest of Phase S; the instrument seam is a Phase S
@@ -998,21 +1016,17 @@ artifact, not extension-only.
capture. Batched into one REAPER undo point (`Undo_BeginBlock2`/`EndBlock2`) so the
whole gesture is one Ctrl-Z (mirrors the bank-verb undo discipline).
- [ ] **ReaSampler 9000 (Phase S artifact):** add the **load-capture seam** — the entry
point that lets the just-added instance be told which capture to play (mechanism chosen
per the CONTEXT.md open question). This is the cross-artifact half; it lands in the
instrument, not the extension.
point that lets the just-added instance be told which capture to play via **(B)
component-state injection**: the instrument's `getChunk`/`setChunk` must round-trip a
component-state blob whose byte layout is the **shared cross-artifact contract** (defined
in Phase S) that the extension constructs with the target capture pre-selected and writes
via `TrackFX_SetNamedConfigParm(..., "vst_chunk", ...)`. This is the cross-artifact half;
it lands in the instrument, not the extension. **Coordination dependency:** the blob
format must be agreed between extension and instrument before either half is final.
- [ ] Tests: gesture disambiguation (inside-panel / over-REAPER-UI / left-REAPER) across
single- and multi-capture payloads; FX-button hit resolution (pure geometry where it
can be factored out); M11 OS drag-out and internal bank-to-bank drag both unchanged.
> **Numbering reconciliation (resolved on merge to dev).** This wave was authored on
> **dev** as a provisional **S7** while dev's Phase S ran only S1S6. On merge with the
> `phase-s` worktree, the worktree's authoritative numbering (S7 stereo, S8 ingest, S9
> change-detection, S10S16) took the lower labels, so drop-and-load was renumbered to
> **S17** — the next free label past the worktree's Phase S set. It stays a distinct
> integration gesture (drop onto a track's FX button), a sibling of but not the same as
> S8 (ingest into the bank) and S13 (drop-to-load inside the editor).
## Phase S — held and optional-forever (noted, not specified)
- **Tier 2 — "expressive" (HELD).** Velocity layers, round-robin (anti-machine-gun),
full ADSR, per-sample tuning/gain trim, sustain loops. The next depth increment once