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

1. **`src/core/metrics.ts`** (NEW FILE)
   ```typescript
   // 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.ts`** — `createSchedule()` method
   - **Location:** Find `async createSchedule(input: CreateScheduleInput)`
   - **Add before method:**
     ```typescript
     const startTime = performance.now();
     ```
   - **Add after success (before return):**
     ```typescript
     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):**
     ```typescript
     const duration = performance.now() - startTime;
     this.observability?.logEvent('ERROR', EVENT_CODES.SCHEDULE_UPDATE, 
       `Schedule creation failed`, 
       { error: error.message, duration });
     ```

2. **`src/web.ts`** — `updateSchedule()` method
   - **Same pattern:** Add timing before, log after (success/error)

3. **`src/web.ts`** — `deleteSchedule()` 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:**
     ```kotlin
     val startTime = System.currentTimeMillis()
     ```
   - **Add before return:**
     ```kotlin
     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:**
     ```swift
     let startTime = Date()
     ```
   - **Add before return:**
     ```swift
     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):
     ```kotlin
     // 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:**
     ```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
       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)
   ```markdown
   # 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
   ```typescript
   // 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:**
     ```typescript
     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
   ```typescript
   /**
    * 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
   ```typescript
   // 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
   ```typescript
   // 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
   ```typescript
   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
   ```typescript
   // 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
   ```typescript
   // 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:**
     ```typescript
     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
   ```typescript
   /**
    * 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
   ```typescript
   // 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
   ```typescript
   /**
    * 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)
   ```markdown
   # 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)
   ```markdown
   # 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)
   ```markdown
   # 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)
   ```markdown
   # 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)

