| name | aphrodite-release-workflow |
| description | Auto-release, version sync, pre-release verification, and release notes for aphrodite. |
| version | 1.3.0 |
| platforms | ["macos"] |
Aphrodite Release Workflow
Pre-Release Verification
MANDATORY before every release. Missing symbols silently kill the plugin -
Failed to load plugin with no error.
cd /path/to/Aphrodite
python3 -c "import sys; sys.path.insert(0, 'plugins'); import aphrodite; print('OK:', aphrodite.__doc__[:60])"
ruff check plugins/aphrodite/ Maintain/scripts/ crates/
npx pyright plugins/aphrodite/
cargo check -p aphrodite
If any step fails, fix BEFORE releasing. A broken plugin means zero tools, no
compression, no context engine - a silent degradation users won't notice.
Auto-Release
GIT_EDITOR=true Maintain/scripts/release/auto-release.sh "descriptive message"
Handles: stage → commit → bump version → cargo build → cargo test → tag → push
to Source remote.
crates.io publishing goes through Publish.yml, never manually
Do NOT run cargo publish locally. .github/workflows/Publish.yml builds and
publishes aphrodite-headroom-core → aphrodite → aphrodite-hermes (in that
dependency order) - but only on an explicit workflow_dispatch with
publish_crates: true; a plain Aphrodite/v* tag push alone does NOT publish
to crates.io (it only triggers Build.yml's GitHub Release artifacts). This
gate exists because crates.io versions are immutable - a version number burned
by a bad manual cargo publish can never be reused (see the v1.3.1 incident in
Maintain/CHANGELOG.md's "Why 1.3.2, not 1.3.1" note). Trigger it deliberately
via gh workflow run Publish.yml -f publish_crates=true (or the Actions UI)
only once the tagged release's artifacts are verified.
Version Sync
The Aphrodite project has two independent version tracks:
- Binary version (
1.0.4) - Rust crates, must match across Cargo.toml files
- Plugin version (
2.0.1) - Hermes plugin, lives in the plugins/aphrodite submodule
Binary version locations (monorepo - bump these together):
crates/aphrodite/Cargo.toml - version = "1.0.4" (line 3)
crates/aphrodite-hermes/Cargo.toml - version = "1.0.4" (line 3, package)
crates/aphrodite-hermes/Cargo.toml - aphrodite = { ..., version = "1.0.4" } (line 15, dependency)
Plugin version locations (submodule plugins/aphrodite/):
plugin.yaml - version: 2.0.1 (line 2)
plugin.yaml - install_message: block contains aphrodite v2.0.1 - (line 31)
pyproject.toml - version = "2.0.1" (if file exists)
__init__.py - docstring aphrodite v2.0.1 - ... (if version appears there)
_core/config.py - BIN_VERSION + PLUGIN_VERSION constants (if file exists)
Documentation:
README.md - example output shows "version":"v1.0.4" (line ~258)
What auto-release.sh bumps (complete list):
crates/aphrodite/Cargo.toml - binary version ✅
crates/aphrodite-hermes/Cargo.toml - package version + aphrodite dependency ✅
plugins/aphrodite/plugin.yaml - version field + install_message ✅
plugins/aphrodite/pyproject.toml - version (if file exists) ✅
plugins/aphrodite/__init__.py - docstring version (if present) ✅
plugins/aphrodite/_core/config.py - BIN_VERSION + PLUGIN_VERSION (if file exists) ✅
Submodule Release Flow
plugins/aphrodite is a git submodule → separate repo PlayForm/Aphrodite-Hermes.
auto-release.sh handles the full cross-repo release:
- Bump
plugin.yaml (and other submodule files) via sed
- Commit in submodule:
cd plugins/aphrodite && git commit -m "release: plugin vX.Y.Z"
- Tag in submodule:
git tag "vX.Y.Z"
- Push submodule branch + tag to
$SUBMODULE_REMOTE (default: Source)
- Update parent pointer:
git update-index --cacheinfo to track new submodule SHA
(needed because .gitmodules has ignore = all, which hides dirty submodules)
- Commit parent pointer update:
chore: sync aphrodite submodule → plugin vX.Y.Z
Manually verify after release:
README.md:~258 - example output version (non-critical, grep for "version":"v)
Binary Symlink
ln -sf /path/to/repo/target/release/aphrodite ~/.hermes/aphrodite/aphrodite
Release Notes - Content Standards
Every release MUST include: Summary, Changes, Infrastructure, What Ships, and Links.
See .hermes/RELEASE-TEMPLATE.md for the canonical template.
Anti-pattern (DO NOT): bare compare link with zero description.
30+ releases (v0.8.13-v0.8.43) currently ship with only:
**Full Changelog**: https://github.com/PlayForm/Aphrodite/compare/...
Shell Injection
Never use backticks with gh release create --notes. Use --notes-file with a
heredoc:
cat > /tmp/notes.md << 'EOF'
**[Compare vX.Y.Z...vX.Y.Z](https://github.com/PlayForm/Aphrodite/compare/vX.Y.Z...vX.Y.Z)**
One paragraph. What this release is. 2-3 sentences.
- **Feature**: description
- **Fix**: description
- Build: `cargo build --release -p aphrodite` ✅
- Tests: `cargo test -p aphrodite` ✅ (NNN passed)
- Python: `ruff check` + `pyright` ✅
- Lint: `cargo clippy` ✅
Build.yml's 4-target matrix (`aarch64-apple-darwin`, `x86_64-apple-darwin`,
`x86_64-unknown-linux-gnu`, `x86_64-pc-windows-msvc`) stages, per target, the
`aphrodite` binary + `libaphrodite_hermes` dylib + a `SHA256SUMS-<target>.txt`
- 4 targets × 3 files = 12 artifacts total on a full release.
| Artifact pattern | Platform |
|----------|----------|
| `aphrodite-<target>` / `libaphrodite_hermes-<target>.{so,dylib,dll}` | per matrix target above |
| `SHA256SUMS-<target>.txt` | checksums for that target's two binaries |
| Plugin vA.B.C | Hermes (standalone repo) |
- **Full Changelog**: https://github.com/PlayForm/Aphrodite/compare/...
- **CHANGELOG.md**: [CHANGELOG.md](CHANGELOG.md)
- **Plugin**: https://github.com/PlayForm/Aphrodite-Hermes
EOF
gh release create Aphrodite/vX.Y.Z --notes-file /tmp/notes.md \
staging/aphrodite-* staging/libaphrodite_hermes-* staging/SHA256SUMS-*.txt
Cross-Module Import Pitfall
Adding a new import in one module (e.g., from .live import _is_live_tool)
without defining the symbol in the target module silently kills the plugin at
session start. Always test the full import chain before releasing:
python3 -c "import aphrodite".