docs: record Phase E waves 1 and 2 as landed
Backfills W1, which was skipped when it merged, and adds W2's two verbs. Notes the picker deviation: GetUserFileName both directions, not the spec'd Win32/SWELL split.
This commit is contained in:
@@ -942,3 +942,102 @@ own output, the three folder cases, collapsed-mono placement and summing, both m
|
|||||||
transitions waited out past a panel timer tick, undo, name/colour clone, and
|
transitions waited out past a panel timer tick, undo, name/colour clone, and
|
||||||
`GetProjectPathEx` against a non-default recording path — none of it is unit-testable
|
`GetProjectPathEx` against a non-default recording path — none of it is unit-testable
|
||||||
and none has been run.
|
and none has been run.
|
||||||
|
|
||||||
|
### Ε-W1 — The contract, the filesystem, and the ledger's new kind
|
||||||
|
|
||||||
|
Phase Ε's first wave: the `.rsbank` package contract, the filesystem/dialog seam
|
||||||
|
behind it, and a new tracking-ledger origin kind for package-sourced files — three
|
||||||
|
tracks, disjoint by directory, dispatched in parallel.
|
||||||
|
|
||||||
|
**Ε-W1-T1 — `package-format`.** The pure `src/core/package/` codec for the
|
||||||
|
hand-rolled `RSBK` container (Ε-F1, ruled — no ZIP, no compressor, no link edge to
|
||||||
|
`vendor/WDL/WDL/zlib/`): a fixed little-endian header carrying two version
|
||||||
|
integers — `formatVersion` (what the writer emitted) and `minReaderVersion` (the
|
||||||
|
oldest reader that can read it safely) — a length-prefixed JSON manifest, and
|
||||||
|
payloads concatenated in manifest order. `classifyPackageVersion` answers
|
||||||
|
`Readable`/`TooNew`/`Malformed`; a `TooNew` header refuses whole, producing no
|
||||||
|
manifest, so the refusal can still name the writer's semver rather than
|
||||||
|
half-succeeding. Landed as three modules: `package_format` (the contract, the
|
||||||
|
version ladder, and three name-validation rules — `isValidEntryName`,
|
||||||
|
`sameEntryName`'s ASCII-case fold, `isValidNestedSamplePath`), `package_manifest`
|
||||||
|
(the manifest model + JSON codec, carrying the bank's `slot_map` and a whole-file
|
||||||
|
`hashBytes` digest per entry — deliberately not `hashWavContent`, which skips
|
||||||
|
chunks and so cannot answer "did these bytes survive"), and `bank_package`
|
||||||
|
(framing/layout arithmetic: `encodePackage`/`decodePackage`/`requiredPrefixSize`,
|
||||||
|
never holding or hashing a payload itself). Hostile input is refused, never UB,
|
||||||
|
at every byte offset.
|
||||||
|
|
||||||
|
**Ε-W1-T2 — `package-fs-shell`.** `src/shell/package/`: streaming, atomic package
|
||||||
|
filesystem I/O (`package_io`'s `PackageFileWriter`/`PackageFileReader`, at most one
|
||||||
|
entry's payload materialized at a time, backed by a `.rsbanktmp` sibling that
|
||||||
|
reaches the destination only through a `commit()` rename — process-crash atomic,
|
||||||
|
not power-loss atomic, deliberately, since an `fsync` over a whole sample bank is a
|
||||||
|
real stall) and the rollback journal (`package_rollback`'s `LandedFileJournal`,
|
||||||
|
citing the `prune_fs.cpp` carve-out rather than restating it, disarmed only after
|
||||||
|
the caller's own write has returned success). **Deviation from spec:** the plan
|
||||||
|
called for asymmetric pickers — REAPER's `GetUserFileNameForRead` for import, Win32
|
||||||
|
`GetSaveFileNameW`/SWELL `BrowseForSaveFile` for export, reasoning that the REAPER
|
||||||
|
API has no save picker. The landed `package_pickers` instead rides `GetUserFileName`
|
||||||
|
for both directions (mode 1 import, mode 0 export) — no platform split, since
|
||||||
|
`main.cpp` already aborts extension load if any needed API pointer fails to
|
||||||
|
resolve. Both pickers are `[verify — DAW]`, never exercised in a live REAPER
|
||||||
|
session.
|
||||||
|
|
||||||
|
**Ε-W1-T3 — `import-origin-kind`.** `OriginKind::PackageImport` appended to the
|
||||||
|
tracking ledger as value 5 — package-sourced vs `Ingest`'s user-picked. Append-only,
|
||||||
|
per `core/tracking/CLAUDE.md`'s persisted-integer rule; an unrecognized kind
|
||||||
|
degrades to `Unknown` rather than failing the parse, and `kLedgerVersion` stays at
|
||||||
|
2 — a vocabulary addition, not a document-version bump. No decision surface
|
||||||
|
changed: `pruneProtection`'s output is unaffected for every existing kind.
|
||||||
|
|
||||||
|
### Ε-W2 — The two verbs
|
||||||
|
|
||||||
|
Two tracks landed on Ε-W1's contract: a bank leaves the project as one `.rsbank`
|
||||||
|
file, or the export refuses and says why; a `.rsbank` becomes a **new** bank,
|
||||||
|
completely or not at all. Both tracks were code-reviewed and remediated before
|
||||||
|
merging; the merged tree (Ε-W1 + Ε-W2) builds clean and passes 100/100 tests.
|
||||||
|
|
||||||
|
**Ε-W2-T1 — `bank-export`.** New `core/package/export_plan` (pure: which entries,
|
||||||
|
what names, what is missing, and therefore whether the export may proceed — verdict
|
||||||
|
`Ready`/`Incomplete`/`Refused`) and `shell/package/export_bank` (the promptless
|
||||||
|
verb, in three composable public steps — `surveyBankExport`, `digestSources`,
|
||||||
|
`writePackageFile` — arriving with a **const** `ReaSamplerSession&`, so "writes no
|
||||||
|
ext state, opens no undo point, never bumps the generation" holds by the type
|
||||||
|
rather than by memory), plus `shell/actions/package_export_action`, one
|
||||||
|
`main.cpp` action-table row, and one panel bank-menu row. Nothing is re-encoded;
|
||||||
|
payloads are copied and hashed. The exported unit is one bank — the pool included,
|
||||||
|
since the pool is structurally one `BankIndex` among many — and whole-book export
|
||||||
|
stays out of scope for the phase. Both open questions were answered at review:
|
||||||
|
affordance ships as **both** the bindable action and the panel row, and the
|
||||||
|
default file name derives from the bank's display name through
|
||||||
|
`capture_paths::sanitizeStem`.
|
||||||
|
|
||||||
|
**Ε-W2-T2 — `bank-import`.** New `core/package/import_plan` (pure: the id remap
|
||||||
|
table, the parent remap, the per-entry land/skip-already-present/rename
|
||||||
|
disposition, and the destination bank's display name after `BankBook`'s own
|
||||||
|
uniqueness fold — reached through a new additive `BankBook::uniqueDisplayName`
|
||||||
|
member, the only `core/model/` edit in the phase), and on the shell side a
|
||||||
|
REAPER-free `import_landing` (decode, verify every payload's `hashBytes` digest
|
||||||
|
against the manifest BEFORE the bank folder is created, then land through the
|
||||||
|
rollback journal) plus a REAPER-facing `import_bank` (the only piece touching the
|
||||||
|
extension's project state — the undo-batched persist and the generation bump),
|
||||||
|
`shell/actions/package_import_action`, the panel's `.rsbank` drop route, one
|
||||||
|
`main.cpp` row, one panel menu row, and a new `src/core/util/ascii_ws.h`. The
|
||||||
|
tracking-ledger guard runs before the file picker opens (Ε-F3, ruled: refuse
|
||||||
|
outright on `Unreadable`/`FutureVersion`, no confirm-and-proceed); the version
|
||||||
|
gate runs before any byte is written; all four collision classes — sample id, file
|
||||||
|
name, content hash, bank display name — are answered explicitly, with the
|
||||||
|
display-name collision auto-suffixed and never prompted (Ε-F2, ruled: always a new
|
||||||
|
bank, never a merge); birth records land via
|
||||||
|
`recordCreated(sample, OriginKind::PackageImport)` in the same straight-line block
|
||||||
|
as the index add; the index mutation is one Ctrl-Z, and the landed files'
|
||||||
|
survival as orphans until the next prune is stated in the user-facing summary, not
|
||||||
|
left implicit. **Beyond spec:** `import_plan`'s `spelledLikeABankFile` mints a
|
||||||
|
fresh name even absent a collision, whenever the package's own entry name isn't
|
||||||
|
spelled the way `deriveBankPaths` would spell it — counted separately from a
|
||||||
|
genuine folder-name collision (`sanitizeRenameCount` vs `collisionRenameCount`) so
|
||||||
|
a hostile or foreign-spelled entry name (e.g. an unexpected extension) always
|
||||||
|
lands sanitized rather than verbatim.
|
||||||
|
|
||||||
|
**Ε-W3 (`package-compat-fixtures`) has not landed** and is the only remaining
|
||||||
|
wave of Phase Ε.
|
||||||
|
|||||||
Reference in New Issue
Block a user