refactor: complete Phoenix rebrand with safe persisted-file migration - #711
Conversation
- Rename iOS project: VitruvianPhoenix → PhoenixApp (directory, xcodeproj, scheme, swift, entitlements) - Rename Android: VitruvianApp → PhoenixApp, VitruvianTheme → PhoenixTheme - Rename SQLDelight: VitruvianDatabase.sq → PhoenixDatabase.sq - Update settings.gradle.kts root project name: ProjectPhoenix - Update AndroidManifest: Theme.PhoenixProject, .PhoenixApp - Replace all icon assets with new Phoenix logo - Scrub all Vitruvian/vitphoe references from source, tests, docs, CI workflows - Verified: grep -ri vitruvian = 0, grep -ri vitphoe = 0 - Builds verified: SQLDelight interface gen, Kotlin Android/iOS, Xcode device build
- Revert DATABASE_NAME to 'vitruvian.db' (Android + iOS) to preserve existing user data on upgrade - Revert Android preference filenames to 'vitruvian_secure_preferences' / 'vitruvian_preferences' - Update SQLDelight fault injection statement IDs to match regenerated PhoenixDatabase identifiers - Add opaque black background to iOS AppIcon (Apple requires no transparent corners) - Remove accidentally committed HTML file (failed Google Drive download)
…ed-file-migration # Conflicts: # .github/ISSUE_TEMPLATE/bug_report.yml # ANDROID_INSTALL.md # androidApp/src/main/res/xml/backup_rules.xml # androidApp/src/main/res/xml/data_extraction_rules.xml # docs/index.html # docs/superpowers/specs/2026-06-16-per-cable-weight-handling-design.md # iOS_INSTALL.md # iosApp/README.md # iosApp/TESTFLIGHT_NOTES.txt # shared/src/androidHostTest/kotlin/com/devil/phoenixproject/data/repository/SqlDelightSyncRepositoryTest.kt # shared/src/androidMain/kotlin/com/devil/phoenixproject/presentation/screen/WorkoutTabPreviews.kt # shared/src/androidMain/kotlin/com/devil/phoenixproject/util/CsvExporter.android.kt # shared/src/androidMain/kotlin/com/devil/phoenixproject/util/DataBackupManager.android.kt # shared/src/androidMain/kotlin/com/devil/phoenixproject/util/DeviceInfo.android.kt # shared/src/commonMain/composeResources/values-de/strings.xml # shared/src/commonMain/composeResources/values-es/strings.xml # shared/src/commonMain/composeResources/values-fr/strings.xml # shared/src/commonMain/composeResources/values-nl/strings.xml # shared/src/commonMain/composeResources/values/strings.xml # shared/src/commonMain/kotlin/com/devil/phoenixproject/data/ble/BleCompatibilityMode.kt # shared/src/commonMain/kotlin/com/devil/phoenixproject/data/ble/BleExceptions.kt # shared/src/commonMain/kotlin/com/devil/phoenixproject/data/ble/DiagnosticFaultDecoder.kt # shared/src/commonMain/kotlin/com/devil/phoenixproject/data/ble/KableBleConnectionManager.kt # shared/src/commonMain/kotlin/com/devil/phoenixproject/data/ble/MetricPollingEngine.kt # shared/src/commonMain/kotlin/com/devil/phoenixproject/data/local/LegacyCatalogueIdMap.kt # shared/src/commonMain/kotlin/com/devil/phoenixproject/data/migration/MigrationManager.kt # shared/src/commonMain/kotlin/com/devil/phoenixproject/data/repository/ConnectionLogRepository.kt # shared/src/commonMain/kotlin/com/devil/phoenixproject/presentation/components/ConnectingOverlay.kt # shared/src/commonMain/kotlin/com/devil/phoenixproject/presentation/screen/SetSummaryCard.kt # shared/src/commonMain/kotlin/com/devil/phoenixproject/presentation/screen/SplashScreen.kt # shared/src/commonMain/kotlin/com/devil/phoenixproject/presentation/viewmodel/DiagnosticsViewModel.kt # shared/src/commonMain/kotlin/com/devil/phoenixproject/util/BleConstants.kt # shared/src/commonMain/kotlin/com/devil/phoenixproject/util/BlePacketFactory.kt # shared/src/commonMain/kotlin/com/devil/phoenixproject/util/DataBackupManager.kt # shared/src/commonMain/kotlin/com/devil/phoenixproject/util/HardwareDetection.kt # shared/src/commonTest/kotlin/com/devil/phoenixproject/data/ble/DiagnosticFaultDecoderTest.kt # shared/src/commonTest/kotlin/com/devil/phoenixproject/data/ble/ProtocolParserTest.kt # shared/src/commonTest/kotlin/com/devil/phoenixproject/data/integration/HealthDataMappingTest.kt # shared/src/iosMain/kotlin/com/devil/phoenixproject/util/DeviceInfo.ios.kt # third_party/kable-core-android-patched/README.md # third_party/kable-core-android-patched/src/androidMain/BluetoothGatt.kt
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: bd3a7fc6b2
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
|
||
| companion object { | ||
| const val CHANNEL_ID = "vitruvian_workout_channel" | ||
| const val CHANNEL_ID = "phoenix_workout_channel" |
There was a problem hiding this comment.
Keep the released notification channel ID
On Android upgrades, changing CHANNEL_ID creates a new notification channel rather than renaming the existing one. Users' per-channel choices—especially disabling workout notifications, importance, sound, and vibration—remain attached to vitruvian_workout_channel, while foreground-service notifications use the newly enabled default channel. Retain the released ID and change only the channel's display name, or explicitly migrate the old channel behavior.
Useful? React with 👍 / 👎.
| return | ||
| when (resolution) { | ||
| is StartupDependencyResolution.Failed -> { | ||
| Logger.e { "iOS app dependency resolution blocked: ${resolution.diagnosticCode}" } |
There was a problem hiding this comment.
Re-run Koin initialization from the iOS retry action
When doInitKoin() throws in the Swift entry point, the exception is swallowed and no usable global Koin instance exists. This retry callback only increments Compose state, so every retry calls KoinPlatform.getKoin() again without re-running initialization and leaves the user in a permanent STARTUP_INITIALIZATION_FAILED loop. The retry path needs to invoke the Koin initializer or the initialization failure must be handled separately as non-retryable.
Useful? React with 👍 / 👎.
| @@ -73,7 +73,7 @@ class ThemeViewModel(private val settings: Settings) : ViewModel() { | |||
|
|
|||
| companion object { | |||
| const val THEME_MODE_KEY = "theme_mode" | |||
| const val THEME_PREFS_FILE = "vitruvian_preferences" | |||
| const val THEME_PREFS_FILE = "phoenix_preferences" | |||
There was a problem hiding this comment.
Fall back to legacy preferences before startup migration
On the first Android launch after upgrading, PhoenixApp.attachBaseContext() calls applyPersistedApplicationNightMode() before Koin runs AndroidPreferenceFileMigrator, but this constant now points only to the not-yet-created Phoenix preference file. On API 31+, users with an explicit legacy LIGHT or DARK setting therefore get a SYSTEM-themed starting window until Compose loads, causing a visible incorrect-theme flash. The early reader should use the legacy file when the target file has not yet been migrated.
Useful? React with 👍 / 👎.
Code Review Roast 🔥Verdict: 0 Critical Issues | Recommendation: Address before merge Overview
Correctness / Safety FindingsNo correctness or safety findings. The persisted-file migration coordinator ( Ponytail Review
var current: Throwable? = this; var depth = 0
while (current != null && depth < 16) { ... depth++ }Replace with
Ponytail net: -20 lines. Suggested Minimal PatchNo critical patch needed. Optional cleanup (all independent, pick what you want):
Final Merge GuidanceCan merge; Ponytail suggestions are optional cleanup. The rebrand is clean, the migration coordinator is correct, the preference migrator is idempotent, the DI wiring orders dependencies safely, and the test coverage is thorough (all 7 🏆 Best part: The 💀 Worst part: The 📊 Overall: Like a well-organized moving day — the boxes are labeled, the fragile stuff is wrapped, and you actually read the instructions before touching the antique vase. The 463-file diff is intimidating, but 95% of it is a clean Vitruvian→Phoenix string substitution; the remaining 5% (the migration coordinator + tests + wiring) is the real work, and it's done carefully. Files Reviewed (463 files, focused on ~20 new/changed)Core migration logic:
Tests:
Rebrand completeness: verified across Reviewed by minimax-m3 · Input: 113.3K · Output: 26.4K · Cached: 1.4M Review guidance: REVIEW.md from base branch |
Summary
Legal rebrand plus an upgrade-safe persisted-file migration. Existing Android and iOS installations keep their sandbox and local data while the active database and Android preference stores move to Phoenix filenames.
This supersedes the earlier “keep legacy persisted filenames” workaround. Fresh installs and successful upgrades use:
phoenix.dbphoenix-recovery.dbphoenix.db.migrating,phoenix-db-migration.lockphoenix_preferences,phoenix_secure_preferencesphoenix_preferences_recovery,phoenix_secure_preferences_recoveryRebrand changes
Persisted-file compatibility
DB_DUAL_DATABASES; neither candidate is opened, changed, selected, or merged automatically.Compatibility lookup remains indefinitely for skipped-release upgrades. Legacy filename literals are intentionally limited to:
DatabaseFileNames.LEGACYAndroidPreferenceFileNames.LEGACY_PLAINTEXTandLEGACY_ENCRYPTEDVerification completed
:shared:verifyCommonMainPhoenixDatabaseMigration: pass:shared:compileKotlinIosArm64: passgit diff --check: passThe disposable API 35 upgrade matrix passed all six scenarios: Fresh, v0.9.6 Upgrade, InterruptedStaging, CorruptSource, DualDatabase, and LowStorage. The upgrade retained 3 profiles, 10 workouts, 1 routine, plaintext preferences, and a real committed-WAL payload while advancing schema 43 → 47. Low storage reached 372 KiB free, blocked with
DB_RECOVERY_COPY_FAILED, preserved the legacy source, and completed through the rendered Retry action after recovery. Successful scenarios retained neutral recovery on the migrated launch and removed it on the next launch.Detailed procedure and evidence contract:
docs/qa/persisted-file-upgrade.md.Remaining release gates — do not merge yet
compileTestKotlinIosArm64is still blocked by three pre-existing errors in unchanged tests (RoutineSetWeightResolverTestandWorkoutExecutionGuardRestoredRuntimeTest).spotlessCheckstill reports 168 unrelated pre-existing Kotlin formatting violations; all files touched here are clean.Published implementation branch:
codex/phoenix-persisted-file-migrationatb98aa9f8.This PR is intentionally a draft. Mark it ready only after the production-signed Android, true skipped-version, and physical-iOS device gates are attached.