Grounding pipeline and recipe-search settings API #70

Member

Maestro task: 53afd59c-6c35-4af7-904c-6c167d1ad69d (Feature 10 - Recipe Grounding and Photos).

Done-condition

recipes.Ground(ctx, dishName, query) runs search -> fetch/extract top candidates -> rank -> CachePhoto -> normalize_recipe -> persists recipe, ingredients, steps and photo in one transaction and returns Grounded{recipe_id, photo_id, method, minutes_total, difficulty, source_url, site_name} or a typed ErrUngroundable with reasons; GroundURL(url) for a pasted URL; GroundMany concurrent (limit 3) with per-item failure isolation; a stored source_url is reused without refetching. GET/PATCH /api/v1/settings/recipe-search (provider, base_url, Brave key via secretbox with last4) and POST /api/v1/settings/recipe-search/test (searches 'chicken tikka masala', returns count or error). Tests with fake search/fetch/LLM cover the happy path, ungroundable dish, one failing sibling not affecting others, URL grounding, stored-recipe reuse and that the Brave key never appears in responses. Go quality floor passes.

Evidence

  • New grounding pipeline + settings API; migration 0007 adds recipes.photo_id/method/difficulty/minutes_total and search settings api_key_last4; CachePhoto split into DownloadPhoto + transactional CachePhoto with rollback cleanup.
  • gofmt/vet/go test -race/build pass; sqlc-check no drift.
  • Note: Grounder is not yet wired into cmd/server (nothing calls it yet); routes were exercised through the real router with real SQLite/secretbox and a local SearXNG-style server.
Maestro task: 53afd59c-6c35-4af7-904c-6c167d1ad69d (Feature 10 - Recipe Grounding and Photos). ## Done-condition recipes.Ground(ctx, dishName, query) runs search -> fetch/extract top candidates -> rank -> CachePhoto -> normalize_recipe -> persists recipe, ingredients, steps and photo in one transaction and returns Grounded{recipe_id, photo_id, method, minutes_total, difficulty, source_url, site_name} or a typed ErrUngroundable with reasons; GroundURL(url) for a pasted URL; GroundMany concurrent (limit 3) with per-item failure isolation; a stored source_url is reused without refetching. GET/PATCH /api/v1/settings/recipe-search (provider, base_url, Brave key via secretbox with last4) and POST /api/v1/settings/recipe-search/test (searches 'chicken tikka masala', returns count or error). Tests with fake search/fetch/LLM cover the happy path, ungroundable dish, one failing sibling not affecting others, URL grounding, stored-recipe reuse and that the Brave key never appears in responses. Go quality floor passes. ## Evidence - New grounding pipeline + settings API; migration 0007 adds recipes.photo_id/method/difficulty/minutes_total and search settings api_key_last4; CachePhoto split into DownloadPhoto + transactional CachePhoto with rollback cleanup. - gofmt/vet/go test -race/build pass; sqlc-check no drift. - Note: Grounder is not yet wired into cmd/server (nothing calls it yet); routes were exercised through the real router with real SQLite/secretbox and a local SearXNG-style server.
dev_agent added 1 commit 2026-10-05 15:11:11 -04:00
Grounding pipeline and recipe-search settings API
CI / ci (push) Successful in 1m49s
CI / ci (pull_request) Successful in 1m48s
ceedc45309
recipes.Grounder ties search, fetch/extract, Rank, the photo cache and
normalize_recipe together. Ground(ctx, dishName, query) reuses a stored
source_url among the top candidates without fetching, otherwise tries the
ranked recipes best first and stores the photo, recipe, ingredients and
steps in one transaction, returning Grounded{recipe_id, photo_id, method,
minutes_total, difficulty, source_url, site_name} or a typed
*ErrUngroundable listing every reason. GroundURL grounds a pasted URL the
same way; GroundMany grounds items 3 at a time with per-item results.

PhotoCache is split into DownloadPhoto (fetch, decode, size check, no
writes) and CachePhoto(ctx, tx, photo), which records the row in the
caller's transaction; RemovePhotoFiles cleans up after a rollback.

Migration 0007 adds photo_id, method, difficulty and minutes_total to
recipes, and api_key_last4 to recipe_search_settings. SearXNG is now
called at recipe_search_settings.base_url.

GET/PATCH /api/v1/settings/recipe-search (Brave key sealed with
secretbox, only api_key_set and api_key_last4 returned) and POST
/api/v1/settings/recipe-search/test (searches "chicken tikka masala",
returns the count, 409 not_configured or 502 search_failed).

Maestro task 53afd59c-6c35-4af7-904c-6c167d1ad69d.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
overseer added 1 commit 2026-10-05 15:11:12 -04:00
Grounding pipeline and recipe-search settings API
CI / ci (push) Successful in 1m49s
CI / ci (pull_request) Successful in 1m48s
ceedc45309
recipes.Grounder ties search, fetch/extract, Rank, the photo cache and
normalize_recipe together. Ground(ctx, dishName, query) reuses a stored
source_url among the top candidates without fetching, otherwise tries the
ranked recipes best first and stores the photo, recipe, ingredients and
steps in one transaction, returning Grounded{recipe_id, photo_id, method,
minutes_total, difficulty, source_url, site_name} or a typed
*ErrUngroundable listing every reason. GroundURL grounds a pasted URL the
same way; GroundMany grounds items 3 at a time with per-item results.

PhotoCache is split into DownloadPhoto (fetch, decode, size check, no
writes) and CachePhoto(ctx, tx, photo), which records the row in the
caller's transaction; RemovePhotoFiles cleans up after a rollback.

Migration 0007 adds photo_id, method, difficulty and minutes_total to
recipes, and api_key_last4 to recipe_search_settings. SearXNG is now
called at recipe_search_settings.base_url.

GET/PATCH /api/v1/settings/recipe-search (Brave key sealed with
secretbox, only api_key_set and api_key_last4 returned) and POST
/api/v1/settings/recipe-search/test (searches "chicken tikka masala",
returns the count, 409 not_configured or 502 search_failed).

Maestro task 53afd59c-6c35-4af7-904c-6c167d1ad69d.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
review_agent requested changes 2026-10-05 15:13:34 -04:00
Dismissed
review_agent left a comment
Member

Independent Codex gpt-6-sol review: CHANGES_REQUIRED for this exact commit.

Independent Codex gpt-6-sol review: CHANGES_REQUIRED for this exact commit.
overseer added 1 commit 2026-10-05 15:23:44 -04:00
Rank stored candidates instead of reusing them first
CI / ci (push) Successful in 1m48s
CI / ci (pull_request) Successful in 1m53s
871a566800
Ground returned the first search result that was already stored before
fetching or ranking anything, so an unrelated stored recipe could win
over a relevant new one. Ground now ranks every candidate and walks the
ranking best first, reusing a stored recipe only at its ranked place.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
review_agent requested changes 2026-10-05 15:28:35 -04:00
Dismissed
review_agent left a comment
Member

Independent Codex gpt-6-sol review: CHANGES_REQUIRED for this exact commit.

Independent Codex gpt-6-sol review: CHANGES_REQUIRED for this exact commit.
overseer added 1 commit 2026-10-05 15:39:10 -04:00
Rank stored candidates on their stored recipe without refetching
CI / ci (push) Successful in 1m48s
CI / ci (pull_request) Successful in 1m48s
24d91b5bf6
Ground no longer fetches the page of a candidate whose source URL is
already stored: the stored recipe, its photo, ingredients and steps are
scored as a fetched page would be, so ranking still decides between it
and new candidates, and a stored page that is now unavailable can still
be reused. Rank's fetch-and-extract is split out behind a loader so
Rank and Ground share one scoring path.

The reuse test removes the stored page and checks that only the new
candidate is fetched; the ranking regression test lists the unrelated
stored recipe first so search order cannot pass it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
review_agent requested changes 2026-10-05 15:44:13 -04:00
Dismissed
review_agent left a comment
Member

Independent Codex gpt-6-sol review: CHANGES_REQUIRED for this exact commit.

Independent Codex gpt-6-sol review: CHANGES_REQUIRED for this exact commit.
overseer added 1 commit 2026-10-05 15:53:49 -04:00
Document grounding selection and pipeline order
CI / ci (push) Successful in 1m51s
CI / ci (pull_request) Successful in 1m50s
1cba099f40
The architecture of record still described the first stored search
result being returned before ranking. It now says ranking decides among
stored and new candidates, a stored source_url being scored on its stored
recipe without refetching its page.

State the exact per-candidate order in the docs and in ground: the photo
download and validation (DownloadPhoto) precedes normalize_recipe, and
CachePhoto records the photo in the same transaction as the recipe.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
review_agent left a comment
Member

Independent Codex gpt-6-sol review: APPROVED for this exact commit.

Independent Codex gpt-6-sol review: APPROVED for this exact commit.
approver_agent merged commit 33d46453db into feature/cebed295-8c05-453b-807b-8078dcd8d250-recipe-grounding 2026-10-05 16:05:06 -04:00
Sign in to join this conversation.
No Reviewers
No labels
3 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: Maniacs_Mansion/MealQuest#70