#12 — partial consume:
- POST /inventory/items/{id}/consume now accepts optional {quantity}
body; decrements by that amount and only marks status=consumed when
quantity reaches zero (store.partial_consume_item)
- OFFs barcode scan pre-fills sub-unit quantity when product data
includes a pack size (number_of_units or 'N x ...' quantity string)
- Consume button shows quantity-aware label and opens ActionDialog
with number input for multi-unit items ('use some or all')
- consumeItem() in api.ts now returns InventoryItem and accepts
optional quantity param
#60 — disposal logging:
- Migration 031: adds disposal_reason TEXT column to inventory_items
(status='discarded' was already in the CHECK constraint)
- POST /inventory/items/{id}/discard endpoint with optional DiscardRequest
body (free text or preset reason)
- Calm framing: 'item not used' not 'wasted'; reason presets avoid
blame language ('went bad before I could use it', 'too much — had excess')
- Muted discard button (X icon, tertiary color) — not alarming
Shared:
- New ActionDialog.vue component for dialogs with inline inputs
(quantity stepper or reason dropdown); keeps ConfirmDialog simple
- disposal_reason field added to InventoryItemResponse
Closes#12Closes#60
Aligns llm_recipe.py with the pattern already used by the meal plan
service. cf-text routes through a lighter GGUF/llama.cpp path and
shares VRAM budget with other products via cf-orch, rather than
requiring a dedicated vLLM process. Also drops model_candidates
(not applicable to cf-text allocation).
Closes#70
- Backend: new /api/v1/feedback/attach endpoint uploads image to
Forgejo as an issue asset, then pins it as a comment so the
screenshot is visible inline on the issue
- Frontend: file input in feedback form (all types, max 5 MB)
with inline thumbnail preview and remove button
- Attach call is non-fatal: if upload fails after issue creation,
the issue is still filed and the user sees success
- Screenshot state clears on modal reset
Closes#82
- Settings: add unit_system key (metric | imperial, default metric)
- Recipe LLM prompts: inject unit instruction into L3 and L4 prompts
so generated recipes use the user's preferred units throughout
- Frontend: new utils/units.ts converter (mirrors Python units.py)
- Inventory list: display quantities converted to preferred units
- Settings view: metric/imperial toggle with save button
- Settings store: load/save unit_system alongside cooking_equipment
Closes#81
Adds full meal planning workflow to Kiwi:
- Weekly meal plan creation with configurable meal types (Paid gate)
- Drag-and-assign recipe slots per day
- Prep session generation with sequenced task lists and time estimates
- LLM-assisted full-week plan and timing fill-in (BYOK-unlockable)
- Community feed (local ActivityPub-compat + cloud federation)
- Build Your Own recipe tab with assembly templates
- Save/bookmark any recipe with star rating, notes, and style tags
- Shopping list export from built recipes
- Tab reorder: Saved > Build > Community > Find > Browse
- Auto-redirect from empty Saved tab to Build
- Custom ingredient injection persists in candidate list
- z-index fix: save modal above recipe detail panel
- Route ordering fix: /recipes/saved before /{recipe_id} catch-all
- Persist built recipes to recipes table on /build so they get real DB IDs
and can be bookmarked via saved_recipes (FK was pointing at negative IDs)
- Populate missing_ingredients in build_from_selection() from role_overrides
vs pantry diff -- backend now owns shopping list computation
- Remove client-side cartItems tracking; shopping list derived from
builtRecipe.missing_ingredients instead
- Fix saved_recipes 422: mount saved_recipes router before recipes router in
routes.py so /recipes/saved isn't captured by /recipes/{recipe_id}
- Bump SaveRecipeModal z-index to 500 (above detail-overlay at 400)
- Replace "Add to pantry" primary action with "Grocery list" clipboard copy;
"Add to pantry" demoted to compact secondary button
Wires the three Build Your Own API routes into the recipes router,
registered before the catch-all /{recipe_id} route to avoid shadowing.
Adds 5 endpoint tests covering template list count/shape, candidate
response structure, successful recipe build, and 404 on unknown template.
Both functions are DB-free public API additions to assembly_recipes.py.
get_role_candidates() scores pantry candidates against a wizard step using
element-profile overlap with prior picks; build_from_selection() builds a
RecipeSuggestion from explicit role overrides with required-role validation.
Extends AssemblyTemplate dataclass with slug, icon, descriptor, and
role_hints fields. Updates all 13 template instantiations with
appropriate values. Adds _TEMPLATE_BY_SLUG lookup dict and
get_templates_for_api() serialiser for the templates endpoint.
Adds GET /community/posts, GET /community/posts/{slug}, GET /community/feed.rss,
GET /community/local-feed, POST /community/posts, DELETE /community/posts/{slug},
POST /community/posts/{slug}/fork, and POST /community/posts/{slug}/fork-adapt (501 stub).
Wires init_community_store into main.py lifespan. 7 new tests; 115 total passing.
- update_prep_task: move whitelist guard above filter so invalid column
check runs on raw kwargs (was dead code — set(filtered) - allowed is
always empty); fixes latent SQL injection path for future callers
- main.py: move register_kiwi_programs() into lifespan context manager
so it runs once at startup, not at module import time
- MealPlanView.vue: remove debug console.log stubs from onSlotClick and
onAddMealType (follow-up issue handlers, not ready for production)
- Add GET /{plan_id}/prep-session endpoint so frontend can retrieve existing sessions without creating
- Fix list_plans response_model from list[dict] to list[PlanSummary] with proper _plan_summary() mapping
- Replace assert in store.update_prep_task with ValueError (assert is stripped under python -O)
- Add day_of_week 0-6 validation to upsert_slot endpoint
- Remove MagicMock sqlite artifact files left by pytest (already in .gitignore)
- upsert_slot: raise 422 immediately if meal_type not in VALID_MEAL_TYPES
- update_prep_task: assert whitelist safety contract after dict comprehension
- CreatePlanRequest: week_start typed as date with must_be_monday validator; str() cast at call site
- PrepTask: frozen=True; build_prep_tasks rewired to use (priority, kwargs) tuples so frozen instances are built with correct sequence_order in one pass (no post-construction mutation)
- Move deferred import json to file-level in meal_plans.py
- Fix test dates: "2026-04-14" was a Tuesday; updated request bodies to "2026-04-13" (Monday)