Dev:
  ./run.sh setup:install-pre-commit
  ./run.sh uv:venv:sync
  ./run.sh pretty / check / tests
  ./run.sh docs:serve

Maintainer:
  ./run.sh pkg:bump VERSION
  ./run.sh release:check
  ./run.sh release:tag
  ./run.sh gh:ci [ref]
  ./run.sh gh:docs [ref]
  ./run.sh gh:publish:testpypi [ref]
  ./run.sh gh:publish:pypi
  ./run.sh gh:release
  ./run.sh release:testpypi
  ./run.sh release:pypi

Lock: edit pyproject.toml, then ./run.sh uv:lock
MRE: ./run.sh docker:check / docker:test

Commands:
  help                         Usual path + command list
  pkg:version                  Read or update the package version and lockfile
  pkg:bump                     Update version, lockfile, & commit
  pkg:build                    Build sdist and wheel into dist/
 Setup
  setup:install-pre-commit     Install git hooks (once per clone; sync first)
  setup:shellcheck             Install pinned shellcheck if not found
  setup:check-image            Assert /etc/os-release and apt pkgs match pinned versions
  setup:check-installs         Checks all host deps present
 Dep management
  uv:venv:sync                 Sync .venv from uv.lock (no resolve)
  uv:lock                      Write uv.lock. Edit pyproject.toml by hand first (no uv add)
  uv:lock:bootstrap            Re-create uv.lock from scratch, ignore current pins
 Dev: checks
  lint                         ruff check + shellcheck
  typecheck                    basedpyright
  check                        pre-commit + docs build
  tests                        pytest against the installed package
 Dev: formatters
  fmt                          ruff format
  pretty                       fmt + ruff check --fix
 Docs
  docs:build                   Build the docs site into site/
  docs:serve                   Serve docs locally
 CI / maintainer
  ci:tox                       Local tox matrix (no act). skip_missing_interpreters in tox.
  release:check                Build, inspect, and clean-install both local artifacts
  release:tag                  Create annotated vVERSION tag at clean release HEAD
  release:testpypi             Run local TestPyPI release
  release:pypi                 Run local tagged PyPI release
  gh:ci                        Dispatch and watch remote test.yml
  gh:docs                      Dispatch and watch remote docs.yml
  gh:publish:testpypi          Dispatch, test, publish, and verify through gh
  gh:release                   Create gh release for current vVERSION tag
  gh:publish:pypi              Dispatch, test, publish, and verify prod release through gh
 Docker
  docker:build                 Build image
  docker:check                 GHA Checks job (same ./run.sh chain)
  docker:test                  Build prod image + ./run.sh tests
  docker:shell                 Dev shell. Host tree at /app, host uid
  docker:prune                 Dangling image / buildx prune
  docker:matrix                docker:test for each PYTHON_VERSIONS (local MRE; GHA uses baipp matrix)
