Skip to content

fix(skills): update Homebrew-managed archdev with brew, never a second copy - #85

Merged
calvin-archastro merged 1 commit into
mainfrom
fix/bootstrap-homebrew-upgrade
Oct 23, 2026
Merged

calvin-archastro merged 1 commit into
mainfrom
fix/bootstrap-homebrew-upgrade

Conversation

@calvin-archastro

Copy link
Copy Markdown
Contributor

Problem

On a Linux machine with archdev installed by Homebrew (/home/linuxbrew/.linuxbrew/bin/archdev → ../Cellar/archdev/<v>/bin/archdev, formula archastro/tools/archdev), the archdev skill's scripts/bootstrap.sh installed a second copy at ~/.local/bin/archdev twice:

  • 2026-10-22 22:58:34Z. The keg binary had been overwritten by a test stub (#!/bin/sh / echo "archdev 3.0.0"). The stub passes the version check but fails the agents run --help capability probe. Bootstrap printed "Updating ArchDev: this skill requires 0.49.6+ …" and installed 0.54.14 into ~/.local/bin.
  • 2026-10-23 15:44:31Z. Same message. The brew copy was an outdated 0.54.13, and bootstrap installed 0.54.19 into ~/.local/bin.

That second copy came first on PATH and later took over the user's daemon.

How it happened:

command -v archdev → <prefix>/bin/archdev (Homebrew keg)
supports_skill fails (outdated, or a stub with a high version)
→ install_archdev: pinned installer with ARCHDEV_INSTALL_DIR=~/.local/bin forced
→ ~/.local/bin/archdev now shadows the keg; brew and the installer upgrade separately

The bootstrap never checked who owns the binary it found. The pinned installer (a530d21…) has no Homebrew detection either. The current install.sh does detect Homebrew (is_homebrew_binary), but only reports a brew copy and never upgrades it.

Change

Before → after for the three bootstrap sources (archdev/, tasks/, tasks-execute/ scripts/bootstrap.{sh,ps1}):

  • Homebrew-managed archdev. This is the archdev on PATH, or <brew --prefix>/bin/archdev, whose real path is under Cellar/archdev/. Before, the bootstrap installed a second copy into ~/.local/bin. Now it repairs the copy with the keg's own brew:
    • brew upgrade archastro/tools/archdev when the binary reports a version older than the minimum.
    • brew reinstall when the binary is missing (dangling link), crashes, or reports a current version but fails the probes (the stub case, where upgrade would do nothing).
  • What it prints. It prints the brew command to stderr and sends brew's output to stderr, so stdout is still only the path.
  • Brew missing or failing. It stops with an error naming the brew-managed binary and does not install a second copy.
  • Brew copy present but behind another copy on PATH. The bootstrap uses the brew copy and says which copy it skipped.
  • Which brew runs. It runs the brew that owns the keg (<keg prefix>/bin/brew), not whichever brew comes first on PATH. A second Homebrew prefix could otherwise install another copy.
  • Opt links. A PATH that reaches the keg through opt/archdev/bin keeps that path. Bash used to resolve it to the versioned keg, which an upgrade removes.
  • Unchanged.
    • An installer copy that is not brew-managed, and a fresh install with no archdev, keep the pinned installer and its sha256 check. This repo had no policy for preferring brew on fresh installs, so this PR doesn't add one.
    • Windows PowerShell behavior is unchanged: Homebrew detection returns early on Windows_NT.
    • No hooks, login or configuration changes.

Pinned installer not bumped. ARCHDEV_INSTALL_DIR is still forced, but now only for non-Homebrew installs, so it can no longer bypass Homebrew detection. A newer installer would not fix the problem: it only reports a brew copy and never upgrades it. It would also bring sign-in and setup into bootstrap (fc3d6d4 and later), which the skill contract forbids.

archdev/references/bootstrap.md gains one sentence on the Homebrew behavior. The plugin copies under plugins/* were regenerated with tools/plugin-gen (npm run build). That is where most of this diff comes from.

Risk

Medium-low. This is the path every skill session takes to resolve the CLI. Machines without a Cellar-linked archdev take the old code path. The one added call is brew --prefix, and it runs only when brew is on PATH and the archdev found is not brew-managed.

Two behaviors are new:

  • Bootstrap may now run brew upgrade or brew reinstall. It runs these in the same place it used to run the installer, under the same agent approval for installs and upgrades that the skill already requires.
  • When a brew copy exists, bootstrap now returns it even if a working installer copy comes first on PATH.

Testing

  • scripts/test-skill-bootstrap.sh: all cases pass, including 16 new Homebrew cases per skill. Each case builds a temp HOME, a fake <prefix>/Cellar/archdev/<v>/bin/archdev with bin/ and opt/ links, and scripts/fake-brew, which records its calls and does nothing on upgrade when the keg is already the latest version, like real brew. The new cases:
    • Brew repairs the keg, no second copy: outdated → upgrade; stub reporting 3.0.0 → reinstall; crashing binary → reinstall; dangling link → reinstall; PATH through the opt link → upgrade, opt path kept; another prefix's brew first on PATH → the keg's own brew runs, the other one never does; outdated brew copy behind an installer copy → upgrade.
    • Brew copy used as is, brew never asked to change anything: current brew copy (brew not run at all); current brew copy behind an installer copy.
    • Stops with an error naming the binary, no download: brew missing; brew upgrade failing.
    • Old behavior kept: outdated installer copy → pinned installer; working installer copy with brew present but no keg → used as is; nothing installed (with and without brew) → pinned installer.
    • Every case also checks that stdout is only the path, that no download was attempted, that no file under HOME changed and that no hook was written.
    • Against the old bootstrap, every Homebrew repair case fails.
  • scripts/test-skill-bootstrap.ps1 with pwsh 7.6 on Linux: 79 cases pass, the same Homebrew cases plus the existing ones.
  • CI runs both on ubuntu-latest, and PowerShell on windows-latest, where the Homebrew cases are skipped.
  • tools/plugin-gen: npm run check, npm run typecheck and npm test pass. One local run failed the nudge hook's "under 100 ms" timing test while the machine was loaded; the rerun passed.
  • Not tested against a real Homebrew install. The tests use a fake brew only.

Known limitations

  • A keg that is installed but unlinked (brew unlink archdev, so no bin/archdev) is not detected and falls through to the installer path.
  • pwsh before 7.1 has no LinkTarget, so Homebrew detection there finds nothing and the bootstrap keeps the old installer behavior.

🤖 Generated with Claude Code

…d copy

The skill bootstrap scripts resolved `archdev` and, when it was missing,
too old or failed the capability probes, ran the pinned installer with a
forced ARCHDEV_INSTALL_DIR (~/.local/bin). On a Homebrew machine that
left a second copy beside the keg, which later shadowed it on PATH.

Now, when the archdev on PATH, or <brew --prefix>/bin/archdev, resolves
into a Homebrew Cellar, the bootstrap repairs it with the keg's own brew:
`brew upgrade archastro/tools/archdev` for a too-old version, `brew
reinstall` for a missing, crashing or current-but-broken binary. It prints
the brew command, keeps brew output off stdout, and stops with an error
naming the brew-managed binary when brew is missing or fails. Installer
copies and fresh installs keep the pinned installer. PowerShell mirrors
this on macOS/Linux; Windows is unchanged.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@calvin-archastro
calvin-archastro merged commit 23845c5 into main Oct 23, 2026
20 checks passed
@calvin-archastro
calvin-archastro deleted the fix/bootstrap-homebrew-upgrade branch October 23, 2026 21:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant