Files
daily-notification-plugin/docs/progress/P3-EXECUTION-CHECKLIST.md
Matthew Raymer 1591d7ab89 docs: Add P3 mechanical execution checklist
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.
2025-12-23 05:10:16 +00:00

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.sh and 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.exports matches build artifacts
  • Verify new docs are linked in docs/00-INDEX.md or placed in docs/_archive/

After every batch:

  • Run ./ci/run.shSTOP 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:

  1. 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 = [];
      }
    }
    
  2. src/core/index.ts (UPDATE)

    • Add export: export * from './metrics';

Verification:

  • npm run build succeeds
  • ./ci/run.sh passes
  • No new dependencies added

Batch 2: Instrument Hot Paths — Scheduling

Files to modify:

  1. src/web.tscreateSchedule() 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 });
      
  2. src/web.tsupdateSchedule() method

    • Same pattern: Add timing before, log after (success/error)
  3. src/web.tsdeleteSchedule() method

    • Same pattern: Add timing before, log after (success/error)

Verification:

  • TypeScript compiles (npm run build)
  • ./ci/run.sh passes
  • No behavior changes (tests still pass)

Batch 3: Instrument Hot Paths — Recovery

Files to modify:

  1. 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")
      
  2. Android: android/src/main/java/com/timesafari/dailynotification/ReactivationManager.kt

    • Function: performForceStopRecovery()
    • Same pattern: Add timing, log duration
  3. 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)
      

Verification:

  • Android builds (cd test-apps/android-test-app && ./gradlew :daily-notification-plugin:build)
  • iOS builds (if macOS available)
  • ./ci/run.sh passes

Batch 4: Instrument Hot Paths — Database Operations

Files to modify:

  1. 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"
      
  2. 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}")
      }
      

Verification:

  • Android builds
  • ./ci/run.sh passes
  • No database contract violations

Batch 5: Document Performance Characteristics

Files to create:

  1. 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
    
  2. docs/00-INDEX.md (UPDATE)

    • Add link: - [PERFORMANCE.md](./PERFORMANCE.md) — Performance characteristics and benchmarks

Verification:

  • File created and linked
  • ./ci/run.sh passes

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.sh green
  • No new dependencies
  • No behavior changes (tests pass)

P3.2 — Enhanced Observability

Batch 1: Expand Event Logging Coverage

Files to modify:

  1. 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',
    
  2. 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 });
      }
      

Verification:

  • TypeScript compiles
  • ./ci/run.sh passes
  • Event codes are exported from src/core/index.ts

Batch 2: Add Structured Metrics Export

Files to modify:

  1. 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
      };
    }
    
  2. src/definitions.ts — Add to plugin interface (if needed)

    • Check if DailyNotificationPlugin interface needs exportMetrics() method
    • If yes, add: exportMetrics(): Promise<{ metrics: string }>;

Verification:

  • TypeScript compiles
  • ./ci/run.sh passes
  • JSON export is valid JSON

Batch 3: Improve Error Context

Files to modify:

  1. 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()
      };
    }
    
  2. 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.sh passes
  • Error context includes stack traces

Batch 4: Add Diagnostic Mode

Files to modify:

  1. src/observability.ts — Add diagnostic flag

    private 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;
    }
    
  2. 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.sh passes
  • 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.sh green
  • No new dependencies

P3.3 — Developer Experience Improvements

Batch 1: Improve Error Messages

Files to modify:

  1. 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'
      }
    }
    
  2. 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'
          }
        );
      }
      

Verification:

  • TypeScript compiles
  • ./ci/run.sh passes
  • Error messages are actionable

Batch 2: Add Development Mode Helpers

Files to modify:

  1. 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.sh passes
  • Debug methods only work in development

Batch 3: Enhance TypeScript Types

Files to modify:

  1. 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 }
    );
    
  2. 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.sh passes

Batch 4: Expand Integration Examples

Files to create:

  1. 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);
    \`\`\`
    
  2. docs/examples/COMMON_PATTERNS.md (NEW FILE)

    • Add patterns for: scheduling, recovery, error handling, platform-specific
  3. docs/00-INDEX.md (UPDATE)

    • Add section: ## Examples
    • Link: - [Quick Start](./examples/QUICK_START.md)
    • Link: - [Common Patterns](./examples/COMMON_PATTERNS.md)

Verification:

  • Examples are accurate and runnable
  • ./ci/run.sh passes
  • 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.sh green
  • No breaking changes

P3.4 — Documentation Polish

Batch 1: Complete API Documentation (JSDoc)

Files to modify (exact list):

  1. src/definitions.ts — All public methods

    • createSchedule() — Add JSDoc (see P3.3 Batch 3 example)
    • updateSchedule() — Add JSDoc
    • deleteSchedule() — Add JSDoc
    • getSchedules() — Add JSDoc
    • createConfig() — Add JSDoc
    • updateConfig() — Add JSDoc
    • deleteConfig() — Add JSDoc
    • getConfigs() — Add JSDoc
    • createCallback() — Add JSDoc
    • updateCallback() — Add JSDoc
    • deleteCallback() — Add JSDoc
    • getCallbacks() — Add JSDoc
    • requestPermission() — Add JSDoc
    • checkPermission() — Add JSDoc
  2. src/core/contracts.ts — All interfaces

    • Add JSDoc to: Schedule, Config, Callback, History, ContentCache
  3. src/core/errors.ts — Error codes

    • Add JSDoc to ErrorCode enum values
    • Add JSDoc to DailyNotificationError class

Verification:

  • All public APIs have JSDoc
  • JSDoc includes: params, returns, throws, examples
  • npm run build generates .d.ts files with JSDoc

Batch 2: Add Troubleshooting Guides

Files to create:

  1. 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
    
  2. docs/00-INDEX.md (UPDATE)

    • Add link: - [TROUBLESHOOTING.md](./TROUBLESHOOTING.md) — Common issues and solutions

Verification:

  • Troubleshooting guide is comprehensive
  • ./ci/run.sh passes
  • Guide linked in index

Batch 3: Improve Onboarding Documentation

Files to create/modify:

  1. 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
    
  2. docs/00-INDEX.md (UPDATE)

    • Add link: - [GETTING_STARTED.md](./GETTING_STARTED.md) — Step-by-step onboarding

Verification:

  • Getting started guide is clear
  • ./ci/run.sh passes
  • Guide linked in index

Batch 4: Add Migration Guides (if needed)

Files to create (only if breaking changes exist):

  1. 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.sh passes
  • 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.sh green

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 complete
    • docs/progress/01-CHANGELOG-WORK.md — Add P3 completion entry
    • docs/progress/03-TEST-RUNS.md — Add performance test results (if applicable)
  • ./ci/run.sh green
  • 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.sh after 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)