# 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<string, unknown>;
}

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<Schedule> {`

**Current:**
```typescript
async createSchedule(_schedule: CreateScheduleInput): Promise<Schedule> {
  this.throwNotSupported();
}
```

**Replace with:**
```typescript
async createSchedule(_schedule: CreateScheduleInput): Promise<Schedule> {
  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.

