# P3.1 Cursor Task Block — Performance Optimization & Metrics **Purpose:** Ultra-compressed, mechanical execution steps for P3.1 only. **Baseline:** `v1.0.11-p2.3-p1.5b-complete` **Invariants:** See `docs/progress/P3-EXECUTION-CHECKLIST-MECHANICAL.md` section 0 --- ## Preflight (Before Each Batch) ```bash ./ci/run.sh # STOP IF FAILS ``` --- ## Batch P3.1-A: Metrics Contract **File:** `src/core/metrics.ts` (NEW) **Action:** Create file with exact content: ```typescript /** * Core Metrics * * Performance metrics contract and lightweight collector. * Platform-agnostic, no dependencies. * * @author Matthew Raymer * @version 1.0.0 */ export interface PerformanceMetric { operation: string; duration: number; timestamp: number; success: boolean; metadata?: Record; } export interface MetricsCollector { record(metric: PerformanceMetric): void; getMetrics(): PerformanceMetric[]; clear(): void; } 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 = []; } } ``` **File:** `src/core/index.ts` (UPDATE) **Search:** `export * from './guards';` **Action:** Add after: `export * from './metrics';` **Verify:** ```bash npm run build ./ci/run.sh grep -r "@capacitor\|react\|fs\|path\|os" src/core/metrics.ts # Must be empty ``` --- ## Batch P3.1-B: Instrument Scheduling **File:** `src/web.ts` (UPDATE) **Search:** `async createSchedule(_schedule: CreateScheduleInput): Promise {` **Current:** ```typescript async createSchedule(_schedule: CreateScheduleInput): Promise { this.throwNotSupported(); } ``` **Replace with:** ```typescript async createSchedule(_schedule: CreateScheduleInput): Promise { const startTime = performance.now(); try { this.throwNotSupported(); } catch (error) { const duration = performance.now() - startTime; if (this.observability) { this.observability.logEvent('INFO', EVENT_CODES.SCHEDULE_UPDATE, 'Schedule creation attempted (not supported on web)', { duration, platform: 'web' }); } throw error; } } ``` **Import check:** Ensure `EVENT_CODES` imported: ```typescript import { EVENT_CODES } from './core/events'; ``` **Repeat for:** - `updateSchedule()` (line ~338) - `deleteSchedule()` (line ~342) **Verify:** ```bash npm run build ./ci/run.sh ``` --- ## Batch P3.1-C: Instrument Recovery **File:** `android/src/main/java/com/timesafari/dailynotification/ReactivationManager.kt` (UPDATE) **Search:** `private suspend fun performColdStartRecovery(): RecoveryResult {` **Add at start:** ```kotlin val startTime = System.currentTimeMillis() ``` **Find return statement, add before:** ```kotlin val duration = System.currentTimeMillis() - startTime Log.i(TAG, "Cold start recovery completed: duration=${duration}ms, missed=$missedCount, rescheduled=$rescheduledCount, errors=$errors") ``` **Repeat for:** `performForceStopRecovery()` **File:** `ios/Plugin/DailyNotificationReactivationManager.swift` (UPDATE) **Search:** `func performColdStartRecovery() async throws -> RecoveryResult {` **Add at start:** ```swift let startTime = Date() ``` **Find return, add before:** ```swift let duration = Date().timeIntervalSince(startTime) * 1000 os_log("Cold start recovery completed: duration=%.0fms, missed=%d, rescheduled=%d", log: .default, type: .info, duration, missedCount, rescheduledCount) ``` **Verify:** ```bash cd test-apps/android-test-app && ./gradlew :daily-notification-plugin:build ./ci/run.sh ``` --- ## Batch P3.1-D: Instrument Database **File:** `android/src/main/java/com/timesafari/dailynotification/ReactivationManager.kt` (UPDATE) **Search:** `val enabledSchedules = try { db.scheduleDao().getEnabled() }` **Replace with:** ```kotlin 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 if (dbDuration > 100) { Log.w(TAG, "Database query slow: ${dbDuration}ms for getEnabled()") } else { Log.d(TAG, "Database query: ${dbDuration}ms, schedules=${enabledSchedules.size}") } } ``` **Verify:** ```bash cd test-apps/android-test-app && ./gradlew :daily-notification-plugin:build ./ci/run.sh ``` --- ## Batch P3.1-E: Performance Documentation **File:** `docs/PERFORMANCE.md` (NEW) **Action:** Create with exact content: ```markdown # Performance Characteristics **Purpose:** Expected performance characteristics and benchmarks. **Owner:** Development Team **Last Updated:** 2025-12-22 **Status:** active ## Expected Operation Times ### Scheduling Operations - Schedule creation: < 50ms (typical), < 100ms (p95) - Schedule update: < 50ms (typical), < 100ms (p95) - Schedule deletion: < 50ms (typical), < 100ms (p95) ### Recovery Operations - Cold start recovery: < 500ms (typical), < 1000ms (p95) - Force stop recovery: < 500ms (typical), < 1000ms (p95) - Boot recovery: < 1000ms (typical), < 2000ms (p95) ### Database Operations - Query (getEnabled): < 50ms (typical), < 100ms (p95) - Query (getById): < 10ms (typical), < 20ms (p95) - Insert/Update: < 50ms (typical), < 100ms (p95) ## Memory Footprint - In-memory metrics: ~10KB per 100 metrics - Event logs: ~5KB per 100 events - Total overhead: < 100KB (development), < 10KB (production) ## Platform-Specific Considerations ### iOS - Background task limits: ~30 seconds - CoreData migrations: typically < 100ms ### Android - WorkManager limits: flexible (minutes) - Room migrations: typically < 200ms ### Web - No background execution limits - No native database operations ## Measurement Methodology Metrics use: - `performance.now()` (Web/TypeScript) - `System.currentTimeMillis()` (Android) - `Date.timeIntervalSince()` (iOS) All timings in milliseconds. --- **See also:** - [SYSTEM_INVARIANTS.md](./SYSTEM_INVARIANTS.md) - [docs/progress/03-TEST-RUNS.md](./progress/03-TEST-RUNS.md) ``` **File:** `docs/00-INDEX.md` (UPDATE) **Search:** `## Policy & Contracts (Executable)` **Add link:** ```markdown - [PERFORMANCE.md](./PERFORMANCE.md) — Performance characteristics and benchmarks ``` **Verify:** ```bash ./ci/run.sh ``` --- ## P3.1 Acceptance - [ ] `src/core/metrics.ts` exists - [ ] Scheduling methods instrumented - [ ] Recovery methods instrumented - [ ] Database operations instrumented - [ ] `docs/PERFORMANCE.md` created and linked - [ ] `./ci/run.sh` green - [ ] No new dependencies - [ ] Tests pass --- ## Update Progress Docs (After P3.1 Complete) **Files:** - `docs/progress/00-STATUS.md` — Mark P3.1 complete - `docs/progress/01-CHANGELOG-WORK.md` — Add P3.1 entry - `docs/progress/03-TEST-RUNS.md` — Add performance metrics note **Verify:** ```bash ./ci/run.sh ``` --- **Next:** Proceed to P3.2 (Observability) after P3.1 is green.