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
14 KiB
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
pwd
ls
✅ Expected to include folders like:
src/android/ios/docs/scripts/
0.2 Capture current revision (for receipts)
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
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
npm run todo:scan
✅ Pass condition:
docs/TODO-CLASSIFICATION.mdanddocs/todo-scan.jsonget updated successfully.
Verify split reporting:
- Check that
docs/todo-scan.jsonincludescoreCountanddocsCountfields.
2) TypeScript layer: contract + build sanity
2.1 Unit tests
npm test
✅ Pass condition:
- exits 0.
Expected output:
Test Suites: 8 passed, 8 total
Tests: 115 passed, 115 total
2.2 Typecheck
npm run typecheck
✅ Pass condition:
- exits 0.
Expected output:
> @timesafari/daily-notification-plugin@1.0.11 typecheck
> tsc --noEmit
2.3 Lint (if present)
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
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:
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:
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:
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:
- Install test host app on emulator/device.
- Use a test UI action "Schedule notification in 2 minutes" (or equivalent).
- Observe logs.
Log capture:
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
ls ios | grep -E "xcworkspace|xcodeproj" || true
✅ Pass condition:
- you see
DailyNotificationPlugin.xcworkspace(or equivalent).
4.2 iOS build/test sanity
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):
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:
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:
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:
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:
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:
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:
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:
- Schedule notification for +2 minutes on Android
- Check pending count (should be > 0)
- Schedule notification for +2 minutes on iOS
- Check pending count (should be > 0)
- Cancel all notifications
- Check pending count (should be 0)
5.2 "Count for date" definition is consistent
Expected behavior:
- Date is
YYYY-MM-DDlocal 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:
- Get current date:
date +%Y-%m-%d - Schedule notification for tomorrow 9:00 AM
- Check count for today (should be unchanged)
- 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:
- Create content with
fetchedAt= 2 days ago - Set TTL = 1 day
- Attempt to schedule
- 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 notificationDNP: TTL validation failedDNP: rolling window count pending=DNP: fetch worker startDNP: 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:
# 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:
- Attempt to schedule with invalid TTL
- Check logs for:
- Error reason
- Notification ID
- TTL value
- Scheduled time
7) Release packaging sanity
7.1 Ensure scripts/todo-scan.js is executable
ls -l scripts/todo-scan.js
✅ Pass condition:
- executable bit set OR npm script runs regardless.
7.2 Clean archive recipe (no junk)
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:
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
assembleDebugfails - iOS
xcodebuild testfails (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 metricsandroid/src/main/java/com/timesafari/dailynotification/DailyNotificationRollingWindow.java- Rolling window logicandroid/src/main/java/com/timesafari/dailynotification/DailyNotificationPlugin.kt- Main plugin (thin adapter)
iOS
ios/Plugin/DailyNotificationScheduler.swift- Scheduler with TTL + fetch hooksios/Plugin/DailyNotificationDatabase.swift- SQLite persistenceios/Plugin/DailyNotificationRollingWindow.swift- Rolling window with UNUserNotificationCenterios/Plugin/DailyNotificationPlugin.swift- Main plugin (thin adapter)
TypeScript
src/- Core TypeScript implementationpackages/- Internal packagestests/- 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:
- Run:
npm run todo:scan - Check
docs/todo-scan.jsonforcoreCount - If > 0, grep for TODOs in core directories
- Resolve or move to docs/test-apps
Issue: Logs not appearing
Check:
- Android:
adb logcat -c(clear) thenadb 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