Skip to content

Codex/profile readiness fixtures#651

Open
9thLevelSoftware wants to merge 95 commits into
mainfrom
codex/profile-readiness-fixtures
Open

Codex/profile readiness fixtures#651
9thLevelSoftware wants to merge 95 commits into
mainfrom
codex/profile-readiness-fixtures

Conversation

@9thLevelSoftware

Copy link
Copy Markdown
Owner

No description provided.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request implements a comprehensive profile-based training settings system, including a new Profile tab, migration of training preferences to a profile-scoped SQLDelight aggregate, and a robust QA seeding mechanism for local testing. My feedback highlights that the rackSnapshotJson function currently uses brittle hardcoded JSON strings, which should be replaced with the project's JSON serializer to ensure consistency and maintainability.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +506 to +512
private fun rackSnapshotJson(profileKey: String, isProfileA: Boolean): String {
val id = rackItemId(profileKey)
return if (isProfileA) {
"[{\"id\":\"$id\",\"name\":\"QA Weighted Vest A\",\"category\":\"WEIGHTED_VEST\",\"weightKg\":10.0,\"behavior\":\"ADDED_RESISTANCE\",\"enabled\":true,\"sortOrder\":1,\"createdAt\":1700000001000,\"updatedAt\":1700000001100}]"
} else {
"[{\"id\":\"$id\",\"name\":\"QA Assistance B\",\"category\":\"ASSISTANCE\",\"weightKg\":7.5,\"behavior\":\"COUNTERWEIGHT\",\"enabled\":true,\"sortOrder\":2,\"createdAt\":1700000002000,\"updatedAt\":1700000002100}]"
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The rackSnapshotJson function uses hardcoded JSON strings to create the rack snapshot. This is brittle and can easily break if the RackItem serialization changes. To improve maintainability and ensure consistency with the rest of the app, consider using the project's JSON serializer to generate this string from RackItem data classes.

This would involve creating the RackItem instances and then using a Json instance to encode them into a string, which ensures that the generated JSON is always in sync with the RackItem data class definition and its serialization rules.

private fun rackSnapshotJson(profileKey: String, isProfileA: Boolean): String {
    val json = kotlinx.serialization.json.Json { encodeDefaults = true }
    val rackItem = if (isProfileA) {
        RackItem(
            id = rackItemId(profileKey),
            name = "QA Weighted Vest A",
            category = RackItemCategory.WEIGHTED_VEST,
            weightKg = 10.0f,
            behavior = RackItemBehavior.ADDED_RESISTANCE,
            enabled = true,
            sortOrder = 1,
            createdAt = 1_700_000_001_000L,
            updatedAt = 1_700_000_001_100L,
        )
    } else {
        RackItem(
            id = rackItemId(profileKey),
            name = "QA Assistance B",
            category = RackItemCategory.ASSISTANCE,
            weightKg = 7.5f,
            behavior = RackItemBehavior.COUNTERWEIGHT,
            enabled = true,
            sortOrder = 2,
            createdAt = 1_700_000_002_000L,
            updatedAt = 1_700_000_002_100L,
        )
    }
    return json.encodeToString(listOf(rackItem))
}

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: eff8470a88

ℹ️ 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".

),
selected = core.weightUnit,
enabled = enabled,
onSelected = { onCoreChange(core.copy(weightUnit = it)) },

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reset increments when changing weight units

When a profile switches between KG and LB here, the existing weightIncrement is carried over even though UserPreferences.effectiveWeightIncrement interprets the stored value in the newly selected display unit. For example, a user with a 5 kg step who switches to LB now silently gets a 5 lb step (and a 1 lb step becomes 1 kg when switching back), which changes workout weight adjustments/recommendations; reset to the unit default (-1f) or convert the increment when changing weightUnit.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: efac83b241

ℹ️ 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".

Comment on lines +1363 to +1365
if (verbalEvent != null) {
coordinator._hapticEvents.emit(verbalEvent)
Logger.i { "VBT: VERBAL_ENCOURAGEMENT emitted (tier=${verbalEvent.vulgarTier}, dominatrix=${verbalEvent.dominatrixMode}, vulgar=${verbalEvent.vulgarMode})" }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Remove the legacy verbal encouragement emission

When a velocity-loss alert fires with verbal encouragement enabled, this new if (verbalEvent != null) block is not closed before the legacy val prefs = ... emission below, so the code emits a second VERBAL_ENCOURAGEMENT using raw vulgarModeEnabled/dominatrixModeActive values. In the inspected workout path, a profile with persisted vulgar/dominatrix intent but adultsOnlyConfirmed == false first emits the neutralized event and then immediately emits an adult-routed event, bypassing the local adult confirmation gate; close this block before the legacy code or remove the legacy emission.

Useful? React with 👍 / 👎.

@kilo-code-bot

kilo-code-bot Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Code Review Roast 🔥

Verdict: Comment Only | Recommendation: Address existing review feedback; can merge after.

Overview

Severity Count
🚨 critical 0
⚠️ warning 0
💡 suggestion 0
🤏 nitpick 0

No new inline comments this pass. I verified the three outstanding review comments against the current HEAD (e15c7953) and the relevant code on disk:

  • ActiveSessionEngine.kt:1365 (P1, unclosed if (verbalEvent != null) block) — resolved in commit e15c7953 ("fix: resolve verbal encouragement merge conflict"). The block now closes cleanly at line 1389, and verbalEncouragementEventOrNull() (ActiveSessionEngine.kt:88) correctly gates vulgarModeEnabled on adultsOnlyConfirmed so the dual-emission regression can't recur. Nice work closing that loop.
  • 🟡 ProfileQaSeeder.kt:512 (medium, hardcoded JSON) — still present at lines 509 and 511. Active comment, no duplicate added.
  • 🟡 ProfilePreferenceComponents.kt:212 (P2, weight unit change doesn't reset increment) — still valid at line 212. Active comment, no duplicate added.
Files Reviewed (high-risk subset, 14 files)
  • shared/.../presentation/manager/ActiveSessionEngine.kt (verbal event gate + just-lift codec bridges)
  • shared/.../presentation/manager/SettingsManager.kt (mutexed per-section updates, profile-context guards)
  • shared/.../data/preferences/ProfilePreferencesCodec.kt (decode/encode + validation folding)
  • shared/.../data/repository/UserProfileRepository.kt
  • shared/.../data/repository/ProfilePreferencesRepository.kt
  • shared/.../data/repository/SqlDelightWorkoutRepository.kt
  • shared/.../presentation/components/ProfilePreferenceComponents.kt (weight unit + increment chips)
  • shared/.../presentation/viewmodel/ProfileViewModel.kt
  • shared/.../data/sync/SyncManager.kt
  • shared/.../sqldelight/.../VitruvianDatabase.sq + migrations/42.sqm
  • androidApp/.../qa/ProfileQaSeeder.kt
  • shared/.../data/preferences/LegacyProfilePreferencesReader.kt
  • shared/.../data/repository/SqlDelightWorkoutRepository.kt queries

(Full PR is 197 files / 55k insertions — only the changed-code paths relevant to the profile preferences feature and the three flagged lines were read in full.)

Ponytail Review

  • shared/.../presentation/manager/SettingsManager.kt:L115-120: shrink: combine(globalPrefs, profileCtx.filterIsInstance<Ready>()) → stateIn(Eagerly, initialUserPreferences()). initialUserPreferences() reads both flows again and filters the context for the initial value. The combined flow's first emission already covers the same case; drop the eager initializer and use combine(...).stateIn(scope, SharingStarted.Eagerly, UserPreferences()) with global as the fallback when Ready isn't present. ~10 lines deletable.
  • shared/.../presentation/manager/SettingsManager.kt:L199-249: shrink: updateSection + updateCore are the same pattern (mutex, expectedId, transform, write) with different capture sites. Consolidate into a single mutateSection(mutex, transform, write) and have updateCore/updateWorkout/etc. be one-liners. ~40 lines deletable.
  • shared/.../data/preferences/ProfilePreferencesCodec.kt:L60-92: shrink: decodeVbt (and partially decodeLed) re-map the document into a domain object twice — once inside the decode(...) validator closure and again in the outer .mapValue { ... }. Move the mapping into the decode validator so decodeVbt/decodeLed become a single decode(...).mapValue(::toDomain). ~15 lines deletable, and you stop repeating the nine-property copy.
  • shared/.../presentation/components/ProfilePreferenceComponents.kt:L212: covered by existing P2 comment (reset weightIncrement to -1f on unit change).
  • androidApp/.../qa/ProfileQaSeeder.kt:L509,511: covered by existing medium comment (use the ProfilePreferencesCodec/RackItem serializer).

Ponytail net: ~65 lines removable (Ponytail suggestions are optional cleanup on top of the open review feedback).

Suggested Minimal Patch

No patch from this review. Existing comments cover the correctness items; the Ponytail shrink pass is optional and should land as a separate refactor if at all.

Final Merge Guidance

Can merge after the existing ProfilePreferenceComponents.kt:212 (P2) and ProfileQaSeeder.kt:512 (medium) comments are addressed. The previously blocking P1 ActiveSessionEngine.kt issue is resolved at HEAD. Ponytail shrink suggestions are optional.

🏆 Best part: The verbal encouragement fix at ActiveSessionEngine.kt:88-98 — extracting verbalEncouragementEventOrNull() and gating vulgar/dominatrix on adultsOnlyConfirmed is a textbook defense-in-depth move. Whoever caught the merge-conflict regression and added the explicit if/return early-exit deserves a coffee.

💀 Worst part: The two pre-existing comments (ProfileQaSeeder hardcoded JSON, ProfilePreferenceComponents weight-increment-on-unit-change) are still wide open after 55k lines of churn. Either they're being saved for a follow-up, or nobody's tripping over the bot's been ignoring them.

📊 Overall: A feature branch that ate the whole codebase and somehow came out the other side with the critical adult-confirmation gate intact. Ship after addressing the open comments — this isn't a code-quality problem, it's a follow-up hygiene problem.


Fix these issues in Kilo Cloud


Reviewed by minimax-m3 · Input: 71.2K · Output: 14.9K · Cached: 2.4M

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e15c7953a4

ℹ️ 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".

@@ -466,7 +559,7 @@ abstract class BaseDataBackupManager(
dominantSide = session.dominantSide,
strengthProfile = session.strengthProfile,
formScore = session.formScore,
profile_id = session.profileId ?: "default",
profile_id = session.profileId ?: activeProfileId,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Adopt session backups whose profile is absent

When importing a per-session/per-routine backup from another install or from a deleted profile, the backup has no userProfiles entry (see exportSession/exportRoutine) but mapSessionToBackup still serializes the source profileId. This assignment preserves that non-existent profile id instead of falling back to the active profile, so the imported workout history is written under an orphan profile and disappears from the user's active profile; resolve session.profileId against the imported/existing profile set, and mirror the fix in the streaming import path.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant