feat(docs): add production readiness runbook and enhanced TODO scan
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
This commit is contained in:
File diff suppressed because it is too large
Load Diff
476
docs/progress/PRODUCTION-READINESS-RUNBOOK.md
Normal file
476
docs/progress/PRODUCTION-READINESS-RUNBOOK.md
Normal file
@@ -0,0 +1,476 @@
|
||||
# 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
|
||||
|
||||
73511
docs/todo-scan.json
73511
docs/todo-scan.json
File diff suppressed because it is too large
Load Diff
@@ -94,6 +94,29 @@ function bucketForPath(rel) {
|
||||
return "Other";
|
||||
}
|
||||
|
||||
function isCoreCode(rel) {
|
||||
// Core code directories (production code)
|
||||
return (
|
||||
rel.startsWith("ios/Plugin/") ||
|
||||
rel.startsWith("android/src/main/") ||
|
||||
rel.startsWith("src/") ||
|
||||
rel.startsWith("packages/") ||
|
||||
rel.startsWith("lib/") ||
|
||||
(rel.startsWith("scripts/") && !rel.includes("test"))
|
||||
);
|
||||
}
|
||||
|
||||
function isDocsOrTestApp(rel) {
|
||||
// Documentation and test harness directories
|
||||
return (
|
||||
rel.startsWith("docs/") ||
|
||||
rel.startsWith("test-apps/") ||
|
||||
rel.startsWith("ios/Tests/") ||
|
||||
rel.startsWith("android/src/test/") ||
|
||||
rel.startsWith("tests/")
|
||||
);
|
||||
}
|
||||
|
||||
function main() {
|
||||
const results = [];
|
||||
for (const d of TARGET_DIRS) {
|
||||
@@ -113,7 +136,27 @@ function main() {
|
||||
a.line - b.line
|
||||
);
|
||||
|
||||
fs.writeFileSync(path.join(ROOT, "docs/todo-scan.json"), JSON.stringify(results, null, 2), "utf8");
|
||||
// Split core vs docs/test-apps
|
||||
const coreResults = results.filter(r => isCoreCode(r.file));
|
||||
const docsTestResults = results.filter(r => isDocsOrTestApp(r.file));
|
||||
const otherResults = results.filter(r => !isCoreCode(r.file) && !isDocsOrTestApp(r.file));
|
||||
|
||||
// Enhanced JSON output with split counts
|
||||
const jsonOutput = {
|
||||
summary: {
|
||||
total: results.length,
|
||||
coreCount: coreResults.length,
|
||||
docsTestCount: docsTestResults.length,
|
||||
otherCount: otherResults.length,
|
||||
generatedAt: new Date().toISOString()
|
||||
},
|
||||
core: coreResults,
|
||||
docsTest: docsTestResults,
|
||||
other: otherResults,
|
||||
all: results
|
||||
};
|
||||
|
||||
fs.writeFileSync(path.join(ROOT, "docs/todo-scan.json"), JSON.stringify(jsonOutput, null, 2), "utf8");
|
||||
|
||||
// markdown
|
||||
const byBucket = new Map();
|
||||
@@ -125,7 +168,13 @@ function main() {
|
||||
let md = "";
|
||||
md += `# TODO Classification (auto-generated)\n\n`;
|
||||
md += `Generated by \`scripts/todo-scan.js\`\n\n`;
|
||||
md += `Total markers: **${results.length}**\n\n`;
|
||||
md += `## Summary\n\n`;
|
||||
md += `- **Total markers:** ${results.length}\n`;
|
||||
md += `- **Core code (production):** ${coreResults.length} ⚠️\n`;
|
||||
md += `- **Docs/test-apps:** ${docsTestResults.length} ✅ (expected)\n`;
|
||||
md += `- **Other:** ${otherResults.length}\n\n`;
|
||||
md += `> **Note:** Core code TODOs should be near zero. Docs/test-app TODOs are expected and acceptable.\n\n`;
|
||||
md += `---\n\n`;
|
||||
|
||||
for (const [bucket, items] of byBucket.entries()) {
|
||||
md += `## ${bucket} (${items.length})\n\n`;
|
||||
@@ -146,7 +195,14 @@ function main() {
|
||||
|
||||
fs.writeFileSync(path.join(ROOT, "docs/TODO-CLASSIFICATION.md"), md, "utf8");
|
||||
|
||||
console.log(`todo-scan complete: ${results.length} markers`);
|
||||
// Console output with split summary
|
||||
console.log(`\n📊 TODO Scan Complete`);
|
||||
console.log(`━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━`);
|
||||
console.log(`Total markers: ${results.length}`);
|
||||
console.log(`Core code: ${coreResults.length} ${coreResults.length === 0 ? '✅' : '⚠️ (should be 0)'}`);
|
||||
console.log(`Docs/test-apps: ${docsTestResults.length} ✅ (expected)`);
|
||||
console.log(`Other: ${otherResults.length}`);
|
||||
console.log(`━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n`);
|
||||
}
|
||||
|
||||
main();
|
||||
|
||||
Reference in New Issue
Block a user