diff --git a/ClaudeQAPlan.md b/ClaudeQAPlan.md
index 47f92e8a..5bf4d53a 100644
--- a/ClaudeQAPlan.md
+++ b/ClaudeQAPlan.md
@@ -117,7 +117,7 @@ confirms + enumerates this; the fix phase applies couple-shared everywhere.
are the authoritative state** and survive any compaction — after a summary, **re-read them and continue at the next
chunk**. Never pause a run merely because context is getting long; only stop for a true blocker (a denied gated action
even with standing auth, or the macOS requirement for iOS).
-- **Commit before anything interruptible** so a mid-chunk compaction never loses progress. Keep chunks atomic; if a
+- **Checkpoint (save the working-tree MD files + run-state) before anything interruptible** so a mid-chunk compaction never loses progress (the user commits — never run git yourself). Keep chunks atomic; if a
chunk is cut off mid-way (e.g., a game session left active), the **session-start ritual recovers it** (clear the stuck
session via in-app "End their game", then redo that chunk). Right-sized chunks (see Batch sizing) make this rare.
- **Don't pause for "by-design vs bug":** log the ambiguous finding and keep going (don't unilaterally rewrite
@@ -305,7 +305,7 @@ State lives in **files**, not memory:
path crashed → don't fix by reasoning; reproduce via the real channel + read the stack").
This is how the plan self-improves between rounds — treat the human pointing out a missed bug as a signal the plan had
a gap, and close the gap here, not just the bug.
-- **Commit cadence**: commit `ClaudeReport.md` + `ClaudeQACoverage.md` after each pass and each chunk.
+- **Checkpoint cadence**: save `ClaudeReport.md` + `ClaudeQACoverage.md` + run-state after each pass and each chunk (the **user** commits — never run git yourself; see Guardrails).
- **Chunking**: run small chunks (Pass C one screen-group; Pass A one feature), checkpoint after each.
- **Session-start ritual**: (1) read run-state header + both MD files; (2) `adb devices` shows **both** emulators
online; (3) **installed build == current HEAD** (rebuild+reinstall if unsure — never QA a stale APK); (4) continue
@@ -314,14 +314,15 @@ State lives in **files**, not memory:
## Batch sizing — sub-batch each pass to ONE context window (Round-1 calibration)
A pass is a **category**, not a unit of work. Execute each pass as **sub-batches (chunks)**, where a chunk = the
-**largest coherent unit that reliably finishes AND commits within one context window, with margin**. End every chunk
-with a commit + run-state update. If a chunk starts overflowing, split it; if chunks feel trivial, merge them.
+**largest coherent unit that reliably finishes AND checkpoints within one context window, with margin**. End every chunk
+by saving the MD files + run-state (the user commits — never run git yourself). If a chunk starts overflowing, split it;
+if chunks feel trivial, merge them.
**Why:** in Round 1, A & D fit as single batches, but B/C/E were too large → got cut off → deferred. Sub-batching
-prevents half-done/lost work and gives cleaner per-chunk verification + revertable commits.
+prevents half-done/lost work and gives cleaner per-chunk verification + revertable history.
Default small: if a chunk requires two-device live driving, screenshots/montage review, logcat checks, or admin/API
verification, keep it to **one small route family, one game phase, or one notification type**. A chunk is too large if
-it cannot produce a precise coverage update, issue log, and commit before context gets tight. Split before starting
+it cannot produce a precise coverage update, issue log, and file-checkpoint before context gets tight. Split before starting
rather than leaving a half-tested matrix behind. **Prefer Claude-friendly micro-batches**: smaller chunks let the agent
fully inspect screenshots, tap every CTA, vary app states, update files accurately, and avoid shallow "covered" rows.
@@ -348,6 +349,11 @@ Context-cost tips: prefer **code/admin-read audits** (cheap) before live UI swee
(dark|light pairs) to review many at once; keep one chunk = one TodoWrite focus.
## Guardrails & efficiency
+- **⛔ NEVER `git commit` / `git push` — the USER does ALL commits.** This overrides every "commit" verb elsewhere in
+ this doc: wherever a step says "commit," read it as **"checkpoint = save the working-tree files (`ClaudeReport.md` +
+ `ClaudeQACoverage.md` + run-state, plus any code/docs)"** and leave the actual `git commit` to the user. Your durable
+ state lives in those files (they survive compaction), not in a commit you make. Never stage, commit, push, branch, or
+ amend.
- **Never `pm clear` / wipe app data** — breaks the App Check debug token. Pre-pairing QA: sign-out → fresh sign-up.
- **Never run `seed/build_db.py`.** Admin seeds/writes, entitlement toggles, and any deploys are **user-authorized per occurrence**.
- **By-design vs bug:** if a finding may be intended behavior, **log it and keep going** (don't stop to ask; don't unilaterally rewrite deliberate design — the log captures it).
diff --git a/Engineering_Reference_Manual_Plan.md b/Engineering_Reference_Manual_Plan.md
new file mode 100644
index 00000000..2b48670a
--- /dev/null
+++ b/Engineering_Reference_Manual_Plan.md
@@ -0,0 +1,92 @@
+# Engineering Reference Manual Update Plan
+
+Goal: bring `docs/Engineering_Reference_Manual.md` into alignment with the current
+relationship-app codebase. Verify every claim, file path, function name,
+collection name, and architectural fact. Never assume.
+
+## Approach
+
+- **Batch-based.** Each batch covers one major section or a coherent set of
+ subsections. One agent/turn per batch.
+- **Evidence-first.** For every factual claim in the manual, verify against the
+ repo (grep, read file, run build/test if needed).
+- **Log everything.** Each batch updates this plan with findings and
+ changes made.
+- **Ripley coordinates.** Ripley does not code unless necessary; Bishop or
+ subagents may verify builds/docs.
+
+## Batches
+
+### Batch 1 — Front matter + System overview + Repository layout
+- Read: `## How to use this document` through `### Shared configuration`.
+- Verify all directory paths under `app/src/main/java/app/closer/`,
+ `functions/src/`, `functions/dist/`, `res/drawable-*`.
+- Flag missing or renamed directories/files.
+- Update any stale paths or naming.
+
+### Batch 2 — Authentication, pairing, couples model
+- Read: `## Authentication and pairing flow` through `### Key Cloud Functions`.
+- Verify auth files, pairing flow files, `couples` collection fields,
+ recovery phrase implementation.
+- Cross-check `firestore.rules` for the rules described.
+- Update if code diverges from doc.
+
+### Batch 3 — E2EE model + Firestore data model
+- Read: `## End-to-end encryption model` and `## Firestore data model`.
+- Verify crypto files, encryption versions, collection schemas, field names.
+- Check `firestore.rules` regex helpers and invariants.
+- Update data model tables if collections/fields changed.
+
+### Batch 4 — Daily question lifecycle + Cloud Functions module
+- Read: `## Daily question lifecycle` and `## Cloud Functions`.
+- Verify function triggers, handlers, schedules, module responsibilities.
+- Cross-check `functions/src/` tree.
+- Update if functions were added/removed/renamed.
+
+### Batch 5 — Firestore security rules
+- Read: `## Firestore security rules`.
+- Line-by-line verify against `firestore.rules`.
+- Note any rules in the file not documented, or documented rules not in file.
+
+### Batch 6 — Billing + Notifications
+- Read: `## Billing` and `## Notifications`.
+- Verify RevenueCat integration files, entitlement checks, webhook handler.
+- Verify notification classes, quiet hours, deep-link routing.
+- Update for quiet-hours server-side suppression if doc still says local-only.
+
+### Batch 7 — iOS-specific + Build and release
+- Read: `## iOS-specific notes` and `## Build and release`.
+- Verify iOS project state (is it still broken? still exists?).
+- Verify build commands, secrets, ProGuard, Functions deploy commands.
+- Update stale commands or iOS status.
+
+### Batch 8 — Known landmines and recent fixes (cross-cutting)
+- Read: `## Known landmines and recent fixes`.
+- Verify each listed issue ID and fix matches git history and current code.
+- Add any missing recent landmines (e.g., theme scanner, quiet hours server-side).
+- Prune obsolete entries.
+
+### Batch 9 — Final review, TOC update, formatting
+- Update `## Table of Contents` if headings changed.
+- Run markdown lint / build check if applicable.
+- Final pass for consistency.
+
+## Status tracking
+
+| Batch | Status | Findings | Changes made |
+|---|---|---|---|
+| 1 | todo | | |
+| 2 | todo | | |
+| 3 | todo | | |
+| 4 | todo | | |
+| 5 | todo | | |
+| 6 | todo | | |
+| 7 | todo | | |
+| 8 | todo | | |
+| 9 | todo | | |
+
+## Notes
+
+- If a section is found to be mostly correct, still spot-check a few claims.
+- If a section is wildly out of date, rewrite it and note the divergence.
+- Commit after each batch (per Git Discipline Rules).
diff --git a/app/src/main/java/app/closer/ui/dates/DateBuilderViewModel.kt b/app/src/main/java/app/closer/ui/dates/DateBuilderViewModel.kt
index 6da4bde7..86d43da7 100644
--- a/app/src/main/java/app/closer/ui/dates/DateBuilderViewModel.kt
+++ b/app/src/main/java/app/closer/ui/dates/DateBuilderViewModel.kt
@@ -41,10 +41,6 @@ class DateBuilderViewModel @Inject constructor(
_uiState.update { it.copy(duration = duration) }
}
- fun setDateIdeaId(dateIdeaId: String) {
- _uiState.update { it.copy(dateIdeaId = dateIdeaId) }
- }
-
fun savePreference() {
val state = _uiState.value
diff --git a/app/src/main/res/drawable-nodpi/glyph_add.xml b/app/src/main/res/drawable-nodpi/glyph_add.xml
new file mode 100644
index 00000000..a15f1c16
--- /dev/null
+++ b/app/src/main/res/drawable-nodpi/glyph_add.xml
@@ -0,0 +1,3 @@
+
+
+
diff --git a/app/src/main/res/drawable-nodpi/glyph_attach_money.xml b/app/src/main/res/drawable-nodpi/glyph_attach_money.xml
new file mode 100644
index 00000000..6acd4103
--- /dev/null
+++ b/app/src/main/res/drawable-nodpi/glyph_attach_money.xml
@@ -0,0 +1,3 @@
+
+
+
diff --git a/app/src/main/res/drawable-nodpi/glyph_back.xml b/app/src/main/res/drawable-nodpi/glyph_back.xml
new file mode 100644
index 00000000..a5ca9f4c
--- /dev/null
+++ b/app/src/main/res/drawable-nodpi/glyph_back.xml
@@ -0,0 +1,3 @@
+
+
+
diff --git a/app/src/main/res/drawable-nodpi/glyph_bell.xml b/app/src/main/res/drawable-nodpi/glyph_bell.xml
new file mode 100644
index 00000000..479d1048
--- /dev/null
+++ b/app/src/main/res/drawable-nodpi/glyph_bell.xml
@@ -0,0 +1,3 @@
+
+
+
diff --git a/app/src/main/res/drawable-nodpi/glyph_cake.xml b/app/src/main/res/drawable-nodpi/glyph_cake.xml
new file mode 100644
index 00000000..07aa1d6d
--- /dev/null
+++ b/app/src/main/res/drawable-nodpi/glyph_cake.xml
@@ -0,0 +1,3 @@
+
+
+
diff --git a/app/src/main/res/drawable-nodpi/glyph_calendar.xml b/app/src/main/res/drawable-nodpi/glyph_calendar.xml
new file mode 100644
index 00000000..23626372
--- /dev/null
+++ b/app/src/main/res/drawable-nodpi/glyph_calendar.xml
@@ -0,0 +1,3 @@
+
+
+
diff --git a/app/src/main/res/drawable-nodpi/glyph_camera.xml b/app/src/main/res/drawable-nodpi/glyph_camera.xml
new file mode 100644
index 00000000..c159f1b6
--- /dev/null
+++ b/app/src/main/res/drawable-nodpi/glyph_camera.xml
@@ -0,0 +1,3 @@
+
+
+
diff --git a/app/src/main/res/drawable-nodpi/glyph_card_giftcard.xml b/app/src/main/res/drawable-nodpi/glyph_card_giftcard.xml
new file mode 100644
index 00000000..c6b45bf7
--- /dev/null
+++ b/app/src/main/res/drawable-nodpi/glyph_card_giftcard.xml
@@ -0,0 +1,3 @@
+
+
+
diff --git a/app/src/main/res/drawable-nodpi/glyph_chat.xml b/app/src/main/res/drawable-nodpi/glyph_chat.xml
new file mode 100644
index 00000000..2286c6bb
--- /dev/null
+++ b/app/src/main/res/drawable-nodpi/glyph_chat.xml
@@ -0,0 +1,3 @@
+
+
+
diff --git a/app/src/main/res/drawable-nodpi/glyph_check.xml b/app/src/main/res/drawable-nodpi/glyph_check.xml
new file mode 100644
index 00000000..bd1b4f45
--- /dev/null
+++ b/app/src/main/res/drawable-nodpi/glyph_check.xml
@@ -0,0 +1,3 @@
+
+
+
diff --git a/app/src/main/res/drawable-nodpi/glyph_close.xml b/app/src/main/res/drawable-nodpi/glyph_close.xml
new file mode 100644
index 00000000..c27e2e3a
--- /dev/null
+++ b/app/src/main/res/drawable-nodpi/glyph_close.xml
@@ -0,0 +1,3 @@
+
+
+
diff --git a/app/src/main/res/drawable-nodpi/glyph_copy.xml b/app/src/main/res/drawable-nodpi/glyph_copy.xml
new file mode 100644
index 00000000..d9efdbbd
--- /dev/null
+++ b/app/src/main/res/drawable-nodpi/glyph_copy.xml
@@ -0,0 +1,3 @@
+
+
+
diff --git a/app/src/main/res/drawable-nodpi/glyph_couple.xml b/app/src/main/res/drawable-nodpi/glyph_couple.xml
new file mode 100644
index 00000000..66f24b61
--- /dev/null
+++ b/app/src/main/res/drawable-nodpi/glyph_couple.xml
@@ -0,0 +1,3 @@
+
+
+
diff --git a/app/src/main/res/drawable-nodpi/glyph_edit.xml b/app/src/main/res/drawable-nodpi/glyph_edit.xml
new file mode 100644
index 00000000..b11fa12a
--- /dev/null
+++ b/app/src/main/res/drawable-nodpi/glyph_edit.xml
@@ -0,0 +1,3 @@
+
+
+
diff --git a/app/src/main/res/drawable-nodpi/glyph_eye.xml b/app/src/main/res/drawable-nodpi/glyph_eye.xml
new file mode 100644
index 00000000..88760792
--- /dev/null
+++ b/app/src/main/res/drawable-nodpi/glyph_eye.xml
@@ -0,0 +1,3 @@
+
+
+
diff --git a/app/src/main/res/drawable-nodpi/glyph_eye_off.xml b/app/src/main/res/drawable-nodpi/glyph_eye_off.xml
new file mode 100644
index 00000000..e4cf1392
--- /dev/null
+++ b/app/src/main/res/drawable-nodpi/glyph_eye_off.xml
@@ -0,0 +1,3 @@
+
+
+
diff --git a/app/src/main/res/drawable-nodpi/glyph_fingerprint.xml b/app/src/main/res/drawable-nodpi/glyph_fingerprint.xml
new file mode 100644
index 00000000..de5acaee
--- /dev/null
+++ b/app/src/main/res/drawable-nodpi/glyph_fingerprint.xml
@@ -0,0 +1,3 @@
+
+
+
diff --git a/app/src/main/res/drawable-nodpi/glyph_forward.xml b/app/src/main/res/drawable-nodpi/glyph_forward.xml
new file mode 100644
index 00000000..0ec096ee
--- /dev/null
+++ b/app/src/main/res/drawable-nodpi/glyph_forward.xml
@@ -0,0 +1,3 @@
+
+
+
diff --git a/app/src/main/res/drawable-nodpi/glyph_heart.xml b/app/src/main/res/drawable-nodpi/glyph_heart.xml
new file mode 100644
index 00000000..d67e6b17
--- /dev/null
+++ b/app/src/main/res/drawable-nodpi/glyph_heart.xml
@@ -0,0 +1,3 @@
+
+
+
diff --git a/app/src/main/res/drawable-nodpi/glyph_heart_outline.xml b/app/src/main/res/drawable-nodpi/glyph_heart_outline.xml
new file mode 100644
index 00000000..7d40a987
--- /dev/null
+++ b/app/src/main/res/drawable-nodpi/glyph_heart_outline.xml
@@ -0,0 +1,3 @@
+
+
+
diff --git a/app/src/main/res/drawable-nodpi/glyph_home.xml b/app/src/main/res/drawable-nodpi/glyph_home.xml
new file mode 100644
index 00000000..b281cd63
--- /dev/null
+++ b/app/src/main/res/drawable-nodpi/glyph_home.xml
@@ -0,0 +1,3 @@
+
+
+
diff --git a/app/src/main/res/drawable-nodpi/glyph_hourglass.xml b/app/src/main/res/drawable-nodpi/glyph_hourglass.xml
new file mode 100644
index 00000000..9afe1aaa
--- /dev/null
+++ b/app/src/main/res/drawable-nodpi/glyph_hourglass.xml
@@ -0,0 +1,3 @@
+
+
+
diff --git a/app/src/main/res/drawable-nodpi/glyph_key.xml b/app/src/main/res/drawable-nodpi/glyph_key.xml
new file mode 100644
index 00000000..ef9bc27d
--- /dev/null
+++ b/app/src/main/res/drawable-nodpi/glyph_key.xml
@@ -0,0 +1,3 @@
+
+
+
diff --git a/app/src/main/res/drawable-nodpi/glyph_lock.xml b/app/src/main/res/drawable-nodpi/glyph_lock.xml
new file mode 100644
index 00000000..e89b91cc
--- /dev/null
+++ b/app/src/main/res/drawable-nodpi/glyph_lock.xml
@@ -0,0 +1,3 @@
+
+
+
diff --git a/app/src/main/res/drawable-nodpi/glyph_lock_open.xml b/app/src/main/res/drawable-nodpi/glyph_lock_open.xml
new file mode 100644
index 00000000..93ed2c1c
--- /dev/null
+++ b/app/src/main/res/drawable-nodpi/glyph_lock_open.xml
@@ -0,0 +1,3 @@
+
+
+
diff --git a/app/src/main/res/drawable-nodpi/glyph_mic.xml b/app/src/main/res/drawable-nodpi/glyph_mic.xml
new file mode 100644
index 00000000..cfea5f48
--- /dev/null
+++ b/app/src/main/res/drawable-nodpi/glyph_mic.xml
@@ -0,0 +1,3 @@
+
+
+
diff --git a/app/src/main/res/drawable-nodpi/glyph_open_in_new.xml b/app/src/main/res/drawable-nodpi/glyph_open_in_new.xml
new file mode 100644
index 00000000..aa97fc35
--- /dev/null
+++ b/app/src/main/res/drawable-nodpi/glyph_open_in_new.xml
@@ -0,0 +1,3 @@
+
+
+
diff --git a/app/src/main/res/drawable-nodpi/glyph_palette.xml b/app/src/main/res/drawable-nodpi/glyph_palette.xml
new file mode 100644
index 00000000..0a5d63b5
--- /dev/null
+++ b/app/src/main/res/drawable-nodpi/glyph_palette.xml
@@ -0,0 +1,3 @@
+
+
+
diff --git a/app/src/main/res/drawable-nodpi/glyph_pause.xml b/app/src/main/res/drawable-nodpi/glyph_pause.xml
new file mode 100644
index 00000000..9ecb0b19
--- /dev/null
+++ b/app/src/main/res/drawable-nodpi/glyph_pause.xml
@@ -0,0 +1,3 @@
+
+
+
diff --git a/app/src/main/res/drawable-nodpi/glyph_person.xml b/app/src/main/res/drawable-nodpi/glyph_person.xml
new file mode 100644
index 00000000..e017d36a
--- /dev/null
+++ b/app/src/main/res/drawable-nodpi/glyph_person.xml
@@ -0,0 +1,3 @@
+
+
+
diff --git a/app/src/main/res/drawable-nodpi/glyph_photo.xml b/app/src/main/res/drawable-nodpi/glyph_photo.xml
new file mode 100644
index 00000000..322220c1
--- /dev/null
+++ b/app/src/main/res/drawable-nodpi/glyph_photo.xml
@@ -0,0 +1,3 @@
+
+
+
diff --git a/app/src/main/res/drawable-nodpi/glyph_play.xml b/app/src/main/res/drawable-nodpi/glyph_play.xml
new file mode 100644
index 00000000..4f9f4d45
--- /dev/null
+++ b/app/src/main/res/drawable-nodpi/glyph_play.xml
@@ -0,0 +1,3 @@
+
+
+
diff --git a/app/src/main/res/drawable-nodpi/glyph_psychology.xml b/app/src/main/res/drawable-nodpi/glyph_psychology.xml
new file mode 100644
index 00000000..c8cfd5fa
--- /dev/null
+++ b/app/src/main/res/drawable-nodpi/glyph_psychology.xml
@@ -0,0 +1,3 @@
+
+
+
diff --git a/app/src/main/res/drawable-nodpi/glyph_question_answer.xml b/app/src/main/res/drawable-nodpi/glyph_question_answer.xml
new file mode 100644
index 00000000..34bc9084
--- /dev/null
+++ b/app/src/main/res/drawable-nodpi/glyph_question_answer.xml
@@ -0,0 +1,3 @@
+
+
+
diff --git a/app/src/main/res/drawable-nodpi/glyph_refresh.xml b/app/src/main/res/drawable-nodpi/glyph_refresh.xml
new file mode 100644
index 00000000..75114d01
--- /dev/null
+++ b/app/src/main/res/drawable-nodpi/glyph_refresh.xml
@@ -0,0 +1,3 @@
+
+
+
diff --git a/app/src/main/res/drawable-nodpi/glyph_send.xml b/app/src/main/res/drawable-nodpi/glyph_send.xml
new file mode 100644
index 00000000..b1e80d1b
--- /dev/null
+++ b/app/src/main/res/drawable-nodpi/glyph_send.xml
@@ -0,0 +1,3 @@
+
+
+
diff --git a/app/src/main/res/drawable-nodpi/glyph_settings.xml b/app/src/main/res/drawable-nodpi/glyph_settings.xml
new file mode 100644
index 00000000..16e63ca2
--- /dev/null
+++ b/app/src/main/res/drawable-nodpi/glyph_settings.xml
@@ -0,0 +1,3 @@
+
+
+
diff --git a/app/src/main/res/drawable-nodpi/glyph_share.xml b/app/src/main/res/drawable-nodpi/glyph_share.xml
new file mode 100644
index 00000000..64a5f9dc
--- /dev/null
+++ b/app/src/main/res/drawable-nodpi/glyph_share.xml
@@ -0,0 +1,3 @@
+
+
+
diff --git a/app/src/main/res/drawable-nodpi/glyph_shield.xml b/app/src/main/res/drawable-nodpi/glyph_shield.xml
new file mode 100644
index 00000000..0f1b26c9
--- /dev/null
+++ b/app/src/main/res/drawable-nodpi/glyph_shield.xml
@@ -0,0 +1,3 @@
+
+
+
diff --git a/app/src/main/res/drawable-nodpi/glyph_star.xml b/app/src/main/res/drawable-nodpi/glyph_star.xml
new file mode 100644
index 00000000..822b4534
--- /dev/null
+++ b/app/src/main/res/drawable-nodpi/glyph_star.xml
@@ -0,0 +1,3 @@
+
+
+
diff --git a/app/src/main/res/drawable-nodpi/glyph_streak.xml b/app/src/main/res/drawable-nodpi/glyph_streak.xml
new file mode 100644
index 00000000..706fdc1f
--- /dev/null
+++ b/app/src/main/res/drawable-nodpi/glyph_streak.xml
@@ -0,0 +1,3 @@
+
+
+
diff --git a/app/src/main/res/drawable-nodpi/glyph_sync.xml b/app/src/main/res/drawable-nodpi/glyph_sync.xml
new file mode 100644
index 00000000..2684a3ac
--- /dev/null
+++ b/app/src/main/res/drawable-nodpi/glyph_sync.xml
@@ -0,0 +1,3 @@
+
+
+
diff --git a/app/src/main/res/drawable-nodpi/glyph_timeline.xml b/app/src/main/res/drawable-nodpi/glyph_timeline.xml
new file mode 100644
index 00000000..f96a89b9
--- /dev/null
+++ b/app/src/main/res/drawable-nodpi/glyph_timeline.xml
@@ -0,0 +1,3 @@
+
+
+
diff --git a/app/src/main/res/drawable-nodpi/glyph_trash.xml b/app/src/main/res/drawable-nodpi/glyph_trash.xml
new file mode 100644
index 00000000..2a21f796
--- /dev/null
+++ b/app/src/main/res/drawable-nodpi/glyph_trash.xml
@@ -0,0 +1,3 @@
+
+
+
diff --git a/app/src/main/res/drawable-nodpi/glyph_trending_up.xml b/app/src/main/res/drawable-nodpi/glyph_trending_up.xml
new file mode 100644
index 00000000..bea132f3
--- /dev/null
+++ b/app/src/main/res/drawable-nodpi/glyph_trending_up.xml
@@ -0,0 +1,3 @@
+
+
+
diff --git a/app/src/main/res/drawable-nodpi/glyph_warning.xml b/app/src/main/res/drawable-nodpi/glyph_warning.xml
new file mode 100644
index 00000000..d6c9bb18
--- /dev/null
+++ b/app/src/main/res/drawable-nodpi/glyph_warning.xml
@@ -0,0 +1,3 @@
+
+
+
diff --git a/docs/Engineering_Reference_Manual.md b/docs/Engineering_Reference_Manual.md
index b6e97b02..179014aa 100644
--- a/docs/Engineering_Reference_Manual.md
+++ b/docs/Engineering_Reference_Manual.md
@@ -89,14 +89,14 @@ app/src/main/java/app/closer/
│ ├── analytics/ # Firebase Analytics + Crashlytics wrappers
│ ├── billing/ # EntitlementChecker + FirestoreEntitlementChecker
│ ├── crash/ # CrashReporter abstraction
-│ ├── feature/ # (reserved for feature flags; currently empty — no feature-flag code in repo today)
+│ ├── feature/ # (reserved for feature flags; no directory exists today — no feature-flag code in repo)
│ ├── navigation/ # AppRoute constants, NavHost, ExternalLinks
│ └── notifications/ # AppMessagingService, NotificationHelper, NotificationPermissionHelper, QuietHours, TokenRegistrar
├── crypto/ # E2EE: Tink AEAD, BouncyCastle Argon2id, key stores
├── data/
│ ├── challenges/ # Connection Challenges data sources
│ ├── local/ # Room DAOs, DataStore, EncryptedSharedPreferences (RecoveryPhraseStore, SecurePreferencesFactory, SettingsDataStore)
-│ ├── questions/ # Question pack data sources (QuestionDao + QuestionJsonParser)
+│ ├── questions/ # Question pack data sources (`QuestionJsonParser`)
│ ├── remote/ # Firestore data sources, Cloud Functions callable wrappers
│ ├── repository/ # Repository implementations
│ └── security/ # PlayIntegrityChecker
@@ -131,7 +131,7 @@ app/src/main/java/app/closer/
└── wheel/ # Spin the wheel
```
-**Note on the manual's older description**: a `core/security/` package and `data/local/QuestionJsonParser.kt` were documented in earlier revisions of this manual but don't exist in the current source. The auth rate limiter is in `domain/security/AuthRateLimiter.kt`, and the question JSON parser is at `data/questions/QuestionJsonParser.kt`.
+**Note on the manual's older description**: a `core/security/` package was documented in earlier revisions of this manual but doesn't exist in the current source. The auth rate limiter is in `domain/security/AuthRateLimiter.kt`, and `QuestionJsonParser` is at `data/questions/QuestionJsonParser.kt` while `QuestionDao` is in `data/local/QuestionDao.kt`.
The Android settings package contains: `SettingsScreen`, `SettingsViewModel`, `SettingsVisuals`, `AccountScreen`, `EditProfileScreen` + `EditProfileViewModel`, `AppearanceScreen`, `DeleteAccountScreen`, `NotificationSettingsScreen`, `PrivacyScreen`, `RelationshipSettingsScreen`, `SecurityScreen`, `SubscriptionScreen`. The `SecurityScreen` is biometric-gated for the recovery phrase reveal.
diff --git a/scripts/wiring-scan.sh b/scripts/wiring-scan.sh
new file mode 100755
index 00000000..db257af1
--- /dev/null
+++ b/scripts/wiring-scan.sh
@@ -0,0 +1,111 @@
+#!/bin/bash
+#
+# CloserApp — automated WIRING / dead-feature scanner (Pass N + discovery pre-check)
+#
+# Catches the "silent dead feature" class that QA found live (N-001 Bucket List, N-002
+# Date Builder): a feature looks like an empty/initial state but is actually non-functional
+# because a required id is never wired, a setter is never called, or saved data is never read.
+#
+# ⛔ CLAUDE: This is a LIVING tool — IMPROVE IT whenever you discover a new dead-wiring / orphan
+# failure mode. Add the new grep, keep the script self-contained + runnable from the project
+# root, write findings to stdout + SCAN_OUTPUT, and update this header with what you added.
+# Do not remove an existing check unless it is provably wrong (note why in the header).
+#
+# Usage:
+# ./scripts/wiring-scan.sh > /tmp/claude-wiring-scan-$(date +%Y%m%d).md
+# cat /tmp/claude-wiring-scan-$(date +%Y%m%d).md
+#
+# Tiers:
+# 🔴 CRITICAL — a `fun setX(...)` in a *ViewModel.kt with ZERO callers. The screen/nav never
+# pushes the value, so any op gated on it silently no-ops (N-001 class).
+# 🟠 MAJOR — a repository/data-source READ method (`fun observe*/get*/load*`) with no caller
+# in `ui/`. Data the app can write but never displays → orphan feature (N-002 class).
+# 🟡 REVIEW — a `if (x.isEmpty()/== null) return` / `?: return` bail-guard inside a *ViewModel.
+# Legitimate, BUT confirm something actually provides `x` — an un-provided guard is
+# exactly how a feature goes silently dead. Verify each by persisting real data and
+# reading it back from Firestore (admin), not by trusting the empty-state render.
+#
+# Findings are HINTS, not proofs — a flagged item can be intentional (e.g. a setter used only in a
+# @Preview, or a read method genuinely pending UI). Confirm each against live behavior + ground truth.
+#
+set -uo pipefail
+
+PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
+SRC_DIR="$PROJECT_ROOT/app/src/main/java/app/closer"
+UI_DIR="$SRC_DIR/ui"
+SCAN_OUTPUT="${1:-/tmp/claude-wiring-scan-$(date +%Y%m%d).md}"
+: > "$SCAN_OUTPUT"
+
+log() { echo "$1" | tee -a "$SCAN_OUTPUT"; }
+
+crit=0; major=0; review=0
+
+log "# Wiring / dead-feature scan — $(date '+%Y-%m-%d %H:%M')"
+log ""
+log "> Hints, not proofs. Confirm each against live behavior + a Firestore admin read."
+log ""
+
+# ── Tier 1: dead setters (CRITICAL) ───────────────────────────────────────────
+log "## 🔴 CRITICAL — ViewModel \`setX(...)\` with no caller (N-001 class)"
+log ""
+while IFS= read -r decl; do
+ file="${decl%%:*}"
+ name="$(echo "$decl" | grep -oE 'fun set[A-Z][A-Za-z0-9]*' | head -1 | sed 's/^fun //')"
+ [ -z "$name" ] && continue
+ # callers = references to `name(` or `::name` anywhere under SRC, minus the declaration itself
+ callers="$(grep -rEn "(\.|::| )${name}\(|::${name}\b" "$SRC_DIR" 2>/dev/null | grep -v "fun ${name}(" | grep -cv '^$')"
+ if [ "${callers:-0}" -eq 0 ]; then
+ log "- 🔴 \`${name}()\` — **no callers** — ${file#$PROJECT_ROOT/}"
+ crit=$((crit+1))
+ fi
+done < <(grep -rEn 'fun set[A-Z][A-Za-z0-9]*\(' "$SRC_DIR" --include=*ViewModel.kt 2>/dev/null)
+[ "$crit" -eq 0 ] && log "- none ✅"
+log ""
+
+# ── Tier 2: orphan readers (MAJOR) ────────────────────────────────────────────
+log "## 🟠 MAJOR — repository/data-source read method never called from \`ui/\` (N-002 class)"
+log ""
+# Scan REPOSITORY INTERFACES only (*Repository.kt, which the glob excludes *RepositoryImpl.kt
+# + *DataSource.kt) — those are the read entry points a VM/screen would call. A repo read method
+# with no ui/ caller means the app can fetch the data but no screen ever shows it (N-002).
+while IFS= read -r decl; do
+ file="${decl%%:*}"
+ name="$(echo "$decl" | grep -oE 'fun (observe|get|load)[A-Z][A-Za-z0-9]*' | head -1 | sed 's/^fun //')"
+ [ -z "$name" ] && continue
+ ui_callers="$(grep -rEn "(\.|::| )${name}\(|::${name}\b" "$UI_DIR" 2>/dev/null | grep -cv '^$')"
+ if [ "${ui_callers:-0}" -eq 0 ]; then
+ log "- 🟠 \`${name}()\` — **no \`ui/\` caller** (written data may never be displayed) — ${file#$PROJECT_ROOT/}"
+ major=$((major+1))
+ fi
+done < <(grep -rEn 'fun (observe|get|load)[A-Z][A-Za-z0-9]*\(' "$SRC_DIR" --include=*Repository.kt 2>/dev/null)
+[ "$major" -eq 0 ] && log "- none ✅"
+log ""
+
+# ── Tier 3: silent bail-guards in ViewModels (REVIEW) ─────────────────────────
+log "## 🟡 REVIEW — \`if (x.isEmpty()/==null) return\` / \`?: return\` bail-guards in ViewModels"
+log ""
+log "Each is a point where the feature silently does nothing if state was never initialized."
+log "Confirm something provides the value (persist real data → read it back via admin)."
+log "Auth-presence guards (\`currentUserId ?: return\`) are filtered out — they're never the wiring gap;"
+log "the risky ones gate on a STATE field a \`setX\`/resolver is supposed to populate (e.g. coupleId)."
+log ""
+while IFS= read -r hit; do
+ log "- 🟡 ${hit#$PROJECT_ROOT/}"
+ review=$((review+1))
+done < <(grep -rEn 'if \([a-zA-Z0-9_.]+\.(isEmpty\(\)|isBlank\(\))\) return|[a-zA-Z0-9_.]+ \?: return\b' "$SRC_DIR" --include=*ViewModel.kt 2>/dev/null \
+ | grep -viE 'currentUser|currentUserId|authRepository|FirebaseAuth|firstOrNull|\.find ?\{|getOrNull')
+[ "$review" -eq 0 ] && log "- none"
+log ""
+
+log "## Summary"
+log ""
+log "| Tier | Count |"
+log "|---|---|"
+log "| 🔴 CRITICAL (dead setters) | $crit |"
+log "| 🟠 MAJOR (orphan readers) | $major |"
+log "| 🟡 REVIEW (bail-guards) | $review |"
+log ""
+log "_Record these counts in ClaudeQACoverage.md under Pass N before driving the interactive features._"
+
+# Exit non-zero only on CRITICAL so CI/automation can gate on it.
+[ "$crit" -gt 0 ] && exit 1 || exit 0