src/macos/PermissionCalloutResolverSpecs.md
Line coverage: pending — run
/coverage-exploreto populate
The menubar shows a callout — "AltTab is running without Screen Recording permissions. X won't show." — with a "Grant permission" button. It used to appear for every user missing the permission, including users who deliberately skipped it and don't use any feature that needs it (reported in #5623), and it always blamed "Thumbnails" even when the user relied on window previews instead.
Screen Recording is consumed by exactly two features: the Thumbnails appearance style (window
screenshots) and the preview selected window overlay. PermissionCalloutResolver is the pure
kernel deciding when the callout is worth showing and which feature(s) it names, split in two:
dependentFeatures(usesThumbnails:usesPreviews:) — classify the two independent "used by any
shortcut" flags into the affected feature set: .none, .thumbnails, .previews, or .both.shouldShowCallout(screenRecordingGranted:dependentFeatures:) — given the permission state and the
affected features, show the callout? (missing permission AND not .none.)dependentFeatures is .none, all hide it.screenRecordingGranted collapses three permission states into two. Production passes
ScreenRecordingPermission.status == .granted, so both .skipped (user opted out) and
.notGranted (never granted) map to false → "permission missing". They behave identically.dependentFeatures is an OR of each flag across all shortcut slots. Production computes it as
Preferences.screenRecordingDependentFeatures, which OR-s the Thumbnails flag and the Preview flag
independently over every shortcut (0...maxShortcutCount) using the effective per-shortcut
appearance style and preview flag. So a per-shortcut override that turns on Thumbnails/Preview on a
single slot is enough to surface the callout, even when the global appearance is Titles/App Icons.usesThumbnails: false. Only the
.thumbnails style passes true.%@; the
Thumbnails subject reuses the existing "Thumbnails" appearance-style translation.Mirrors PermissionCalloutResolverTests.swift 1:1.
.thumbnails..previews..both..none (the #5623 case)..both → hide (it works, no need to nag)..none → hide..thumbnails → show (names Thumbnails)..previews → show (names Window previews)..both → show (names both)..none → hide (the #5623 fix).