docs: anchor the product-doc citations to symbols, not line numbers
Ten more were already pointing at unrelated code. Vendor SDK and SWELL citations left alone — those files don't move.
This commit is contained in:
@@ -22,9 +22,9 @@ stated; contradict it in review with an argument, not a preference.
|
||||
|
||||
**A bank package is one file that carries one bank — its audio and its index —
|
||||
out of a project and into another.** Today a bank is per-project by construction:
|
||||
the audio sits in `<projectDir>/reasampler_bank/` (`core/capture/capture_paths.h:16`,
|
||||
the audio sits in `<projectDir>/reasampler_bank/` (`core/capture/capture_paths.h`'s
|
||||
`kBankSubfolder`) and the index that gives that audio meaning lives in the `.rpp`'s
|
||||
project ext state under the `"reasampler"` namespace (`src/ext_keys.h:25`,
|
||||
project ext state under the `"reasampler"` namespace (`src/ext_keys.h`'s
|
||||
`kProjExtBanksKey`). The two travel together with the project and nowhere else.
|
||||
Export writes both halves into a single `.rsbank` file; import lands them into
|
||||
another project's bank folder and index.
|
||||
@@ -116,7 +116,7 @@ cases, and still no compression.
|
||||
**The two costs, accepted with the ruling.** (1) The package is **opaque without our
|
||||
tool** — no unzip-and-look support path. (2) We own the hostile-input hardening of our
|
||||
own parser, to the discipline `bank_model::deserialize` and `parseLedger` already
|
||||
carry — *error signaled, never UB* (`bank_model.h:204-206`). Both are priced in; a
|
||||
carry — *error signaled, never UB* (`bank_model.h`'s `BankModel::deserialize`). Both are priced in; a
|
||||
later "let's make it inspectable" impulse is a new phase's argument, not this one's.
|
||||
|
||||
**This was a one-way door and it is now shut** — packages are in users' hands the day
|
||||
@@ -133,28 +133,29 @@ moves from here.
|
||||
The repo already carries **two** versioning mechanisms, and they answer different
|
||||
questions:
|
||||
|
||||
1. **A blob-schema ladder.** `src/core/tracking/origin_ledger.cpp:8-30` states the
|
||||
ladder for the `owned_files` blob in a header comment (v1 legacy path-only, v2
|
||||
1. **A blob-schema ladder.** `src/core/tracking/origin_ledger.cpp`'s version-ladder
|
||||
header comment states the
|
||||
ladder for the `owned_files` blob (v1 legacy path-only, v2
|
||||
current), pins `constexpr int kLedgerVersion = 2`, and — the load-bearing part —
|
||||
**reads and validates `"v"`, not merely writes it**: "A version above
|
||||
`kLedgerVersion` is therefore its own degraded status, never a Loaded ledger"
|
||||
(`:20-21`). The parse outcome is a three-way `Ok` / `Malformed` / `FutureVersion`
|
||||
(`:171`, `:185`), deliberately distinguished so the operator gets the right
|
||||
(the same comment). The parse outcome is a three-way `Ok` / `Malformed` / `FutureVersion`
|
||||
(`origin_ledger.cpp`'s `ParseOutcome` enum and `parseStored`), deliberately distinguished so the operator gets the right
|
||||
recovery advice. A *field-vocabulary* gap behaves oppositely: an unrecognized
|
||||
`OriginKind` integer degrades to `Unknown` rather than failing the parse
|
||||
(`:32-40`), because "a vocabulary gap must not halt the prune"
|
||||
(`src/core/tracking/CLAUDE.md:82-86`).
|
||||
2. **An app writing-version stamp.** `src/core/version/app_version.h:165-186` —
|
||||
(`origin_ledger.cpp`'s `kindFromInt`), because "a vocabulary gap must not halt the prune"
|
||||
(`src/core/tracking/CLAUDE.md` §"Gotchas").
|
||||
2. **An app writing-version stamp.** `src/core/version/app_version.h`'s
|
||||
`WritingVersion` with `PreVersioning` / `Unknown` / `Stamped`, classified by
|
||||
`classifyWritingVersion`, stamped into project ext state by
|
||||
`src/shell/persist/ext_state_io.cpp:172-176` using `stampVersion()` (the numeric
|
||||
`ReaSamplerSession::saveToActiveProject` (`src/shell/persist/ext_state_io.cpp`) using `stampVersion()` (the numeric
|
||||
triple only, no channel suffix). It is informational: an absent stamp is "not an
|
||||
error and not a warning" (`app_version.h:166-168`).
|
||||
error and not a warning" (`app_version.h`'s `WritingVersion` comment, the `PreVersioning` case).
|
||||
|
||||
**An observation worth recording, not a defect to fix here:** `BankBook` writes
|
||||
`"version": 1` into the banks blob (`src/core/model/bank_book_json.cpp:37`) but its
|
||||
`"version": 1` into the banks blob (`src/core/model/bank_book_json.cpp`'s `BankBook::serialize`) but its
|
||||
parser skips the key along with every other unknown one
|
||||
(`bank_book_json.cpp:182` — `if (!r.skipValue()) return false; // version, or unknown`).
|
||||
(`bank_book_json.cpp`'s `parseBook` — `if (!r.skipValue()) return false; // version, or unknown`).
|
||||
The book's version field is therefore **decorative today** — written, never read,
|
||||
never gating. The ledger's is the precedent to extend; the book's is the precedent
|
||||
not to repeat.
|
||||
@@ -175,10 +176,10 @@ advise (an integer tells a user nothing about which build to install).
|
||||
|
||||
A single ladder has one bad property: **every change strands every older reader,
|
||||
even a purely additive one.** That is not hypothetical here — look at what `Sample`
|
||||
has actually accumulated: `rootNote` and `loop` (`bank_model.h:112-122`,
|
||||
has actually accumulated: `rootNote` and `loop` (`bank_model.h`'s `Sample::rootNote` / `Sample::loop`,
|
||||
"additive like `provenance`. Both default cleanly empty"), `captureTimeSigNum` /
|
||||
`captureTimeSigDenom` (`:103-108`, "0/0 means UNSTAMPED"), `channelCount`
|
||||
(`:91-96`, "0 = unknown — a pre-field entry"). Every one of those was additive with
|
||||
`captureTimeSigDenom` (`Sample::captureTimeSigNum` / `Sample::captureTimeSigDenom`, "0/0 means UNSTAMPED"), `channelCount`
|
||||
(`Sample::channelCount`, "0 = unknown — a pre-field entry"). Every one of those was additive with
|
||||
a defined absent-value. Under a single ladder, each would have blocked older readers
|
||||
for no reason.
|
||||
|
||||
@@ -197,17 +198,17 @@ message text and the log.
|
||||
|
||||
**One change class that looks additive and is not: a new enum value.**
|
||||
`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
|
||||
degrading it (`bank_model.cpp`'s `parseSample` — the `sourceMode` and `tier` branches), and every enum a package carries
|
||||
rides inside the nested `BankModel` blob. So growing either vocabulary is
|
||||
**structural** and bumps `minReaderVersion` too. This is wider than packages and
|
||||
predates them: `BankModel::deserialize` is also the live project ext-state parser
|
||||
(`bank_book_json.cpp:99`), so appending a `SourceMode` value already strands an older
|
||||
(`bank_book_json.cpp`'s `parseBank`), so appending a `SourceMode` value already strands an older
|
||||
build opening a newer project's `.rpp`. Phase Ε inherits that property; it did not
|
||||
cause it, and changing it — degrade-to-`Unknown` at those two sites, the way
|
||||
`BakeStatus` already does — is a change to the model layer, not a package concern. It
|
||||
leaves the argument above untouched: the four fields that motivated the two-integer
|
||||
design are *fields*, and `parseSample`'s `skipValue()` fallback
|
||||
(`bank_model.cpp:370-372`), plus the manifest parsers' equivalent at each level, still
|
||||
(`bank_model.cpp`), plus the manifest parsers' equivalent at each level, still
|
||||
carries them forward.
|
||||
|
||||
This is a borrowed pattern, not an invention: Matroska's `EBMLVersion` /
|
||||
@@ -216,7 +217,7 @@ This is a borrowed pattern, not an invention: Matroska's `EBMLVersion` /
|
||||
must understand to read me." It costs one extra integer and one writer discipline —
|
||||
*decide honestly whether your change is additive* — and that discipline is exactly
|
||||
the one `origin_ledger` already enforces on `OriginKind`
|
||||
(`src/core/tracking/CLAUDE.md:82-83`: "PERSISTED INTEGERS — never renumber, only
|
||||
(`src/core/tracking/CLAUDE.md` §"Gotchas": "PERSISTED INTEGERS — never renumber, only
|
||||
append").
|
||||
|
||||
### Both directions, concretely
|
||||
@@ -225,12 +226,12 @@ append").
|
||||
Every reader reads every `minReaderVersion <= kPackageFormatVersion`. Absent manifest
|
||||
keys take their defined defaults, exactly as `Sample`'s additive fields already do,
|
||||
and exactly as `origin_ledger` lifts a v1 path-only blob into v2 records with kind
|
||||
`Unknown` and empty ids (`origin_ledger.cpp:14-16`). Unrecognized manifest keys are
|
||||
`Unknown` and empty ids (`origin_ledger.cpp`'s version-ladder header comment). Unrecognized manifest keys are
|
||||
skipped, which is already how every parser in this repo behaves
|
||||
(`bank_book_json.cpp:182`). Unrecognized enum integers (the manifest's own —
|
||||
(`bank_book_json.cpp`'s `parseBook`). Unrecognized enum integers (the manifest's own —
|
||||
`BankModel`'s nested ones reject) degrade to their defined `Unknown`-equivalent,
|
||||
never to the numeric default and never to a parse failure —
|
||||
`bake_wire`'s rule verbatim (`src/core/wire/CLAUDE.md:83`: "an unrecognized value
|
||||
`bake_wire`'s rule verbatim (`src/core/wire/CLAUDE.md` §"Modules", the `bake_wire` bullet: "an unrecognized value
|
||||
decodes as `Failed` rather than as the numeric default `Ok`").
|
||||
**The user sees:** a normal import summary. Optionally a single console line naming
|
||||
the older writer version. No dialog, no warning, no ceremony — a supported case is
|
||||
@@ -240,7 +241,7 @@ not an incident.
|
||||
written.** `minReaderVersion > kPackageFormatVersion` is a hard stop, before a single
|
||||
byte is written to the bank folder and before the index is touched. This is exactly
|
||||
`LedgerStatus::FutureVersion`'s treatment, and for the same reason stated at
|
||||
`origin_ledger.cpp:18-21`: parsing an unknown shape by old rules "would yield a
|
||||
`origin_ledger.cpp`'s version-ladder header comment: parsing an unknown shape by old rules "would yield a
|
||||
plausible-but-partial" result, and a partial bank is worse than no bank.
|
||||
**The user sees** a message box (`ShowMessageBox`, verified —
|
||||
`vendor/reaper-sdk/sdk/reaper_plugin_functions.h:6546`,
|
||||
@@ -273,13 +274,13 @@ will not find out which twelve of forty samples were dropped until they need one
|
||||
`bank_book_json` precedent applied outward: the book writer "emits the bank
|
||||
envelope … plus a raw `index` member whose value is the `BankModel` blob verbatim,
|
||||
so per-bank sample serialization stays owned by `bank_model` and is not duplicated
|
||||
here" (`bank_book_json.cpp:15-20`). The package does the same, so a future `Sample`
|
||||
here" (`bank_book_json.cpp`'s file-header comment). The package does the same, so a future `Sample`
|
||||
field reaches packages for free and the shape has exactly one owner.
|
||||
- **Per entry, additionally:** the payload's **bare file name** inside the package,
|
||||
its byte length, and a whole-file `hashBytes` digest
|
||||
(`core/capture/wav_codec.h:143` — FNV-1a 64-bit over raw bytes, 16-char lowercase
|
||||
(`core/capture/wav_codec.h`'s `hashBytes` — FNV-1a 64-bit over raw bytes, 16-char lowercase
|
||||
hex). Note carefully: `hashBytes`, **not** `hashWavContent`. The latter deliberately
|
||||
skips non-`fmt `/`data` chunks (`wav_codec.h:145-151`), which is right for dedup
|
||||
skips non-`fmt `/`data` chunks (`wav_codec.h`'s `hashWavContent`), which is right for dedup
|
||||
identity and wrong for "did these bytes survive the trip." Both hashes are already
|
||||
in the codebase; the package needs the raw one for integrity and carries the
|
||||
`Sample`'s existing `contentHash` for dedup, and they are different fields
|
||||
@@ -306,14 +307,14 @@ guarantee for this feature; overselling it would be the error.
|
||||
one genuinely security-shaped surface this feature has.
|
||||
- **The origin ledger.** The ledger is *this project's* record of files *it*
|
||||
created, and it is the authority prune's protected set is computed from
|
||||
(`src/core/tracking/CLAUDE.md:5-13`). Importing foreign ownership records would
|
||||
(`src/core/tracking/CLAUDE.md` §"Scope"). Importing foreign ownership records would
|
||||
assert this project's authority over another project's history. Instead the
|
||||
importer writes **its own** birth records for the files it lands, at the moment it
|
||||
lands them, through the one writer (`ReaSamplerSession::recordCreated`,
|
||||
`src/shell/persist/session.h:95` — it already takes an `OriginKind`). Without that,
|
||||
`src/shell/persist/session.h` — it already takes an `OriginKind`). Without that,
|
||||
every imported file would be "foreign, therefore never reclaimed"
|
||||
(`core/tracking/CLAUDE.md:24-31`) and a user's bank folder would grow forever.
|
||||
- **Live-instance usage records** (`rsusage_*`, `src/ext_keys.h:65`). Per-instance
|
||||
(`core/tracking/CLAUDE.md` §"Invariants", the "No silent gaps" bullet) and a user's bank folder would grow forever.
|
||||
- **Live-instance usage records** (`rsusage_*`, `src/ext_keys.h`'s `kProjExtUsageKeyPrefix`). Per-instance
|
||||
runtime state of a specific project's specific FX instances. Meaningless elsewhere.
|
||||
- **Project state that is not bank state:** which bank was active, the Design View
|
||||
mode model (`view_state`), the tail setting, the project GUID, the bank-generation
|
||||
@@ -342,11 +343,11 @@ Four distinct collisions hide under the word "collision," and they need four
|
||||
different answers.
|
||||
|
||||
1. **Sample id.** Ids are minted as `"cap-" + uniqueTag + "-" + fileName`
|
||||
(`src/shell/capture/capture.cpp:567`) and `"imp-" + …`
|
||||
(`src/shell/actions/ingest.cpp:269`) — unique within a project, **not** globally.
|
||||
(`src/shell/capture/capture.cpp`'s `OfflineRenderBackend::capture`) and `"imp-" + …`
|
||||
(`src/shell/actions/ingest.cpp`'s `importFileIntoActiveBank`) — unique within a project, **not** globally.
|
||||
Re-importing a package into the project it came from would collide.
|
||||
**Answer: remint every sample id on import**, under its own prefix, and remap
|
||||
`Provenance::parentSampleId` (`bank_model.h:45-50`) through the same map — to the
|
||||
`Provenance::parentSampleId` (`bank_model.h`'s `Provenance` struct) through the same map — to the
|
||||
reminted parent if that parent came in the same package, cleared otherwise. A
|
||||
foreign id never enters the destination index. This also makes "import the same
|
||||
package twice" a clean, duplicative, correct operation rather than an undefined
|
||||
@@ -354,18 +355,18 @@ different answers.
|
||||
2. **File name in the destination bank folder.** **Never overwrite.** Overwriting
|
||||
would destroy an existing capture, and only prune touches existing bank bytes.
|
||||
Mint a fresh unique name through the existing `deriveBankPaths` +
|
||||
unique-tag machinery (`core/capture/capture_paths.h:42`), automatically, no
|
||||
unique-tag machinery (`core/capture/capture_paths.h`'s `deriveBankPaths`), automatically, no
|
||||
prompt, and report the count in the summary.
|
||||
3. **Content hash.** `BankModel::add` collapses an equal-`contentHash` add onto the
|
||||
existing entry (`bank_model.h:144-147`, `AddResult::Collapsed`). Desirable — but
|
||||
existing entry (`bank_model.h`'s `AddResult::Collapsed`). Desirable — but
|
||||
if the file was already written to disk before the collapse, it becomes an
|
||||
instant orphan. **Answer: check the destination bank's `findByHash` BEFORE writing
|
||||
the payload**; on a hit, skip the write entirely and report "N already present."
|
||||
This is the one place the import must consult the model before touching the
|
||||
filesystem, and it is a concrete acceptance criterion rather than an optimization.
|
||||
4. **Bank display name.** `bank_book` enforces unique display names, trimmed and
|
||||
case-insensitive ASCII (`src/core/model/CLAUDE.md:22-26`; `createBank`'s own
|
||||
contract at `bank_book.h:92-96` — *"Drums"/"drums"/" Drums " collide, including
|
||||
case-insensitive ASCII (`src/core/model/CLAUDE.md` §"Invariants", the "Bank identity, movement, dedup" bullet; `createBank`'s own
|
||||
contract at `bank_book.h` — *"Drums"/"drums"/" Drums " collide, including
|
||||
against the pool's "Pool"*), so `createBank("Drums")` into a project that already
|
||||
has "Drums" returns `false` with no mutation. **Answer: an automatic numeric
|
||||
suffix, specified below.** No prompt, no overwrite, no refusal.
|
||||
@@ -381,7 +382,7 @@ trim, the seed is the literal `Imported bank`.
|
||||
|
||||
**The probe.** Let `seed` be that string and `fold(x)` be `BankBook`'s own uniqueness
|
||||
key — strip leading/trailing ASCII whitespace, lower-case ASCII letters
|
||||
(`bank_book.h:263-269`). Take the **first** name in this sequence whose fold is not
|
||||
(`bank_book.h`'s `BankBook::nameKey`). Take the **first** name in this sequence whose fold is not
|
||||
already carried by a bank in the destination book:
|
||||
|
||||
seed, seed + " 2", seed + " 3", seed + " 4", …
|
||||
@@ -410,7 +411,7 @@ implementations diverge:**
|
||||
`B + 1` candidates is free by pigeonhole, so no cap is needed and none should be
|
||||
added.
|
||||
4. **The fold has exactly one home.** `import_plan` must **not** re-implement
|
||||
`nameKey` — `bank_book.h:263-269` says in as many words that a drifted second copy
|
||||
`nameKey` — `bank_book.h`'s `BankBook::nameKey` says in as many words that a drifted second copy
|
||||
would let the uniqueness invariant be violated. The probe therefore runs behind
|
||||
`BankBook`'s own folding, which means Ε-W2-T2 adds **one additive public `const`
|
||||
member** to `BankBook` (recommended: `std::string uniqueDisplayName(const
|
||||
@@ -422,7 +423,7 @@ name. Sample ids are reminted by collision rule 1 regardless of whether a name
|
||||
collision occurred, and the two mechanisms are independent. **`Sample` display names
|
||||
are never suffixed** — two banks may legitimately hold a sample called `"Kick"`, and
|
||||
`resample_name`'s own contract already states that sample display names are not unique
|
||||
(`resample_name.h:13-16`). Bank-folder file names are handled by collision rule 2 and
|
||||
(`resample_name.h`'s `nextIterationName`). Bank-folder file names are handled by collision rule 2 and
|
||||
are unaffected by the bank's name. `slot_map` positions ride along unchanged.
|
||||
|
||||
**The pool case is guaranteed, not hypothetical.** Exporting the pool is in scope (the
|
||||
@@ -456,7 +457,7 @@ settled on: report before acting, and never leave a half-state that looks whole.
|
||||
| Destination package file exists | export | Platform save dialog's own overwrite confirm | Native dialog |
|
||||
| Write fails partway | export | Temp file in the destination directory, atomic rename only on complete success | Console error; no `.rsbank` left behind. A truncated package must never exist |
|
||||
| `minReaderVersion` above this build | import | Refuse whole. Nothing written, index untouched | The three-part message box above (package needs / this build reads / what to install) |
|
||||
| Malformed or truncated container | import | Refuse whole. Reported **distinctly from** the version case | "This file is not a readable bank package (corrupt or truncated)." The distinction matters: the two have opposite recoveries — one is "install a newer build," the other is "get an intact copy." `origin_ledger.cpp:178-185` makes exactly this distinction for exactly this reason |
|
||||
| Malformed or truncated container | import | Refuse whole. Reported **distinctly from** the version case | "This file is not a readable bank package (corrupt or truncated)." The distinction matters: the two have opposite recoveries — one is "install a newer build," the other is "get an intact copy." `origin_ledger.cpp`'s `parseStored` makes exactly this distinction for exactly this reason |
|
||||
| Entry name contains a path separator, `..`, or is absolute | import | Refuse whole, before any write | "This package is not well-formed." Hostile input, not user error — no need to elaborate |
|
||||
| Payload hash mismatch on any entry | import | Refuse whole, before landing anything | "This bank package is damaged (entry `<name>` failed its integrity check). Nothing was imported." |
|
||||
| A write fails mid-import (disk full, permission) | import | Roll back: delete the files **this import wrote** and abandon the index mutation | "Import failed and was rolled back. Nothing was added." |
|
||||
@@ -467,7 +468,7 @@ settled on: report before acting, and never leave a half-state that looks whole.
|
||||
|
||||
**On the rollback, and why it is not an invariant breach.** Prune is the single
|
||||
exclusive file-deletion authority, with exactly one carve-out, stated in one place —
|
||||
`src/shell/persist/prune_fs.cpp:5-11`: "a shell removing a file it wrote itself
|
||||
`src/shell/persist/prune_fs.cpp`'s file-header comment: "a shell removing a file it wrote itself
|
||||
moments earlier and that no index ever referenced is self-cleanup, not authority
|
||||
over user data … the discriminator is 'did this call create it, and did anything ever
|
||||
reference it', not where it sits." An import rollback fits that discriminator
|
||||
@@ -477,11 +478,11 @@ restate it, or a reviewer will correctly read the rollback as a breach.
|
||||
|
||||
**On undo.** The index side of an import is one Ctrl-Z, through the same
|
||||
`persistBankOp` undo batching every bank verb already uses
|
||||
(`src/shell/bank_ops/CLAUDE.md:29-31`; `Undo_BeginBlock2` / `Undo_EndBlock2` verified
|
||||
(`src/shell/bank_ops/CLAUDE.md` §"Invariants", the "One bank operation is one Ctrl-Z" bullet; `Undo_BeginBlock2` / `Undo_EndBlock2` verified
|
||||
at `reaper_plugin_functions.h:7758` and `:7806`). **Undo does not un-write the
|
||||
files** — they remain on disk, referenced by no index, until a prune reclaims them.
|
||||
That is the same designed orphaned-until-prune window a non-empty bank delete already
|
||||
produces (`src/core/model/CLAUDE.md:38-40`). Say it out loud in the spec; do not let
|
||||
produces (`src/core/model/CLAUDE.md` §"Invariants", the "Bank identity, movement, dedup" bullet). Say it out loud in the spec; do not let
|
||||
a user infer that Ctrl-Z cleans the folder.
|
||||
|
||||
### Import under a degraded tracking ledger (Ε-F3, RULED: refuse)
|
||||
@@ -494,7 +495,7 @@ reasoning that carried it is recorded below rather than re-argued.
|
||||
|
||||
**The trigger, exactly.** The guard fires when `tracking::ledgerDegraded(status)` holds
|
||||
for the project's loaded ledger status — that is, `LedgerStatus::Unreadable` or
|
||||
`LedgerStatus::FutureVersion` (`src/core/tracking/origin_ledger.h:94`, `:100-101`).
|
||||
`LedgerStatus::FutureVersion` (`src/core/tracking/origin_ledger.h`'s `LedgerStatus` and `ledgerDegraded`).
|
||||
`Fresh` (absent key — a legitimate new project) and `Loaded` both proceed normally.
|
||||
|
||||
**Two things the guard is deliberately NOT keyed on:**
|
||||
@@ -513,7 +514,7 @@ package is read, before any allocation. Making the user find and pick a file we
|
||||
already decided to refuse is the wrong order.
|
||||
|
||||
**What the user sees.** A console block through `ShowConsoleMsg`, mirroring prune's
|
||||
abort (`src/shell/actions/prune_action.cpp:30-69`) in structure and in tone, because a
|
||||
abort (`src/shell/actions/prune_action.cpp`'s `doBankPruneFolder` — the `blockedByTracking` console block) in structure and in tone, because a
|
||||
user who has hit prune's block should recognise this one. Every recovery line names
|
||||
**this build's** ext-state namespace via `version::extStateNamespace()` — the
|
||||
beta/stable trap prune already documents, where a beta user handed the stable spelling
|
||||
@@ -545,7 +546,7 @@ clears the wrong key and is still blocked. Two cases, exactly one of which fires
|
||||
> them could be given a birth record, and every one would be permanently unreclaimable.
|
||||
|
||||
**The recovery path.** The status is written only by `loadFromProject`, so it is sticky
|
||||
for the session (`src/shell/persist/CLAUDE.md:39-46`): repair or clear the key
|
||||
for the session (`src/shell/persist/CLAUDE.md` §"Invariants", the "A ledger this build cannot read is degraded" bullet): repair or clear the key
|
||||
(malformed case only), or install the newer build (future-version case), **reopen the
|
||||
project**, then import again. The package needs no re-export, and nothing about the
|
||||
destination project was changed by the refusal.
|
||||
@@ -557,7 +558,7 @@ likely to want to. Only the landing side refuses.
|
||||
|
||||
**Why the ruling went this way.** The rejected option — allow the import behind an
|
||||
up-front confirm — matched the accepted residual already stated at
|
||||
`core/tracking/CLAUDE.md:24-31`, where a capture made during a degraded session is
|
||||
`core/tracking/CLAUDE.md` §"Invariants" (the "No silent gaps" bullet), where a capture made during a degraded session is
|
||||
recorded in memory but not persisted and degrades to foreign. The argument that carried
|
||||
is **scale**: that residual contemplates *one* untracked capture, and a bulk import can
|
||||
strand two hundred files in a single gesture. Same mechanism, different animal. A
|
||||
@@ -611,7 +612,7 @@ Two new directories, following the split the whole repo turns on.
|
||||
and `classifyPackageVersion(formatVersion, minReader) -> Readable | TooNew |
|
||||
Malformed`. The ladder lives with the framing because the ladder *is* the framing's
|
||||
contract, and it gets a header-comment ladder written the way
|
||||
`origin_ledger.cpp:8-21` writes one.
|
||||
`origin_ledger.cpp`'s version-ladder header comment writes one.
|
||||
- `package_manifest` — the manifest model and its JSON codec, nesting `BankModel`'s
|
||||
own blob verbatim.
|
||||
- `bank_package` — header encode / prefix decode / entry layout, composing the two
|
||||
@@ -643,21 +644,21 @@ responsibility seam, which is what the structural heuristic asks for.
|
||||
`mode=1` an existing one (import's source). `extension_list` takes the
|
||||
`'ReaSampler banks|*.rsbank|All files|*.*'` form. `GetUserFileNameForRead` is
|
||||
explicitly "Superseded, see GetUserFileName" (`:3796`) and is not used. No fallback
|
||||
is needed: `src/app/main.cpp:15` defines `REAPERAPI_IMPLEMENT` without
|
||||
is needed: `src/app/main.cpp`'s `#define REAPERAPI_IMPLEMENT` appears without
|
||||
`REAPERAPI_MINIMAL`, so the resolver walks the full table (`GetUserFileName` at
|
||||
`:9084`), and `main.cpp:292-293` refuses to load the extension if any one function
|
||||
`:9084`), and `REAPER_PLUGIN_ENTRYPOINT`'s `REAPERAPI_LoadAPI` check refuses to load the extension if any one function
|
||||
fails to resolve — so no REAPER build that loads us can lack it.
|
||||
- `export_bank` / `import_bank` — the promptless verbs, mirroring
|
||||
`src/shell/bank_ops/`'s pattern exactly: take a `ReaSamplerSession&`, do the work,
|
||||
return an outcome, **no prompts and no message boxes**. The bindable action and the
|
||||
panel menu item are then thin skins over one verb apiece, so the logic has one home
|
||||
(`src/shell/bank_ops/CLAUDE.md:1-12`).
|
||||
(`src/shell/bank_ops/CLAUDE.md` §"Scope").
|
||||
|
||||
**The dependency-shape criterion, stated because the brief demands it.** The pure
|
||||
planners take **explicit value inputs** — the decoded manifest, the destination
|
||||
`BankBook`, the set of file names present in the bank folder — never a session handle,
|
||||
never a service container, never a "pass me the thing that has everything." The shell
|
||||
*gathers*; the core *decides*. That is the same shape `src/shell/persist/CLAUDE.md:11`
|
||||
*gathers*; the core *decides*. That is the same shape `src/shell/persist/CLAUDE.md` §"Scope"
|
||||
already states ("it gathers rather than decides"). If a circular dependency shows up
|
||||
during the build, the fix is a service split or a thin interface at the seam — never
|
||||
threading an extra parameter through a chain of constructors, and never handing a
|
||||
@@ -690,12 +691,12 @@ constructors.
|
||||
freshly-generated pair.
|
||||
- **Bank generation.** Import mutates bank content that live ReaSampler 9000
|
||||
instances may play, so it must `bumpBankGeneration()`
|
||||
(`src/shell/persist/session.h:121`, whose own comment says call sites "err toward
|
||||
(`src/shell/persist/session.h`'s `ReaSamplerSession::bumpBankGeneration`, whose own comment says call sites "err toward
|
||||
bumping"). Export mutates nothing and must bump nothing, write no ext state, and
|
||||
open no undo point.
|
||||
- **Beta/stable channel isolation.** Packages are channel-**agnostic** and this is
|
||||
deliberate. Channel isolation exists so a beta cannot rewrite a stable project's
|
||||
ext state (`app_version.h:73-76`); a package is a file the user moves by hand, not
|
||||
ext state (`app_version.h`'s `extStateNamespace` — the ISOLATION comment); a package is a file the user moves by hand, not
|
||||
ambient project state, so there is no isolation property to preserve. A beta build
|
||||
and a stable build at the same package format read each other's packages, and that
|
||||
is the useful behaviour. The version ladder — not the channel — is what gates.
|
||||
|
||||
Reference in New Issue
Block a user