docs: correct two false claims in the Phase Ε spec
REAPER's GetUserFileName serves both pickers — the no-save-picker finding was a regex miss. A new enum value is structural, not additive: BankModel rejects an out-of-range SourceMode/Tier rather than degrading.
This commit is contained in:
+50
-22
@@ -2451,15 +2451,20 @@ root `CMakeLists.txt`. **Does not own:** `export_plan` / `import_plan` (Ε-W2),
|
||||
- **Two version integers, not one.** `formatVersion` = what this writer emitted;
|
||||
`minReaderVersion` = the oldest reader that can read it safely. The reader's whole rule is
|
||||
`minReaderVersion <= kPackageFormatVersion`. An **additive** change (a new optional
|
||||
manifest key, a new enum value with a defined degrade) bumps `formatVersion` only; a
|
||||
**structural** change bumps both. The header carries the writer's semver
|
||||
manifest key, a new `Sample` field with a defined absent-value) bumps `formatVersion`
|
||||
only; a **structural** change bumps both. **Growing a persisted enum's vocabulary is
|
||||
structural here, not additive** — `BankModel::deserialize` *rejects* an out-of-range
|
||||
`SourceMode` or `Tier` rather than degrading it (`bank_model.cpp:232-239`, `:339-346`),
|
||||
and every enum a package carries rides inside the nested `BankModel` blob, so a new
|
||||
`SourceMode` or `Tier` value bumps both integers. The header carries the writer's semver
|
||||
(`version::stampVersion()`) alongside them, informational, so a refusal message can name
|
||||
what to install.
|
||||
- **The ladder is documented the way `origin_ledger.cpp:8-21` documents its own** — a header
|
||||
comment listing every shipped version and what changed, with the read-and-validate rule
|
||||
stated, not implied.
|
||||
- **Unknown manifest keys are skipped** (the `bank_book_json.cpp:182` behaviour), and
|
||||
**unknown persisted enum integers degrade to their defined `Unknown` equivalent**, never to
|
||||
**unknown persisted enum integers — the manifest's own, not `BankModel`'s nested ones, which
|
||||
reject per the bullet above — degrade to their defined `Unknown` equivalent**, never to
|
||||
the numeric default and never to a parse failure (`core/wire/CLAUDE.md`'s `BakeStatus` rule,
|
||||
verbatim). Both are pinned by tests, not left to inheritance.
|
||||
- **The manifest nests `BankModel`'s own serialization verbatim**, exactly as
|
||||
@@ -2510,9 +2515,11 @@ parallel with the format it will carry.
|
||||
|
||||
**Surface boundary — owns:** new `src/shell/package/package_io` (read a file's bytes, write
|
||||
bytes through temp + atomic rename, read one bank file, write one landed file, enumerate the
|
||||
bank folder's existing names, and the rollback delete), the platform file-picker seam under
|
||||
the `#ifdef _WIN32` / `#else swell/swell.h` split this codebase already uses
|
||||
(`src/shell/panel/draw_kit.cpp:11-15`, `src/shell/persist/prune_fs.cpp:35-38`), the
|
||||
bank folder's existing names, and the rollback delete), the file-picker seam for both verbs —
|
||||
**one picker, REAPER's own, on every platform**, so this track carries **no**
|
||||
`#ifdef _WIN32` / `#else swell/swell.h` split. That split is a real pattern in this codebase
|
||||
(`src/shell/panel/draw_kit.cpp:11-15`, `src/shell/persist/prune_fs.cpp:35-38`); it is simply
|
||||
not this track's shape, because REAPER owns the dialog. Also owns the
|
||||
directory's `CMakeLists.txt` and `CLAUDE.md`, and one appended `add_subdirectory` line in the
|
||||
root `CMakeLists.txt`. **Does not own:** `export_bank` / `import_bank` (Ε-W2), anything under
|
||||
`core/`, and — emphatically — `prune_fs`, which stays the deletion authority.
|
||||
@@ -2529,12 +2536,22 @@ root `CMakeLists.txt`. **Does not own:** `export_bank` / `import_bank` (Ε-W2),
|
||||
`src/shell/persist/prune_fs.cpp:5-11` and states the discriminator it satisfies — this call
|
||||
created the file, and no index ever referenced it — rather than restating the carve-out's
|
||||
text. Anything that does not satisfy that discriminator is not this function's business.
|
||||
- **The two pickers are asymmetric, and the asymmetry is real.** Import uses REAPER's own
|
||||
`GetUserFileNameForRead(char* filenameNeed4096, const char* title, const char* defext)` —
|
||||
**verified**, `vendor/reaper-sdk/sdk/reaper_plugin_functions.h:3798`. There is **no save
|
||||
picker in the REAPER API** — a sweep of that header for `FileNameFor|SaveFile|Browse`
|
||||
returns only the read picker — so export uses Win32 `GetSaveFileNameW` on Windows and
|
||||
SWELL's `BrowseForSaveFile` elsewhere (`vendor/WDL/WDL/swell/swell-functions.h:167`).
|
||||
- **Both pickers are REAPER's own, and they are symmetric.** `GetUserFileName(int mode,
|
||||
const char* caption, const char* initial_file_or_path, const char* extension_list, char*
|
||||
fnOutNeedBig, int fnOutNeedBig_sz)` — **verified**,
|
||||
`vendor/reaper-sdk/sdk/reaper_plugin_functions.h:3790`, documented at `:3788` — serves
|
||||
both directions: `mode=0` ("choose a new file") is export's destination picker, `mode=1`
|
||||
("existing file") is import's source picker. `extension_list` takes the
|
||||
`'ReaSampler banks|*.rsbank|All files|*.*'` form, and `initial_file_or_path` may be a bare
|
||||
`'.rsbank'` to set the default extension. There is **no `#ifdef _WIN32` / SWELL split and
|
||||
no wide-char round trip here** — REAPER owns the dialog on every platform, so no
|
||||
`GetSaveFileNameW` and no `BrowseForSaveFile`. `GetUserFileNameForRead` is explicitly
|
||||
"Superseded, see GetUserFileName" (`:3796`) and is not used.
|
||||
- **No fallback path.** `src/app/main.cpp:15` defines `REAPERAPI_IMPLEMENT` *without*
|
||||
`REAPERAPI_MINIMAL`, so the resolver walks the full table — `GetUserFileName` included
|
||||
(`reaper_plugin_functions.h:9084`) — and `main.cpp:292-293` aborts the extension load if
|
||||
any single function fails to resolve. No REAPER build that loads this extension can lack
|
||||
`GetUserFileName`, which makes a fallback unreachable code.
|
||||
- **No REAPER project state is touched here.** No ext-state read or write, no undo block, no
|
||||
generation bump; those belong to the verbs in Ε-W2.
|
||||
|
||||
@@ -2545,16 +2562,27 @@ root `CMakeLists.txt`. **Does not own:** `export_bank` / `import_bank` (Ε-W2),
|
||||
test asserts against a seam counter, not against a memory measurement.
|
||||
- The rollback deletes exactly the files it was given and nothing else, and is a no-op on a
|
||||
path it did not write.
|
||||
- The Windows and SWELL picker paths both compile; the SWELL signature matches
|
||||
`swell-functions.h:167` exactly. `[verify — DAW]` — neither picker is exercised in a live
|
||||
REAPER session by this track.
|
||||
- No file exceeds ~600 lines; the platform picker lives in its own TU, following the
|
||||
`drag_out` / `drag_out_win` precedent.
|
||||
- **There is exactly one picker call site and it is REAPER's.** Both verbs reach
|
||||
`GetUserFileName` — export with `mode=0`, import with `mode=1` — and no symbol named
|
||||
`GetSaveFileNameW`, `BrowseForSaveFile`, or `GetUserFileNameForRead` appears anywhere in
|
||||
`src/shell/package/`, nor any platform `#ifdef` in the picker's TU. Greppable, so it stays
|
||||
true. `[verify — DAW]` — the picker is not exercised in a live REAPER session by this track.
|
||||
- No file exceeds ~600 lines; the picker lives in its own TU with its own header. **Not** for
|
||||
the `drag_out` / `drag_out_win` reason — that precedent isolates a *Win32-only* TU, and
|
||||
there is no platform split here — but because the picker is the only REAPER-facing part of
|
||||
an otherwise REAPER-free, unit-tested module: folding it into a shared header would drag
|
||||
`reaper_plugin_functions.h` into the testable seam's include graph.
|
||||
|
||||
**Open questions.** **[propose at review]** whether the file-picker seam is its own module or
|
||||
part of `package_io` — the `drag_out_win` precedent argues its own TU; whether it also wants
|
||||
its own header is a judgment call at the size it lands. **[verify — DAW]** the default
|
||||
extension and filter strings each platform's picker actually accepts.
|
||||
**Open questions.** **[propose at review]** where the `extension_list` and default-extension
|
||||
strings live — this track's picker TU, or the Ε-W2 verbs that call it. They are user-facing
|
||||
text, and the verbs own the rest of the user-facing text; the counter-argument is that they
|
||||
are picker plumbing and only one picker exists. **[verify — DAW]** two things the header does
|
||||
not answer: (1) whether the `mode=0` picker **appends** an extension when the user types a
|
||||
bare name — `:3788` documents that `initial_file_or_path` may be `'.rsbank'` "to set the
|
||||
default extension," but not that the dialog enforces it on return, so the verb may still have
|
||||
to append `.rsbank` itself; (2) **dialog parenting** — `GetUserFileName`'s signature
|
||||
(`:3790`) takes no owner window, where the abandoned Win32 `OPENFILENAME` path would have
|
||||
passed `GetMainHwnd()`, so modality against the REAPER main window is unobserved.
|
||||
|
||||
#### Ε-W1-T3 — `import-origin-kind`
|
||||
|
||||
@@ -4321,7 +4349,7 @@ Phase Epsilon — The bank package (none of the seventeen; a direct reque
|
||||
T1 package-format .............. core/package: framing + TWO version ints
|
||||
[E-F1 RULED: proprietary RSBK. No ZIP, no zlib]
|
||||
T2 package-fs-shell ............ shell/package: atomic write, streaming, pickers
|
||||
[no REAPER save-picker exists; SWELL/Win32 split]
|
||||
[ONE picker: REAPER GetUserFileName, mode 0/1]
|
||||
T3 import-origin-kind .......... OriginKind::PackageImport = 5, append-only
|
||||
W2 The two verbs [2 tracks; disjointness CONDITIONAL — see below]
|
||||
T1 bank-export ................. export_plan + verb + action; project untouched
|
||||
|
||||
Reference in New Issue
Block a user