Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
6 changes: 3 additions & 3 deletions .agents/skills/update-phoenix-version/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@ The four app-version values to keep aligned are:

- `androidApp/build.gradle.kts`: Android `versionName`.
- `shared/src/commonMain/kotlin/com/devil/phoenixproject/util/Constants.kt`: `Constants.APP_VERSION`, used by Android `DeviceInfo`, Settings display, and backup metadata.
- `iosApp/VitruvianPhoenix/VitruvianPhoenix.xcodeproj/project.pbxproj`: Debug `MARKETING_VERSION`.
- `iosApp/VitruvianPhoenix/VitruvianPhoenix.xcodeproj/project.pbxproj`: Release `MARKETING_VERSION`.
- `iosApp/PhoenixApp/PhoenixApp.xcodeproj/project.pbxproj`: Debug `MARKETING_VERSION`.
- `iosApp/PhoenixApp/PhoenixApp.xcodeproj/project.pbxproj`: Release `MARKETING_VERSION`.

The helper can also update build-number fields near those values:

- `androidApp/build.gradle.kts`: default `versionCode = injectedVersionCode ?: ...` when `--android-code` is supplied.
- `iosApp/VitruvianPhoenix/VitruvianPhoenix.xcodeproj/project.pbxproj`: both `CURRENT_PROJECT_VERSION` entries when `--ios-build` is supplied.
- `iosApp/PhoenixApp/PhoenixApp.xcodeproj/project.pbxproj`: both `CURRENT_PROJECT_VERSION` entries when `--ios-build` is supplied.

Do not edit `shared/src/commonMain/composeResources/*/strings.xml` for a version bump. `settings_version` is only the localized label template. Do not edit `androidApp/release/output-metadata.json`; it is generated release output.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ def make_targets(args: argparse.Namespace) -> list[Target]:
),
Target(
label="iOS MARKETING_VERSION",
relative_path=Path("iosApp/VitruvianPhoenix/VitruvianPhoenix.xcodeproj/project.pbxproj"),
relative_path=Path("iosApp/PhoenixApp/PhoenixApp.xcodeproj/project.pbxproj"),
pattern=re.compile(r"^(\s*MARKETING_VERSION\s*=\s*)([^;]+)(;.*)$", re.MULTILINE),
expected_matches=2,
replacement_value=version,
Expand All @@ -147,7 +147,7 @@ def make_targets(args: argparse.Namespace) -> list[Target]:
targets.append(
Target(
label="iOS CURRENT_PROJECT_VERSION",
relative_path=Path("iosApp/VitruvianPhoenix/VitruvianPhoenix.xcodeproj/project.pbxproj"),
relative_path=Path("iosApp/PhoenixApp/PhoenixApp.xcodeproj/project.pbxproj"),
pattern=re.compile(r"^(\s*CURRENT_PROJECT_VERSION\s*=\s*)([0-9]+)(;.*)$", re.MULTILINE),
expected_matches=2,
replacement_value=args.ios_build,
Expand Down
4 changes: 2 additions & 2 deletions .almanac/pages/data-backup-and-repair.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ sources:
note: Defines the persisted default-versus-custom destination model and iOS bookmark storage.
- id: app-startup
type: file
path: androidApp/src/main/kotlin/com/devil/phoenixproject/VitruvianApp.kt
path: androidApp/src/main/kotlin/com/devil/phoenixproject/PhoenixApp.kt
note: Shows that startup migration and repair runs immediately after Koin initialization on Android.
- id: backup-routing-test
type: file
Expand Down Expand Up @@ -82,7 +82,7 @@ Pruning behavior is also platform-specific. Android queries Downloads entries an

## Startup repair

Startup repair is part of normal app boot. Android calls `migrationManager.checkAndRunMigrations()` during `VitruvianApp.onCreate()`, and the migration manager then refreshes profiles, strips fabricated `legacy_session_<id>` routine session IDs, normalizes legacy workout-mode names, backfills bad routine names on old workout rows, repairs PRs from workout history, audits profile-scoped data, and checks for orphaned records [@app-startup] [@migration-manager] [@migration-tests].
Startup repair is part of normal app boot. Android calls `migrationManager.checkAndRunMigrations()` during `PhoenixApp.onCreate()`, and the migration manager then refreshes profiles, strips fabricated `legacy_session_<id>` routine session IDs, normalizes legacy workout-mode names, backfills bad routine names on old workout rows, repairs PRs from workout history, audits profile-scoped data, and checks for orphaned records [@app-startup] [@migration-manager] [@migration-tests].

Profile-scope repair can become interactive when old default-profile rows and current active-profile rows both exist. `ProfileScopeRepairState.NeedsChoice` carries both row counts plus the active profile identity so the app can either move legacy `default`-scoped data into the active profile or switch back to the default profile without moving rows [@migration-state] [@migration-manager]. Read [[profiles]] with this page when the symptom is "data disappeared after I changed profiles" rather than a failed restore or broken file export.

Expand Down
4 changes: 2 additions & 2 deletions .almanac/pages/data.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ sources:
note: Defines the SQLDelight database version and schema-manifest validation task.
- id: schema-file
type: file
path: shared/src/commonMain/sqldelight/com/devil/phoenixproject/database/VitruvianDatabase.sq
path: shared/src/commonMain/sqldelight/com/devil/phoenixproject/database/PhoenixDatabase.sq
note: Defines the broad shared schema, including profile-scoped workout, routine, sync, and analytics tables.
- id: migration-manager
type: file
Expand Down Expand Up @@ -48,7 +48,7 @@ Phoenix has one shared persistence cluster, but future tasks usually hit it thro

Read this hub when the symptom is about where state lives, why rows moved, or why one feature is seeing data produced by another. The data cluster spans [[local-data-model]] for schema and repair mechanics, [[profiles]] for active-profile visibility and deletion reassignment, [[data-backup-and-repair]] for backup, restore, auto-backup, and import-time adoption rules, [[routines-and-training-cycles]] for persisted workout-programming state, [[strength-assessment-and-insights]] for stored 1RM and Smart Insights state, [[gamification]] for badges, streaks, and RPG-summary state, [[equipment-rack]] for settings-backed accessory inventory plus per-session rack snapshots, and [[csv-workout-import-export]] or [[external-provider-sync]] for external data that eventually lands in local tables [@schema-file] [@profile-repo] [@backup-manager] [@csv-importer] [@sync-manager] [@gamification-page].

The key boundary is that Phoenix data is shared even when features are not. `VitruvianDatabase.sq` keeps workout, routine, assessment, badge, streak, sync, and external-activity entities in one schema; `MigrationManager` then runs startup repair across that shared space; and `DataBackupManager` exports and imports nearly all of it as one backup surface [@schema-file] [@migration-manager] [@backup-manager].
The key boundary is that Phoenix data is shared even when features are not. `PhoenixDatabase.sq` keeps workout, routine, assessment, badge, streak, sync, and external-activity entities in one schema; `MigrationManager` then runs startup repair across that shared space; and `DataBackupManager` exports and imports nearly all of it as one backup surface [@schema-file] [@migration-manager] [@backup-manager].

## Default read order

Expand Down
2 changes: 1 addition & 1 deletion .almanac/pages/external-provider-sync.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ sources:
note: Shows activity storage, provider status storage, and provider-scoped deletes.
- id: db-schema
type: file
path: shared/src/commonMain/sqldelight/com/devil/phoenixproject/database/VitruvianDatabase.sq
path: shared/src/commonMain/sqldelight/com/devil/phoenixproject/database/PhoenixDatabase.sq
note: Defines integration status and cursor tables and the provider-scoped local schema surface.
status: active
verified: 2026-06-25
Expand Down
2 changes: 1 addition & 1 deletion .almanac/pages/frontend.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ sources:
note: Defines the routine-editor and training-cycle programming boundary that can surface through shared Compose screens without being part of the live session engine.
- id: ios-content
type: file
path: iosApp/VitruvianPhoenix/VitruvianPhoenix/ContentView.swift
path: iosApp/PhoenixApp/PhoenixApp/ContentView.swift
note: Shows that iOS hosts the shared Compose controller rather than a parallel SwiftUI screen tree.
- id: android-host
type: file
Expand Down
4 changes: 2 additions & 2 deletions .almanac/pages/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -170,8 +170,8 @@ Keep [[app-architecture]] nearby when the remaining question is which manager, r
- New shared-UI feature where the owning feature boundary is still unclear: [[frontend]] -> [[app-architecture]] -> [[workouts]] or [[integrations]] [@frontend-page] [@architecture-page] [@workouts-page] [@integrations-page]
- Shared screen or route bug where the owning feature is still unclear: [[frontend]] -> [[app-architecture]] -> [[workouts]] or [[integrations]]
- Settings-tab issue that could still be auth, integration, backup, or workout-preference state: [[settings-surface]] -> [[auth]] or [[integrations]] or [[data-backup-and-repair]] or [[workout-safety-and-feedback]] [@settings-page]
- BLE bug or machine-behavior mismatch: [[project-phoenix]] -> [[workouts]] -> [[vitruvian-ble-protocol]]
- Diagnostics fault codes or crash snapshots: [[workouts]] -> [[machine-diagnostics]] -> [[vitruvian-ble-protocol]] [@diagnostics-page]
- BLE bug or machine-behavior mismatch: [[project-phoenix]] -> [[workouts]] -> [[phoenix-ble-protocol]]
- Diagnostics fault codes or crash snapshots: [[workouts]] -> [[machine-diagnostics]] -> [[phoenix-ble-protocol]] [@diagnostics-page]
- Voice stop or cue playback mismatch: [[workouts]] -> [[workout-safety-and-feedback]] -> [[platform-hosts]] [@workout-safety-page] [@hosts-page]
- Routine editor or training-cycle bug before live execution starts: [[workouts]] -> [[routines-and-training-cycles]] -> [[profiles]] or [[strength-assessment-and-insights]] [@routines-page] [@profiles-page] [@assessment-page]
- 1RM assessment or Smart Insights issue: [[workouts]] -> [[strength-assessment-and-insights]] -> [[local-data-model]] [@assessment-page]
Expand Down
6 changes: 3 additions & 3 deletions .almanac/pages/local-data-model.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ sources:
note: Defines SQLDelight schema versioning and the schema manifest validation task.
- id: schema-file
type: file
path: shared/src/commonMain/sqldelight/com/devil/phoenixproject/database/VitruvianDatabase.sq
path: shared/src/commonMain/sqldelight/com/devil/phoenixproject/database/PhoenixDatabase.sq
note: Defines the current database schema and many migration-added columns.
- id: migration-manager
type: file
Expand Down Expand Up @@ -38,7 +38,7 @@ The shared database uses SQLDelight, but numbered migrations are not the whole p

## Persistence contract

The schema itself is broad and profile-aware. `VitruvianDatabase.sq` persists workout sessions, metric samples, PRs, routines, supersets, routine groups, training cycles, completed sets, progressions, gamification state, connection logs, diagnostics snapshots, sync metadata, and external integration entities, with many tables carrying `profile_id`, `updatedAt`, `serverId`, and `deletedAt` fields [@schema-file]. Read [[profiles]] alongside this page when the bug is really about active-profile filtering, delete-time reassignment, or why the same local database can show different slices of data after a profile switch.
The schema itself is broad and profile-aware. `PhoenixDatabase.sq` persists workout sessions, metric samples, PRs, routines, supersets, routine groups, training cycles, completed sets, progressions, gamification state, connection logs, diagnostics snapshots, sync metadata, and external integration entities, with many tables carrying `profile_id`, `updatedAt`, `serverId`, and `deletedAt` fields [@schema-file]. Read [[profiles]] alongside this page when the bug is really about active-profile filtering, delete-time reassignment, or why the same local database can show different slices of data after a profile switch.

## Repair layers

Expand All @@ -52,7 +52,7 @@ This makes database safety here more about idempotent repair than about trusting

Backup, restore, and startup-repair behavior are first-class parts of this persistence layer, but they now have their own retrieval page. Read [[data-backup-and-repair]] when the task is about streamed export or import, auto-backup timing, profile-scope repair, or other user-visible data recovery behavior [@backup-manager] [@migration-manager].

`DiagnosticsHistory` is currently reserved schema, not an active feature path. `VitruvianDatabase.sq` and `SchemaManifest.kt` still define the table plus recent or fault-only queries, but the live diagnostics flow in `KableBleRepository` only updates the in-memory `BleRepository.diagnostics` state and connection-log stream, and `DiagnosticsViewModel` renders directly from that live state instead of reading SQLDelight history rows [@schema-file] [@kable-repo] [@diagnostics-vm]. Read [[machine-diagnostics]] with this in mind when a future task proposes persisting diagnostic snapshots, because the schema surface already exists but the current product path is live-only.
`DiagnosticsHistory` is currently reserved schema, not an active feature path. `PhoenixDatabase.sq` and `SchemaManifest.kt` still define the table plus recent or fault-only queries, but the live diagnostics flow in `KableBleRepository` only updates the in-memory `BleRepository.diagnostics` state and connection-log stream, and `DiagnosticsViewModel` renders directly from that live state instead of reading SQLDelight history rows [@schema-file] [@kable-repo] [@diagnostics-vm]. Read [[machine-diagnostics]] with this in mind when a future task proposes persisting diagnostic snapshots, because the schema surface already exists but the current product path is live-only.

## Reading boundary

Expand Down
8 changes: 4 additions & 4 deletions .almanac/pages/machine-diagnostics.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ sources:
note: Defines the shared diagnostic packet and crash payload models.
- id: schema-file
type: file
path: shared/src/commonMain/sqldelight/com/devil/phoenixproject/database/VitruvianDatabase.sq
path: shared/src/commonMain/sqldelight/com/devil/phoenixproject/database/PhoenixDatabase.sq
note: Defines the reserved DiagnosticsHistory table and diagnostic snapshot queries in the local schema.
- id: diagnostics-tests
type: file
Expand All @@ -54,14 +54,14 @@ Machine diagnostics is a shared troubleshooting workflow for a connected trainer

The screen is driven by live BLE state. `BleRepository` exposes `diagnostics` as a `StateFlow<DiagnosticPacket?>`, `DiagnosticsViewModel` combines that stream with connection state, and `DiagnosticsScreen` renders one of three states: disconnected with no snapshot, connected but waiting for the characteristic to answer, or a populated packet with sections for uptime, faults, temperatures, crash data, and warnings [@ble-repo] [@diagnostics-vm] [@diagnostics-screen].

The binary contract is broader than just four fault words. `parseDiagnosticPacket()` accepts an empty payload as a valid zero snapshot, rejects non-empty payloads shorter than `18` bytes, then decodes uptime seconds, four unsigned `16-bit` fault words, six required temperatures, two optional extra temperatures, an optional `52`-byte crash block, and an optional `32-bit` warnings field [@protocol-parser] [@protocol-models]. [[vitruvian-ble-protocol]] is the neighboring page for the broader scan, rep, monitor, and command surface around this diagnostic characteristic.
The binary contract is broader than just four fault words. `parseDiagnosticPacket()` accepts an empty payload as a valid zero snapshot, rejects non-empty payloads shorter than `18` bytes, then decodes uptime seconds, four unsigned `16-bit` fault words, six required temperatures, two optional extra temperatures, an optional `52`-byte crash block, and an optional `32-bit` warnings field [@protocol-parser] [@protocol-models]. [[phoenix-ble-protocol]] is the neighboring page for the broader scan, rep, monitor, and command surface around this diagnostic characteristic.

Fault decoding is intentionally category-specific. `DiagnosticFaultDecoder` always projects the packet into four display slots named `Controller`, `Other`, `Motor A`, and `Motor B`, then maps bitmasks to labels such as `Controller restarted`, `Overvoltage`, `Encoder fault`, or `Motor over-temperature` instead of exposing only raw integers [@fault-decoder]. The view-model tests pin that labeling contract and verify that the export text includes both the human label and the raw hex code [@diagnostics-tests].

The export contract is deliberately narrow. `buildDiagnosticsExportText()` prepends app version, export timestamp, `REDACTED_DIAGNOSTICS` classification, and a privacy line that excludes workout history, profiles, auth or session tokens, Supabase config, and keystore data before listing the current packet contents [@diagnostics-vm]. The copy button stays disabled until a packet exists, so the feature exports the latest machine snapshot rather than an empty template [@diagnostics-screen].

Live publication and logging happen below the UI. `KableBleRepository.publishDiagnostics()` stamps packets with `receivedAtMillis` when needed, publishes them to the shared diagnostics flow, and emits a diagnostic log entry only when the fault-word set changes so repeated identical snapshots do not spam the log stream [@kable-repo].

The current workflow is live-only even though the schema already reserves a history table. `VitruvianDatabase.sq` still defines `DiagnosticsHistory` plus recent and fault-only queries, but the present code path does not write packets into that table or read it back into `DiagnosticsViewModel`; the user-facing diagnostics screen is driven by the current BLE packet and the export text derived from it [@schema-file] [@kable-repo] [@diagnostics-vm].
The current workflow is live-only even though the schema already reserves a history table. `PhoenixDatabase.sq` still defines `DiagnosticsHistory` plus recent and fault-only queries, but the present code path does not write packets into that table or read it back into `DiagnosticsViewModel`; the user-facing diagnostics screen is driven by the current BLE packet and the export text derived from it [@schema-file] [@kable-repo] [@diagnostics-vm].

Read [[workouts]] first when the bug source is still unclear, [[vitruvian-ble-protocol]] when the question is whether the packet itself is being parsed or delivered correctly, [[local-data-model]] when the task is about turning the reserved diagnostics schema into a real persistence feature, and [[platform-hosts]] when Android and iOS disagree about the conditions under which a connected machine reaches this screen.
Read [[workouts]] first when the bug source is still unclear, [[phoenix-ble-protocol]] when the question is whether the packet itself is being parsed or delivered correctly, [[local-data-model]] when the task is about turning the reserved diagnostics schema into a real persistence feature, and [[platform-hosts]] when Android and iOS disagree about the conditions under which a connected machine reaches this screen.
Loading
Loading