Imported from ZeroSum-Solutions/mish (
tools/release/AGENTS.md). Install upstream withnpx skills add ZeroSum-Solutions/mish --skill release. Copyright stays with the author.
tools/release/AGENTS.md
Follow the root AGENTS.md and tools/AGENTS.md first. This file records the release channel policy that tools/release and tools/pack encode.
Release channel model
The release workflows this section names (release-*.yml, notify-*-feishu.yml, cut-release and the rest) are not published in this repository; the section records the channel policy that tools/release and tools/pack still encode.
betais the daily R&D/development validation channel. It is optimized for fast development feedback and is not part of the stable promotion gate.prereleaseis the internal validation channel for stable delivery. Stable releases remain gated by validated prerelease artifacts.- Prerelease builds macOS x64 (Intel) on every run.
release-prerelease.ymltakes anenable_mac_x64input that defaults totrue, andnotify-release-feishu.yml(the push-to-release/**entry point) forwardstrueon push. The switch stays because Intel is the pipeline's critical path — it runs on the slowermacos-15-intelfleet, ~33 min against ~21 min for every other platform — so a run that is explicitly not about Intel can buy that time back. Turning it off costs that build its promotion eligibility, so it is not a routine choice. - A prerelease built without
enable_mac_x64cannot be promoted to stable. Stable ships Intel and stable's gate is validated prerelease artifacts, so an Intel-less prerelease has not validated the Intel artifact stable will ship. This is enforced, not merely documented:validateStablePrereleaseMetadataintools/release/src/metadata/prepare-stable.tsrequiresplatforms.macIntel(withenabled: true,arch: "x64",signed: true, and versioned dmg/zip URLs) in the promoted prerelease's metadata, so promoting an Intel-less prerelease fails the stablemetadatajob instead of silently shipping an unvalidated Intel build.platforms.linuxis deliberately not required there. - The
notify-release-feishu.ymlforwarding for a default-on boolean must be${{ github.event_name != 'workflow_dispatch' || inputs.<flag> }}, never${{ inputs.<flag> || true }}.inputsis unset on push, so the second form reads correctly there — but on a dispatch where the operator UNCHECKED the box,false || trueis also true and the switch becomes unturnoffable. (A default-off boolean uses${{ inputs.<flag> || false }}for the mirror-image reason: a bare${{ inputs.<flag> }}forwards''to atype: booleancallee input.) enable_smokeandenable_testsalso default totrue, because neither costs the build anything any more — see "Prerelease validation runs outside the pipeline" below.mac_sign_mode(defaultsign-only) codesigns without the Apple notary round-trip;notarizeis the pre-stable setting, since stable ships notarized.win_x64_smoke_modestill chooses how deep the Windows smoke goes, and applies only whenenable_smokeis on.- Signing is not optional for a prerelease that will be promoted.
validateStablePrereleaseMetadatarequiresplatforms.mac.signed == trueandplatforms.macIntel.signed == true, somac_sign_mode: noproduces a prerelease that cannot become a stable release. Notarization has no metadata field and is therefore not checked — which is exactly why it, and not signing, is what the fast default drops. - Automated tests do not gate prerelease delivery. A prerelease exists so a person can install it and test it by hand; machine CI is a parallel opinion about the same commit rather than a valve on delivery, and a flaky suite must never cost the team a day's package. Do not reintroduce a test job into
build_*orpublish'sneeds/if; if a check must genuinely block delivery, that is a channel-policy decision, not a workflow edit. - Prerelease validation runs outside the pipeline.
release-prerelease.ymlholds one repository-wide concurrency group (mish-release-prerelease,cancel-in-progress: false), so any job that outlivespublishkeeps that group held and stops the NEXT prerelease from starting. Making the test jobs advisory was therefore not enough — they still blocked the next build. The workflow now contains onlymetadata → build_* → publishplus two fire-and-forget dispatcher jobs, and the validation lives in three dispatched workflows with concurrency groups scoped to the origin run:release-prerelease-tests.yml—functional_e2e,e2e_vitest,daemon_unit_tests,verify, dispatched as soon as the build commit resolves.release-prerelease-smoke.yml— the packaged mac/Windows smoke, dispatched afterpublish. It does not test a local build directory:.github/scripts/release/smoke-artifacts.tsreads the published version metadata, downloads the DMG / setup.exe a user would get, verifies its sha256 sidecar, and writes it to the one pathtools-pack <platform> installreads. Linux keeps its in-job smoke, because the whole Linux lane is opt-in behindvars.ENABLE_STABLE_LINUXand is on nobody's critical path.release-prerelease-card.yml— the progressive Feishu card. All three are dispatched through.github/scripts/release/dispatch-validation.sh, which aimsgh workflow runat the built branch first and the default branch second, so a release branch cut before these lanes existed still gets validated. A dispatch failure is loud and non-blocking: nothing depends on the dispatcher jobs.
- One writer owns the release card. A Feishu
PATCHreplaces the entire card, so build jobs updating "their own" row would each erase the other two's.release-prerelease-card.ymltherefore owns the message alone and learns everything by polling the jobs API of the three runs;tools/release/src/notifications/prerelease-card.tsrenders the card as a total function of that state (unit-tested intools/release/tests/prerelease-card.test.ts) andfeishu-app.tsis the application-bot transport. The card is posted on the watcher's first poll, before any package exists, and every later state edits that same message: the channel learns a release is under way rather than waiting out the build window in silence. The rule that used to withhold it (a "构建中…" card left standing forever when a build dies) is now carried by rendering instead — a dead lane renders its own terminal state,allPlatformsFailedturns the card red once every expected platform has reported without shipping, and the watcher's timeout stamps the card rather than abandoning it. Per-platform durations and the whole-round clock come from thestarted_at/completed_atthe watcher already polls; live numbers are quantized to the minute so the card does not PATCH itself on every poll. version_metadata_urlis the authoritative "did a package ship?" signal, not the workflow conclusion. The conclusion goes red for anything anywhere in the release run, including work that happens after publication, so keying a notification off it paints the card red while its own download buttons work. The progressive card takes that answer from the published metadata it verifies rather than from a job conclusion, andtools/release/src/notifications/feishu.tskeeps the same rule (VERSION_METADATA_URLoutranksBUILD_STATE, falling back to it only for callers that do not pass a URL) for the daily beta stream.- The prerelease Feishu notification has exactly one source:
release-prerelease-card.yml. The transitional one-shot webhook card that lived innotify-release-feishu.yml'snotifyjob was retired once the progressive card had carried four real releases; that workflow now only builds. Do not add a second prerelease notifier — the card is a PATCH-in-place message with a single writer, and a second poster produces the duplicate the transition existed to end.tools/release/src/notifications/feishu.tsand theFEISHU_RELEASE_WEBHOOK/FEISHU_RELEASE_SIGN_SECRETsecrets stay:notify-daily-feishu.ymlstill renders the beta download card through them, andcut-release,cut-patch-release,backport-automerge,bake-plugin-previews-automerge,main-prerelease-win-smoke, anddsh-upstream-driftall post through the same webhook. previewis an independent early-access channel with stable-like release rigor. It should use preview versions such asX.Y.Z-preview.N, publish to thepreviewR2 channel, publish updater feeds underpreview/latest, and follow stable's platform policy including the existing optional Linux enablement.stableis the formal delivery channel. Do not make stable promotion depend on preview; stable continues to depend on prerelease only.- Public packaged app identity must stay channel-distinct: macOS stable uses
Mishh Mashh, beta usesMishh Mashh Beta, prerelease usesMishh Mashh Prerelease, and preview usesMishh Mashh Preview. Do not ship beta, prerelease, or preview mac DMGs whose drag-install app bundle isMishh Mashh.app. Internal compatibility identifiers and Windows identities keep their existing values. - Windows beta updater validation must use the real beta namespace
release-beta-win; otherwise a local beta-like namespace can create a separate uninstall registry key while looking like the sameMish Betaapp. Seetools/pack/AGENTS.mdfor the architecture map and high-confidence acceptance harness.
