Releasing
klera ships through a changesets-driven pipeline. Contributors author a
changeset alongside their PR; merging to main triggers an automated
workflow that bumps versions, rewrites changelogs, commits the result
back to main, and publishes to npm. Nobody types npm publish by
hand.
Contributor flow
Whenever you change a public package under packages/, add a
changeset:
pnpm changesetThe interactive picker walks you through three questions:
- Which packages did this change touch? Tick every one whose
public surface (or wire protocol — anything reachable from
src/index.ts) changed. - What semver tier for each package? Patch / minor / major.
- One-line summary. This becomes the changelog entry adopters read.
The picker writes a .changeset/<random-name>.md file. Commit it
alongside your code. Reviewers see your semver intent in the diff.
When the change is no-op for adopters
A typo fix in a test file, a docs-only change, an internal refactor invisible to consumers — none of these need a release. Record the intent explicitly with an empty changeset:
pnpm changeset --emptyThis produces a .changeset/*.md file with no package bumps,
satisfying the CI gate while declaring “this PR ships nothing new to
adopters.”
CI gate
changeset-check.yml runs on every PR and fails when files under
packages/*/src/ (excluding *.test.ts / *.integration.test.ts)
change without an accompanying .changeset/*.md. The error message
tells you exactly which files triggered the gate. Add a changeset
(or an empty one) and push.
Lockstep group
@klera/protocol, @klera/runtime, @klera/bridge, and
@klera/engine ship in lockstep — bumping any one bumps all four.
The four share the wire protocol; pinning them together is a
correctness constraint, not a stylistic choice. An adopter who pins
@klera/engine@1.4.2 is guaranteed to get the matching
@klera/protocol@1.4.2.
The other packages version independently:
@klera/cli@klera/planner@klera/mcp@klera/native-driver-ios@klera/native-driver-android@klera/metro-plugin@klera/devtools
Adopters can pin a CLI minor without forcing an engine bump.
The lockstep is enforced by changesets’ fixed config block; you don’t
have to remember it when authoring a changeset, but the auto-commit
will reflect it (a single changeset that bumps @klera/protocol
produces matching bumps on the other three).
Release flow
A push to main triggers .github/workflows/release.yml. The workflow
keeps history linear: it commits the version bump directly back to
main rather than opening a Version Packages PR.
Verification gate
pnpm install
pnpm typecheck
pnpm -r run build
pnpm test
pnpm lint
pnpm format:checkA broken main never publishes.
Detect pending changesets
The job greps for .changeset/*.md files (excluding README.md). If
none, the workflow exits — nothing to release.
Bump, commit, publish
When changesets are present, the workflow:
- Runs
pnpm changeset version, which bumps each affected package’sversion, rewrites every affectedCHANGELOG.md, and consumes the.changeset/*.mdsource files. - Commits the result directly to
mainaschore(release): version packages [skip ci]under thegithub-actions[bot]identity. The[skip ci]marker prevents the resulting push from re-triggering the workflow. - Runs
pnpm changeset publish, which tags each bumped package as@klera/cli@0.6.0/@klera/engine@0.6.0/ etc. and runspnpm publish -r --access publicfiltered to bumped packages withprivate: false. - Pushes tags via
git push --tags.
The two-step layout — bump conditional on pending changesets, publish
always-runs — is deliberate. If a partial publish failure leaves some
packages bumped but not yet on npm, the next push to main retries
the publish step without needing a fresh changeset.
Reviewing the version + changelog diff happens on the auto-commit on
main rather than in a “Version Packages” PR. Faster release cadence
- linear history, at the cost of explicit pre-publish human review.
Secrets and tokens
The release job needs exactly one secret:
NPM_TOKEN— publish access for the@klerascope.
GITHUB_TOKEN is provided by the runner with contents: write so the
auto-commit can push back to main.
Granular npm tokens that list specific packages cannot auto-create
new ones. If you add a new public package to the workspace, the
NPM_TOKEN must have scope-level write (or be regenerated to list
the new package) before the next release will publish it. The first
publish of a brand-new package will fail loudly otherwise.
Versioning policy
- Major bumps require a design note. The lockstep group bumps
major when the wire shape breaks — a removed field, a renamed step
kind, a non-backwards-compatible report
schemaVersionbump. - Minor bumps add capability — new step kinds, new CLI commands, new optional protocol fields, new shipped features.
- Patch bumps fix bugs without behaviour shifts.
- Pre-release channels use the changesets
pre enter/pre exitworkflow when an alpha / beta / rc window opens. v0.6 ships frommaindirectly; pre-release channels are documented but unused until a v1 stabilisation window starts.
Distribution
| Package | Notes |
|---|---|
klera | Unscoped alias of @klera/cli |
@klera/cli | Independent |
@klera/engine | Lockstep group |
@klera/protocol | Lockstep group |
@klera/runtime | Lockstep group |
@klera/bridge | Lockstep group, transitive |
@klera/planner | Independent |
@klera/mcp | Independent |
@klera/native-driver-ios | Native module; ships ios/ source |
@klera/native-driver-android | Native module; ships android/ source |
@klera/metro-plugin | Independent |
@klera/devtools | Independent |
Manually triggering a release
You shouldn’t need to. If you do (recovering from a CI outage, republishing after a withdrawn package):
# From a clean checkout of main
pnpm install
pnpm build
pnpm test
pnpm changeset publishThe publish step refuses to act when there are no pending changesets
to apply. To ship a hotfix by hand, author the changeset, run
pnpm changeset version to bump versions and rewrite changelogs
locally, commit, then publish.
Docs version label
The version label rendered on docs.klera.dev tracks the latest
published @klera/cli version on npm. The site reads it at build
time, so a fresh release publishes the new version label on the next
docs deploy. There’s no separate docs version to bump — the docs site
is versioned in lockstep with the CLI.
Where the changelog lives
pnpm changeset version rewrites a per-package CHANGELOG.md. Each
package’s history lives next to its source:
packages/cli/CHANGELOG.mdpackages/engine/CHANGELOG.mdpackages/protocol/CHANGELOG.md- …and so on for every package in the workspace.
The changelogs follow the Keep a Changelog format with semver section headers; each entry links back to the PR that introduced it. Reviewers reading a release commit can see exactly which prose summary lands in which package’s changelog.
For the canonical list of every changeset and its semver tier, browse
the per-package CHANGELOG.md files on the main branch — they are
the human-readable version of the npm registry’s tag history.