feat(tools): harden list-tool and ratings inputs#120
Conversation
960cb56 to
b00822b
Compare
|
/review-pr |
|
Claude finished @alexander-wenzel-dev's task in 7m 9s —— View job Review complete
Findings1. Duplicated UUID-parsing helper — 2. No unit test for the new cache-hit path — 3. Minor: commit scope — Verified as sound (no action needed)
|
b00822b to
1b9ad8c
Compare
|
/review-pr |
|
Claude finished @alexander-wenzel-dev's task in 3m 20s —— View job Review in progress
|
|
/review-pr |
|
Claude finished @alexander-wenzel-dev's task in 4m 24s —— View job Review complete
Findings1. Stale
CLAUDE.md's own "Maintaining this file" rule requires removing/updating obsolete sections in the same commit that obsoletes them. 2. Minor: commit scope — Resolved since the last review pass
Verified as sound (no action needed)
|
1b9ad8c to
d666e85
Compare
require_pagination replaces require_per_page and bounds both inputs: per_page to 1..100 and page to >= 1. The rejected low values are the ones Mealie mishandles, per_page=-1 as an unbounded "all rows" fetch, per_page=0 as an empty page, and page<=0 as silent coercion or a surprising last page. The list tool docstrings state the per_page range in place of the ceiling-only wording. list_recipe_timeline_events UUID-parses recipe_id before interpolating it into the queryFilter it builds, so a value carrying a quote or a DSL operator cannot alter the parsed expression. The parse is a shared parse_recipe_uuid helper in _common.py, also adopted by the meal plan recipe_id parser. tools.md states the two-sided pagination bound and the filter-interpolation rule. The CLAUDE.md and rubric shared-helper lists name require_pagination and parse_recipe_uuid, not the removed require_per_page.
set_recipe_rating rejects a rating outside Mealie's 0..5 convention, which Mealie itself stores unchecked, so an out-of-range value can no longer corrupt the user's ratings. The resolved user id is cached per token, sparing the five rating and favorite tools a GET /api/users/self on every call after the first. The cache-hit path has a unit test that pre-seeds the cache and asserts no client call.
d666e85 to
33ed762
Compare

Changes
require_pagination(page, per_page)replacesrequire_per_pageand bounds both inputs:per_pageto1..100,pageto>= 1. The rejected low values are the ones Mealie mishandles:per_page=-1is an unbounded "all rows" fetch,per_page=0an empty page, andpage<=0gets silently coerced or returns a surprising last page. All 11 list tools call the new helper, and their wrapper docstrings now state theper_pagerange instead of the ceiling-only wording.list_recipe_timeline_eventsUUID-parsesrecipe_idbefore interpolating it into thequeryFilterit builds, so a value with a quote or a DSL operator cannot alter the parsed expression. The parse is a sharedparse_recipe_uuidhelper in_common.py, also adopted by the meal plan recipe_id parser that carried the same logic.set_recipe_ratingrejects a rating outside Mealie's0..5convention, which Mealie itself stores unchecked.GET /api/users/selfon every call after the first. The cache-hit path has a unit test.tools.mdstates the two-sided pagination bound and the filter-interpolation rule (and a stale_iter_tool_modulesreference in "Naming and grouping" was corrected to_require_register), and theCLAUDE.mdshared-helper list now namesrequire_paginationandparse_recipe_uuidin place of the removedrequire_per_page.How tested
Operator decisions taken first: bound
page >= 1(yes), rating range0..5only (no half-step rule). Both grounded in the live probe recorded in the task file.ruff format --check,ruff check, andmypy srcall clean.Risks
None. The pagination and filter changes are input hardening with no happy-path behaviour change; the existing timeline and ratings live tests still pass. The user-id cache is process-scoped and keyed per token; this repo is one Mealie instance and one token per process, so the cache cannot serve a stale id across tokens. Rejections are raised client-side before any HTTP call and are unit-tested.