# Release Openclaw Maintainer

> Prepare or verify OpenClaw stable, beta, and extended-stable releases, including backport discovery, changelogs, release notes, publish commands, and artifacts.

- Skill: `linux2010/release-openclaw-maintainer` (Agent Skill, multi-file: 4 files)
- Install (CLI): `npx skillmds@latest add linux2010/release-openclaw-maintainer`
- Raw SKILL.md: https://api.skillmd.com/api/skills/linux2010/release-openclaw-maintainer/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Product & Planning
- Author: Linux2010 (https://skillmd.com/u/linux2010)
- Updated: 2026-09-10
- Page: https://skillmd.com/skills/linux2010/release-openclaw-maintainer

---


# OpenClaw Release Maintainer

Use this skill for release and publish-time workflow, including preparing the
approved backport set for an extended-stable maintenance release. Load
`$release-private` if it exists before resolving Peter-owned credential
locators or private host topology. Keep ordinary development changes and
GHSA-specific advisory work outside this skill.

## Freeze the release state

Before validation or publication, write one compact state record and keep it
current:

- goal and terminal success criteria
- release version, tag, branch, cut SHA, Code SHA, and Release SHA
- active Full Release Validation parent run id and attempt
- npm preflight and publish parent run ids
- completed phases and immutable child artifacts
- approved backports or main changes
- current phase, next action, and one precise blocker if stopped

Use `references/release-handoff-template.md` when starting a release session,
recovering after compaction, or handing the release to another operator.
Completed phases stay complete. Reopen one only when a named event invalidates
its evidence, such as a Code SHA change, a non-changelog Release SHA change, or
a workflow fix that the existing parent run cannot consume.

## Respect release guardrails

- Do not change version numbers without explicit operator approval.
- When normal beta/stable release planning includes a backport audit, read
  `references/backport-discovery.md` before selecting commits. Freeze the
  release baseline and main SHA, complete the durable candidate ledger, and get
  approval for its categorized set before mutating the release branch. This
  audit is required for discovery; it does not authorize optional backports on
  an already-frozen candidate.
- Versions use `YYYY.M.PATCH`, where `PATCH` is the sequential release-train number within the month, not the calendar day.
- Choose a new beta train from stable and beta releases only. Alpha-only tags do not consume or advance the beta/stable patch number. Continue the highest existing unpublished/published beta train with the next `beta.N` when appropriate; otherwise increment the highest stable/beta patch by one and start at `beta.1`.
- Example: after stable `2026.6.5`, the next new beta train is `2026.6.6-beta.1`, even if automated alpha-only tags such as `2026.6.10-alpha.1` exist.
- Obtain explicit operator approval before the first irreversible publish
  action. Instructions to cut, ship, publish, or get a named release out carry
  through that release's validated publish and verification steps; do not ask
  again at final dispatch. Reconfirm only if the target version, tag, channel,
  publish scope, or a material risk changes.
- 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.
- Do not edit the root `README.md` as release prep, release closeout, or a
  substitute for release notes. Package-root README validation is a hard
  packaging gate, but a release only changes README content when an actual
  user-facing documentation contract changed.
- Normal release work happens on a branch cut from `main`, not directly on
  `main`. Use `release/YYYY.M.PATCH` for the branch name.
- Hold release scope from cut-SHA selection through publish and verification.
  The active release is the work queue; moving `main` is only a trusted
  workflow and provenance source unless the operator explicitly requests main
  work.
- Touch `main` during the active release only for an operator-requested change
  or a critical main-owned blocker that prevents this release and cannot be
  fixed or proven from the release branch. Examples include a live installer or
  trusted workflow that is sourced only from `main`. A red unrelated `main`
  check, baseline, refactor, cleanup, or later improvement is not a release
  blocker.
- Do not broaden a release-critical main fix to make moving `main` green. Keep
  the change to the exact blocker, run focused proof, follow the required main
  landing policy, then return immediately to the release branch. If unrelated
  main health blocks that landing, report the blocker and continue independent
  release work instead of adopting the failure.
- Defer normal forward-ports and main closeout until after publication.
  Forward-port before publish only when the operator requests it or `main`
  itself owns the exact release-critical runtime or workflow surface.
- 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.
- Resolve the intended cut SHA once. If the operator supplies a SHA, use it
  exactly; do not pull, rebase, or advance it to newer `main`. Otherwise fetch
  `origin/main` once and record the selected full SHA plus its CI state. A red
  unrelated main check does not authorize healing main.
- Create a clean release worktree and `release/YYYY.M.PATCH` from that selected
  SHA. Do not commit or absorb unrelated dirty files as release preparation.
  Finish version preparation plus any operator-selected backports,
  release-only fixes, and explicitly required pre-publish main changes.
  Backports are optional.
  Freeze this product-complete tree as the **Code SHA** without changing the
  release changelog.
- Full product validation belongs to the Code SHA. If validation finds a code
  defect, fix it, freeze a new Code SHA, and validate that SHA. If the failure
  belongs to trusted workflow tooling, the harness, credentials, or
  infrastructure, repair the smallest owning surface and rerun against the same
  Code SHA. Touch `main` only under the active release scope lock above. Never
  mutate the release candidate to satisfy newer tooling or heal unrelated main.
- Generate `CHANGELOG.md` only after the Code SHA is green. The resulting
  **Release SHA** must be a descendant whose complete diff from the Code SHA is
  exactly `CHANGELOG.md`. Release-note checks, npm preflight/package bytes,
  install/update acceptance, tagging, and publication run against the Release
  SHA. Full product validation is reused through the
  `changelog-only-release-v1` evidence policy; any non-changelog source change
  returns to the Code SHA loop.
- During release planning, inspect both `src/plugins/compat/registry.ts` and
  `src/commands/doctor/shared/deprecation-compat.ts` before branching and again
  before final publish. For every deprecated or removal-pending compatibility
  record whose `removeAfter` date is on or before the release date, either
  remove the compatibility path where safe and validate the affected tests, or
  write down why removal is blocked and get explicit maintainer approval before
  shipping the expired compatibility path.
- When removing deprecated runtime/config compatibility, preserve any doctor
  migration, repair, or hint that is still needed by supported upgrade paths.
  Doctor-side compatibility should stay tracked in
  `src/commands/doctor/shared/deprecation-compat.ts` until maintainers confirm
  the repair is no longer needed.
- Revalidate compatibility replacement text during release planning. The
  recommended replacement can shift as plugin ownership, externalization, and
  config footprint move, so do not blindly copy stale replacement annotations
  into release notes.
- Do not delete or rewrite beta tags after their matching npm package has been
  published. If a pushed beta tag fails before npm publish, the version is not
  consumed: keep the same `-beta.N`, delete/recreate or force-move the git tag
  and prerelease to the fixed commit, and rerun preflight. Do not increment to
  the next beta number until the matching npm package has actually published.
  If a published beta needs a fix, commit the fix on the release branch and
  increment to the next `-beta.N`.
- For a beta release train, keep Full Release Validation as a pre-publish Code
  SHA gate unless the operator explicitly waives it. Run independent validation
  lanes in parallel where safe, but do not start changelog or package
  finalization until the Code SHA is green. After the changelog-only Release SHA
  exists, run npm preflight and the package/install/update acceptance roster
  against its exact bytes. If a product defect appears, return to a new Code
  SHA; if a release-tooling or publication child fails, repair/resume that child
  without changing the candidate. After a published beta needs a code fix,
  increment the beta number and repeat. Defer its forward-port until after
  publication unless the operator requests it. Do not scan moving `main` for
  extra fixes during an active release unless the operator explicitly asks for
  that audit. Operators may authorize up to 4 autonomous beta attempts; after
  4 failed beta attempts, stop and report.
- As soon as the Code SHA exists, dispatch `OpenClaw Performance`
  with `target_ref=<code-sha>` in parallel with the other release work. Do
  not wait for full release validation to start the performance signal.
- Before publish/closeout, compare available product performance metrics with
  earlier releases: Kova agent-turn/resource metrics, gateway startup
  ready/listen/RSS/CPU metrics, and CLI startup metrics from release evidence
  or clawgrit reports. Report regressions explicitly. A major regression is a
  release blocker unless the operator waives it or the data clearly proves
  infrastructure noise.
- Heal release-owned CI before changelog, tagging, or publishing. The exact
  Code SHA must have green `Full Release Validation`, including the root
  Dockerfile/install-smoke path.
  Treat a red Docker, package, or release workflow lane as a release-branch
  defect until the smallest correct fix is landed and proven; do not waive it
  because npm preflight or another sibling lane passed. Unrelated moving-main
  failures are not part of this gate.
- Keep the canonical `scripts/pr` runner authoritative for prepare and merge
  artifacts. A release-gate policy change may use focused candidate tests and
  exact-SHA hosted CI for proof, but never route `prepare-*` or `merge-*`
  through PR-controlled scripts or synthesize prepare artifacts to bootstrap
  the change. If the current canonical gate cannot validate the new policy,
  stop for explicit maintainer direction rather than weakening that boundary.
- In maintainer Testbox mode, use `OPENCLAW_TESTBOX=1 scripts/pr prepare-run
<PR>` only after the exact PR head has passed `CI` and every scheduled
  hosted gate. For a workflow change, that means `Blacksmith Testbox`,
  `Blacksmith ARM Testbox`, `Blacksmith Build Artifacts Testbox`, and
  `Workflow Sanity`; only gates GitHub actually scheduled for that exact head
  are required. This preserves the canonical prepare artifacts while avoiding
  a redundant broad local suite. A
  literal `CHANGELOG.md`-only head gets a clean diff check instead because
  those workflows intentionally do not dispatch. Documentation and README
  changes still require CI. If `merge-run` requires a mainline sync, run
  `OPENCLAW_TESTBOX=1 scripts/pr prepare-sync-head <PR>`, wait for those hosted
  gates on the newly pushed SHA, then run `prepare-run` again.
- If an exact PR-head CI run has no active jobs because Blacksmith capacity is
  stalled, a maintainer may dispatch the explicit GitHub-hosted fallback from
  the PR head branch. First verify its workflow carries the current schema with
  `gh api 'repos/openclaw/openclaw/contents/.github/workflows/ci.yml?ref=<pr-head-branch>'
--jq .content | base64 --decode | rg -q 'pull_request_number:'`. If absent,
  refresh the PR head from `main`, use the new SHA, and let normal CI run before
  considering another fallback. Then dispatch:
  `gh workflow run ci.yml --repo openclaw/openclaw --ref <pr-head-branch> -f
target_ref=<full-pr-sha> -f pull_request_number=<pr-number> -f
include_android=true -f release_gate=true`.
  Use it only for an observed provider queue stall, never for failed CI or as a
  routine shortcut. The run must be named `CI release gate <full-pr-sha>` and
  pass on that exact SHA; the native hosted-gate verifier rejects generic manual
  CI runs. If `Blacksmith Build Artifacts Testbox` is the only remaining
  required gate and it is still queued without a runner, the same completed
  fallback CI may cover it because its `build-artifacts` job builds, packages,
  and smoke tests those artifacts. The verifier records that coverage. Never
  use this coverage when the artifact workflow has started, failed, been
  cancelled, or been skipped. Then rerun `OPENCLAW_TESTBOX=1 scripts/pr
prepare-run <PR>`.
- Generate the changelog once after the final Code SHA is fully green. Do not
  regenerate it for same-candidate tooling reruns, resumed publication, or
  promotion. If code changes, validate the replacement Code SHA first and then
  regenerate the release section once for that new history. Use
  `$openclaw-changelog-update` for the rewrite. Do not continue release prep if
  the target `CHANGELOG.md` section does not have `### Highlights`,
  `### Changes`, and `### Fixes`, grouped by user-facing surface while
  preserving every relevant PR/issue ref and every human `Thanks @...`
  attribution in the grouped bullet.
- Changelog PR provenance follows `origin/main`, not the release integration
  PR. Cite the original merged main PR for equivalent backports. Keep a
  release-branch PR only when the change landed there first and has not yet
  been forward-ported to `main`.
- Do not create beta-specific `CHANGELOG.md` headings. Beta releases use the
  stable base version section, for example `v2026.4.20-beta.1` uses
  `## 2026.4.20` release notes.
- When any beta, stable, or extended-stable release is live, make a best-effort
  Discord announcement using the release-track-specific wording; do not block
  or roll back the release if the announcement fails.
- When asked to announce on X, use `~/Projects/bird/bird` and follow the
  release tweet style below.

## Prepare extended-stable backports

When asked to create the initial `.33` extended-stable line or a later
maintenance patch, read
`references/backport-discovery.md` and
`references/extended-stable-backports.md` and follow both before version, tag,
or publication work. Treat backport discovery and preparation as an ability of
this release skill, not as a separate release workflow.

The backport flow covers mainline inventory, private-security reconciliation,
approval, the staging PR, and proof handoff. After it lands, use the sequence
below. Never route `.33+` through regular beta/stable release steps.

Extended-stable requires a visible **SDK/config backport warning** whenever a
candidate changes the public plugin SDK or a config/default/schema/migration
surface. Prefer an adaptation that uses the SDK and configuration already
shipped on that line. If a contract change remains necessary, record its
published impact and the maintainer decision in the ledger and staging PR.
Read `references/extended-stable-backports.md`; a clean cherry-pick, green
release checks, or a regenerated baseline does not by itself explain the
maintenance risk.

## Publish Gateway extended-stable releases

Use this path only for the trailing completed month's `.33+` Gateway
distribution: the `openclaw` npm package, official npm plugins, and matching
Docker Gateway images. Treat
`docs/reference/RELEASING.md`,
`scripts/openclaw-npm-extended-stable-release.mjs`, and the release workflows
on pinned current `main` as the exact command and validation contract.

1. On `extended-stable/YYYY.M.33`, verify the root and every publishable official
   plugin have the intended version. Generate and commit the complete
   `## YYYY.M.P` changelog section with `### Highlights`, `### Changes`, and
   `### Fixes`. Carry the full current-main Docker
   release-channel unit: workflow, promoter, policy, shared classifier, tests,
   and workflow validation. Run focused checks and freeze the untagged tip SHA.
2. From that branch, run npm preflight with the SHA as `tag`,
   `preflight_only=true`, and `npm_dist_tag=extended-stable`; save the run ID.
3. Run complete Full Release Validation against the canonical branch with
   `release_profile=stable`; save its run ID and successful `run_attempt`.
   Prefer the trusted main-pinned harness, which attests the immutable target
   SHA in its v3 manifest. Any candidate branch change invalidates both gates.
4. Require the tip still equals the frozen SHA, then create signed `vYYYY.M.P`.
   Never move or delete a final tag; later source changes need a new patch.
5. Require the saved validation run to be complete and successful, bind its
   manifest target SHA and attempt to the tag, and accept a direct run from the
   canonical branch, a direct current-`main` run whose workflow SHA is still
   reachable from main, or a trusted main-pinned `release-ci/*` harness. Reject
   narrow reruns.
6. Dispatch `plugin-npm-release.yml` from the same branch with
   `publish_scope=all-publishable`, the full release SHA as `ref`, and
   `npm_dist_tag=extended-stable`. Require complete exact-version and selector
   readback, then save the successful plugin run ID.
7. Publish core with the tag, `npm_dist_tag=extended-stable`, all three run IDs,
   and `full_release_validation_run_attempt=<saved-attempt>`. Normally dispatch
   from the canonical branch. For a workflow-only recovery after the candidate
   is immutable, dispatch trusted current `main` with
   `release_candidate_branch=extended-stable/YYYY.M.33`; it still publishes the
   tag checkout and accepts canonical-branch, current-main, or trusted-pinned
   validation evidence; the prepared tarball and every evidence identity must
   still match the candidate SHA.
8. From a clean current-`main` checkout, run
   `node --import tsx scripts/openclaw-npm-postpublish-verify.ts YYYY.M.P`.
   Verify signatures, provenance, inventories, exact versions, and selectors.
   Use the generated repair only for the root selector; repair other selectors
   with approved credential-isolated tooling. Never republish a version.
9. Require `Docker Release` to verify default, slim, browser, and architecture
   images in GHCR and Docker Hub, including attestations and platform versions.
   It must advance only
   `extended-stable`, `extended-stable-slim`, and `extended-stable-browser` by
   digest and refuse automatic rollback. For alias repair, dispatch the
   approval-gated `docker-channel-promote.yml` from current `main` with the exact
   tag; never rebuild or move the release tag.
10. Do not create a GitHub Release or publish macOS, Windows, mobile, website,
    ClawHub, or private dist-tag artifacts from this path.

## Keep release channel naming aligned

- `stable`: user updates resolve npm `latest`; tagged regular releases publish
  to npm `beta` by default, then operators may target or promote to `latest`
  explicitly
- `extended-stable`: user updates resolve npm `extended-stable`; operators
  publish the trailing completed month's `.33+` line from
  `extended-stable/YYYY.M.33`
- `beta`: prerelease tags like `vYYYY.M.PATCH-beta.N`, with npm dist-tag `beta`
- Prefer `-beta.N`; do not mint new `-1` or `-2` beta suffixes
- `dev`: moving head on `main`
- When using a beta Git tag, publish npm with the matching beta version suffix so the plain version is not consumed or blocked

## Close stable releases on main

This gate starts only after stable publication. It is a narrow shipped-state
closeout, not permission to heal broader `main`. Stable publication is not
complete until `main` carries the actual shipped release state.

1. Start from fresh latest `main`. Audit `release/YYYY.M.PATCH` against it and
   forward-port real fixes that are absent from `main`. Do not blindly merge
   release-only compatibility, test, or validation adapters into newer `main`.
2. Set `main` to the shipped stable version, not a speculative next train. Run
   `pnpm release:prep` after the root version change, then
   `pnpm deps:npm-lock:check`.
3. Make `CHANGELOG.md`'s `## YYYY.M.PATCH` section on `main` exactly match the
   tagged release branch. Include the stable `appcast.xml` update when the mac
   release published one.
4. Do not add `YYYY.M.PATCH+1`, a beta version, or an empty future changelog
   section to `main` until the operator explicitly starts that release train.
5. Run `pnpm release:generated:check`, `pnpm deps:npm-lock:check`, and
   `OPENCLAW_TESTBOX=1 pnpm check:changed`. Push, then verify `origin/main`
   contains the shipped version and changelog before calling the stable release
   done.
6. Keep repository variables `RELEASE_ROLLBACK_DRILL_ID` and
   `RELEASE_ROLLBACK_DRILL_DATE` current after each private rollback drill.
   `openclaw-stable-main-closeout.yml` starts from the `main` push carrying the
   shipped version, changelog, and appcast after stable publication, then binds
   immutable evidence to the published tag. Do not declare stable complete
   until it writes the immutable closeout manifest to the GitHub release. The
   drill must be within 90 days; manual dispatch is only for repair/replay, and
   private rollback commands remain in the maintainer-only runbook.

## Handle versions and release files consistently

Use the release preparation controller before manual version edits:

```bash
pnpm release:prepare -- --version YYYY.M.PATCH-beta.N --shadow
pnpm release:prepare -- --version YYYY.M.PATCH-beta.N --write
pnpm release:prepare -- --version YYYY.M.PATCH-beta.N --check
```

Shadow mode is the default and never runs mutating commands. Write mode aligns
the root and macOS versions, optionally Android with `--android`, then runs only
the version-owned generated metadata DAG. Every mode writes an exact
HEAD/worktree-bound manifest under git metadata for cutover review.

- Version locations include:
  - `package.json`
  - `apps/android/app/build.gradle.kts`
  - `apps/ios/Sources/Info.plist`
  - `apps/ios/Tests/Info.plist`
  - `apps/macos/Sources/OpenClaw/Resources/Info.plist`
  - `docs/install/updating.md`
  - Peekaboo Xcode project and plist version fields
- Before creating a release tag, make every version location above match the version encoded by that tag.
- For fallback correction tags like `vYYYY.M.PATCH-N`, the repo version locations still stay at `YYYY.M.PATCH`.
- “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 stable OpenClaw release ships the npm package, macOS app, and signed
  Windows Hub installers together. Beta releases normally ship npm/package
  artifacts first and skip native app build/sign/notarize/promote unless the
  operator requests native beta validation.
- Do not let the slower macOS signing/notary path block npm publication once
  the npm preflight has passed. Keep mac validation/publish running in
  parallel, publish npm from the successful npm preflight, then start published
  npm install/update, Docker, and Parallels verification while mac artifacts
  continue.
- After a beta is published, overlap remote/manual release rosters where useful,
  but avoid piling local Docker, Parallels, and QA-Lab work onto the same host
  when it would create system-load noise. Use selective reruns after failures or
  fixes, but keep proof that Docker, Parallels, and QA-Lab each passed at least
  once before stable/latest promotion.
- Mac packaging may be built from a slight release-branch variation of the
  tagged commit when the delta is mac packaging, signing, workflow, or
  validation-only release machinery. If mac packaging needs release-branch-only
  fixes after the stable npm package or GitHub tag is already published, do not
  create a `vYYYY.M.PATCH-N` correction tag just to change the workflow source.
  Dispatch the release-ops mac workflows for the original `tag=vYYYY.M.PATCH`
  with `source_ref=release/YYYY.M.PATCH` and
  `public_release_branch=release/YYYY.M.PATCH`;
  provenance checks must prove the source SHA descends from the tag and
  validation/preflight use the same source. Reserve `vYYYY.M.PATCH-N` correction
  tags for emergency hotfixes that must publish a new npm package/release
  identity, not for ordinary mac-only packaging recovery.
- 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
  `appcast.xml` unless a separate beta feed exists.
- For fallback correction tags like `vYYYY.M.PATCH-N`, the repo version still stays
  at `YYYY.M.PATCH`, but the mac release must use a strictly higher numeric
  `APP_BUILD` / Sparkle build than the original release so existing installs
  see it as newer.
- Stable Windows Hub release closeout requires the signed
  `OpenClawCompanion-Setup-x64.exe`, `OpenClawCompanion-Setup-arm64.exe`, and
  `OpenClawCompanion-SHA256SUMS.txt` assets on the canonical
  `openclaw/openclaw` GitHub Release. Pass the exact signed
  `openclaw/openclaw-windows-node` release tag as `windows_node_tag` to
  `OpenClaw Release Publish`, together with the candidate-approved
  `windows_node_installer_digests` map; it prevalidates the published source
  release and required installers against that map before any publish child,
  dispatches the public `Windows Node Release` workflow while the OpenClaw
  release is still a draft, carries those pinned source asset digests
  unchanged, verifies the expected OpenClaw Foundation Authenticode signer on
  Windows, re-downloads and checksum-verifies the promoted asset contract, and
  blocks publication until the canonical asset contract is present. Use direct
  `Windows Node Release` dispatch only for recovery, always with an exact tag,
  never `latest`, and the explicit `expected_installer_digests` JSON map from
  the approved source release. Recovery rejects unexpected
  `OpenClawCompanion-*` target asset names, then replaces the expected contract
  assets with the pinned source bytes.
- Website Windows Hub download links should target exact canonical
  `openclaw/openclaw/releases/download/vYYYY.M.PATCH/...` assets for the current
  stable release, or `releases/latest/download/...` only after verifying the
  redirect resolves to that same tag, so the installable signed Windows artifact
  is visible from both the GitHub release page and openclaw.ai.

## Build changelog-backed release notes

- `CHANGELOG.md` is release-owned. Normal PRs and direct `main` fixes should
  not edit it.
- After the product-complete Code SHA passes Full Release Validation, rewrite
  the target `CHANGELOG.md` section from history, not existing notes. Use the
  last reachable stable or beta release tag as the base, then inspect every
  commit through the Code SHA. This is the one release-note mutation that
  creates the Release SHA.
- Generate `$openclaw-changelog-update`'s full contribution manifest before
  the editorial rewrite. It is the required source for `### Highlights`,
  `### Changes`, and `### Fixes`; do not preserve old grouped prose without
  comparing it to the manifest's PRs, contributors, direct commits, and
  unlinked commits.
- A same-Code-SHA retry reuses the existing changelog. A new beta containing
  code changes must first validate its replacement Code SHA, then refresh the
  same stable-base `## YYYY.M.PATCH` section once.
- Always fetch and pass current `origin/main` as the canonical main ref.
  Equivalent release/backport PRs are omitted in favor of the original merged
  main PR. A release-branch PR remains only until that change is forward-ported.
- Include both merged PR commits and direct commits on `main`. Direct commits
  matter: infer notes from their subject, body, touched files, linked issues,
  tests, and nearby code when no PR body exists.
- Keep direct commits in the generated manifest and use them to shape grouped
  user outcomes, but never dump them into `CHANGELOG.md` or GitHub release
  bodies. The public complete record is PR-first and exhaustive for PRs.
- Prefer PR bodies, issue links, review proof, and commit bodies over commit
  subjects alone. If a commit fixed an issue directly, the commit body should
  name the user-visible behavior, affected surface, issue ref, and credited
  reporter/contributor when known.
- Treat missing context as a release-note audit gap: inspect the diff and linked
  issue, draft the best accurate entry, and note the uncertainty for maintainer
  review rather than inventing impact.
- Add missed user-facing changes, remove internal-only noise, dedupe overlapping
  PR/direct-commit entries, and sort each section from most to least interesting
  for users.
- Group related highlights, changes, and fixes by user-facing surface and
  impact, but never lose traceability: each grouped bullet keeps every relevant
  `#issue`, `(#PR)`, `Fixes #...`, and every human `Thanks @...` handle.
  Multiple thanks in one bullet are expected when multiple contributor PRs are
  grouped.
- Highlights earn their place only when they are a visible capability/workflow
  unlock, a material reliability or safety repair, a broad user-facing
  improvement, or a release-defining integration/compatibility change. Keep
  five to eight user-outcome bullets; omit tests, CI, refactors, docs, and
  implementation trivia unless their outcome materially affects users.
- Do not give `docs`, `test`, `refactor`, `ci`, `build`, `chore`, or `style`
  PRs/direct commits their own Highlights, Changes, or Fixes entry. They remain
  accounted for in the PR record or manifest, but are not product release
  content. Treat explicit internal title signals such as `QA`, `lint`, or
  `testing` the same way even when the PR has no conventional prefix.
- Use the generated `### Complete contribution record` as PR-first accounting:
  every merged source PR appears once with author/co-author credit, including
  PRs identified only by an explicit active-commit `#NNN` reference after a
  cherry-pick or squash. Keep issues inline as `#NNN` in titles and grouped
  prose; do not create a linked-issues inventory or a direct-commit listing.
  When grouped prose names a PR, keep every contributor and linked-reporter
  credit from that PR's record on the same bullet.
- Changelog entries should be user-facing, not internal release-process notes.
- GitHub release and prerelease bodies use
  `scripts/render-github-release-notes.mjs`. When the full matching
  `CHANGELOG.md` version section fits GitHub's 125,000-character limit and
  the renderer's matching 125,000-byte safety ceiling, publish the exact
  `## YYYY.M.PATCH` block through the line before the next level-2 heading,
  including the version heading.
- When that complete body exceeds either limit, keep the exact grouped
  editorial notes through the line before `### Complete contribution record`,
  then replace the oversized record with the canonical tag-pinned
  `CHANGELOG.md` link emitted by the renderer. Never truncate bullets or emit a
  partial contribution record. Candidate validation, publish, and
  `verify-release-notes.mjs` must share this renderer so the compact form cannot
  drift.
- Choose the full or compact changelog body before adding
  `### Release verification`. Append that proof only when the final body still
  fits; otherwise leave the immutable evidence assets attached and omit the
  body tail. Do not discard a fitting full contribution record to make room
  for proof.
- Before publishing or closing a release, run
  `$openclaw-changelog-update`'s `verify-release-notes.mjs` with every stable
  and beta release tag in the train. Do not publish or leave a page live when
  it is missing a source-history reference, eligible human credit, or the
  complete matching changelog body.
- Treat the selected `--base` as a strict history boundary: it must be an
  ancestor of the target, and existing changelog prose or contribution rows
  cannot pull older PRs into the new release. Use `--seed-ref` only for an
  intentional historical backfill. When a divergent prior release tag or later
  forward-port re-associates already-shipped PRs, pass repeatable explicit
  `--shipped-ref <tag>` values. They subtract only explicit PR rows in complete
  contribution records from numbered sections of those tag snapshots, ignore
  `Unreleased`, and retain the exact excluded PR inventory and count in
  manifest/provenance for candidate checks.
- To update an existing GitHub Release body, resolve the numeric release id and
  patch that resource with the notes file as the `body` field:
  `gh api repos/openclaw/openclaw/releases/tags/vYYYY.M.PATCH --jq .id`, then
  `gh api -X PATCH repos/openclaw/openclaw/releases/<id> -F body=@/tmp/notes.md`.
  Do not trust `gh release edit --notes-file` or `--input` JSON if verification
  disagrees; verify with `gh api repos/openclaw/openclaw/releases/<id>` because
  the tag lookup and `gh release view` can lag or show stale body text.
- When preparing release notes, scan `src/plugins/compat/registry.ts` and
  `src/commands/doctor/shared/deprecation-compat.ts` for compatibility records
  with `warningStarts` or `removeAfter` within 7 days after the release date.
  Add an `Upcoming deprecations` note to the release notes when any exist,
  including the compatibility code, target date, replacement, and a link to the
  record's `docsPath` or `/plugins/compatibility` when no more specific
  deprecation page exists.
- When cutting a mac release with a beta GitHub prerelease:
  - tag `vYYYY.M.PATCH-beta.N` from the release commit
  - create a prerelease titled `openclaw YYYY.M.PATCH-beta.N`
  - use release notes from the stable base `CHANGELOG.md` version section
    (`## YYYY.M.PATCH`), not a beta-specific heading
  - attach at least the zip and dSYM zip, plus dmg if available
- Keep the top version entries in `CHANGELOG.md` sorted by impact:
  - `### Changes` first
  - `### Fixes` deduped with user-facing fixes first

## Write release tweets

Use the OpenClaw account's existing release-post style:

- Format: `OpenClaw YYYY.M.PATCH 🦞` or `🦞 OpenClaw YYYY.M.PATCH is live`, blank line,
  then 3-4 emoji-led bullets, blank line, one short punchline, then the release
  link.
- For beta: say `OpenClaw YYYY.M.PATCH-beta.N 🦞` or `OpenClaw YYYY.M.PATCH beta N is
live`; keep it clearly beta and avoid implying stable promotion.
- Lead with user-visible capabilities, then important integrations, then
  reliability/security/install fixes. Compress "lots of fixes" into one
  readable bullet.
- Read the full changelog section before drafting. Do not lead with coverage,
  CI, validation, or internal release mechanics unless the release is explicitly
  about those. Peter prefers concrete user wins: features, integrations,
  workflow improvements, and practical reliability fixes.
- Do not feature QA parity, test coverage, release gates, or validation lanes in
  user-facing launch tweets. Keep them for release notes or maintainer proof
  unless the operator explicitly asks for validation-focused copy.
- Do not feature plugin-author or developer tooling such as SDK helpers,
  tool-plugin scaffolding, build/validate/init commands, or internal CLI
  plumbing in general user-facing launch tweets unless the operator explicitly
  asks for developer-focused copy.
- Tone: high-signal, slightly cheeky, confident, not corporate. One joke is
  enough. Avoid punching down, insulting users, or promising what was not
  verified.
- Peter likes dry, compact taglines when they feel earned. Good example:
  `Big release, tiny release notes... kidding.` Keep the joke short and let the
  feature bullets carry the tweet; do not turn the punchline into a second
  paragraph or a forced bit.
- Length: release tweets are always standard tweets under 280 characters, with
  room for one URL. Trim to 3-4 bullets and count the final text before posting.
- Links/media: include the GitHub release or changelog link at the end of the
  first release tweet.
- Thread follow-ups: if doing a thread, keep the first release tweet as the
  compact launch post, then publish one focused feature explainer per reply.
  Follow-up replies should not repeat "new in VERSION" or the version number
  when the thread context already makes it obvious.
- Peter's preferred thread workflow: first agree on the generic launch tweet,
  then proceed through follow-up tweets one by one. When he says `next`, provide
  or copy the next follow-up only; do not dump the full thread again unless asked.
- Every follow-up tweet should include a docs URL for that specific feature.
  Prefer a bare URL over `Docs: <url>` unless the label is needed for clarity.
  Keep follow-ups concise: around 160-220 raw characters is usually the sweet
  spot; under 280 is the hard cap. If a URL makes a tweet fail, trim prose
  before dropping the URL.
  Prefer explaining diagnostics, trajectory/export, provider setup, model
  commands, or other setup-heavy features in follow-ups instead of overloading
  the first release tweet.
- Hotfix/correction: be direct and accountable. State what slipped, what is
  fixed, and the new version. Keep jokes out of incident-style posts.

Examples to adapt:

```text
OpenClaw 2026.4.20-beta.1 🦞

🐳 Docker install/update smoke
🖥️ Parallels upgrade checks
🔧 Package verification tightened

Beta first. Stable after the gauntlet.
<release link>
```

```text
OpenClaw 2026.4.20 🦞

🚀 Faster install + update
🐳 Docker + Parallels verified
🍎 macOS signed + notarized
🔧 Channel/plugin fixes

Good boring release. Best kind.
<release link>
```

```text
Packaging issue in 2026.4.20-beta.1.

2026.4.20-beta.2 fixes install/update verification. No tag rewrites; beta moves
forward.

Upgrade with the beta channel.
<release link>
```

## Run publish-time validation

Before tagging or publishing, run:

```bash
pnpm release:fast-pretag-check
pnpm check:architecture
pnpm build
pnpm ui:build
pnpm qa:otel:smoke
pnpm release:check
pnpm test:install:smoke
```

- Treat `pnpm release:fast-pretag-check` as a hard packaging gate. Every
  publishable plugin must have a non-empty package-root `README.md`, build its
  package-local runtime, and pass the npm and ClawHub release metadata checks
  before a tag or publish workflow can start. Do not defer README, entrypoint,
  or packed-artifact failures to postpublish verification.
- Before generating the changelog, require green CI for the exact Code SHA, not
  an earlier branch SHA. Heal every related red CI, release-check, packaging,
  or root-Dockerfile lane on the release branch and rerun the affected exact-SHA
  gates. Defer product-fix forward-ports until after publication unless the
  active release scope lock requires an exact main-owned blocker fix. Never
  waive a red Docker lane because npm preflight passed.
- Root Dockerfile proof is mandatory on the Code SHA before every beta and
  stable tag. The changelog-only Release SHA reuses that product proof, while
  exact Release SHA npm preflight and package/install acceptance prove the
  changed package bytes. The tag-triggered Docker Release workflow is
  post-tag publishing, not the first valid proof that the root Dockerfile can
  build.
- Before tagging, diff publishable plugin package manifests against the last
  reachable stable/beta release tag. For every newly publishable package
  (`openclaw.release.publishToNpm: true` or `publishToClawHub: true`) whose
  package name did not exist in the base tag, verify the target registry package
  already exists in npm/ClawHub or s

…(truncated)
