Create unified alarm documentation system with strict role separation: - Doc A: Platform capability reference (canonical OS facts) - Doc B: Plugin behavior exploration (executable test harness) - Doc C: Plugin requirements (guarantees, JS/TS contract, traceability) Changes: - Add canonical rule to Doc A preventing platform fact duplication - Convert Doc B to pure executable test spec with scenario tables - Complete Doc C with guarantees matrix, JS/TS API contract, recovery contract, unsupported behaviors, and traceability matrix - Remove implementation details from unified directive - Add compliance milestone tracking and iOS parity gates - Add deprecation banners to legacy platform docs All documents now enforce strict role separation with cross-references to prevent duplication and ensure single source of truth.
48 KiB
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 ReferenceDoc B= Plugin Behavior ExplorationPhase 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 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, Doc A §3.1.1 |
| Survives reboot | ⚠️ Only if rescheduled | ✅ Yes | Android: SHOULD (best-effort), iOS: MUST (OS-guaranteed) | Doc A §2.1.2, Doc A §3.1.2 |
| Runs code on trigger | ✅ Yes (PendingIntent) | ❌ No (never) | Android: MUST, iOS: MUST NOT | Doc A §2.1.1, Doc A §3.2.1 |
| Missed alarm detection | ✅ Required | ✅ Required | MUST (Plugin-required) | Doc A §2.1.4 |
| Force stop recovery | ✅ Required | N/A | MUST (Plugin-required) | Doc A §2.2.1 |
| Exact timing | ⚠️ With permission | ⚠️ ±180s tolerance | Android: SHOULD (with permission), iOS: SHOULD (OS limitation) | Doc A §5.2, Doc A §6.1 |
| Background execution | ⚠️ WorkManager | ⚠️ BGTaskScheduler | SHOULD (best-effort, system-controlled) | Doc A §3.1.3 |
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 for complete list.
Summary:
- Android Force Stop: Cannot auto-recover - See Doc A §2.2.1
- iOS Code Execution: Cannot run code on notification fire - See Doc A §3.2.1
- iOS Timing: ±180s tolerance - See Doc A §6.1
- Background Execution: System-controlled, not guaranteed - See Doc A §3.1.3
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_idMUST be unique across all schedulesscheduleIdMUST reference an existing Schedule.idscheduledTimeMUST be a valid timestampdeliveryStatusMUST 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:
- Load all enabled alarms from persistent storage
- Reschedule each alarm using AlarmManager
- Detect missed alarms (trigger_time < now)
- Generate missed alarm events/notifications
- Log recovery actions
Code Reference: BootReceiver.kt line 24
Implementation Status: See Phase 3 Implementation
Platform Reference: Android §2.1.2
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)
- 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:
- Load all enabled alarms from persistent storage
- Verify active alarms match stored alarms
- Detect missed alarms (trigger_time < now)
- Reschedule future alarms
- Generate missed alarm events/notifications
- Log recovery actions
Implementation Status: See Phase 1 Implementation
Code Location: Plugin initialization (DailyNotificationPlugin.load() or equivalent)
Platform Reference: Android §2.1.4
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:
- Verify active alarms are still scheduled
- Detect missed alarms (trigger_time < now)
- Reschedule if needed
- Log recovery actions
Implementation Status: Deferred to future phase
Platform Reference: Android §2.1.4
3.1.4 Force Stop Recovery (Android Only)
Trigger: App launched after Force Stop
Required Actions:
- Detect force stop scenario (DB has alarms, AlarmManager has zero)
- Mark all past alarms as missed
- Reschedule all future alarms
- Generate missed alarm events/notifications
- Log recovery actions
Implementation Status: See Phase 2 Implementation
Platform Reference: Android §2.2.1, Android §2.2.2
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:
- Launch app (OS handles)
- Detect if notification was missed
- Handle notification action
- Update alarm state if needed
Implementation Status: Basic support exists
Platform Reference: Android §2.1.1, iOS §3.1.1
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:
- App cold start - When app launches from terminated state
- App warm start - When app returns from background (optional, future phase)
- Boot event (Android) - When device reboots
- Force stop recovery (Android) - When app launches after force stop
4.3 Required Actions
When a missed alarm is detected, the plugin MUST:
- Detect missed alarms during recovery
- Mark missed alarms in database (
delivery_status = 'missed') - Generate missed alarm event/notification (optional, future phase)
- Reschedule future occurrences (if repeating)
- Log missed alarm for debugging
- 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_atflag 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
- Phase 2: Force stop recovery - See Phase 2 Implementation
- Phase 3: Boot recovery - See Phase 3 Implementation
- 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:
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 succeededalarmId(string, optional) - Unique identifier for scheduled alarmerror(object, optional) - Error details if scheduling failedcode(string) - Error code for programmatic handlingmessage(string) - Human-readable error messageaction(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_ALARMpermission on Android 12+ - See Doc A §5.2 - Android requires
RECEIVE_BOOT_COMPLETEDpermission for reboot recovery - See Doc A §2.1.2 - iOS requires notification authorization - See Doc A §3.1.1
Error States:
EXACT_ALARM_PERMISSION_REQUIRED- Android 12+ exact alarm permission needed- Response: Plugin opens system settings, returns
action: "opened_settings"
- Response: Plugin opens system settings, returns
NOTIFICATIONS_DENIED- Notification permission denied- Response: Plugin shows error message, returns
action: "permission_denied"
- Response: Plugin shows error message, returns
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:
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
- iOS: Notifications persist automatically across reboot - See Doc A §3.1.2
- Android: App code runs when alarm fires - See Doc A §2.1.1
- iOS: App code does NOT run when notification fires - See Doc A §3.2.1
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:
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 alarmslastNotificationTime(number, optional) - Timestamp of last notificationmissedAlarms(array, optional) - Array of missed alarmsalarmId(string) - Unique identifierscheduledTime(number) - When alarm was scheduled to firedetectedAt(number) - When missed alarm was detected
permissions(object) - Current permission statusexactAlarm(boolean, optional) - Android 12+ exact alarm permissionnotifications(boolean, optional) - iOS notification authorizationbootReceiver(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
- "Force Stop will prevent all notifications until the app is manually opened" - See Doc A §2.2.1
- "Exact alarm permission is required on Android 12+ for precise timing" - See Doc A §5.2
iOS Warnings:
- "Notifications have ±180 seconds timing tolerance" - See Doc A §6.1
- "App code does not run when notifications fire (unless user interacts)" - See Doc A §3.2.1
- "Background execution is system-controlled and not guaranteed" - See Doc A §3.1.3
Cross-Platform Warnings:
- "Missed alarms are detected on app launch, not at trigger time" - See Doc C §4.2
- "Repeating alarms must be rescheduled for each occurrence (Android)" - See Doc A §2.1.2
- "Location-based triggers do not persist across reboot (iOS)" - See Doc A §3.1.2
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:
{
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 |
| App code execution on iOS notification fire | iOS | OS limitation - code does not run | Doc A §3.2.1 |
| Exact timing on iOS | iOS | ±180s tolerance is OS limitation | Doc A §6.1 |
| Background execution timing | iOS | System-controlled, not guaranteed | Doc A §3.1.3 |
| Alarm persistence without storage | Both | Must persist in durable storage | Doc A §2.2.3 |
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, Doc A §3.1.1 |
| Notification fires after reboot | ⚠️ Best-effort | ✅ Guaranteed | Android: SHOULD (requires boot receiver), iOS: MUST | Doc A §2.1.2, Doc A §3.1.2 |
| Missed alarm detection | ✅ Guaranteed | ✅ Guaranteed | MUST - Plugin-required | Doc C §4.2 |
| Force stop recovery | ✅ Guaranteed | N/A | MUST - Plugin-required | Doc C §3.1.4 |
| Exact timing | ⚠️ Best-effort | ⚠️ Best-effort | Android: SHOULD (with permission), iOS: SHOULD (±180s) | Doc A §5.2, Doc A §6.1 |
| Background execution | ⚠️ Best-effort | ⚠️ Best-effort | SHOULD - System-controlled | Doc A §3.1.3 |
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 |
| App code execution on iOS notification fire | iOS | OS limitation - code does not run | Doc A §3.2.1 |
| Exact timing on iOS | iOS | ±180s tolerance is OS limitation | Doc A §6.1 |
| Background execution timing guarantees | Both | System-controlled, not guaranteed | Doc A §3.1.3 |
| Alarm persistence without durable storage | Both | Must use database/files | Doc A §2.2.3 |
| Repeating alarms without rescheduling | Android | Must reschedule each occurrence | Doc A §2.1.2 |
| Location-based triggers | iOS | Does not persist across reboot | Doc A §3.1.2 |
| Silent rescheduling without user launch | Android | Force stop blocks all execution | Doc A §2.2.1 |
Documentation Requirement: All unsupported behaviors MUST be documented in JS/TS API with clear warnings. See §5.2 - API Warnings.
8.2 Platform-Specific Limitations
Android:
- Force Stop is a hard kill that cannot be bypassed - See Doc A §2.2.1
- Reboot wipes all alarms; must reschedule from storage - See Doc A §2.1.2
- Exact alarm permission required on Android 12+ - See Doc A §5.2
- Doze mode may defer inexact alarms - See Doc A §2.1.1
iOS:
- Background execution is severely limited - See Doc A §3.1.3
- App code does not run when notifications fire - See Doc A §3.2.1
- Timing has ±180s tolerance - See Doc A §6.1
- BGTaskScheduler execution is not guaranteed - See Doc A §3.1.3
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 | §4.2 - Detection Triggers | Test 4 Step 6 | Phase 1 |
| Boot reschedule | Android §2.1.2 | §3.1.1 - Boot Event | Test 4 Step 7 | Phase 3 |
| Force stop recovery | Android §2.2.1 | §3.1.4 - Force Stop Recovery | Test 5 | Phase 2 |
| Cold start recovery | Android §2.1.4 | §3.1.2 - App Cold Start | Test 4 Step 5 | Phase 1 |
| Swipe from recents | Android §2.1.1, iOS §3.1.1 | §1.1 - Guarantees Matrix | Test 2 | OS-guaranteed |
| iOS notification persistence | iOS §3.1.2 | §1.1 - Guarantees Matrix | Test 3 | OS-guaranteed |
| Persistence | Android §2.2.3 | §2 - Persistence Requirements | Persistence Investigation | Phase 1 |
| Exact alarm permission | Android §5.2 | §10.1.1 - Permissions | Test 6 | Phase 1 |
10. Platform-Specific Requirements
10.1 Android Requirements
10.1.1 Permissions
Required Permissions:
RECEIVE_BOOT_COMPLETED- Boot receiverSCHEDULE_EXACT_ALARM- Android 12+ (API 31+) for exact alarmsPOST_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
10.1.2 Manifest Entries
Required Manifest Entries:
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<receiver
android:name="com.timesafari.dailynotification.BootReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.LOCKED_BOOT_COMPLETED" />
</intent-filter>
</receiver>
<receiver
android:name="com.timesafari.dailynotification.DailyNotificationReceiver"
android:enabled="true"
android:exported="false">
<intent-filter>
<action android:name="com.timesafari.daily.NOTIFICATION" />
</intent-filter>
</receiver>
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 channeldaily_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
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 fetchcom.timesafari.dailynotification.notify- Notification task (if used)
Background Task Registration:
- Register in
Info.plist:<key>BGTaskSchedulerPermittedIdentifiers</key> <array> <string>com.timesafari.dailynotification.fetch</string> </array>
Code Reference: DailyNotificationPlugin.swift line 31-32
Platform Reference: iOS §3.1.3
10.2.3 Notification Scheduling
Required API Usage:
UNUserNotificationCenter.add()- Schedule notificationsUNCalendarNotificationTrigger- Calendar-based triggers (preferred)UNTimeIntervalNotificationTrigger- Time interval triggers
Code Reference: DailyNotificationScheduler.swift line 185
Platform Reference: iOS §3.1.1
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 for test matrices.
12. Migration Rules
12.1 Storage Format Changes
If plugin storage format changes, migration MUST define:
- Version detection: How to identify old vs new format
- Safe upgrade path: Step-by-step migration process
- Failure fallback: What happens if migration fails
- 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:
// 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:
- Doc C update - Requirements document updated first
- MINOR or MAJOR version bump - Semantic versioning enforced
- Migration notes - Clear upgrade path documented
- 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 |
[Doc B §X.Y] |
[Doc B §1.2] |
Exploration §1.2 |
[Doc C §X.Y] |
[Doc C §3.1.2] |
Requirements §3.1.2 |
[Phase N §X.Y] |
[Phase 1 §2.3] |
Phase 1 Implementation |
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 - OS-level facts
- Plugin Behavior Exploration - Exploration template
- Unified Alarm Directive - Master coordination document
- Phase 1: Cold Start Recovery - Implementation
- Phase 2: Force Stop Recovery - Implementation
- Phase 3: Boot Recovery - 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