fix(routines): keep reps when repRange is set - #270
Conversation
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.
The core logic change looks correct and aligned with the PR goal (preserve reps and infer fixed-range reps). Main improvements are around maintainability: avoid duplicated normalization between buildRepRange() and getFixedRepsFromRepRange(), and avoid recomputing fixed-range checks in a second pass for warning detection. Tests add valuable coverage but are currently quite repetitive; refactoring into helpers/table-driven tests would reduce maintenance overhead.
Additional notes (3)
- Maintainability |
src/tools/routines.ts:93-120
getFixedRepsFromRepRange()duplicates the normalization logic already present inbuildRepRange()(coercingundefinedtonull, checking for nulls, etc.). This creates two sources of truth for rep-range normalization and increases the chance these helpers drift (e.g., ifbuildRepRangeever changes its semantics).
Since you already call buildRepRange() before getFixedRepsFromRepRange(), consider tightening the contract and accepting only the normalized output type of buildRepRange (i.e., {start: number|null; end: number|null} | null). That eliminates redundant checks and makes the call sites clearer.
- Performance |
src/tools/routines.ts:253-253
TheusesRepRangesdetection now recomputesgetFixedRepsFromRepRange(set.rep_range)for each set after you already computedfixedRepswhile buildingsets. This is minor but it’s still avoidable extra work and a bit harder to read.
More importantly, the logic is now subtly split across two passes: one pass computes reps, another pass decides whether to warn. That separation makes it easier to accidentally change one without updating the other.
A single-pass approach can compute both sets and usesRepRanges together, or at least reuse fixedReps computed earlier.
- Maintainability |
src/tools/routines.test.ts:241-382
The new tests introduce substantial duplication (same routine scaffold, samehevyClientmock setup, same registration/handler fetch). This makes the suite harder to maintain and increases the cost of future changes to the tool contract.
These are good scenarios to cover, but they can be expressed with a shared helper (e.g., setupCreateRoutine() / setupUpdateRoutine()) and/or table-driven tests for the cases (reps omitted), (reps null), (reps provided).
Also, both new create-routine tests cast the handler input to Record<string, unknown>, which weakens the test’s ability to catch shape regressions. Even if you keep the cast, consider centralizing it in a helper so you’re not repeating it everywhere.
Summary of changes
What changed
✅ Routine set mapping now preserves/infers reps
- In both
create-routineandupdate-routine, set mapping no longer forcesrepstonullwhenrepRangeis present. - Added a helper
getFixedRepsFromRepRange()to inferrepswhenrepRangeis fixed (start === end). - Mapping logic now computes
repsas:set.repswhen it’s a number, otherwise- inferred from a fixed
repRange, otherwise null
⚠️ Rep-range warning logic refined
- The “rep ranges may not display” warning is now only emitted for non-fixed rep ranges.
🧪 Tests expanded
- Updated existing expectations to reflect the new behavior (e.g.,
reps: 10is retained). - Added coverage for:
- inferring reps from fixed
repRangewhen reps is omitted ornull(create + update) - ensuring non-fixed
repRangekeepsreps: nulland triggers warning
- inferring reps from fixed
Greptile SummaryThis PR fixes a bug where
Confidence Score: 5/5
Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[Input: set.reps, set.repRange] --> B{buildRepRange}
B --> C{repRange result}
C -->|null| D[fixedReps = null]
C -->|non-null| E{getFixedRepsFromRepRange}
E -->|start == end| F[fixedReps = start]
E -->|start != end or partial nulls| D
D --> G{typeof set.reps === number?}
F --> G
G -->|yes| H[reps = set.reps]
G -->|no| I[reps = fixedReps ?? null]
H --> J[Build API request set]
I --> J
J --> K{Check warning condition}
K -->|rep_range != null AND not fixed| L[usesRepRanges = true]
K -->|rep_range is null or fixed| M[No warning]
L --> N[Append rep-range display warning]
M --> O[Return response]
N --> O
Last reviewed commit: aa2dd87 |
|
would love a merge here @chrisdoc ! |
## [1.20.6](v1.20.5...v1.20.6) (2026-02-24) ### Bug Fixes * **routines:** keep reps when repRange is set ([#270](#270)) ([48da995](48da995))
Fixes #269 by keeping
repswhenrepRangeis present, and by backfillingrepsfor fixed ranges (e.g.8–8) so the Hevy apps show reps again.Changes
create-routineandupdate-routineset mapping sorepsis no longer forced tonullwhenrepRangeis provided.repRange.start === repRange.endandrepsis not a number, inferrepsfrom the fixed range.input_modifierfield — rep ranges created via MCP appear blank in app #261).src/tools/routines.test.tscoverage for fixed and non-fixed rep ranges.Verification
reviewChanges notes skipped:
getFixedRepsFromRepRange()in the rep-range warning check — small refactor/perf cleanup, out of scope for this fix.reps: null+ non-fixedrepRangeon updates — would change tool contract, out of scope.getFixedRepsFromRepRangeedge cases — current behavior is covered indirectly via routine tool tests; keeping helper private.Closes #269.
✨ PR Description
Purpose: Fix routine creation and update logic to preserve reps field when repRange is set, preventing data loss during routine operations.
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