Files
daily-notification-plugin/doc/progress/P3.1-CURSOR-TASK-BLOCK.md

320 lines
7.1 KiB
Markdown

# 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.