Bug fixes: spacing relaunch, hotkeys, XPC on ad hoc builds, permission wait, data race, allowlist - #35
Merged
Conversation
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BSzFQk1ZzGXFVBDZMYq8eu
- Add the pure, tested SpacingRelaunch in holzBar/Core: every owner except holzBar, Control Center and MenuBarAgent, distinct and sorted; a skipped owner no longer ends the loop (BUG-01) - Read the item owners through MenuBarItemProvider27 on macOS 27 and never quit MenuBarAgent (BUG-03) - Collect failures from the task group's results instead of mutating a captured array - Add the HolzBarCore and HolzBarCoreTests targets to the test-only package Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BSzFQk1ZzGXFVBDZMYq8eu
- waitUntil returns true when the event happens, false at the timeout and false at once when cancelled; quitTimeout is 10 seconds - SpacingRelaunch gets stubs so the tests compile and fail on their assertions Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BSzFQk1ZzGXFVBDZMYq8eu
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BSzFQk1ZzGXFVBDZMYq8eu
- SpacingRelaunch.waitUntil races the event against the timeout in a task group: true when the event happens, false at the timeout or at once when cancelled; it always returns and polls nothing - quitTimeout is 10 seconds; an app still running then is left alone and named in the alert, never force terminated (BUG-02) - quit(_:) observes isTerminated through key-value observation and waits through waitUntil; the Combine sink, the checked continuation and the force-termination fallback are gone Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BSzFQk1ZzGXFVBDZMYq8eu
- SUMMARY with PR #35, CI results (124 tests) and the open human checks - BUG-01, BUG-02 and BUG-03 marked complete; roadmap and state updated Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BSzFQk1ZzGXFVBDZMYq8eu
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BSzFQk1ZzGXFVBDZMYq8eu
- Move the Modifiers type, canonicalOrder and symbolicValue into the pure holzBar/Core/Modifiers.swift; the AppKit and Carbon conversions stay in holzBar/Hotkeys/ModifierFlags.swift - Add Modifiers.Rejection and a stub rejection(refusesOptionOnly:) - Add the Modifiers Swift Testing suite Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BSzFQk1ZzGXFVBDZMYq8eu
- Modifiers.rejection(refusesOptionOnly:) refuses no modifier, Shift alone and, on macOS 15 and later, Option or Option and Shift alone - The recorder shows an alert that says why and to add Command or Control, and keeps recording; the system-reserved alert gets a message - HotkeyRegistry logs the reason instead of calling RegisterEventHotKey for an Option-only combination; the hotkey signature is unchanged - Fix the colon spacing in the ModifierFlags initializers Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BSzFQk1ZzGXFVBDZMYq8eu
- waitForPermission() returns a Bool from its own AsyncStream, so any number of waits can run at once and each one ends - A grant ends every pending wait with true; stopCheck() ends them with false, and a cancelled task returns at once - The Grant buttons only reopen the permissions window on true - No stored single waiter and no checked continuation any more Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BSzFQk1ZzGXFVBDZMYq8eu
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BSzFQk1ZzGXFVBDZMYq8eu
- Drop cask_renames.json and the former-name app, quit and zap entries - The release workflow only updates version and sha256 - The cask workflow runs brew style and brew audit from the local tap Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BSzFQk1ZzGXFVBDZMYq8eu
…ntry - Import settings only from the original Ice - Accept only holzbar:// URLs (ice-bar stays as a shelf alias) - An unknown stored icon image set decodes as the default one Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BSzFQk1ZzGXFVBDZMYq8eu
- README drops the section for users of the former name and the gallery caption - NOTICE, CLAUDE.md and the Raycast README name only holzBar and Ice - Past release notes use the holzBar tap, cask, app and URLs - CLAUDE.md forbids the former name; a former-name job in build.yml enforces it Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BSzFQk1ZzGXFVBDZMYq8eu
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BSzFQk1ZzGXFVBDZMYq8eu
- Shared/CodeSigning/CodeSignature.swift reads the team of this process and the signing identifier and per-slice code directory hashes of code on disk (Security only, so the app keeps launching on macOS 14.0) - Package.swift compiles it as SharedCodeSigning with a test target - The CodeSignature suite checks the hashes against a running process with SecCodeCheckValidityWithProcessRequirement Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BSzFQk1ZzGXFVBDZMYq8eu
SecCodeCopySelf's code made SecCodeCheckValidityWithProcessRequirement return errSecParam (-50) on the runner. The test now looks the process up by its audit token and logs the compared hashes, the running cdhash and SecTask's verdict when it fails. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BSzFQk1ZzGXFVBDZMYq8eu
- On macOS 26 the listener requires holzBar's signing identifier and one of the code directory hashes of the app it is embedded in when the service has no team (ad hoc), and the same team plus the identifier otherwise - The listener does not listen when that requirement cannot be built (fail closed); the app then looks up source processes itself - The app sets its same-team peer requirement only when it has a team - MenuBarItemService.appIdentifier names the only app the service answers Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BSzFQk1ZzGXFVBDZMYq8eu
The app comment explains why lightweight code requirements are not used there without naming the module, so the module name appears only in MenuBarItemService/Listener.swift among the app and service sources. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BSzFQk1ZzGXFVBDZMYq8eu
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BSzFQk1ZzGXFVBDZMYq8eu
- getEventSource(with:) looks up, creates and stores its sources inside one OSAllocatedUnfairLock (withLockUnchecked, as CGEventSource is not Sendable) - concurrent moves and clicks can no longer race on the static cache (BUG-04) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BSzFQk1ZzGXFVBDZMYq8eu
…ange - every drawn item (0, 2, 6, 8) is allowed - the allowlist is exactly 0 through 127, the highest measured number - nothing beyond the measurement is offered Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BSzFQk1ZzGXFVBDZMYq8eu
- SystemItems27 (Core) holds the drawn numbers, the highest measured number (127) and the allowlist derived from them, 0 through 127 - MenuBarAssessmentAssertion27 builds its configuration from SystemItems27.allowed; its comment now says what the code does and how jordanbaird/Ice#1001's 0 to 63 relates, instead of claiming they match (BUG-08) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BSzFQk1ZzGXFVBDZMYq8eu
- 02-04-SUMMARY.md with the allowlist decision (D-03), CI result and the phase's open human checks - STATE, ROADMAP and REQUIREMENTS (BUG-04, BUG-08 complete) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BSzFQk1ZzGXFVBDZMYq8eu
holzcloud
marked this pull request as ready for review
October 2, 2026 16:25
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Phase 2, the bug fixes, is complete: this pull request fixes BUG-01 to BUG-08 (spacing relaunch, hotkeys, XPC on ad hoc builds, permission wait, data race, allowlist) and removes the app's former name from the repository. It stays a draft until it is marked ready.
holzBar/Corefolder (HolzBarCore,SpacingRelaunchsuite).isTerminatedinstead of polling and always returns, at the event, the timeout or cancellation.MenuBarItemManager.getEventSource(with:)is read and filled only inside oneOSAllocatedUnfairLock(withLockUnchecked, asCGEventSourceis not Sendable), so concurrent item moves and clicks can no longer corrupt it.Modifierssuite).Shared/CodeSigning/CodeSignature.swiftreads the hashes with Security only, so the app still launches on macOS 14.0 (CodeSignaturesuite).Permission.waitForPermission()returns a Bool and any number of waits end, so the permissions window comes to the front once when a permission is granted and does not reopen by itself.SystemItems27.swift(SystemItems27suite), the assertion builds its configuration from it, and its comment says what the code does: macOS 27 support: remaining gaps on top of #995 jordanbaird/Ice#1001 keeps 0 to 63, which lies inside it.holzbar://, an unknown stored icon decodes as the default, past release notes say holzBar, and aformer-namejob in build.yml keeps the name out.Verified by: the CI build (BUILD SUCCEEDED, no warning in the changed files),
swift test(140 tests in 26 suites, including the newSpacingRelaunch,Modifiers,CodeSignatureandSystemItems27suites of theHolzBarCore,SharedCodeSigningandHolzBarMacOS27Coretargets), and strict SwiftLint (0 violations).Still to check by hand on a Mac:
tccutil reset ScreenCapture com.holzcloud.holzBar): "Grant Permission", then "Reset and Grant Again", then grant in System Settings: the window comes to the front once and shows it as granted. Reset again, "Grant Permission", do not grant, click Continue: the window does not reopen by itself later.Scripts/install.sh): withlog stream --level info --predicate 'process == "MenuBarItemService" OR subsystem BEGINSWITH "com.holzcloud.holzBar"'running, launch holzBar and open Settings, Menu Bar Layout. The service logs "Listener requires the app's exact code (N code directory hashes)" with N ≥ 1, the layout shows the menu bar items instead of "Loading menu bar items…", and no "looking up source processes in the app instead" line appears.🤖 Generated with Claude Code
https://claude.ai/code/session_01BSzFQk1ZzGXFVBDZMYq8eu
Generated by Claude Code