docs(manual): drop dead references to files removed by the 21392ec2 dead-file sweep
The 2026-07-09 Tier 1 dead-file sweep (commit21392ec2) 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 the21392ec2commit 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 the21392ec2retirement. 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:
parent
51efff3f78
commit
4103c43393
|
|
@ -99,13 +99,13 @@ app/src/main/java/app/closer/
|
|||
│ ├── firebase/ # FirebaseInitializer (manual App Check / init path)
|
||||
│ ├── media/ # MediaCompressor (image/video compression before encrypted upload)
|
||||
│ ├── navigation/ # AppRoute constants, NavHost, ExternalLinks
|
||||
│ └── notifications/ # AppMessagingService, NotificationHelper, NotificationPermissionHelper, QuietHours, TokenRegistrar
|
||||
│ └── notifications/ # AppMessagingService, QuietHours, TokenRegistrar
|
||||
├── crypto/ # E2EE: Tink AEAD, BouncyCastle Argon2id, key stores
|
||||
├── data/
|
||||
│ ├── backup/ # BackupManager + BackupRestoreManager + RestoreManager (R24 E2EE backup + partner-assist restore)
|
||||
│ ├── challenges/ # Connection Challenges data sources
|
||||
│ ├── 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
|
||||
│ ├── repository/ # Repository implementations
|
||||
│ └── security/ # PlayIntegrityChecker
|
||||
|
|
@ -115,7 +115,7 @@ app/src/main/java/app/closer/
|
|||
│ ├── repository/ # Repository interfaces
|
||||
│ ├── security/ # AuthRateLimiter, DeviceIntegrityChecker (interface for PlayIntegrityChecker)
|
||||
│ └── 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
|
||||
├── activity/ # Together / Activity screen
|
||||
├── answers/ # Answer write/reveal/history
|
||||
|
|
@ -144,7 +144,7 @@ app/src/main/java/app/closer/
|
|||
└── 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.
|
||||
|
||||
|
|
@ -1217,7 +1217,7 @@ SCRIPTS.md
|
|||
### Logging
|
||||
|
||||
- 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.
|
||||
|
||||
### 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`
|
||||
**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.
|
||||
**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).
|
||||
|
||||
### B-ABANDON-001 - never UPDATE an existing session via the full-document `saveSession()`; the rule rejects dropped server-only keys
|
||||
|
|
|
|||
Loading…
Reference in New Issue