Release automation and evidence ledger for OpenClaw.
The source of truth stays in openclaw/openclaw:
- source code
- git tags
- GitHub releases
- npm publish workflow
appcast.xmlonmain
This repo keeps release packaging, macOS publication support, npm dist-tag maintenance, and durable release evidence separate from the product source repo.
.github/workflows/ci.ymlchecks the repository scripts, runs workflow regression tests, and verifies package-manager setup plus cache/artifact round trips on Linux and macOS for pull requests and pushes tomain. It uses a read-only repository token and temporary fixtures; it does not publish releases or update the evidence ledger..github/workflows/openclaw-macos-validate.ymlruns the release-blocking macOS Swift test lane for an existing OpenClaw tag..github/workflows/openclaw-macos-publish.ymlprepares and promotes signed macOS release artifacts for an existing OpenClaw tag..github/workflows/openclaw-npm-dist-tags.ymlpromotes or syncs npmlatestfor OpenClaw and enforces the beta floor: after every successfullatestmutation, on manualsync_beta_to_stabledispatch, and daily as a backstop,betaforopenclaw, every published core package (such as@openclaw/ai,@openclaw/gateway-client, and@openclaw/gateway-protocol), and every published official plugin is advanced to at least its ownlatest; an equal or newerbetais preserved. The package inventory is read as data fromopenclaw/openclawmain: its core npm package policy (scripts/lib/npm-core-release-packages.json) and package manifests. Its manualpromote_extended_stablemode promotes an already-published core version toextended-stable, without changing other selectors..github/workflows/openclaw-release-evidence.ymlrecords manually supplied release proof runs..github/workflows/openclaw-release-evidence-from-full-validation.ymlingests child runs from the publicFull Release Validationworkflow.
The macOS publish workflow builds from public openclaw/openclaw tags and uses
the public repo's packaging scripts. Real publish runs promote previously
prepared artifacts rather than rebuilding during the final upload step. Stable
appcasts must match the packaged app's version and build, release ZIP URL and
byte length, and contain a Sparkle signature before retention and promotion.
An existing seed feed alone is not valid release output. The preflight uses
pnpm release:check to build and validate package contents once before metadata
validation; native app packaging retains its own matching runtime build.
Promotion attaches assets to the matching GitHub release whether it is still a draft or already public. The core npm publisher flips it public.
After the exact final release has passed stable/full validation, including soak
and blocking performance, promote its existing npm package from beta to
latest:
gh workflow run openclaw-npm-dist-tags.yml --repo openclaw/releases --ref main \
-f mode=promote_beta_to_latest -f tag=vYYYY.M.PATCHThe operator must verify the successful validation evidence before dispatch.
This action checks the public Git tag, published npm version, and current beta
selector; it does not run or authenticate stable validation. Dispatch does not
waive failed checks, soak, or performance requirements. Follow the
source repository release procedure
for qualification.
The target must be a final regular version with patch below 33, optionally
with a correction suffix. A -beta.N version cannot be promoted with this
action; prepare and qualify its final release version first. Promotion reuses
the published package without rebuilding or republishing it. After latest
changes, the beta-floor job runs for core packages and official plugins, preserving any
newer beta.
For selector recovery after qualification, mode=sync_stable_dist_tags accepts
an exact already-published final version without requiring beta to point at it.
The same operator validation prerequisite applies. Neither mode changes the
source repository's validation policy or restores publication waivers.
In Actions → OpenClaw NPM Dist-Tag Operations → Run workflow, choose
branch main, mode promote_extended_stable, and the exact public release tag.
Replace vYYYY.M.PATCH below with that tag:
gh workflow run openclaw-npm-dist-tags.yml --repo openclaw/releases --ref main \
-f mode=promote_extended_stable -f tag=vYYYY.M.PATCHThe action verifies that the Git tag exists in openclaw/openclaw and that the
exact openclaw version is already published on the public npm registry. It
supports both newer and older published extended-stable final versions with patch
33 or higher and no suffix. Extended-stable fixes increment the patch (33,
34, 35, and so on); they do not use correction suffixes. Regular stable/beta
promotion and sync reject patch 33 or higher, including the scheduled beta floor.
Choosing an older version performs a rollback through the same promotion action. Prereleases and floating selectors are rejected; new-publication
eligibility rules do not apply to an already-published target; the channel/patch
boundary still applies.
It uses this repository's existing NPM_TOKEN, writes only
openclaw@<version>'s extended-stable dist-tag, and records the previous and
requested versions in the run summary. An already-correct selector is a no-op.
Readback retries allow five minutes of wait time; the write is never retried
automatically. If the write is unconfirmed or readback fails, inspect the actual
registry state before another dispatch—do not republish the package.
This does not change npm latest, beta, plugin selectors, Git tags,
GitHub Releases, or Docker images. It does not invoke the beta-floor job. Docker
channel promotion remains the separate docker-channel-promote.yml workflow in
openclaw/openclaw. Normal workflow dispatch permissions and the main-only
execution guard apply; no new publication or credentials are required.
After freezing the final signed source commit on release/YYYY.M.PATCH, start
signing/notarization and Swift validation concurrently from this repository's
trusted main. Neither run creates a tag or publishes assets:
gh workflow run openclaw-macos-publish.yml --repo openclaw/releases --ref main \
-f tag=vYYYY.M.PATCH -f pretag_source_sha=<exact-signed-final-source-sha> \
-f preflight_only=true -f smoke_test_only=false \
-f public_release_branch=release/YYYY.M.PATCH
gh workflow run openclaw-macos-validate.yml --repo openclaw/releases --ref main \
-f tag=vYYYY.M.PATCH -f pretag_source_sha=<exact-signed-final-source-sha>Pretag preparation supports stable versions. It requires the exact canonical
release branch head, valid GitHub commit signature verification, and matching
package identity before checking out or executing public source. It rejects
source_ref, smoke mode, notarization recovery, and publication. A tag created
while the run queues must point at the same commit. Normal tagged recovery and
promotion keep their existing provenance checks.
The app embeds its real source SHA in signed metadata. A later CHANGELOG-only commit is therefore not equivalent for artifact promotion: finalize the changelog before starting, or rerun the producer to rebuild, sign, and notarize the new final source. Never relabel previously signed bytes. When the eventual tag selects the unchanged pretag SHA, use the successful preflight and validation run IDs in ordinary promotion after the matching GitHub release exists (draft or public).
A new signed preflight from main for the same tag and source automatically
resumes the newest resumable checkpoint per variant. Discovery uses exact
macos-resume-<tag>-<variant>-<sha> index artifacts from failed or cancelled
main dispatches of this workflow. Successful preflights are promoted, not
resumed. Set ignore_checkpoints=true to build fresh instead of discovering
checkpoints. Smoke, pretag, non-main, and promotion runs skip discovery.
For sources supporting package-mac-dist.sh --checkpoint-only, Re-run
failed jobs also works (gh run rerun <run-id> --failed --repo openclaw/releases).
The build_and_sign matrix builds, signs, audits, and retains the app, symbols,
and signed DMG before contacting Apple. The separate notarize_and_package
matrix verifies that checkpoint, notarizes and staples the retained bytes, and
produces the preflight artifacts and appcast. A failed notarization job can be
rerun without compiling or importing the Developer ID key again.
Rerunning one consumer also reruns the collector. Final aggregate artifacts are replaced after their completeness and provenance checks; build checkpoints remain immutable.
The immutable build inputs use macos-signed-<tag><variant-suffix>-<run-id>-<attempt>
artifacts with the same recovery checkpoint format. Each variant records its
actual build attempt and exact source SHA, so a later workflow attempt consumes
the original successful build, even if the source branch has advanced. Recovery
output is retained separately; it never overwrites the build input. Both have
30-day retention. Sources without --checkpoint-only keep the single-job
packaging path and can resume retained checkpoints on a new preflight.
Signed preflights retain a macos-notarization-<tag>-<run-id>-<attempt> Actions
artifact for universal builds and macos-notarization-<tag>-<variant>-<run-id>-<attempt>
for arm64 and x86_64 when packaging produces a valid recovery checkpoint. Each
contains the signed app, symbols, available DMG, Apple submission records, and
the producer's Sparkle tools. Private signing keys are never included. Retention
is 30 days; keep these payloads in Actions rather than the evidence ledger.
Explicit resume_notarization_* inputs pin a specific run and attempt for the
selected variants, even with ignore_checkpoints=true. Checkpoints created
before resume indexes were added are reachable only through these explicit
inputs. Use the checkpoint's exact public source commit and failed run attempt:
gh workflow run openclaw-macos-publish.yml --repo openclaw/releases --ref main \
-f tag=vYYYY.M.PATCH -f source_ref=<checkpoint-source-sha> \
-f preflight_only=true -f smoke_test_only=false \
-f resume_notarization_run_id=<failed-run-id> \
-f resume_notarization_run_attempt=<failed-run-attempt> \
-f public_release_branch=release/YYYY.M.PATCHThe default resume_notarization_variant=universal preserves existing recovery
commands. Set it to arm64 or x86_64 to resume that variant, or all to resume
all three checkpoints from the same run and attempt. Unselected variants use
automatic discovery, then build if none match. Each pinned variant requires its
exact checkpoint; a missing or expired checkpoint fails that job without
rebuilding. Use all when all three checkpoints exist to avoid rebuilding any variant.
Recovery uses the same release authorization and mac-release environment. It verifies the producer,
release/source binding, checkpoint hashes, the app's variant feed and signing
identity, then resumes Apple submissions without rebuilding the selected app.
Selected recovery variants bypass build_and_sign and feed the notarization
job directly. New checkpoints always include the signed DMG. Older source-bound
checkpoints may need the original packager to create and sign a missing DMG;
only that legacy recovery path imports the Developer ID key in the notarization
job. The original Sparkle tools generate the final appcast.
Use the successful recovery run as preflight_run_id for ordinary promotion,
together with the successful validation run for the same source. Recovery does
not replace validation or allow direct promotion from a failed run. Once selected,
a checkpoint must pass every recovery check; missing/expired payloads, unsupported
recovery interfaces, or changed source commits fail without rebuilding. Discovery
builds fresh when no valid index and live checkpoint match; API failures stop
preparation.
The scripts use only Node.js built-ins and the Python standard library; no dependency installation or build step is needed. Run local checks with Node.js 24 and Python 3:
node --check scripts/openclaw-release-evidence.mjs
node --check scripts/openclaw-release-evidence-from-full-validation.mjs
node --check scripts/release-npm-beta-floor.mjs
PYTHONDONTWRITEBYTECODE=1 python3 -m unittest discover -s tests -v
node --test 'tests/*.test.mjs'An explicit request to release OpenClaw authorizes the macOS release through validation, signing, notarization, and promotion. Do not ask for a separate macOS approval after the release has already been authorized.
- real macOS preflight and promotion jobs must use
mac-release mac-releasehas no required reviewers or wait timer; the authorized operator dispatches the release workflows- the environment permits deployments only from this repo's
mainbranch - successful validation and signed preflight artifacts for the exact tag and source remain required before promotion
- read-only maintainers can inspect runs; repository permissions control who can dispatch them
Keep signing and promotion secrets in mac-release, with its main-only branch
policy intact. Do not approve a job on someone else's behalf or use an alternate
signing path to bypass an enforced rule. Organization owners manage the
environment policy; changes to that policy require explicit owner direction.
The evidence workflows write release summaries under evidence/<release-id>/.
Each evidence directory contains:
release-evidence.mdrelease-evidence.jsonindex.jsonruns/<label>.json
Evidence records include release ref provenance, npm package metadata, run URLs, workflow names, refs, SHAs, pass/fail state, timing summaries, artifact names, artifact sizes, and selected release performance summaries.
Evidence records do not store raw logs, provider payloads, live-channel transcripts, signing material, credentials, environment dumps, or downloaded release artifacts.
Both evidence workflows publish with checkout-managed ephemeral GITHUB_TOKEN
credentials and contents:write; they do not require a persistent push PAT.
Both writers run from main, share a per-release concurrency group, and verify
that the complete generated evidence directory is byte-identical on origin/main.
Unrelated concurrent commits can be rebased; changes within the same evidence
directory fail with a request to regenerate from current main. Even an unchanged
local record is checked against fresh remote state before reporting success.
Evidence commits do not trigger push-triggered Actions workflows. Auth repair
verification must use a new, clearly labeled verification record because
regenerating an existing release ID overwrites its stored evidence.
Manual evidence input format:
<label> <owner/repo> <run-id> <blocking|advisory>
Example:
full-release-validation openclaw/openclaw 24972498713 advisory
normal-ci openclaw/openclaw 24972500000 blocking
release-checks openclaw/openclaw 24972511111 blocking
Recommended labels:
full-release-validation
normal-ci
release-checks
plugin-prerelease
product-performance
macos-validate
macos-preflight
macos-publish
npm-dist-tags
Mark a run as blocking when a release should not proceed without it passing.
Mark a run as advisory when it informed the release decision but should not
fail the release by itself.
OpenClaw Release Evidence From Full Validation takes a completed
openclaw/openclaw full-validation run id, reads that parent run's logs,
extracts child run ids, and writes the same evidence directory shape.
Manual ingest example:
gh workflow run openclaw-release-evidence-from-full-validation.yml \
--repo openclaw/releases \
--ref main \
-f full_validation_run_id=24977011361 \
-f release_id=2026.4.24 \
-f release_ref=v2026.4.24 \
-f package_spec=openclaw@2026.4.24Store only release summaries, normalized run metadata, artifact metadata, timing summaries, package specs, and short release-manager notes here.
Do not commit:
- raw logs
- provider prompts or responses
- Matrix, Telegram, Discord, or other live-channel transcripts
- signing material, certificates, notarization credentials, or Sparkle keys
- token-bearing npm, GitHub, Apple, channel, or provider config
- downloaded release artifacts,
.zip,.dmg,.tgz, or dSYM payloads - secret-bearing environment dumps
Raw logs and bulky proof artifacts belong in GitHub Actions retention, external artifact storage, or the public GitHub release when they are intended for users.
