Dated proposal · #916 · 2026-09-24 · hand-written and frozen, not a description of shipped behavior

Window Names Hint

Window titles and application names sit behind one consent (ADR-0032), off by default and off until the user turns it on. Many users never find the Settings row, so list_windows and describe_screen keep reporting anonymous rectangles, fidget://windows stays empty, and the fidget knows where the windows are but not what they are. This proposes a hint that closes that gap once, without ever reading as the product asking for more surveillance permission.

The stage

Arm the hint with one of the three triggers #916 names, then switch between the three candidate surfaces. The dismissal is real: Quit and relaunch reloads the stage from persistence, and only Reset everything brings the hint back.

Trigger
Variant
Stage
platform.rs — Fidget
#916 · omesser/fidget
zsh — 80×24
$ cargo test -p fidget-core
Chat
What am I looking at?
Three windows. I know where they are, not what they are.
Message the fidget…

Nothing has asked for a name yet. NamesHint::Quiet.

list_windows
  { "bounds": [22,52,300,168],   "owner": null, "title": null }
  { "bounds": [150,132,300,150], "owner": null, "title": null }
  { "bounds": [70,250,250,120],  "owner": null, "title": null }

The state model

One derived value, not three booleans that have to stay in step. The hint is on screen because the state says so, and the state has exactly one reason to be what it is.

StateWhat produced itWhat the user sees
QuietNames are usable, or no nameless read is outstanding.Nothing.
DueSomething asked for windows and got them nameless while the consent was off. The hint, once, in whichever variant ships.
DismissedThe user said no. Nothing, now or after a restart. The Settings row stays where it is.
/// Why the hint is or is not on screen. One value, derived — never three
/// booleans that have to stay in step.
enum NamesHint {
    /// Names are usable, or no nameless read is outstanding.
    Quiet,
    /// Something asked for windows and got them nameless while the consent was off.
    Due,
    /// The user said no. Persisted, so it survives a restart.
    Dismissed,
}

What the copy refuses to say

The hint never names macOS Screen Recording, never names ScreenCast, never says screenshot, and never reaches for a verb of sight — a fidget that sees is a fidget that captures, whatever the next clause says. It names what the fidget knows and what it does not — where the windows are, not what they are — in the words the Settings row's own disclosure already uses, and the row names the grant one line down, which is where #888 and #979 put it and where a disclosure belongs. A hint that leads with the permission is asking for the permission; a hint that leads with the capability is describing the fidget.

One consent, one thing it gates. Before ADR-0032 the copy had to explain a split — application names free, titles gated — and that split is gone, so the hint no longer says what the fidget still knows without consent, because without it the fidget knows no name at all. The same product rule holds on every OS. Only the platform label under the Settings row changes: Screen Recording on macOS, ScreenCast on Linux, no system permission on Windows.

Not in this proposal

Alternatives weighed

AlternativeThe case for itVerdict
A Settings banner alone — surface the offer inside the Privacy pane and nowhere else. Zero new surfaces, zero new copy, and it cannot interrupt anyone. Rejected. The user who needs the hint is the user who never opens Settings.
Append a nudge to the fidget://windows resource text so the agent relays it. One string, no UI at all, and it reaches every Harness for free. Rejected. The copy then goes through a language model, so "never foreshadows Capture" stops being something the repository can hold.
Repeat the hint on every nameless read. Nobody misses it, and a user who ignored it once gets another chance. Rejected as a nag. An agent can read windows many times a minute; the second showing is already an interruption.
Make names on by default. No hint needed, because there is no gap to close. Rejected. It is the issue's own out-of-scope line, and it breaks the one consent rule that holds identically on every OS.