Commit graph

187 commits

Author SHA1 Message Date
79f345aae6 fix: install circuitforge-orch in kiwi image for cf-orch-agent sidecar
Some checks failed
CI / Backend (Python) (push) Has been cancelled
CI / Frontend (Vue) (push) Has been cancelled
Mirror / mirror (push) Has been cancelled
cf-orch-agent in compose.override.yml was crash-looping (exit 127) because
the circuitforge_orch package wasn't installed in the kiwi conda env.
Same COPY + editable-install pattern already used for circuitforge-core.
2026-04-18 22:29:08 -07:00
5385adc52a feat: title search and sort controls in recipe browser
Some checks are pending
CI / Backend (Python) (push) Waiting to run
CI / Frontend (Vue) (push) Waiting to run
Mirror / mirror (push) Waiting to run
Adds minimal sort/search to the recipe browser for cognitive access diversity —
linear scanners, alphabet browsers, and keyword diggers each get a different
way in without duplicating the full search tab.

- browse_recipes: q (LIKE title filter) + sort (default/alpha/alpha_desc)
- API endpoint: q/sort query params with validation
- Frontend: debounced search input (350ms) + sort pills (Default/A→Z/Z→A)
- Search and sort reset on domain/category change
- _all path supports q+sort; keyword-FTS path adds AND filter on top
2026-04-18 22:14:36 -07:00
e7ba305e63 feat: hierarchical subcategory navigation in recipe browser
Some checks are pending
CI / Backend (Python) (push) Waiting to run
CI / Frontend (Vue) (push) Waiting to run
Mirror / mirror (push) Waiting to run
Adds a two-level browse tree (domain → category → subcategory) to the
recipe browser, plus an "All" unfiltered option at the top of every
domain.

browser_domains.py:
- Category values now support list[str] (flat) or dict with "keywords"
  and "subcategories" keys — backward compatible with all existing flat
  categories
- Added subcategories to: Italian (Sicilian, Neapolitan, Tuscan, Roman,
  Venetian, Ligurian), Mexican (Oaxacan, Yucatecan, Veracruz, Street
  Food, Mole), Asian (Korean, Japanese, Chinese, Thai, Vietnamese,
  Filipino, Indonesian), Indian (North, South, Bengali, Gujarati),
  Mediterranean (Greek, Turkish, Moroccan, Lebanese, Israeli), American
  (Southern, Cajun/Creole, BBQ, Tex-Mex, New England), European
  (French, Spanish, German, British/Irish, Scandinavian), Latin American
  (Peruvian, Brazilian, Colombian, Cuban, Caribbean), Dinner, Lunch,
  Breakfast, Snack, Dessert, Chicken, Beef, Pork, Fish, Vegetables
- New helpers: category_has_subcategories, get_subcategory_names,
  get_keywords_for_subcategory

store.py:
- get_browser_categories now accepts has_subcategories_by_category and
  includes has_subcategories: bool in each result row
- New get_browser_subcategories method for subcategory count queries

recipes.py endpoints:
- GET /browse/{domain}/{category}/subcategories — returns subcategory
  list with recipe counts (registered before /{subcategory} to avoid
  path collision)
- GET /browse/{domain}/{category} gains optional ?subcategory=X param
  to narrow results within a category
- GET /browse/{domain}/{category}/_all — unfiltered paginated browse
  (landed in previous commit)

api.ts: BrowserCategory adds has_subcategories; new BrowserSubcategory
type; listSubcategories() call; browse() gains subcategory param

RecipeBrowserPanel.vue:
- Category pills show a › indicator when subcategories exist
- Selecting such a category fetches subcategories in the background
  (non-blocking — recipes load immediately at the category level)
- Subcategory row appears below the category list with an
  "All [Category]" pill + one pill per subcategory with count
- Active subcategory highlighted; clicking "All [Category]" resets
  to the full category view
2026-04-18 21:07:06 -07:00
b2c546e86a feat: wire secondary-use window hints into recipe engine (#83)
Secondary-state items (stale bread, overripe bananas, day-old rice, etc.)
are now surfaced to the recipe engine so relevant recipes get matched even
when the ingredient is phrased differently in the corpus (e.g. "day-old
rice" vs. "rice").

Backend:
- Add rice and tortillas entries to SECONDARY_WINDOW in expiration_predictor
- Add secondary_pantry_items: dict[str, str] field to RecipeRequest schema
  (maps product_name → secondary_state label, e.g. {"Bread": "stale"})
- Add _SECONDARY_STATE_SYNONYMS lookup in recipe_engine — keyed by
  (category, state_label), returns corpus-matching ingredient phrases
- Update _expand_pantry_set() to accept secondary_pantry_items and inject
  synonym terms into the expanded pantry set used for FTS matching

Frontend:
- Add secondary_pantry_items to RecipeRequest interface in api.ts
- Add secondaryPantryItems param to _buildRequest / suggest / loadMore
  in the recipes store
- Add secondaryPantryItems computed to RecipesView — reads secondary_state
  from inventory items (expired but still in secondary window) and builds
  the product_name → state_label map
- Pass secondaryPantryItems into handleSuggest and handleLoadMore

Closes #83
2026-04-18 19:06:53 -07:00
8fd77bd1f2 fix: suppress E2E test sessions from log-based analytics
Add E2E_TEST_USER_ID setting (opt-in via env); session bootstrap logs
at DEBUG instead of INFO for the known test user so test runs don't
inflate session counts.  Still visible with DEBUG=true.
2026-04-18 19:06:37 -07:00
22a3da61c3 fix: frontend concurrent-mount errors, nginx routing, and browser UX (#98 #106 #107)
Some checks are pending
CI / Backend (Python) (push) Waiting to run
CI / Frontend (Vue) (push) Waiting to run
Mirror / mirror (push) Waiting to run
- App.vue: lazy-mount pattern (v-if + v-show) so non-active tabs only mount on
  first visit, eliminating concurrent onMounted calls across all components (#98)
- nginx.cloud.conf: add /kiwi/api/ location to proxy API calls on direct-port
  access (localhost:8515); was serving SPA HTML → causing M.map/filter/find
  TypeError chain on load (#98)
- nginx.cloud.conf: $host → $http_host so 307 redirects preserve port number (#107)
- RecipeBrowserPanel: show friendly "corpus not loaded" notice and skip auto-select
  when all category counts are 0, instead of rendering confusing empty buttons (#106)
- Defensive Array.isArray guards in inventory store, mealPlan store, ReceiptsView
2026-04-18 17:12:34 -07:00
bea61054fa fix: re-fetch inventory item after insert to populate product_name (#99)
Some checks are pending
CI / Backend (Python) (push) Waiting to run
CI / Frontend (Vue) (push) Waiting to run
Mirror / mirror (push) Waiting to run
2026-04-18 16:02:35 -07:00
38382a4fc9 fix: merge recipe enrichment backfill, main_ingredient browser, bug batch (#109)
Some checks failed
CI / Backend (Python) (push) Waiting to run
CI / Frontend (Vue) (push) Waiting to run
Mirror / mirror (push) Has been cancelled
Release / release (push) Has been cancelled
2026-04-18 15:39:45 -07:00
01aae2eec8 fix: recipe enrichment backfill, main_ingredient browser domain, bug batch
Some checks failed
CI / Backend (Python) (push) Has been cancelled
CI / Frontend (Vue) (push) Has been cancelled
CI / Backend (Python) (pull_request) Has been cancelled
CI / Frontend (Vue) (pull_request) Has been cancelled
Recipe corpus (#108):
- Add _MAIN_INGREDIENT_SIGNALS to tag_inferrer.py (Chicken/Beef/Pork/Fish/Pasta/
  Vegetables/Eggs/Legumes/Grains/Cheese) — infers main:* tags from ingredient names
- Update browser_domains.py main_ingredient categories to use main:* tag queries
  instead of raw food terms; recipe_browser_fts now has full 3.19M row coverage
  (was ~1.2K before backfill)

Bug fixes:
- Fix community posts response shape (#96): add total/page/page_size fields
- Fix export endpoint arg types (#92)
- Fix household invite store leak (#93)
- Fix receipts endpoint issues
- Fix saved_recipes endpoint
- Add session endpoint (app/api/endpoints/session.py)

Shopping list:
- Add migration 033_shopping_list.sql
- Add shopping schemas (app/models/schemas/shopping.py)
- Add ShoppingView.vue, ShoppingItemRow.vue, shopping.ts store

Frontend:
- InventoryList, RecipesView, RecipeDetailPanel polish
- App.vue routing updates for shopping view

Docs:
- Add user-facing docs under docs/ (getting-started, user-guide, reference)
- Add screenshots
2026-04-18 15:38:56 -07:00
890216a1f0 fix: wire recipe corpus to cloud per-user DBs via SQLite ATTACH (#102)
Some checks are pending
CI / Backend (Python) (push) Waiting to run
CI / Frontend (Vue) (push) Waiting to run
Mirror / mirror (push) Waiting to run
Cloud mode: attach shared read-only corpus DB (RECIPE_DB_PATH env var)
as "corpus" schema so per-user SQLite DBs can access 3.19M recipes.
All corpus table references now use self._cp prefix ("corpus." in cloud,
"" in local). FTS5 pseudo-column kept unqualified per SQLite spec.
compose.cloud.yml: bind-mount /Library/Assets/kiwi/kiwi.db read-only.

Also fix batch of audit issues:
- #101: OCR approval used source="receipt_ocr" for inventory_items — use "receipt"
- #89/#100: Shopping confirm-purchase used source="shopping_list" — use "manual"
- #103: Frontend inventory filter sent ?status= but API expects ?item_status=
- #104: InventoryItemUpdate schema missing purchase_date field; store.py allowed set also missing it
- #105: Guest cookie Secure flag tied to CLOUD_MODE instead of X-Forwarded-Proto; broke HTTP direct-port access
2026-04-18 14:21:56 -07:00
8483b9ae5f feat: add Plausible analytics to Vue SPA and docs
Some checks failed
CI / Backend (Python) (push) Has been cancelled
CI / Frontend (Vue) (push) Has been cancelled
Mirror / mirror (push) Has been cancelled
2026-04-16 21:15:56 -07:00
23a2e8fe38 feat: remove and reorder meal types in weekly planner
Some checks are pending
CI / Backend (Python) (push) Waiting to run
CI / Frontend (Vue) (push) Waiting to run
Mirror / mirror (push) Waiting to run
- MealPlanGrid: edit-mode toggle (visible when >1 meal type) reveals
  per-row ↑/↓ reorder and ✕ remove controls; label column expands to
  auto width via CSS class swap
- mealPlan store: removeMealType() and reorderMealTypes() — both send
  the full updated array to the existing PATCH /meal-plans/{id} endpoint
- MealPlanView: wires remove-meal-type and reorder-meal-types emits;
  shares mealTypeAdding loading flag to disable controls during save
- Guard: remove disabled when only one type remains (always keep ≥1)
2026-04-16 15:13:59 -07:00
6aa63cf2f0 chore: bump version to 0.3.0
Some checks failed
CI / Backend (Python) (push) Waiting to run
CI / Frontend (Vue) (push) Waiting to run
Mirror / mirror (push) Has been cancelled
Release / release (push) Has been cancelled
2026-04-16 14:24:16 -07:00
e745ce4375 feat: wire meal planner slot editor and meal type picker
Slot click now opens an inline editor panel:
- Pick from saved recipes via dropdown (pre-loaded on mount)
- Or type a custom label
- Clear slot button when a slot is already filled
- Save/Cancel with loading state

Add meal type opens a chip picker showing the types not yet active
(breakfast / lunch / snack minus whatever is already on the plan).
Selecting one calls the new PATCH /meal-plans/{plan_id} endpoint.

Backend:
- PATCH /meal-plans/{plan_id} with UpdatePlanRequest(meal_types)
- store.update_meal_plan_types() UPDATE ... RETURNING *
- 409 on IntegrityError in create_plan (already in place)
2026-04-16 14:23:38 -07:00
de0008f5c7 fix: meal planner auto-selects current week on load, + New week idempotent
- Add autoSelectPlan() to the store: after loadPlans() resolves, set
  activePlan to the current week's plan (or most recent) without a second
  API round-trip -- list already returns full PlanSummary with slots
- Call autoSelectPlan(mondayOfCurrentWeek()) in onMounted so the grid
  populates immediately without the user touching the dropdown
- Make onNewPlan idempotent: if a 409 comes back, activate the existing
  plan for that week instead of surfacing an error to the user
2026-04-16 10:50:34 -07:00
dbaf2b6ac8 fix: meal planner week add button crashing on r.name / add duplicate guard
- Fix sqlite3.OperationalError: the recipes table uses `title` not `name`;
  get_plan_slots JOIN was crashing every list_plans call with a 500,
  making the + New week button appear broken (plans were being created
  silently but the selector refresh always failed)
- Add migration 032 to add UNIQUE INDEX on meal_plans(week_start)
  to prevent duplicate plans accumulating while the button was broken
- Raise HTTP 409 on IntegrityError in create_plan so duplicates produce
  a clear error instead of a 500
- Fix mondayOfCurrentWeek to build the date string from local date parts
  instead of toISOString(), which converts through UTC and can produce the
  wrong calendar day for UTC+ timezones
- Add planCreating/planError state to MealPlanView so button shows
  "Creating..." during the request and displays errors inline
2026-04-16 10:46:28 -07:00
9a277f9b42 fix: barcode scan performance + timeout + success message
- Refactor _lookup_in_database to accept a shared httpx.AsyncClient so
  all three Open*Facts database attempts reuse one TLS connection instead
  of opening a new one per call; restores pre-fallback scan speed
- Increase recipe suggest timeout to 120s (was 30s) to survive cf-orch
  model cold-start on first request of a session
- Include product brand in barcode scan success message so the user can
  clearly see what was found (e.g. "Added: Cheerios (General Mills) to pantry")
2026-04-16 09:57:53 -07:00
200a6ef87b feat(recipes): complexity badges, time hints, Surprise Me, Just Pick One
#55 — Complexity rating on recipe cards:
  - Derived from direction text via _classify_method_complexity()
  - Badge displayed on every card: easy (green), moderate (amber), involved (red)
  - Filterable via complexity filter chips in the results bar

#58 — Cooking time + difficulty as filter domains:
  - estimated_time_min derived from step count + complexity
  - Time hint (~Nm) shown on every card
  - complexity_filter and max_time_min fields in RecipeRequest
  - Both applied in the engine before suggestions are built

#53 — Surprise Me: picks a random suggestion from the filtered pool,
  avoids repeating the last pick. Shown in a spotlight card.

#57 — Just Pick One: surfaces the top-matched suggestion in the same
  spotlight card. One tap to commit to cooking it.

Closes #55, #58, #53, #57
2026-04-16 09:27:34 -07:00
c8fdc21c29 feat(export): JSON full-backup download (pantry + saved recipes)
Adds GET /export/json that bundles inventory and saved recipes into a
single timestamped JSON file for data portability. The export envelope
includes schema version and export timestamp so future import logic can
handle version differences.

Frontend: new primary-styled JSON download button in the Export card with
a short description of what is included.

Closes #62
2026-04-16 09:16:33 -07:00
2ad71f2636 feat(recipes): pantry match floor filter — 'can make now' toggle
Adds pantry_match_only flag to RecipeRequest. When enabled, any recipe
with one or more missing ingredients (after swaps) is excluded from
results. Swapped ingredients count as covered.

Frontend: toggle checkbox in recipe settings panel, disabled when
shopping mode is active (the two modes are mutually exclusive). Hides
the max-missing input when pantry-match-only is on (irrelevant there).

Closes #63
2026-04-16 09:12:24 -07:00
0de6182f48 feat(scan): barcode miss fallback chain — Open Beauty Facts + Open Products Facts
When a barcode is not found in Open Food Facts, the service now tries
Open Beauty Facts and Open Products Facts before giving up. All three
share the same API format; only the host URL differs.

When all databases miss, the scan endpoint sets needs_manual_entry=true
in the result. The frontend detects this, shows a calm informational
message, and switches to manual entry mode automatically.

Also fixes a latent bug where not-found scans showed 'Added: item to
pantry' due to the success condition checking barcodes_found (always 1)
instead of added_to_inventory.

Closes #65
2026-04-16 08:30:49 -07:00
fb18a9c78c feat: partial consumption tracking and waste/disposal logging (#12 #60)
#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 #12
Closes #60
2026-04-16 07:28:21 -07:00
443e68ba3f fix: wire recipe engine to cf-text service instead of vllm
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
2026-04-16 06:25:46 -07:00
64a0abebe3 feat: pantry intel cluster — #61 expiry display, #64 cook log, #66 scaling, #59 open-package tracking
#61: expiry badge now shows relative + calendar date ("5d · Apr 15") with
tooltip "Expires in 5 days (Apr 15)"; traffic-light colors already in place

#64: RecipeDetailPanel.handleCook() calls recipesStore.logCook(); SavedRecipesPanel
shows "Last made: X ago" below each card using cookLog entries

#66: Serving multiplier (1x/2x/3x/4x) in RecipeDetailPanel scales ingredient
quantities using regex; handles integers, decimals, fractions (1/2, 3/4),
mixed numbers (1 1/2), and ranges (2-3); leaves unrecognised strings unchanged

#59: migration 030 adds opened_date column; ExpirationPredictor gains
SHELF_LIFE_AFTER_OPENING table + days_after_opening(); POST /inventory/items/{id}/open
sets opened_date=today and returns computed opened_expiry_date; InventoryList
shows lock-open button for unopened items and an "📂 5d · Apr 15" badge once opened
2026-04-16 06:01:25 -07:00
4423373750 feat: screenshot attachment in feedback form (#82)
Some checks are pending
CI / Backend (Python) (push) Waiting to run
CI / Frontend (Vue) (push) Waiting to run
Mirror / mirror (push) Waiting to run
- 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
2026-04-15 23:08:02 -07:00
76516abd62 feat: metric/imperial unit preference (#81)
Some checks are pending
CI / Backend (Python) (push) Waiting to run
CI / Frontend (Vue) (push) Waiting to run
Mirror / mirror (push) Waiting to run
- 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
2026-04-15 23:04:29 -07:00
757f779030 ci: add GitHub Actions CI for public credibility badge
Some checks are pending
CI / Backend (Python) (push) Waiting to run
CI / Frontend (Vue) (push) Waiting to run
Mirror / mirror (push) Waiting to run
Lean self-contained workflow — no Forgejo-specific secrets.
circuitforge-core installs from Forgejo git (public repo).
Forgejo (.forgejo/workflows/ci.yml) remains the canonical CI.

Backend: ruff + pytest | Frontend: vue-tsc only (no vitest yet)
2026-04-15 20:20:13 -07:00
c984e6162e ci: wire Forgejo Actions CI, mirror, and release workflows
Some checks are pending
CI / Backend (Python) (push) Waiting to run
CI / Frontend (Vue) (push) Waiting to run
Mirror / mirror (push) Waiting to run
- .forgejo/workflows/ci.yml — backend (ruff + pytest) + frontend (vue-tsc)
- .forgejo/workflows/mirror.yml — push to GitHub + Codeberg on main/tags
- .forgejo/workflows/release.yml — git-cliff changelog + Forgejo release on v* tags
- .cliff.toml — conventional commits changelog config

Frontend CI runs typecheck only (no vitest yet; tracked separately).
circuitforge-core installed from Forgejo git (public; not on PyPI).
Docker push disabled pending BSL registry policy (cf-agents#3).

Closes #23
2026-04-15 20:10:57 -07:00
ab97af73f7 Merge pull request 'fix(a11y): accessibility and ND-design audit fixes (#42-#48, #54, #80)' (#85) from fix/a11y-audit into main 2026-04-15 10:21:13 -07:00
6741c6981d fix(kiwi-a11y): chip-remove touch targets, btn-link undo target, star rating label, notes aria-expanded 2026-04-15 10:12:17 -07:00
5c135d0860 fix(kiwi-a11y): undo toast for 'I cooked this' dismiss action (#45) 2026-04-15 10:06:31 -07:00
bc04739447 fix(kiwi-a11y): SavedRecipesPanel empty state, remove confirmation, notes expand (#43 #44 #48) 2026-04-15 10:01:41 -07:00
ceb03f8b5b fix(kiwi-a11y): ND/calm-UX policy fixes — deficit language, wildcard styling, depletion framing (#42 #46 #47 #80-M)
- #42: Replace deficit framing — "You'd need:" → "To complete this recipe:", element_gaps
  card-warning → card-secondary, missing/gap chips status-warning → status-info,
  "Your pantry is missing..." → "These would expand your options:"
- #46: Add activeNutritionFilterCount computed; show count in Advanced filters summary
  when filters are active so it's visible while collapsed
- #47: Wildcard confirmation status-warning → status-info, copy updated to calm framing;
  wildcard recipe card badge status-warning → status-info
- M1: Add re-search hint below Hard Day Mode toggle when results are already showing
- M8: Move swap candidates collapsible to after directions/steps section
- L2: Add autocomplete="off" to filter search, constraint, and allergy text inputs
- L5: Add title="This is an affiliate link" disclosure to grocery affiliate links

Items already correct (no change needed):
- M2: Level description already always visible via activeLevel computed
- M3: Rate limit copy already using calm framing
- M5: No-results copy already calm
- M6: levelLabels already uses full names
- M7: "that's part of the fun" was part of the wildcard copy fixed under #47
- L1: Neon/konami handler not present in this file
2026-04-15 09:57:48 -07:00
9de42c3088 fix(kiwi-a11y): tab focus, silent fail, emoji labels, form for/id pairs (H3-H8, #80) 2026-04-15 09:53:15 -07:00
41837f348c fix(kiwi-a11y): darken light-mode muted text to #7a5c2e for WCAG 1.4.3 AA (H1, #80) 2026-04-15 09:51:26 -07:00
4de4f63614 fix(kiwi-a11y): btn-icon touch targets; aria-busy loading; role=alert on error (C4-C6, #80) 2026-04-15 09:48:19 -07:00
391e79ac86 fix(kiwi-a11y): deep watchers for constraint/allergy persistence (#54) 2026-04-15 09:43:54 -07:00
91724caf96 fix(kiwi-a11y): persist constraint and allergy preferences to localStorage (#54) 2026-04-15 09:42:13 -07:00
fdc477b395 fix(kiwi-fe): MealPlanView strict TS split index type narrowing 2026-04-14 15:55:38 -07:00
33c619b6b5 feat(kiwi-fe): wire OrchUsagePill into RecipesView and Settings opt-in toggle
- Import and mount OrchUsagePill near the recipe level selector in RecipesView;
  pill is self-hiding when not enabled or no lifetime key is present
- Add useOrchUsage composable to SettingsView with a Display section checkbox
  so users can opt in to seeing the cloud recipe call budget pill
- Add @/ path alias to vite.config.ts and tsconfig.app.json to resolve the
  existing @/services/api import in useOrchUsage.ts (fixes vite build error)
- tsc --noEmit and vite build both pass clean
2026-04-14 15:51:34 -07:00
1ae54c370d feat(kiwi-fe): add OrchUsagePill component with calm low-budget state 2026-04-14 15:46:58 -07:00
b4f8bde952 feat(kiwi-fe): add useOrchUsage composable with opt-in localStorage persistence 2026-04-14 15:46:12 -07:00
bdfbc963b7 feat(kiwi-fe): add getOrchUsage API call and OrchUsage type 2026-04-14 15:45:22 -07:00
99e9cbb8c1 refactor(kiwi): remove unused LIFETIME_SOURCES import from recipes.py 2026-04-14 15:44:42 -07:00
006582b179 feat(kiwi): add /orch-usage proxy endpoint for frontend budget display 2026-04-14 15:42:58 -07:00
1a6898324c feat(kiwi): merge meal planner feature into main
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
2026-04-14 15:37:57 -07:00
01216b82c3 feat(kiwi): gate L3/L4 recipes behind orch budget check; fallback to L2 on exhaustion 2026-04-14 15:24:57 -07:00
2071540a56 feat(kiwi): add Heimdall orch budget client with fail-open semantics 2026-04-14 15:15:43 -07:00
bd73ca0b6d fix(tests): correct build endpoint test fixture
- Use monkeypatch.setattr to patch cloud_session._LOCAL_KIWI_DB
  instead of wrong KIWI_DB_PATH env var (module-level singleton
  computed at import time; env var had no effect)
- Assert id > 0 (real persisted DB id) instead of -1 (old
  pre-persistence sentinel value)
2026-04-14 14:57:16 -07:00
9941227fae chore: merge main into feature/meal-planner
Resolves three conflicts:
- app/api/routes.py: fixed saved_recipes-before-recipes ordering from main;
  meal_plans and community_router from feature branch
- app/db/store.py: meal plan/prep session methods (feature) + community
  pseudonym methods (main) -- both additive
- app/tiers.py: KIWI_BYOK_UNLOCKABLE includes meal_plan_llm,
  meal_plan_llm_timing (feature) and community_fork_adapt (main)
2026-04-14 14:53:52 -07:00