The public release path is GitHub Actions. Tag-triggered Linux/macOS
packaging selects BuildBuddy only for the literal actor
lukacf when
MEERKAT_RELEASE_BUILDBUDDY is true or 1; other actors use the hosted
fallback. Eligible manual dispatches select release validation and packaging
under the BuildBuddy rules below. Both release backends still
require exact-main GitHub Actions CI (the Cargo-only pull-request lanes),
and the tag path additionally runs the full BuildBuddy graph as its
validation gate. Windows binaries are cross-compiled
and packaged on hosted Ubuntu, then verified on Windows; credentialed registry
publishing stays GitHub-hosted.
Versioning and Compatibility
Meerkat is pre-1.0 and releases on a fast0.x.y patch train. The policy,
stated plainly so downstream embedders can build against it:
- Patch releases may change public APIs. A
0.8.xto0.8.(x+1)bump can add required fields, change function signatures, or remove items. Cargo’s default caret requirement (meerkat = "0.8") treats the whole0.8family as compatible, which is stronger than this project guarantees. - Embedders must pin exact versions. Libraries and applications that
build against Meerkat crates should declare
=0.8.24-style exact pins and move deliberately, reading the changelog for each hop. - The only supported crate combination is exact version parity. All
workspace crates (
meerkat,meerkat-core,meerkat-runtime, …), the Python/TypeScript/Web SDKs, andContractVersion::CURRENTare lock-stepped to one version per release. Mixing crate versions across releases is unsupported. - Breaking API changes are flagged in the changelog. Public-signature and
behavior-only compatibility breaks land under a
### Breakingheading inCHANGELOG.mdfor the release that ships them. Non-breaking observable behavior changes land under### Changed. A release with neither heading is intended to be a drop-in replacement for the previous patch version.
Downstream compatibility
This repository does not maintain a live cross-repository compatibility matrix. Each downstream project owns its supported Meerkat version in its release manifest and release notes. Downstream Rust projects should declare that version as an exact pin inCargo.toml, not only in Cargo.lock, so the
supported combination is visible without reconstructing historical lockfiles.
Release Checks
Run the release gate before cutting a tag:make release-dry-run for the larger no-upload rehearsal; it includes
the preflight plus Rust, Python, TypeScript, and Web SDK publish dry-runs and
package smoke tests.
Local preflight is not the only publication prerequisite. The independent
Release semver readiness workflow
(.github/workflows/release-semver-readiness.yml) must also supply the normal
tag path’s exact-tree, exact-version evidence from a successful main push.
Its artifact has 30-day configured retention. Wait for both CI and readiness
on the prepared release tree before tagging to avoid racing the release
gate’s artifact lookup; the release workflow below
describes the enforcement point and narrower manual recovery paths.
Declared Breaks (semver-breaks)
0.x patch releases may break public API; every break must be declared. The
semver-breaks gate runs cargo-semver-checks against the published crates.io
baseline and fails the release unless all three hold:
- Measured. Every crate the release publishes was either rebuilt and
compared, or proven identical to the baseline release. Only crates whose
source directory or declared dependency specs differ from the baseline tag
are rebuilt (
scripts/semver_changed_crates.pyclassifies them; generated Bazel files and test, bench, example and doc trees do not count). Neither side is built inside cargo-semver-checks: the baseline is the rustdoc JSON the release workflow attached to the baseline’s GitHub release (semver-rustdoc-<version>.tar.zst), the candidate is generated once for every publishable library crate by the samescripts/semver-rustdoc-json.shon the pinned rustc, and the tool compares the two files per crate (--baseline-rustdoc,--current-rustdoc). One cargo invocation documents all library crates on each side so workspace feature unification is identical across releases. Only if the asset is missing or was built by a different rustc is the baseline tag checked out and the generator run there. A release measurement is therefore a minutes-scale job. Identical crates are recorded as reached by equivalence because cargo-semver-checks reports on a crate’s own items, which cannot differ when nothing it is built from differs. The tool’s exit code must agree with its report; a run that died halfway, or failed for a reason other than a detected break, is a failure rather than a pass. - Named. Every finding the tool reports is named in the pending release
section’s
### Breakingbody, at the granularity of the finding. A type gaining a field and the same type losing a derive are two findings; naming one does not declare the other. - Stamped. The pending section is stamped
## [VERSION] - DATEagainst the version being released. Notes still sitting under## [Unreleased]after the version bump has landed would publish as release notes titled “Unreleased”, and that is a failure.
### Breaking.
Three published crates are outside what cargo-semver-checks looks at, and the
gate prints them on every run rather than hiding the gap: meerkat-machine-derive
and meerkat-machine-dsl are proc-macro crates (a --workspace run emits no
output for them at all), and rkat has no lib target. Breaks in those three are
declared by hand or not at all.
Changelog stamping is part of the release commit
Write pending notes under## [Unreleased] and leave them there for
make release-preflight. When ./scripts/repo-cargo release creates the version-bump commit,
its scripts/release-hook.sh pre-release hook:
- stamps the pending section as
## [<version>] - <YYYY-MM-DD>, creates a fresh## [Unreleased]stub, and advances the comparison links; - bumps SDK, documentation, and contract versions;
- regenerates schemas, SDK wrappers, and Bazel metadata; and
- verifies and stages those generated release files in the same commit.
release_semver_gate requires separate semver-readiness evidence that
declared breaks and stamped notes were verified for that exact tree and
version. Asset-only and Web-SDK-only recovery paths skip this Rust semver gate.
Where it runs:
The judgement lives in
scripts/check_semver_breaks.py, which is a pure
function of (report, changelog, version, tool exit code) and has no environment
override that relaxes it. scripts/check-semver-breaks.sh only produces the
report and hands it over.
Binary Artifacts
Release assets are built for these binaries:rkatrkat-rpcrkat-restrkat-mcp
x86_64-unknown-linux-gnuaarch64-unknown-linux-gnuaarch64-apple-darwinx86_64-apple-darwinx86_64-pc-windows-msvc
checksums.sha256index.json
Homebrew Tap
The featured CLI install path is the Homebrew tap:rkat plus the companion binaries:
rkat-rpcrkat-restrkat-mcp
SDK Bootstrap
Python and TypeScript SDK users should not need a local Rust toolchain.
The SDKs are clients. They start or connect to the JSON-RPC surface rather than
embedding a separate runtime implementation.
Release Workflow
- Prepare the release commit through cargo-release and its release hook, so version projections and stamped notes stay in one hook-owned tree. Do not manually edit versions or create tags to bypass that workflow.
- Prefer waiting for both exact-main CI and Release semver readiness on that prepared tree before cargo-release publishes the tag. Ordinary CI and semver readiness produce separate artifacts; one cannot substitute for the other.
- The tag path verifies the exact-tree CI attestation instead of recomputing
the broad CI graph. After
require_ci_green,release_semver_gatelooks upmeerkat-semver-attestation-main-<tree_sha>and requires unexpired evidence for that tree and version from a successful readinessmainpush. - In parallel, verify registry credentials, consume declared-break evidence, build platform binaries, and build the Web SDK package artifact.
- Publish the macOS/Linux assets early when available, then complete the
GitHub release with every platform archive,
checksums.sha256, andindex.json. - Update the Homebrew tap from the published assets.
- Publish Rust crates plus the Python and TypeScript SDKs on GitHub-hosted runners. Their publish jobs build and smoke-test the package artifacts before upload.
- Publish the prebuilt Web SDK artifact on a GitHub-hosted runner.
./scripts/repo-cargo release patch --execute flow combines
bump, commit, tag, and push; that command summary alone does not establish
readiness. A combined push can succeed if readiness completes before the
semver artifact lookup, but it races that lookup. The gate checks evidence
availability then, not whether readiness completed before tag creation.
Readiness evidence is configured to expire after 30 days. PR and manually
dispatched readiness artifacts use the meerkat-semver-attestation-preview-
prefix and do not qualify for the normal tag path.
The current schema-4 CI attestation binds the repository, commit SHA, Git tree
SHA, CI workflow run and attempt, branch, and event to backend
github-hosted-cargo, its aggregate validation_result, the selected lane
plan, and every lane’s component_results. The release gate downloads it
from the successful exact-main workflow run and verifies every required field
before tag-triggered publication starts. The consumer also accepts the
supported legacy schema-1 Cargo, schema-2 BuildBuddy, and schema-3
BuildBuddy-plus-dense-Mob attestations; the current producer does not emit
those formats.
Manual recovery dispatches still require successful exact-main CI for the
selected release commit. When release validation applies, the dispatch runs
the selected validation lane directly rather than requiring a retained CI
attestation artifact. Narrow assets,
packages, web-sdk, and web-sdk-publish modes repair one publication
surface without rebuilding or republishing unrelated surfaces.
This CI-artifact exception does not bypass the semver gate. An ordinary
explicit-release_tag package recovery still consumes main-push readiness
evidence. When this gate applies, a manual dispatch with neither release_tag
nor semver_evidence_job_id runs make semver-breaks directly. The separate
completed-measurement recovery path requires release_tag,
semver_evidence_run_id, and semver_evidence_job_id; its verifier checks
the exact completed measurement and amended declarations from the workflow
ref, while published source remains the release tag. It accepts only a
completed measurement on that exact immutable tag/SHA that failed solely for
missing breaking-change declarations, not an incomplete or failed setup.
A generic manual readiness preview is not that recovery path.
To inspect the exact GitHub CLI dispatch without starting a workflow, use:
REGISTRY_DRY_RUN=true changes registry publication inside a dispatched
workflow; it is not a workflow dry run and does not suppress binary, GitHub
Release, or Homebrew publication.
BuildBuddy
Automatic tag packaging selects BuildBuddy only whengithub.actor is
literally lukacf and the repository variable
MEERKAT_RELEASE_BUILDBUDDY is true or 1. For eligible manual dispatches,
explicit release_backend=buildbuddy selects BuildBuddy validation and
Linux/macOS packaging only for that same literal actor; it does not also
require the automatic-selection variable. This is a named-actor condition,
not a general owner/admin permission check.
The following explicit BuildBuddy dispatch is for lukacf:
release_backend=buildbuddy.
Windows packaging and every credentialed registry publish still run on
GitHub-hosted runners, and the exact-main GitHub Actions CI requirement remains
in force. This release
backend selection is separate from per-push CI, which is Cargo-only on
GitHub-hosted runners; the full BuildBuddy graph runs nightly and on the tag.
For local Make commands, MEERKAT_BUILDBUDDY=1 selects the optional BuildBuddy
developer backend:
make buildbuddy-doctor when the local BuildBuddy setup looks suspicious.
It checks the API key, pinned bb CLI, generated Bazel files, selector
behavior, and lane isolation without printing secrets.
Credentials
Registry credentials are independent:
Keep tokens in CI secrets or a local secret store. Do not commit registry
tokens, private BuildBuddy endpoints, or enterprise infrastructure names.
Hard Rules
- Release only from tagged versions.
- Never publish mismatched Rust, Python, TypeScript, or contract versions.
- Never publish SDKs from a commit with stale generated schema artifacts.
- Keep public binary names stable:
rkat,rkat-rpc,rkat-rest,rkat-mcp. - Publish checksums and an index for release binary consumers.
