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.
This commit is contained in:
835
docs/progress/P3-EXECUTION-CHECKLIST.md
Normal file
835
docs/progress/P3-EXECUTION-CHECKLIST.md
Normal file
@@ -0,0 +1,835 @@
|
|||||||
|
# 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)
|
||||||
|
|
||||||
Reference in New Issue
Block a user