# Release Process

MemClaw follows [Semantic Versioning](https://semver.org/) and uses
[release-please](https://github.com/googleapis/release-please) to manage
releases automatically from [Conventional Commits](https://www.conventionalcommits.org/).

The repo ships **two independently-versioned packages**:

| Package | Path     | Tag format       | Baseline |
| ------- | -------- | ---------------- | -------- |
| backend | `.`      | `backend-vX.Y.Z` | 2.5.0    |
| plugin  | `plugin/`| `plugin-vX.Y.Z`  | 2.5.0    |

Backend and plugin release on independent cadences. Plugin fixes no longer
require a backend release; backend changes don't force a plugin version
bump unless the plugin source itself changes.

## How it works

1. Push commits to `main` using Conventional Commit messages (`feat:`,
   `fix:`, `perf:`, `docs:`, `refactor:`, `chore:` etc.).
2. release-please reads `release-please-config.json` +
   `.release-please-manifest.json` and routes each commit to one or both
   packages **by path**: commits touching files under `plugin/**` bump the
   plugin package; everything else bumps the backend package.
3. release-please opens a single combined Release PR
   (`separate-pull-requests: false`) containing per-component sections in
   `CHANGELOG.md` and updated version files for whichever packages had
   changes.
4. Merging the Release PR tags each updated package (e.g. `plugin-v2.5.1`)
   and triggers the corresponding release workflows.

## Version files release-please rewrites

**Backend (`.`):**
- `core-api/pyproject.toml` (`$.project.version`)
- `core-worker/pyproject.toml` (`$.project.version`)
- `core-storage-api/pyproject.toml` (`$.project.version`)

**Plugin (`plugin/`):**
- `plugin/package.json` (`$.version`) — handled by `release-type: node`
- `plugin/openclaw.plugin.json` (`$.version`)

`plugin/src/version.ts` is generated by `scripts/gen-version.sh` from
`plugin/package.json` at build time — it is NOT in `extra-files`.

## Commit scope conventions

To make changelogs scannable, prefix the conventional-commit description
with a scope when it helps:

- `feat(plugin): add deploy command retry`
- `fix(core-api): handle null tenant_id in heartbeat`
- `fix(plugin,core-api): align deploy payload schema`

The scope is cosmetic — package routing is purely path-based. A commit
that touches both `plugin/` and `core-api/` will bump **both** packages
with the same conventional-commit type. Split such commits when the
change is logically separable; bundle when the cross-cut is intentional
(e.g. a new API endpoint plus the plugin client that calls it).

## Compatibility

There is no hard handshake. Backend logs a warning when a heartbeat
reports a plugin version below `MIN_RECOMMENDED_PLUGIN_VERSION`
(`core-api/src/core_api/version_compat.py`). Bump that constant when a
backend change requires a newer plugin.

The plugin's install endpoint (`/api/v1/install-plugin`) stamps the
installed plugin with **plugin's** version (`plugin/package.json`), not
backend's `VERSION`. Both are reported in heartbeat payloads
(`plugin_version`, `openclaw_version`).

## Manual emergency release

If release-please is unavailable, bump the affected package's version
files manually, update `CHANGELOG.md`, and tag with the component-
namespaced format (`backend-vX.Y.Z` or `plugin-vX.Y.Z`).
