docs(manual): drop dead references to files removed by the 21392ec2 dead-file sweep

The 2026-07-09 Tier 1 dead-file sweep (commit 21392ec2) removed
8 files the manual still cited. Each was verified unreferenced by
symbol-level git-grep + a clean :app:compileDebugKotlin + unit-test
compile. This patch aligns the manual with the post-sweep source.

Five stale references fixed:

1. Android Repository layout (line 102): dropped NotificationHelper +
   NotificationPermissionHelper from the core/notifications/ file
   list. Both removed (dead duplicates of NotificationChannelSetup).

2. Android Repository layout (line 108): dropped the data/questions/
   package from the layout. The directory is gone (QuestionJsonParser
   was the only file; the path is now empty + dead).

3. Android Repository layout (line 118): dropped PartnerNotificationScheduler
   from the top-level notifications/ package list. Removed as
   superseded by PartnerNotificationManager.

4. 'Note on the manual's older description' paragraph (line 147):
   rewritten to record the swept files (NotificationHelper,
   NotificationPermissionHelper, PartnerNotificationScheduler, the
   domain/model/ Entitlment.kt / InviteStatus.kt / QuestionSessionStatus.kt
   trio, and the data/questions/ package) with one-line reasons
   pulled from the 21392ec2 commit body, and to point QuestionDao at
   data/local/QuestionDao.kt as the now-canonical question-content
   source.

5. Logging section (line 1220): replaced the dead QuestionJsonParser
   BuildConfig.DEBUG example pointer with the live
   analytics/CompositeRetentionAnalytics.kt (one of the few current
   Android files that still guards android.util.Log with
   BuildConfig.DEBUG). The historical QJP pointer is preserved as a
   one-line breadcrumb so the next reader knows why it changed.

6. R23-DQ-001 landmine (line 1355): the parenthetical said
   'QuestionDetailViewModel (pack questions) is local-only and not
   affected' - QuestionDetailViewModel is gone, pack questions are
   now read directly by QuestionPackLibraryViewModel. Updated the
   parenthetical + added a one-line breadcrumb pointing at the
   21392ec2 retirement.

Anchor check: 30/30 internal anchors still resolve (Python GFM slug
checker).

Deliberately NOT in this commit:
- No mention of the CloserCopy catalog added - that's a documentation
  addition, not a stale-reference fix, and belongs in its own batch
  (the brand/ package is currently undocumented in the manual; a
  future pass should add CloserBrandCopy + CloserCopy to the
  Android Repository layout).
- No mention of the strings.xml purge (59e03369) - the manual
  doesn't reference any specific string resources, so the purge is
  invisible to the manual.
This commit is contained in:
null 2026-07-09 00:29:26 -05:00
parent 51efff3f78
commit 4103c43393
1 changed files with 6 additions and 6 deletions

View File

@ -99,13 +99,13 @@ app/src/main/java/app/closer/
│ ├── firebase/ # FirebaseInitializer (manual App Check / init path) │ ├── firebase/ # FirebaseInitializer (manual App Check / init path)
│ ├── media/ # MediaCompressor (image/video compression before encrypted upload) │ ├── media/ # MediaCompressor (image/video compression before encrypted upload)
│ ├── navigation/ # AppRoute constants, NavHost, ExternalLinks │ ├── navigation/ # AppRoute constants, NavHost, ExternalLinks
│ └── notifications/ # AppMessagingService, NotificationHelper, NotificationPermissionHelper, QuietHours, TokenRegistrar │ └── notifications/ # AppMessagingService, QuietHours, TokenRegistrar
├── crypto/ # E2EE: Tink AEAD, BouncyCastle Argon2id, key stores ├── crypto/ # E2EE: Tink AEAD, BouncyCastle Argon2id, key stores
├── data/ ├── data/
│ ├── backup/ # BackupManager + BackupRestoreManager + RestoreManager (R24 E2EE backup + partner-assist restore) │ ├── backup/ # BackupManager + BackupRestoreManager + RestoreManager (R24 E2EE backup + partner-assist restore)
│ ├── challenges/ # Connection Challenges data sources │ ├── challenges/ # Connection Challenges data sources
│ ├── local/ # Room DAOs, DataStore, EncryptedSharedPreferences (RecoveryPhraseStore, SecurePreferencesFactory, SettingsDataStore); also converters/, entity/, mapper/ subdirs │ ├── local/ # Room DAOs, DataStore, EncryptedSharedPreferences (RecoveryPhraseStore, SecurePreferencesFactory, SettingsDataStore); also converters/, entity/, mapper/ subdirs
│ ├── questions/ # Question pack data sources (`QuestionJsonParser`) │ ├── (no `questions/` package — JSON parsing was retired; see note below)
│ ├── remote/ # Firestore data sources, Cloud Functions callable wrappers │ ├── remote/ # Firestore data sources, Cloud Functions callable wrappers
│ ├── repository/ # Repository implementations │ ├── repository/ # Repository implementations
│ └── security/ # PlayIntegrityChecker │ └── security/ # PlayIntegrityChecker
@ -115,7 +115,7 @@ app/src/main/java/app/closer/
│ ├── repository/ # Repository interfaces │ ├── repository/ # Repository interfaces
│ ├── security/ # AuthRateLimiter, DeviceIntegrityChecker (interface for PlayIntegrityChecker) │ ├── security/ # AuthRateLimiter, DeviceIntegrityChecker (interface for PlayIntegrityChecker)
│ └── usecase/ # Cross-VM use cases: DailyQuestionResolver, GameSessionManager, SoloAnswerMigrator │ └── usecase/ # Cross-VM use cases: DailyQuestionResolver, GameSessionManager, SoloAnswerMigrator
├── notifications/ # Notification surfaces: ActiveGameSessionMonitor, GamePromptController, PartnerNotificationManager, PartnerNotificationScheduler, QuietHoursManager, NotificationChannelSetup, NotificationRateLimiter, MessageBubbleController, ActiveThreadMonitor ├── notifications/ # Notification surfaces: ActiveGameSessionMonitor, GamePromptController, PartnerNotificationManager, QuietHoursManager, NotificationChannelSetup, NotificationRateLimiter, MessageBubbleController, ActiveThreadMonitor
└── ui/ # Compose screens + ViewModels └── ui/ # Compose screens + ViewModels
├── activity/ # Together / Activity screen ├── activity/ # Together / Activity screen
├── answers/ # Answer write/reveal/history ├── answers/ # Answer write/reveal/history
@ -144,7 +144,7 @@ app/src/main/java/app/closer/
└── wheel/ # Spin the wheel └── wheel/ # Spin the wheel
``` ```
**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`. **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 question content is loaded from the bundled Room/asset-DB (`QuestionDao` in `data/local/QuestionDao.kt`) — the old `data/questions/QuestionJsonParser.kt` was retired by commit `21392ec2` (Tier 1 dead-file sweep, 2026-07-09). The `core/notifications/` package no longer contains `NotificationHelper.kt` or `NotificationPermissionHelper.kt` (both removed by the same sweep as dead duplicates of `NotificationChannelSetup`); the `notifications/` package no longer contains `PartnerNotificationScheduler.kt` (superseded by `PartnerNotificationManager`); and the `domain/model/` package no longer contains `Entitlement.kt`, `InviteStatus.kt`, or `QuestionSessionStatus.kt` (entitlement state is read as Firestore booleans, not a domain model). All removals were verified with symbol-level git-grep + a clean `:app:compileDebugKotlin` + `compileDebugUnitTestKotlin`.
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. 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.
@ -1217,7 +1217,7 @@ SCRIPTS.md
### Logging ### Logging
- Cloud Functions prefix every log line with the function name: `[acceptInviteCallable] ...`. - Cloud Functions prefix every log line with the function name: `[acceptInviteCallable] ...`.
- Android production builds must not log secrets, recovery phrases, keyset bytes, or invite codes. Wrap `android.util.Log` calls in `BuildConfig.DEBUG` guards (see `app/src/main/java/app/closer/data/questions/QuestionJsonParser.kt` for an example). - Android production builds must not log secrets, recovery phrases, keyset bytes, or invite codes. Wrap `android.util.Log` calls in `BuildConfig.DEBUG` guards (see `app/src/main/java/app/closer/analytics/CompositeRetentionAnalytics.kt` for a current example). The historical reference `data/questions/QuestionJsonParser.kt` is gone (retired in commit `21392ec2`); the same guard pattern is used wherever logging survives the dead-file sweep.
- Crashlytics is the production observability path. Do not log to both Crashlytics and console in production. - Crashlytics is the production observability path. Do not log to both Crashlytics and console in production.
### Error handling ### Error handling
@ -1352,7 +1352,7 @@ OOB code = `truncate6(SHA-256(pubkey ‖ nonce))`.
### R23-DQ-001 - sourcing "already answered?" from local Room only → silent re-answer data loss against the immutable `secure/payload` ### R23-DQ-001 - sourcing "already answered?" from local Room only → silent re-answer data loss against the immutable `secure/payload`
**Symptom (R23)**: on a device whose local answer store was empty while Firestore still held the user's daily answer (fresh device / reinstall with cleared data / wiped prefs), Home showed a stale **"your turn"** and the daily-question screen offered an **editable re-answer form**. Submitting logged `Write failed at couples/{id}/daily_question/{date}/answers/{uid}/secure/payload: PERMISSION_DENIED` (swallowed). If the user picked a *different* answer it was **silently lost** - the `secure/payload` doc is immutable (`allow update: if false`), so the overwrite is denied and the reveal keeps the *old* content while the UI claimed "saved". **Symptom (R23)**: on a device whose local answer store was empty while Firestore still held the user's daily answer (fresh device / reinstall with cleared data / wiped prefs), Home showed a stale **"your turn"** and the daily-question screen offered an **editable re-answer form**. Submitting logged `Write failed at couples/{id}/daily_question/{date}/answers/{uid}/secure/payload: PERMISSION_DENIED` (swallowed). If the user picked a *different* answer it was **silently lost** - the `secure/payload` doc is immutable (`allow update: if false`), so the overwrite is denied and the reveal keeps the *old* content while the UI claimed "saved".
**Root cause**: `DailyQuestionViewModel.loadDailyQuestion` and `HomeViewModel` derived answered-state from **local Room/prefs only** (`localAnswerRepository.getAnswer` / `observeAnswers``answeredQuestionIds`), with no fallback to Firestore. Room and Firestore can legitimately diverge (Auth + Firestore persist across a reinstall; the local answer store does not), so the app offered an action the rules forbid. **Root cause**: `DailyQuestionViewModel.loadDailyQuestion` and `HomeViewModel` derived answered-state from **local Room/prefs only** (`localAnswerRepository.getAnswer` / `observeAnswers``answeredQuestionIds`), with no fallback to Firestore. Room and Firestore can legitimately diverge (Auth + Firestore persist across a reinstall; the local answer store does not), so the app offered an action the rules forbid.
**Fix (R23)**: `reconcileLocalAnswerFromFirestore` ([`ui/questions/LocalAnswerMapping.kt`](../app/src/main/java/app/closer/ui/questions/LocalAnswerMapping.kt)) - **Room-first** (returns the local answer immediately when present, so the common path is unchanged and network-free); otherwise reads the answer metadata + decrypts the owner's own read-gated couple-key payload, rebuilds the `LocalAnswer` (mapping option-texts), and **writes it back to Room** (only when it actually decrypts, so a transient key-miss never poisons Room). Wired into `DailyQuestionViewModel.loadDailyQuestion` (awaited → screen shows submitted/reveal) and `HomeViewModel.loadHome` (non-blocking heal → no stale "your turn"). Covered by `ReconcileLocalAnswerTest` (5 branches). `QuestionDetailViewModel` (pack questions) is **local-only** and not affected. **Fix (R23)**: `reconcileLocalAnswerFromFirestore` ([`ui/questions/LocalAnswerMapping.kt`](../app/src/main/java/app/closer/ui/questions/LocalAnswerMapping.kt)) - **Room-first** (returns the local answer immediately when present, so the common path is unchanged and network-free); otherwise reads the answer metadata + decrypts the owner's own read-gated couple-key payload, rebuilds the `LocalAnswer` (mapping option-texts), and **writes it back to Room** (only when it actually decrypts, so a transient key-miss never poisons Room). Wired into `DailyQuestionViewModel.loadDailyQuestion` (awaited → screen shows submitted/reveal) and `HomeViewModel.loadHome` (non-blocking heal → no stale "your turn"). Covered by `ReconcileLocalAnswerTest` (5 branches). Pack-question loading (now via `QuestionPackLibraryViewModel` + bundled Room) is **local-only** and not affected. Note: the older landmine draft referenced `QuestionDetailViewModel` for pack questions, but that VM was retired by commit `21392ec2` (Tier 1 dead-file sweep) - pack questions are now read directly by `QuestionPackLibraryViewModel`.
**Re-introduction risk**: any UI that decides whether to offer a **submit-once / immutable** write (daily answers, date reflections, anything with a read-gated `secure/payload` whose `allow update:false`) must treat **Firestore as authoritative for existence**, not local cache - a local-only check re-offers the action after the local DB is lost and the immutable rule rejects the re-write silently. The date feature already does this (its `hasReflected` reads Firestore). When adding a new private→reveal collection, reconcile from Firestore on load and verify the fresh-device path (Pass N **R23-DQ-001** check), since `pm clear` (the faithful repro) is blocked in our emulator setup (App Check token). **Re-introduction risk**: any UI that decides whether to offer a **submit-once / immutable** write (daily answers, date reflections, anything with a read-gated `secure/payload` whose `allow update:false`) must treat **Firestore as authoritative for existence**, not local cache - a local-only check re-offers the action after the local DB is lost and the immutable rule rejects the re-write silently. The date feature already does this (its `hasReflected` reads Firestore). When adding a new private→reveal collection, reconcile from Firestore on load and verify the fresh-device path (Pass N **R23-DQ-001** check), since `pm clear` (the faithful repro) is blocked in our emulator setup (App Check token).
### B-ABANDON-001 - never UPDATE an existing session via the full-document `saveSession()`; the rule rejects dropped server-only keys ### B-ABANDON-001 - never UPDATE an existing session via the full-document `saveSession()`; the rule rejects dropped server-only keys