Conversation
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>
There was a problem hiding this comment.
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
…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>
There was a problem hiding this comment.
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
left a comment
There was a problem hiding this comment.
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:
brew bundleseescask "ublue-os/homebrew-tap/chairlift"and askscask_installed?. That check is by token —cask_in_array?falls back toUtils.name_from_full_name(cask), i.e.chairlift— andchairliftis installed. It then askscask_upgradable?, which compares the installed cask against its own tap's definition;frostyard/tapis frozen at 0.10.1, so it isn't outdated. Result: "Skipping install of ublue-os/homebrew-tap/chairlift cask. It is already installed."- The removal pass then sees
frostyard/tap/chairliftdropped from the managed set, butmanaged_name_presentalso compares by token (${managed##*/}), matches it against the new cask, and skips the uninstall. - 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.
There was a problem hiding this comment.
intent-alignment
- Merge-ordering dependency on Dakota is real and will fail closed (medium). The PR body's coordination note is accurate:
projectbluefin/dakotafiles/chairlift/install.sh:20-24hard-checks the old Brewfile shape (tap "frostyard/tap", trusted: true) andexit 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
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/chairliftv0.12.2 produces GitHub releases directly (projectbluefin/chairlift#3), andublue-os/homebrew-tapalready carries a cask pinned to it (sha256 values verified against the release'schecksums.txt).Changes
chairlift.Brewfilenow tapsublue-os/homebrew-tapand casksublue-os/homebrew-tap/chairlift, which pins theprojectbluefin/chairliftv0.12.2 release. The oldfrostyard/tapcask is frozen at the pre-rebrand v0.10.1.tests/check-chairlift-configbumpsCHAIRLIFT_SCHEMA_REFv0.10.1 -> v0.12.2 and repointsUPSTREAM_RAWtohttps://raw.githubusercontent.com/projectbluefin/chairlift/...in the same commit as the cask change, per the gate's own "bump together with the cask" invariant.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 wrapperExec=path); the icons are byte-identical to v0.10.1, so they are pure renames.bootc-update-stageheader comments follow the new action ID and policy path.brew-preinstall --external-chairliftnow also protectsublue-os/homebrew-tap/chairliftfrom OS-diet removal, alongside the historical unqualified,frostyard/tap, andublue-os/tapspellings.test_chairlift_config.pyupdated 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 fieldsai_images,ai_model). Bluefin'sconfig.ymlgroup choices are intentionally unchanged: every key it uses still exists in the v0.12.2 schema (verified live below).Verification
pytest tests/test_chairlift_config.py— 29 passedpytest tests/test_skill_docs.py— 10 passedpython3 tests/check-chairlift-config(live networked run) — "ChairLift config uses only keys upstream defines." against v0.12.2bats tests/test_brew_preinstall.bats— 48 passed, including the extended handoff test covering the new cask nameublue-os/homebrew-tapmatch the v0.12.2 releasechecksums.txtexactlyCoordination notes
files/chairlift/install.shhard-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 decide whether its override should also move fromublue-os/tap/chairlifttoublue-os/homebrew-tap/chairlift.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:9823518