Created detailed, file-by-file, function-by-function execution plan for P3. Includes: - Exact files to modify - Exact functions to instrument - Exact event codes to add - Exact JSDoc patterns - Exact commands to run - Batch-by-batch execution plan Ready for Cursor execution after approval.
23 KiB
P3 Execution Checklist — Mechanical Step-by-Step
Purpose: Exact, file-by-file, function-by-function execution plan for P3 work.
Owner: Development Team
Last Updated: 2025-12-22
Status: execution-ready
Baseline: v1.0.11-p2.3-p1.5b-complete
0) Non-Negotiable Invariants (DO NOT BREAK)
Before every batch:
- Run
./ci/run.shand verify all checks pass - Verify
npm pack --dry-run | grep -E "xcuserdata|xcuserstate|DerivedData|ios/App/"returns empty - Verify no platform imports in
src/core/(grep for@capacitor|react|fs|path|os) - Verify
package.json.exportsmatches build artifacts - Verify new docs are linked in
docs/00-INDEX.mdor placed indocs/_archive/
After every batch:
- Run
./ci/run.sh— STOP IF FAILS - Update progress docs if applicable
- Commit with clear message
P3.1 — Performance Optimization & Metrics
Batch 1: Add Metrics Collection Infrastructure
Files to create/modify:
-
src/core/metrics.ts(NEW FILE)// Exact structure: export interface PerformanceMetric { operation: string; duration: number; timestamp: number; success: boolean; metadata?: Record<string, unknown>; } export interface MetricsCollector { record(metric: PerformanceMetric): void; getMetrics(): PerformanceMetric[]; clear(): void; } // Lightweight in-memory collector (no deps) export class InMemoryMetricsCollector implements MetricsCollector { private metrics: PerformanceMetric[] = []; private maxMetrics = 100; record(metric: PerformanceMetric): void { this.metrics.push(metric); if (this.metrics.length > this.maxMetrics) { this.metrics = this.metrics.slice(-this.maxMetrics); } } getMetrics(): PerformanceMetric[] { return [...this.metrics]; } clear(): void { this.metrics = []; } } -
src/core/index.ts(UPDATE)- Add export:
export * from './metrics';
- Add export:
Verification:
npm run buildsucceeds./ci/run.shpasses- No new dependencies added
Batch 2: Instrument Hot Paths — Scheduling
Files to modify:
-
src/web.ts—createSchedule()method- Location: Find
async createSchedule(input: CreateScheduleInput) - Add before method:
const startTime = performance.now(); - Add after success (before return):
const duration = performance.now() - startTime; this.observability?.logEvent('INFO', EVENT_CODES.SCHEDULE_UPDATE, `Schedule created: ${result.schedule.id}`, { scheduleId: result.schedule.id, duration }); - Add after error (in catch):
const duration = performance.now() - startTime; this.observability?.logEvent('ERROR', EVENT_CODES.SCHEDULE_UPDATE, `Schedule creation failed`, { error: error.message, duration });
- Location: Find
-
src/web.ts—updateSchedule()method- Same pattern: Add timing before, log after (success/error)
-
src/web.ts—deleteSchedule()method- Same pattern: Add timing before, log after (success/error)
Verification:
- TypeScript compiles (
npm run build) ./ci/run.shpasses- No behavior changes (tests still pass)
Batch 3: Instrument Hot Paths — Recovery
Files to modify:
-
Android:
android/src/main/java/com/timesafari/dailynotification/ReactivationManager.kt- Function:
performColdStartRecovery() - Add at start:
val startTime = System.currentTimeMillis() - Add before return:
val duration = System.currentTimeMillis() - startTime Log.i(TAG, "Cold start recovery completed: duration=${duration}ms, missed=$missedCount, rescheduled=$rescheduledCount")
- Function:
-
Android:
android/src/main/java/com/timesafari/dailynotification/ReactivationManager.kt- Function:
performForceStopRecovery() - Same pattern: Add timing, log duration
- Function:
-
iOS:
ios/Plugin/DailyNotificationReactivationManager.swift- Function:
performColdStartRecovery() - Add at start:
let startTime = Date() - Add before return:
let duration = Date().timeIntervalSince(startTime) * 1000 // ms os_log("Cold start recovery completed: duration=%.0fms, missed=%d, rescheduled=%d", log: .default, type: .info, duration, missedCount, rescheduledCount)
- Function:
Verification:
- Android builds (
cd test-apps/android-test-app && ./gradlew :daily-notification-plugin:build) - iOS builds (if macOS available)
./ci/run.shpasses
Batch 4: Instrument Hot Paths — Database Operations
Files to modify:
-
Android:
android/src/main/java/com/timesafari/dailynotification/DatabaseSchema.kt- Find: Room DAO methods (e.g.,
getEnabled(),getById()) - Add timing wrapper (if possible without breaking Room contracts):
// For critical queries, add timing in calling code, not DAO // Document in comments: "Timing measured in ReactivationManager"
- Find: Room DAO methods (e.g.,
-
Android:
android/src/main/java/com/timesafari/dailynotification/ReactivationManager.kt- Function:
runBootRecovery() - Add timing around DB query:
val dbStartTime = System.currentTimeMillis() val enabledSchedules = try { db.scheduleDao().getEnabled() } catch (e: Exception) { Log.e(TAG, "Failed to load schedules from DB", e) emptyList() } finally { val dbDuration = System.currentTimeMillis() - dbStartTime Log.d(TAG, "Database query duration: ${dbDuration}ms, schedules=${enabledSchedules.size}") }
- Function:
Verification:
- Android builds
./ci/run.shpasses- No database contract violations
Batch 5: Document Performance Characteristics
Files to create:
-
docs/PERFORMANCE.md(NEW FILE)# Performance Characteristics ## Expected Operation Times - Schedule creation: < 50ms (typical), < 100ms (p95) - Schedule update: < 50ms (typical), < 100ms (p95) - Cold start recovery: < 500ms (typical), < 1000ms (p95) - Database query (getEnabled): < 50ms (typical), < 100ms (p95) ## Memory Footprint - In-memory metrics: ~10KB per 100 metrics - Event logs: ~5KB per 100 events - Total overhead: < 100KB (development mode) ## Platform-Specific Considerations - iOS: Background task time limits (~30 seconds) - Android: WorkManager execution time limits (flexible) - Web: No background execution limits -
docs/00-INDEX.md(UPDATE)- Add link:
- [PERFORMANCE.md](./PERFORMANCE.md) — Performance characteristics and benchmarks
- Add link:
Verification:
- File created and linked
./ci/run.shpasses
P3.1 Acceptance Checklist
- Metrics collection infrastructure exists (
src/core/metrics.ts) - Hot paths instrumented (scheduling, recovery, DB operations)
- Performance characteristics documented (
docs/PERFORMANCE.md) ./ci/run.shgreen- No new dependencies
- No behavior changes (tests pass)
P3.2 — Enhanced Observability
Batch 1: Expand Event Logging Coverage
Files to modify:
-
src/core/events.ts— Add missing event codes// Add to EVENT_CODES object: RECOVERY_START: 'DNP-RECOVERY-START', RECOVERY_COMPLETE: 'DNP-RECOVERY-COMPLETE', RECOVERY_ERROR: 'DNP-RECOVERY-ERROR', DB_QUERY_START: 'DNP-DB-QUERY-START', DB_QUERY_COMPLETE: 'DNP-DB-QUERY-COMPLETE', DB_QUERY_ERROR: 'DNP-DB-QUERY-ERROR', STATE_TRANSITION: 'DNP-STATE-TRANSITION', BACKGROUND_TASK_START: 'DNP-BG-TASK-START', BACKGROUND_TASK_COMPLETE: 'DNP-BG-TASK-COMPLETE', -
src/observability.ts— Add recovery event logging- Function:
logEvent()(already exists, no changes needed) - Add helper method:
logRecovery(operation: string, result: { success: boolean; missed?: number; rescheduled?: number; errors?: number; duration?: number }): void { const level = result.success ? 'INFO' : 'ERROR'; this.logEvent(level, EVENT_CODES.RECOVERY_COMPLETE, `Recovery ${operation} completed`, { operation, ...result }); }
- Function:
Verification:
- TypeScript compiles
./ci/run.shpasses- Event codes are exported from
src/core/index.ts
Batch 2: Add Structured Metrics Export
Files to modify:
-
src/observability.ts— Add export method/** * Export metrics as JSON * @returns JSON string of all metrics */ exportMetrics(): string { return JSON.stringify({ performance: this.performanceMetrics, user: this.userMetrics, platform: this.platformMetrics, events: this.eventLogs.slice(0, 100), // Last 100 events exportedAt: Date.now() }, null, 2); } /** * Get metrics summary (lightweight) * @returns Summary object */ getMetricsSummary(): { eventCount: number; successRate: number; avgFetchTime: number; avgNotifyTime: number; } { const fetchTimes = this.performanceMetrics.fetchTimes; const notifyTimes = this.performanceMetrics.notifyTimes; const total = this.performanceMetrics.successCount + this.performanceMetrics.failureCount; return { eventCount: this.eventLogs.length, successRate: total > 0 ? this.performanceMetrics.successCount / total : 0, avgFetchTime: fetchTimes.length > 0 ? fetchTimes.reduce((a, b) => a + b, 0) / fetchTimes.length : 0, avgNotifyTime: notifyTimes.length > 0 ? notifyTimes.reduce((a, b) => a + b, 0) / notifyTimes.length : 0 }; } -
src/definitions.ts— Add to plugin interface (if needed)- Check if
DailyNotificationPlugininterface needsexportMetrics()method - If yes, add:
exportMetrics(): Promise<{ metrics: string }>;
- Check if
Verification:
- TypeScript compiles
./ci/run.shpasses- JSON export is valid JSON
Batch 3: Improve Error Context
Files to modify:
-
src/core/errors.ts— Enhance error class// Find DailyNotificationError class // Add method: toJSON(): Record<string, unknown> { return { code: this.code, message: this.message, cause: this.cause ? String(this.cause) : undefined, stack: this.stack, timestamp: Date.now() }; } -
src/observability.ts— Enhance error logging// In logEvent(), if level === 'ERROR' and data contains error: logError(eventCode: string, message: string, error: Error, context?: Record<string, unknown>): void { const errorData: Record<string, unknown> = { error: error.message, errorCode: error instanceof DailyNotificationError ? error.code : undefined, stack: error.stack, ...context }; this.logEvent('ERROR', eventCode, message, errorData); }
Verification:
- TypeScript compiles
./ci/run.shpasses- Error context includes stack traces
Batch 4: Add Diagnostic Mode
Files to modify:
-
src/observability.ts— Add diagnostic flagprivate diagnosticMode = false; /** * Enable diagnostic mode (verbose logging) */ enableDiagnosticMode(): void { this.diagnosticMode = true; this.logEvent('INFO', EVENT_CODES.METRICS_RESET, 'Diagnostic mode enabled'); } /** * Disable diagnostic mode */ disableDiagnosticMode(): void { this.diagnosticMode = false; } /** * Check if diagnostic mode is enabled */ isDiagnosticMode(): boolean { return this.diagnosticMode; } -
src/definitions.ts— Add to plugin interface// Add to DailyNotificationPlugin: enableDiagnosticMode(): Promise<void>; disableDiagnosticMode(): Promise<void>; getDiagnosticInfo(): Promise<{ metrics: string; eventCount: number }>;
Verification:
- TypeScript compiles
./ci/run.shpasses- Diagnostic mode can be toggled
P3.2 Acceptance Checklist
- Event logging coverage expanded (new event codes added)
- Structured metrics export implemented (
exportMetrics()) - Error context improved (stack traces, state snapshots)
- Diagnostic mode added (toggle, info export)
./ci/run.shgreen- No new dependencies
P3.3 — Developer Experience Improvements
Batch 1: Improve Error Messages
Files to modify:
-
src/core/errors.ts— Enhance error messages// For each error code, add actionable guidance: // Example: PERMISSION_DENIED: { code: 'PERMISSION_DENIED', message: 'Notification permission denied', guidance: 'Request permission using requestPermission() before scheduling notifications', platformHints: { ios: 'Check Info.plist for notification permission description', android: 'Check AndroidManifest.xml for POST_NOTIFICATIONS permission' } } -
src/web.ts— Improve error handling- Find:
throwNotSupported()method - Enhance:
private throwNotSupported(): never { throw new DailyNotificationError( ErrorCode.NOT_SUPPORTED, 'This operation is not supported on the web platform', undefined, { guidance: 'Use native iOS or Android implementation for this feature', platform: 'web' } ); }
- Find:
Verification:
- TypeScript compiles
./ci/run.shpasses- Error messages are actionable
Batch 2: Add Development Mode Helpers
Files to modify:
src/web.ts— Add debug helpers/** * Get current plugin state (development only) * @internal */ async getDebugState(): Promise<{ schedules: Schedule[]; configs: Config[]; callbacks: Callback[]; metrics: ReturnType<ObservabilityManager['getMetricsSummary']>; }> { if (process.env.NODE_ENV === 'production') { throw new DailyNotificationError(ErrorCode.NOT_SUPPORTED, 'Debug methods not available in production'); } const schedules = await this.getSchedules(); const configs = await this.getConfigs(); const callbacks = await this.getCallbacks(); const metrics = this.observability?.getMetricsSummary() || { eventCount: 0, successRate: 0, avgFetchTime: 0, avgNotifyTime: 0 }; return { schedules: schedules.schedules, configs: configs.configs, callbacks: callbacks.callbacks, metrics }; }
Verification:
- TypeScript compiles
./ci/run.shpasses- Debug methods only work in development
Batch 3: Enhance TypeScript Types
Files to modify:
-
src/core/contracts.ts— Add discriminated unions where appropriate// Example: Enhance ScheduleWithStatus export type ScheduleWithStatus = Schedule & { status: 'active' | 'paused' | 'error'; nextRunAt: number | null; lastRunAt: number | null; } & ( | { status: 'active'; nextRunAt: number } | { status: 'paused'; nextRunAt: null } | { status: 'error'; nextRunAt: null; error: string } ); -
src/definitions.ts— Add JSDoc improvements/** * Create a new notification schedule * * @param input - Schedule configuration * @param input.id - Unique schedule identifier (required) * @param input.kind - Schedule type: 'notify' for notifications, 'fetch' for content fetching * @param input.cron - Cron expression (e.g., '0 9 * * *' for daily at 9 AM) * @param input.clockTime - Time of day in HH:mm format (alternative to cron) * @param input.enabled - Whether schedule is active (default: true) * @returns Created schedule with status * @throws {DailyNotificationError} If schedule creation fails * * @example * ```typescript * const schedule = await DailyNotification.createSchedule({ * id: 'morning-notification', * kind: 'notify', * clockTime: '09:00', * enabled: true * }); * ``` */ createSchedule(input: CreateScheduleInput): Promise<{ schedule: ScheduleWithStatus }>;
Verification:
- TypeScript compiles
- IntelliSense shows improved types
./ci/run.shpasses
Batch 4: Expand Integration Examples
Files to create:
-
docs/examples/QUICK_START.md(NEW FILE)# Quick Start Guide ## Minimal Working Example \`\`\`typescript import { DailyNotification } from '@timesafari/daily-notification-plugin'; // 1. Request permission const { state } = await DailyNotification.requestPermission(); if (state !== 'granted') { console.error('Permission denied'); return; } // 2. Create schedule const { schedule } = await DailyNotification.createSchedule({ id: 'daily-morning', kind: 'notify', clockTime: '09:00', enabled: true }); // 3. Verify schedule const { schedules } = await DailyNotification.getSchedules(); console.log('Active schedules:', schedules); \`\`\` -
docs/examples/COMMON_PATTERNS.md(NEW FILE)- Add patterns for: scheduling, recovery, error handling, platform-specific
-
docs/00-INDEX.md(UPDATE)- Add section:
## Examples - Link:
- [Quick Start](./examples/QUICK_START.md) - Link:
- [Common Patterns](./examples/COMMON_PATTERNS.md)
- Add section:
Verification:
- Examples are accurate and runnable
./ci/run.shpasses- Examples linked in index
P3.3 Acceptance Checklist
- Error messages improved (actionable, context-rich)
- Development mode helpers added (
getDebugState()) - TypeScript types enhanced (discriminated unions, JSDoc)
- Integration examples expanded (quick-start, patterns)
./ci/run.shgreen- No breaking changes
P3.4 — Documentation Polish
Batch 1: Complete API Documentation (JSDoc)
Files to modify (exact list):
-
src/definitions.ts— All public methodscreateSchedule()— Add JSDoc (see P3.3 Batch 3 example)updateSchedule()— Add JSDocdeleteSchedule()— Add JSDocgetSchedules()— Add JSDoccreateConfig()— Add JSDocupdateConfig()— Add JSDocdeleteConfig()— Add JSDocgetConfigs()— Add JSDoccreateCallback()— Add JSDocupdateCallback()— Add JSDocdeleteCallback()— Add JSDocgetCallbacks()— Add JSDocrequestPermission()— Add JSDoccheckPermission()— Add JSDoc
-
src/core/contracts.ts— All interfaces- Add JSDoc to:
Schedule,Config,Callback,History,ContentCache
- Add JSDoc to:
-
src/core/errors.ts— Error codes- Add JSDoc to
ErrorCodeenum values - Add JSDoc to
DailyNotificationErrorclass
- Add JSDoc to
Verification:
- All public APIs have JSDoc
- JSDoc includes: params, returns, throws, examples
npm run buildgenerates.d.tsfiles with JSDoc
Batch 2: Add Troubleshooting Guides
Files to create:
-
docs/TROUBLESHOOTING.md(NEW FILE)# Troubleshooting Guide ## Common Issues ### CI Failures **Problem:** `./ci/run.sh` fails **Solutions:** 1. Check forbidden files: `npm pack --dry-run | grep -E "xcuserdata|xcuserstate|DerivedData|ios/App/"` 2. Check core purity: `grep -r "@capacitor\|react\|fs\|path" src/core/` 3. Check exports: `node -e "const p=require('./package.json'); console.log(p.exports)"` ### Packaging Failures **Problem:** `npm pack` includes forbidden files **Solution:** Update `package.json.files` whitelist ### Platform Test Failures **Problem:** Android/iOS tests fail **Solutions:** - Android: Run from test-app: `cd test-apps/android-test-app && ./gradlew :daily-notification-plugin:test` - iOS: Requires macOS + Xcode -
docs/00-INDEX.md(UPDATE)- Add link:
- [TROUBLESHOOTING.md](./TROUBLESHOOTING.md) — Common issues and solutions
- Add link:
Verification:
- Troubleshooting guide is comprehensive
./ci/run.shpasses- Guide linked in index
Batch 3: Improve Onboarding Documentation
Files to create/modify:
-
docs/GETTING_STARTED.md(NEW FILE or UPDATE existing)# Getting Started ## Step 1: Installation \`\`\`bash npm install @timesafari/daily-notification-plugin \`\`\` ## Step 2: Platform Setup - iOS: Add to `Info.plist` (see integration guide) - Android: Add to `AndroidManifest.xml` (see integration guide) ## Step 3: Basic Usage See [Quick Start](./examples/QUICK_START.md) ## Architecture Overview See [ARCHITECTURE.md](./ARCHITECTURE.md) ## Key Concepts - **Scheduling**: Recurring notification patterns - **Recovery**: Automatic rescheduling after app restart - **Persistence**: State survives app/OS restarts -
docs/00-INDEX.md(UPDATE)- Add link:
- [GETTING_STARTED.md](./GETTING_STARTED.md) — Step-by-step onboarding
- Add link:
Verification:
- Getting started guide is clear
./ci/run.shpasses- Guide linked in index
Batch 4: Add Migration Guides (if needed)
Files to create (only if breaking changes exist):
docs/MIGRATION.md(NEW FILE, only if needed)# Migration Guide ## Version Upgrades ### v1.0.11 → v1.0.12 No breaking changes. ### v1.0.10 → v1.0.11 - Core module introduced: Use `@timesafari/daily-notification-plugin/core` for core types
Verification:
- Migration guide only created if needed
./ci/run.shpasses- Guide linked in index (if created)
P3.4 Acceptance Checklist
- API documentation complete (all public APIs have JSDoc)
- Troubleshooting guides added (
docs/TROUBLESHOOTING.md) - Onboarding documentation improved (
docs/GETTING_STARTED.md) - Migration guides added (if needed)
- Documentation index updated (
docs/00-INDEX.md) ./ci/run.shgreen
P3 Close-out Checklist
When all P3 items are complete:
- Parity matrix updated (if any parity-related items added)
- Progress docs updated:
docs/progress/00-STATUS.md— Mark P3 completedocs/progress/01-CHANGELOG-WORK.md— Add P3 completion entrydocs/progress/03-TEST-RUNS.md— Add performance test results (if applicable)
./ci/run.shgreen- Create baseline tag:
v1.0.11-p3-complete - Push tag:
git push --tags
Execution Notes
Batch Discipline:
- Complete one batch at a time
- Run
./ci/run.shafter each batch - Commit after each batch (if desired) or after completing a full P3.x item
No New Dependencies:
- Use built-in APIs only (
performance.now(),Date.now(), etc.) - No external metrics libraries
- No external logging libraries
Testing:
- Existing tests must continue to pass
- No new test infrastructure required (unless explicitly in acceptance criteria)
Documentation:
- All new docs must be linked in
docs/00-INDEX.md - All docs must have drift guards (Purpose, Owner, Last Updated, Status)
Last Updated: 2025-12-22
Status: Execution-ready (awaiting approval to begin)