--- name: cli-release description: 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. --- # 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..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 `## ` 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..HEAD -- README.md` first — best single view of user-facing changes. - Read commits in bulk (`git log --oneline v..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-start bun run release:beta:start # creates .changeset/pre.json # write .changeset/executor--beta.md (frontmatter + user-facing body) git add ... && git commit # ONLY when owner says commit git push -u origin rs/beta-v-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/` for Rhys's branches. Beta-start branch: `rs/beta-v-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` 1. `.github/workflows/release.yml` opens/updates a `Version Packages` PR. 2. 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.