Status: proposed Date: 2026-05-20 Author: Ship Hub rebuild session
prellr/multica is a fork of multica-ai/multica. The fork has its own
CLI commands that upstream doesn't have — squad, channel, memory,
mention, and (as of #130) ship. But the fork has no working
distribution path for its CLI. Every distribution reference points at
upstream:
| Reference | Location | Points at |
|---|---|---|
| Release pipeline | .github/workflows/release.yml:70 — if: github.repository_owner == 'multica-ai' |
The whole GoReleaser + Homebrew job is gated off for forks. A git tag on prellr/multica runs tests, then skips release entirely. No binaries, no formula. |
| Update check | server/internal/cli/update.go:76,102 |
multica update polls api.github.com/repos/multica-ai/multica/releases — a fork user gets upstream's binary. |
| Install source | scripts/install.sh:20,68 |
brew tap multica-ai/tap + BREW_PACKAGE="multica-ai/tap/multica". |
The Mac mini production host (192.168.2.14) runs multica v0.2.20 via
Homebrew. Its multica --help lists none of the fork commands
(squad, channel, memory, mention). That binary is upstream's
CLI, not the fork's. The fork has never shipped its own CLI to that
host — or anywhere.
Running multica update on the mini today refreshes it to a newer
upstream build — still zero fork commands. There is currently no way
for an operator to get the fork's CLI onto a machine except building it
by hand from source.
- Every fork-specific CLI command (
ship introspect-pipelines, the squad commands, etc.) is unreachable on any machine that installed via the documented path. - The Ship Hub rebuild's
multica ship introspect-pipelines(#130) cannot be run on the Mac mini until this is fixed. - It silently degrades: an operator who runs
multica updatereplaces a hand-built fork CLI with upstream's, losing every fork command, with no warning.
- The fork can cut its own CLI releases (
git tag→ binaries + Homebrew formula) without depending on upstream secrets or repos. multica updateon a fork-installed CLI pulls fork releases.- The source still merges cleanly from upstream — the distribution config must be the only fork-specific delta, ideally isolated so upstream syncs never conflict on it.
- No regression for upstream: upstream's release path is untouched.
- Republishing upstream's CLI. The fork ships its own binary that
includes upstream's commands (they're all in
server/cmd/multica/) plus the fork additions. - A separate CLI codebase. The CLI stays one tree; only distribution diverges.
Create prellr/homebrew-multica (a public repo — Homebrew taps must be
public or the consumer needs auth). GoReleaser writes the formula there
on each fork release.
Two viable shapes:
Option A — owner-keyed config (recommended). Replace the hard
if: github.repository_owner == 'multica-ai' gate with a job that runs
on any owner, and have GoReleaser's config select the tap by owner:
# release.yml — release job runs for both upstream and fork.
# GoReleaser's brews.repository.owner is set from a repo variable..goreleaser.yaml reads ${{ github.repository_owner }} (or a repo
variable HOMEBREW_TAP_OWNER) so upstream publishes to
multica-ai/homebrew-tap and the fork to prellr/homebrew-multica
with no per-fork edit beyond the variable + the HOMEBREW_TAP_GITHUB_TOKEN
secret.
Option B — parallel fork job. Leave the upstream release job as-is
and add a second job gated if: github.repository_owner == 'prellr'.
Simpler diff, but it's a fork-specific block that every upstream sync
has to skip over. Option A keeps the workflow identical between repos —
preferred for sync hygiene.
Either way the fork needs a HOMEBREW_TAP_GITHUB_TOKEN repo secret
with write access to the fork tap.
server/internal/cli/update.go hardcodes multica-ai/multica in two
URLs. Replace with a single package-level constant — or better, a
build-time -ldflags value — so the fork's released binary checks the
fork's releases:
// update.go
// updateRepo is the GitHub repo the CLI polls for new releases.
// Overridable at build time via -ldflags so a fork's binary checks
// the fork's releases instead of upstream's.
var updateRepo = "multica-ai/multica"GoReleaser sets -X .../cli.updateRepo=prellr/multica for the fork
build. Upstream's build keeps the default. This is the one source
change — and because it's a single var with an upstream-correct
default, an upstream sync never conflicts on it (upstream has no reason
to touch it, and if they do, the merge is a one-line resolve).
Point REPO_URL, REPO_WEB_URL, BREW_PACKAGE, and the brew tap
line at the fork. These are pure fork-config edits. To keep upstream
syncs clean, consider extracting them to variables at the top of the
script with a comment marking them as fork-config — so a sync conflict
(if upstream edits install.sh) is an obvious, localized resolve.
- PR-D1 — create
prellr/homebrew-multicatap repo + add theHOMEBREW_TAP_GITHUB_TOKENsecret toprellr/multica. - PR-D2 —
update.goconfigurableupdateRepovar +.goreleaser.yaml-ldflags+ owner-keyedbrews.repository. Un-gaterelease.yml(Option A). This is the load-bearing PR. - PR-D3 —
install.sh/install.ps1fork-config edits. - Cut the first fork CLI release:
git tag v0.3.4(patch bump from the last upstream-shared tag v0.3.3) → fork GoReleaser runs → binaries + formula land in the fork tap. - On the Mac mini: re-tap to the fork (
brew untap multica-ai/tap, install fromprellr/homebrew-multica) — a ONE-TIME migration. After that,multica updatestays on the fork.
- Tag collision. The fork and upstream share a tag namespace
conceptually (both use
vX.Y.Z). Once the fork cutsv0.3.4, upstream's nextv0.3.4would diverge. Recommend the fork either (a) use a fork-distinct suffix (v0.3.4-roa) or (b) move to its own numbering line. Decide before the first fork release. - The Mac mini re-tap is manual and one-time. Until it's done, the mini stays on upstream's CLI. Document it in the deploy runbook.
- Homebrew tap must be public OR every consumer configures auth. Public is the pragmatic choice; the formula carries no secrets.
Fork CLI commands can only run via go run ./server/cmd/multica ...
from a source checkout, or a hand-built binary. The
ship introspect-pipelines seed was run that way (from the dev
checkout, using the operator's local ~/.multica/config.json auth).