Skip to content

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

Open
eltorrero wants to merge 2 commits into
projectbluefin:mainfrom
eltorrero:fix/chairlift-projectbluefin-rebrand
Open

eltorrero wants to merge 2 commits into
projectbluefin:mainfrom
eltorrero:fix/chairlift-projectbluefin-rebrand

Conversation

@eltorrero

Copy link
Copy Markdown

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/homebrew-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/homebrew-tap and casks ublue-os/homebrew-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.
  • 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 now also protects ublue-os/homebrew-tap/chairlift from OS-diet removal, alongside the historical unqualified, frostyard/tap, and ublue-os/tap spellings.
  • 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 — 48 passed, including the extended handoff test covering the new cask name
  • cask sha256s in ublue-os/homebrew-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 decide whether its override should also move from ublue-os/tap/chairlift to ublue-os/homebrew-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: 9823518

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>

@kubestellar-hive kubestellar-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>

@kubestellar-hive kubestellar-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

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.

@kubestellar-hive kubestellar-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

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.

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

2 participants