| name | release-yohaku-companion |
| description | Prepare and publish a production Yohaku Companion macOS release. Use when asked to determine a release version, write verified release notes, update Xcode marketing/build versions, create an annotated Git tag, push a release, monitor the GitHub Release workflow, or diagnose a failed Yohaku Companion release or Sparkle appcast. |
Release Yohaku Companion
Produce one auditable release commit and annotated tag. Let GitHub Actions own the deterministic Apple Silicon arm64 build, Developer ID signing, Apple notarization, DMG creation, Sparkle EdDSA appcast, and GitHub Release publication. Every release is Developer ID signed and notarized; there is no unsigned or ad-hoc distribution path.
Safety contract
- Read
AGENTS.md, .github/workflows/release.yml, ExportOptions.plist, and the current Xcode build settings before acting.
- Require a clean working tree and
main checked out at origin/main before changing versions.
- Treat an explicit request to release a named version as authorization to commit, tag, and atomically push. Otherwise, obtain confirmation before the external push.
- Never move, replace, force-push, or delete an existing tag. Never replace an existing public GitHub Release.
- Stop when the version is not strictly greater than the version inside the newest real distributed application. Do not trust GitHub's
latest label alone; inspect semantic tags and release assets when history is inconsistent. The sole exception is the repository bootstrap release described below, where no Yohaku Companion tag, Release, or distributed application exists yet.
- Stop when notes contain claims that cannot be traced to source changes or verification evidence.
- Preserve unrelated user changes; do not use destructive restore/reset commands.
One-time repository setup
The release job always requires this stable Sparkle key pair, either as repository secrets or in the protected GitHub release environment:
| Secret | Content |
|---|
SPARKLE_PRIVATE_KEY | Fixed private key exported by Sparkle generate_keys -x |
SPARKLE_PUBLIC_ED_KEY | Matching base64 public EdDSA key |
Generate a Yohaku Companion-specific Sparkle pair once, back it up securely, and reuse it for every release. Never copy ProcessReporter’s pair and never generate a new key in CI. This key pair does not require an Apple developer account.
Developer ID distribution additionally requires all five of the following. The release job fails immediately when any one is missing; distribution never degrades to an unsigned artifact.
| Secret | Content |
|---|
BUILD_CERTIFICATE_BASE64 | Base64-encoded Developer ID Application .p12 for team KAMM5N88X3 |
P12_PASSWORD | Password for the .p12 |
NOTARY_PRIVATE_KEY_BASE64 | Base64-encoded App Store Connect API .p8 |
NOTARY_KEY_ID | App Store Connect API key ID |
NOTARY_ISSUER_ID | App Store Connect issuer ID |
The notarization key must be a Team Key issued at App Store Connect → Users and Access → Integrations → App Store Connect API → Team Keys. The Developer role is sufficient. Individual keys are not accepted by notarytool.
DEVELOPMENT_TEAM in project.pbxproj and teamID in ExportOptions.plist must both match the team that issued the certificate. A mismatch fails at xcodebuild -exportArchive with no signing certificate found.
See references/developer-id-provisioning.md for certificate selection and export, API key issuance, the local distribution rehearsal, and the commands that store these secrets. Rehearse locally before the first release on a new certificate or key.
Protect v* tags from deletion or force updates.
Release workflow
1. Establish the release boundary
- Fetch tags and inspect
git status, the current branch, origin/main, semantic tags, and GitHub Releases.
- Inspect the actual version in the newest distributed app when tag history or
latest is inconsistent.
- Select a semantic version
X.Y.Z strictly greater than the distributed version. If the repository has no Yohaku Companion tags or Releases, use the existing 1.7.3 project version and .github/release-notes/v1.7.3.md as the one-time bootstrap release.
- Confirm that no local/remote tag or public Release already uses
vX.Y.Z.
2. Update deterministic versions
Run:
rtk proxy python3 .agents/skills/release-yohaku-companion/scripts/set-release-version.py X.Y.Z
The script updates every Xcode configuration to MARKETING_VERSION=X.Y.Z and increments the unique positive CURRENT_PROJECT_VERSION.
Skip this script only for the one-time bootstrap v1.7.3 release when the repository still has no tags or Releases; that version and its notes are already committed as the initial Core-compatible baseline.
3. Write evidence-based release notes
Create .github/release-notes/vX.Y.Z.md with this structure:
# Yohaku Companion vX.Y.Z
## Highlights
- User-visible outcome.
## Fixes and reliability
- Corrected behavior and affected boundary.
## Update notes
- Compatibility or configuration note, if required.
Derive statements from the committed diff since the relevant previous release. Prefer observable behavior over implementation details. Omit empty sections and speculative claims. Mention migrations, permission changes, removed compatibility, or required secret/configuration changes explicitly.
4. Verify before tagging
Run the repository's focused checks, at minimum:
rtk proxy xcodebuild -project YohakuCompanion.xcodeproj -scheme YohakuCompanion -configuration Debug -destination 'generic/platform=macOS' build
rtk proxy xcodebuild -project YohakuCompanion.xcodeproj -scheme YohakuCompanion -configuration Release -destination 'generic/platform=macOS' ARCHS=arm64 ONLY_ACTIVE_ARCH=NO build
rtk git diff --check
rtk proxy plutil -lint YohakuCompanion/Info.plist ExportOptions.plist
Also run strict-concurrency and static-analysis checks when the release changes Swift runtime or data-flow code. The workflow runs scripts/prepare_arm64_app.sh after export because Sparkle's macOS binary framework contains multiple architecture slices even when the app target builds for arm64. Verify that Sparkle resolves to the reviewed version, every shipped executable is arm64-only after that preparation step, no legacy Discord SDK dynamic library is present, release notes are non-empty, and tag/project versions match.
5. Commit, tag, and atomically push
Review the complete diff. Then create one release commit and annotated tag:
rtk git add YohakuCompanion.xcodeproj/project.pbxproj .github/release-notes/vX.Y.Z.md
rtk git commit -m "release: vX.Y.Z"
rtk git tag -a vX.Y.Z -m "Yohaku Companion vX.Y.Z"
rtk git push --atomic origin main vX.Y.Z
Do not create the GitHub Release manually. The tag-triggered workflow verifies that all distribution credentials are present, creates a hidden draft, validates the arm64-only hardened-runtime application, notarizes and staples both the application and the DMG, generates an appcast whose DMG enclosure has a Sparkle EdDSA signature, publishes the release, and explicitly repairs GitHub's latest pointer.
6. Monitor the real endpoint
Watch the Release Actions run through completion. Confirm all of the following before reporting success:
- The workflow concluded successfully.
- The Release is public, non-prerelease, and marked latest.
- The DMG,
appcast.xml, checksum-covered Markdown notes, and SHA256SUMS.txt exist.
- The latest appcast references the new tag, marketing version, build number, and an EdDSA signature.
- The asset URLs return successfully.
If Actions fails, preserve the immutable tag, report the failing stage and evidence, and fix forward with explicit user direction. Do not silently republish different bytes under an already public version.