diff --git a/.agents/skills/release-maintainer/SKILL.md b/.agents/skills/release-maintainer/SKILL.md new file mode 100644 index 00000000..9d6f247f --- /dev/null +++ b/.agents/skills/release-maintainer/SKILL.md @@ -0,0 +1,210 @@ +--- +name: release-maintainer +description: Internal maintainer SOP for version bumps, release PRs, tagging, publishing, and post-publish verification in this repository. +advertise: false +--- + +# agent-tty release maintainer SOP + +This is a project-local maintainer skill for `agent-tty` releases. The canonical human policy lives in [`docs/RELEASE-PROCESS.md`](../../../docs/RELEASE-PROCESS.md); use this skill as the execution checklist when an agent is asked to cut or publish a release from this repository. + +## When to use this skill + +Use this skill when you are asked to: + +- bump the package version for a stable release or prerelease, +- create or manage the release PR, +- wait for CI and merge the release PR, +- tag and publish a release, +- or verify the published npm package and GitHub Release assets. + +## Core guardrails + +- Do **not** release from an unmerged branch. The release tag must reference a commit already merged into `main`. +- Keep the version bump minimal unless the user explicitly asks for additional release-related changes. +- Follow the repo's PR body/footer requirements from `AGENTS.md` when creating the release PR. +- Treat `gh auth status` as advisory only. When access looks suspicious, verify with a real API call instead. +- Run post-publish verification under Node 24. If the ambient shell is older, point `NODE_BIN` at an explicit Node 24 binary. +- For `doctor --json`, the health signal is `.result.ok`; the outer `.ok` field only says the CLI command envelope succeeded. + +## Preflight + +1. Re-read `RELEASE.md`, `ROADMAP.md`, and `docs/RELEASE-PROCESS.md`. +2. Confirm the workspace is clean and based on up-to-date `main`. +3. Verify GitHub CLI access with a real API call: + +```bash +gh api graphql -f query='query { viewer { login } }' +``` + +4. Prefer `mise run ci` when `mise` is available; otherwise use `npm run verify`. + +## Prepare the version bump + +Start from fresh `main`: + +```bash +git checkout main +git pull origin main +``` + +Create a release branch and bump without tagging. + +Stable patch release example: + +```bash +git switch -c release/0.1.1 +npm version patch --no-git-tag-version +``` + +First beta on the next patch line: + +```bash +git switch -c release/0.1.1-beta.0 +npm version prepatch --preid beta --no-git-tag-version +``` + +Next beta on the same line: + +```bash +npm version prerelease --preid beta --no-git-tag-version +``` + +Then validate and commit the pure version bump: + +```bash +npm run verify +npm run version:json +git add package.json package-lock.json +git commit -m "chore(release): " +``` + +## Create the release PR + +```bash +git push -u origin +gh pr create --base main --head --title "chore(release): " +``` + +Before creating the PR, check whether one already exists for the release branch: + +```bash +gh pr list --head --state all +``` + +## Wait for CI and merge the PR + +Interactive waiting is fine with: + +```bash +gh pr checks --watch +``` + +For automation or agent-driven waiting, prefer structured workflow inspection: + +```bash +gh pr checks +gh run list --branch --event pull_request --limit 5 +gh run view --json status,conclusion,jobs +``` + +Merge the PR only after the required checks succeed. + +Normal merge path: + +```bash +gh pr merge --squash --delete-branch +``` + +If branch policy still blocks the merge after checks pass and an authorized releaser is allowed to override it: + +```bash +gh pr merge --squash --admin --delete-branch +``` + +If remote refs look stale after the merge, refresh them before checking `origin/main` or deleted release branches: + +```bash +git fetch origin --prune +``` + +## Tag and publish the release + +Default to the documented tag flow unless the user explicitly asks for the GitHub CLI shortcut. + +Documented flow: + +```bash +git checkout main +git pull origin main +git tag -a vX.Y.Z -m "vX.Y.Z" +git push origin vX.Y.Z +``` + +If you are explicitly asked to create the tag and GitHub Release with `gh`, use the merged `main` commit SHA: + +```bash +MERGED_SHA= +gh release create vX.Y.Z \ + --target "$MERGED_SHA" \ + --title vX.Y.Z \ + --notes-file +``` + +For prereleases, also add: + +```bash +--prerelease --latest=false +``` + +The release workflow should publish the verified tarball to both GitHub Releases and npm. + +## Verify the published npm package + +Run the installed CLI under Node 24: + +```bash +PACKAGE_NAME='agent-tty' +PACKAGE_VERSION='' +NODE_BIN=${NODE_BIN:-node} +INSTALL_PREFIX=$(mktemp -d) +AGENT_TTY_HOME=$(mktemp -d) + +npm view "$PACKAGE_NAME" dist-tags --json +npm install -g --prefix "$INSTALL_PREFIX" "$PACKAGE_NAME@$PACKAGE_VERSION" +"$NODE_BIN" "$INSTALL_PREFIX/bin/agent-tty" version --json | jq -r '.result.cliVersion' +"$NODE_BIN" "$INSTALL_PREFIX/bin/agent-tty" --home "$AGENT_TTY_HOME" doctor --json | jq '.result.ok' +``` + +For prereleases, also confirm the expected dist-tag points at the exact version. + +## Verify the published GitHub Release assets + +```bash +VERSION= +RELEASE_TAG="v${VERSION}" +RELEASE_TGZ="agent-tty-${VERSION}.tgz" +NODE_BIN=${NODE_BIN:-node} + +DOWNLOAD_DIR=$(mktemp -d) +INSTALL_PREFIX=$(mktemp -d) +AGENT_TTY_HOME=$(mktemp -d) + +gh release download "$RELEASE_TAG" --repo coder/agent-tty --dir "$DOWNLOAD_DIR" --pattern "$RELEASE_TGZ" +gh release download "$RELEASE_TAG" --repo coder/agent-tty --dir "$DOWNLOAD_DIR" --pattern "${RELEASE_TGZ}.sha256" +( + cd "$DOWNLOAD_DIR" + sha256sum -c "${RELEASE_TGZ}.sha256" +) + +npm install -g --prefix "$INSTALL_PREFIX" "$DOWNLOAD_DIR/$RELEASE_TGZ" +"$NODE_BIN" "$INSTALL_PREFIX/bin/agent-tty" version --json | jq -r '.result.cliVersion' +"$NODE_BIN" "$INSTALL_PREFIX/bin/agent-tty" --home "$AGENT_TTY_HOME" doctor --json | jq '.result.ok' +``` + +## Failure recovery checklist + +- If a release tag was pushed before the PR merged, cancel the workflow run, delete the remote tag, delete the local tag, and redo the release through the PR-first flow. +- If the GitHub Release exists but assets or npm publish are missing, inspect the `Release` workflow run before attempting any manual repair. +- If `gh auth status` looks broken but the real API calls succeed, keep using real `gh` command results as the source of truth. +- If installation succeeds with an `EBADENGINE` warning because `npm` itself is running under an older Node, rerun the installed CLI under Node 24 before deciding whether the release is healthy. diff --git a/docs/README.md b/docs/README.md index e171a5f8..70c6740c 100644 --- a/docs/README.md +++ b/docs/README.md @@ -8,4 +8,5 @@ Use this directory when you need project workflow guidance rather than product-f - [`../design/README.md`](../design/README.md) — architecture and design references. - [`CONTRIBUTING.md`](./CONTRIBUTING.md) — setup, validation, and day-to-day contribution flow. - [`RELEASE-PROCESS.md`](./RELEASE-PROCESS.md) — maintainer release checklist and proof expectations. +- [`../.agents/skills/release-maintainer/SKILL.md`](../.agents/skills/release-maintainer/SKILL.md) — internal agent SOP for version bumps, release PRs, tagging, and publish verification. - [`../dogfood/CATALOG.md`](../dogfood/CATALOG.md) — curated proof bundles for review. diff --git a/docs/RELEASE-PROCESS.md b/docs/RELEASE-PROCESS.md index 0caf9dc7..ef7e32ed 100644 --- a/docs/RELEASE-PROCESS.md +++ b/docs/RELEASE-PROCESS.md @@ -26,6 +26,18 @@ 5. Confirm the published package metadata still points at `agent-tty` and the public GitHub repository. 6. Remember that `main` is protected: release changes must land through a pull request, and the release tag must be created only after that PR is merged. +## GitHub CLI readiness + +This flow assumes `gh` can create PRs, inspect checks, merge, and create releases. `gh auth status` is useful for a quick summary, but in some environments it can report a stale or misleading state even when real GitHub API calls still work. + +Before treating release automation as blocked, verify with a real API call such as: + +```bash +gh api graphql -f query='query { viewer { login } }' +``` + +If that succeeds, prefer the result of the real `gh` operation over the status summary. + ## Validation bar Preferred local validation uses `mise`: @@ -138,6 +150,26 @@ gh pr create --base main --head --title "chore(release): --watch` is fine. For automation or agent-driven release work, prefer inspecting the workflow run directly so you can wait on structured `status` and `conclusion` fields instead of parsing live terminal refresh output. + +Typical sequence: + +```bash +gh pr checks +gh run list --branch --event pull_request --limit 5 +gh run view --json status,conclusion,jobs +``` + +If the PR still cannot be merged after every required check passes, inspect the base-branch policy first. When normal merge and `--auto` are unavailable but an authorized releaser is allowed to override the policy, use: + +```bash +gh pr merge --squash --admin --delete-branch +``` + +Use `--admin` sparingly and only after confirming the required release checks succeeded. + ## Tag the merged `main` commit After the release PR has merged: @@ -166,6 +198,22 @@ or: - `package.json`: `0.1.1-beta.0` - tag: `v0.1.1-beta.0` +### GitHub CLI alternative: create the tag and release in one step + +If you already know the merged `main` commit SHA and want GitHub to create the tag and release together, this also works: + +```bash +MERGED_SHA= +gh release create v0.1.1-beta.0 \ + --target "$MERGED_SHA" \ + --prerelease \ + --latest=false \ + --title v0.1.1-beta.0 \ + --notes-file +``` + +`gh release create` creates the tag on the specified merged commit and still triggers the `Release` workflow via the pushed `v*` tag. Use the prerelease flags only for prerelease versions; omit them for stable releases. + ## Publish the GitHub Release and npm package The hand-curated workflow lives at [`.github/workflows/release.yml`](../.github/workflows/release.yml). @@ -187,20 +235,23 @@ Prerelease versions publish with the prerelease identifier as the dist-tag, so ` ## Verify the published npm package -After the workflow succeeds, verify the exact npm package version before announcing the release: +After the workflow succeeds, verify the exact npm package version before announcing the release. Run these checks under Node 24; if your interactive shell is older, point `NODE_BIN` at an explicit Node 24 binary first. ```bash PACKAGE_NAME='agent-tty' PACKAGE_VERSION='' +NODE_BIN=${NODE_BIN:-node} INSTALL_PREFIX=$(mktemp -d) AGENT_TTY_HOME=$(mktemp -d) npm view "$PACKAGE_NAME" dist-tags --json npm install -g --prefix "$INSTALL_PREFIX" "$PACKAGE_NAME@$PACKAGE_VERSION" -"$INSTALL_PREFIX"/bin/agent-tty version --json -"$INSTALL_PREFIX"/bin/agent-tty --home "$AGENT_TTY_HOME" doctor --json +"$NODE_BIN" "$INSTALL_PREFIX/bin/agent-tty" version --json | jq -r '.result.cliVersion' +"$NODE_BIN" "$INSTALL_PREFIX/bin/agent-tty" --home "$AGENT_TTY_HOME" doctor --json | jq '.result.ok' ``` +`doctor --json` uses the outer `ok` field for command-envelope success; the release-health signal is `.result.ok`. + If the release is a prerelease, also confirm the intended dist-tag points at the exact published version: ```bash @@ -227,12 +278,13 @@ printf 'expected dist-tag %s for %s\n' "$DIST_TAG" "$PACKAGE_VERSION" ## Verify the published GitHub Release assets -Also verify the hosted tarball fallback before announcing the release: +Also verify the hosted tarball fallback before announcing the release. Run these checks under Node 24 for the same reason as the npm verification above. ```bash VERSION= RELEASE_TAG="v${VERSION}" RELEASE_TGZ="agent-tty-${VERSION}.tgz" +NODE_BIN=${NODE_BIN:-node} DOWNLOAD_DIR=$(mktemp -d) INSTALL_PREFIX=$(mktemp -d) @@ -240,11 +292,14 @@ AGENT_TTY_HOME=$(mktemp -d) gh release download "$RELEASE_TAG" --repo coder/agent-tty --dir "$DOWNLOAD_DIR" --pattern "$RELEASE_TGZ" gh release download "$RELEASE_TAG" --repo coder/agent-tty --dir "$DOWNLOAD_DIR" --pattern "${RELEASE_TGZ}.sha256" -sha256sum -c "$DOWNLOAD_DIR/${RELEASE_TGZ}.sha256" +( + cd "$DOWNLOAD_DIR" + sha256sum -c "${RELEASE_TGZ}.sha256" +) npm install -g --prefix "$INSTALL_PREFIX" "$DOWNLOAD_DIR/$RELEASE_TGZ" -"$INSTALL_PREFIX"/bin/agent-tty version --json -"$INSTALL_PREFIX"/bin/agent-tty --home "$AGENT_TTY_HOME" doctor --json +"$NODE_BIN" "$INSTALL_PREFIX/bin/agent-tty" version --json | jq -r '.result.cliVersion' +"$NODE_BIN" "$INSTALL_PREFIX/bin/agent-tty" --home "$AGENT_TTY_HOME" doctor --json | jq '.result.ok' ``` For private releases, authenticated download is the expected verification route.