Skip to content

fix(workouts): omit routine_id from create/update - #229

Merged
chrisdoc merged 4 commits into
mainfrom
ai-228-error-workout-routine-id-is-not-allowed-whe
Jan 8, 2026
Merged

fix(workouts): omit routine_id from create/update#229
chrisdoc merged 4 commits into
mainfrom
ai-228-error-workout-routine-id-is-not-allowed-whe

Conversation

@charliecreates

@charliecreates charliecreates Bot commented Jan 4, 2026

Copy link
Copy Markdown
Contributor

Fixes the Hevy API 400 error where routine_id was being sent (often as null) in the /v1/workouts request body. The Hevy API schema rejects this field (\"workout.routine_id\" is not allowed).

Changes

  • Removed routineId from the create-workout and update-workout tool schemas and stopped sending workout.routine_id in payloads.
  • Typed the outgoing workout payload as PostWorkoutsRequestBody[\"workout\"] so invalid request fields don’t creep back in.
  • Fixed argument ordering for createExerciseTemplate in src/utils/hevyClientKubb.ts to match the generated Kubb client signature.

Verification

# Biome: Checked 41 files; no fixes needed
pnpm run check

# TypeScript: No errors
pnpm run check:types

# Vitest: 15 files passed, 116 tests passed
node --env-file .env node_modules/vitest/vitest.mjs --run \
  --exclude tests/integration/hevy-mcp.integration.test.ts

# Build: success (Sentry sourcemap upload warns locally without token)
pnpm run build
  • The excluded integration test requires a real HEVY_API_KEY, which isn’t available in the devbox.

reviewChanges skipped:

  • compatibility warning about removing routineId from tool schemas — intentional; the upstream API rejects routine_id, so the tool contract now matches reality.
  • suggestion to strengthen stdout-pollution test to actually run initialization — out of scope (the test didn’t exercise init previously either).
  • suggestion to add a dedicated unit test for hevyClientKubb wrapper argument ordering — out of scope for this fix.

Closes #228.

✨ PR Description

Purpose: Fix API schema validation errors by removing unsupported routine_id field from workout create and update operations to align with Hevy API specifications.

Main changes:

  • Removed routine_id field from createWorkoutSchema and updateWorkoutSchema validation schemas and payload construction
  • Added wrapApi helper function to enforce type-safe API parameter ordering and prevent argument order regressions
  • Updated test suite to verify routine_id exclusion and refactored stdout mocking to use vi.spyOn

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


Note

Fixes Hevy workout create/update payloads and hardens the generated client call signatures.

  • Removes routineId from create-workout/update-workout schemas and stops sending workout.routine_id; types outgoing workout as PostWorkoutsRequestBody["workout"] and normalizes nullable fields
  • Adds wrapApi helper and refactors src/utils/hevyClientKubb.ts calls to match generated Kubb signatures; corrects postV1ExerciseTemplates arg order
  • Updates tests: ensure routine_id is omitted in create payloads and improves stdout mocking in tests/unit/index.test.ts

Written by Cursor Bugbot for commit a78951f. This will update automatically on new commits. Configure here.

@coderabbitai

coderabbitai Bot commented Jan 4, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Bot user detected.

To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.


Comment @coderabbitai help to get the list of available commands and usage tips.

@sentry

sentry Bot commented Jan 4, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 18.18182% with 18 lines in your changes missing coverage. Please review.
✅ Project coverage is 64.40%. Comparing base (6ba1dbc) to head (a78951f).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
src/utils/hevyClientKubb.ts 11.11% 16 Missing ⚠️
src/tools/workouts.ts 50.00% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #229      +/-   ##
==========================================
+ Coverage   64.25%   64.40%   +0.15%     
==========================================
  Files          13       13              
  Lines         414      413       -1     
  Branches      130      128       -2     
==========================================
  Hits          266      266              
  Misses         99       99              
+ Partials       49       48       -1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@gitstream-cm gitstream-cm 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.

✨ 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

@charliecreates charliecreates Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Main risk is contract ambiguity: tests still pass routineId even though the tool schema removed it, so it’s unclear whether the tool should reject unknown keys or silently ignore/strip them. The Kubb wrapper fix is correct but suggests a maintainability gap—wrapper signatures can drift from generated APIs unless you mechanically align them via Parameters<>/typeof. Minor correctness/behavior concern: description || null converts empty strings to null; use description ?? null if you want to preserve intentional empty-string input.

Additional notes (2)
  • Maintainability | src/tools/workouts.test.ts:203-203
    The tests now include routineId in args, but createWorkoutSchema no longer accepts it. If the handler is wired to validate using this Zod schema (likely), this test may now be unintentionally exercising the validation/unknown-key stripping behavior rather than explicitly asserting that routineId is rejected/ignored at the tool boundary.

Right now, the test asserts routine_id is omitted from the outbound payload, which is good—but it doesn’t clearly assert whether routineId is rejected (error) or silently ignored (strip/passthrough). If the intended contract is “routineId is not part of the tool API anymore”, it’s better to assert that providing it fails validation (or at least that it is stripped before handler logic).

  • Readability | src/tools/workouts.ts:188-197
    workoutPayload is now typed as NonNullable<PostWorkoutsRequestBody["workout"]>, which is a good guardrail. However, the code still accepts description as optional().nullable() and then uses description || null, which will convert an empty string ("") into null.

That might be fine, but it’s a behavior change from “send empty string if provided” to “drop to null”, and it’s not obviously intended. If the API distinguishes between empty-string vs null (or you want to preserve user input), this should be description ?? null.

Summary of changes

What changed

  • Workouts tool payloads

    • Removed routineId from the create-workout and update-workout Zod schemas.
    • Stopped sending workout.routine_id in the /v1/workouts request body.
    • Added a stricter payload type: NonNullable<PostWorkoutsRequestBody["workout"]> to reduce the chance of reintroducing unsupported fields.
  • Tests

    • Updated create-workout tests to assert routine_id is not present in the outbound payload.
    • Refactored stdout pollution test to use vi.spyOn(process.stdout, "write") instead of manual reassignment.
    • Tweaked JSON env var parsing test to avoid non-null assertions.
  • Kubb client wrapper

    • Fixed createExerciseTemplate wrapper argument ordering to match api.postV1ExerciseTemplates(data, headers, { client }).

Comment thread src/utils/hevyClientKubb.ts Outdated
Comment on lines +129 to +132
createExerciseTemplate: (
data: PostV1ExerciseTemplatesMutationRequest,
): ReturnType<typeof api.postV1ExerciseTemplates> =>
api.postV1ExerciseTemplates(headers, data, { client }),
api.postV1ExerciseTemplates(data, headers, { client }),

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This change correctly fixes the wrapper call signature, but it also highlights a bigger risk: these hand-written wrapper methods can silently drift from the generated Kubb client signatures.

Given this caused a real bug, consider adding a small compile-time guard around these wrappers—e.g., by typing the wrapper function as typeof api.postV1ExerciseTemplates (or using Parameters<>/ReturnType<>) so argument order mismatches are caught immediately during implementation.

Suggestion

Make wrapper signatures mechanically follow the generated client to prevent arg-order regressions. For example:

createExerciseTemplate: (
  ...args: Parameters<typeof api.postV1ExerciseTemplates>
): ReturnType<typeof api.postV1ExerciseTemplates> =>
  api.postV1ExerciseTemplates(...args, { client }),

If you need to inject headers consistently, you can still use Parameters<> for the other args (e.g., data/params) and keep the wrapper aligned.

Reply with "@CharlieHelps yes please" if you'd like me to add a commit implementing a safer typing pattern for this and similar wrapper methods.

@charliecreates
charliecreates Bot removed the request for review from CharlieHelps January 4, 2026 09:08
CharlieHelps and others added 2 commits January 8, 2026 21:42
Hevy's /v1/workouts API rejects routine_id in the request body.

Also fixes a mismatched arg order in the Kubb client wrapper and cleans
up a couple of Biome violations in unit tests.
@chrisdoc
chrisdoc force-pushed the ai-228-error-workout-routine-id-is-not-allowed-whe branch from 38d1477 to a76bdcc Compare January 8, 2026 20:42

@cursor cursor 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.

This PR is being reviewed by Cursor Bugbot

Details

You are on the Bugbot Free tier. On this plan, Bugbot will review limited PRs each billing cycle.

To receive Bugbot reviews on all of your PRs, visit the Cursor dashboard to activate Pro and start your 14-day free trial.

Comment thread src/tools/workouts.ts
const workoutPayload: NonNullable<PostWorkoutsRequestBody["workout"]> = {
title,
description: description || null,
description: description ?? null,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Empty string description handling behavior change

Low Severity

The change from description || null to description ?? null alters behavior for empty string inputs. Previously, an empty string "" would be converted to null (since || treats empty strings as falsy). Now, empty strings are passed through unchanged (since ?? only checks for null/undefined). If the Hevy API expects null for "no description" but receives "" instead, this could cause unexpected behavior. This change affects both create-workout and update-workout operations.

Additional Locations (1)

Fix in Cursor Fix in Web

…sions

Addresses PR review comment about wrapper signatures drifting from generated
Kubb client signatures. The wrapApi helper uses Parameters<> to enforce
compile-time type checking on API call argument order.
@chrisdoc
chrisdoc force-pushed the ai-228-error-workout-routine-id-is-not-allowed-whe branch from 3bd68b3 to ee3a094 Compare January 8, 2026 20:47
All wrapper methods now use wrapApi() to enforce compile-time type checking,
preventing arg-order regressions across the entire client.
@chrisdoc
chrisdoc merged commit bca641a into main Jan 8, 2026
19 of 20 checks passed
github-actions Bot pushed a commit that referenced this pull request Jan 8, 2026
## [1.18.11](v1.18.10...v1.18.11) (2026-01-08)

### Bug Fixes

* **workouts:** omit routine_id from create/update ([#229](#229)) ([bca641a](bca641a))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

error: '"workout.routine_id" is not allowed' when create-workout

2 participants