Skip to main content

Release Checklist (npm + macOS)

Use pnpm (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 ~/.profile and confirm SPARKLE_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/Sparkle if needed.
  1. Version & metadata
Do not hand-edit release versions. 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.json version bump.
  • Confirm the release-please sync commit ran pnpm version:sync and 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.md for a pinned npm version; it intentionally installs wednesdayai@latest.
  • Peekaboo and Swabble version independently; bump them only when that specific release requires it.
Do not touch appcast.xml version here — that is updated in step 5 (macOS Sparkle release).
  • Confirm package metadata: root package name is wednesdayai, bin exposes both wednesdayai and the compatibility openclaw command, and WhatsApp package name is @wednesdayai/whatsapp.
  • Confirm secrets.NPM_TOKEN can publish the unscoped wednesdayai package and scoped public packages under the @wednesdayai npm organization.
  • If dependencies changed, run pnpm install so pnpm-lock.yaml is current.
  1. Build & artifacts
  • If A2UI inputs changed, run pnpm canvas:a2ui:bundle and commit any updated src/canvas-host/a2ui/a2ui.bundle.js.
  • pnpm run build (regenerates dist/).
  • Verify npm package files includes all required dist/* folders (notably dist/node-host/** and dist/acp/** for headless node + ACP CLI).
  • Confirm dist/build-info.json exists and includes the expected commit hash (CLI banner uses this for npm installs).
  • Optional: npm pack --pack-destination /tmp after the build; inspect the tarball contents and keep it handy for the GitHub release (do not commit it).
  1. 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-*.md entry for the engineering record when the release contains feature work.
  • Update dev-docs/CHANGELOG.md only as the narrative index to that dev log.
  • Ensure README examples/flags match current CLI behavior (notably new commands or options).
  1. Validation
  • pnpm build
  • pnpm check
  • pnpm test (or pnpm test:coverage if 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> or OPENCLAW_INSTALL_SMOKE_SKIP_PREVIOUS=1 for the preinstall step.
  • (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 (requires OPENAI_API_KEY)
    • pnpm test:install:e2e:anthropic (requires ANTHROPIC_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.
  1. 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 update appcast.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_BUILD must be numeric + monotonic (no -beta) so Sparkle compares versions correctly.
    • If notarizing, use the openclaw-notary keychain profile created from App Store Connect API env vars (see macOS release).
  1. Publish (npm)
  • Confirm the release-please publish job completed. It builds core, UI, and @wednesdayai/whatsapp, runs pnpm 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, and npx -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 via package.json files (include dist subdirs, docs, skills; exclude app bundles). Confirm with npm pack --dry-run that dist/OpenClaw.app is 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 latest
    • NPM_CONFIG_AUTH_TYPE=legacy npm dist-tag add @wednesdayai/whatsapp@X.Y.Z latest
  • npx verification fails with ECOMPROMISED: 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
  1. GitHub release + appcast
  • Tag and push: git tag vX.Y.Z && git push origin vX.Y.Z (or git push --tags).
  • Create/refresh the GitHub release for vX.Y.Z with title openclaw 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 pack tarball (optional), OpenClaw-X.Y.Z.zip, and OpenClaw-X.Y.Z.dSYM.zip (if generated).
  • Commit the updated appcast.xml and push it (Sparkle feeds from main).
  • From a clean temp directory (no package.json), run npx -y wednesdayai@X.Y.Z send --help to 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.
Bundled plugins that are not on npm stay disk-tree only (still shipped in extensions/**). @wednesdayai/whatsapp is the first dependency-bearing first-party plugin on the npm rail. Process to derive future publish candidates:
  1. Start from plugin ids under extensions/*.
  2. Compare with extensions/*/package.json names.
  3. Publish new WednesdayAI-native packages under @wednesdayai/<plugin-id> only after the package has built JavaScript entries and runtime dependency validation.
  4. Keep inherited @openclaw/* packages compatibility-only unless a package is known to target this fork.
Release notes must also call out new optional bundled plugins that are not on by default (example: tlon).

Trusted npm publishing

Core (and the WhatsApp dependency plugin) publish through a closed, OIDC-backed workflow — never a local npm publish:
  • Workflow: .github/workflows/npm-publish.yml runs with permissions: { contents: read, id-token: write } (npm trusted publishing via OIDC). It triggers on pushes to main (dev path; docs and markdown-only changes are ignored) and on workflow_dispatch with a closed mode set: dev-canary, stable-create, stable-resume, reconcile. Dispatch inputs pin an exact source-sha, plus release-tag / candidate-id / stage-evidence as the mode requires — no floating refs.
  • Invocation identity (scripts/npm-publish/invocation.ts): every run carries an InvocationInput (event push | workflow_dispatch, mode stable | dev | recover) resolved into an InvocationContext whose package order is fixed: @wednesdayai/whatsapp first, 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 is sealed; receipts record every transition, and a core package published out of order lands in core-published-out-of-order for reconcile instead of diverging silently.
  • Static checks in the meantime: scripts/release-check.ts performs static-only release checks; the OIDC workflow is the settled sole-publisher design (see #407), not yet the enforced only path.