Created ultra-mechanical checklist with: - Exact file paths and line numbers - Exact search strings to find locations - Exact code snippets with insertion points - Before/after examples - Import checks - Verification steps Ready for Cursor execution with minimal ambiguity.
30 KiB
P3 EXECUTION CHECKLIST — Mechanical (Cursor-Ready)
Purpose: Ultra-mechanical, file-by-file, function-by-function execution plan with exact search strings and insertion points.
Owner: Development Team
Last Updated: 2025-12-22
Status: execution-ready
Baseline: v1.0.11-p2.3-p1.5b-complete
0) P3 Invariants to Preserve (DO NOT VIOLATE)
Hard / enforced (policy-as-code):
- CI authority: All gates must run
./ci/run.sh(never bypass withnpm run buildin gates) - Packaging invariants:
npm pack --dry-runmust not include forbidden files (enforced inscripts/verify.sh) - Export correctness:
package.json.exportsmust match build artifacts (enforced inscripts/verify.sh) - Core purity:
src/core/must not import platform deps or Node builtins (enforced inscripts/verify.sh)
Process / repo discipline:
- Docs structure: Index-first rule; progress docs are authoritative; archive goes under
docs/_archive/ - Baseline integrity: Keep baseline tags valid; don't "fix forward" by weakening checks
P3 scope constraints:
- No new features, no architectural changes, no breaking API changes, no new platforms, no new dependencies
P3 Preflight (MUST DO BEFORE EACH BATCH)
- Run
./ci/run.sh— STOP IF FAILS - If changes touch docs: ensure drift-guard headers remain intact (Purpose/Owner/Last Updated/Status/Baseline Tag)
- Update progress docs only after batch is green:
docs/progress/00-STATUS.mddocs/progress/01-CHANGELOG-WORK.mddocs/progress/03-TEST-RUNS.mddocs/progress/04-PARITY-MATRIX.md(only if parity items changed)
P3.1 — Performance Optimization & Metrics
Batch P3.1-A — Define Metrics Contract (Core-Only, Zero Behavior Change)
File: src/core/metrics.ts (NEW FILE)
Action: Create new file with exact content:
/**
* Core Metrics
*
* Performance metrics contract and lightweight collector.
* Platform-agnostic, no dependencies.
*
* @author Matthew Raymer
* @version 1.0.0
*/
/**
* Performance metric entry
*/
export interface PerformanceMetric {
/** Operation name (e.g., 'schedule.create', 'recovery.coldStart') */
operation: string;
/** Duration in milliseconds */
duration: number;
/** Timestamp when metric was recorded (milliseconds since epoch) */
timestamp: number;
/** Whether operation succeeded */
success: boolean;
/** Optional metadata */
metadata?: Record<string, unknown>;
}
/**
* Metrics collector interface
*/
export interface MetricsCollector {
record(metric: PerformanceMetric): void;
getMetrics(): PerformanceMetric[];
clear(): void;
}
/**
* Lightweight in-memory metrics collector
* No dependencies, platform-agnostic
*/
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 for: export * from './guards'; (should be near end of file)
Action: Add after existing exports:
export * from './metrics';
Verification:
npm run buildsucceeds./ci/run.shpasses- No new dependencies in
package.json grep -r "@capacitor\|react\|fs\|path\|os" src/core/metrics.tsreturns empty
Batch P3.1-B — Instrument Critical Path: Scheduling + Delivery
File: src/web.ts (UPDATE)
Note: src/web.ts is a stub implementation (all methods call throwNotSupported()). Instrumentation here is for future-proofing. Real instrumentation happens in platform implementations.
Search for: async createSchedule(_schedule: CreateScheduleInput): Promise<Schedule> {
Current code (line ~334):
async createSchedule(_schedule: CreateScheduleInput): Promise<Schedule> {
this.throwNotSupported();
}
Action: Add timing (even though it throws immediately):
async createSchedule(_schedule: CreateScheduleInput): Promise<Schedule> {
const startTime = performance.now();
try {
this.throwNotSupported();
} catch (error) {
const duration = performance.now() - startTime;
// Log timing even for unsupported operations (for consistency)
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 is imported:
- Search for:
import.*EVENT_CODES.*from - If missing: Add
import { EVENT_CODES } from './core/events';near top of file
Repeat pattern for:
updateSchedule()(line ~338)deleteSchedule()(line ~342)
Verification:
- TypeScript compiles (
npm run build) ./ci/run.shpasses- No behavior changes (web still throws errors)
Batch P3.1-C — Instrument Recovery Path (Cold Start + Dedupe + Rollover)
File: android/src/main/java/com/timesafari/dailynotification/ReactivationManager.kt (UPDATE)
Search for: private suspend fun performColdStartRecovery(): RecoveryResult {
Current code (should be around line ~654):
private suspend fun performColdStartRecovery(): RecoveryResult {
val db = DailyNotificationDatabase.getDatabase(context)
val currentTime = System.currentTimeMillis()
Log.i(TAG, "Cold start recovery: checking for missed notifications")
// ... existing code ...
}
Action: Add timing at start and end:
private suspend fun performColdStartRecovery(): RecoveryResult {
val startTime = System.currentTimeMillis() // ADD THIS LINE
val db = DailyNotificationDatabase.getDatabase(context)
val currentTime = System.currentTimeMillis()
Log.i(TAG, "Cold start recovery: checking for missed notifications")
// ... existing code ...
// Before return statement, add:
val duration = System.currentTimeMillis() - startTime
Log.i(TAG, "Cold start recovery completed: duration=${duration}ms, missed=$missedCount, rescheduled=$rescheduledCount, errors=$errors")
return RecoveryResult(missedCount, rescheduledCount, verifiedCount, errors)
}
Search for: private suspend fun performForceStopRecovery(): RecoveryResult {
Action: Same pattern — add startTime at start, log duration before return
File: ios/Plugin/DailyNotificationReactivationManager.swift (UPDATE)
Search for: func performColdStartRecovery() async throws -> RecoveryResult {
Action: Add timing:
func performColdStartRecovery() async throws -> RecoveryResult {
let startTime = Date() // ADD THIS LINE
// ... existing code ...
// Before return, add:
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)
return RecoveryResult(missedCount: missedCount, rescheduledCount: rescheduledCount, errors: errors)
}
Verification:
- Android builds (
cd test-apps/android-test-app && ./gradlew :daily-notification-plugin:build) - iOS builds (if macOS available)
./ci/run.shpasses- Recovery tests still pass
Batch P3.1-D — Instrument Database Operations
File: android/src/main/java/com/timesafari/dailynotification/ReactivationManager.kt (UPDATE)
Search for: val enabledSchedules = try { db.scheduleDao().getEnabled() }
Current code (in runBootRecovery(), around line ~71):
val enabledSchedules = try {
db.scheduleDao().getEnabled()
} catch (e: Exception) {
Log.e(TAG, "Failed to load schedules from DB", e)
emptyList()
}
Action: Add timing wrapper:
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) { // Warn if > 100ms
Log.w(TAG, "Database query slow: ${dbDuration}ms for getEnabled()")
} else {
Log.d(TAG, "Database query: ${dbDuration}ms, schedules=${enabledSchedules.size}")
}
}
Verification:
- Android builds
./ci/run.shpasses- No database contract violations
Batch P3.1-E — Document Benchmarks + Regression Guard
File: docs/PERFORMANCE.md (NEW FILE)
Action: Create with exact content:
# Performance Characteristics
**Purpose:** Expected performance characteristics and benchmarks for Daily Notification Plugin operations.
**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 mode), < 10KB (production, metrics disabled)
## Platform-Specific Considerations
### iOS
- Background task time limits: ~30 seconds
- CoreData auto-migration: typically < 100ms
### Android
- WorkManager execution time limits: flexible (minutes)
- Room migrations: typically < 200ms
### Web
- No background execution limits
- No native database operations
## Measurement Methodology
Metrics are collected using:
- `performance.now()` (Web/TypeScript)
- `System.currentTimeMillis()` (Android)
- `Date.timeIntervalSince()` (iOS)
All timings are in milliseconds.
---
**See also:**
- [SYSTEM_INVARIANTS.md](./SYSTEM_INVARIANTS.md) — Enforced system invariants
- [docs/progress/03-TEST-RUNS.md](./progress/03-TEST-RUNS.md) — Test run history
File: docs/00-INDEX.md (UPDATE)
Search for: ## Policy & Contracts (Executable) section
Action: Add link in appropriate section:
- [PERFORMANCE.md](./PERFORMANCE.md) — Performance characteristics and benchmarks
Verification:
- File created and linked
./ci/run.shpasses- Drift guard headers present
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.shgreen- No new dependencies
- No behavior changes (tests pass)
P3.2 — Enhanced Observability
Batch P3.2-A — Expand Event Coverage
File: src/core/events.ts (UPDATE)
Search for: ELECTRON_NOTIFICATION: 'DNP-ELECTRON-NOTIFICATION', (line ~78)
Action: Add new event codes after existing ones:
// Recovery events
RECOVERY_START: 'DNP-RECOVERY-START',
RECOVERY_COMPLETE: 'DNP-RECOVERY-COMPLETE',
RECOVERY_ERROR: 'DNP-RECOVERY-ERROR',
// Database events
DB_QUERY_START: 'DNP-DB-QUERY-START',
DB_QUERY_COMPLETE: 'DNP-DB-QUERY-COMPLETE',
DB_QUERY_ERROR: 'DNP-DB-QUERY-ERROR',
// State transition events
STATE_TRANSITION: 'DNP-STATE-TRANSITION',
// Background task events
BACKGROUND_TASK_START: 'DNP-BG-TASK-START',
BACKGROUND_TASK_COMPLETE: 'DNP-BG-TASK-COMPLETE',
BACKGROUND_TASK_ERROR: 'DNP-BG-TASK-ERROR',
} as const;
Verification:
- TypeScript compiles
./ci/run.shpasses- Event codes exported from
src/core/index.ts(should already be viaexport * from './events')
Batch P3.2-B — Structured Metrics Export
File: src/observability.ts (UPDATE)
Search for: getHealthStatus(): Promise<HealthStatus> { (around line ~166)
Action: Add new methods after getHealthStatus():
/**
* 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(),
schemaVersion: 1
}, 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
};
}
Verification:
- TypeScript compiles
./ci/run.shpasses- JSON export is valid JSON (test with
JSON.parse())
Batch P3.2-C — Improve Error Context
File: src/core/errors.ts (UPDATE)
Search for: class DailyNotificationError extends Error {
Action: Add toJSON() method to class:
/**
* Convert error to JSON for structured logging
* @returns JSON-serializable error representation
*/
toJSON(): Record<string, unknown> {
return {
code: this.code,
message: this.message,
cause: this.cause ? String(this.cause) : undefined,
stack: this.stack,
timestamp: Date.now()
};
}
File: src/observability.ts (UPDATE)
Search for: logEvent( (around line ~96)
Action: Add helper method after logEvent():
/**
* Log error with enhanced context
* @param eventCode Event code
* @param message Human-readable message
* @param error Error object
* @param context Additional context
*/
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);
}
Import check: Ensure DailyNotificationError is imported:
- Search for:
import.*DailyNotificationError.*from - If missing: Add
import { DailyNotificationError } from './core/errors';
Verification:
- TypeScript compiles
./ci/run.shpasses- Error context includes stack traces
Batch P3.2-D — Diagnostic Mode (Opt-In)
File: src/observability.ts (UPDATE)
Search for: private maxLogs = 1000; (around line ~90)
Action: Add diagnostic mode flag after maxMetrics:
private maxLogs = 1000;
private maxMetrics = 100;
private diagnosticMode = false; // ADD THIS LINE
Action: Add methods after generateEventId():
/**
* 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;
}
/**
* Get diagnostic information
* @returns Diagnostic info object
*/
getDiagnosticInfo(): { metrics: string; eventCount: number; diagnosticMode: boolean } {
return {
metrics: this.exportMetrics(),
eventCount: this.eventLogs.length,
diagnosticMode: this.diagnosticMode
};
}
Verification:
- TypeScript compiles
./ci/run.shpasses- 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,
toJSON()) - Diagnostic mode added (toggle, info export)
./ci/run.shgreen- No new dependencies
P3.3 — Developer Experience Improvements
Batch P3.3-A — Error Messages Upgrade
File: src/core/errors.ts (UPDATE)
Search for: export enum ErrorCode {
Action: Enhance error messages with actionable guidance. For each error code, add guidance:
Example pattern:
export enum ErrorCode {
PERMISSION_DENIED = 'PERMISSION_DENIED',
// ... existing codes ...
}
/**
* Error code metadata with actionable guidance
*/
export const ERROR_GUIDANCE: Record<ErrorCode, { message: string; guidance: string; platformHints?: Record<string, string> }> = {
[ErrorCode.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'
}
},
// ... add for other error codes ...
};
File: src/web.ts (UPDATE)
Search for: private throwNotSupported(): never {
Current code (around line ~38):
private throwNotSupported(): never {
throw new Error(DailyNotificationWeb.WEB_NOT_SUPPORTED_ERROR);
}
Action: Enhance error:
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'
}
);
}
Import check: Ensure DailyNotificationError and ErrorCode are imported
Verification:
- TypeScript compiles
./ci/run.shpasses- Error messages are actionable
Batch P3.3-B — Debug Helpers (Read-Only)
File: src/web.ts (UPDATE)
Search for: async deleteSchedule(_id: string): Promise<void> { (around line ~342)
Action: Add debug method after deleteSchedule():
/**
* 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'
);
}
// Web implementation returns empty (not supported)
return {
schedules: [],
configs: [],
callbacks: [],
metrics: this.observability?.getMetricsSummary() || {
eventCount: 0,
successRate: 0,
avgFetchTime: 0,
avgNotifyTime: 0
}
};
}
Verification:
- TypeScript compiles
./ci/run.shpasses- Debug methods only work in development
Batch P3.3-C — Type Tightening
File: src/core/contracts.ts (UPDATE)
Search for: export interface ScheduleWithStatus extends Schedule {
Current code (around line ~51):
export interface ScheduleWithStatus extends Schedule {
isActuallyScheduled: boolean;
}
Action: Enhance with discriminated union (optional, if it improves type safety):
export interface ScheduleWithStatus extends Schedule {
isActuallyScheduled: boolean;
status: 'active' | 'paused' | 'error';
}
// Optional: Add discriminated union type
export type ScheduleWithStatusUnion = ScheduleWithStatus & (
| { status: 'active'; isActuallyScheduled: true }
| { status: 'paused'; isActuallyScheduled: false }
| { status: 'error'; isActuallyScheduled: false; error?: string }
);
Note: Only add discriminated union if it doesn't break existing code
Verification:
- TypeScript compiles
- IntelliSense shows improved types
./ci/run.shpasses
Batch P3.3-D — Integration Examples
File: docs/examples/QUICK_START.md (NEW FILE)
Action: Create with exact content:
# Quick Start Guide
**Purpose:** Minimal working example for Daily Notification Plugin.
**Owner:** Development Team
**Last Updated:** 2025-12-22
**Status:** active
---
## 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);
\`\`\`
## Platform Setup
### iOS
Add to `Info.plist`:
\`\`\`xml
<key>BGTaskSchedulerPermittedIdentifiers</key>
<array>
<string>com.timesafari.dailynotification.fetch</string>
</array>
\`\`\`
### Android
Add to `AndroidManifest.xml`:
\`\`\`xml
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
\`\`\`
---
**See also:**
- [Common Patterns](./COMMON_PATTERNS.md) — Common integration patterns
- [Integration Guide](../integration/INTEGRATION_GUIDE.md) — Full integration guide
File: docs/examples/COMMON_PATTERNS.md (NEW FILE)
Action: Create with common patterns (scheduling, recovery, error handling)
File: docs/00-INDEX.md (UPDATE)
Search for: ## Archive & Reference-only section
Action: Add before archive section:
## Examples
- [Quick Start](./examples/QUICK_START.md) — Minimal working example
- [Common Patterns](./examples/COMMON_PATTERNS.md) — Common integration patterns
Verification:
- Examples are accurate and runnable
./ci/run.shpasses- 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.shgreen- No breaking changes
P3.4 — Documentation Polish
Batch P3.4-A — Public API JSDoc Completeness
File: src/definitions.ts (UPDATE)
Search for: createSchedule(schedule: CreateScheduleInput): Promise<Schedule>;
Current JSDoc (around line ~610):
/**
* Create a new recurring schedule
*
* @param schedule Schedule configuration
* @returns Promise resolving to created Schedule object
*
* @example
* ```typescript
* const schedule = await DailyNotification.createSchedule({
* kind: 'notify',
* cron: '0 9 * * *', // Daily at 9 AM
* enabled: true
* });
* ```
*/
createSchedule(schedule: CreateScheduleInput): Promise<Schedule>;
Action: Enhance with complete JSDoc:
/**
* Create a new recurring schedule
*
* @param schedule Schedule configuration
* @param schedule.id - Unique schedule identifier (required, must be unique)
* @param schedule.kind - Schedule type: 'notify' for notifications, 'fetch' for content fetching
* @param schedule.cron - Cron expression (e.g., '0 9 * * *' for daily at 9 AM). Mutually exclusive with clockTime
* @param schedule.clockTime - Time of day in HH:mm format (e.g., '09:00'). Mutually exclusive with cron
* @param schedule.enabled - Whether schedule is active (default: true)
* @param schedule.jitterMs - Random jitter in milliseconds (default: 0)
* @param schedule.backoffPolicy - Backoff policy for retries (default: 'exp')
* @returns Promise resolving to created Schedule object
* @throws {DailyNotificationError} If schedule creation fails (e.g., invalid cron, duplicate ID, permission denied)
*
* @example
* ```typescript
* const schedule = await DailyNotification.createSchedule({
* id: 'morning-notification',
* kind: 'notify',
* clockTime: '09:00',
* enabled: true
* });
* ```
*
* @example
* ```typescript
* // Using cron expression
* const schedule = await DailyNotification.createSchedule({
* id: 'daily-fetch',
* kind: 'fetch',
* cron: '0 */6 * * *', // Every 6 hours
* enabled: true
* });
* ```
*/
createSchedule(schedule: CreateScheduleInput): Promise<Schedule>;
Repeat for all public methods:
updateSchedule()deleteSchedule()getSchedules()getSchedulesWithStatus()createConfig()updateConfig()deleteConfig()getConfigs()createCallback()updateCallback()deleteCallback()getCallbacks()requestPermission()checkPermission()
Verification:
- All public APIs have complete JSDoc
- JSDoc includes: params, returns, throws, examples
npm run buildgenerates.d.tsfiles with JSDoc
Batch P3.4-B — Troubleshooting Guide
File: docs/TROUBLESHOOTING.md (NEW FILE)
Action: Create with exact content:
# Troubleshooting Guide
**Purpose:** Common issues, symptoms, causes, and solutions.
**Owner:** Development Team
**Last Updated:** 2025-12-22
**Status:** active
---
## CI Failures
### Symptom: `./ci/run.sh` fails
**Causes:**
- Forbidden files in package
- Core module imports platform deps
- Export paths don't match artifacts
**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\|os" src/core/`
3. Check exports: `node -e "const p=require('./package.json'); console.log(JSON.stringify(p.exports, null, 2))"`
---
## Packaging Failures
### Symptom: `npm pack` includes forbidden files
**Solution:** Update `package.json.files` whitelist
---
## Platform Test Failures
### Symptom: 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
---
## DST Issues
### Symptom: Notifications fire at wrong time during DST transitions
**Solution:** Plugin handles DST automatically. If issues persist, check timezone configuration.
---
## Duplicate Deliveries
### Symptom: Same notification delivered multiple times
**Solution:** Plugin includes deduplication. Check recovery logs for duplicate detection.
---
## Cold Start Recovery
### Symptom: Notifications not restored after app restart
**Solution:** Check recovery logs. Ensure schedules are persisted to database.
---
**See also:**
- [SYSTEM_INVARIANTS.md](./SYSTEM_INVARIANTS.md) — Enforced invariants
- [PERFORMANCE.md](./PERFORMANCE.md) — Performance characteristics
File: docs/00-INDEX.md (UPDATE)
Action: Add link in appropriate section
Verification:
- Troubleshooting guide is comprehensive
./ci/run.shpasses- Guide linked in index
Batch P3.4-C — Onboarding/Architecture Doc Pass
File: docs/GETTING_STARTED.md (NEW FILE or UPDATE existing)
Action: Create/update with:
- Step-by-step installation
- Platform setup
- Basic usage
- Links to authoritative docs (
docs/00-INDEX.md,docs/SYSTEM_INVARIANTS.md,./ci/run.sh)
File: docs/00-INDEX.md (UPDATE)
Action: Add link
Verification:
- Getting started guide is clear
./ci/run.shpasses- Guide linked in index
Batch P3.4-D — Migration Guide (Only If Needed)
Note: Only create if breaking changes exist. Current baseline has no breaking changes.
Verification:
- Migration guide only created if needed
./ci/run.shpasses
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.shgreen
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 completedocs/progress/01-CHANGELOG-WORK.md— Add P3 completion entrydocs/progress/03-TEST-RUNS.md— Add performance test results (if applicable)
./ci/run.shgreen- 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.shafter 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)