Skip to content

fix(notifications): ANCS resilience bundle — retry-spam, reconnect race, backlog, Open button#56

Merged
quad341 merged 7 commits into
mainfrom
feature/tincan-tt3o6
Jun 6, 2026
Merged

fix(notifications): ANCS resilience bundle — retry-spam, reconnect race, backlog, Open button#56
quad341 merged 7 commits into
mainfrom
feature/tincan-tt3o6

Conversation

@quad341
Copy link
Copy Markdown
Owner

@quad341 quad341 commented Jun 6, 2026

ANCS notification-resilience + UX bundle (builder-1, validator-tested) — squash-merges 6 commits as one. Fixes: tincan-tt3o6 (stop ANCS WriteValue retry-spam on LE drop), tincan-e4mbf (validator tests for WriteValue→healing paths), tincan-rvbqa (wire notification Open button), tincan-b6hya (suppress iOS backlog flood on connect), tincan-jyobl (recover from StartNotify InProgress on rapid reconnect), tincan-dzlv8 (global desktop_enabled toggle for ANCS notifications). 1160 tests pass. Squash collapses the mixed-subsystem branch to one clean commit.

Jim Wordelman and others added 7 commits June 5, 2026 16:17
When the LE link dropped mid-session, BlueZ replayed queued Notification
Source events; each fired _on_notif_source_changed → WriteValue → "Not
connected" x38 with no backoff. Two fixes:

- _on_notif_source_changed: on "Not connected"/"att" error, immediately
  clear _control_point_proxy (stops all subsequent retries via the
  existing `if self._control_point_proxy is None: return` guard) and
  call _enter_healing() to mark ANCS down + schedule rearm.
- _enter_healing: cancel any existing _heal_timer_id before creating a
  new one to prevent duplicate heal timers when entering HEALING from
  multiple sites (health check + write failure).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Three deterministic tests using the lc fixture:
- test_write_value_not_connected_clears_proxy_and_enters_healing
- test_write_value_not_connected_stops_subsequent_retries
- test_enter_healing_twice_cancels_first_timer

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…can-rvbqa)

_on_action_invoked_signal guarded with `if conv_id:` which blocked the
callback for ANCS app notifications (conv_id="" — no conversation to
select, but window should still raise).

Change guard to `nid in self._notif_to_conv`: calls on_action_invoked
whenever the notification ID is known (conv_id may be ""), ignores
spurious/unknown IDs as before.  _on_notification_clicked already
handles empty conv_id correctly (raises window, skips select_conversation).

1137 tests pass, ruff clean.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…-b6hya)

iOS replays the entire Notification Center (~50+ events) when ANCS
first connects. Add a 2-second suppression window that starts when
ACTIVE is confirmed in _check_notifying_after_subscribe: any
_on_data_source_changed dispatch is dropped until _end_backlog_suppress
fires. The window is cancelled (and the flag reset) on stop,
disconnect, or healing entry so reconnects get a fresh window.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ncan-jyobl)

On BLE link flap the prior StartNotify may still be pending in BlueZ
when the daemon reconnects. Previously this produced InProgress on both
chars -> notify_ok=0 -> "ANCS unavailable" with no recovery path.

Fix: detect InProgress, set _subscribe_pending=True, and retry
_on_device_connected after 1.5 s via GLib timer. The retry runs with
subscribe_was_pending=True so a second InProgress is logged as a
warning rather than triggering another retry loop. Clear
_subscribe_pending on stop() and disconnect() so link drops are clean.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…pp notifications (tincan-dzlv8)

`dispatch_app_notification` now checks `notifications/desktop_enabled`
in QSettings before sending — matching the guard already in `_should_notify`.
Existing `TestDispatchAppNotification` tests updated to mock `app_settings`
for proper isolation (they were implicitly environment-sensitive before).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ic index (tincan-7lvcd)

`timers[3]` was a fragile index assumption that breaks in CI. Use
`backend._backlog_timer_id` (the value stored by the production code)
to fire the backlog-suppress timer explicitly — index-agnostic and
correct per tests-tick-mocks-no-real-timing standard.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@quad341 quad341 merged commit 22d3bf2 into main Jun 6, 2026
1 check passed
@quad341 quad341 deleted the feature/tincan-tt3o6 branch June 6, 2026 00:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant