docs(plan): rewrite Engineering_Reference_Manual_Plan for Phase 3 (13-batch re-review)

Reset the plan file from the v0.2.1 + Phase 2 single-pass state to a new
Phase 3 batch plan covering a full evidence-first re-review of the manual.
13 batches, one per major section of docs/Engineering_Reference_Manual.md.
This file is the working log for the Phase 3 review; per-batch findings
get appended as each batch completes.

Batch 1 (iOS platform row 'E2EE not yet implemented' was stale) has
already shipped as ef5a2331 - this commit just lays out the plan for
batches 2-13.
This commit is contained in:
null 2026-07-08 23:18:54 -05:00
parent ef5a2331fd
commit f5194a4453
1 changed files with 102 additions and 89 deletions

View File

@ -1,113 +1,126 @@
# Engineering Reference Manual Update Plan # Engineering Reference Manual Review Plan — Phase 3 (2026-07-08)
**Status: ✅ COMPLETE — v0.2.1 phase (2026-06-28, 9 batches) + ✅ v0.2.x phase (2026-07-08, single comprehensive pass)** **Status: 🚧 IN PROGRESS** — full evidence-first re-review of `docs/Engineering_Reference_Manual.md`.
Goal: bring `docs/Engineering_Reference_Manual.md` into alignment with the current Triggered by `_null` after the Phase 2 sync (`213cfddb`) + review pass (`d31b58d2`).
relationship-app codebase. Verify every claim, file path, function name, Goal: cross-check every claim in the manual against the live repo + `firestore.rules`
collection name, and architectural fact. Never assume. + `functions/src/` + iOS source + Android source, batch by batch. No skipped sections.
## Phase 2 (2026-07-08) — single comprehensive pass
Triggered by the 2026-07-08 Cloud Functions v2 deploy (B0B6d, Node 22 bump, 5 per-game part-finished triggers) + the R30 games/UX/seed round + the `b99a8338` RevenueCat↔Firebase-Auth identity link. Single comprehensive pass rather than the original 9-batch plan because the changes were concentrated in three areas (Cloud Functions, Billing, Daily question) and the manual had drifted in known directions.
### What changed (Phase 2)
- **Cloud Functions** — replaced the stale functions tree with the v2 layout (added `options.ts`, `log.ts`, the `push.ts`/`quietHours.ts`/`idempotency.ts`/`pruneTokens.ts`/`time.ts` shared infra, `releaseKey/`, `backup/`, `dates/onDate*`, `couples/aggregateOutcomes`, `notifications/sendStreakReminder` + `sendThinkingOfYouCallable`). Replaced `onGamePartFinished` (single) with the four per-game part-finished triggers (`onThisOrThatPartFinished`, `onWheelPartFinished`, `onHowWellPartFinished`, `onDesireSyncPartFinished`). Added the v2-migration deploy-failure pattern landmine (`FUNCTIONS-V2-DEPLOY`). Webhook reliability section now states the webhook is **not deployed** (RevenueCat project not yet created).
- **Daily question lifecycle** — replaced the "picks a random active free question" description with the server-authoritative, mode-aware, deterministic picker that mirrors the client's `DailyModeResolver`; added a new sub-section `### Server-authoritative mode-aware deterministic selection` with the frozen DOW → mode map; noted the picker is FROZEN against `daily_fun_mc` for HowWell/Desire Sync pool hygiene.
- **Billing** — added the `Purchases.logIn(firebaseAuth.currentUser!!.uid)` identity-link flow (commit `b99a8338`) and the `BillingException` typed error mapping; added a "Currently NOT deployed" callout on the webhook section that points to the Webhook reliability deploy steps.
- **iOS section** — corrected the Repository layout iOS `Crypto/` block (it is no longer empty; ships the R24 E2EE code) and the "pairing from iOS currently fails" claim (it does not, for the schemaVersion 2 path; schemaVersion 3 is still infrastructure-gated per `IOS_E2EE_STATUS.md`).
- **TOC + anchors** — added the new sub-anchors (`server-authoritative-mode-aware-deterministic-selection`, `webhook-reliability`, `schedule`, `server-verified-entitlements`, `webhook`, `premium-gated-features-and-gate-pattern`); fixed three pre-existing broken anchors (`#foreground-game-alert-banner-r10`, `#ios--android-sealed-answer-bridge-wrapreleasekeycallable`, `#recovery-phrase-change-desync--...`).
- **New landmines**`BANNER-LIFE-001` (R30 game banner lifecycle) + `FUNCTIONS-V2-DEPLOY` (2nd-gen deploy / CPU-quota / Eventarc propagation pattern).
### What was NOT changed (Phase 2 — deliberately)
- The Android Gradle config drift (`versionCode = 1`, `versionName = "0.1.0"` in `app/build.gradle.kts` while HISTORY.md describes v0.2.1) is already flagged in the manual ("build config has drifted from HISTORY; do not ship a release until they're reconciled"). Bumping the version is a release-readiness task, not a docs task.
- The iOS E2EE section (sub-section "iOS E2EE gap (status as of current batch)") was already accurate after the R24 work; left unchanged.
- The "Files that must never be committed" gitignore + the iOS CryptoKit guidance section were both already correct.
## Approach ## Approach
- **Batch-based.** Each batch covers one major section or a coherent set of - **13 batches**, each scoped to one major section of the manual.
subsections. One agent/turn per batch. - **Evidence-first.** For every factual claim (file path, function name, collection
- **Evidence-first.** For every factual claim in the manual, verify against the name, cron expression, preference field, threshold, AAD string), verify against
repo (grep, read file, run build/test if needed). the live source.
- **Log everything.** Each batch updates this plan with findings and - **One commit per batch** per the standing git discipline (one batch = one commit = one push).
changes made. - **Log findings to this file** as each batch completes.
- **Ripley coordinates.** Ripley does not code unless necessary; Bishop or - **Anchors + TOC** verified at the end (Batch 13), not per-batch, to avoid churn.
subagents may verify builds/docs.
## Batches ## Batches
### Batch 1 — Front matter + System overview + Repository layout ### Batch 1 — Front matter + System overview + Repository layout (Android + iOS + Cloud Functions + Shared configuration)
- Read: `## How to use this document` through `### Shared configuration`. - Read: `## How to use this document` through `### Shared configuration` (lines ~1-260).
- Verify all directory paths under `app/src/main/java/app/closer/`, - Verify all directory paths under `app/src/main/java/app/closer/`,
`functions/src/`, `functions/dist/`, `res/drawable-*`. `functions/src/`, `iphone/Closer/`, `res/drawable-*`, Hilt module list.
- Cross-check Android package list against the live `find app/src/main/java/app/closer -maxdepth 3 -type d`.
- Cross-check iOS file list against `ls iphone/Closer/...`.
- Cross-check Cloud Functions tree against `find functions/src -name "*.ts"`.
- Flag missing or renamed directories/files. - Flag missing or renamed directories/files.
- Update any stale paths or naming.
### Batch 2 — Authentication, pairing, couples model ### Batch 2 — Authentication + pairing + couples doc model + rate limit + recovery
- Read: `## Authentication and pairing flow` through `### Key Cloud Functions`. - Read: `## Authentication and pairing flow` (lines ~270-370).
- Verify auth files, pairing flow files, `couples` collection fields, - Verify auth files in `data/remote/FirebaseAuthDataSource.kt`,
recovery phrase implementation. `data/repository/FirebaseAuthRepositoryImpl.kt`, `domain/repository/AuthRepository.kt`.
- Cross-check `firestore.rules` for the rules described. - Verify `couples/{coupleId}` field list against `acceptInviteCallable.ts` + the
- Update if code diverges from doc. domain `Couple.kt` model.
- Verify rate-limit env values against `AuthRateLimiter.kt`.
- Verify recovery phrase flow against `RecoveryKeyManager.kt` + `CoupleEncryptionManager.kt`.
### Batch 3 — E2EE model + Firestore data model ### Batch 3 — E2EE model (versions, Argon2id, Tink, sealed-answer, ECIES, single-device, iOS bridge)
- Read: `## End-to-end encryption model` and `## Firestore data model`. - Read: `## End-to-end encryption model` (lines ~370-475).
- Verify crypto files, encryption versions, collection schemas, field names. - Verify each `Encryption versions` row against `firestore.rules` regex helpers.
- Check `firestore.rules` regex helpers and invariants. - Verify the Argon2id v1.3 parameter names + the AAD strings against the actual
- Update data model tables if collections/fields changed. Kotlin/Swift code.
- Verify the `keybox:v1:` wire format against `Keybox.swift` + `ReleaseKeyEncryptor.kt`.
- Verify the `wrapReleaseKeyCallable` path against the function source.
### Batch 4 — Daily question lifecycle + Cloud Functions module ### Batch 4 — Daily question lifecycle + Reveal flow + Thread questions
- Read: `## Daily question lifecycle` and `## Cloud Functions`. - Read: `## Daily question lifecycle` (lines ~480-575).
- Verify function triggers, handlers, schedules, module responsibilities. - Verify `assignDailyQuestion` schedule + the deterministic picker
- Cross-check `functions/src/` tree. (DOW → mode map, daily_fun_mc exclusion) against
- Update if functions were added/removed/renamed. `functions/src/questions/assignDailyQuestion.ts` + `DailyModeResolver.kt`.
- Verify the schemaVersion 2 reveal flow + the read-gated `secure/payload` subdoc
against `firestore.rules` (the `isCoupleAnswerCreate` + `isEncryptedPayloadUpdate`
helpers).
- Verify schemaVersion 3 sealed flow against `SealedAnswerEncryptor.kt` +
`isSealedAnswerCreate` rules.
- Verify thread questions path against `isSealedThreadAnswerCreate` + the model.
### Batch 5 — Firestore security rules ### Batch 5 — Firestore security rules
- Read: `## Firestore security rules`. - Read: `## Firestore security rules` (lines ~720-810).
- Line-by-line verify against `firestore.rules`. - Line-by-line verify every per-collection note in the manual against `firestore.rules`.
- Cross-check helper-function names (`coupleEncryptionEnabled`, `isCiphertext`,
`isUpdatingCoupleRhythm`, `isUpdatingRecoveryWrap`, `isCoupleAnswerCreate`,
`isEncryptedPayloadUpdate`, `isSealedAnswerCreate`, `isSealedAnswerUpdate`,
`isSealedThreadAnswerCreate`, `isSealedThreadAnswerUpdate`).
- Note any rules in the file not documented, or documented rules not in file. - Note any rules in the file not documented, or documented rules not in file.
### Batch 6 — Billing + Notifications ### Batch 6 — Cloud Functions
- Read: `## Billing` and `## Notifications`. - Read: `## Cloud Functions` (lines ~815-845).
- Verify RevenueCat integration files, entitlement checks, webhook handler. - Verify function triggers, handlers, schedules, module responsibilities.
- Verify notification classes, quiet hours, deep-link routing. - Cross-check `functions/src/` tree (already done in Batch 1 but verify exports).
- Update for quiet-hours server-side suppression if doc still says local-only. - Cross-check Schedule table against each function's source.
### Batch 7 — iOS-specific + Build and release ### Batch 7 — Billing
- Read: `## iOS-specific notes` and `## Build and release`. - Read: `## Billing` (lines ~850-920).
- Verify iOS project state (is it still broken? still exists?). - Verify RevenueCat integration files, entitlement checkers, webhook handler,
- Verify build commands, secrets, ProGuard, Functions deploy commands. Purchases.logIn identity link.
- Update stale commands or iOS status. - Cross-check the "Gated features" list against the actual ViewModels that
inject `CouplePremiumChecker` / `EntitlementChecker`.
### Batch 8 — Known landmines and recent fixes (cross-cutting) ### Batch 8 — Notifications
- Read: `## Known landmines and recent fixes`. - Read: `## Notifications` (lines ~925-1020).
- Verify each listed issue ID and fix matches git history and current code. - Verify FCM setup + tokenRegistrar + quiet hours.
- Add any missing recent landmines (e.g., theme scanner, quiet hours server-side). - Verify each reminder callable's actual schedule + dedupe.
- Prune obsolete entries. - Verify game-push semantics against `onGameSessionUpdate.ts` + the
part-finished triggers.
- Verify the foreground banner surface + the deep-link routing table against
`PartnerNotificationManager.kt`.
### Batch 9 — Final review, TOC update, formatting ### Batch 9 — iOS-specific notes
- Update `## Table of Contents` if headings changed. - Read: `## iOS-specific notes` (lines ~1023-1080).
- Run markdown lint / build check if applicable. - Verify Architecture, CloserTheme tokens, XcodeGen setup, build commands,
- Final pass for consistency. E2EE gap status, CryptoKit guidance.
- Cross-check `iphone/Closer/Theme/CloserTheme.swift` for the actual color values.
## Status tracking ### Batch 10 — Build and release
- Read: `## Build and release` (lines ~1080-1200).
- Verify Android module/package, biometric recovery phrase reveal, required
build secrets, Gradle config, ProGuard rules.
- Cross-check the iOS build commands against the actual XcodeGen spec.
| Batch | Status | Findings | Changes made | ### Batch 11 — Engineering conventions
|---|---|---|---| - Read: `## Engineering conventions` (lines ~1200-1280).
| 1 | ✅ done | `core/feature/` note inaccurate (dir doesn't exist); `data/questions/` listed `QuestionDao` but it's in `data/local/` | Corrected `core/feature/` note; moved `QuestionDao` to `data/local/`; kept `QuestionJsonParser` in `data/questions/`; updated older-description note. | - Verify the architectural rules (Clean layers, package boundaries, what
| 2 | ✅ done | No anonymous sign-in or account linking in code; Android uses legacy Google Sign-In SDK (idToken), not Credential Manager; `encryptionMigrationUsers` field does not exist | Removed anonymous auth and account-linking claims; corrected Google Sign-In description; removed `encryptionMigrationUsers` from couples model and added note that `encryptionVersion` is always `2`. | lives where) against the actual code.
| 3 | ✅ done | `/users/{uid}` model missing `sex`, `partnerId`, `plan`, `lastActiveAt`, notification prefs, quiet-hours, `fcmToken`; `hasPremium` is not a real root field (premium lives in `/entitlements/premium`); `/couples/{coupleId}` listed non-existent `encryptionMigrationUsers`; date plan fields wrong; date plan preference fields wrong; bucket list fields wrong; missing `/answers/{userId}/secure/{doc}` for schemaVersion 2 | Updated `/users/{uid}` to full allowlist; removed `hasPremium` root field and added note about `/entitlements/premium`; removed `encryptionMigrationUsers`; corrected date plan, preference, and bucket list fields; added `secure` subdoc to daily-question model. | - Verify the "what NOT to do" list (e.g. raw Firestore listeners, no
| 4 | ✅ done | Handler table missing functions (`syncEntitlement`, `sendDailyQuestionProactiveReminder`, `sendReengagementReminder`, `sendChallengeDayReminders`, `unlockDueMemoryCapsules`, `sendGentleReminderCallable`, `onEntitlementChanged`, `onAnswerRevealed`, `onCoupleLeave`, `leaveCoupleCallable`, `submitOutcomeCallable`, `scheduledOutcomesReminder`, `onGamePartFinished`, `notifyOnDateMatch`); still listed removed `health` endpoint and old name `createDateMatchOnMutualLove`; webhook section said 200-ack-before-process (now process-before-ack); `scheduledOutcomesReminder` timezone wrong | Removed `health`; added all current functions and corrected `notifyOnDateMatch`; updated module responsibilities; corrected webhook to process-before-ack with 500-on-failure; fixed schedule timezones (only `assignDailyQuestion`, `sendDailyQuestionProactiveReminder`, `sendReengagementReminder` use `America/Chicago`). | `FirebaseAuth.getInstance()` in VMs) is still accurate.
| 5 | ✅ done | `users/{uid}` section still described non-existent `hasPremium` root field; `couples/{coupleId}` section said all client writes denied (create is shape-restricted but possible); helper paragraph listed non-existent `isStartingEncryptionMigration`/`isCompletingOwnEncryptionMigration` | Updated `users/{uid}` to clarify `plan` is client-written and premium lives in `/entitlements/premium`; corrected couples create/update description; removed migration-helper references. | - Check the version drift (build.gradle vs HISTORY.md) and ProGuard note.
| 6 | ✅ done | Billing Webhook flow still said "ack 200 then process" (process-before-ack was fixed in Batch 4 but Billing section had stale duplicate); missing `CouplePremiumChecker`; Notifications said quiet-hours server-side suppression not implemented (it is, via `recipientInQuietHours`); Notifications said `notification_queue` reads denied for clients (Android reads it for Together feed); `onEntitlementChanged` handler table description said it mirrors `plan` (it notifies partner instead) | Updated Billing webhook flow; added `CouplePremiumChecker` note; corrected quiet-hours to server-side-implemented; corrected `notification_queue` read claim; fixed `onEntitlementChanged` description. |
| 7 | ✅ done | iOS E2EE gap paragraph said iOS create invite writes an invite with null E2EE fields (server now rejects empty data, so no invite is created); ProGuard section claimed Tink reflection paths are kept (they are not in current rules) | Updated iOS create-invite consequence to "rejected by server"; clarified ProGuard does not currently keep Tink. |
| 8 | ✅ done | Theme landmine entry said C-ART-EDGE-002 still open (R13 fixed it); missing the mandatory `theme-scan.sh` Pass C pre-check | Updated theme landmine to mark C-ART-EDGE-002 closed and document `scripts/theme-scan.sh` as mandatory pre-check. |
| 9 | ✅ done | TOC incorrectly nested `Premium-gated features` under Notifications; broken anchor `[iOS E2EE gap](#ios-e2ee-gap)`; Repository layout still claimed iOS creates v0 couples | Removed misplaced TOC nesting; fixed broken anchor; corrected iOS Repository layout claim to "pairing fails". |
## Notes ### Batch 12 — Known landmines and recent fixes
- Read: `## Known landmines and recent fixes` (lines ~1280-1450).
- Verify EVERY landmine still applies:
- Does the file/component it cites still exist?
- Is the fix still in place?
- Is the "Re-introduction risk" description still accurate?
- Look for any landmine whose fix is now in a different file.
- If a section is found to be mostly correct, still spot-check a few claims. ### Batch 13 — Where to look first
- If a section is wildly out of date, rewrite it and note the divergence. - Read: `## Where to look first` (lines ~1450-end).
- Commit after each batch (per Git Discipline Rules). - Verify every file path still exists.
- Verify the "what to read first for a new area" list still applies to the
current code.
### Final pass
- TOC integrity + every anchor resolves.
- One final commit if any TOC/anchor cleanup is needed.