diff --git a/PLAN.md b/PLAN.md index 17003d1..a1e6e16 100644 --- a/PLAN.md +++ b/PLAN.md @@ -445,29 +445,33 @@ CONTEXT.md §Prune (Settled forks). > Namespaced **`V` (Versioning)** alongside `M`/`D`/`B`/`R` because it is a distinct > concern (build identity + channel isolation) that touches CMake, `main.cpp`'s > forever-stable command-id contract, and the `"reasampler"` ext-state — not a -> capture step. Product framing + the full option analysis + open forks: -> `docs/product/versioning-and-release.md`. **Forks V1–V4 UNSETTLED — awaiting -> Daniel.** No points are scoped-to-build until Daniel picks; the note lays out the -> recommendation (semver via `project(VERSION)` + ext-state version stamp; beta = -> branch-discipline/one-at-a-time built on a compile-time channel flag, with true -> simultaneous coexistence designed-as-a-seam but deferred). Deploy/CD wiring hands -> off to dev-ops. +> capture step. Product framing + the full option analysis: `docs/product/versioning- +> and-release.md`. **Forks V1–V4 SETTLED (Daniel, 2026-07-26)** — semver via +> `project(VERSION)` + ext-state version stamp prioritized first-wave; plain `-beta` +> suffix; console line + panel readout (about-box deferred); and **beta ships as a +> separate, fully isolated coexisting binary (beta-in-isolation)** — a reversal of the +> note's original one-at-a-time recommendation. Deploy/CD wiring (now two named +> artifacts per platform) hands off to dev-ops. Build-scoped points to be drawn up. -## Open forks — for Daniel (see `docs/product/versioning-and-release.md`) -- **V1 — version scheme.** Recommend semver, single source of truth in CMake - `project(reaper_reasampler VERSION x.y.z)`, threaded into the binary; **plus a - writing-version stamp in the `"reasampler"` ext-state blob** for forward - migration/detection. Approve scheme + whether the ext-state stamp lands first wave. -- **V2 — beta suffix source.** Recommend `git describe` decorating beta builds while - `project(VERSION)` owns the release triple. Approve or simplify to plain `-beta`. -- **V3 — user-visible home.** Recommend startup console line (`"ReaSampler x.y.z - loaded"`) + a bank-panel version/channel readout; about-box deferred. Panel - placement is the residual polish call. -- **V4 — beta channel shape (the big one).** Recommend **branch-discipline / - one-installed-at-a-time (Option B) built on a compile-time channel flag (Option - C)**, with **true simultaneous coexistence (Option A) seam-designed but deferred**. - Sharp edges that must be decided explicitly: the forever-stable command-id prefix - (`CEREBELLUM_REASAMPLER_`) must not collide between coexisting builds, and the - `"reasampler"` ext-state namespace must not let a beta corrupt a stable project's - state — so the **ext-state-across-a-channel-swap policy** (isolate vs. - stay-compatible/stamp-and-detect vs. accept-discipline) is genuinely Daniel's call. +## Settled decisions (Daniel, 2026-07-26 — see `docs/product/versioning-and-release.md`) +- **V1 — version scheme: APPROVED as recommended.** Semver, single source of truth in + CMake `project(reaper_reasampler VERSION x.y.z)`, threaded into the binary. **The + `"reasampler"` ext-state writing-version stamp is prioritized to the first wave, not + deferred** — every project saved without the stamp is harder to migrate later, so + the migration seam lands early. +- **V2 — beta suffix: plain `-beta`.** `project(VERSION)` owns the release triple; beta + carries a `-beta` suffix. `git describe` decoration considered and rejected for + legibility. +- **V3 — user-visible home: recommendation accepted.** Startup console line + (`"ReaSampler x.y.z loaded"`) + a bank-panel version/channel readout; about-box + deferred. Panel placement is the residual polish call. +- **V4 — beta channel shape: BETA-IN-ISOLATION (full coexistence).** *Reverses the + original recommendation.* Beta ships as a **separate binary** (`reaper_reasampler_ + beta`) with an **isolated ext-state namespace** (distinct from stable's + `"reasampler"` — a beta cannot corrupt a stable project's saved state) and an + **isolated forever-stable command-id prefix** (beta/stable keybindings don't + collide), so both install and run side-by-side. Built through a compile-time channel + flag (`-DREASAMPLER_CHANNEL=beta`) as the mechanism. **Two permanent commitments + locked in:** a second forever-stable command-id prefix and a second ext-state + namespace. **Dev-ops:** the build now produces two named artifacts (stable + beta) + per platform. diff --git a/docs/product/versioning-and-release.md b/docs/product/versioning-and-release.md index 4c06123..847662e 100644 --- a/docs/product/versioning-and-release.md +++ b/docs/product/versioning-and-release.md @@ -12,8 +12,21 @@ This doc holds the *why*, the forks, and a recommendation. When Daniel picks, th tickable points land in `PLAN.md` and the deploy/build wiring hands off to dev-ops. This is a framing note; it changes no source or CMake. -Status: framed by product-designer (2026-07-26). **Forks V1–V4 open — awaiting -Daniel.** Nothing here is settled. +Status: framed by product-designer (2026-07-26); **all four forks settled by Daniel +(2026-07-26).** V1 approved as recommended (ext-state version stamp prioritized to +the first wave); V2 plain `-beta` suffix (`git describe` decoration rejected); V3 +recommendation accepted (console line + panel readout, about-box deferred); **V4 +reversed the recommendation** — Daniel chose **beta-in-isolation / full coexistence** +(separate binary, isolated ext-state namespace, isolated command-id prefix) rather +than the branch-discipline/one-at-a-time path the note originally recommended. The +"Recommendation" and "Open decisions" sections below have been superseded to reflect +the settled state; each records what was chosen and why. Deploy/build wiring hands off +to dev-ops. + +> **Note on V4 phrasing (provisional).** The-boss's read of "beta-in-isolation" as +> the separate-binary + isolated-namespace + isolated-command-id-prefix coexistence +> path is provisional pending a final confirm from Daniel. Written that way here; a +> minor correction is a cheap edit. --- @@ -66,7 +79,15 @@ allowed to touch. - The natural user-visible readout already exists: the docked LICE bank panel, and the console (`ShowConsoleMsg`). A version has cheap homes; none is wired. -## The number itself (V1) — recommendation: **semver, sourced from CMake** +## The number itself (V1) — SETTLED (2026-07-26): **semver, sourced from CMake; ext-state stamp lands ASAP / first wave** + +> **Decision (V1).** Approved as recommended. Semver, single source of truth in CMake +> `project(reaper_reasampler VERSION x.y.z)`, threaded into the binary. **The +> `"reasampler"` ext-state writing-version stamp is prioritized to the first wave, not +> deferred** — Daniel emphasized ASAP because *every project saved without the stamp +> is harder to migrate later*, so the migration seam must exist before more real +> projects accrue un-stamped state. Treat the stamp as an early, high-priority +> deliverable that ships with (or ahead of) the first versioned build. Recommend **semantic versioning** (`MAJOR.MINOR.PATCH`) with the number's single source of truth in `CMakeLists.txt` via `project(reaper_reasampler VERSION x.y.z)`, @@ -99,9 +120,10 @@ about-string in lockstep from one edit. build literally cannot disagree with the tag; encodes commits-since-tag + dirty state, which is *excellent for a beta* ("1.4.0-beta.3+7.gab12cd"). Con: needs git present at build and a tag discipline; a source tarball without `.git` builds - "unknown." **Recommendation: use it for the beta channel's suffix, not as the base - number.** Best of both: `project(VERSION)` owns the release triple; `git describe` - decorates beta builds. (See V2.) + "unknown." **This was originally recommended for the beta suffix — Daniel rejected + it** (V2 settled below: a plain `-beta` suffix is more legible than a decorated + `git describe` string). `project(VERSION)` still owns the release triple; the beta + simply carries `-beta`. (See V2.) - **A hand-maintained `version.h`.** Rejected — a second source of truth that drifts from the tag. The whole point of one source is that release can't ship a binary that lies about its number. @@ -110,7 +132,13 @@ about-string in lockstep from one edit. about migration compatibility, which is the load-bearing use here. Semver earns its place; CalVer doesn't. -## Where the user sees it (V3) — recommendation: **panel readout + startup log, defer an about box** +## Where the user sees it (V3) — SETTLED (2026-07-26): **panel readout + startup log; about box deferred** + +> **Decision (V3).** Recommendation accepted (Daniel deferred to product-designer +> judgment — "whatever"). Startup console line (`"ReaSampler x.y.z loaded"`) + a +> bank-panel version/channel readout ship; the about-box stays deferred. Panel +> placement (header corner / footer / folded into existing chrome) remains a small +> residual polish call, same class as the settled active-bank-indicator placement. Three candidate homes, cheap to expensive: @@ -232,62 +260,95 @@ wants. It's the knob; A and B are policies for the knob. The flag is worth having either way because it makes "what makes a beta a beta" a **single, auditable definition** instead of scattered `#ifdef`s. -## Recommendation: **B now, built on C, with A as the deliberate upgrade later** +## Decision (V4) — SETTLED (2026-07-26): **Option A — beta-in-isolation / full coexistence** -Start with **Option B (branch discipline, one at a time) implemented through a -compile-time channel flag (Option C)**: +> **This reverses the note's original recommendation.** The note originally +> recommended Option B (branch discipline, one installed at a time) built on the +> Option C channel flag, with Option A (true simultaneous coexistence) *seam-designed +> but deferred*. **Daniel chose the coexistence path instead.** The superseded +> recommendation prose is retained below the decision (struck through in intent, kept +> for the reasoning trail) so a future reader sees what was weighed; the decision here +> governs. -- Ship the release from `main` as `reaper_reasampler` with a clean semver - (`x.y.z`). -- Build beta from `dev` through `-DREASAMPLER_CHANNEL=beta`, which stamps a - `-beta`/`-dev.` **version suffix** and a **panel channel indicator** - (so a beta is unmistakable at a glance — the version readout from Part 1 doubles as - the channel badge). This is where `git describe` earns its keep (V2): the beta - suffix carries commits-since-tag so two betas are distinguishable. -- **Do not fork the command-id prefix or the ext-state namespace yet** — because - under B only one binary loads, so there is nothing to collide with, and forking - them prematurely commits you to a second forever-stable id family and splits your - own test data for no gain. +**Settled shape: beta ships as a separate, fully isolated binary that coexists with +stable in one REAPER.** Concretely, all three global identities fork (this *is* +Option A, built through the Option C compile-time channel flag as the mechanism): -Then treat **Option A (true coexistence) as a later, deliberate upgrade** if and -only if "swap-and-restart" proves too slow — i.e. you find you genuinely need stable -and beta live in one REAPER to compare them. The upgrade is *clean* precisely -because you built B on the channel flag: flipping A "on" means extending the same -flag to also fork the output name, the command-id prefix (`_BETA_`), and the -ext-state namespace (`"reasampler_beta"`). The seam is designed now; the feature is -deferred (the same *defer-the-feature-design-the-seam* discipline the project -already applies to the owned-file manifest and Phase D adaptability). +- **Separate binary** — `reaper_reasampler_beta` alongside `reaper_reasampler`, so + REAPER's startup dlopen loads both and the user runs stable and beta side-by-side. +- **Isolated ext-state namespace** — the beta writes under its own namespace (e.g. + `"reasampler_beta"`), distinct from stable's `"reasampler"`, so **a beta can never + read or rewrite a stable project's saved bank/view/tail state.** The price is + accepted: a beta does not see a stable project's bank (it looks empty until + re-captured under the beta namespace). That is the correct price — isolation over + convenience. +- **Isolated forever-stable command-id prefix** — the beta mints its own prefix (e.g. + `CEREBELLUM_REASAMPLER_BETA_`), so beta and stable actions never collide in + REAPER's one Actions list and their keybindings stay independent. -Why not A first: A's power is real but it forces you to mint a **second permanent -command-id namespace** and a **second ext-state namespace** on day one — both -irreversible-ish commitments (the ids are forever-stable; the namespace split means -beta and stable never share a project's bank). That is a large, permanent price for -a convenience (simultaneous compare) you may not need. B pays nothing permanent and -keeps A one flag away. +The compile-time channel flag (`-DREASAMPLER_CHANNEL=beta`, Option C) remains the +implementation mechanism: one flip coordinates output name + command-id prefix + +ext-state namespace + `-beta` version suffix + panel channel badge as a single +auditable definition, so the three-way divergence can't drift by hand. -Why not B without the flag: hand-managing the suffix and channel badge across two -branches drifts. The flag makes "beta vs. release" one definition and is the exact -thing you'd extend for A — so building B *through* C costs little now and saves the -A upgrade later. +### The two permanent commitments this locks in (recorded honestly) -### The sharp edge that survives every option: ext-state across a channel swap +Choosing coexistence over the deferred-seam path accepts two commitments that are +**permanent once the first beta ships** — exactly the prices the original +recommendation flagged as reasons to defer: -Whichever way this goes, call out loudly (and this is a genuine **Daniel decision**, -V4): **can a beta build write experimental `"reasampler"` ext-state schema that a -stable build might later read?** +1. **A second forever-stable command-id prefix.** The beta's ids are their own + forever-family (`STABLE_FOREVER_STRING` contract applies to them too the moment a + beta ships). Beta keybindings do not carry to stable and vice versa. Irreversible. +2. **A second ext-state namespace.** `"reasampler_beta"` is permanent — and its + isolation means beta and stable never share a project's bank. This is the safety + property, but it is also a fork of your own test data that you own from day one. -- Under **A** with a forked namespace: no — isolated, safe, at the cost of beta not - seeing stable's bank. -- Under **B**: yes, sequentially — the project file is shared across a swap. Needs a - policy: beta stays schema-compatible, **or** the version-stamp-in-blob (V1) lets - stable detect-and-refuse, **or** "don't open beta-touched real projects in stable" - is an accepted discipline. +Both are deliberate and accepted; recorded here so no future reader treats them as +oversights. -The cheapest robust mitigation across both is **V1's version stamp in the ext-state -blob** — it turns a silent mis-read into a detectable "this project was written by a -newer/other build." Recommend adopting it as part of the first release wave -regardless of the A/B choice, because it's the seam that makes every later channel -and migration decision safe. +### Deploy / CD implication (dev-ops hand-off) + +The build now **produces two named artifacts per platform** — `reaper_reasampler` +(stable) and `reaper_reasampler_beta` (beta) — selected by `REASAMPLER_CHANNEL`. +That is a dev-ops handoff: the pipeline builds, names, and publishes both channels +(three platform artifacts each). See Part 3. + +### The ext-state safety property, now settled by isolation + +The sharp edge the original note flagged as the genuine V4 sub-decision — *can a beta +write experimental ext-state that a stable build later reads?* — is **resolved by the +isolated namespace: no.** Beta writes only under `"reasampler_beta"`; stable reads +only `"reasampler"`. There is no shared-project corruption path across the channels, +neither simultaneous nor sequential. V1's writing-version stamp still lands first-wave +(it guards *within-channel* forward migration — stable-1.4 reading stable-1.6 state — +which isolation does not address), but it is no longer load-bearing for cross-channel +safety. + +--- + +### Superseded recommendation (retained for the reasoning trail) + +> The following was the note's original V4 recommendation. **It is superseded by the +> settled decision above** (Daniel chose coexistence). Kept because the trade-off +> reasoning — the permanent-price argument in particular — is what the decision was +> weighed against. + +~~Start with **Option B (branch discipline, one at a time) implemented through a +compile-time channel flag (Option C)**~~: ship stable as `reaper_reasampler` from +`main`; build beta from `dev` via the channel flag with only a `-beta` suffix + panel +badge; **do not fork the command-id prefix or ext-state namespace**, since under B +only one binary loads and forking prematurely commits a second forever-stable id +family for no gain. Then treat **Option A (true coexistence) as a later, deliberate +upgrade** if swap-and-restart proved too slow — the upgrade being clean precisely +because B was built on the channel flag (flip A on = extend the flag to fork name + +`_BETA_` prefix + `"reasampler_beta"` namespace). The stated reason to defer A: it +forces minting a **second permanent command-id namespace** and a **second ext-state +namespace** on day one — a large, permanent price for a convenience (simultaneous +compare) that might not be needed. **Daniel weighed that price and chose to pay it: +the value of running stable and beta side-by-side, with hard isolation guaranteeing a +beta can never corrupt stable state, outweighed avoiding the two permanent +commitments.** --- @@ -314,21 +375,24 @@ implies these touchpoints: --- -# Open decisions for Daniel +# Settled decisions (all four — Daniel, 2026-07-26) -- **V1 — version scheme.** Recommend semver via `project(VERSION)` as single source - of truth, **plus a writing-version stamp in the `"reasampler"` ext-state blob** for - forward migration/detection. Approve scheme + whether the ext-state stamp lands in - the first release wave. -- **V2 — beta suffix source.** Recommend `git describe` decorating beta builds - (`x.y.z-beta.N+g`) while `project(VERSION)` owns the release triple. Approve - or simplify to a plain `-beta` suffix. -- **V3 — user-visible home + placement.** Recommend startup console line + a - bank-panel version/channel readout now; about-box deferred. Panel placement - (header corner / footer / folded into existing chrome) is the residual call. -- **V4 — beta channel shape (the big one).** Recommend **Option B (branch - discipline, one at a time) built on an Option C compile-time channel flag**, with - **Option A (true simultaneous coexistence) designed-as-a-seam but deferred**. The - sub-decision that must be made explicitly either way: **the ext-state-across-a- - channel-swap policy** — isolate (A), stay schema-compatible / stamp-and-detect (B), - or accept the discipline. This is genuinely Daniel's call, not one to default. +- **V1 — version scheme: APPROVED as recommended.** Semver, single source of truth in + CMake `project(reaper_reasampler VERSION x.y.z)`, threaded into the binary. **The + `"reasampler"` ext-state writing-version stamp is prioritized to the first wave** — + every project saved without it is harder to migrate later, so the migration seam + lands early, not deferred. +- **V2 — beta suffix: plain `-beta`.** `project(VERSION)` owns the release triple; + beta builds carry a `-beta` suffix. The `git describe` decoration was considered and + **rejected** for legibility ("-beta is better than a random string"). +- **V3 — user-visible home: recommendation accepted.** Startup console line + (`"ReaSampler x.y.z loaded"`) + a bank-panel version/channel readout; about-box + deferred. Panel placement remains a small residual polish call. +- **V4 — beta channel shape: BETA-IN-ISOLATION (full coexistence).** *Reverses the + original recommendation.* Beta ships as a **separate binary** + (`reaper_reasampler_beta`) with an **isolated ext-state namespace** and an + **isolated forever-stable command-id prefix**, so stable and beta install and run + side-by-side with no shared-state corruption path. Locks in two permanent + commitments — a second forever-stable command-id prefix and a second ext-state + namespace — both accepted. Dev-ops implication: the build now produces two named + artifacts (stable + beta) per platform.