fix: preserve weights when updating routines & workouts - #190
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #190 +/- ##
=========================================
Coverage 100.00% 100.00%
=========================================
Files 1 1
Lines 2 2
=========================================
Hits 2 2 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
✨ PR Review
LGTM
Generated by LinearB AI and added by gitStream.
AI-generated content may contain inaccuracies. Please verify before using.
💡 Tip: You can customize your AI Review using Guidelines Learn how
There was a problem hiding this comment.
Overall, the changes correctly introduce dual-field support (weight/weightKg, distance/distanceMeters, duration/durationSeconds) and fix the || null bug by switching to ?? null, which is a clear correctness improvement. The main risk introduced is subtle: the silent precedence of new fields over legacy ones when both are present could be confusing without documentation, and the altered semantics for notes (empty string vs null) might have downstream effects if any consumers distinguish them. It would be beneficial to add small inline comments to document precedence decisions and, if necessary, normalize notes values consistently. No obvious performance or architectural regressions are visible in the modified code.
Additional notes (5)
- Maintainability |
src/tools/routines.ts:149-152
Usingweight ?? weightKg ?? nullpreserves existing behavior and adds the desired precedence for the newweightfield. However, when bothweightandweightKgare provided and differ, the current logic silently prefersweight. Given your intentional soft deprecation, that’s acceptable, but it would be safer to at least document that precedence in a comment here so future maintainers don't accidentally reverse it or assume they must be equal.
If conflicting values are likely due to tool misuse, a follow-up improvement could be to surface a validation error or log a warning in that specific case.
- Maintainability |
src/tools/workouts.ts:18-43
The removal of these custom types in favor ofInferToolParams-derived types is a good simplification. One potential follow-up concern is that any external code (tests, helper functions) that might have been relying on these interfaces as a stable contract will now need to depend directly on the tool param types, which are more tightly coupled to the Zod schemas.
If those interfaces were ever exported (they weren't here), this would be a breaking API surface change; as-is, it's internal-only, but worth keeping in mind when evolving the schemas in the future.
- Maintainability |
src/tools/workouts.ts:170-170
The schema now allows bothweightandweightKg(and likewise for distance/duration) asnumber | null | undefined. Downstream, the mapping uses??to choose a value, which will treat0as a valid value for both fields. This is correct for preserving zero values but increases the number of possible shapes that a set can have.
Given that both fields are accepted for backward compatibility, this complexity is acceptable, but be aware that any future validation or business logic working on these tool params must always prefer the new short names to avoid accidentally reintroducing the bug you're fixing here.
- Maintainability |
src/tools/workouts.ts:213-213
Switching from|| nullto?? nullis a functional improvement, preserving falsy-but-valid values like0or empty strings. However, fornotes, this means an empty string will now be sent to the API instead ofnull. If the Hevy API (or your formatting utilities) have any semantic difference between "no notes" and "empty notes", this may subtly change behavior or output.
If that distinction doesn't matter, this is fine; if it does, you might want to normalize empty strings to null specifically for notes while still using ?? for numeric fields.
- Maintainability |
src/tools/workouts.ts:200-207
Same precedence / conflict resolution concern as in the routines mapping:weight/distance/durationnow silently override the legacy fields when both are present. That matches the stated intent, but the behavior is implicit and duplicated in multiple places (create & update, routines & workouts).
If you expect more such dual-field transitions in the future, this logic might be a candidate for a small shared helper to centralize precedence and keep the mapping code from diverging or getting copy-pasted inconsistently.
Summary of changes
Summary of Changes
- Routines tools (
src/tools/routines.ts)- Extended the set schemas for both
create-routineandupdate-routineto accept new human-friendly fields:weight,distance, anddurationalongside existingweightKg,distanceMeters, anddurationSeconds. - Updated the mapping to the Hevy API to prefer the new short field names (
weight,distance,duration) and fall back to the legacy names when the new ones are absent forweight_kg,distance_meters, andduration_seconds.
- Extended the set schemas for both
- Workouts tools (
src/tools/workouts.ts)- Removed unused helper types/interfaces (
SetType,ExerciseSetInput,ExerciseInput). - Updated the
create-workoutandupdate-workoutschemas to mirror the dual-field pattern used in routines (addingweight,distance,durationfields while keeping the legacy ones). - Changed mappings to the Hevy API to prefer the new short field names while falling back to the legacy names, and switched several
|| nullusages to?? nullto preserve falsy-but-valid values. - Slight cleanup of comments and minor simplifications around type assertions and mapping logic.
- Removed unused helper types/interfaces (
## [1.13.2](v1.13.1...v1.13.2) (2025-12-10) ### Bug Fixes * align routine and workout weight fields ([#190](#190)) ([14d10d1](14d10d1))
Align routine and workout MCP tools so that updating routines/workouts no longer clears
weightfields, and make the tool inputs match the formatted outputs.Changes
Routines tools (
create-routine,update-routine)weightKg,distanceMeters,durationSeconds) and the new, more human-friendly names (weight,distance,duration).weight_kgnow usesweightif present, otherwiseweightKg.distance_metersnow usesdistanceif present, otherwisedistanceMeters.duration_secondsnow usesdurationif present, otherwisedurationSeconds.weightKgworking, while allowing tools and LLMs to work naturally with theweight/distance/durationfields they see in formatted routine outputs.Workouts tools (
create-workout,update-workout)weight/weightKg,distance/distanceMeters, andduration/durationSeconds.weight_kg,distance_meters, andduration_secondsprefer the new short names but fall back to the legacy ones.superset_idandnotesmapping to use?? nullinstead of|| nullso falsy-but-valid values (like0or empty strings) are preserved.SetType/ExerciseSetInput/ExerciseInputhelper interfaces and rely onInferToolParams-derived types for the tool contracts.General
.int()) for distance and duration fields, matching the underlying Hevy API, while fixing theweightnaming mismatch that caused values like62.5kg to be dropped.Verification
pnpm run build: ✅pnpm vitest run --exclude 'tests/integration/**': ✅ (8 files, 36 tests)pnpm run check: ✅ (only pre-existing Biome warnings about config$schemaandanyinwebhooks.ts)pnpm run check:types: ❌ already failing onsrc/index.test.tsdue to aprocess.exitspy signature mismatch; this file was not modified in this branch.Notes on self-review feedback
weightnow wins. This is intentional to softly deprecateweightKgwhile keeping backward compatibility. In practice, MCP clients should send one field or the other, so I did not add extra validation logic for conflicting values.distance,duration) to match the formatted tool outputs and avoid reintroducing the earlier mismatch. Units remain identical to the Hevy API: meters and seconds, respectively.duration/durationSecondsasz.coerce.number().int()because the Hevy API expects whole seconds. Changing this would be a larger behavior change than the current bug fix.Closes #188
✨ PR Description
Purpose: Fix weight and metric data loss when updating workout routines and exercise sets by adding backward-compatible field aliases.
Main changes:
Generated by LinearB AI and added by gitStream.
AI-generated content may contain inaccuracies. Please verify before using.
💡 Tip: You can customize your AI Description using Guidelines Learn how