diff --git a/.agents/skills/openclaw-release-maintainer/SKILL.md b/.agents/skills/openclaw-release-maintainer/SKILL.md index f2f1422a797..1265fe5fa0c 100644 --- a/.agents/skills/openclaw-release-maintainer/SKILL.md +++ b/.agents/skills/openclaw-release-maintainer/SKILL.md @@ -14,6 +14,22 @@ Use this skill for release and publish-time workflow. Keep ordinary development - This skill should be sufficient to drive the normal release flow end-to-end. - Use the private maintainer release docs for credentials, recovery steps, and mac signing/notary specifics, and use `docs/reference/RELEASING.md` for public policy. - Core `openclaw` publish is manual `workflow_dispatch`; creating or pushing a tag does not publish by itself. +- Normal release work happens on a branch cut from `main`, not directly on + `main`. Use `release/YYYY.M.D` for the branch name. +- If the operator asks for a release without saying stable/full, default to + beta only. Continue from beta to stable only when the operator explicitly asks + for the full release or an automated beta-and-stable train. +- Before release branching, pull latest `main` and confirm current `main` CI is + green. Then branch from that commit so regular development can continue on + `main` while release validation runs. +- Do not delete or rewrite beta tags after they leave the machine. If a + published or pushed beta needs a fix, commit the fix on the release branch and + increment to the next `-beta.N`. +- Use `/changelog` before version/tag preparation so the top changelog section + is deduped and ordered by user impact. +- When any beta or stable release is live, make a best-effort Discord + announcement using Peter's bot token from `.profile`; do not block or roll + back the release if the announcement fails. ## Keep release channel naming aligned @@ -37,7 +53,9 @@ Use this skill for release and publish-time workflow. Keep ordinary development - For fallback correction tags like `vYYYY.M.D-N`, the repo version locations still stay at `YYYY.M.D`. - “Bump version everywhere” means all version locations above except `appcast.xml`. - Release signing and notary credentials live outside the repo in the private maintainer docs. -- Every OpenClaw release ships the npm package and macOS app together. +- Every stable OpenClaw release ships the npm package and macOS app together. + Beta releases normally ship npm/package artifacts first and skip mac app + build/sign/notarize unless the operator requests mac beta validation. - The production Sparkle feed lives at `https://raw.githubusercontent.com/openclaw/openclaw/main/appcast.xml`, and the canonical published file is `appcast.xml` on `main` in the `openclaw` repo. - That shared production Sparkle feed is stable-only. Beta mac releases may upload assets to the GitHub prerelease, but they must not replace the shared @@ -131,6 +149,8 @@ node --import tsx scripts/openclaw-npm-postpublish-verify.ts - Any fix after preflight means a new commit. Delete and recreate the tag and matching GitHub release from the fixed commit, then rerun preflight from scratch before publishing. + Exception: never delete or recreate a beta tag that has already been pushed or + published; increment to the next beta number instead. - For stable mac releases, generate the signed `appcast.xml` before uploading public release assets so the updater feed cannot lag the published binaries. - Serialize stable appcast-producing runs across tags so two releases do not @@ -141,14 +161,13 @@ node --import tsx scripts/openclaw-npm-postpublish-verify.ts ## Use the right auth flow - OpenClaw publish uses GitHub trusted publishing. -- Stable npm promotion from `beta` to `latest` is an explicit mode on - `.github/workflows/openclaw-npm-release.yml`, but it still needs a valid - `NPM_TOKEN` because `npm dist-tag` management is separate from trusted - publishing. -- Direct stable publishes can also run the same workflow with - `sync_stable_dist_tags=true` to point both `latest` and `beta` at the - already-published stable version. This also needs the `npm-release` - environment approval and `NPM_TOKEN`. +- Stable npm promotion from `beta` to `latest` uses the private + `openclaw/releases-private/.github/workflows/openclaw-npm-dist-tags.yml` + workflow because `npm dist-tag` management needs `NPM_TOKEN`, while the + public npm release workflow stays OIDC-only. +- Direct stable publishes can also use that private dist-tag workflow to point + `beta` at the already-published `latest` version when the operator wants both + tags aligned immediately. - The publish run must be started manually with `workflow_dispatch`. - The npm workflow and the private mac publish workflow accept `preflight_only=true` to run validation/build/package steps without uploading @@ -164,8 +183,9 @@ node --import tsx scripts/openclaw-npm-postpublish-verify.ts - `preflight_only=true` on the npm workflow is also the right way to validate an existing tag after publish; it should keep running the build checks even when the npm version is already published. -- Validation-only runs may be dispatched from a branch when you are testing a - workflow change before merge. +- npm validation-only preflight may still be dispatched from ordinary branches + when testing workflow changes before merge. Release checks and real publish + use only `main` or `release/YYYY.M.D`. - `.github/workflows/macos-release.yml` in `openclaw/openclaw` is now a public validation-only handoff. It validates the tag/release state and points operators to the private repo. It still rebuilds the JS outputs needed for @@ -173,7 +193,7 @@ node --import tsx scripts/openclaw-npm-postpublish-verify.ts artifacts. - `openclaw/releases-private/.github/workflows/openclaw-macos-validate.yml` is the required private mac validation lane for `swift test`; keep it green - before any real mac publish run starts. + before any real stable mac publish run starts. - Real mac preflight and real mac publish both use `openclaw/releases-private/.github/workflows/openclaw-macos-publish.yml`. - The private mac validation lane runs on GitHub's standard macOS runner. @@ -183,10 +203,15 @@ node --import tsx scripts/openclaw-npm-postpublish-verify.ts instead of uploading public GitHub release assets. - Private smoke-test runs upload ad-hoc, non-notarized build artifacts as workflow artifacts and intentionally skip stable `appcast.xml` generation. -- npm preflight, public mac validation, private mac validation, and private mac - preflight must all pass before any real publish run starts. -- Real publish runs must be dispatched from `main`; branch-dispatched publish - attempts should fail before the protected environment is reached. +- For stable releases, npm preflight, public mac validation, private mac + validation, and private mac preflight must all pass before any real publish + run starts. For beta releases, npm preflight plus the selected Docker, + install/update, Parallels, and release-check lanes are sufficient unless mac + beta validation was explicitly requested. +- Real publish runs may be dispatched from `main` or from a + `release/YYYY.M.D` branch. For release-branch runs, the tag must be contained + in that release branch, and the real publish must reuse a successful preflight + from the same branch. - The release workflows stay tag-based; rely on the documented release sequence rather than workflow-level SHA pinning. - The `npm-release` environment must be approved by `@openclaw/openclaw-release-managers` before publish continues. @@ -247,58 +272,73 @@ node --import tsx scripts/openclaw-npm-postpublish-verify.ts 1. Confirm the operator explicitly wants to cut a release. 2. Choose the exact target version and git tag. -3. Make every repo version location match that tag before creating it. -4. Update `CHANGELOG.md` and assemble the matching GitHub release notes. -5. Run the full preflight for all relevant release builds, including mac readiness. -6. Confirm the target npm version is not already published. -7. Create and push the git tag. -8. Create or refresh the matching GitHub release. -9. Start `.github/workflows/openclaw-npm-release.yml` with `preflight_only=true` - and choose the intended `npm_dist_tag` (`beta` default; `latest` only for - an intentional direct stable publish). Wait for it to pass. Save that run id - because the real publish requires it to reuse the prepared npm tarball. -10. Start `.github/workflows/macos-release.yml` in `openclaw/openclaw` and wait - for the public validation-only run to pass. -11. Start +3. Pull latest `main`, confirm `main` CI is green, and create + `release/YYYY.M.D` from that commit. +4. Run `/changelog` for the target version, then make every repo version + location match that tag before creating it. +5. Commit release preparation changes on the release branch and push the branch. +6. Run the full preflight for all relevant release builds from the release + branch, including release checks, Docker install/update, and Parallels + install/update. +7. For beta releases, skip mac app build/sign/notarize unless beta scope or a + release blocker specifically requires it. For stable releases, include the + mac app, signing, notarization, and appcast path. +8. Confirm the target npm version is not already published. +9. Create and push the git tag from the release branch. +10. Create or refresh the matching GitHub release. +11. Start `.github/workflows/openclaw-npm-release.yml` from the release branch + with `preflight_only=true` + and choose the intended `npm_dist_tag` (`beta` default; `latest` only for + an intentional direct stable publish). Wait for it to pass. Save that run id + because the real publish requires it to reuse the prepared npm tarball. +12. For stable releases, start `.github/workflows/macos-release.yml` in + `openclaw/openclaw` and wait for the public validation-only run to pass. +13. For stable releases, start `openclaw/releases-private/.github/workflows/openclaw-macos-validate.yml` with the same tag and wait for the private mac validation lane to pass. -12. Start +14. For stable releases, start `openclaw/releases-private/.github/workflows/openclaw-macos-publish.yml` with `preflight_only=true` and wait for it to pass. Save that run id because the real publish requires it to reuse the notarized mac artifacts. -13. If any preflight or validation run fails, fix the issue on a new commit, +15. If any preflight or validation run fails, fix the issue on a new commit, delete the tag and matching GitHub release, recreate them from the fixed commit, and rerun all relevant preflights from scratch before continuing. - Never reuse old preflight results after the commit changes. -14. Start `.github/workflows/openclaw-npm-release.yml` with the same tag for - the real publish, choose `npm_dist_tag` (`beta` default, `latest` only when - you intentionally want direct stable publish), keep it the same as the - preflight run, and pass the successful npm `preflight_run_id`. -15. Wait for `npm-release` approval from `@openclaw/openclaw-release-managers`. -16. If the stable release was published to `beta`, start - `.github/workflows/openclaw-npm-release.yml` again after beta validation - passes with the same stable tag, `promote_beta_to_latest=true`, - `preflight_only=false`, empty `preflight_run_id`, and `npm_dist_tag=beta`, - then verify `latest` now points at that version. -17. If the stable release was published directly to `latest` and `beta` should - follow it, start `.github/workflows/openclaw-npm-release.yml` again with - the same stable tag, `sync_stable_dist_tags=true`, - `promote_beta_to_latest=false`, `preflight_only=false`, empty - `preflight_run_id`, and `npm_dist_tag=latest`, then verify both `latest` - and `beta` point at that version. -18. Start + Never reuse old preflight results after the commit changes. For pushed or + published beta tags, do not delete/recreate; increment to the next beta tag. +16. Start `.github/workflows/openclaw-npm-release.yml` from the same branch with + the same tag for the real publish, choose `npm_dist_tag` (`beta` default, + `latest` only when you intentionally want direct stable publish), keep it + the same as the preflight run, and pass the successful npm + `preflight_run_id`. +17. Wait for `npm-release` approval from `@openclaw/openclaw-release-managers`. +18. Run postpublish verification: + `node --import tsx scripts/openclaw-npm-postpublish-verify.ts `. +19. Announce the beta/stable release on Discord best-effort using Peter's bot + token from `.profile`. +20. If the operator requested beta only, stop after beta verification and the + announcement. +21. If the stable release was published to `beta`, start the private + `openclaw/releases-private/.github/workflows/openclaw-npm-dist-tags.yml` + workflow after beta validation passes to promote that stable version from + `beta` to `latest`, then verify `latest` now points at that version. +22. If the stable release was published directly to `latest` and `beta` should + follow it, start that same private dist-tag workflow to point `beta` at the + stable version, then verify both `latest` and `beta` point at that version. +23. For stable releases, start `openclaw/releases-private/.github/workflows/openclaw-macos-publish.yml` for the real publish with the successful private mac `preflight_run_id` and wait for success. -19. Verify the successful real private mac run uploaded the `.zip`, `.dmg`, +24. Verify the successful real private mac run uploaded the `.zip`, `.dmg`, and `.dSYM.zip` artifacts to the existing GitHub release in `openclaw/openclaw`. -20. For stable releases, download `macos-appcast-` from the successful - private mac run, update `appcast.xml` on `main`, and verify the feed. -21. For beta releases, publish the mac assets but expect no shared production +25. For stable releases, download `macos-appcast-` from the successful + private mac run, update `appcast.xml` on `main`, and verify the feed. Merge + or cherry-pick release branch changes back to `main` after stable succeeds. +26. For beta releases, publish the mac assets only when intentionally requested; + expect no shared production `appcast.xml` artifact and do not update the shared production feed unless a separate beta feed exists. -22. After publish, verify npm and the attached release artifacts. +27. After publish, verify npm and the attached release artifacts. ## GHSA advisory work diff --git a/.github/workflows/openclaw-npm-release.yml b/.github/workflows/openclaw-npm-release.yml index 255940554c4..a752087281c 100644 --- a/.github/workflows/openclaw-npm-release.yml +++ b/.github/workflows/openclaw-npm-release.yml @@ -4,7 +4,7 @@ on: workflow_dispatch: inputs: tag: - description: Release tag to publish, or a full 40-character main commit SHA for validation-only preflight (for example v2026.3.22 or 0123456789abcdef0123456789abcdef01234567) + description: Release tag to publish, or a full 40-character workflow-branch commit SHA for validation-only preflight (for example v2026.3.22 or 0123456789abcdef0123456789abcdef01234567) required: true type: string preflight_only: @@ -131,19 +131,20 @@ jobs: OPENCLAW_NPM_RELEASE_SKIP_PACK_CHECK: "1" RELEASE_REF: ${{ inputs.tag }} PREFLIGHT_ONLY: ${{ inputs.preflight_only }} - RELEASE_MAIN_REF: origin/main + WORKFLOW_REF_NAME: ${{ github.ref_name }} OPENCLAW_NPM_PUBLISH_TAG: ${{ inputs.npm_dist_tag }} run: | set -euo pipefail RELEASE_SHA=$(git rev-parse HEAD) - export RELEASE_SHA RELEASE_MAIN_REF - # Fetch the full main ref so merge-base ancestry checks keep working - # for older tagged commits that are still contained in main. - git fetch --no-tags origin +refs/heads/main:refs/remotes/origin/main + RELEASE_BRANCH_REF="refs/remotes/origin/${WORKFLOW_REF_NAME}" + export RELEASE_SHA RELEASE_BRANCH_REF + # Fetch the workflow branch so merge-base ancestry checks keep working + # for older tagged commits contained in a release branch. + git fetch --no-tags origin "+refs/heads/${WORKFLOW_REF_NAME}:refs/remotes/origin/${WORKFLOW_REF_NAME}" if [[ "${RELEASE_REF}" =~ ^[0-9a-fA-F]{40}$ ]]; then - MAIN_SHA="$(git rev-parse origin/main)" - if [[ "${RELEASE_SHA}" != "${MAIN_SHA}" ]]; then - echo "Validation-only SHA mode only supports the current origin/main HEAD." >&2 + BRANCH_SHA="$(git rev-parse "${RELEASE_BRANCH_REF}")" + if [[ "${RELEASE_SHA}" != "${BRANCH_SHA}" ]]; then + echo "Validation-only SHA mode only supports the current ${WORKFLOW_REF_NAME} HEAD." >&2 exit 1 fi RELEASE_TAG="v$(node -p "require('./package.json').version")" @@ -153,6 +154,8 @@ jobs: RELEASE_TAG="${RELEASE_REF}" export RELEASE_TAG fi + RELEASE_MAIN_REF="${RELEASE_BRANCH_REF}" + export RELEASE_MAIN_REF pnpm release:openclaw:npm:check # KEEP THIS LANE LIMITED TO FAST, REPEATABLE RELEASE READINESS CHECKS. @@ -253,13 +256,13 @@ jobs: permissions: contents: read steps: - - name: Require main workflow ref for publish + - name: Require main or release workflow ref for publish env: WORKFLOW_REF: ${{ github.ref }} run: | set -euo pipefail - if [[ "${WORKFLOW_REF}" != "refs/heads/main" ]]; then - echo "Real publish runs must be dispatched from main. Use preflight_only=true for branch validation." + if [[ "${WORKFLOW_REF}" != "refs/heads/main" ]] && [[ ! "${WORKFLOW_REF}" =~ ^refs/heads/release/[0-9]{4}\.[1-9][0-9]*\.[1-9][0-9]*$ ]]; then + echo "Real publish runs must be dispatched from main or release/YYYY.M.D. Use preflight_only=true for other branch validation." exit 1 fi @@ -329,10 +332,11 @@ jobs: env: GH_TOKEN: ${{ github.token }} PREFLIGHT_RUN_ID: ${{ inputs.preflight_run_id }} + EXPECTED_PREFLIGHT_BRANCH: ${{ github.ref_name }} run: | set -euo pipefail RUN_JSON="$(gh run view "$PREFLIGHT_RUN_ID" --repo "$GITHUB_REPOSITORY" --json workflowName,headBranch,event,conclusion,url)" - printf '%s' "$RUN_JSON" | node -e 'const fs = require("node:fs"); const run = JSON.parse(fs.readFileSync(0, "utf8")); const checks = [["workflowName", "OpenClaw NPM Release"], ["headBranch", "main"], ["event", "workflow_dispatch"], ["conclusion", "success"]]; for (const [key, expected] of checks) { if (run[key] !== expected) { console.error(`Referenced npm preflight run ${process.env.PREFLIGHT_RUN_ID} must have ${key}=${expected}, got ${run[key] ?? ""}.`); process.exit(1); } } console.log(`Using npm preflight run ${process.env.PREFLIGHT_RUN_ID}: ${run.url}`);' + printf '%s' "$RUN_JSON" | node -e 'const fs = require("node:fs"); const run = JSON.parse(fs.readFileSync(0, "utf8")); const checks = [["workflowName", "OpenClaw NPM Release"], ["headBranch", process.env.EXPECTED_PREFLIGHT_BRANCH], ["event", "workflow_dispatch"], ["conclusion", "success"]]; for (const [key, expected] of checks) { if (run[key] !== expected) { console.error(`Referenced npm preflight run ${process.env.PREFLIGHT_RUN_ID} must have ${key}=${expected}, got ${run[key] ?? ""}.`); process.exit(1); } } console.log(`Using npm preflight run ${process.env.PREFLIGHT_RUN_ID}: ${run.url}`);' - name: Download prepared npm tarball uses: actions/download-artifact@v8 @@ -348,14 +352,15 @@ jobs: env: OPENCLAW_NPM_RELEASE_SKIP_PACK_CHECK: "1" RELEASE_TAG: ${{ inputs.tag }} - RELEASE_MAIN_REF: origin/main + WORKFLOW_REF_NAME: ${{ github.ref_name }} run: | set -euo pipefail RELEASE_SHA=$(git rev-parse HEAD) + RELEASE_MAIN_REF="refs/remotes/origin/${WORKFLOW_REF_NAME}" export RELEASE_SHA RELEASE_TAG RELEASE_MAIN_REF - # Fetch the full main ref so merge-base ancestry checks keep working - # for older tagged commits that are still contained in main. - git fetch --no-tags origin +refs/heads/main:refs/remotes/origin/main + # Fetch the workflow branch so merge-base ancestry checks keep working + # for older tagged commits contained in a release branch. + git fetch --no-tags origin "+refs/heads/${WORKFLOW_REF_NAME}:refs/remotes/origin/${WORKFLOW_REF_NAME}" pnpm release:openclaw:npm:check - name: Verify prepared tarball provenance diff --git a/.github/workflows/openclaw-release-checks.yml b/.github/workflows/openclaw-release-checks.yml index 1b86cda366d..c780b9875e2 100644 --- a/.github/workflows/openclaw-release-checks.yml +++ b/.github/workflows/openclaw-release-checks.yml @@ -4,7 +4,7 @@ on: workflow_dispatch: inputs: ref: - description: Existing release tag or current full 40-character main commit SHA to validate (for example v2026.4.12 or 0123456789abcdef0123456789abcdef01234567) + description: Existing release tag or current full 40-character workflow-branch commit SHA to validate (for example v2026.4.12 or 0123456789abcdef0123456789abcdef01234567) required: true type: string provider: @@ -45,13 +45,13 @@ jobs: provider: ${{ steps.inputs.outputs.provider }} mode: ${{ steps.inputs.outputs.mode }} steps: - - name: Require main workflow ref for release checks + - name: Require main or release workflow ref for release checks env: WORKFLOW_REF: ${{ github.ref }} run: | set -euo pipefail - if [[ "${WORKFLOW_REF}" != "refs/heads/main" ]]; then - echo "Release checks must be dispatched from main so the workflow logic and secrets stay canonical." >&2 + if [[ "${WORKFLOW_REF}" != "refs/heads/main" ]] && [[ ! "${WORKFLOW_REF}" =~ ^refs/heads/release/[0-9]{4}\.[1-9][0-9]*\.[1-9][0-9]*$ ]]; then + echo "Release checks must be dispatched from main or release/YYYY.M.D so workflow logic and secrets stay controlled." >&2 exit 1 fi @@ -61,7 +61,7 @@ jobs: run: | set -euo pipefail if [[ ! "${RELEASE_REF}" =~ ^v[0-9]{4}\.[1-9][0-9]*\.[1-9][0-9]*((-beta\.[1-9][0-9]*)|(-[1-9][0-9]*))?$ ]] && [[ ! "${RELEASE_REF}" =~ ^[0-9a-fA-F]{40}$ ]]; then - echo "Expected an existing release tag or current full 40-character main commit SHA, got: ${RELEASE_REF}" >&2 + echo "Expected an existing release tag or current full 40-character workflow-branch commit SHA, got: ${RELEASE_REF}" >&2 exit 1 fi @@ -75,20 +75,22 @@ jobs: id: ref run: echo "sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT" - - name: Validate selected ref is on main + - name: Validate selected ref is on workflow branch env: RELEASE_REF: ${{ inputs.ref }} + WORKFLOW_REF_NAME: ${{ github.ref_name }} run: | set -euo pipefail - git fetch --no-tags origin +refs/heads/main:refs/remotes/origin/main + RELEASE_BRANCH_REF="refs/remotes/origin/${WORKFLOW_REF_NAME}" + git fetch --no-tags origin "+refs/heads/${WORKFLOW_REF_NAME}:refs/remotes/origin/${WORKFLOW_REF_NAME}" if [[ "${RELEASE_REF}" =~ ^[0-9a-fA-F]{40}$ ]]; then - MAIN_SHA="$(git rev-parse origin/main)" - if [[ "$(git rev-parse HEAD)" != "${MAIN_SHA}" ]]; then - echo "Commit SHA mode only supports the current origin/main HEAD. Use a release tag for older commits." >&2 + BRANCH_SHA="$(git rev-parse "${RELEASE_BRANCH_REF}")" + if [[ "$(git rev-parse HEAD)" != "${BRANCH_SHA}" ]]; then + echo "Commit SHA mode only supports the current ${WORKFLOW_REF_NAME} HEAD. Use a release tag for older commits." >&2 exit 1 fi else - git merge-base --is-ancestor HEAD origin/main + git merge-base --is-ancestor HEAD "${RELEASE_BRANCH_REF}" fi - name: Capture selected inputs diff --git a/docs/reference/RELEASING.md b/docs/reference/RELEASING.md index eebe1221ecd..a311ce425f1 100644 --- a/docs/reference/RELEASING.md +++ b/docs/reference/RELEASING.md @@ -26,12 +26,19 @@ OpenClaw has three public release lanes: - `latest` means the current promoted stable npm release - `beta` means the current beta install target - Stable and stable correction releases publish to npm `beta` by default; release operators can target `latest` explicitly, or promote a vetted beta build later -- Every OpenClaw release ships the npm package and macOS app together +- Every stable OpenClaw release ships the npm package and macOS app together; + beta releases normally validate and publish the npm/package path first, with + mac app build/sign/notarize reserved for stable unless explicitly requested ## Release cadence - Releases move beta-first - Stable follows only after the latest beta is validated +- Maintainers normally cut releases from a `release/YYYY.M.D` branch created + from current `main`, so release validation and fixes do not block new + development on `main` +- If a beta tag has been pushed or published and needs a fix, maintainers cut + the next `-beta.N` tag instead of deleting or recreating the old beta tag - Detailed release procedure, approvals, credentials, and recovery notes are maintainer-only @@ -55,14 +62,15 @@ OpenClaw has three public release lanes: - This split is intentional: keep the real npm release path short, deterministic, and artifact-focused, while slower live checks stay in their own lane so they do not stall or block publish -- Release checks must be dispatched from the `main` workflow ref so the - workflow logic and secrets stay canonical +- Release checks must be dispatched from the `main` workflow ref or from a + `release/YYYY.M.D` workflow ref so the workflow logic and secrets stay + controlled - That workflow accepts either an existing release tag or the current full - 40-character `main` commit SHA -- In commit-SHA mode it only accepts the current `origin/main` HEAD; use a + 40-character workflow-branch commit SHA +- In commit-SHA mode it only accepts the current workflow-branch HEAD; use a release tag for older release commits - `OpenClaw NPM Release` validation-only preflight also accepts the current - full 40-character `main` commit SHA without requiring a pushed tag + full 40-character workflow-branch commit SHA without requiring a pushed tag - That SHA path is validation-only and cannot be promoted into a real publish - In SHA mode the workflow synthesizes `v` only for the package metadata check; real publish still requires a real release tag @@ -81,6 +89,8 @@ OpenClaw has three public release lanes: install path in a fresh temp prefix - Maintainer release automation now uses preflight-then-promote: - real npm publish must pass a successful npm `preflight_run_id` + - the real npm publish must be dispatched from the same `main` or + `release/YYYY.M.D` branch as the successful preflight run - stable npm releases default to `beta` - stable npm publish can target `latest` explicitly via workflow input - token-based npm dist-tag mutation now lives in @@ -119,7 +129,7 @@ OpenClaw has three public release lanes: - `tag`: required release tag such as `v2026.4.2`, `v2026.4.2-1`, or `v2026.4.2-beta.1`; when `preflight_only=true`, it may also be the current - full 40-character `main` commit SHA for validation-only preflight + full 40-character workflow-branch commit SHA for validation-only preflight - `preflight_only`: `true` for validation/build/package only, `false` for the real publish path - `preflight_run_id`: required on the real publish path so the workflow reuses @@ -129,14 +139,19 @@ OpenClaw has three public release lanes: `OpenClaw Release Checks` accepts these operator-controlled inputs: - `ref`: existing release tag or the current full 40-character `main` commit - SHA to validate + SHA to validate when dispatched from `main`; from a release branch, use an + existing release tag or the current full 40-character release-branch commit + SHA Rules: - Stable and correction tags may publish to either `beta` or `latest` - Beta prerelease tags may publish only to `beta` -- Full commit SHA input is allowed only when `preflight_only=true` -- Release checks commit-SHA mode also requires the current `origin/main` HEAD +- For `OpenClaw NPM Release`, full commit SHA input is allowed only when + `preflight_only=true` +- `OpenClaw Release Checks` is always validation-only and also accepts the + current workflow-branch commit SHA +- Release checks commit-SHA mode also requires the current workflow-branch HEAD - The real publish path must use the same `npm_dist_tag` used during preflight; the workflow verifies that metadata before publish continues @@ -145,12 +160,13 @@ Rules: When cutting a stable npm release: 1. Run `OpenClaw NPM Release` with `preflight_only=true` - - Before a tag exists, you may use the current full `main` commit SHA for a - validation-only dry run of the preflight workflow + - Before a tag exists, you may use the current full workflow-branch commit + SHA for a validation-only dry run of the preflight workflow 2. Choose `npm_dist_tag=beta` for the normal beta-first flow, or `latest` only when you intentionally want a direct stable publish 3. Run `OpenClaw Release Checks` separately with the same tag or the - full current `main` commit SHA when you want live prompt cache coverage + full current workflow-branch commit SHA when you want live prompt cache + coverage - This is separate on purpose so live coverage stays available without recoupling long-running or flaky checks to the publish workflow 4. Save the successful `preflight_run_id`