# Bug Tracker
> Vibe Reader - Known issues, challenges, and their resolution status
> Last Updated: 2026-08-26
---
## Format
Each bug follows this structure:
- **ID:** Unique identifier (BUG-XXX)
- **Status:** `Open` | `In Progress` | `Resolved` | `Won't Fix`
- **Severity:** `Critical` | `High` | `Medium` | `Low`
- **Description:** What is happening
- **Steps to Reproduce:** How to trigger the issue
- **Root Cause:** Technical explanation (if known)
- **Resolution:** How it was fixed (if resolved)
---
## Open Issues
### BUG-012: Local Data Wiped Between Dev Builds
**Status:** In Progress (fix applied 2026-08-26; verification pending)
**Severity:** High
**Reported:** 2026-08-26
**Related:** [[Project - Vibe Reader/Build & Iterate/Technical Decisions#TDR-014|Technical Decisions > TDR-014]], [[Project - Vibe Reader/Build & Iterate/Technical Decisions#TDR-001|Technical Decisions > TDR-001]], [[Project - Vibe Reader/Build & Iterate/Technical Decisions#TDR-011|Technical Decisions > TDR-011]]
#### Description
Surfaced through routine daily use and the push to expand the tester population: the library - books, sessions, captures - gets wiped when a new build lands on the device. Rapid iteration is the point of the current phase, but every reinstall that costs a tester their data costs us their trust and their next session. Silent data loss was the ultimate vibe killer inside the app (BUG-R010); this is the same failure one level up, in the delivery loop.
#### Root Cause
Two independent causes, both self-inflicted opt-outs from Android's default (data *survives* normal reinstalls):
1. `fallbackToDestructiveMigration()` in `AppDatabase` - any schema version bump nukes the database.
2. Signing mismatch between install paths - local Android Studio builds and CI builds are signed with different debug keystores, so installing one over the other fails the signature check and forces an uninstall, which wipes app data.
#### Resolution
Per [[Project - Vibe Reader/Build & Iterate/Technical Decisions#TDR-014|Technical Decisions > TDR-014]], applied 2026-08-26:
1. `AppDatabase`: `exportSchema = true`, `fallbackToDestructiveMigration()` removed. Future schema changes = version bump + AutoMigration (or hand-written Migration). Next local build regenerates `schemas/1.json` - commit it.
2. CI workflow: new "Restore shared debug keystore" step signs CI APKs with the local `debug.keystore` (via `DEBUG_KEYSTORE_B64` secret; warns loudly if unset).
**Remaining to close:** add the `DEBUG_KEYSTORE_B64` repo secret; run the verification protocol - build N → capture data → build N+1 → data intact, from both install paths.
---
## Resolved Issues
### BUG-R010: Captures With No Active Session Vanished Silently
**Status:** Resolved
**Severity:** High
**Reported:** 2026-08-01
**Resolved:** 2026-08-01
**Related:** Product Log 2026-08-01
#### Description
Triggering a capture with no active session dropped the word or quote with no feedback. The capture appeared to succeed from the user's side, then simply did not exist. Silent data loss is the ultimate vibe killer.
#### Resolution
Captures with no active session now surface a toast explaining why nothing was saved. Shipped in the same hardening pass (commit `bbdefef`): `startForegroundService` for session start (API 26+ correctness) and Quick Settings tile updates moved to the main thread.
---
### BUG-R011: CRLF Phantom Diffs (29 Files of "Lost Work")
**Status:** Resolved
**Severity:** Medium
**Reported:** 2026-08-01
**Resolved:** 2026-08-01
**Related:** [[Project - Vibe Reader/Build & Iterate/Technical Decisions#TDR-011|Technical Decisions > TDR-011]], Product Log 2026-08-01
#### Description
On re-entry after the six-month gap, 29 files showed as uncommitted changes that looked like lost or diverged work. Every diff was line-ending noise (CRLF vs LF) - a classic Windows git gotcha that cost real archaeology time and a scare.
#### Root Cause
No line-ending normalization policy in the repo; Windows checkouts rewrote endings.
#### Resolution
Added `.gitattributes` + autocrlf configuration and renormalized the index. This class of problem cannot recur.
---
### BUG-R007: No Feedback on "Tap to Look Up" for Converted Words
**Status:** Resolved
**Severity:** High
**Reported:** 2026-01-18
**Resolved:** 2026-02-07
**Related:** [[Project - Vibe Reader/Build & Iterate/User Feedback#Session-002|User Feedback > Session-002]]
#### Description
When a Quote is converted to a Word (via swipe-right), the definition is set to "Converted from quote - tap to look up". However, tapping on the word in Session Detail did nothing.
#### Resolution
Implemented tap-to-relookup functionality:
1. Added `updateWord()` DAO method
2. Added `relookupWord()` function in `SessionViewModel` (Dictionary API → Wikipedia fallback)
3. Made undefined words tappable in `SwipeableWordItem`
4. Added "Tap to look up" hint text below undefined definitions
5. Once looked up successfully, word updates and is no longer tappable
---
### BUG-R008: Lock Screen Media Controls Inconsistent
**Status:** Resolved (Approach Changed)
**Severity:** Critical
**Reported:** 2026-01-18
**Resolved:** 2026-02-07
**Related:** [[Project - Vibe Reader/Build & Iterate/Technical Decisions#TDR-002|Technical Decisions > TDR-002]]
#### Description
The MediaSession "Trojan Horse" approach was unreliable due to Android platform restrictions:
- Notification shade would not collapse
- Cold-start failures due to background activity launch restrictions
#### Resolution
**Abandoned MediaSession approach entirely.** Replaced with:
1. **Quick Settings Tile** (`QuickCaptureTileService`) as primary capture trigger
2. **Simplified foreground notification** with action buttons
3. Removed all `MediaSessionCompat` code from `ReadingSessionService`
4. Changed foreground service type from `mediaPlayback` to `specialUse`
This approach:
- Uses stable, system-supported APIs
- Works reliably from lock screen
- Simplifies codebase for future iOS port
- Trades one extra tap for 100% reliability
---
### BUG-R009: Play Button Shows Pause After Tap
**Status:** Resolved (No Longer Applicable)
**Severity:** Medium
**Reported:** 2026-01-18
**Resolved:** 2026-02-07
**Related:** [[#BUG-R008]]
#### Description
After tapping play button, the icon changed to pause and stayed that way.
#### Resolution
No longer applicable. MediaSession approach was removed entirely. The Quick Settings tile does not have this issue.
---
### BUG-R006: Book Detail View Crashes App
**Status:** Resolved
**Severity:** Critical
**Resolved:** 2026-01-24
**Device:** Pixel 10 Pro, Android 13
**Related:** [[Project - Vibe Reader/Build & Iterate/Feature Backlog#Book Layer View|Feature Backlog > Book Layer View]]
#### Description
Tapping a Book card in the Library view to open Book Detail crashed the application. Only the first book's sessions would load; others caused crashes.
#### Root Cause
In `ReviewScreen.kt`, the `LaunchedEffect` was collecting `Flow` emissions inside a `forEach` loop. The first `collect` call blocks indefinitely, preventing subsequent books from having their sessions loaded.
```kotlin
// BROKEN: collect blocks, only first book works
LaunchedEffect(books) {
books.forEach { bookWithMetrics ->
viewModel.database.vibeReaderDao()
.getRecentSessionsForBook(bookWithMetrics.book.bookId, 5)
.collect { sessions -> ... } // Blocks here!
}
}
```
#### Resolution
Wrapped each Flow collection in its own `launch {}` coroutine to run in parallel:
```kotlin
// FIXED: Each book gets its own coroutine
LaunchedEffect(books) {
books.forEach { bookWithMetrics ->
launch {
viewModel.database.vibeReaderDao()
.getRecentSessionsForBook(bookWithMetrics.book.bookId, 5)
.collect { sessions -> ... }
}
}
}
```
Added import: `import kotlinx.coroutines.launch`
---
### BUG-R004: Capture Activity Does not Re-launch After Close
**Status:** Resolved
**Severity:** Critical
**Resolved:** 2026-01-18
**Related:** [[Project - Vibe Reader/Build & Iterate/User Feedback#Session-002|User Feedback > Session-002]]
#### Description
After closing or dismissing the `SpeechCaptureActivity` (via Cancel, error state, or system back), subsequent attempts to trigger capture from the lock screen or notification did not restart listening.
#### Root Cause
The activity was launched with `FLAG_ACTIVITY_SINGLE_TOP`, which reuses existing instances. When the activity was already in the back stack but not destroyed, Android called `onNewIntent()` instead of `onCreate()`. Without an `onNewIntent()` handler, `startListening()` was never re-invoked.
Additionally, stale `SpeechRecognizer` state from the previous session was not being cleaned up when the activity went to background.
#### Resolution
1. Added `onNewIntent()` override that resets capture mode and calls `startListening()`
2. Added `onStop()` override that calls `speechRecognizer.stopListening()` and `speechRecognizer.cancel()`
```kotlin
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
setIntent(intent)
captureMode = /* parse from intent */
startListening()
}
override fun onStop() {
super.onStop()
speechRecognizer.stopListening()
speechRecognizer.cancel()
}
```
---
### BUG-R005: Quote-to-Word Conversion Crashes
**Status:** Resolved
**Severity:** High
**Resolved:** 2026-01-18
**Related:** [[Project - Vibe Reader/Build & Iterate/User Feedback#Session-002|User Feedback > Session-002]]
#### Description
Swiping right on a Quote to convert it to a Word occasionally caused a silent crash. No error shown to user; conversion simply did not happen.
#### Root Cause
Unhandled exception during database operations. The conversion required both inserting a new Word and deleting the Quote, but if either operation failed, the entire transaction would fail silently.
#### Resolution
Wrapped conversion operations in try-catch blocks with error logging:
```kotlin
fun convertQuoteToWord(quote: Quote) {
viewModelScope.launch {
try {
dao.insertWord(Word(...))
dao.deleteQuote(quote)
} catch (e: Exception) {
Log.e("SessionViewModel", "Failed to convert quote to word", e)
}
}
}
```
---
### BUG-R001: Notification Buttons Invisible on Android 12+
**Status:** Resolved
**Severity:** Critical
**Resolved:** 2025-11-12
**Related:** Product Log 2025-11-12
#### Description
Notification action buttons ("Define", "Quote", "End") were not visible on devices running Android 12+.
#### Root Cause
Android 12+ requires notification actions to have valid icon resources. Actions without icons are silently hidden by the OS.
#### Resolution
Added explicit icon resources to all notification actions:
```kotlin
.addAction(android.R.drawable.ic_menu_search, "Define", definePendingIntent)
.addAction(android.R.drawable.ic_input_add, "Quote", quotePendingIntent)
```
---
### BUG-R002: Button Tap "Glows" But Nothing Happens
**Status:** Resolved
**Severity:** High
**Resolved:** 2025-11-12
**Related:** Product Log 2025-11-12
#### Description
Notification buttons showed visual feedback (glow animation) when tapped, but capture overlay did not appear.
#### Root Cause
Race condition: The UI was starting the Service and writing to the database simultaneously. The Service would wake up, query for active session, find nothing (DB write not yet committed), and exit.
#### Resolution
Passed `session_id` directly to the Service via Intent extra, removing dependency on database query timing.
---
### BUG-R003: Gradle/KSP Version Mismatch with Hilt
**Status:** Resolved (by removing Hilt)
**Severity:** High
**Resolved:** 2026-01-10
**Related:** [[Project - Vibe Reader/Build & Iterate/Technical Decisions#TDR-004|Technical Decisions > TDR-004]]
#### Description
Build failures with error: `ksp ... is too old for kotlin...`
#### Root Cause
Hilt's KSP processor version was incompatible with the project's Kotlin version. The Hilt → KSP → Kotlin version matrix is fragile.
#### Resolution
Removed Hilt entirely; switched to Manual DI / Singleton pattern. Build stability restored immediately.
---
## Issue Template
```markdown
### BUG-XXX: [Title]
**Status:** Open
**Severity:** [Critical/High/Medium/Low]
**Reported:** YYYY-MM-DD
**Device:** [Device + Android version]
**Related:** [[Link to related docs]]
#### Description
[What's happening]
#### Steps to Reproduce
1. Step one
2. Step two
3. **Expected:** [What should happen]
4. **Actual:** [What actually happens]
#### Root Cause
[Technical explanation if known]
#### Resolution
[How it was fixed, or workaround]
```
---
## Bug Metrics
| Status | Count |
|--------|-------|
| Open | 0 |
| In Progress | 1 |
| Resolved | 11 |
| Will not Fix | 0 |
| Severity | Open | Resolved |
|----------|------|----------|
| Critical | 0 | 4 |
| High | 1 | 5 |
| Medium | 0 | 2 |
| Low | 0 | 0 |