Skip to content

fix(notifications): stop sleep-control and session-close spam (macOS 2.7.3) - #15

Merged
katipally merged 1 commit into
mainfrom
fix/notification-spam
Aug 13, 2026
Merged

fix(notifications): stop sleep-control and session-close spam (macOS 2.7.3)#15
katipally merged 1 commit into
mainfrom
fix/notification-spam

Conversation

@katipally

Copy link
Copy Markdown
Owner

macOS only. The iOS companion is unchanged and stays on its current App Store version.

Kills three notifications that fired without cause. Two for sleep control, one for closing an agent.

1 + 2. Sleep control banners

SleepStateNotifier fired on every isActive flip in Auto mode, and the hold condition is an OR of three volatile signals with no hysteresis and no debounce:

shouldHoldAuto = isSnoozed || isAgentHookFresh || isUserActivityFresh
 coding      ──► HOLD     "Keeping Mac Awake"
 walk away   ──► RELEASE  "Sleep Control Returned"
 come back   ──► HOLD     "Keeping Mac Awake"
 lunch       ──► RELEASE  "Sleep Control Returned"

Two banners per walk-away cycle. Toggling Auto off and back on re-fired the first one. App launch in Auto with the user at the keyboard fired it immediately.

They were also the only notifications in the app that bypassed the preference gate entirely:

 agent events:  hook -> normalize -> shouldNotify -> PauseFlag
                     -> RateLimiter -> NotificationDispatcher

 sleep events:  SleepStateNotifier ------------------> banner + CloudKit
                (no gate, no toggle, no pause, no rate limit)

So there was no way to turn them off. Sleep state is already live in the menu bar icon, the panel, and the iOS Dashboard via MacStatus.sleepActive, so the banners repeated what the UI already showed.

Both call sites removed. SleepStateNotifier.swift deleted, it had no other callers.

3. "Task completed" when you close an agent

A close event and a turn-end event collapse into the same phase:

Agent turn-end close both map to
Claude Code Stop SessionEnd .sessionEnd
Cursor stop sessionEnd .sessionEnd
VS Code Copilot Stop SessionEnd .sessionEnd
Copilot CLI agentStop sessionEnd .sessionEnd

So quitting an agent fired a duplicate "done" alert a second or two after the real one.

Fixed at the gate, not in the normalizer. Remapping the phase would have broken the session state machine, since .sessionEnd drives hasEnded, scheduleTerminalRevert, and insertSessionHistory. For Cursor and VS Code, closing a chat session does not kill the process, so their badge would have sat stale until the 10-minute ideIdleWindow sweep.

 SessionEnd hook
       |
       v
 phase = .sessionEnd  -------> state machine   unchanged
       |
       v
 shouldNotify()
   isSessionClose? --yes--> return housekeeping  (default OFF)  silent
              `----no---> return completed                     turn-end alert

Events are still recorded and visible in Activity. Turn-end alerts are untouched. Flip Housekeeping on in Configure > Agents to get close alerts back.

.housekeeping added to the VS Code Copilot and Copilot CLI category lists so that toggle is reachable for them. This mirrors what opencode already does with session.deleted -> .housekeeping.

Verification

  • Mac Debug build: passes
  • Mac Release build: passes (the config release.yml uses)
  • plutil -lint on project.pbxproj: OK
  • Built app stamps 2.7.3 / 2703, dc-hook present and --ping exits 0
  • Zero dangling references to the deleted file across code, docs, and pbxproj
  • SwiftLint: no new violations on touched files

Not verified locally: the iOS companion build, since iOS 26 is not installed in my Xcode. No iOS or DoomCoderCore file is touched, so build-ios is expected to be unaffected. That is the main thing this PR is here to confirm.

Unrelated pre-existing flake noticed while verifying: ringBufferCapsAt200 in SyncTelemetryTests fails about 25% of the time because sibling tests race on the SyncTelemetry.shared singleton. ci.yml runs no tests, so it does not gate anything. Left alone as out of scope.

Not in scope

Copilot CLI has a third close path, errorOccurred with a clean-exit signal (Ctrl+C, SIGTERM, exit 0/130/143), which is remapped to .sessionEnd and still notifies. Deliberately left for a separate change.

…2.7.3)

Three notifications fired without cause and had no way to switch off.

Sleep control ("Keeping Mac Awake" / "Sleep Control Returned")

Both were edge-triggered on the isActive flip in Auto mode, where the
hold condition is an OR of three volatile signals with no hysteresis and
no debounce. Every walk-away-and-come-back cycle cost two banners, and
toggling Auto off and back on re-fired the first. They were also the only
notifications in the app that bypassed AgentNotificationPrefs entirely,
so there was no toggle to silence them.

Sleep state is already live in the menu bar icon, the panel, and the iOS
Dashboard via MacStatus.sleepActive, so the banners only repeated what
the UI already showed. Both call sites are gone and SleepStateNotifier is
deleted (it had no other callers).

Session close ("Task completed" on quitting an agent)

A close event (SessionEnd / sessionEnd) normalizes to the same
.sessionEnd phase as a turn-end event (Stop / agentStop), so quitting an
agent produced a duplicate "done" alert seconds after the real one.

Split at the gate rather than in the normalizer: the phase stays
.sessionEnd, so hasEnded, scheduleTerminalRevert, and session history are
untouched, and IDE badges do not go stale waiting on the 10-minute sweep.
shouldNotify routes close events to the default-off housekeeping switch
via the new AgentNotificationCatalog.isSessionClose. Events are still
recorded and visible in Activity. Turn-end alerts are unchanged.

Housekeeping is added to the VS Code Copilot and Copilot CLI category
lists so the toggle that re-enables close alerts is reachable there.
This mirrors what opencode already does with session.deleted.

macOS only. The iOS companion is unchanged.
@github-actions github-actions Bot added documentation Improvements or additions to documentation mac Changes to the macOS app or dc-hook labels Aug 13, 2026
@katipally
katipally merged commit d450008 into main Aug 13, 2026
9 checks passed
@katipally
katipally deleted the fix/notification-spam branch August 13, 2026 03:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation mac Changes to the macOS app or dc-hook

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant