Skip to content

Name uncontrolled displays in diagnostics, keep scheduled updates out of the way, refuse a second copy, and gate the accessibility prompt on a prior grant - #92

Merged
Rydersel merged 11 commits into
mainfrom
fix/lifecycle-stabilization
Sep 11, 2026
Merged

Name uncontrolled displays in diagnostics, keep scheduled updates out of the way, refuse a second copy, and gate the accessibility prompt on a prior grant#92
Rydersel merged 11 commits into
mainfrom
fix/lifecycle-stabilization

Conversation

@Rydersel

@Rydersel Rydersel commented Sep 10, 2026

Copy link
Copy Markdown
Owner

Closes #57.

What changed

The diagnostics report names what Candela is not controlling. Display discovery now surveys every online display and classifies each one it leaves out of the DDC pool with a reason: built in, no DDC service, or beyond the slot cap. The report carries an online count beside the controlled count and a section listing the uncontrolled displays by vendor and model with the reason word, never a serial number. The same report states whether the media-key tap is running and whether Screen Recording is granted, so the two most common "it stopped working" reports can be read off one paste. The format number moves to 3 because a section heading changed.

Scheduled updates never take focus. A scheduled check that finds an update no longer raises the dialog over whatever the person is doing. The updater records that an update is waiting, the menu-bar panel shows a reminder row, and choosing the row brings the update dialog forward. The row's state is frozen when the menu is about to open, before it lays out, so the panel's height is settled before it is shown. The marker clears when the update session ends, installed or declined. A manual Check for Updates is unchanged and still comes to the front at once.

A second copy quits itself. Two Candela processes on one machine are two DDC writers on one bus. A launching copy that finds another running instance now alerts, naming both paths, and quits, before any display work starts. Instances already terminating are ignored so the updater's own relaunch is not mistaken for a duplicate. The virtual display helper is not an instance and is unaffected.

No bare accessibility dialog after a reset. The launch-time prompt for the media-key grant now runs only when a machine-scoped marker records that the grant was observed on this machine before. A fresh install, or a preferences domain whose grant was reset out from under it, gets the panel banner and the Keyboard pane instead of a system dialog it did not ask for; changing the key mode in the Keyboard pane still prompts. The marker is a file under Application Support bound to this Mac’s machine identity. A copied marker, legacy timestamp, corrupt file, or unavailable identity cannot authorize the launch prompt. Observing a real grant upgrades the record. The identifier stays local and is never logged or exported.

Hardware verification

Verified locally with the Dell U2725QE and MSI MAG 341C OLED on a signed Release build combining the five pending fixes.

  • The stable virtual-display helper created and removed a display without triggering the second-instance guard. With it attached, diagnostics reported four online and three controlled displays, with the virtual display's exclusion reason and no serial number on that line.
  • A Debug copy launched beside the installed Release copy showed the already-running dialog and closed on Quit. The original process remained running.
  • A scheduled update from a loopback-only feed set the reminder while the foreground app retained focus and no Candela window appeared. On the first menu opening, the reminder, all three display controls, Settings, and Quit were present in the accessibility tree; the measured menu window fit the Dell. A first-open pixel capture remains unverified.
  • Choosing the reminder brought the updater forward. Remind Me Later closed it and cleared the marker. A manual Check for Updates opened the dialog immediately.
  • A second scheduled session downloaded and installed a locally signed higher-version build through Install and Relaunch. The installed version and binary hash matched the local archive. The normal version was restored afterward, as were the original updater preferences; the loopback server is stopped. No release or public appcast was published.
  • The installed machine-bound marker upgraded after observing the existing Accessibility grant. Tests cover matching, migrated, legacy, unavailable, and corrupt identity records.
  • Screen Recording revoke/regrant passed. Saved reports changed from “not granted” to “granted” after restoring the original permission and relaunching.
  • The final manual migration-permission check passed, covering the launch behaviour, menu-bar permission banner, and explicit prompt control. The earlier local reset also exercised the Keyboard explanation and restored the grant.
  • Live VoiceOver testing was deliberately skipped; accessibility markers and labels remain in place.

The branch includes the shared-identity recovery fix from #90 and must merge after it. The final combined build passed 2,632 engine tests and 762 app tests, plus the Release marker gate. Required CI checks passed on this branch, and independent review found no actionable issues.

…the Protection pane

A crash or force-quit while a lock dim was engaged left a display's DDC
register dimmed with the slider showing full brightness, and on a write-only
display nothing could tell. A per-display marker is now written before the
dim's first submit and cleared after the restoring one; at the next launch a
surviving marker runs the restore pass's own memo reset and reassert, gated
on Safe Mode directly. The launch readback holds off while a marker stands,
because on a display that answers reads it would otherwise import the dim as
the user's value. The startup caption names the one exception it makes.

Idle dim and blackout entries now fade in over about 400 milliseconds on the
overlay's content view; every decrease and every removal still lands in one
write, so restore timing is unchanged. A nudge from the care loop's verify
pass declines while a fade toward the same state is in flight, bounded in
time so a completion that never arrives cannot park the loop, and the
declined nudge counts as settling rather than a mismatch.

The Protection pane opens with a Dimming row that reveals OLED Care and says
how many displays are enrolled, or Paused while Safe Mode holds the care
loop, with one sentence on what the other half of the pillar does.
… of the way, refuse a second copy, and gate the accessibility prompt on a prior grant

Display discovery now surveys every online display and the diagnostics
report lists the ones Candela is not controlling, by vendor and model with
a reason word and no serial number, beside an online count and lines for
the media-key tap and Screen Recording. A scheduled update no longer raises
its dialog over the person's work: the menu-bar panel shows a reminder row,
frozen for each open, and choosing it brings the dialog forward. A second
Candela process alerts, names both paths and quits before any display work
starts. The launch-time accessibility prompt runs only when a machine-scoped
marker records a grant observed before, so a reset or a fresh install gets
the panel banner and the Keyboard pane instead of a bare system dialog.
@Rydersel Rydersel added the needs-hardware Verification needs hardware this setup cannot provide label Sep 10, 2026
@Rydersel
Rydersel merged commit e536ee5 into main Sep 11, 2026
8 checks passed
@Rydersel
Rydersel deleted the fix/lifecycle-stabilization branch September 11, 2026 21:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-hardware Verification needs hardware this setup cannot provide

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Lifecycle: the diagnostics report cannot describe the most common bug report, and two Candela builds can drive DDC at once

1 participant