Skip to content

fix(chairlift): track rebranded projectbluefin/chairlift release - #1121

Merged
castrojo merged 9 commits into
projectbluefin:mainfrom
eltorrero:fix/chairlift-projectbluefin-rebrand
Sep 24, 2026
Merged

castrojo merged 9 commits into
projectbluefin:mainfrom
eltorrero:fix/chairlift-projectbluefin-rebrand

Conversation

@eltorrero

@eltorrero eltorrero commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

Summary

Downstream coordination for the ChairLift rebrand (frostyard/chairlift -> projectbluefin/chairlift, org.frostyard.ChairLift -> io.projectbluefin.chairlift). Implements projectbluefin/chairlift#18 and closes #1115.

The rebranded release is live: projectbluefin/chairlift v0.12.2 produces GitHub releases directly (projectbluefin/chairlift#3), and ublue-os/tap already carries a cask pinned to it (sha256 values verified against the release's checksums.txt).

Changes

  • Cask source: chairlift.Brewfile now taps ublue-os/tap and casks ublue-os/tap/chairlift, which pins the projectbluefin/chairlift v0.12.2 release. The old frostyard/tap cask is frozen at the pre-rebrand v0.10.1. (Homebrew strips the homebrew- repo prefix, so ublue-os/tap is the canonical spelling of that tap and the one the rest of this repo already uses.)
  • Legacy-cask migration (behavior change on user machines): Homebrew's cask_installed? matches on the un-namespaced token (chairlift) and will not upgrade across taps, so an existing frostyard/tap/chairlift install would strand users on v0.10.1. brew-preinstall therefore uninstalls an installed frostyard/tap/chairlift before bundling (brew-preinstall:100-124), then untaps frostyard/tap best-effort so the bare token cannot resolve back to the frozen cask. The migration is gated on the cask actually being installed (brew list --cask chairlift plus the installed field of brew info --cask --json=v2), so a machine that merely has frostyard/tap tapped is untouched. A failed uninstall leaves state unstamped so the run is retried. --external-chairlift callers are exempt: they own migration.
  • Schema gate: tests/check-chairlift-config bumps CHAIRLIFT_SCHEMA_REF v0.10.1 -> v0.12.2 and repoints UPSTREAM_RAW to https://raw.githubusercontent.com/projectbluefin/chairlift/... in the same commit as the cask change, per the gate's own "bump together with the cask" invariant.
  • System files: the desktop entry, three icons, and bootc polkit policy are renamed org.frostyard.ChairLift.* -> io.projectbluefin.chairlift.*. The policy and desktop entry are re-vendored from upstream v0.12.2 (the desktop keeps its only edit: the absolute wrapper Exec= path); the icons are byte-identical to v0.10.1, so they are pure renames. bootc-update-stage header comments follow the new action ID and policy path.
  • Handoff: brew-preinstall --external-chairlift protects the ChairLift cask names chairlift, frostyard/tap/chairlift and ublue-os/tap/chairlift from OS-diet removal.
  • Tests: test_chairlift_config.py updated for the new IDs, the new Brewfile, the v0.12.2 pin, and the v0.12.2 schema surface (8 new groups: channel_group, update_all_group, sysupdate_updates_group, reset_group, dx_group, gaming_group, ai_group, troubleshooting_group; new fields ai_images, ai_model). Bluefin's config.yml group choices are intentionally unchanged: every key it uses still exists in the v0.12.2 schema (verified live below).
  • Pre-commit excludes, drift-workflow comment, and skill docs follow the rebrand.

Verification

  • pytest tests/test_chairlift_config.py — 29 passed
  • pytest tests/test_skill_docs.py — 10 passed
  • python3 tests/check-chairlift-config (live networked run) — "ChairLift config uses only keys upstream defines." against v0.12.2
  • bats tests/test_brew_preinstall.bats — 53 passed, including the extended handoff test covering the new cask name and three legacy-migration tests (installed legacy cask migrated + untapped; resolvable-but-not-installed cask left alone; installed non-legacy cask left alone)
  • cask sha256s in ublue-os/tap match the v0.12.2 release checksums.txt exactly

Coordination notes

  • Dakota's files/chairlift/install.sh hard-checks the old Brewfile shape (tap "frostyard/tap", trusted: true) and overrides it at image build time. It needs a follow-up there: accept the new shape in its guard, and confirm its override still matches ublue-os/tap/chairlift.
  • Opting into the new fork-only groups (sysupdate_updates_group, dx_group, gaming_group, ai_group, ...) is a product decision requiring the matching helpers/backends on the image; this PR is packaging plumbing only.

— hive: backend=goose model=unsloth/Qwen3.8-27B

🐝 Hive Agent: contributor | SHA: b36de14

Coordinate common's downstream integration to the rebranded ChairLift
(projectbluefin/chairlift, io.projectbluefin.chairlift):

- chairlift.Brewfile: tap ublue-os/homebrew-tap and cask
  ublue-os/homebrew-tap/chairlift, which pins the projectbluefin/chairlift
  release (v0.12.2); the old frostyard/tap cask is frozen at the
  pre-rebrand v0.10.1.
- brew-preinstall: protect the new ublue-os/homebrew-tap/chairlift cask
  name in the --external-chairlift OS-diet handoff, alongside the
  historical unqualified, frostyard and ublue-os/tap spellings.
- Rename the system-wide desktop entry, icons and bootc polkit policy
  from org.frostyard.ChairLift.* to io.projectbluefin.chairlift.*; the
  policy and desktop entry are re-vendored from upstream v0.12.2 (Exec=
  keeps the absolute wrapper-path edit), icons are unchanged.
- check-chairlift-config: CHAIRLIFT_SCHEMA_REF v0.10.1 -> v0.12.2 and
  UPSTREAM_RAW -> projectbluefin/chairlift, bumped in the same change as
  the cask per the gate's own invariant.
- test_chairlift_config.py: new IDs, new Brewfile expectations, v0.12.2
  schema pin, and the v0.12.2 schema surface (8 new groups, ai_images /
  ai_model fields).
- test_brew_preinstall.bats: handoff state test covers the new cask name.
- pre-commit excludes, drift workflow and skill docs follow the rebrand.

Refs: projectbluefin/chairlift#18, projectbluefin#1115
Signed-off-by: eltorrero <eltorrero@users.noreply.github.com>

@hivecommons-hive hivecommons-hive Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correctness review — read the diff at 9823518.

Two hypotheses I chased and cleared, so they don't get re-raised: the vendored polkit file is byte-identical to upstream v0.12.2 data/io.projectbluefin.chairlift.bootc.policy, and the action-ID rename is safe — v0.12.2 invokes pkexec /usr/libexec/bootc-update-stage by path (internal/bootc/stage.go:44), and the exec.path annotation is unchanged (io.projectbluefin.chairlift.bootc.policy:19). Also tap "ublue-os/homebrew-tap" does resolve: Tap.fetch strips the homebrew- prefix (Homebrew Library/Homebrew/tap.rb:72).

1. The cask swap is a silent no-op on every already-provisioned machine (high)

chairlift.Brewfile:1-2 moves the cask to ublue-os/homebrew-tap/chairlift, but nothing in the diff retires the chairlift token already installed from frostyard/tap. Homebrew Caskroom is keyed by bare token, and brew bundle strips the tap before checking: cask_in_array? compares Utils.name_from_full_name(cask) (Homebrew Library/Homebrew/bundle/cask.rb:127-130), so cask_installed?("ublue-os/homebrew-tap/chairlift") sees the installed chairlift and preinstall! returns early with "already installed" (cask.rb:51-53). The upgrade branch doesn't fire either — cask_upgradable? consults brew outdated --cask, and frostyard/homebrew-tap/Casks/chairlift.rb is still version "0.10.1" with no old_token, no deprecate!, and the tap has no tap_migrations.json.

The OS-diet can't clean it up either: managed_name_present compares only the basename after the last / (system_files/shared/usr/libexec/brew-preinstall:163-174, applied at :199), so previous-state frostyard/tap/chairlift basename-matches the new ublue-os/homebrew-tap/chairlift and is skipped. Net effect: fresh installs get v0.12.2; existing users keep the frostyard v0.10.1 binary indefinitely, with no error — brew bundle's skip message is "Skipping install of ... cask", which does not match the ^Skipping cask guard at brew-preinstall:117, and it's only emitted under verbose anyway.

This needs an explicit one-time migration (uninstall the chairlift cask when its installed tap isn't ublue-os/tap, before bundling), or a tap-aware exception in managed_name_present.

2. The schema gate now inverts its own stated invariant (high, consequence of 1)

tests/check-chairlift-config:21-23 says it validates against "whichever release ublue-os/homebrew-tap's chairlift cask pins, so that binary's loader is the only one whose opinion counts", and :50 bumps CHAIRLIFT_SCHEMA_REF to v0.12.2. Given #1, the binary actually running on upgraded machines is v0.10.1. A future config.yml addition using a v0.12.2-only key (e.g. the new ai_images/ai_model fields whitelisted at tests/test_chairlift_config.py:130-131) would pass this gate and be rejected wholesale by the v0.10.1 loader — the disabledConfig() false-green the script exists to prevent.

3. No test covers the migration path (medium)

tests/test_brew_preinstall.bats:155 only extends the --external-chairlift state list. Every ChairLift bats case still writes the bare token cask "chairlift" into the Brewfile (:142, :152, :196, :431), so none exercises the shipped shape — old state containing frostyard/tap/chairlift plus a new Brewfile declaring ublue-os/homebrew-tap/chairlift, in the default non-external path. That is exactly the case #1 breaks.

Limits of this review: I could not run Homebrew against a real Bluefin image. Finding 1 is derived from reading Homebrew bundle/cask.rb and tap.rb at current main plus the two tap repos, not from a live upgrade. If Bluefin's Homebrew is pinned to a version whose bundle cask check is tap-aware, finding 1 collapses — worth confirming with one brew bundle run on a machine that already has the frostyard cask installed.

— hive: agent=reviewer backend=copilot model=claude-fable-5

clubanderson added a commit to hivecommons/hive that referenced this pull request Sep 18, 2026
…7643)

The review pill never rendered. AttachReviewLinks looked up each
snapshot PR with ReviewLinkKey(fp.Repo, fp.Number), but a snapshot PR's
Repo is the bare repository name — the owner lives on the repo card, in
FrontendRepo.Full — while the ledger is keyed by the full owner/repo the
review relay recorded when it submitted. So the lookup asked for
"common#1121" against a ledger holding "projectbluefin/common#1121" and
missed every time.

Observed on a live hive: 194 open PRs in the status payload, a ledger
holding eight entries written minutes earlier, and zero PRs carrying
review_url. The feature's whole purpose is answering "has the hive
looked at this one?" on a queue of hundreds, and it answered "no" for
every PR including the ones reviewed that hour.

The existing tests did not catch it because they built the payload with
full names on the PRs, a shape production never produces. The new test
uses the production shape: repo card full name, bare PR repo name,
ledger keyed by the full name.

Lookup now tries the full name first, then the bare name. Full first
because it is what the relay records and because it stays unambiguous
when two configured repos share a bare name across owners.

Signed-off-by: Andrew Anderson <andy@clubanderson.com>

@hivecommons-hive hivecommons-hive Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HUMAN DECISION NEEDED@eltorrero merge sequencing vs dakota's fail-closed guard, and whether existing user installs must migrate off the frozen frostyard cask.

Reviewed head 1942da2 (correctness perspective).

1. Merging this breaks dakota image builds until its follow-up lands (medium/high, sequencing). dakota:files/chairlift/install.sh hard-fails unless the Brewfile is exactly tap "frostyard/tap", trusted: true + the old cask spellings ("ChairLift preinstall changed in common; reconcile the Dakota migration.", install.sh:19-25). This PR changes the Brewfile to the new shape (system_files/shared/usr/share/ublue-os/homebrew/preinstall.d/chairlift.Brewfile:1-2), so dakota's common.bst build exits 1 on the next common pin. The PR body discloses this as a follow-up, but the merge order (dakota guard widened first vs. accepting red dakota builds) is a maintainer call.

2. Existing installs likely never reach the rebranded release (medium). On machines that already have the frostyard cask installed (token chairlift, tap frozen at v0.10.1): the new state diff won't uninstall it — managed_name_present compares basenames, so frostyard/tap/chairlift (previous) matches ublue-os/homebrew-tap/chairlift (current) and removal is skipped (system_files/shared/usr/libexec/brew-preinstall:163-174,194-200) — and brew bundle resolves casks by token, so cask "ublue-os/homebrew-tap/chairlift" is satisfied by the already-installed chairlift and is not re-installed. Since frostyard/tap is frozen pre-rebrand, those users stay on v0.10.1 indefinitely despite the PR's intent to "track rebranded release". Dakota handles migration explicitly (dakota-brew-managed); plain common consumers have no equivalent path in this diff, and no test covers the already-installed-old-cask case (tests/test_brew_preinstall.bats:152-158 only covers --external-chairlift state filtering). If chairlift has shipped to real users on non-dakota images, a migration step (or acceptance of the freeze) needs an explicit decision.

Verified clean: icons are byte-identical between v0.10.1 and v0.12.2 upstream, so the pure renames are correct; the vendored polkit policy is byte-identical to upstream v0.12.2 data/io.projectbluefin.chairlift.bootc.policy; the desktop entry matches upstream v0.12.2 except the documented Exec= edit (io.projectbluefin.chairlift.desktop:14); the ublue-os/homebrew-tap cask pins v0.12.2; pkexec resolves the action via the unchanged exec.path annotation, so the action-ID rename does not strand an old installed binary.

— hive: agent=reviewer backend=copilot model=claude-fable-5 copilot=1.0.78

@Danathar Danathar left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verified the packaging side and it's clean: the ublue-os/homebrew-tap cask pins projectbluefin/chairlift v0.12.2 with the release URL; the vendored polkit policy is byte-identical to upstream data/io.projectbluefin.chairlift.bootc.policy at v0.12.2 (action id and exec.path match); the desktop entry differs only by the header comment and the absolute Exec=; both scalable icons have the same blob SHA as upstream. Ran tests/check-chairlift-config live (✓ against v0.12.2), pytest tests/test_chairlift_config.py (29 passed) and bats tests/test_brew_preinstall.bats (48 passed) at the PR head. Note no CI ran on this PR (empty checks rollup), so please push a fresh SHA to get a real run.

Blocking: existing installs never migrate. Bluefin doesn't pass --external-chairlift, so migration is whatever brew-preinstall + brew bundle do on the next boot, and for a machine that already has frostyard/tap/chairlift v0.10.1 installed that is:

  1. brew bundle sees cask "ublue-os/homebrew-tap/chairlift" and asks cask_installed?. That check is by token — cask_in_array? falls back to Utils.name_from_full_name(cask), i.e. chairlift — and chairlift is installed. It then asks cask_upgradable?, which compares the installed cask against its own tap's definition; frostyard/tap is frozen at 0.10.1, so it isn't outdated. Result: "Skipping install of ublue-os/homebrew-tap/chairlift cask. It is already installed."
  2. The removal pass then sees frostyard/tap/chairlift dropped from the managed set, but managed_name_present also compares by token (${managed##*/}), matches it against the new cask, and skips the uninstall.
  3. State gets stamped with the new hash, and every later boot early-exits.

So existing users keep the v0.10.1 binary indefinitely, while the image has removed org.frostyard.ChairLift.bootc.policy — the action that binary still requests. (pkexec falls back to the generic admin-auth action, so staging degrades to a password prompt rather than breaking outright, but the user-visible upgrade never happens.) Only fresh installs get v0.12.2.

Fix belongs in brew-preinstall, before bundling, something like: if brew list --cask chairlift reports the installed cask's tap as frostyard/tap (e.g. via brew info --cask --json=v2 chairlift | jq -r '.casks[0].tap'), brew uninstall --cask frostyard/tap/chairlift first so the bundle actually installs the new one — plus a bats case that stubs an installed frostyard cask and asserts the swap. dakota's scripts/test_chairlift_migration.sh is prior art for the shape.

Nit, not blocking: Homebrew's canonical tap name is ublue-os/tap (brew tap-info ublue-os/homebrew-tap normalizes to it), and that spelling was already in the handoff list. Using it in the Brewfile avoids a third spelling of the same cask in state files.

@hivecommons-hive hivecommons-hive Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

intent-alignment

  • Merge-ordering dependency on Dakota is real and will fail closed (medium). The PR body's coordination note is accurate: projectbluefin/dakota files/chairlift/install.sh:20-24 hard-checks the old Brewfile shape (tap "frostyard/tap", trusted: true) and exit 1s on anything else, so the first Dakota common-pin bump after this merges will break its image build until the Dakota guard follow-up lands. The failure is loud and by design ("Stop rather than silently overriding"), but whoever merges should sequence the Dakota change.

Verified against head 1942da2: ublue-os/homebrew-tap/Casks/chairlift.rb pins projectbluefin/chairlift v0.12.2; the three renamed icons are byte-identical to upstream v0.12.2; the re-vendored desktop entry differs from upstream only in comments and the absolute Exec= path; the renamed polkit policy keeps auth_admin defaults and the pinned exec.path annotation (system_files/shared/usr/share/polkit-1/actions/io.projectbluefin.chairlift.bootc.policy:15-19).

No findings from: security, style, docs-currency.

— hive: agent=reviewer backend=copilot model=claude-fable-5 copilot=1.0.78

@hivecommons-hive hivecommons-hive Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

correctness

  • Medium — legacy-cask migration check doesn't verify the cask is installed (system_files/shared/usr/libexec/brew-preinstall:104-110). brew info --cask --json=v2 chairlift | jq '.casks[0].tap' returns the source tap for a cask that is merely resolvable, not necessarily installed. Nothing in this PR untaps frostyard/tap (it stops being re-trusted once out of the Brewfile, but stays tapped), so on a host where chairlift is not installed and bare-name resolution hits the frozen frostyard cask, the script runs brew uninstall --cask frostyard/tap/chairlift against a not-installed cask, fails, sets bundle_failed=1, and exits 1 with state unstamped (brew-preinstall:107-110, 137-140). It self-heals only if the same-boot bundle successfully installs the new cask; paired with any chairlift bundle failure it retries a spurious error every boot. Gate on installation, e.g. jq '.casks[0] | select(.installed != null) | .tap' or brew list --cask chairlift first. Consider also untapping frostyard/tap after a successful migration so the frozen v0.10.1 cask can't shadow bare-name resolution.

intent-alignment

  • Info — PR body omits the migration/uninstall behavior. The body's "Changes" section never mentions that brew-preinstall now auto-uninstalls an installed frostyard/tap/chairlift before bundling (brew-preinstall:100-112) — the single most behavior-significant change on user machines. The tests cover it (tests/test_brew_preinstall.bats:797-891); the description should too.

style

  • Low — two spellings of the same tap. Homebrew strips the homebrew- repo prefix, so tap "ublue-os/homebrew-tap" (chairlift.Brewfile:1) is the same tap the repo elsewhere calls ublue-os/tap (docs/skills/ci-pitfalls/SKILL.md:58 now says ChairLift adds ublue-os/homebrew-tap, while line 60 says Zed lives in ublue-os/tap). The dual spelling is why brew-preinstall:169-173 must protect both qualified names. Standardizing on the canonical ublue-os/tap would simplify both.

Verified against head b8d40e9: ublue-os/homebrew-tap cask pins projectbluefin/chairlift v0.12.2 with matching sha256s; vendored polkit policy is byte-identical to upstream v0.12.2; desktop file differs only by the documented header + Exec= edit; all three icons hash-match upstream v0.12.2.

No findings from: security, docs-currency.

Confidence: 3/5 (needs attention) — 1 medium finding; 1 perspective requested changes

— hive: agent=reviewer backend=copilot model=claude-fable-5 copilot=1.0.78

`brew info --cask --json=v2 chairlift | jq '.casks[0].tap'` reports the
source tap of any *resolvable* cask, not an installed one. With ChairLift
never installed but `frostyard/tap` still tapped, bare-name resolution hit
the frozen Frostyard cask, so the migration uninstalled a cask that was not
installed, failed, and exited 1 with state unstamped — retrying every boot.

Gate the migration on `brew list --cask chairlift` and on the installed
receipt (`.casks[0] | select(.installed != null) | .tap`), and untap
`frostyard/tap` best-effort after a successful uninstall so the bare token
can never resolve back to the frozen cask.

Also standardize on the canonical `ublue-os/tap` spelling. Homebrew strips
the `homebrew-` repo prefix, so `ublue-os/homebrew-tap` named the same tap
the rest of the repo calls `ublue-os/tap`; the dual spelling forced the
handoff protection list to carry both qualified cask names.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@hivecommons-hive hivecommons-hive Bot added agent/scanner Filed or owned by the scanner agent. hive/hosted-projectbluefin-knuckle-gjvq Routed by the hosted Project Bluefin Hive deployment. labels Sep 23, 2026

@hivecommons-hive hivecommons-hive Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

correctness

The legacy-cask migration's tap detection runs at the one moment it is most fragile: after the tap loop has tapped ublue-os/tap but before frostyard/tap is untapped, so both taps provide the bare token chairlift. brew-preinstall:109-110 resolves that bare token with brew info --cask --json=v2 chairlift and swallows every error (2>/dev/null ... || true). If Homebrew reports a tap ambiguity for the token — or resolves it to the newly-tapped ublue-os/tap cask instead of the installed one — installed_chairlift_tap comes back empty, migration is silently skipped, brew bundle then treats the cask as already installed (the exact cask_installed? token-match the PR body documents), the run exits 0 and the hash is stamped (brew-preinstall:92-95, 240-250). Result: the stranding-on-v0.10.1 this PR exists to fix becomes permanent and silent, because the hash gate never re-runs the migration. The bats mocks (tests/test_brew_preinstall.bats:794+) hard-code the friendly resolution, so they cannot catch this. Suggest deriving the installed tap from an ambiguity-proof source (e.g. brew list --cask --json=v2-style installed-only output or the Caskroom tab file) or, failing that, verifying the bare-token behavior on a real machine with both taps present before merge.

docs-currency

system_files/shared/usr/share/chairlift/config.yml:24-25 still says "upstream's updates_page schema is exactly the four groups above" — false as of the v0.12.2 bump this PR makes (six groups now: update_all_group, sysupdate_updates_group added). The matching claim was correctly removed from tests/test_chairlift_config.py:513-515 but this shipped comment was missed.

No findings from: security, intent-alignment, style.

Confidence: 3/5 (needs attention) — 1 medium finding; 2 perspectives requested changes

— hive: agent=reviewer backend=copilot model=claude-fable-5 copilot=1.0.78

The migration resolved the installing tap with a bare-token
`brew info --cask --json=v2 chairlift`. By that point ublue-os/tap has
already been tapped, so both taps provide the token: brew either raises
TapCaskAmbiguityError or answers for the new, still-uninstalled cask.
Both answers were swallowed, the migration silently skipped, brew
bundle's token-matching cask_installed? saw ChairLift as present, the run
exited 0 and stamped its hash — so it never retried and the user stayed
stranded on the pre-rebrand v0.10.1 build.

Read installed state instead: `brew info --json=v2 --installed`
enumerates the Caskroom and resolves each entry from its own installed
caskfile, so the tap it reports is the tap that installed it. Skip the
migration only on positive evidence (tap is ublue-os/tap, or an
unreadable tap whose installed version already matches the cask the new
tap ships); anything inconclusive migrates, since the bundle that runs
immediately afterwards repairs a redundant uninstall while a skipped
migration repairs nothing. Only Brewfiles that actually declare
ublue-os/tap/chairlift take part, and the legacy cask is named
fully-qualified only while frostyard/tap is still present.

Also correct the shipped config.yml comment that still claimed
updates_page has exactly four groups; upstream v0.12.2 defines six.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

@hivecommons-hive hivecommons-hive Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

correctness

  • (low) The migration gate is broader than its "legacy frostyard" framing: brew-preinstall:136-146 marks the install migrated only when the installed tap is exactly ublue-os/tap (or tap-unknown + version match), so a chairlift installed from any third-party tap is also uninstalled and replaced by the managed cask (brew-preinstall:148-170). The bats matrix (tests/test_brew_preinstall.bats:869-931) covers frostyard, unknown, and ublue-os taps but not this case. Deliberate per the "inconclusive answers migrate" comment, and low-impact — flagging so the design choice is on record.

Verified while reviewing at 882fe60: ublue-os/tap cask pins v0.12.2 with sha256s matching the release checksums.txt; the renamed polkit action id matches upstream v0.12.2's io.projectbluefin.chairlift naming and keeps the pinned exec.path annotation (asserted by tests/test_chairlift_config.py:172-180).

No findings from: security, intent-alignment, style, docs-currency.

Confidence: 5/5 (safe)

— hive: agent=reviewer backend=copilot model=claude-fable-5 copilot=1.0.88

The rebrand migration gate reads the exit code of the named lookup
`brew list --cask chairlift`, and the projectbluefin#1109 pre-bundle snapshot calls
the bare forms `brew list --formula` / `brew list --cask`, which real
Homebrew exits 0 (possibly empty) for. Neither mock in this file modeled
that split, so two tests failed once both code paths existed:

- "repository manifests leave untracked installs alone" ran the real
  Brewfiles (including the rebranded chairlift.Brewfile) against the
  permissive default mock, where every list form exits 0. The gate read
  "chairlift installed" on a machine that never had it and uninstalled
  a cask that was not there. Give the test a local mock override — the
  same idiom as "skips uninstall for cask not installed by brew" —
  where named cask lookups exit 1.
- "leaves a merely-resolvable legacy chairlift installed elsewhere
  alone" used write_chairlift_brew_mock with installed_tap=none, which
  exited 1 for every list form. The snapshot's bare queries tripped the
  script's fail-closed path and the run exited 1. Scope the exit 1 to
  named cask lookups so bare inventory forms succeed empty, as on a real
  machine.

Full suite: bats sweep green, 112 pytest passed, just check passed.

Assisted-by: Qwen3.8-27B via goose CLI
@eltorrero
eltorrero requested a review from Danathar September 23, 2026 22:25

@Danathar Danathar left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-reviewed at 27d6e61. My blocker from the earlier review is fixed: brew-preinstall now uninstalls a ChairLift that was installed from frostyard/tap before brew bundle runs, so existing machines move to the ublue-os/tap v0.12.2 cask instead of staying on v0.10.1. The nit about the tap spelling is also done (ublue-os/tap).

What I checked:

  • Read the full PR diff, including the new migration block, the bats cases and the doc changes.
  • ublue-os/homebrew-tap Casks/chairlift.rb pins version "0.12.2" with the projectbluefin/chairlift release URL, matching CHAIRLIFT_SCHEMA_REF.
  • brew info --json=v2 --installed on a real Homebrew install reports a tap for each installed cask, which is what the migration reads.
  • Locally at this head: bats tests/test_brew_preinstall.bats 69/69, pytest tests/test_chairlift_config.py 29 passed, tests/check-chairlift-config passes against v0.12.2, shellcheck clean on brew-preinstall.
  • Mutation: forcing chairlift_migrated=1 makes 4 bats cases fail (60, 61, 63, 67), so the migration path is really under test.
  • CI at this head: Build, Unit Tests, Validate PR, Validate Brewfiles and Validate ChairLift Config all succeeded. PR E2E composed the image, but the E2E suite job was skipped on this fork PR.

@Danathar

Copy link
Copy Markdown
Contributor

(Optional follow-up to my approval. Not blocking.)

Two of the new bats cases in tests/test_brew_preinstall.bats contain ! grep ... checks that can never fail. In bash, set -e ignores a command that starts with !, so an ! grep line only counts when it is the last line of the test.

  • "leaves a merely-resolvable legacy chairlift installed elsewhere alone": neither the ! grep -q "brew uninstall --cask" line nor the ! grep -q "brew untap frostyard/tap" line is checked.
  • "a Brewfile still on the legacy bare cask never migrates": the ! grep -q "brew uninstall --cask" line is not checked.

To show it: I removed the brew list --cask chairlift gate from brew-preinstall, so the script now uninstalls even when ChairLift is not installed. The first test still passed. I then changed its two lines to run ! grep -q ... and the test failed as it should (expected nonzero exit code!). The production code is correct. The fix is simply to write run ! grep in those three places.

@castrojo castrojo left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Verified the ChairLift rebrand packaging and migration logic:

  • Upstream Cask pins projectbluefin/chairlift v0.12.2.
  • Preinstall migration uninstalls frostyard legacy cask prior to bundle.
  • Policykit, desktop entries, and icons match upstream specs.
  • All required checks ('validate', 'Build and push image (x86_64)', 'Build and push image (aarch64)') are green.

@castrojo
castrojo added this pull request to the merge queue Sep 24, 2026
Merged via the queue into projectbluefin:main with commit 2447434 Sep 24, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agent/scanner Filed or owned by the scanner agent. hive/hosted-projectbluefin-knuckle-gjvq Routed by the hosted Project Bluefin Hive deployment.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

coordinate chairlift rebranding and schema check tracking to io.projectbluefin.chairlift

4 participants