Cut an OpenWork release, release the app, publish a new version, rerun or recover a release tag, verify release assets. Tag-driven GitHub Actions release that makes zero commits to the repo.
Cut an OpenWork release. The "Release App" workflow
(.github/workflows/release-macos-aarch64.yml) builds, signs, and publishes
the desktop app assets on the GitHub release. Full runbook:
docs/RELEASING.md.
Versions live in git tags only. Every committed package.json holds the
permanent 0.0.0-dev placeholder; CI stamps the tag-derived version into the
workspace at build time (scripts/release/stamp-version.mjs). A release makes
zero commits to this repo — no bump commit, no backfill PR, no packaging
PR.
A release is done when the run is green and the GitHub release is published (not a draft) — never when the tag is created.
Run this before release:cut or any manual tag. It exists because a fork PR
carrying the exact fix for a blank-window-on-launch regression sat open for a
day before the affected version was tagged (incident 2026-09-09): fork PRs
never receive Warden clearance (warden.yml skips them and
warden-clearance.yml requires head_repository == repository), so nothing
surfaces them until a human looks.
List, against dev, every open PR opened since the previous stable tag whose
title or body matches fix(app)|crash|blank|white screen|regression|first launch, plus every open fork PR created or updated in that window:
export R=different-ai/openwork
PREV=$(gh release list -R $R --exclude-drafts --exclude-pre-releases --limit 1 --json tagName --jq '.[0].tagName')
SINCE=$(gh release view "$PREV" -R $R --json createdAt --jq '.createdAt')
echo "previous tag $PREV created $SINCE"
gh pr list -R $R --state open --base dev --limit 300 --search "sort:created-asc" \
--json number,title,body,isCrossRepository,createdAt,updatedAt,url \
| jq -r --arg since "$SINCE" '.[]
| select((.createdAt >= $since
and ((.title + " " + .body) | test("fix\\(app\\)|crash|blank|white screen|regression|first launch"; "i")))
or (.isCrossRepository and .updatedAt >= $since))
| "#\(.number)\(if .isCrossRepository then " [FORK]" else "" end) \(.title) \(.url)"'Then the fork backlog: open fork PRs of any age describing a crash or startup failure. Scan the titles; a match for the bug class you are shipping (or any blank/white-screen/first-launch fix) blocks the same way.
gh pr list -R $R --state open --base dev --limit 300 \
--json number,title,body,isCrossRepository,url \
| jq -r '.[] | select(.isCrossRepository
and ((.title + " " + .body) | test("crash|blank|white screen|first launch|regression"; "i")))
| "#\(.number) \(.title) \(.url)"'For every PR listed, decide one of:
review-a-contributor-pr skill),
merge to dev, then tag.changelog.yml
opens after publish (docs(changelog): release notes for vX.Y.Z, branch
automation/changelog-vX.Y.Z-*), under a Known open fixes heading. If
the notes PR has not appeared yet, put the same text in the GitHub release
body (gh release edit vX.Y.Z -R $R --notes-file ...) and move it when the
PR opens.An empty list is also recorded (Known open fixes: none matched). Tagging
with a non-empty list and no recorded decision is the failure mode this step
prevents; do not proceed.
pnpm release:cut # dispatches Release App with bump=patch
pnpm release:cut minor # or major
pnpm release:cut --version 0.19.0
pnpm release:cut:watch # same as release:cut, then tails the runEquivalent by hand:
gh workflow run "Release App" --repo different-ai/openwork -f bump=patchThe run resolves the next version from existing v* tags, creates the tag on
origin/dev HEAD, verifies it (scripts/release/verify-tag.mjs: stable
format + strictly greater than every other stable tag), stamps the version
into the CI workspace, builds all 18 electron matrix legs, publishes npm +
Daytona + AUR, and flips the draft release public.
The tag ref is created via REST with the org-owned diff-warden app token
(a v* ruleset bypass actor; WARDEN_APP_ID + WARDEN_PRIVATE_KEY in the
warden-clearance environment). The app's tag retriggers the workflow; that duplicate run is skipped by an
actor guard. If the tag push is rejected, the run fails with instructions —
fix the ruleset bypass or fall back to a manual admin tag push.
To release a commit that is not yet reviewed onto dev (incident response),
push the tag manually — the tag names exactly the code that ships:
git tag vX.Y.Z <sha>
git push origin vX.Y.Z # v* ruleset grants admins bypassThe Expedited Release Audit workflow opens a post-hoc review issue when the
tagged commit is not on dev. Never push dev directly or bypass its branch
rules.
gh run list --repo different-ai/openwork --workflow "Release App" --limit 1
gh run watch <run-id> --repo different-ai/openwork --exit-status --interval 90Publishing is gated on the electron matrix, electron assets, and npm publish.
Publish AUR (continue-on-error) and Build + Push Daytona Snapshot are
non-blocking channels: their failures don't stop the release — rerun the
workflow with the same tag once the channel recovers.
Rerun an existing tag (recovery) — transient failures, or replaying non-blocking channels:
gh workflow run "Release App" --repo different-ai/openwork -f tag=vX.Y.ZIf the release is already published, a recovery run skips every desktop
build leg and the updater-manifest merge; it replays only npm, AUR, and
Daytona. Rebuilding re-signs the installers, and a run that fails before
publish-release would leave latest*.yml pointing at bytes that no longer
exist. Every auto-update then fails with sha512 checksum mismatch (v0.18.52).
Ship new desktop bytes as the next patch.
Recovery runs skip tag creation and monotonicity, build source pinned to the
tag, and pick up workflow-file fixes from dev automatically (the workflow
definition runs from the dispatched ref; only the checked-out sources are
pinned to the tag).
If the run fails before the release is published: land the fix on dev
via a normal protected-branch PR and cut the next patch (pnpm release:cut).
Only delete/recreate a tag after verifying the GitHub release is still
draft-only:
git push --delete origin vX.Y.Zgh release view vX.Y.Z --repo different-ai/openwork --json assets --jq '.assets[].name'Expect the app assets (openwork-<platform>-X.Y.Z.*, latest*.yml updater
manifests), including:
openwork-mac-arm64-X.Y.Z.dmgopenwork-mac-x64-X.Y.Z.dmgopenwork-win-x64-X.Y.Z.exeThe desktop updater 404s on latest*.yml until the release is published —
that error in a running app during the build window is expected and
self-heals. Spot-check a download URL resolves (302 to release-assets CDN):
curl -sI "https://github.com/different-ai/openwork/releases/download/vX.Y.Z/openwork-mac-arm64-X.Y.Z.dmg" | head -2Confirm npm view openwork-server version matches.
Once the assets are published, run the validate-a-release skill
(.opencode/skills/validate-a-release/SKILL.md) against the released
mac-arm64 zips before telling anyone the version is safe to roll out: it
boots the released enterprise and cloud binaries through
packaged-first-launch and released-enterprise-activated (fresh install,
activated install, and a previous-release profile opened by the new build),
checks the updater manifests' sha512, and verifies signing/notarization.
/v1/install/:platform) 302s to versioned assets.ee/apps/den-api/src/desktop-releases.ts): the new version is
live for orgs as soon as the release is published — no den deploy needed.
The committed generated/desktop-versions.ts is only a cold-start/offline
fallback.packaging/aur template
(pkgver=0.0.0) in the CI workspace and pushing to aur.archlinux.org — the
AUR-side commit is that channel's publish protocol; this repo stays
untouched.417244c
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.