# Plugin Requirements & Implementation Rules **Author**: Matthew Raymer **Date**: November 2025 **Status**: Active Requirements - Implementation Guide **Version**: 1.1.0 **Last Synced With Plugin Version**: v1.1.0 ## Purpose This document defines the **rules the plugin MUST follow** to behave predictably across Android and iOS platforms. It specifies: **Language Standards**: This document uses RFC-2119 keywords: - **MUST** — Required for correctness. Violation breaks plugin guarantees. - **SHOULD** — Strong recommendation. Exceptions MUST be documented. - **MUST NOT** — Prohibited behavior. Plugin must not attempt this. - **MAY** — Optional. Plugin may or may not implement. **Cross-Reference Format**: All references use format `[Doc X §Y.Z]` where: - `Doc A` = [Platform Capability Reference](./01-platform-capability-reference.md) - `Doc B` = [Plugin Behavior Exploration](./02-plugin-behavior-exploration.md) - `Phase N` = Implementation phase directive * Plugin behavior guarantees and limitations * Persistence requirements * Recovery strategies * Missed alarm handling contract * JS/TS API contract and caveats * Platform-specific requirements * Testing requirements **Reference**: See [Platform Capability Reference](./01-platform-capability-reference.md) for OS-level facts. --- ## 1. Plugin Behavior Guarantees & Limitations ### 1.1 Cross-Platform Guarantees Matrix | Behavior | Android | iOS | Guarantee Level | Platform Source | | -------- | ------- | --- | --------------- | --------------- | | **Survives swipe-kill** | ✅ Yes | ✅ Yes | **MUST** (OS-guaranteed) | [Doc A §2.1.1](./01-platform-capability-reference.md#211-alarms-survive-ui-kills-swipe-from-recents), [Doc A §3.1.1](./01-platform-capability-reference.md#311-notifications-survive-app-termination) | | **Survives reboot** | ⚠️ Only if rescheduled | ✅ Yes | Android: **SHOULD** (best-effort), iOS: **MUST** (OS-guaranteed) | [Doc A §2.1.2](./01-platform-capability-reference.md#212-alarms-can-be-preserved-across-device-reboot), [Doc A §3.1.2](./01-platform-capability-reference.md#312-notifications-persist-across-device-reboot) | | **Runs code on trigger** | ✅ Yes (PendingIntent) | ❌ No (never) | Android: **MUST**, iOS: **MUST NOT** | [Doc A §2.1.1](./01-platform-capability-reference.md#211-alarms-survive-ui-kills-swipe-from-recents), [Doc A §3.2.1](./01-platform-capability-reference.md#321-app-code-does-not-run-when-notification-fires) | | **Missed alarm detection** | ✅ Required | ✅ Required | **MUST** (Plugin-required) | [Doc A §2.1.4](./01-platform-capability-reference.md#214-alarms-can-be-restored-after-app-restart) | | **Force stop recovery** | ✅ Required | N/A | **MUST** (Plugin-required) | [Doc A §2.2.1](./01-platform-capability-reference.md#221-you-cannot-survive-force-stop) | | **Exact timing** | ⚠️ With permission | ⚠️ ±180s tolerance | Android: **SHOULD** (with permission), iOS: **SHOULD** (OS limitation) | [Doc A §5.2](./01-platform-capability-reference.md#52-android-s-exact-alarm-permission-decision-tree), [Doc A §6.1](./01-platform-capability-reference.md#61-notification-timing-accuracy) | | **Background execution** | ⚠️ WorkManager | ⚠️ BGTaskScheduler | **SHOULD** (best-effort, system-controlled) | [Doc A §3.1.3](./01-platform-capability-reference.md#313-background-tasks-for-prefetching) | **Guarantee Level Definitions**: - **MUST** = Plugin guarantees this behavior - **SHOULD** = Plugin attempts this but cannot guarantee (best-effort) - **MUST NOT** = Plugin cannot and will not attempt this ### 1.2 Limitations by Platform **See [§8 - Explicit Unsupported Behaviors](./03-plugin-requirements.md#8-explicit-unsupported-behaviors) for complete list.** **Summary**: * **Android Force Stop**: Cannot auto-recover - See [Doc A §2.2.1](./01-platform-capability-reference.md#221-you-cannot-survive-force-stop) * **iOS Code Execution**: Cannot run code on notification fire - See [Doc A §3.2.1](./01-platform-capability-reference.md#321-app-code-does-not-run-when-notification-fires) * **iOS Timing**: ±180s tolerance - See [Doc A §6.1](./01-platform-capability-reference.md#61-notification-timing-accuracy) * **Background Execution**: System-controlled, not guaranteed - See [Doc A §3.1.3](./01-platform-capability-reference.md#313-background-tasks-for-prefetching) --- ## 2. Persistence Requirements ### 2.1 Required Persistence Items The plugin **MUST** persist the following for each scheduled alarm/notification: | Field | Type | Required | Purpose | Storage Location | | ----- | ---- | -------- | ------- | ---------------- | | `alarm_id` | String | ✅ Yes | Unique identifier | Schedule.id, NotificationContentEntity.id | | `trigger_time` | Long/TimeInterval | ✅ Yes | When to fire | Schedule.nextRunAt, NotificationContentEntity.scheduledTime | | `repeat_rule` | String/Enum | ✅ Yes | NONE, DAILY, WEEKLY, CUSTOM | Schedule.cron, Schedule.clockTime | | `channel_id` | String | ✅ Yes | Notification channel (Android) | NotificationContentEntity (implicit) | | `priority` | String/Int | ✅ Yes | Notification priority | NotificationContentEntity.priority | | `title` | String | ✅ Yes | Notification title | NotificationContentEntity.title | | `body` | String | ✅ Yes | Notification body | NotificationContentEntity.body | | `sound_enabled` | Boolean | ✅ Yes | Sound preference | NotificationContentEntity.soundEnabled | | `vibration_enabled` | Boolean | ✅ Yes | Vibration preference | NotificationContentEntity.vibrationEnabled | | `payload` | String/JSON | ⚠️ Optional | Additional content | ContentCache.payload | | `created_at` | Long/TimeInterval | ✅ Yes | Creation timestamp | NotificationContentEntity.createdAt | | `updated_at` | Long/TimeInterval | ✅ Yes | Last update timestamp | NotificationContentEntity.updatedAt | | `enabled` | Boolean | ✅ Yes | Whether alarm is active | Schedule.enabled | ### 2.2 Storage Implementation **Android**: * **Primary**: Room database (`DailyNotificationDatabase`) * **Location**: `android/src/main/java/com/timesafari/dailynotification/` * **Entities**: `Schedule`, `NotificationContentEntity`, `ContentCache` **iOS**: * **Primary**: UNUserNotificationCenter (OS-managed) * **Secondary**: Plugin storage (UserDefaults, CoreData, or files) * **Location**: `ios/Plugin/` * **Component**: `DailyNotificationStorage?` ### 2.3 Persistence Validation The plugin **MUST**: * Validate persistence on every alarm schedule * Log persistence failures * Handle persistence errors gracefully * Provide recovery mechanism if persistence fails **Acceptance Criteria**: * **Test**: Schedule alarm, kill app immediately, verify alarm persists in database * **Test**: Schedule alarm, fill storage, verify graceful error handling * **Test**: Schedule alarm, corrupt database, verify recovery mechanism * **Pass**: All persistence operations succeed or fail gracefully with logging ### 2.4 Data Integrity Rules **Required Validation**: * **MUST** validate all required fields before persistence * **MUST** validate schedule format (cron or HH:mm) * **MUST** validate trigger time is in the future (or handle past times appropriately) * **MUST** ensure foreign key relationships are valid (Schedule → NotificationContentEntity) **Data Integrity Constraints**: * `alarm_id` **MUST** be unique across all schedules * `scheduleId` **MUST** reference an existing Schedule.id * `scheduledTime` **MUST** be a valid timestamp * `deliveryStatus` **MUST** be one of: "pending", "delivered", "missed" ### 2.5 Failure Modes **Persistence Failure Handling**: * Database errors: Log error, continue with alarm scheduling (best effort), retry persistence * Storage full: Log warning, attempt cleanup, retry, notify user if critical * Invalid data: Skip invalid entries, log warning, continue processing * Migration failures: Rollback to backup, notify user, preserve existing data --- ## 3. Recovery Contract ### 3.1 Recovery Triggers & Required Actions The plugin **MUST** implement recovery at the following points with the specified actions. This is the complete recovery contract that defines what the plugin guarantees for each recovery scenario. #### 3.1.1 Boot Event (Android Only) **Trigger**: `BOOT_COMPLETED` broadcast **Required Actions**: 1. Load all enabled alarms from persistent storage 2. Reschedule each alarm using AlarmManager 3. Detect missed alarms (trigger_time < now) 4. Generate missed alarm events/notifications 5. Log recovery actions **Code Reference**: `BootReceiver.kt` line 24 **Implementation Status**: See [Phase 3 Implementation](../android-implementation-directive-phase3.md) **Platform Reference**: [Android §2.1.2](../01-platform-capability-reference.md#212-alarms-can-be-preserved-across-device-reboot) **Acceptance Criteria**: * **Test**: Schedule alarm for 10 minutes, reboot device, do NOT open app, wait 15 minutes * **Expected**: Alarm does NOT fire (OS limitation - see [Doc A §2.1.2](./01-platform-capability-reference.md#212-alarms-can-be-preserved-across-device-reboot)) * **Test**: Open app after reboot * **Expected**: Plugin detects missed alarm, reschedules future alarms * **Pass**: Missed alarm detected exactly once, future alarms rescheduled **Missed Alarm Detection Acceptance Criteria**: * **Test**: Create alarm scheduled for 2 minutes, kill app, wait 5 minutes, launch app * **Expected**: Plugin detects missed alarm, marks in database (`delivery_status = 'missed'`) * **Test**: Create repeating alarm, miss 3 occurrences, launch app * **Expected**: Plugin detects all 3 missed alarms, reschedules next occurrence * **Pass**: All missed alarms detected and marked, no duplicates, future alarms rescheduled --- #### 3.1.2 App Cold Start **Trigger**: App launched from terminated state **Required Actions**: 1. Load all enabled alarms from persistent storage 2. Verify active alarms match stored alarms 3. Detect missed alarms (trigger_time < now) 4. Reschedule future alarms 5. Generate missed alarm events/notifications 6. Log recovery actions **Implementation Status**: See [Phase 1 Implementation](../android-implementation-directive-phase1.md) **Code Location**: Plugin initialization (`DailyNotificationPlugin.load()` or equivalent) **Platform Reference**: [Android §2.1.4](../01-platform-capability-reference.md#214-alarms-can-be-restored-after-app-restart) **Acceptance Criteria**: * **Test**: Schedule alarm for 5 minutes, kill app process (not force stop), wait 10 minutes * **Expected**: Alarm fires (OS handles) * **Test**: Launch app after alarm time passed * **Expected**: Plugin detects missed alarm if alarm did not fire, reschedules future alarms * **Pass**: Missed alarm detected if applicable, future alarms verified/rescheduled --- #### 3.1.3 App Warm Start **Trigger**: App returning from background **Required Actions**: 1. Verify active alarms are still scheduled 2. Detect missed alarms (trigger_time < now) 3. Reschedule if needed 4. Log recovery actions **Implementation Status**: Deferred to future phase **Platform Reference**: [Android §2.1.4](../01-platform-capability-reference.md#214-alarms-can-be-restored-after-app-restart) --- #### 3.1.4 Force Stop Recovery (Android Only) **Trigger**: App launched after Force Stop **Required Actions**: 1. Detect force stop scenario (DB has alarms, AlarmManager has zero) 2. Mark all past alarms as missed 3. Reschedule all future alarms 4. Generate missed alarm events/notifications 5. Log recovery actions **Implementation Status**: See [Phase 2 Implementation](../android-implementation-directive-phase2.md) **Platform Reference**: [Android §2.2.1](./01-platform-capability-reference.md#221-you-cannot-survive-force-stop), [Android §2.2.2](./01-platform-capability-reference.md#222-you-cannot-auto-resume-after-force-stop) **Acceptance Criteria**: * **Test**: Schedule multiple alarms (past and future), force stop app, wait past scheduled times * **Expected**: No alarms fire (OS limitation) * **Test**: Open app after force stop * **Expected**: Plugin detects force stop scenario, marks all past alarms as missed, reschedules all future alarms * **Pass**: All past alarms marked as missed, all future alarms rescheduled --- #### 3.1.5 User Taps Notification **Trigger**: User interaction with notification **Required Actions**: 1. Launch app (OS handles) 2. Detect if notification was missed 3. Handle notification action 4. Update alarm state if needed **Implementation Status**: Basic support exists **Platform Reference**: [Android §2.1.1](../01-platform-capability-reference.md#211-alarms-survive-ui-kills-swipe-from-recents), [iOS §3.1.1](../01-platform-capability-reference.md#311-notifications-survive-app-termination) --- ### 3.2 Recovery Matrix | Recovery Point | Android | iOS | Implementation Phase | | -------------- | ------- | --- | -------------------- | | Boot event | ✅ Required | ❌ Not needed (OS handles) | Phase 3 | | Cold start | ✅ Required | ✅ Required | Phase 1 | | Warm start | ⚠️ Optional | ⚠️ Optional | Future | | Force stop | ✅ Required | N/A | Phase 2 | | User tap | ✅ Required | ✅ Required | Basic | --- ## 4. Missed Alarm Handling Contract ### 4.1 Definition An alarm is "missed" if: * `trigger_time < now` * Alarm was not fired (or firing status unknown) * Alarm is still enabled * Alarm has not been manually cancelled ### 4.2 Detection Triggers Missed alarms **MUST** be detected at: 1. **App cold start** - When app launches from terminated state 2. **App warm start** - When app returns from background (optional, future phase) 3. **Boot event** (Android) - When device reboots 4. **Force stop recovery** (Android) - When app launches after force stop ### 4.3 Required Actions When a missed alarm is detected, the plugin **MUST**: 1. **Detect** missed alarms during recovery 2. **Mark** missed alarms in database (`delivery_status = 'missed'`) 3. **Generate** missed alarm event/notification (optional, future phase) 4. **Reschedule** future occurrences (if repeating) 5. **Log** missed alarm for debugging 6. **Update** alarm state (mark as missed or reschedule) ### 4.4 Missed Alarm Resolution Rules **Resolution Strategy**: | Scenario | Detection Time | Resolution Action | Next Occurrence | | -------- | -------------- | ----------------- | --------------- | | **Single alarm missed** | Cold start | Mark as missed, do not reschedule | N/A (one-time alarm) | | **Repeating alarm missed** | Cold start | Mark as missed, reschedule next occurrence | Calculate from current time | | **Multiple alarms missed** | Force stop recovery | Mark all as missed, reschedule all future | Calculate from current time | | **Boot recovery** | Boot event | Mark past as missed, reschedule all future | Calculate from current time | **State Transition Table**: ``` NONE (first launch, empty DB) ↓ COLD_START (DB populated, alarms may exist) ↓ FORCE_STOP? (detect: DB populated & no scheduled alarms) ↓ BOOT (detect: SharedPreferences flag set) ↓ Recovery Actions: - Detect missed alarms - Mark missed in database - Reschedule future alarms - Update state ``` **Transition Detection**: * **NONE → COLD_START**: DB empty on first launch * **COLD_START → FORCE_STOP**: DB has schedules but AlarmManager has zero alarms * **COLD_START → BOOT**: SharedPreferences `last_boot_at` flag set * **Any → Recovery**: Execute recovery actions based on scenario ### 4.5 Implementation Requirements * **MUST** run on app launch (cold/warm start) * **MUST** run on boot (Android) * **MUST NOT** duplicate missed alarm notifications * **MUST** handle timezone changes * **MUST** handle clock adjustments **Implementation Status**: - Phase 1: Basic detection and marking - See [Phase 1 Implementation](../android-implementation-directive-phase1.md) - Phase 2: Force stop recovery - See [Phase 2 Implementation](../android-implementation-directive-phase2.md) - Phase 3: Boot recovery - See [Phase 3 Implementation](../android-implementation-directive-phase3.md) - Future: Event emission, user notifications --- ## 5. JS/TS API Contract ### 5.1 JS/TS API Contract The plugin **MUST** document and guarantee the following behaviors to JavaScript/TypeScript developers: #### 5.1.1 `scheduleDailyNotification(options)` **Method Signature**: ```typescript scheduleDailyNotification(options: { schedule: string; // Cron or HH:mm format title: string; body: string; sound?: boolean; vibration?: boolean; priority?: 'high' | 'normal' | 'low'; payload?: object; }): Promise<{ success: boolean; alarmId?: string; error?: { code: string; message: string; action?: 'opened_settings' | 'permission_denied'; }; }> ``` **Returned Fields**: * `success` (boolean) - Whether scheduling succeeded * `alarmId` (string, optional) - Unique identifier for scheduled alarm * `error` (object, optional) - Error details if scheduling failed * `code` (string) - Error code for programmatic handling * `message` (string) - Human-readable error message * `action` (string, optional) - Action taken (e.g., "opened_settings") **Guarantees**: * ✅ Notification will fire if app is swiped from recents - **MUST** (OS-guaranteed) * ✅ Notification will fire if app is terminated by OS - **MUST** (OS-guaranteed) * ⚠️ Notification will fire after reboot **only if**: * Android: Boot receiver is registered and working - **SHOULD** (best-effort) * iOS: Automatic (OS handles) - **MUST** (OS-guaranteed) * ❌ Notification will **NOT** fire after Android Force Stop until app is opened - **MUST NOT** (OS limitation) * ⚠️ iOS notifications have ±180s timing tolerance - **SHOULD** (OS limitation) **Platform Caveats**: * Android requires `SCHEDULE_EXACT_ALARM` permission on Android 12+ - See [Doc A §5.2](./01-platform-capability-reference.md#52-android-s-exact-alarm-permission-decision-tree) * Android requires `RECEIVE_BOOT_COMPLETED` permission for reboot recovery - See [Doc A §2.1.2](./01-platform-capability-reference.md#212-alarms-can-be-preserved-across-device-reboot) * iOS requires notification authorization - See [Doc A §3.1.1](./01-platform-capability-reference.md#311-notifications-survive-app-termination) **Error States**: * `EXACT_ALARM_PERMISSION_REQUIRED` - Android 12+ exact alarm permission needed * **Response**: Plugin opens system settings, returns `action: "opened_settings"` * `NOTIFICATIONS_DENIED` - Notification permission denied * **Response**: Plugin shows error message, returns `action: "permission_denied"` * `SCHEDULE_FAILED` - Scheduling failed (check logs) * **Response**: Plugin logs error, returns error with message * `STORAGE_FULL` - Cannot persist alarm (storage full) * **Response**: Plugin attempts cleanup, retries, or returns error * `INVALID_SCHEDULE` - Schedule format invalid * **Response**: Plugin returns error with validation message **Platform References**: * [Android §2.1.1](./01-platform-capability-reference.md#211-alarms-survive-ui-kills-swipe-from-recents) * [Android §2.1.2](./01-platform-capability-reference.md#212-alarms-can-be-preserved-across-device-reboot) * [Android §2.2.1](./01-platform-capability-reference.md#221-you-cannot-survive-force-stop) * [iOS §3.1.1](./01-platform-capability-reference.md#311-notifications-survive-app-termination) * [iOS §3.1.2](./01-platform-capability-reference.md#312-notifications-persist-across-device-reboot) **Developer Responsibilities**: * **MUST** handle permission requests (Android 12+ exact alarm, iOS notification authorization) * **MUST** handle error responses and provide user feedback * **SHOULD** check `getNotificationStatus()` to verify alarm scheduling * **MUST NOT** assume alarms will fire after Force Stop (Android) until app is opened * **MUST NOT** assume exact timing on iOS (±180s tolerance) * **SHOULD** implement retry logic for scheduling failures **Platform Differences**: * **Android**: Requires boot receiver for reboot recovery - See [Doc A §2.1.2](./01-platform-capability-reference.md#212-alarms-can-be-preserved-across-device-reboot) * **iOS**: Notifications persist automatically across reboot - See [Doc A §3.1.2](./01-platform-capability-reference.md#312-notifications-persist-across-device-reboot) * **Android**: App code runs when alarm fires - See [Doc A §2.1.1](./01-platform-capability-reference.md#211-alarms-survive-ui-kills-swipe-from-recents) * **iOS**: App code does NOT run when notification fires - See [Doc A §3.2.1](./01-platform-capability-reference.md#321-app-code-does-not-run-when-notification-fires) --- #### 5.1.2 `scheduleDailyReminder(options)` **Method Signature**: Same as `scheduleDailyNotification()` but without `payload` option. **Guarantees**: * Same as `scheduleDailyNotification()` above * Static reminder (no content dependency) * Fires even if content fetch fails --- #### 5.1.3 `getNotificationStatus()` **Method Signature**: ```typescript getNotificationStatus(): Promise<{ pendingCount: number; lastNotificationTime?: number; missedAlarms?: Array<{ alarmId: string; scheduledTime: number; detectedAt: number; }>; permissions: { exactAlarm?: boolean; // Android 12+ notifications?: boolean; // iOS bootReceiver?: boolean; // Android }; }> ``` **Returned Fields**: * `pendingCount` (number) - Number of scheduled alarms * `lastNotificationTime` (number, optional) - Timestamp of last notification * `missedAlarms` (array, optional) - Array of missed alarms * `alarmId` (string) - Unique identifier * `scheduledTime` (number) - When alarm was scheduled to fire * `detectedAt` (number) - When missed alarm was detected * `permissions` (object) - Current permission status * `exactAlarm` (boolean, optional) - Android 12+ exact alarm permission * `notifications` (boolean, optional) - iOS notification authorization * `bootReceiver` (boolean, optional) - Android boot receiver capability **Guarantees**: * Returns current notification status - **MUST** * Includes pending notifications - **MUST** * Includes last notification time - **SHOULD** * May include missed alarm information - **SHOULD** (if detected) --- ### 5.2 API Warnings The plugin **MUST** document the following warnings in JS/TS API documentation: **Android Warnings**: * "Notifications will not fire after device reboot unless the app is opened at least once" - See [Doc A §2.1.2](./01-platform-capability-reference.md#212-alarms-can-be-preserved-across-device-reboot) * "Force Stop will prevent all notifications until the app is manually opened" - See [Doc A §2.2.1](./01-platform-capability-reference.md#221-you-cannot-survive-force-stop) * "Exact alarm permission is required on Android 12+ for precise timing" - See [Doc A §5.2](./01-platform-capability-reference.md#52-android-s-exact-alarm-permission-decision-tree) **iOS Warnings**: * "Notifications have ±180 seconds timing tolerance" - See [Doc A §6.1](./01-platform-capability-reference.md#61-notification-timing-accuracy) * "App code does not run when notifications fire (unless user interacts)" - See [Doc A §3.2.1](./01-platform-capability-reference.md#321-app-code-does-not-run-when-notification-fires) * "Background execution is system-controlled and not guaranteed" - See [Doc A §3.1.3](./01-platform-capability-reference.md#313-background-tasks-for-prefetching) **Cross-Platform Warnings**: * "Missed alarms are detected on app launch, not at trigger time" - See [Doc C §4.2](./03-plugin-requirements.md#42-detection-triggers) * "Repeating alarms must be rescheduled for each occurrence (Android)" - See [Doc A §2.1.2](./01-platform-capability-reference.md#212-alarms-can-be-preserved-across-device-reboot) * "Location-based triggers do not persist across reboot (iOS)" - See [Doc A §3.1.2](./01-platform-capability-reference.md#312-notifications-persist-across-device-reboot) --- ### 5.3 API Error Handling The plugin **MUST**: * Return clear error messages * Include error codes for programmatic handling * Open system settings when permission is needed * Provide actionable guidance in error messages **Example Error Response**: ```typescript { code: "EXACT_ALARM_PERMISSION_REQUIRED", message: "Exact alarm permission required. Please grant 'Alarms & reminders' permission in Settings, then try again.", action: "opened_settings" // or "permission_denied" } ``` --- ## 6. Plugin WILL NOT Guarantee ### 6.1 Hard Boundaries (Cannot Ever Guarantee) The plugin **MUST NOT** promise or attempt to guarantee: | Behavior | Platform | Reason | Platform Reference | | -------- | -------- | ------ | ------------------ | | **Auto-recovery after Force Stop** | Android | OS hard kill - cannot bypass | [Doc A §2.2.1](./01-platform-capability-reference.md#221-you-cannot-survive-force-stop) | | **App code execution on iOS notification fire** | iOS | OS limitation - code does not run | [Doc A §3.2.1](./01-platform-capability-reference.md#321-app-code-does-not-run-when-notification-fires) | | **Exact timing on iOS** | iOS | ±180s tolerance is OS limitation | [Doc A §6.1](./01-platform-capability-reference.md#61-notification-timing-accuracy) | | **Background execution timing** | iOS | System-controlled, not guaranteed | [Doc A §3.1.3](./01-platform-capability-reference.md#313-background-tasks-for-prefetching) | | **Alarm persistence without storage** | Both | Must persist in durable storage | [Doc A §2.2.3](./01-platform-capability-reference.md#223-alarms-cannot-be-preserved-solely-in-ram) | **Enforcement**: These behaviors **MUST** be documented as unsupported in JS/TS API documentation. --- ### 6.2 Guarantee vs Best-Effort Matrix | Behavior | Android | iOS | Guarantee Level | Notes | | -------- | ------- | --- | -------------- | ----- | | **Notification fires after swipe** | ✅ Guaranteed | ✅ Guaranteed | **MUST** - OS-guaranteed | [Doc A §2.1.1](./01-platform-capability-reference.md#211-alarms-survive-ui-kills-swipe-from-recents), [Doc A §3.1.1](./01-platform-capability-reference.md#311-notifications-survive-app-termination) | | **Notification fires after reboot** | ⚠️ Best-effort | ✅ Guaranteed | Android: **SHOULD** (requires boot receiver), iOS: **MUST** | [Doc A §2.1.2](./01-platform-capability-reference.md#212-alarms-can-be-preserved-across-device-reboot), [Doc A §3.1.2](./01-platform-capability-reference.md#312-notifications-persist-across-device-reboot) | | **Missed alarm detection** | ✅ Guaranteed | ✅ Guaranteed | **MUST** - Plugin-required | [Doc C §4.2](./03-plugin-requirements.md#42-detection-triggers) | | **Force stop recovery** | ✅ Guaranteed | N/A | **MUST** - Plugin-required | [Doc C §3.1.4](./03-plugin-requirements.md#314-force-stop-recovery-android-only) | | **Exact timing** | ⚠️ Best-effort | ⚠️ Best-effort | Android: **SHOULD** (with permission), iOS: **SHOULD** (±180s) | [Doc A §5.2](./01-platform-capability-reference.md#52-android-s-exact-alarm-permission-decision-tree), [Doc A §6.1](./01-platform-capability-reference.md#61-notification-timing-accuracy) | | **Background execution** | ⚠️ Best-effort | ⚠️ Best-effort | **SHOULD** - System-controlled | [Doc A §3.1.3](./01-platform-capability-reference.md#313-background-tasks-for-prefetching) | **Legend**: - **MUST** = Plugin guarantees this behavior - **SHOULD** = Plugin attempts this but cannot guarantee (best-effort) - **MUST NOT** = Plugin cannot and will not attempt this --- ### 6.3 Failure Modes & Required Responses | Failure Mode | Platform | Required Plugin Response | User Impact | | ------------ | -------- | ------------------------ | ----------- | | **Persistence failure** | Both | Log error, continue with alarm scheduling (best effort), retry persistence | Alarm may be lost if app killed | | **Storage full** | Both | Log warning, attempt cleanup, retry, notify user if critical | User must free space | | **Invalid data** | Both | Skip invalid entries, log warning, continue processing | Invalid alarms ignored | | **Permission denied** | Android | Open settings, show error message, return error code | User must grant permission | | **Boot receiver not registered** | Android | Log error, alarms not rescheduled on boot | Alarms lost on reboot | | **Force stop** | Android | Detect on app restart, recover all alarms, mark missed | Alarms lost until app opened | | **Background execution denied** | iOS | Log warning, schedule next attempt, notify user | Prefetch may fail | | **Notification authorization denied** | iOS | Show error, return error code, cannot schedule | Notifications disabled | **Required Actions**: * **MUST** log all failures with sufficient context * **MUST** provide user-visible error messages for critical failures * **SHOULD** provide recovery mechanisms where possible * **MUST NOT** crash or silently fail --- ## 7. Explicit Storage Schema ### 7.1 Required Database Schema **Android (Room Database)**: | Table | Field | Type | Required | Purpose | | ----- | ----- | ----- | -------- | ------- | | **Schedule** | `id` | String | ✅ Yes | Unique identifier | | | `kind` | String | ✅ Yes | "notify" or "fetch" | | | `enabled` | Boolean | ✅ Yes | Whether alarm is active | | | `cron` | String? | ⚠️ Optional | Cron expression | | | `clockTime` | String? | ⚠️ Optional | HH:mm format | | | `nextRunAt` | Long? | ⚠️ Optional | Next scheduled time | | | `createdAt` | Long | ✅ Yes | Creation timestamp | | | `updatedAt` | Long | ✅ Yes | Last update timestamp | | **NotificationContentEntity** | `id` | String | ✅ Yes | Unique identifier | | | `scheduleId` | String | ✅ Yes | Foreign key to Schedule | | | `title` | String | ✅ Yes | Notification title | | | `body` | String | ✅ Yes | Notification body | | | `scheduledTime` | Long | ✅ Yes | When to fire | | | `deliveryStatus` | String | ✅ Yes | "pending", "delivered", "missed" | | | `priority` | String | ✅ Yes | Notification priority | | | `soundEnabled` | Boolean | ✅ Yes | Sound preference | | | `vibrationEnabled` | Boolean | ✅ Yes | Vibration preference | | | `createdAt` | Long | ✅ Yes | Creation timestamp | | | `updatedAt` | Long | ✅ Yes | Last update timestamp | | **ContentCache** | `id` | String | ✅ Yes | Unique identifier | | | `scheduleId` | String | ✅ Yes | Foreign key to Schedule | | | `payload` | String? | ⚠️ Optional | Additional content (JSON) | | | `createdAt` | Long | ✅ Yes | Creation timestamp | **iOS (Plugin Storage)**: | Storage Type | Field | Type | Required | Purpose | | ------------ | ----- | ----- | -------- | ------- | | **UserDefaults/CoreData** | `alarm_id` | String | ✅ Yes | Unique identifier | | | `trigger_time` | TimeInterval | ✅ Yes | When to fire | | | `repeat_rule` | String | ✅ Yes | NONE, DAILY, WEEKLY, CUSTOM | | | `title` | String | ✅ Yes | Notification title | | | `body` | String | ✅ Yes | Notification body | | | `enabled` | Boolean | ✅ Yes | Whether alarm is active | | | `created_at` | TimeInterval | ✅ Yes | Creation timestamp | | | `updated_at` | TimeInterval | ✅ Yes | Last update timestamp | **Note**: iOS also uses UNUserNotificationCenter (OS-managed) for notification scheduling, but plugin **MUST** maintain parallel storage for missed detection. --- ## 8. Explicit Unsupported Behaviors ### 8.1 Hard Boundaries (Plugin WILL NOT Guarantee) The plugin **MUST NOT** support or guarantee the following behaviors: | Behavior | Platform | Reason | Platform Reference | | -------- | -------- | ------ | ------------------ | | **Auto-recovery after Android Force Stop** | Android | OS hard kill - cannot bypass | [Doc A §2.2.1](./01-platform-capability-reference.md#221-you-cannot-survive-force-stop) | | **App code execution on iOS notification fire** | iOS | OS limitation - code does not run | [Doc A §3.2.1](./01-platform-capability-reference.md#321-app-code-does-not-run-when-notification-fires) | | **Exact timing on iOS** | iOS | ±180s tolerance is OS limitation | [Doc A §6.1](./01-platform-capability-reference.md#61-notification-timing-accuracy) | | **Background execution timing guarantees** | Both | System-controlled, not guaranteed | [Doc A §3.1.3](./01-platform-capability-reference.md#313-background-tasks-for-prefetching) | | **Alarm persistence without durable storage** | Both | Must use database/files | [Doc A §2.2.3](./01-platform-capability-reference.md#223-alarms-cannot-be-preserved-solely-in-ram) | | **Repeating alarms without rescheduling** | Android | Must reschedule each occurrence | [Doc A §2.1.2](./01-platform-capability-reference.md#212-alarms-can-be-preserved-across-device-reboot) | | **Location-based triggers** | iOS | Does not persist across reboot | [Doc A §3.1.2](./01-platform-capability-reference.md#312-notifications-persist-across-device-reboot) | | **Silent rescheduling without user launch** | Android | Force stop blocks all execution | [Doc A §2.2.1](./01-platform-capability-reference.md#221-you-cannot-survive-force-stop) | **Documentation Requirement**: All unsupported behaviors **MUST** be documented in JS/TS API with clear warnings. See [§5.2 - API Warnings](./03-plugin-requirements.md#52-api-warnings). --- ### 8.2 Platform-Specific Limitations **Android**: * Force Stop is a hard kill that cannot be bypassed - See [Doc A §2.2.1](./01-platform-capability-reference.md#221-you-cannot-survive-force-stop) * Reboot wipes all alarms; must reschedule from storage - See [Doc A §2.1.2](./01-platform-capability-reference.md#212-alarms-can-be-preserved-across-device-reboot) * Exact alarm permission required on Android 12+ - See [Doc A §5.2](./01-platform-capability-reference.md#52-android-s-exact-alarm-permission-decision-tree) * Doze mode may defer inexact alarms - See [Doc A §2.1.1](./01-platform-capability-reference.md#211-alarms-survive-ui-kills-swipe-from-recents) **iOS**: * Background execution is severely limited - See [Doc A §3.1.3](./01-platform-capability-reference.md#313-background-tasks-for-prefetching) * App code does not run when notifications fire - See [Doc A §3.2.1](./01-platform-capability-reference.md#321-app-code-does-not-run-when-notification-fires) * Timing has ±180s tolerance - See [Doc A §6.1](./01-platform-capability-reference.md#61-notification-timing-accuracy) * BGTaskScheduler execution is not guaranteed - See [Doc A §3.1.3](./01-platform-capability-reference.md#313-background-tasks-for-prefetching) --- ## 9. Traceability Matrix **Complete mapping from platform facts → requirements → tests → implementation**: | Requirement | Platform Source (Doc A) | Expected Plugin Behavior (Doc C) | Test Scenario (Doc B) | Implemented in Phase | | ----------- | ----------------------- | ------------------------------- | --------------------- | -------------------- | | **Missed alarm detection** | [Android §2.1.4](./01-platform-capability-reference.md#214-alarms-can-be-restored-after-app-restart) | [§4.2 - Detection Triggers](./03-plugin-requirements.md#42-detection-triggers) | [Test 4 Step 6](./02-plugin-behavior-exploration.md#test-4-device-reboot) | Phase 1 | | **Boot reschedule** | [Android §2.1.2](./01-platform-capability-reference.md#212-alarms-can-be-preserved-across-device-reboot) | [§3.1.1 - Boot Event](./03-plugin-requirements.md#311-boot-event-android-only) | [Test 4 Step 7](./02-plugin-behavior-exploration.md#test-4-device-reboot) | Phase 3 | | **Force stop recovery** | [Android §2.2.1](./01-platform-capability-reference.md#221-you-cannot-survive-force-stop) | [§3.1.4 - Force Stop Recovery](./03-plugin-requirements.md#314-force-stop-recovery-android-only) | [Test 5](./02-plugin-behavior-exploration.md#test-5-android-force-stop) | Phase 2 | | **Cold start recovery** | [Android §2.1.4](./01-platform-capability-reference.md#214-alarms-can-be-restored-after-app-restart) | [§3.1.2 - App Cold Start](./03-plugin-requirements.md#312-app-cold-start) | [Test 4 Step 5](./02-plugin-behavior-exploration.md#test-4-device-reboot) | Phase 1 | | **Swipe from recents** | [Android §2.1.1](./01-platform-capability-reference.md#211-alarms-survive-ui-kills-swipe-from-recents), [iOS §3.1.1](./01-platform-capability-reference.md#311-notifications-survive-app-termination) | [§1.1 - Guarantees Matrix](./03-plugin-requirements.md#11-cross-platform-guarantees-matrix) | [Test 2](./02-plugin-behavior-exploration.md#test-2-swipe-from-recents) | OS-guaranteed | | **iOS notification persistence** | [iOS §3.1.2](./01-platform-capability-reference.md#312-notifications-persist-across-device-reboot) | [§1.1 - Guarantees Matrix](./03-plugin-requirements.md#11-cross-platform-guarantees-matrix) | [Test 3](./02-plugin-behavior-exploration.md#test-3-device-reboot-1) | OS-guaranteed | | **Persistence** | [Android §2.2.3](./01-platform-capability-reference.md#223-alarms-cannot-be-preserved-solely-in-ram) | [§2 - Persistence Requirements](./03-plugin-requirements.md#2-persistence-requirements) | [Persistence Investigation](./02-plugin-behavior-exploration.md#13-persistence-investigation) | Phase 1 | | **Exact alarm permission** | [Android §5.2](./01-platform-capability-reference.md#52-android-s-exact-alarm-permission-decision-tree) | [§10.1.1 - Permissions](./03-plugin-requirements.md#1011-permissions) | [Test 6](./02-plugin-behavior-exploration.md#test-6-exact-alarm-permission-android-12) | Phase 1 | --- ## 10. Platform-Specific Requirements ### 10.1 Android Requirements #### 10.1.1 Permissions **Required Permissions**: * `RECEIVE_BOOT_COMPLETED` - Boot receiver * `SCHEDULE_EXACT_ALARM` - Android 12+ (API 31+) for exact alarms * `POST_NOTIFICATIONS` - Android 13+ (API 33+) for notifications **Permission Handling**: * Check permission before scheduling * Request permission if not granted * Open system settings if permission denied * Provide clear error messages **Code Reference**: `DailyNotificationPlugin.kt` line 1309 **Platform Reference**: [Android §2.2.4](../01-platform-capability-reference.md#224-you-cannot-bypass-doze-or-battery-optimization-restrictions-without-permission) --- #### 10.1.2 Manifest Entries **Required Manifest Entries**: ```xml ``` **Location**: Test app manifest: `test-apps/android-test-app/app/src/main/AndroidManifest.xml` --- #### 10.1.3 Notification Channels **Required Channels**: * `timesafari.daily` - Primary notification channel * `daily_reminders` - Reminder notifications (if used) **Channel Configuration**: * Importance: HIGH (for alarms), DEFAULT (for reminders) * Sound: Enabled by default * Vibration: Enabled by default * Show badge: Enabled **Code Reference**: `ChannelManager.java` or `NotifyReceiver.kt` line 454 --- #### 10.1.4 Alarm Scheduling **Required API Usage**: * `setAlarmClock()` for Android 5.0+ (preferred) * `setExactAndAllowWhileIdle()` for Android 6.0+ (fallback) * `setExact()` for older versions (fallback) **Code Reference**: `NotifyReceiver.kt` line 219, 223, 231 **Platform Reference**: [Android §2.1.1](../01-platform-capability-reference.md#211-alarms-survive-ui-kills-swipe-from-recents) --- ### 10.2 iOS Requirements #### 10.2.1 Permissions **Required Permissions**: * Notification authorization (requested at runtime) **Permission Handling**: * Request permission before scheduling * Handle authorization status * Provide clear error messages --- #### 10.2.2 Background Tasks **Required Background Task Identifiers**: * `com.timesafari.dailynotification.fetch` - Background fetch * `com.timesafari.dailynotification.notify` - Notification task (if used) **Background Task Registration**: * Register in `Info.plist`: ```xml BGTaskSchedulerPermittedIdentifiers com.timesafari.dailynotification.fetch ``` **Code Reference**: `DailyNotificationPlugin.swift` line 31-32 **Platform Reference**: [iOS §3.1.3](../01-platform-capability-reference.md#313-background-tasks-for-prefetching) --- #### 10.2.3 Notification Scheduling **Required API Usage**: * `UNUserNotificationCenter.add()` - Schedule notifications * `UNCalendarNotificationTrigger` - Calendar-based triggers (preferred) * `UNTimeIntervalNotificationTrigger` - Time interval triggers **Code Reference**: `DailyNotificationScheduler.swift` line 185 **Platform Reference**: [iOS §3.1.1](../01-platform-capability-reference.md#311-notifications-survive-app-termination) --- #### 10.2.4 Notification Categories **Required Categories**: * `DAILY_NOTIFICATION` - Primary notification category **Category Configuration**: * Actions: Configure as needed * Options: Custom sound, custom actions **Code Reference**: `DailyNotificationScheduler.swift` line 62+ --- ## 11. Testing Requirements ### 11.1 Required Test Scenarios The plugin **must** be tested for: **Android**: * [ ] Base case (alarm fires on time) * [ ] Swipe from recents * [ ] OS kill (memory pressure) * [ ] Device reboot (with and without app launch) * [ ] Force stop (with app restart) * [ ] Exact alarm permission (Android 12+) * [ ] Boot receiver functionality * [ ] Missed alarm detection **iOS**: * [ ] Base case (notification fires on time) * [ ] Swipe app away * [ ] Device reboot (without app launch) * [ ] Hard termination and relaunch * [ ] Background execution limits * [ ] Missed notification detection **Cross-Platform**: * [ ] Timezone changes * [ ] Clock adjustments * [ ] Multiple simultaneous alarms * [ ] Repeating alarms * [ ] Alarm cancellation **Test Documentation**: See [Plugin Behavior Exploration](./02-plugin-behavior-exploration.md) for test matrices. --- ## 12. Migration Rules ### 12.1 Storage Format Changes **If plugin storage format changes**, migration **MUST** define: 1. **Version detection**: How to identify old vs new format 2. **Safe upgrade path**: Step-by-step migration process 3. **Failure fallback**: What happens if migration fails 4. **No data loss**: All existing alarms must be preserved or explicitly marked as lost **Migration Requirements**: * **MUST** run automatically on app launch * **MUST** log migration steps for debugging * **MUST** handle partial migrations gracefully * **MUST NOT** lose user data without explicit user confirmation **Example Migration**: ```kotlin // Pseudo-code if (databaseVersion < currentVersion) { migrateDatabase(oldVersion, currentVersion) verifyMigrationIntegrity() if (migrationFailed) { rollbackToBackup() notifyUser() } } ``` ### 12.2 API Contract Changes **If JS/TS API changes**: * **MUST** maintain backward compatibility for at least one major version * **MUST** provide deprecation warnings before removal * **MUST** document migration path in changelog * **MUST** update Doc C before releasing breaking changes --- ## 13. API Stability Guarantees ### 13.1 Breaking Change Policy **No breaking change may occur without**: 1. **Doc C update** - Requirements document updated first 2. **MINOR or MAJOR version bump** - Semantic versioning enforced 3. **Migration notes** - Clear upgrade path documented 4. **Deprecation period** - Old API deprecated for at least one release cycle ### 13.2 Stability Guarantees **Plugin guarantees**: * **MUST** maintain API contract within same major version * **SHOULD** maintain API contract across minor versions (exceptions documented) * **MUST NOT** break API contract without major version bump **Example**: * `v1.0.0` → `v1.1.0`: New features added, no breaking changes ✅ * `v1.1.0` → `v2.0.0`: Breaking API change, migration guide required ✅ * `v1.1.0` → `v1.2.0`: Breaking API change ❌ (must be v2.0.0) --- ## 14. "What Happens If We Do Nothing?" Analysis ### 14.1 No Recovery Implementation **If plugin does NOT implement recovery**: | Scenario | What Happens | Impact | | -------- | ------------ | ------ | | **Device Reboot** (Android) | All alarms wiped, never fire | **Critical**: User loses all scheduled alarms | | **Force Stop** (Android) | All alarms cleared, never fire until app opened | **Critical**: User loses alarms until manual app launch | | **Cold Start** | Missed alarms never detected | **Major**: User unaware of missed notifications | | **Warm Start** | Alarms may be missing, never verified | **Minor**: Edge case, alarms usually still work | **Conclusion**: Recovery **MUST** be implemented for Android reboot and force stop scenarios. Cold start recovery **SHOULD** be implemented for user experience. ### 14.2 No Persistence **If plugin does NOT persist alarms**: | Scenario | What Happens | Impact | | -------- | ------------ | ------ | | **App Kill** | All alarms lost | **Critical**: User loses all scheduled alarms | | **Reboot** | Cannot reschedule | **Critical**: Recovery impossible | | **Force Stop** | Cannot detect missed alarms | **Major**: Recovery incomplete | **Conclusion**: Persistence **MUST** be implemented. It is foundational for all recovery scenarios. ### 14.3 No Missed Alarm Detection **If plugin does NOT detect missed alarms**: | Scenario | What Happens | Impact | | -------- | ------------ | ------ | | **Missed Alarm** | User never notified | **Major**: User unaware of missed notifications | | **Repeating Alarm** | Next occurrence may be wrong | **Major**: Schedule drift over time | **Conclusion**: Missed alarm detection **SHOULD** be implemented for user experience and schedule accuracy. --- ## 15. Versioning Requirements ### 15.1 Breaking Changes Any change to alarm behavior is **breaking** and requires: * **MAJOR version bump** (semantic versioning) * **Migration guide** for existing users * **Deprecation warnings** (if applicable) * **Clear changelog entry** ### 10.2 Non-Breaking Changes Non-breaking changes include: * Bug fixes * Performance improvements * Additional features (backward compatible) * Documentation updates --- ## 16. Cross-Reference Anchors **All cross-references use standardized anchors**: | Reference Format | Example | Resolves To | | ---------------- | ------- | ----------- | | `[Doc A §X.Y]` | `[Doc A §2.1.1]` | [Platform Reference §2.1.1](./01-platform-capability-reference.md#211-alarms-survive-ui-kills-swipe-from-recents) | | `[Doc B §X.Y]` | `[Doc B §1.2]` | [Exploration §1.2](./02-plugin-behavior-exploration.md#12-behavior-testing-matrix) | | `[Doc C §X.Y]` | `[Doc C §3.1.2]` | [Requirements §3.1.2](#312-app-cold-start) | | `[Phase N §X.Y]` | `[Phase 1 §2.3]` | [Phase 1 Implementation](../android-implementation-directive-phase1.md#23-cold-start-recovery) | **Anchor Rules**: * All section headers MUST have unique anchors * Cross-references MUST use exact anchor format * Broken links MUST be fixed immediately --- ## Related Documentation - [Platform Capability Reference](./01-platform-capability-reference.md) - OS-level facts - [Plugin Behavior Exploration](./02-plugin-behavior-exploration.md) - Exploration template - [Unified Alarm Directive](./000-UNIFIED-ALARM-DIRECTIVE.md) - Master coordination document - [Phase 1: Cold Start Recovery](../android-implementation-directive-phase1.md) - Implementation - [Phase 2: Force Stop Recovery](../android-implementation-directive-phase2.md) - Implementation - [Phase 3: Boot Recovery](../android-implementation-directive-phase3.md) - Implementation --- ## Version History - **v1.1.0** (November 2025): Enhanced with non-guarantees, failure modes, storage schema - Added "Plugin WILL NOT Guarantee" section - Added Guarantee vs Best-Effort matrix - Added Failure Modes & Required Responses table - Added Explicit Storage Schema - Enhanced JS/TS API contract with returned fields and error states - Added Missed Alarm Resolution Rules - Added State Transition Table - Added Explicit Unsupported Features List - **v1.0.0** (November 2025): Initial requirements document - Plugin behavior guarantees and limitations - Persistence requirements - Recovery requirements - Missed alarm handling contract - JS/TS API contract - Platform-specific requirements - Testing requirements - Traceability matrix