Release Checklist (npm + macOS)
Usepnpm (Node 24+) from the repo root. Keep the working tree clean before tagging/publishing.
Operator trigger
When the operator says “release”, immediately do this preflight (no extra questions unless blocked):- Read this doc and
docs/platforms/mac/release.md. - Load env from
~/.profileand confirmSPARKLE_PRIVATE_KEY_FILE+ App Store Connect vars are set (SPARKLE_PRIVATE_KEY_FILE should live in~/.profile). - Use Sparkle keys from
~/Library/CloudStorage/Dropbox/Backup/Sparkleif needed.
- Version & metadata
package.json is the single source of truth and release-please
owns the bump. Derived version files are synchronized by pnpm version:sync in CI and by the
release workflow.
- Confirm the release-please PR contains the intended
package.jsonversion bump. - Confirm the release-please sync commit ran
pnpm version:syncand updated derived version files. - Confirm extension package versions match the root version when they are part of the workspace release.
- Do not edit
docs/install/updating.mdfor a pinned npm version; it intentionally installswednesdayai@latest. - Peekaboo and Swabble version independently; bump them only when that specific release requires it.
appcast.xml version here — that is updated in step 5 (macOS Sparkle release).
- Confirm package metadata: root package name is
wednesdayai,binexposes bothwednesdayaiand the compatibilityopenclawcommand, and WhatsApp package name is@wednesdayai/whatsapp. - Confirm
secrets.NPM_TOKENcan publish the unscopedwednesdayaipackage and scoped public packages under the@wednesdayainpm organization. - If dependencies changed, run
pnpm installsopnpm-lock.yamlis current.
- Build & artifacts
- If A2UI inputs changed, run
pnpm canvas:a2ui:bundleand commit any updatedsrc/canvas-host/a2ui/a2ui.bundle.js. -
pnpm run build(regeneratesdist/). - Verify npm package
filesincludes all requireddist/*folders (notablydist/node-host/**anddist/acp/**for headless node + ACP CLI). - Confirm
dist/build-info.jsonexists and includes the expectedcommithash (CLI banner uses this for npm installs). - Optional:
npm pack --pack-destination /tmpafter the build; inspect the tarball contents and keep it handy for the GitHub release (do not commit it).
- Changelog & docs
- Do not hand-edit root
CHANGELOG.md; release-please generates it from Conventional Commits. - Add or update a dated
dev-docs/logs/YYYY-MM-DD-*.mdentry for the engineering record when the release contains feature work. - Update
dev-docs/CHANGELOG.mdonly as the narrative index to that dev log. - Ensure README examples/flags match current CLI behavior (notably new commands or options).
- Validation
-
pnpm build -
pnpm check -
pnpm test(orpnpm test:coverageif you need coverage output) -
pnpm release:check(verifies npm pack contents) -
OPENCLAW_INSTALL_SMOKE_SKIP_NONROOT=1 pnpm test:install:smoke(Docker install smoke test, fast path; required before release)- If the immediate previous npm release is known broken, set
OPENCLAW_INSTALL_SMOKE_PREVIOUS=<last-good-version>orOPENCLAW_INSTALL_SMOKE_SKIP_PREVIOUS=1for the preinstall step.
- If the immediate previous npm release is known broken, set
- (Optional) Full installer smoke (adds non-root + CLI coverage):
pnpm test:install:smoke - (Optional) Installer E2E (Docker, runs the WednesdayAI install script, onboards, then runs real tool calls):
pnpm test:install:e2e:openai(requiresOPENAI_API_KEY)pnpm test:install:e2e:anthropic(requiresANTHROPIC_API_KEY)pnpm test:install:e2e(requires both keys; runs both providers)
- (Optional) Spot-check the web gateway if your changes affect send/receive paths.
- macOS app (Sparkle)
- Build + sign the macOS app, then zip it for distribution.
- Generate the Sparkle appcast (HTML notes via
scripts/make_appcast.sh) and updateappcast.xml. - Keep the app zip (and optional dSYM zip) ready to attach to the GitHub release.
- Follow macOS release for the exact commands and required env vars.
APP_BUILDmust be numeric + monotonic (no-beta) so Sparkle compares versions correctly.- If notarizing, use the
openclaw-notarykeychain profile created from App Store Connect API env vars (see macOS release).
- Publish (npm)
- Confirm the release-please publish job completed. It builds core, UI, and
@wednesdayai/whatsapp, runspnpm release:check, packs both npm artifacts, and publishes them before promoting the GitHub release. - Verify the registry:
npm view wednesdayai version,npm view wednesdayai dist-tags,npm view @wednesdayai/whatsapp version,npm view @wednesdayai/whatsapp dist-tags, andnpx -y wednesdayai@X.Y.Z --version(or--help). - If a publish job was rerun, confirm it skipped already-published immutable package versions instead of failing the release.
Troubleshooting
- npm pack/publish hangs or produces huge tarball: the macOS app bundle in
dist/OpenClaw.app(and release zips) get swept into the package. Fix by whitelisting publish contents viapackage.jsonfiles(include dist subdirs, docs, skills; exclude app bundles). Confirm withnpm pack --dry-runthatdist/OpenClaw.appis not listed. - npm auth web loop for dist-tags: use legacy auth to get an OTP prompt:
NPM_CONFIG_AUTH_TYPE=legacy npm dist-tag add wednesdayai@X.Y.Z latestNPM_CONFIG_AUTH_TYPE=legacy npm dist-tag add @wednesdayai/whatsapp@X.Y.Z latest
npxverification fails withECOMPROMISED: Lock compromised: retry with a fresh cache:NPM_CONFIG_CACHE=/tmp/npm-cache-$(date +%s) npx -y wednesdayai@X.Y.Z --version
- Tag needs repointing after a late fix: force-update and push the tag, then ensure the GitHub release assets still match:
git tag -f vX.Y.Z && git push -f origin vX.Y.Z
- GitHub release + appcast
- Tag and push:
git tag vX.Y.Z && git push origin vX.Y.Z(orgit push --tags). - Create/refresh the GitHub release for
vX.Y.Zwith titleopenclaw X.Y.Z(not just the tag); body should include the full changelog section for that version (Highlights + Changes + Fixes), inline (no bare links), and must not repeat the title inside the body. - Attach artifacts:
npm packtarball (optional),OpenClaw-X.Y.Z.zip, andOpenClaw-X.Y.Z.dSYM.zip(if generated). - Commit the updated
appcast.xmland push it (Sparkle feeds from main). - From a clean temp directory (no
package.json), runnpx -y wednesdayai@X.Y.Z send --helpto confirm install/CLI entrypoints work. - Announce/share release notes.
Plugin publish scope (npm)
WednesdayAI extensions use the@wednesdayai npm organization for new first-party plugin
packages:
@wednesdayai/*— WednesdayAI-native extensions built for this fork (new additions go here).@openclaw/*— inherited extensions from the fork base. Publish only if the package is already on npm under that scope; do not add new extensions under@openclaw/— use@wednesdayai/instead.
extensions/**).
@wednesdayai/whatsapp is the first dependency-bearing first-party plugin on the npm rail.
Process to derive future publish candidates:
- Start from plugin ids under
extensions/*. - Compare with
extensions/*/package.jsonnames. - Publish new WednesdayAI-native packages under
@wednesdayai/<plugin-id>only after the package has built JavaScript entries and runtime dependency validation. - Keep inherited
@openclaw/*packages compatibility-only unless a package is known to target this fork.
tlon).
Trusted npm publishing
Core (and the WhatsApp dependency plugin) publish through a closed, OIDC-backed workflow — never a localnpm publish:
- Workflow:
.github/workflows/npm-publish.ymlruns withpermissions: { contents: read, id-token: write }(npm trusted publishing via OIDC). It triggers on pushes tomain(dev path; docs and markdown-only changes are ignored) and onworkflow_dispatchwith a closed mode set:dev-canary,stable-create,stable-resume,reconcile. Dispatch inputs pin an exactsource-sha, plusrelease-tag/candidate-id/stage-evidenceas the mode requires — no floating refs. - Invocation identity (
scripts/npm-publish/invocation.ts): every run carries anInvocationInput(eventpush|workflow_dispatch, modestable|dev|recover) resolved into anInvocationContextwhose package order is fixed:@wednesdayai/whatsappfirst, core second. Out-of-order or unbounded identifiers are rejected before anything ships. - Sealed inventories (
scripts/npm-publish/stable.ts): stable publications move through a state machine whose terminal inventory state issealed; receipts record every transition, and a core package published out of order lands incore-published-out-of-orderfor reconcile instead of diverging silently. - Static checks in the meantime:
scripts/release-check.tsperforms static-only release checks; the OIDC workflow is the settled sole-publisher design (see #407), not yet the enforced only path.