obadh_engine 0.9.1

A linguistically accurate Roman to Bengali transliteration engine
Documentation
# Releasing obadh_engine

The release and branching process for `obadh_engine`. Follow it exactly. It exists
because the one time it was not followed, a feature branch cut from an out-of-date
local `main` swept two unrelated commits into a squash merge and left `main`
diverged from `origin`.

## Golden rules

1. **Branch only from an up-to-date `origin/main`.** Run `git fetch origin` and cut
   work branches from `origin/main`, never from a local `main` that may carry
   unpushed commits. A squash merge diffs your branch against `origin/main`, so
   anything on your local `main` that origin lacks gets bundled into the merge.
2. **Never strand a release-bound change.** A change meant for the next release
   lives in exactly one of two places: a commit pushed to `origin/main`, or an
   entry under `## [Unreleased]` in `CHANGELOG.md` backed by a pushed commit. Never
   leave it as a local-only commit or an uncommitted working-tree edit.
3. **Start every release, branch, and merge from a clean working tree.** `git
   status` is empty first.

## Two kinds of change

### A. Ship-now fix or feature (goes through a PR)

1. `git fetch origin && git switch -c <type>/<slug> origin/main`
2. Implement, add tests, add a `## [Unreleased]` entry to `CHANGELOG.md`.
3. Push, open a PR that closes the issue, let CI pass.
4. Squash-merge to `main` (admin override only if branch protection blocks a
   green PR).

### B. Change waiting for the next release

Small docs, terminology, or polish that should ride the next version:

1. `git fetch origin && git switch main && git pull --ff-only`.
2. Commit it straight to `main` with a `## [Unreleased]` CHANGELOG entry, and
   **push immediately**. Do not keep it local-only.

`## [Unreleased]` is the ledger of everything on `main` that has not shipped yet.
When a release is cut, that whole section becomes the new version's entry.

## Cutting a release

SemVer with the `0.x` caveat: a `0.x` minor may carry breaking changes; a patch
(`0.9.0` to `0.9.1`) is a bug fix or docs-only release.

1. `git fetch origin && git switch main && git reset --hard origin/main`, with a
   clean working tree, so you are exactly at origin.
2. Run the checklist from the README's Release Checklist section: `cargo test`,
   `cargo test --features cli`, `cargo test --features cabi`, the wasm32 check,
   `cargo bench --bench hot_path --no-run`, `cargo publish --dry-run`.
3. Bump the version together in `Cargo.toml`, `Cargo.lock` (run `cargo build` to
   refresh it), and `www/package.json`.
4. In `CHANGELOG.md`, rename `## [Unreleased]` to `## [X.Y.Z]` and add a fresh
   empty `## [Unreleased]` above it.
5. Regenerate the browser bundle with `./build.sh dist` (updates `docs/`), so the
   playground and the crate version agree.
6. Commit all of the above as a single commit titled `Release vX.Y.Z`.
7. `cargo publish` (crates.io versions are immutable; there is no undo, only yank).
8. Tag the exact published commit and push both:
   `git tag -a vX.Y.Z -m "..."` then `git push origin main vX.Y.Z`.
9. `gh release create vX.Y.Z --title "..." --notes-file <notes>`, notes drawn from
   the CHANGELOG entry.

Do not publish a behavior change that is data-dependent (frequency, ranking) until
the downstream keyboard (`obadh-ios`) has validated it on the real `bn.fst`. CI
runs without the artifacts and cannot test that class of change.

## The failure this prevents

`main` carried two unpushed local commits. A branch cut from that local `main` was
squash-merged onto `origin/main`, which did not have them, so the squash bundled
the two unrelated commits, and `main` then diverged from `origin`. Fetching first,
branching from `origin/main`, and never stranding release-bound work each avoid it
on their own; do all three.