Executor CLI release runbook
Authoritative doc
RELEASING.md at repo root is the source of truth. This skill encodes the owner's preferences on top of it.
What the executor CLI actually ships
The CLI binary bundles:
apps/cli/** — CLI source + daemon
apps/local/** — the web UI (embedded as a virtual module via apps/cli/src/build.ts:178) + drizzle migrations (build.ts:205)
packages/** — core, kernel, hosts/mcp, runtime-quickjs, and every plugin under packages/plugins/**
Does not ship in the CLI:
apps/cloud/** (Cloudflare Workers deployment)
apps/marketing/**, apps/desktop/**
examples/**, tests/**
Implication for changelogs: when asked "what changed since the last release", scope is git log v<last>..HEAD -- apps/cli apps/local packages, not just apps/cli. Skipping apps/local and packages misses the bulk of product changes (Connections UI, OAuth plugins, SDK scope, OTEL, etc.).
Versioning preferences
- Prior convention in this repo uses
patch bumps for feature-heavy releases (see .changeset/executor-1.4.6-beta.md for precedent). Don't push back on patch unless there are genuine SemVer-breaking API changes to a library consumer surface.
- Breaking CLI UX changes (removed flags, changed argv shape) have historically still been
patch bumps. Follow the owner's call — ask, don't assume minor.
- Normal release/patch PRs must add a
.changeset/*.md file with frontmatter like "executor": patch. Do not directly bump apps/cli/package.json or bun.lock in a feature/fix PR.
- Only the Changesets-generated
Version Packages PR should move apps/cli/package.json. If a normal PR directly changes that version, merging it to main can make .github/workflows/release.yml tag the commit and dispatch publish-executor-package.yml, causing an immediate CLI publish.
@executor-js/* library packages have their own publish path.
Release notes: standard Changesets flow — the changeset body IS the changelog
As of v1.5.0 this repo uses the canonical Changesets pipeline. The old
apps/cli/release-notes/next.md rolling file is gone — do not recreate it.
How it's wired
- Every user-visible PR adds a
.changeset/*.md; its body is the
user-facing changelog entry.
changeset version (run by changesets/action@v1 when building the
Version Packages PR) compiles changeset bodies into each bumped
package's CHANGELOG.md using @changesets/changelog-github
(configured in .changeset/config.json), which prefixes each entry
with the PR link and credits the author automatically.
apps/cli/src/release.ts (changelogSectionForVersion) extracts the
released version's ## <version> section from apps/cli/CHANGELOG.md
and uses it as the GitHub Release body. Missing section → falls back to
--generate-notes.
- Per-package
CHANGELOG.md seed files are still required for every
workspace package (bun run lint:changelog-stubs --fix creates them);
changesets/action@v1 crashes with ENOENT on missing files.
@changesets/changelog-github needs GITHUB_TOKEN during
changeset version. CI provides it; locally:
GITHUB_TOKEN=$(gh auth token) bun run changeset:version.
Writing changeset bodies
- Lead with user-visible behavior, not implementation. One sentence for a
typical fix; a short paragraph for a feature.
- Big releases: a changeset body can be a full markdown section — use
bold sub-headings + bullets, never
#/## headings (they end up
nested inside a changelog list item).
- Breaking changes: include the before/after surface in the body.
- Don't duplicate content across changesets — every changeset in the
release lands in the same version section.
- Attribution is automatic via changelog-github; don't hand-write
Thanks @... lines.
When drafting a release-spanning changeset from git log
- Look at
git diff v<last>..HEAD -- README.md first — best single view of user-facing changes.
- Read commits in bulk (
git log --oneline v<last>..HEAD -- apps/cli apps/local packages), bucket by theme, then write prose.
- Merged PRs without changesets still ship in the release — their content
ships regardless; only the changelog text is driven by changesets. If
something important landed without a changeset, fold its story into a
release-summary changeset.
Beta release flow
git checkout -b rs/beta-v<next>-start
bun run release:beta:start # creates .changeset/pre.json
# write .changeset/executor-<next>-beta.md (frontmatter + user-facing body)
git add ... && git commit # ONLY when owner says commit
git push -u origin rs/beta-v<next>-start
# Open PR -> merge -> release.yml opens "Version Packages (beta)" PR -> merge to publish
- Published under npm dist-tag
beta.
- Users install:
npm i -g executor@beta.
- Exit the train with
bun run release:beta:stop when going back to stable.
Stable release flow
Identical to beta except skip release:beta:start/stop. Changesets produce a normal Version Packages PR; merging publishes under latest.
Owner preferences (hard rules)
- Never commit until the owner explicitly says so. Set everything up in the working tree, run
git status, and stop.
- No AI / Claude / Anthropic / Co-Authored-By trailers in commits, commit messages, PRs, or any generated file. This is in
CLAUDE.md — do not violate.
- Branch naming:
rs/<short-topic> for Rhys's branches. Beta-start branch: rs/beta-v<version>-start.
- Remote:
origin = https://github.com/UsefulSoftwareCo/executor.git. If another remote appears (e.g. a fork remote), ask whether to remove it.
- Dirty working tree: if there are uncommitted changes when starting a release, ask whether to include them, stash them, or commit separately first. Don't sweep them into the release commit silently.
- Don't estimate time — code is cheap to write. Focus on what to do, not how long it takes.
- Fact-check scope claims before publishing. If release notes say "does not affect X", verify by reading the diff.
Common commands
bun run changeset # interactive; or write .changeset/*.md directly
bun run lint:changelog-stubs --fix # seed missing per-package CHANGELOG.md files
bun run release:beta:start # enter prerelease
bun run release:beta:stop # exit prerelease
bun run release:publish:dry-run # build full CLI payload without publishing
bun run release:publish:packages:dry-run # pack @executor-js/* without publishing
bun run release:check # invoked by publish workflow
What the workflow does after merge to main
.github/workflows/release.yml opens/updates a Version Packages PR.
- Merging that PR:
- Publishes every
@executor-js/* library that's not yet on npm (via scripts/publish-packages.ts).
- If
apps/cli/package.json bumped, tags the commit and dispatches publish-executor-package.yml, which runs release:check, does a full dry-run build, publishes the CLI to npm, and creates/updates the GitHub Release with binary assets.
Fallback behavior
If something is unclear (bump level, whether to include in-flight work, whether to push), ask the owner. A release is a high-blast-radius action; one clarifying question is cheaper than a rogue publish.
1---2name: cli-release3description: Runbook for releasing the `executor` CLI package (stable and beta). Covers scope of what ships with the CLI, user-facing changelog conventions, Changesets + Version Packages PR flow, beta train entry/exit, and owner preferences. Use when the user asks to cut a release, prepare release notes, enter/exit a beta train, or write changesets for the CLI.4---56# Executor CLI release runbook78## Authoritative doc910`RELEASING.md` at repo root is the source of truth. This skill encodes the owner's preferences on top of it.1112## What the `executor` CLI actually ships1314The CLI binary bundles:1516- `apps/cli/**` — CLI source + daemon17- `apps/local/**` — the web UI (embedded as a virtual module via `apps/cli/src/build.ts:178`) + drizzle migrations (`build.ts:205`)18- `packages/**` — `core`, `kernel`, `hosts/mcp`, `runtime-quickjs`, and every plugin under `packages/plugins/**`1920Does **not** ship in the CLI:2122- `apps/cloud/**` (Cloudflare Workers deployment)23- `apps/marketing/**`, `apps/desktop/**`24- `examples/**`, `tests/**`2526**Implication for changelogs**: when asked "what changed since the last release", scope is `git log v<last>..HEAD -- apps/cli apps/local packages`, not just `apps/cli`. Skipping `apps/local` and `packages` misses the bulk of product changes (Connections UI, OAuth plugins, SDK scope, OTEL, etc.).2728## Versioning preferences2930- Prior convention in this repo uses **`patch`** bumps for feature-heavy releases (see `.changeset/executor-1.4.6-beta.md` for precedent). Don't push back on patch unless there are genuine SemVer-breaking API changes to a library consumer surface.31- Breaking CLI UX changes (removed flags, changed argv shape) have historically still been `patch` bumps. Follow the owner's call — ask, don't assume `minor`.32- Normal release/patch PRs must add a `.changeset/*.md` file with frontmatter like `"executor": patch`. Do **not** directly bump `apps/cli/package.json` or `bun.lock` in a feature/fix PR.33- Only the Changesets-generated `Version Packages` PR should move `apps/cli/package.json`. If a normal PR directly changes that version, merging it to `main` can make `.github/workflows/release.yml` tag the commit and dispatch `publish-executor-package.yml`, causing an immediate CLI publish.34- `@executor-js/*` library packages have their own publish path.3536## Release notes: standard Changesets flow — the changeset body IS the changelog3738As of v1.5.0 this repo uses the canonical Changesets pipeline. The old39`apps/cli/release-notes/next.md` rolling file is gone — do not recreate it.4041### How it's wired4243- Every user-visible PR adds a `.changeset/*.md`; its **body** is the44 user-facing changelog entry.45- `changeset version` (run by `changesets/action@v1` when building the46 Version Packages PR) compiles changeset bodies into each bumped47 package's `CHANGELOG.md` using `@changesets/changelog-github`48 (configured in `.changeset/config.json`), which prefixes each entry49 with the PR link and credits the author automatically.50- `apps/cli/src/release.ts` (`changelogSectionForVersion`) extracts the51 released version's `## <version>` section from `apps/cli/CHANGELOG.md`52 and uses it as the GitHub Release body. Missing section → falls back to53 `--generate-notes`.54- Per-package `CHANGELOG.md` seed files are still required for every55 workspace package (`bun run lint:changelog-stubs --fix` creates them);56 `changesets/action@v1` crashes with `ENOENT` on missing files.57- `@changesets/changelog-github` needs `GITHUB_TOKEN` during58 `changeset version`. CI provides it; locally:59 `GITHUB_TOKEN=$(gh auth token) bun run changeset:version`.6061### Writing changeset bodies6263- Lead with user-visible behavior, not implementation. One sentence for a64 typical fix; a short paragraph for a feature.65- Big releases: a changeset body can be a full markdown section — use66 **bold sub-headings** + bullets, never `#`/`##` headings (they end up67 nested inside a changelog list item).68- Breaking changes: include the before/after surface in the body.69- Don't duplicate content across changesets — every changeset in the70 release lands in the same version section.71- Attribution is automatic via changelog-github; don't hand-write72 `Thanks @...` lines.7374### When drafting a release-spanning changeset from `git log`7576- Look at `git diff v<last>..HEAD -- README.md` first — best single view of user-facing changes.77- Read commits in bulk (`git log --oneline v<last>..HEAD -- apps/cli apps/local packages`), bucket by theme, then write prose.78- Merged PRs without changesets still ship in the release — their content79 ships regardless; only the changelog text is driven by changesets. If80 something important landed without a changeset, fold its story into a81 release-summary changeset.8283## Beta release flow8485```86git checkout -b rs/beta-v<next>-start87bun run release:beta:start # creates .changeset/pre.json88# write .changeset/executor-<next>-beta.md (frontmatter + user-facing body)89git add ... && git commit # ONLY when owner says commit90git push -u origin rs/beta-v<next>-start91# Open PR -> merge -> release.yml opens "Version Packages (beta)" PR -> merge to publish92```9394- Published under npm dist-tag `beta`.95- Users install: `npm i -g executor@beta`.96- Exit the train with `bun run release:beta:stop` when going back to stable.9798## Stable release flow99100Identical to beta except skip `release:beta:start`/`stop`. Changesets produce a normal `Version Packages` PR; merging publishes under `latest`.101102## Owner preferences (hard rules)103104- **Never commit until the owner explicitly says so.** Set everything up in the working tree, run `git status`, and stop.105- **No AI / Claude / Anthropic / Co-Authored-By trailers** in commits, commit messages, PRs, or any generated file. This is in `CLAUDE.md` — do not violate.106- **Branch naming**: `rs/<short-topic>` for Rhys's branches. Beta-start branch: `rs/beta-v<version>-start`.107- **Remote**: `origin` = `https://github.com/UsefulSoftwareCo/executor.git`. If another remote appears (e.g. a fork remote), ask whether to remove it.108- **Dirty working tree**: if there are uncommitted changes when starting a release, ask whether to include them, stash them, or commit separately first. Don't sweep them into the release commit silently.109- **Don't estimate time** — code is cheap to write. Focus on what to do, not how long it takes.110- **Fact-check scope claims** before publishing. If release notes say "does not affect X", verify by reading the diff.111112## Common commands113114```115bun run changeset # interactive; or write .changeset/*.md directly116bun run lint:changelog-stubs --fix # seed missing per-package CHANGELOG.md files117bun run release:beta:start # enter prerelease118bun run release:beta:stop # exit prerelease119bun run release:publish:dry-run # build full CLI payload without publishing120bun run release:publish:packages:dry-run # pack @executor-js/* without publishing121bun run release:check # invoked by publish workflow122```123124## What the workflow does after merge to `main`1251261. `.github/workflows/release.yml` opens/updates a `Version Packages` PR.1272. Merging that PR:128 - Publishes every `@executor-js/*` library that's not yet on npm (via `scripts/publish-packages.ts`).129 - If `apps/cli/package.json` bumped, tags the commit and dispatches `publish-executor-package.yml`, which runs `release:check`, does a full dry-run build, publishes the CLI to npm, and creates/updates the GitHub Release with binary assets.130131## Fallback behavior132133If something is unclear (bump level, whether to include in-flight work, whether to push), **ask the owner**. A release is a high-blast-radius action; one clarifying question is cheaper than a rogue publish.