# Plugin Behavior Exploration - Initial Findings

**Author**: Matthew Raymer  
**Date**: November 2025  
**Status**: Initial Code Review - In Progress

## Purpose

This document contains initial findings from code-level inspection of the plugin. These findings should be verified through actual testing using [Plugin Behavior Exploration Template](./plugin-behavior-exploration-template.md).

---

## 0. Behavior Definitions & Investigation Scope

Before examining the code, we need to clearly define what behaviors we're investigating and what each scenario means.

### 0.1 App State Scenarios

#### Swipe from Recents (Recent Apps List)

**What it is**: User swipes the app away from the Android recent apps list (app switcher) or iOS app switcher.

**What happens**:
- **Android**: The app's UI is removed from the recent apps list, but:
  - The app process may still be running in the background
  - The app may be killed by the OS later due to memory pressure
  - **AlarmManager alarms remain scheduled** and will fire even if the process is killed
  - The OS will recreate the app process when the alarm fires
- **iOS**: The app is terminated, but:
  - **UNUserNotificationCenter notifications remain scheduled** and will fire
  - **Calendar/time-based triggers persist across reboot**
  - **TimeInterval triggers also persist across reboot** (UNLESS they were scheduled with `repeats = false` AND the reboot occurs before the elapsed interval)
  - The app does not run in the background (unless it has active background tasks)
  - Notifications fire even though the app is not running
  - **No plugin code runs when notification fires** unless the user interacts with the notification

**Key Point**: Swiping from recents does **not** cancel scheduled alarms/notifications. The OS maintains them separately from the app process.

**Android Nuance - Swipe vs Kill**:
- **"Swipe away" DOES NOT kill your process**; the OS may kill it later due to memory pressure
- **AlarmManager remains unaffected** by swipe - alarms stay scheduled
- **WorkManager tasks remain scheduled** regardless of swipe
- The app process may continue running in the background after swipe
- Only Force Stop actually cancels alarms and prevents execution

**Investigation Goal**: Verify that alarms/notifications still fire after the app is swiped away.

---

#### Force Stop (Android Only)

**What it is**: User goes to Settings → Apps → [Your App] → Force Stop. This is a **hard kill** that is different from swiping from recents.

**What happens**:
- **All alarms are immediately cancelled** by the OS
- **All WorkManager tasks are cancelled**
- **All broadcast receivers are blocked** (including BOOT_COMPLETED)
- **All JobScheduler jobs are cancelled**
- **The app cannot run** until the user manually opens it again
- **No background execution** is possible

**Key Point**: Force Stop is a **hard boundary** that cannot be bypassed. It's more severe than swiping from recents.

**Investigation Goal**: Verify that alarms do NOT fire after force stop, and that the plugin can detect and recover when the app is opened again.

**Difference from Swipe**:
- **Swipe**: Alarms remain scheduled, app may still run in background
- **Force Stop**: Alarms are cancelled, app cannot run until manually opened

---

#### App Still Functioning When Not Visible

**Android**:
- When an app is swiped from recents but not force-stopped:
  - The app process may continue running in the background
  - Background services can continue
  - WorkManager tasks continue
  - AlarmManager alarms remain scheduled
  - The app is just not visible in the recent apps list
- The OS may kill the process later due to memory pressure, but alarms remain scheduled

**iOS**:
- When an app is swiped from the app switcher:
  - The app process is terminated
  - Background tasks (BGTaskScheduler) may still execute (system-controlled, but **opportunistic, not exact**)
  - UNUserNotificationCenter notifications remain scheduled
  - The app does not run in the foreground or background (unless it has active background tasks)
  - **No persistent background execution** after user swipe
  - **No alarm-like wake** for plugins (unlike Android AlarmManager)
  - **No background execution at notification time** unless user interacts

**iOS Limitations**:
- No background execution at notification fire time unless user interacts
- No alarm-style wakeups exist on iOS
- Background execution (BGTaskScheduler) cannot be used for precise timing
- Notifications survive reboot but plugin code does not run automatically

**Investigation Goal**: Understand that "not visible" does not mean "not functioning" for alarms/notifications, but also understand iOS limitations on background execution.

---

### 0.2 App Launch Recovery - How It Should Work

App Launch Recovery is the mechanism by which the plugin detects and handles missed alarms/notifications when the app starts.

#### Recovery Scenarios

##### Cold Start

**What it is**: App is launched from a completely terminated state (process was killed or never started).

**Recovery Process**:
1. Plugin's `load()` method is called
2. Plugin initializes database/storage
3. Plugin queries for missed alarms/notifications:
   - Find alarms with `scheduled_time < now` and `delivery_status != 'delivered'`
   - Find notifications that should have fired but didn't
4. For each missed alarm/notification:
   - Generate a "missed alarm" event or notification
   - If repeating, reschedule the next occurrence
   - Update delivery status to "missed" or "delivered"
5. Reschedule future alarms/notifications that are still valid
6. Verify active alarms match stored alarms

**Investigation Goal**: Verify that the plugin detects missed alarms on cold start and handles them appropriately.

**Android Force Stop Detection**:
- On cold start, query AlarmManager for active alarms
- Query plugin DB schedules
- If `(DB.count > 0 && AlarmManager.count == 0)`: **Force Stop detected**
- Recovery: Mark all past schedules as missed, reschedule all future schedules, emit missed notifications

---

##### Warm Start

**What it is**: App is returning from background (app was paused but process still running).

**Recovery Process**:
1. Plugin's `load()` method may be called (or app resumes)
2. Plugin checks for missed alarms/notifications (same as cold start)
3. Plugin verifies that active alarms are still scheduled correctly
4. Plugin reschedules if any alarms were cancelled (shouldn't happen, but verify)

**Investigation Goal**: Verify that the plugin checks for missed alarms on warm start and verifies active alarms.

---

##### Force Stop Recovery (Android)

**What it is**: App was force-stopped and user manually opens it again.

**Recovery Process**:
1. App launches (this is the only way to recover from force stop)
2. Plugin's `load()` method is called
3. Plugin detects that alarms were cancelled (all alarms have `scheduled_time < now` or are missing from AlarmManager)
4. Plugin queries database for all enabled alarms
5. For each alarm:
   - If `scheduled_time < now`: Mark as missed, generate missed alarm event, reschedule if repeating
   - If `scheduled_time >= now`: Reschedule the alarm
6. Plugin reschedules all future alarms

**Investigation Goal**: Verify that the plugin can detect force stop scenario and fully recover all alarms.

**Key Difference**: Force stop recovery is more comprehensive than normal app launch recovery because ALL alarms were cancelled, not just missed ones.

---

### 0.3 What We're Investigating

For each scenario, we want to know:

1. **Does the alarm/notification fire?** (OS behavior)
2. **Does the plugin detect missed alarms?** (Plugin behavior)
3. **Does the plugin recover/reschedule?** (Plugin behavior)
4. **What happens on next app launch?** (Recovery behavior)

**Expected Behaviors**:

| Scenario | Alarm Fires? | Plugin Detects Missed? | Plugin Recovers? |
| -------- | ------------ | ---------------------- | ---------------- |
| Swipe from recents | ✅ Yes (OS) | N/A (fired) | N/A |
| Force stop | ❌ No (OS cancels) | ✅ Should detect | ✅ Should recover |
| Device reboot (Android) | ❌ No (OS cancels) | ✅ Should detect | ✅ Should recover |
| Device reboot (iOS) | ✅ Yes (OS persists) | ⚠️ May detect | ⚠️ May recover |
| Cold start | N/A | ✅ Should detect | ✅ Should recover |
| Warm start | N/A | ✅ Should detect | ✅ Should verify |

---

## 1. Android Findings

### 1.1 Boot Receiver Implementation

**Status**: ✅ **IMPLEMENTED**

**Location**: `android/src/main/java/com/timesafari/dailynotification/BootReceiver.kt`

**Findings**:
- Boot receiver exists and handles `ACTION_BOOT_COMPLETED` (line 24)
- Reschedules alarms from database (line 38+)
- Loads enabled schedules from Room database (line 40)
- Reschedules both "fetch" and "notify" schedules (lines 46-81)

**Gap Identified**:
- **Missed Alarm Handling**: Boot receiver only reschedules FUTURE alarms
  - Line 64: `if (nextRunTime > System.currentTimeMillis())`
  - This means if an alarm was scheduled for before the reboot time, it won't be rescheduled
  - **No missed alarm detection or notification**

**Recommendation**: Add missed alarm detection in `rescheduleNotifications()` method

---

### 1.2 Missed Alarm Detection

**Status**: ⚠️ **PARTIAL**

**Location**: `android/src/main/java/com/timesafari/dailynotification/dao/NotificationContentDao.java`

**Findings**:
- DAO has query for missed alarms: `getNotificationsReadyForDelivery()` (line 98)
  - Query: `SELECT * FROM notification_content WHERE scheduled_time <= :currentTime AND delivery_status != 'delivered'`
  - This can identify notifications that should have fired but haven't

**Gap Identified**:
- **Not called on app launch**: The `DailyNotificationPlugin.load()` method (line 91) only initializes the database
- No recovery logic in `load()` method
- Query exists but may not be used for missed alarm detection

**Recommendation**: Add missed alarm detection in `load()` method or create separate recovery method

---

### 1.3 App Launch Recovery

**Status**: ❌ **NOT IMPLEMENTED**

**Location**: `android/src/main/java/com/timesafari/dailynotification/DailyNotificationPlugin.kt`

**Expected Behavior** (as defined in Section 0.2):

**Cold Start Recovery**:
1. Plugin `load()` method called
2. Query database for missed alarms: `scheduled_time < now AND delivery_status != 'delivered'`
3. For each missed alarm:
   - Generate missed alarm event/notification
   - Reschedule if repeating
   - Update delivery status
4. Reschedule all future alarms from database
5. Verify active alarms match stored alarms

**Warm Start Recovery**:
1. Plugin checks for missed alarms (same as cold start)
2. Verify active alarms are still scheduled
3. Reschedule if any were cancelled

**Force Stop Recovery**:
1. Detect that all alarms were cancelled (force stop scenario)
2. Query database for ALL enabled alarms
3. For each alarm:
   - If `scheduled_time < now`: Mark as missed, generate event, reschedule if repeating
   - If `scheduled_time >= now`: Reschedule immediately
4. Fully restore alarm state

**Current Implementation**:
- `load()` method (line 91) only initializes database
- No recovery logic on app launch
- No check for missed alarms
- No rescheduling of future alarms
- No distinction between cold/warm/force-stop scenarios

**Gap Identified**:
- Plugin does not recover on app cold/warm start
- Plugin does not recover from force stop
- Only boot receiver handles recovery (and only for future alarms)
- No missed alarm detection on app launch

**Recommendation**: 
1. Add recovery logic to `load()` method or create `ReactivationManager`
2. Implement missed alarm detection using `getNotificationsReadyForDelivery()` query
3. Implement force stop detection (all alarms cancelled)
4. Implement rescheduling of future alarms from database

---

### 1.4 Persistence Completeness

**Status**: ✅ **IMPLEMENTED**

**Findings**:
- Room database used for persistence
- `Schedule` entity stores: id, kind, cron, clockTime, enabled, nextRunAt
- `NotificationContentEntity` stores: id, title, body, scheduledTime, priority, etc.
- `ContentCache` stores: fetched content with TTL

**All Required Fields Present**:
- ✅ alarm_id (Schedule.id, NotificationContentEntity.id)
- ✅ trigger_time (Schedule.nextRunAt, NotificationContentEntity.scheduledTime)
- ✅ repeat_rule (Schedule.cron, Schedule.clockTime)
- ✅ channel_id (NotificationContentEntity - implicit)
- ✅ priority (NotificationContentEntity.priority)
- ✅ title, body (NotificationContentEntity)
- ✅ sound_enabled, vibration_enabled (NotificationContentEntity)
- ✅ created_at, updated_at (NotificationContentEntity)
- ✅ enabled (Schedule.enabled)

---

### 1.5 Force Stop Recovery

**Status**: ❌ **NOT IMPLEMENTED**

**Expected Behavior** (as defined in Section 0.1 and 0.2):

**Force Stop Scenario**:
- User goes to Settings → Apps → [App] → Force Stop
- All alarms are immediately cancelled by the OS
- App cannot run until user manually opens it
- When app is opened, it's a cold start scenario

**Force Stop Recovery**:
1. Detect that alarms were cancelled (check AlarmManager for scheduled alarms)
2. Compare with database: if database has alarms but AlarmManager has none → force stop occurred
3. Query database for ALL enabled alarms
4. For each alarm:
   - If `scheduled_time < now`: This alarm was missed during force stop
     - Generate missed alarm event/notification
     - Reschedule next occurrence if repeating
     - Update delivery status
   - If `scheduled_time >= now`: This alarm is still in the future
     - Reschedule immediately
5. Fully restore alarm state

**Current Implementation**:
- No specific force stop detection
- No recovery logic for force stop scenario
- App launch recovery (if implemented) would handle this, but app launch recovery is not implemented
- Cannot distinguish between normal app launch and force stop recovery

**Gap Identified**:
- Plugin cannot detect force stop scenario
- Plugin cannot distinguish between normal app launch and force stop recovery
- No special handling for force stop scenario
- All alarms remain cancelled until user opens app, then plugin should recover them

**Recommendation**: 
1. Implement app launch recovery (which will handle force stop as a special case)
2. Add force stop detection: compare AlarmManager scheduled alarms with database
3. If force stop detected, recover ALL alarms (not just missed ones)

---

## 2. iOS Findings

### 2.1 Notification Persistence

**Status**: ✅ **IMPLEMENTED**

**Location**: `ios/Plugin/DailyNotificationStorage.swift`

**Findings**:
- Plugin uses `DailyNotificationStorage` for separate persistence
- Uses UserDefaults for quick access (line 40)
- Uses CoreData for structured data (line 41)
- Stores notifications separately from UNUserNotificationCenter

**Storage Components**:
- UserDefaults: Settings, last fetch, BGTask tracking
- CoreData: NotificationContent, Schedule entities
- UNUserNotificationCenter: OS-managed notification scheduling

---

### 2.2 Missed Notification Detection

**Status**: ⚠️ **PARTIAL**

**Location**: `ios/Plugin/DailyNotificationPlugin.swift`

**Findings**:
- `checkForMissedBGTask()` method exists (line 421)
- Checks for missed background tasks (BGTaskScheduler)
- Reschedules missed BGTask if needed

**Gap Identified**:
- Only checks for missed BGTask, not missed notifications
- UNUserNotificationCenter handles notification persistence, but plugin doesn't check for missed notifications
- No comparison between plugin storage and UNUserNotificationCenter pending notifications

**Recommendation**: Add missed notification detection by comparing plugin storage with UNUserNotificationCenter pending requests

---

### 2.3 App Launch Recovery

**Status**: ⚠️ **PARTIAL**

**Location**: `ios/Plugin/DailyNotificationPlugin.swift`

**Expected Behavior** (iOS Missed Notification Recovery Architecture):

**Required Steps for Missed Notification Detection**:
1. Query plugin storage (CoreData) for all scheduled notifications
2. Query `UNUserNotificationCenter.pendingNotificationRequests()` for future notifications
3. Query `UNUserNotificationCenter.getDeliveredNotifications()` for already-fired notifications
4. Find CoreData entries where:
   - `scheduled_time < now` (should have fired)
   - NOT in `deliveredNotifications` list (didn't fire)
   - NOT in `pendingNotificationRequests` list (not scheduled for future)
5. Generate "missed notification" events for each detected miss
6. Reschedule repeating notifications
7. Verify that scheduled notifications in UNUserNotificationCenter align with CoreData schedules

**This must be placed in `load()` during cold start.**

**Current Implementation**:
- `load()` method exists (line 42)
- `setupBackgroundTasks()` called (line 318)
- `checkForMissedBGTask()` called on setup (line 330)
- Only checks for missed BGTask, not missed notifications
- No recovery of notification state
- No rescheduling of notifications from plugin storage
- No comparison between UNUserNotificationCenter and CoreData

**Gap Identified**:
- Only checks for missed BGTask, not missed notifications
- No recovery of notification state
- No rescheduling of notifications from plugin storage
- No cross-checking between UNUserNotificationCenter and CoreData
- **iOS cannot detect missed notifications** unless plugin compares storage vs `UNUserNotificationCenter.getDeliveredNotifications()` or infers from plugin timestamps

**Recommendation**: 
1. Add notification recovery logic in `load()` or `setupBackgroundTasks()`
2. Implement three-way comparison: CoreData vs pending vs delivered notifications
3. Add missed notification detection using the architecture above
4. Note: iOS does NOT allow arbitrary code execution at notification fire time unless user interacts or Notification Service Extensions are used (not currently used)

---

### 2.4 Background Execution Limits

**Status**: ✅ **DOCUMENTED IN CODE**

**Findings**:
- BGTaskScheduler used for background fetch
- Time budget limitations understood (30 seconds typical)
- System-controlled execution acknowledged
- Rescheduling logic handles missed tasks

**Code Evidence**:
- `checkForMissedBGTask()` handles missed BGTask (line 421)
- 15-minute miss window used (line 448)
- Reschedules if missed (line 462)

---

## 3. Cross-Platform Gaps Summary

| Gap | Android | iOS | Severity | Recommendation |
| --- | ------- | --- | -------- | -------------- |
| Missed alarm/notification detection | ⚠️ Partial | ⚠️ Partial | **High** | Implement on app launch |
| App launch recovery | ❌ Missing | ⚠️ Partial | **High** | **MUST implement for both platforms** |
| Force stop recovery | ❌ Missing | N/A | **Medium** | Android: Implement app launch recovery with force stop detection |
| Boot recovery missed alarms | ⚠️ Only future | N/A | **Medium** | Android: Add missed alarm handling in boot receiver |
| Cross-check mechanism (DB vs OS) | ❌ Missing | ⚠️ Partial | **High** | Android: AlarmManager vs DB; iOS: UNUserNotificationCenter vs CoreData |

**Critical Requirement**: App Launch Recovery **must be implemented on BOTH platforms**:
- Plugin must execute recovery logic during `load()` OR equivalent
- Distinguish cold vs warm start
- Use timestamps in storage to verify last known state
- Reconcile DB entries with OS scheduling APIs
- Android: Cross-check AlarmManager scheduled alarms with DB
- iOS: Cross-check UNUserNotificationCenter with CoreData schedules

---

## 4. Test Validation Outputs

For each scenario, the exploration should produce explicit outputs:

| Scenario | OS Expected | Plugin Expected | Observed Result | Pass/Fail | Notes |
| -------- | ----------- | --------------- | --------------- | --------- | ----- |
| Swipe from recents | Alarm fires | Alarm fires | ☐ | ☐ | |
| Force stop | Alarm does NOT fire | Plugin detects on launch | ☐ | ☐ | |
| Device reboot (Android) | Alarm does NOT fire | Plugin reschedules on boot | ☐ | ☐ | |
| Device reboot (iOS) | Notification fires | Notification fires | ☐ | ☐ | |
| Cold start | N/A | Missed alarms detected | ☐ | ☐ | |
| Warm start | N/A | Missed alarms detected | ☐ | ☐ | |
| Force stop recovery | N/A | All alarms recovered | ☐ | ☐ | |

**This creates alignment with the [Exploration Template](./plugin-behavior-exploration-template.md).**

---

## 5. Next Steps

1. **Verify findings through testing** using [Plugin Behavior Exploration Template](./plugin-behavior-exploration-template.md)
2. **Test boot receiver** on actual device reboot
3. **Test app launch recovery** on cold/warm start
4. **Test force stop recovery** on Android (with cross-check mechanism)
5. **Test missed notification detection** on iOS (with three-way comparison)
6. **Inspect `UNUserNotificationCenter.getPendingNotificationRequests()` vs CoreData** to detect "lost" iOS notifications
7. **Update Plugin Requirements** document with verified gaps
8. **Generate Test Validation Outputs** table with actual test results

---

## 6. Code References for Implementation

### Android - Add Missed Alarm Detection with Force Stop Detection

**Location**: `DailyNotificationPlugin.kt` - `load()` method (line 91)

**Suggested Implementation** (with Force Stop Detection):
```kotlin
override fun load() {
    super.load()
    // ... existing initialization ...
    
    // Check for missed alarms on app launch
    CoroutineScope(Dispatchers.IO).launch {
        detectAndHandleMissedAlarms()
    }
}

private suspend fun detectAndHandleMissedAlarms() {
    val db = DailyNotificationDatabase.getDatabase(context)
    val currentTime = System.currentTimeMillis()
    
    // Cross-check: Query AlarmManager for active alarms
    val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
    val activeAlarmCount = getActiveAlarmCount(alarmManager) // Helper method needed
    
    // Query database for all enabled schedules
    val dbSchedules = db.scheduleDao().getEnabled()
    
    // Force Stop Detection: If DB has schedules but AlarmManager has zero
    val forceStopDetected = dbSchedules.isNotEmpty() && activeAlarmCount == 0
    
    if (forceStopDetected) {
        Log.i(TAG, "Force stop detected - all alarms were cancelled")
        // Recover ALL alarms (not just missed ones)
        recoverAllAlarmsAfterForceStop(db, dbSchedules, currentTime)
    } else {
        // Normal recovery: only check for missed alarms
        val missedNotifications = db.notificationContentDao()
            .getNotificationsReadyForDelivery(currentTime)
        
        missedNotifications.forEach { notification ->
            // Generate missed alarm event/notification
            // Reschedule if repeating
            // Update delivery status
        }
        
        // Reschedule future alarms from database
        rescheduleFutureAlarms(db, dbSchedules, currentTime)
    }
}

private suspend fun recoverAllAlarmsAfterForceStop(
    db: DailyNotificationDatabase,
    schedules: List<Schedule>,
    currentTime: Long
) {
    schedules.forEach { schedule ->
        val nextRunTime = calculateNextRunTime(schedule)
        if (nextRunTime < currentTime) {
            // Past alarm - mark as missed
            // Generate missed alarm notification
            // Reschedule if repeating
        } else {
            // Future alarm - reschedule immediately
            rescheduleAlarm(schedule, nextRunTime)
        }
    }
}
```

### Android - Add Missed Alarm Handling in Boot Receiver

**Location**: `BootReceiver.kt` - `rescheduleNotifications()` method (line 38)

**Suggested Implementation**:
```kotlin
// After rescheduling future alarms, check for missed ones
val missedNotifications = db.notificationContentDao()
    .getNotificationsReadyForDelivery(System.currentTimeMillis())

missedNotifications.forEach { notification ->
    // Generate missed alarm notification
    // Reschedule if repeating
}
```

### iOS - Add Missed Notification Detection

**Location**: `DailyNotificationPlugin.swift` - `setupBackgroundTasks()` or `load()` method

**Suggested Implementation** (Three-Way Comparison):
```swift
private func checkForMissedNotifications() async {
    // Step 1: Get pending notifications (future) from UNUserNotificationCenter
    let pendingRequests = await notificationCenter.pendingNotificationRequests()
    let pendingIds = Set(pendingRequests.map { $0.identifier })
    
    // Step 2: Get delivered notifications (already fired) from UNUserNotificationCenter
    let deliveredNotifications = await notificationCenter.getDeliveredNotifications()
    let deliveredIds = Set(deliveredNotifications.map { $0.request.identifier })
    
    // Step 3: Get notifications from plugin storage (CoreData)
    let storedNotifications = storage?.getAllNotifications() ?? []
    let currentTime = Date().timeIntervalSince1970
    
    // Step 4: Find missed notifications
    // Missed = scheduled_time < now AND not in delivered AND not in pending
    for notification in storedNotifications {
        let scheduledTime = notification.scheduledTime
        let notificationId = notification.id
        
        if scheduledTime < currentTime {
            // Should have fired by now
            if !deliveredIds.contains(notificationId) && !pendingIds.contains(notificationId) {
                // This notification was missed
                // Generate missed notification event
                // Reschedule if repeating
                // Update delivery status
            }
        }
    }
    
    // Step 5: Verify alignment - check if CoreData schedules match UNUserNotificationCenter
    // Reschedule any missing notifications from CoreData
}
```

---

## Related Documentation

- [Plugin Behavior Exploration Template](./plugin-behavior-exploration-template.md) - Use this for testing
- [Plugin Requirements & Implementation](./plugin-requirements-implementation.md) - Requirements based on findings
- [Platform Capability Reference](../alarms/01-platform-capability-reference.md) - OS-level facts

---

## 7. Document Separation Directive

After improvements are complete, separate documents by purpose:

- **This file** → Exploration Findings (final) - Code inspection and test results
- **Android Behavior** → Platform Reference (see [Platform Capability Reference](../alarms/01-platform-capability-reference.md))
- **iOS Behavior** → Platform Reference (see [Platform Capability Reference](../alarms/01-platform-capability-reference.md))
- **Plugin Requirements** → Independent document (see [Plugin Requirements & Implementation](./plugin-requirements-implementation.md))
- **Future Implementation Directive** → Separate document (to be created)

This avoids future redundancy and maintains clear separation of concerns.

---

## Notes

- These findings are from **code inspection only**
- **Actual testing required** to verify behavior
- Findings should be updated after testing with [Plugin Behavior Exploration Template](./plugin-behavior-exploration-template.md)
- iOS missed notification detection requires three-way comparison: CoreData vs pending vs delivered
- Android force stop detection requires cross-check: AlarmManager vs database

