Add comprehensive production readiness checklist and improve TODO scanning. Changes: - Production Readiness Runbook (docs/progress/PRODUCTION-READINESS-RUNBOOK.md) - Complete mechanical execution checklist for TypeScript, Android, iOS - File anchors and search commands for verification - Cross-platform behavior consistency checks - Logging and observability requirements - Release packaging sanity checks - Troubleshooting guide - Quick reference for expected file anchors - Enhanced TODO Scan Script (scripts/todo-scan.js) - Split reporting: core code vs docs/test-apps - Core code count (should be 0) - Docs/test-apps count (expected to be large) - Enhanced JSON output with summary statistics - Improved console output with visual indicators - Clear separation of production code vs planning artifacts Implementation Details: - Core code detection: ios/Plugin/, android/src/main/, src/, packages/, lib/ - Docs/test-apps detection: docs/, test-apps/, tests/, *Tests/ - JSON output includes summary with coreCount, docsTestCount, otherCount - Markdown output includes summary section with split counts - Console output shows visual indicators (✅/⚠️) for quick assessment Benefits: - Clear visibility into production code TODOs (should be 0) - Acceptable TODOs in docs/test-apps are clearly separated - Production readiness checklist provides deterministic verification - File anchors enable quick verification of implementation completeness Verification: - TODO scan runs successfully - JSON output includes summary statistics - Markdown output includes split summary - Console output shows visual indicators
477 lines
14 KiB
Markdown
477 lines
14 KiB
Markdown
# DNP — Production Readiness Execution Checklist (Mechanical)
|
|
|
|
**Date:** 2025-12-24
|
|
**Repo:** `daily-notification-plugin/`
|
|
**Goal:** Prove the plugin is "shippable" by running a deterministic sequence of checks across **TypeScript**, **Android**, **iOS**, and **Docs/Drift**.
|
|
|
|
---
|
|
|
|
## 0) One-time setup
|
|
|
|
### 0.1 Confirm you're at repo root
|
|
```bash
|
|
pwd
|
|
ls
|
|
```
|
|
|
|
**✅ Expected to include folders like:**
|
|
- `src/`
|
|
- `android/`
|
|
- `ios/`
|
|
- `docs/`
|
|
- `scripts/`
|
|
|
|
### 0.2 Capture current revision (for receipts)
|
|
```bash
|
|
git rev-parse HEAD
|
|
git status --porcelain
|
|
```
|
|
|
|
**Record output into a log note** (or paste into your progress doc).
|
|
|
|
---
|
|
|
|
## 1) Repo-wide "truth checks" (fast)
|
|
|
|
### 1.1 Core code must have **zero TODO markers**
|
|
```bash
|
|
grep -RIn --exclude-dir=docs --exclude-dir=test-apps --exclude-dir=node_modules --exclude-dir=.git "TODO:" ios android src packages lib scripts tests || true
|
|
```
|
|
|
|
**✅ Pass condition:**
|
|
- No matches.
|
|
|
|
**If any show up:** treat as "production code TODO" and resolve.
|
|
|
|
### 1.2 Docs/test-apps TODOs are allowed, but must be measurable
|
|
```bash
|
|
npm run todo:scan
|
|
```
|
|
|
|
**✅ Pass condition:**
|
|
- `docs/TODO-CLASSIFICATION.md` and `docs/todo-scan.json` get updated successfully.
|
|
|
|
**Verify split reporting:**
|
|
- Check that `docs/todo-scan.json` includes `coreCount` and `docsCount` fields.
|
|
|
|
---
|
|
|
|
## 2) TypeScript layer: contract + build sanity
|
|
|
|
### 2.1 Unit tests
|
|
```bash
|
|
npm test
|
|
```
|
|
|
|
**✅ Pass condition:**
|
|
- exits 0.
|
|
|
|
**Expected output:**
|
|
```
|
|
Test Suites: 8 passed, 8 total
|
|
Tests: 115 passed, 115 total
|
|
```
|
|
|
|
### 2.2 Typecheck
|
|
```bash
|
|
npm run typecheck
|
|
```
|
|
|
|
**✅ Pass condition:**
|
|
- exits 0.
|
|
|
|
**Expected output:**
|
|
```
|
|
> @timesafari/daily-notification-plugin@1.0.11 typecheck
|
|
> tsc --noEmit
|
|
```
|
|
|
|
### 2.3 Lint (if present)
|
|
```bash
|
|
npm run lint
|
|
```
|
|
|
|
**✅ Pass condition:**
|
|
- exits 0 OR the project explicitly does not have a lint script.
|
|
|
|
---
|
|
|
|
## 3) Android: build + worker behavior
|
|
|
|
### 3.1 Compile sanity
|
|
```bash
|
|
cd android
|
|
./gradlew :assembleDebug
|
|
cd ..
|
|
```
|
|
|
|
**✅ Pass condition:**
|
|
- build succeeds.
|
|
|
|
**Expected output:**
|
|
```
|
|
BUILD SUCCESSFUL in Xs
|
|
```
|
|
|
|
### 3.2 Locate the fetch worker entrypoint
|
|
**File:**
|
|
- `android/src/main/java/com/timesafari/dailynotification/DailyNotificationFetchWorker.java`
|
|
|
|
**Search anchors:**
|
|
```bash
|
|
grep -n "class DailyNotificationFetchWorker" android/src/main/java/com/timesafari/dailynotification/DailyNotificationFetchWorker.java
|
|
grep -n "public Result doWork()" android/src/main/java/com/timesafari/dailynotification/DailyNotificationFetchWorker.java
|
|
grep -n "interface FetchWorkerMetrics" android/src/main/java/com/timesafari/dailynotification/DailyNotificationFetchWorker.java
|
|
grep -n "final class NoopFetchWorkerMetrics" android/src/main/java/com/timesafari/dailynotification/DailyNotificationFetchWorker.java
|
|
grep -n "private boolean isRetryable" android/src/main/java/com/timesafari/dailynotification/DailyNotificationFetchWorker.java
|
|
```
|
|
|
|
**✅ Pass condition:**
|
|
- Those anchors exist.
|
|
- `doWork()` increments metrics and records duration on every return path:
|
|
- `metrics.incRun()`
|
|
- `metrics.observeDurationMs(...)`
|
|
- `metrics.incSuccess()` / `metrics.incFailure()` / `metrics.incRetry()`
|
|
|
|
### 3.3 Rolling window logic must not be stubbed
|
|
**File:**
|
|
- `android/src/main/java/com/timesafari/dailynotification/DailyNotificationRollingWindow.java`
|
|
|
|
**Search anchors:**
|
|
```bash
|
|
grep -n "private int countPendingNotifications()" android/src/main/java/com/timesafari/dailynotification/DailyNotificationRollingWindow.java
|
|
grep -n "private int countNotificationsForDate" android/src/main/java/com/timesafari/dailynotification/DailyNotificationRollingWindow.java
|
|
grep -n "private List<NotificationContent> getNotificationsForDate" android/src/main/java/com/timesafari/dailynotification/DailyNotificationRollingWindow.java
|
|
grep -n "private long\[\] dateBoundsMillis" android/src/main/java/com/timesafari/dailynotification/DailyNotificationRollingWindow.java
|
|
```
|
|
|
|
**✅ Pass condition:**
|
|
- none of these return placeholder defaults like `return 0;` / `return new ArrayList<>();` without logic.
|
|
|
|
**Verify implementation:**
|
|
```bash
|
|
grep -A 5 "countPendingNotifications()" android/src/main/java/com/timesafari/dailynotification/DailyNotificationRollingWindow.java | head -10
|
|
```
|
|
|
|
Should show actual logic (storage access, date calculations), not just `return 0;`.
|
|
|
|
### 3.4 Android smoke test (manual, deterministic)
|
|
You need a host app (likely under `test-apps/`).
|
|
|
|
**Procedure:**
|
|
1. Install test host app on emulator/device.
|
|
2. Use a test UI action "Schedule notification in 2 minutes" (or equivalent).
|
|
3. Observe logs.
|
|
|
|
**Log capture:**
|
|
```bash
|
|
adb logcat | grep -i "DailyNotification\|dnp\|timesafari"
|
|
```
|
|
|
|
**✅ Pass condition:**
|
|
- notification schedules successfully
|
|
- pending count increases
|
|
- no retry storm (worker shouldn't loop endlessly)
|
|
|
|
> **Note:** If the test app doesn't expose a "+2 minutes" button, add it: that becomes your permanent "smoke lever."
|
|
|
|
---
|
|
|
|
## 4) iOS: build + scheduler behavior
|
|
|
|
### 4.1 Workspace exists
|
|
```bash
|
|
ls ios | grep -E "xcworkspace|xcodeproj" || true
|
|
```
|
|
|
|
**✅ Pass condition:**
|
|
- you see `DailyNotificationPlugin.xcworkspace` (or equivalent).
|
|
|
|
### 4.2 iOS build/test sanity
|
|
```bash
|
|
cd ios
|
|
xcodebuild -workspace DailyNotificationPlugin.xcworkspace -scheme DailyNotificationPlugin -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 15' test
|
|
cd ..
|
|
```
|
|
|
|
**✅ Pass condition:**
|
|
- tests pass (or if there are no tests wired, build succeeds).
|
|
|
|
**Alternative (build only):**
|
|
```bash
|
|
cd ios
|
|
xcodebuild -workspace DailyNotificationPlugin.xcworkspace -scheme DailyNotificationPlugin -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 15' build
|
|
cd ..
|
|
```
|
|
|
|
### 4.3 Scheduler must enforce TTL + call fetch scheduler hooks
|
|
**File:**
|
|
- `ios/Plugin/DailyNotificationScheduler.swift`
|
|
|
|
**Search anchors:**
|
|
```bash
|
|
grep -n "validateBeforeArming" ios/Plugin/DailyNotificationScheduler.swift
|
|
grep -n "protocol DailyNotificationFetchScheduling" ios/Plugin/DailyNotificationScheduler.swift
|
|
grep -n "NoopFetcherScheduler" ios/Plugin/DailyNotificationScheduler.swift
|
|
grep -n "fetchScheduler.scheduleFetch" ios/Plugin/DailyNotificationScheduler.swift
|
|
grep -n "fetchScheduler.scheduleImmediateFetch" ios/Plugin/DailyNotificationScheduler.swift
|
|
```
|
|
|
|
**✅ Pass condition:**
|
|
- TTL enforcement is present and returns false when invalid
|
|
- the old Phase-2 TODO lines are gone
|
|
- fetch scheduling calls are real method calls (even if Noop)
|
|
|
|
**Verify TTL enforcement:**
|
|
```bash
|
|
grep -A 10 "validateBeforeArming" ios/Plugin/DailyNotificationScheduler.swift | head -15
|
|
```
|
|
|
|
Should show actual validation logic, not just `return true;`.
|
|
|
|
### 4.4 SQLite persistence must be real (not stubs)
|
|
**File:**
|
|
- `ios/Plugin/DailyNotificationDatabase.swift`
|
|
|
|
**Search anchors:**
|
|
```bash
|
|
grep -n "func saveNotificationContent" ios/Plugin/DailyNotificationDatabase.swift
|
|
grep -n "INSERT OR REPLACE INTO" ios/Plugin/DailyNotificationDatabase.swift
|
|
grep -n "func deleteNotificationContent" ios/Plugin/DailyNotificationDatabase.swift
|
|
grep -n "func clearAllNotifications" ios/Plugin/DailyNotificationDatabase.swift
|
|
```
|
|
|
|
**✅ Pass condition:**
|
|
- SQL is executed for save/delete/clear (no placeholder prints-only).
|
|
|
|
**Verify SQL execution:**
|
|
```bash
|
|
grep -A 5 "INSERT OR REPLACE INTO" ios/Plugin/DailyNotificationDatabase.swift
|
|
```
|
|
|
|
Should show actual SQLite3 calls (`sqlite3_exec` or similar), not just `print()`.
|
|
|
|
### 4.5 Rolling window must use UNUserNotificationCenter pending requests
|
|
**File:**
|
|
- `ios/Plugin/DailyNotificationRollingWindow.swift`
|
|
|
|
**Search anchors:**
|
|
```bash
|
|
grep -n "UNUserNotificationCenter.current().getPendingNotificationRequests" ios/Plugin/DailyNotificationRollingWindow.swift
|
|
grep -n "fetchPendingRequestsSync" ios/Plugin/DailyNotificationRollingWindow.swift
|
|
grep -n "countPendingNotifications" ios/Plugin/DailyNotificationRollingWindow.swift
|
|
grep -n "countNotificationsForDate" ios/Plugin/DailyNotificationRollingWindow.swift
|
|
grep -n "getNotificationsForDate" ios/Plugin/DailyNotificationRollingWindow.swift
|
|
```
|
|
|
|
**✅ Pass condition:**
|
|
- functions do real work and don't return placeholder constants.
|
|
|
|
**Verify implementation:**
|
|
```bash
|
|
grep -A 10 "countPendingNotifications" ios/Plugin/DailyNotificationRollingWindow.swift | head -15
|
|
```
|
|
|
|
Should show actual `UNUserNotificationCenter` calls, not just `return 0;`.
|
|
|
|
---
|
|
|
|
## 5) Cross-platform behavior checklist (what must match)
|
|
|
|
### 5.1 "What is pending?" definition is consistent
|
|
**Expected behavior:**
|
|
- Android pending count: scheduledTime >= now from storage truth
|
|
- iOS pending count: UNNotificationCenter pending request count
|
|
|
|
**✅ Pass condition:**
|
|
- Both counts increase after scheduling a future notification and decrease after delivery/cancel.
|
|
|
|
**Test procedure:**
|
|
1. Schedule notification for +2 minutes on Android
|
|
2. Check pending count (should be > 0)
|
|
3. Schedule notification for +2 minutes on iOS
|
|
4. Check pending count (should be > 0)
|
|
5. Cancel all notifications
|
|
6. Check pending count (should be 0)
|
|
|
|
### 5.2 "Count for date" definition is consistent
|
|
**Expected behavior:**
|
|
- Date is `YYYY-MM-DD` local calendar day
|
|
- Android uses date bounds (midnight→midnight)
|
|
- iOS uses `nextTriggerDate()` and formats to date string
|
|
|
|
**✅ Pass condition:**
|
|
- scheduling a notification for "tomorrow morning" increments tomorrow's date bucket, not today.
|
|
|
|
**Test procedure:**
|
|
1. Get current date: `date +%Y-%m-%d`
|
|
2. Schedule notification for tomorrow 9:00 AM
|
|
3. Check count for today (should be unchanged)
|
|
4. Check count for tomorrow (should be +1)
|
|
|
|
### 5.3 TTL behavior is consistent
|
|
**Expected behavior:**
|
|
- TTL invalid → schedule is skipped (or returns false) and logs explain it.
|
|
|
|
**✅ Pass condition:**
|
|
- both platforms refuse to arm stale content in equivalent circumstances (if TTL logic exists on Android too; if not, document the difference).
|
|
|
|
**Test procedure:**
|
|
1. Create content with `fetchedAt` = 2 days ago
|
|
2. Set TTL = 1 day
|
|
3. Attempt to schedule
|
|
4. Verify schedule fails with TTL error log
|
|
|
|
---
|
|
|
|
## 6) Logging + observability receipts (minimal, but mandatory)
|
|
|
|
### 6.1 Required log lines (choose exact strings and standardize)
|
|
Create/confirm a short standard list like:
|
|
- `DNP: scheduling notification`
|
|
- `DNP: TTL validation failed`
|
|
- `DNP: rolling window count pending=`
|
|
- `DNP: fetch worker start`
|
|
- `DNP: fetch worker success itemsFetched= itemsSaved=`
|
|
- `DNP: fetch worker retry reason=`
|
|
|
|
**✅ Pass condition:**
|
|
- You can grep both Android logcat and iOS console for these.
|
|
|
|
**Verify logging:**
|
|
```bash
|
|
# Android
|
|
adb logcat | grep -i "DNP:"
|
|
|
|
# iOS (requires device/simulator console)
|
|
# Check Xcode console output or device logs
|
|
```
|
|
|
|
### 6.2 Decision logging for failures
|
|
When a schedule fails, logs must answer:
|
|
- why it failed
|
|
- whether it will retry
|
|
- what data was rejected (id / slot / date)
|
|
|
|
**✅ Pass condition:**
|
|
- at least one failure path is testable and produces a complete explanation.
|
|
|
|
**Test procedure:**
|
|
1. Attempt to schedule with invalid TTL
|
|
2. Check logs for:
|
|
- Error reason
|
|
- Notification ID
|
|
- TTL value
|
|
- Scheduled time
|
|
|
|
---
|
|
|
|
## 7) Release packaging sanity
|
|
|
|
### 7.1 Ensure `scripts/todo-scan.js` is executable
|
|
```bash
|
|
ls -l scripts/todo-scan.js
|
|
```
|
|
|
|
**✅ Pass condition:**
|
|
- executable bit set OR npm script runs regardless.
|
|
|
|
### 7.2 Clean archive recipe (no junk)
|
|
```bash
|
|
tar czvf daily-notification-plugin-release.tar.gz \
|
|
--exclude=.git \
|
|
--exclude=node_modules \
|
|
--exclude=.venv \
|
|
--exclude=dist \
|
|
--exclude=build \
|
|
--exclude='*.tar.gz' \
|
|
daily-notification-plugin/
|
|
```
|
|
|
|
**✅ Pass condition:**
|
|
- archive created successfully.
|
|
|
|
**Verify archive contents:**
|
|
```bash
|
|
tar tzf daily-notification-plugin-release.tar.gz | head -20
|
|
```
|
|
|
|
Should show source files, not build artifacts.
|
|
|
|
---
|
|
|
|
## 8) Stop conditions (fail fast rules)
|
|
**Stop and fix before proceeding if any occur:**
|
|
- Any TODO marker found in `ios/`, `android/`, `src/` (core code)
|
|
- Android `assembleDebug` fails
|
|
- iOS `xcodebuild test` fails (unless tests are explicitly not configured, in which case: build must succeed)
|
|
- Smoke scheduling fails to deliver a notification in ≤ 3 minutes
|
|
|
|
---
|
|
|
|
## 9) Final "ready" declaration (what you can say to yourself)
|
|
**You may mark "READY" only if:**
|
|
- ✅ TypeScript tests + typecheck pass
|
|
- ✅ Android builds
|
|
- ✅ iOS builds/tests
|
|
- ✅ One Android + one iOS smoke schedule succeeds
|
|
- ✅ TTL behavior is verified (at least iOS)
|
|
- ✅ todo-scan runs and docs reflect reality
|
|
- ✅ Core code has zero TODOs
|
|
- ✅ Logging is consistent and grep-able
|
|
|
|
---
|
|
|
|
## 10) Quick reference: Expected file anchors
|
|
|
|
### Android
|
|
- `android/src/main/java/com/timesafari/dailynotification/DailyNotificationFetchWorker.java` - Worker with metrics
|
|
- `android/src/main/java/com/timesafari/dailynotification/DailyNotificationRollingWindow.java` - Rolling window logic
|
|
- `android/src/main/java/com/timesafari/dailynotification/DailyNotificationPlugin.kt` - Main plugin (thin adapter)
|
|
|
|
### iOS
|
|
- `ios/Plugin/DailyNotificationScheduler.swift` - Scheduler with TTL + fetch hooks
|
|
- `ios/Plugin/DailyNotificationDatabase.swift` - SQLite persistence
|
|
- `ios/Plugin/DailyNotificationRollingWindow.swift` - Rolling window with UNUserNotificationCenter
|
|
- `ios/Plugin/DailyNotificationPlugin.swift` - Main plugin (thin adapter)
|
|
|
|
### TypeScript
|
|
- `src/` - Core TypeScript implementation
|
|
- `packages/` - Internal packages
|
|
- `tests/` - Unit tests
|
|
|
|
---
|
|
|
|
## 11) Troubleshooting common issues
|
|
|
|
### Issue: Android build fails
|
|
**Check:**
|
|
- Java version: `java -version` (should be 11+)
|
|
- Gradle wrapper: `./gradlew --version`
|
|
- Android SDK: `echo $ANDROID_HOME`
|
|
|
|
### Issue: iOS build fails
|
|
**Check:**
|
|
- Xcode version: `xcodebuild -version`
|
|
- Scheme exists: `xcodebuild -list -workspace DailyNotificationPlugin.xcworkspace`
|
|
- Simulator available: `xcrun simctl list devices`
|
|
|
|
### Issue: TODO scan shows core TODOs
|
|
**Action:**
|
|
1. Run: `npm run todo:scan`
|
|
2. Check `docs/todo-scan.json` for `coreCount`
|
|
3. If > 0, grep for TODOs in core directories
|
|
4. Resolve or move to docs/test-apps
|
|
|
|
### Issue: Logs not appearing
|
|
**Check:**
|
|
- Android: `adb logcat -c` (clear) then `adb logcat | grep DNP`
|
|
- iOS: Xcode console or device logs
|
|
- Verify log tags match expected patterns
|
|
|
|
---
|
|
|
|
**Last Updated:** 2025-12-24
|
|
**Status:** Active production readiness checklist
|
|
|