# ADR-0054: Release versioning policy + version surfaces (`--version` / `version`)

## Status

Accepted — **implemented 2026-06-16** (plan:
`docs/plans/20260616-public-availability.md`, step 1). First step on the
road to public availability. Adds a `--version` / `-V` CLI flag and an
in-app `version` command, both reporting `CARGO_PKG_VERSION`, plus a
release-CI guard that the `v*` tag equals that version. No prior issue or
`requirements.md` item existed for this — it was an untracked gap.

## Context

Before this, `Cargo.toml` carried `version = "0.1.0"`, the binary exposed
**no** way to report its version, and `release.yaml` named assets from the
**git tag** (`rdbms-playground-<tag>-<target>`) while building from
`Cargo.toml`. Tag and crate version were **decoupled**: tagging `v0.2.0`
would publish an asset named `…-v0.2.0-…` containing a binary that (had it
been able to say) reported `0.1.0`. On the way to public availability —
where users download a versioned artifact and file bug reports against "the
version I'm running" — that drift is a correctness problem.

## Decision

1. **`Cargo.toml` `version` is the single source of truth.** This is the
   idiomatic Rust position and avoids making `Cargo.toml` lie. The version
   is read at compile time via `env!("CARGO_PKG_VERSION")`; no build-time
   injection of the tag into the crate.

2. **Two user-facing surfaces, one source:**
   - **`--version` / `-V`** — CLI flag (hand-rolled parser, mirrors
     `--help`): prints and exits before any other work (`main.rs`).
   - **`version`** — an in-app app command (REGISTRY node `app::VERSION`,
     `AppCommand::Version`), emitting the same line into the output panel.
   Both go through `cli::version_text()` → the catalog key
   `cli.version_line` (`"rdbms-playground {version}"`), so there is exactly
   one rendered string and one version source.

3. **Release-CI discipline.** `release.yaml`'s pre-build `test` job gains a
   **version guard**: it reads the `[package]` version directly from
   `Cargo.toml` (`grep -m1 '^version = '` — toolchain-free; an earlier
   `cargo metadata | node` form broke on the flake devShell's stdout banner)
   and **fails the release** unless the pushed tag equals `v<that version>`.
   So `--version`, the release name, and the downloaded asset are always in
   lockstep — enforced by the machine, not by memory.

4. **The release ritual:** bump `Cargo.toml` → commit → tag `v<that
   number>` → push the tag. The guard rejects any deviation.

### Rejected / deferred
- **Inject the tag into the build** (tag as source of truth): fiddly with
  cargo and makes `Cargo.toml` a placeholder/lie. Rejected.
- **Git-hash + build-date enrichment** (a `build.rs` so dev builds read
  `0.2.0 (a1b2c3d)`): useful for bug reports, but not needed for the
  tag↔release↔`--version` consistency this ADR is about. Deferred; can be
  added behind the same `version_text()` seam without changing the policy.
- **UI placement beyond the `version` command** (status-bar string, etc.):
  the command + `help` listing is enough for now (user decision).

## Consequences

- A release can no longer ship a binary whose self-reported version
  disagrees with its tag/filename.
- Cutting a release now *requires* a `Cargo.toml` bump commit — a small,
  deliberate step (and a natural place to update a changelog later).
- New keys: `cli.version_line` (+ `help.app.version`, `parse.usage.version`,
  `hint.cmd.version.what`/`.example`); a new REGISTRY command means the
  comprehensiveness coverage test now also requires `hint.cmd.version`,
  which is supplied. Tested: CLI flag parse (`--version`/`-V`/default-off),
  `version_text()` carries `CARGO_PKG_VERSION`, the in-app command parses to
  `AppCommand::Version` and emits the version.
- This is step 1 of `docs/plans/20260616-public-availability.md`; the
  installer (`install.sh`) and package-manager work (D3) build on top.
