Skip to Content

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 changeset

The interactive picker walks you through three questions:

  1. Which packages did this change touch? Tick every one whose public surface (or wire protocol — anything reachable from src/index.ts) changed.
  2. What semver tier for each package? Patch / minor / major.
  3. 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 --empty

This 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:check

A 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’s version, rewrites every affected CHANGELOG.md, and consumes the .changeset/*.md source files.
  • Commits the result directly to main as chore(release): version packages [skip ci] under the github-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 runs pnpm publish -r --access public filtered to bumped packages with private: 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 @klera scope.

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 schemaVersion bump.
  • 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 exit workflow when an alpha / beta / rc window opens. v0.6 ships from main directly; pre-release channels are documented but unused until a v1 stabilisation window starts.

Distribution

PackageNotes
kleraUnscoped alias of @klera/cli
@klera/cliIndependent
@klera/engineLockstep group
@klera/protocolLockstep group
@klera/runtimeLockstep group
@klera/bridgeLockstep group, transitive
@klera/plannerIndependent
@klera/mcpIndependent
@klera/native-driver-iosNative module; ships ios/ source
@klera/native-driver-androidNative module; ships android/ source
@klera/metro-pluginIndependent
@klera/devtoolsIndependent

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 publish

The 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.md
  • packages/engine/CHANGELOG.md
  • packages/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.

Last updated on