.agents/skills/openclaw-release-maintainer/SKILL.md
Use this skill for release and publish-time workflow. Keep ordinary development changes and GHSA-specific advisory work outside this skill.
docs/reference/RELEASING.md for public policy.openclaw publish is manual workflow_dispatch; creating or pushing a tag does not publish by itself.main, not directly on
main. Use release/YYYY.M.D for the branch name.main and confirm current main CI is
green. Then branch from that commit so regular development can continue on
main while release validation runs./changelog on main and commit/push/pull that
changelog rewrite immediately before creating the release branch.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.src/commands/doctor/shared/deprecation-compat.ts until maintainers confirm
the repair is no longer needed.-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.beta, then run the expensive published-package roster focused
on install/update/Docker/Parallels/NPM Telegram. If anything fails, fix it on
the release branch, commit/push/pull, increment beta number, and repeat. Run
the full expensive roster at least once before stable/latest promotion; for
later beta attempts, rerun only lanes whose evidence changed unless the fix
touches broad release, install/update, plugin, Docker, Parallels, or live QA
behavior. After each beta is published, scan current main once for critical
fixes that landed after the release branch cut and backport only important
low-risk fixes. Operators may authorize up to 4 autonomous beta attempts;
after 4 failed beta attempts, stop and report./changelog before version/tag preparation so the top changelog section
is deduped and ordered by user impact.CHANGELOG.md headings. Beta releases use the
stable base version section, for example v2026.4.20-beta.1 uses
## 2026.4.20 release notes..profile; do not block or roll
back the release if the announcement fails.~/Projects/bird/bird and follow the
release tweet style below.stable: tagged releases only, published to npm beta by default; operators may target npm latest explicitly or promote laterbeta: prerelease tags like vYYYY.M.D-beta.N, with npm dist-tag beta-beta.N; do not mint new -1 or -2 beta suffixesdev: moving head on mainpackage.jsonapps/android/app/build.gradle.ktsapps/ios/Sources/Info.plistapps/ios/Tests/Info.plistapps/macos/Sources/OpenClaw/Resources/Info.plistdocs/install/updating.mdvYYYY.M.D-N, the repo version locations still stay at YYYY.M.D.appcast.xml.vYYYY.M.D-N correction tag just to change the workflow source.
Dispatch the private mac workflows for the original tag=vYYYY.M.D with
source_ref=release/YYYY.M.D and public_release_branch=release/YYYY.M.D;
provenance checks must prove the source SHA descends from the tag and
validation/preflight use the same source. Reserve vYYYY.M.D-N correction
tags for emergency hotfixes that must publish a new npm package/release
identity, not for ordinary mac-only packaging recovery.https://raw.githubusercontent.com/openclaw/openclaw/main/appcast.xml, and the canonical published file is appcast.xml on main in the openclaw repo.appcast.xml unless a separate beta feed exists.vYYYY.M.D-N, the repo version still stays
at YYYY.M.D, 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.CHANGELOG.md
section from commit history, not just from existing notes: scan commits since
the last reachable release tag, add missed user-facing changes, dedupe
overlapping entries, and sort each section from most to least interesting for
users.CHANGELOG.md version section, not highlights or an excerpt. When creating
or editing a release, extract from ## YYYY.M.D through the line before the
next level-2 heading and use that complete block as the release notes.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.vYYYY.M.D-beta.N from the release commitopenclaw YYYY.M.D-beta.NCHANGELOG.md version section
(## YYYY.M.D), not a beta-specific headingCHANGELOG.md sorted by impact:
### Changes first### Fixes deduped with user-facing fixes firstUse the OpenClaw account's existing release-post style:
OpenClaw YYYY.M.D 🦞 or 🦞 OpenClaw YYYY.M.D is live, blank line,
then 3-4 emoji-led bullets, blank line, one short punchline, then the release
link.OpenClaw YYYY.M.D-beta.N 🦞 or OpenClaw YYYY.M.D beta N is live; keep it clearly beta and avoid implying stable promotion.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.next, provide
or copy the next follow-up only; do not dump the full thread again unless asked.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.Examples to adapt:
OpenClaw 2026.4.20-beta.1 🦞
🐳 Docker install/update smoke
🖥️ Parallels upgrade checks
🔧 Package verification tightened
Beta first. Stable after the gauntlet.
<release link>
OpenClaw 2026.4.20 🦞
🚀 Faster install + update
🐳 Docker + Parallels verified
🍎 macOS signed + notarized
🔧 Channel/plugin fixes
Good boring release. Best kind.
<release link>
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>
Before tagging or publishing, run:
pnpm check:architecture
pnpm build
pnpm ui:build
pnpm qa:otel:smoke
pnpm release:check
pnpm test:install:smoke
pnpm qa:otel:smoke when release validation needs telemetry coverage.
It starts a local OTLP/HTTP trace receiver, runs QA-lab's
otel-trace-smoke, and checks span names plus content/identifier redaction
without external Opik or Langfuse credentials.For a non-root smoke path:
OPENCLAW_INSTALL_SMOKE_SKIP_NONROOT=1 pnpm test:install:smoke
After npm publish, run:
node --import tsx scripts/openclaw-npm-postpublish-verify.ts <published-version>
YYYY.M.D-N, it also verifies the
upgrade path from YYYY.M.D to YYYY.M.D-N so a correction publish cannot
silently leave existing global installs on the old base stable payload.pnpm test:install:smoke
now fails the candidate update tarball when npm reports an oversized
unpackedSize, so release-time e2e cannot miss pack bloat that would risk
low-memory install/startup failures.npm install -g <candidate> fresh installs and npm-driven update installs,
because many users install with npm even when docs prefer pnpm.pnpm test:live:media video for bounded video-provider smoke when video
generation is in release scope. The default video smoke skips fal, runs one
text-to-video attempt per provider with a one-second lobster prompt, and caps
each provider operation with OPENCLAW_LIVE_VIDEO_GENERATION_TIMEOUT_MS
(180000 by default).pnpm test:live:media video --video-providers fal only when FAL-specific
proof is required. Its queue latency can dominate release time.OPENCLAW_LIVE_VIDEO_GENERATION_FULL_MODES=1 only when intentionally
validating the slower image-to-video and video-to-video transform lanes.set -a; source "$HOME/.profile"; set +a.OPENAI_API_KEY and ANTHROPIC_API_KEY. If either is missing after sourcing
.profile, stop before starting those local long lanes and report the
missing key.QA-Lab - All Lanes (.github/workflows/qa-live-telegram-convex.yml), not a
local substitute. Dispatch it from Actions against the release tag and wait
for it to pass before npm preflight/publish readiness. Use a SHA only when it
satisfies the workflow's secret-bearing trust gate: main ancestor or open PR
head. It runs the QA Lab mock parity gate plus live Matrix and live Telegram
lanes using the qa-live-shared environment; Telegram uses Convex CI
credential leases.pnpm checkpnpm check:test-typespnpm check:architecturepnpm buildpnpm ui:buildpnpm release:checkOPENCLAW_INSTALL_SMOKE_SKIP_NONROOT=1 pnpm test:install:smokepnpm test:docker:all, plus standalone Docker live lanes
not covered by the aggregate when operator says "all docker tests":
pnpm test:docker:live-acp-bind, pnpm test:docker:live-cli-backend, and
pnpm test:docker:live-codex-harnesspnpm test:parallels:npm-update -- --json plus any needed individual
rerun lanes from openclaw-parallels-smokeQA-Lab - All Lanes
against the release tag and require success. This is the release gate for
live credentialed Matrix/Telegram channel coverage. Use a SHA only when it
satisfies the workflow trust gate. Run local OpenAI/Anthropic suites or
repo-backed character evals only when the operator asks for extra model
coverage or a failure needs local debugging.node --import tsx scripts/openclaw-npm-postpublish-verify.ts <beta-version>NPM Telegram Beta E2E
from main with package_spec=openclaw@<beta-version> and
provider_mode=mock-openai, and require success. This workflow is
maintainer-dispatched and intentionally has no npm-release approval gate;
qa-live-shared only supplies the shared QA secrets. This is the default
button path for installed-package onboarding, Telegram setup, and real
Telegram E2E against the published npm package.
Use the local pnpm test:docker:npm-telegram-live lane with the matching
OPENCLAW_NPM_TELEGRAM_PACKAGE_SPEC and Convex CI env only as a fallback
or debugging path.QA-Lab - All Lanes.timeout --foreground/gtimeout --foreground caps such as:
45m for OPENCLAW_INSTALL_SMOKE_SKIP_NONROOT=1 pnpm test:install:smoke90m for pnpm test:docker:all60m each for standalone Docker live lanes180m for local full QA live OpenAI + Anthropic rosters when explicitly
requested; the default release channel QA gate is Actions >
QA-Lab - All Lanesopenclaw-parallels-smoke skill
If a lane hits its cap, stop and inspect/fix the affected lane before continuing; do not continue to wait on the same process.npm install -g, installer package install, or openclaw update takes longer than 300s in release e2e, stop treating the run as healthy progress and debug the installer/updater or harness.pnpm build, pnpm ui:build, pnpm release:check, install smoke, and any Docker/package-prep lanes before starting Parallels npm pack lanes; otherwise dist can disappear during VM pack prep and produce false failures.openclaw/openclaw and the real mac preflight in
openclaw/releases-private for every release.appcast.xml update on main as part of mac release readiness, not an optional follow-up.appcast.xml before uploading
public release assets so the updater feed cannot lag the published binaries.appcast.xml files from the same stale seed.latest, prefer a light time-bounded verification pass: published npm
postpublish verify, Docker install/update smoke, macOS-only Parallels
install/update smoke, and required QA signal. Do not rerun the full
Docker/Parallels matrix unless the beta evidence is stale, the stable build
differs materially from beta, or the operator explicitly asks for full
retesting.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.NPM_TOKEN secret, scoped to the openclaw package with read/write
and 2FA bypass for automation.NPM_TOKEN is absent
or stale, use the local tmux + 1Password fallback:
npm login and OTP prompts
are observable and recoverable.op directly in the main agent shell during release
work. Any 1Password CLI use must happen inside that tmux session so prompts
and alerts are contained and observable.op://Private/Npmjs for npm credentials and OTP.
Do not print passwords, tokens, or OTPs to the transcript; send them through
tmux buffers, env vars scoped to the tmux command, or expect with
log_user 0.npm login --auth-type=legacy, then confirm npm whoami reports
steipete.npm dist-tag add [email protected] latest --otp "$OTP".npm view openclaw dist-tags --json --prefer-online --cache /tmp/openclaw-npm-cache-verify-$$
and npm view openclaw@latest version dist.tarball --json --prefer-online.beta at the already-published latest version when the operator wants both
tags aligned immediately.workflow_dispatch.preflight_only=true to run validation/build/package steps without uploading
public release assets.smoke_test_only=true for branch-safe
workflow smoke tests that use ad-hoc signing, skip notarization, skip shared
appcast generation, and do not prove release readiness.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.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
release validation, but it does not sign, notarize, or publish macOS
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 stable mac publish run starts.openclaw/releases-private/.github/workflows/openclaw-macos-publish.yml.appcast.xml generation.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.npm-release environment must be approved by @openclaw/openclaw-release-managers before publish continues.openclaw/releases-private/.github/workflows/openclaw-macos-publish.yml for
private mac preflight artifact preparation and real publish artifact
promotion..zip, .dmg, and
.dSYM.zip assets to the existing GitHub release in openclaw/openclaw
automatically when OPENCLAW_PUBLIC_REPO_RELEASE_TOKEN is present in the
private repo mac-release environment.macos-appcast-<tag> artifact from the successful private mac workflow and
then update appcast.xml on main.appcast.xml
unless a separate beta Sparkle feed exists.mac-release environment. If the GitHub
plan does not yet support required reviewers there, do not assume the
environment alone is the approval boundary; rely on private repo access and
CODEOWNERS until those settings can be enabled.NPM_TOKEN or the plugin OTP flow for the OpenClaw package
publish path; package publishing uses trusted publishing.NPM_TOKEN only for explicit npm dist-tag management modes, because npm
does not support trusted publishing for npm dist-tag add.@openclaw/* plugin publishes use a separate maintainer-only flow.scripts/package-mac-dist.sh to build, sign, notarize, and package the app;
manual GitHub release asset upload; then scripts/make_appcast.sh plus the
appcast.xml commit to main.scripts/package-mac-dist.sh now fails closed for release builds if the
bundled app comes out with a debug bundle id, an empty Sparkle feed URL, or a
CFBundleVersion below the canonical Sparkle build floor for that short
version. For correction tags, set a higher explicit APP_BUILD.scripts/make_appcast.sh first uses generate_appcast from PATH, then
falls back to the SwiftPM Sparkle tool output under apps/macos/.build.appcast.xml.appcast.xml unless a separate beta feed exists..zip, .dmg, and .dSYM.zip assetsappcast.xml on main points at the new stable zipCFBundleVersion at or above the canonical Sparkle build floormain and confirm current main CI is green./changelog for the stable base target version on main, commit the
changelog rewrite immediately, push, and pull/rebase. For beta releases,
keep the changelog heading as ## YYYY.M.D, not ## YYYY.M.D-beta.N.release/YYYY.M.D from that post-changelog main commit.QA-Lab - All Lanes against the release tag and wait
for the mock parity, live Matrix, and live Telegram credentialed-channel
lanes to pass..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..github/workflows/macos-release.yml in
openclaw/openclaw and wait for the public validation-only run to pass.openclaw/releases-private/.github/workflows/openclaw-macos-validate.yml
with the same tag and wait for the private mac validation lane to pass.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..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.npm-release approval from @openclaw/openclaw-release-managers.node --import tsx scripts/openclaw-npm-postpublish-verify.ts <published-version>.main
for critical fixes that landed after the release branch cut; backport only
important low-risk fixes before starting expensive lanes, or increment to
the next beta if the fix must change the already-published package. If any
lane fails after the beta package is published, fix, commit/push/pull,
increment to the next beta tag, and rerun the affected beta evidence. Once
the beta is live, start remote/manual rosters where they
can overlap safely, but keep local Docker and Parallels load controlled.
Ensure the full expensive roster has passed at least once before
stable/latest promotion. The roster includes the manual Actions >
NPM Telegram Beta E2E workflow against the exact published beta package.
If a pre-npm lane fails before any tag/package leaves the machine, fix and
rerun the same intended beta attempt. Repeat up to the operator's
authorized beta-attempt limit, normally 4..profile.beta, use the light stable
promotion roster when the matching beta already carried the full confidence
pass: published npm postpublish verify, Docker install/update smoke,
macOS-only Parallels install/update smoke, and required QA signal.
Then start the private
openclaw/releases-private/.github/workflows/openclaw-npm-dist-tags.yml
workflow to promote that stable version from beta to latest, then
verify latest now points at that version.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.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..zip, .dmg,
and .dSYM.zip artifacts to the existing GitHub release in
openclaw/openclaw.macos-appcast-<tag> 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.appcast.xml artifact and do not update the shared production feed unless a
separate beta feed exists.openclaw-ghsa-maintainer for GHSA advisory inspection, patch/publish flow, private-fork validation, and GHSA API-specific publish checks.