diff --git a/apps/cli/src/release-config.test.ts b/apps/cli/src/release-config.test.ts
index 196c8b3321..bbeb68df90 100644
--- a/apps/cli/src/release-config.test.ts
+++ b/apps/cli/src/release-config.test.ts
@@ -362,6 +362,8 @@ describe("release desktop artifacts", () => {
it("keeps the generated tap formula on the installed runtime smoke contract", () => {
const formula = readRepoFile("scripts/release/kandev.rb");
+ const updater = readRepoFile("scripts/release/update-homebrew-tap.sh");
+ const versionDeclaration = 'version "__VERSION__"';
expect(formula).toContain(
'assert_equal "v#{version}", shell_output("#{bin}/kandev --version").strip',
@@ -370,7 +372,11 @@ describe("release desktop artifacts", () => {
expect(formula).toContain("/health");
expect(formula).toContain('"status":"ok"');
expect(formula).toContain("
Kandev");
- expect(formula).not.toContain('version "__VERSION__"');
+ expect(formula).toContain(versionDeclaration);
+ expect(formula.indexOf(versionDeclaration)).toBeLessThan(
+ formula.indexOf('license "AGPL-3.0-only"'),
+ );
+ expect(updater).toContain('-e "s|__VERSION__|$VERSION|g"');
});
it("bumps desktop package and Tauri versions during release preparation", () => {
diff --git a/docs/public/release-process.md b/docs/public/release-process.md
index ff12d557f6..e75eb616cb 100644
--- a/docs/public/release-process.md
+++ b/docs/public/release-process.md
@@ -18,12 +18,12 @@ Kandev uses one semantic version across the Git tag, native runtime bundles, des
The workflow has four mutually exclusive operating modes:
-| Mode | Inputs | Result |
-|---|---|---|
-| Normal release | `bump=patch`, `minor`, or `major` | Creates and merges a release PR, tags its merge, builds, and publishes |
-| Dry run | `dry_run=true` | Computes the next version and exercises CLI package/lock plus changelog generation in the runner; no PR, tag, artifact build, or publication |
-| Desktop validation | `desktop_validation_only=true` | Builds web, five runtime bundles, and five desktop targets from the selected commit; no PR, tag, GitHub release, GHCR, npm, or Homebrew publication |
-| Backfill | `backfill_tag=vX.Y.Z` | Rebuilds and repairs channels for the latest existing release tag without creating a version or tag |
+| Mode | Inputs | Result |
+| ------------------ | --------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
+| Normal release | `bump=patch`, `minor`, or `major` | Creates and merges a release PR, tags its merge, builds, and publishes |
+| Dry run | `dry_run=true` | Computes the next version and exercises CLI package/lock plus changelog generation in the runner; no PR, tag, artifact build, or publication |
+| Desktop validation | `desktop_validation_only=true` | Builds web, five runtime bundles, and five desktop targets from the selected commit; no PR, tag, GitHub release, GHCR, npm, or Homebrew publication |
+| Backfill | `backfill_tag=vX.Y.Z` | Rebuilds and repairs channels for the latest existing release tag without creating a version or tag |
`dry_run` and desktop validation are not release candidates. Backfill cannot be combined with either and accepts only the latest exact SemVer tag after version manifests are checked for agreement.
@@ -65,7 +65,7 @@ Normal mode performs these stages:
5. **Build containers.** Publish amd64/arm64 base manifests, enforce the universal-image size gate, then publish multi-architecture universal images.
6. **Publish GitHub Release.** Attach runtime archives, checksums, desktop artifacts, notes, and the updater feed when eligible.
7. **Publish npm.** Use OIDC trusted publishing for five `@kdlbs/runtime-*` packages first, then the `kandev` launcher. Existing versions are skipped; the main package is not published after a runtime-package failure.
-8. **Update Homebrew.** Push the formula update to `kdlbs/homebrew-kandev` using release checksums and the deploy key.
+8. **Update Homebrew.** Push the formula update to `kdlbs/homebrew-kandev` using release checksums and the deploy key. The generated formula records `X.Y.Z` explicitly; platform archive suffixes such as `x64` are not version identifiers.
GHCR images are built before the GitHub Release. npm and Homebrew start only after the GitHub Release and may run in parallel. A late failure can therefore leave some channels complete and others missing.
@@ -105,7 +105,7 @@ After publication, verify:
- the Git tag points at the generated release merge;
- GitHub notes, five runtime archives, checksums, expected desktop installers, and conditional `latest.json`;
- all five runtime npm packages plus `kandev`, including a clean `npx kandev@latest`;
-- Homebrew install/upgrade and `kandev --version`;
+- Homebrew install/upgrade, a `Cellar/kandev/X.Y.Z` install path, and `kandev --version`;
- GHCR base and universal images on amd64 and arm64, including their immutable version tags;
- desktop launch on affected platforms and signed/notarized status where configured;
- backend health, a minimal task, agentctl startup, and Updates-screen behavior;
diff --git a/docs/specs/homebrew-core/spec.md b/docs/specs/homebrew-core/spec.md
index 63801619ba..918dfb8579 100644
--- a/docs/specs/homebrew-core/spec.md
+++ b/docs/specs/homebrew-core/spec.md
@@ -17,12 +17,12 @@ Kandev is currently installable via `brew install kdlbs/kandev/kandev` from the
## What
- Add a package-manager build contract that produces the native runtime bundle from an already-installed source checkout. It builds the Vite SPA, syncs it into the Go embed input, compiles the host `kandev` and `agentctl` binaries, and cross-compiles the four Linux/macOS remote `agentctl` helpers.
-- Keep package-manager dependency installation outside that contract. Homebrew declares Go, Node 24, and pnpm as build dependencies and Git as a runtime facility.
+- Keep package-manager dependency installation outside that contract. Homebrew declares Go, Node 24, and pnpm 10 as build dependencies. Git is part of Homebrew's standard environment and is not declared as a formula dependency.
- Compile only the host `kandev` binary with cgo and the `fts5` tag. `mattn/go-sqlite3` supplies the SQLite amalgamation, so the formula does not declare an external SQLite dependency unless build or linkage evidence requires it.
- Merge and publish the source-build contract in a Stable Kandev release before authoring the Core formula. Core must consume the immutable GitHub tag archive for that release, never a branch or unreleased commit.
- Install the bundle under `libexec/bin` and expose one `bin/kandev` wrapper produced by `write_env_script`. The wrapper sets `KANDEV_BUNDLE_DIR=` and `KANDEV_VERSION=`.
- Use stable numeric tags for `livecheck`; prerelease and Nightly npm versions are not Homebrew channels.
-- Keep `kdlbs/homebrew-kandev` as the upstream binary fast path alongside Homebrew Core's source-built bottles. The shared release-bundle validator must run before those binary archives are published, and the generated tap formula must smoke-test its version, readiness endpoint, and embedded SPA.
+- Keep `kdlbs/homebrew-kandev` as the upstream binary fast path alongside Homebrew Core's source-built bottles. The generated tap formula must set the Stable SemVer explicitly because platform archive names end in architecture tokens such as `x64`, and it must smoke-test its version, readiness endpoint, and embedded SPA. The shared release-bundle validator runs before those binary archives are published.
- Preserve all four remote `agentctl` helpers in custom-tap installations. The tap must use an exact-path Homebrew mismatched-binary audit allowlist rather than pruning helpers, so Docker and SSH targets can differ from the Homebrew host. Decision: [ADR-2026-08-05-homebrew-remote-helper-audit](../../decisions/2026-08-05-homebrew-remote-helper-audit.md).
The runtime bundle contains exactly:
@@ -44,6 +44,7 @@ The Darwin arm64 helper must carry a Mach-O code signature so Apple Silicon can
- **GIVEN** a new kandev release `vX.Y.Z` is tagged, **WHEN** Homebrew's auto-bump worker runs, **THEN** `livecheck` resolves the new tag from GitHub Releases and a bump PR is opened against the formula.
- **GIVEN** a maintainer reviews the PR, **WHEN** they run `brew install --build-from-source kandev` locally, **THEN** the build completes without network or sandbox failures and `brew test kandev` passes.
- **GIVEN** a Stable release updates `kdlbs/homebrew-kandev`, **WHEN** its platform archive is built and the tap formula is tested, **THEN** the archive contains the complete executable runtime and the installed launcher serves both `/health` and the embedded Kandev page.
+- **GIVEN** a Stable release updates `kdlbs/homebrew-kandev`, **WHEN** Homebrew evaluates a platform archive URL ending in `x64` or `arm64`, **THEN** the generated formula's explicit version keeps the Cellar path and `version` value at `X.Y.Z` rather than the architecture suffix.
- **GIVEN** a tap archive contains remote helpers for CPU architectures other than the Homebrew host, **WHEN** Homebrew audits the installed formula on macOS or Linux, **THEN** only the four declared remote-helper paths are exempted and the complete runtime remains installed.
## Out of scope
diff --git a/scripts/release/kandev.rb b/scripts/release/kandev.rb
index 70b64ec3d6..d811d83bc5 100644
--- a/scripts/release/kandev.rb
+++ b/scripts/release/kandev.rb
@@ -3,6 +3,8 @@
class Kandev < Formula
desc "Manage tasks, orchestrate agents, review changes, and ship value"
homepage "https://github.com/kdlbs/kandev"
+ # Conditional asset names end in x64/arm64; without this, Homebrew uses version 64.
+ version "__VERSION__"
license "AGPL-3.0-only"
on_macos do
diff --git a/scripts/release/update-homebrew-tap.sh b/scripts/release/update-homebrew-tap.sh
index 8f9d6b058d..5a25a0c7a1 100755
--- a/scripts/release/update-homebrew-tap.sh
+++ b/scripts/release/update-homebrew-tap.sh
@@ -118,6 +118,7 @@ mkdir -p "$(dirname "$FORMULA_PATH")"
GITHUB_BASE="https://github.com/kdlbs/kandev/releases/download/${TAG}"
sed \
+ -e "s|__VERSION__|$VERSION|g" \
-e "s|__GITHUB_BASE__|$GITHUB_BASE|g" \
-e "s|__SHA_MACOS_ARM64__|$SHA_MACOS_ARM64|g" \
-e "s|__SHA_MACOS_X64__|$SHA_MACOS_X64|g" \