Skip to content

feat(routines): let add_exercise_with_sets record a rep range - #17

Open
wromansky wants to merge 2 commits into
wger-project:masterfrom
wromansky:feat/max-reps-on-add-exercise
Open

feat(routines): let add_exercise_with_sets record a rep range#17
wromansky wants to merge 2 commits into
wger-project:masterfrom
wromansky:feat/max-reps-on-add-exercise

Conversation

@wromansky

@wromansky wromansky commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

The problem

add_exercise_with_sets takes a single reps number. A plan that means "3 × 8-12" has to be written as 8, and the top of the range is lost.

That is tolerable when the range is decoration. It stops being tolerable when the trainee's progression depends on it. A common double-progression rule is:

  • reps inside the range at the target RiR → hold the weight
  • reps above the top of the range at the target RiR → add weight
  • reps below the bottom at the target RiR → reduce weight

With no stored top, the middle branch is undecidable — there is nothing to beat — and the rule silently degenerates into "hold forever." A coach quoting "8-12" in conversation also leaves the routine disagreeing with what the trainee was told, which is exactly the kind of drift the plan is supposed to prevent.

I hit this driving the server with an LLM client: it quoted rep ranges in chat and wrote single numbers to wger, because that is all the tool accepts.

The change

add_exercise_with_sets gains an optional max_reps.

  • When given, it writes the max_repetitions config — the max_reps kind already exists in CONFIG_KINDS and is already reachable through set_slot_entry_config. This change only reaches that existing capability from the high-level authoring call, so there is no new endpoint, model or dependency.
  • When omitted, behaviour is unchanged: a single rep number, exactly as before.
  • max_reps below reps is refused before the slot is created, so an invalid range writes nothing rather than leaving a half-built slot for someone to find later. reps is the bottom of the range; a top beneath it is a caller mistake, not a plan.
  • Equal values are allowed — a fixed prescription is a range of one.
  • The range round-trips without a read-side change: list_slot_entry_configs already returns every config kind by default, so max_repetitions comes back out the same way it went in.

Tests

Four new cases in tests/test_weight_units_and_rir.py:

  • max_reps records the top alongside the bottom
  • omitting it writes no max-reps config at all
  • max_reps below reps is refused and writes nothing (asserts slot_create was never called)
  • max_reps equal to reps is accepted

Full suite: 251 passed. ruff check and ruff format --check clean on the touched files.

Note

One of four open drafts in this area: #16 (weight unit follows the trainee's profile), #18 (unit names instead of ids), #19 (log_set's exercise_id docstring) and this one. Checked with git merge-tree against the current heads: #18 and #19 conflict with this branch only in the ## Unreleased block of CHANGELOG.md. #16 conflicts in src/wger_mcp/tools/routines.py as well — both PRs edit the same unit = as_weight_unit(...) statement in add_exercise_with_sets, #16 to source the default from the trainee's profile and this one to add the max_reps range check. The resolution is mechanical (keep both), but it is a real code conflict, not just a changelog one.

One thing for whoever lands these second: three of this PR's tests call _mock_creation without passing weight_unit. Once #16 lands, they rely on its version of _mock_creation mocking /userprofile/. If that mock is dropped while resolving the conflict, those tests quietly start making real outbound requests and assert through the fallback path instead of the one they name.

A planned set could only carry a single rep number, so "3 x 8-12" was
stored as 8 and the top of the range lived nowhere. That is fine until
the trainee's progression rule depends on it: "add weight once you beat
the top of the range" has nothing to beat, and a coach quoting a range in
conversation leaves the routine disagreeing with what the trainee was
told.

max_reps is optional and writes the max_repetitions config that
CONFIG_KINDS already defines for set_slot_entry_config, so this only
reaches an existing capability from the high-level authoring call.

A max_reps below reps is refused before the slot is created, rather than
written and left for someone to notice later: reps is the bottom of the
range, and a top beneath it is a caller mistake, not a plan. Equal values
are allowed — a fixed prescription is a range of one.
The tool table documents each signature explicitly, so a new parameter
leaves it stale. PR wger-project#7 exists because this table drifted once already.
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