[Bug] Recipe ingredients are not automatically added to the product catalog #31

Closed
opened 2026-07-11 18:42:05 +00:00 by psycodepath · 0 comments
Owner

Description

Creating a recipe does not add its ingredients to the grocery product catalog automatically. Ingredients silently accumulate in the pending_taxonomy_items queue and only reach the catalog if someone manually runs bin/console grocery:process-taxonomy.

Root cause: commit 070542d ("queue taxonomy suggestions in SQLite and process asynchronously via CLI worker") changed RegisterRecipeIngredientsInCatalogListener from a synchronous catalog write (TaxonomySuggestor::resolveOrSuggest(...)) to an enqueue-only INSERT INTO pending_taxonomy_items. The catalog insertion moved into the grocery:process-taxonomy worker — but nothing runs that worker (no cron, systemd timer, compose service, or in-request trigger anywhere in the repo), so the queue is never drained.

The rest of the pipeline is healthy: Recipe::create() records RecipeCreated, the handler dispatches it, the listener is registered on the shared dispatcher, and enqueue/claim/save all resolve to the same per-tenant SQLite DB.

Reproduction Steps

  1. Ensure the grocery:process-taxonomy worker is not running.
  2. Create a recipe with a new ingredient (e.g. "Tomato").
  3. Open the product catalog (/grocery/catalog).

Current Behavior

"Tomato" is absent from the catalog. The row sits unseen in pending_taxonomy_items indefinitely.

Expected Behavior

"Tomato" appears in the catalog immediately after the recipe is saved, and is later enriched (category, shelf life, canonical unit) by the background worker.

Test Scenarios (Gherkin)

Feature: Recipe ingredients are registered in the product catalog

  Scenario: Bug - ingredients never reach the catalog automatically
    Given the taxonomy enrichment worker is not running
    When I create a recipe containing the ingredient "Tomato"
    Then "Tomato" should appear in the product catalog
    # Currently fails: it only lands in pending_taxonomy_items

  Scenario: Fix - ingredient is in the catalog immediately on recipe creation
    Given AI enrichment is disabled or the worker has not yet run
    When I create a recipe containing the ingredient "Tomato" with unit "pcs"
    Then "Tomato" is immediately present in the product catalog with unit "pcs"

  Scenario: Fix - background worker enriches the existing product in place
    Given a minimal catalog product "Tomato" exists and is queued for enrichment
    And AI enrichment is enabled
    When the taxonomy worker runs
    Then the existing "Tomato" product is updated with a category and shelf life
    And no duplicate catalog product is created

  Scenario: Edge case - duplicate and already-catalogued ingredients
    Given "Garlic" already exists in the catalog
    When I create a recipe containing "Garlic" and two spellings of "Tomato"
    Then exactly one new "Tomato" product is created
    And "Garlic" is not duplicated

Acceptance Criteria (When Fixed)

  • Bug is resolved and cannot be reproduced
  • Ingredients appear in the catalog immediately on recipe creation, independent of the worker
  • The background worker enriches existing products in place (no duplicates) and requeues on failure
  • A runner executes grocery:process-taxonomy automatically in production
  • All Gherkin scenarios above pass

Proposed Fix (hybrid)

  1. Synchronous (listener): immediately persist a minimal CatalogProduct (name, stemmed name, recipe unit), deduped by stemmed name, then enqueue for enrichment. Catalog is correct instantly even if the worker/Gemini is down.
  2. Background (worker): change grocery:process-taxonomy from create to enrich — load the existing product and update it in place via Gemini (category + shelf life + canonical unit). The pending queue is the "needs enrichment" marker.
  3. Runner: add a dedicated worker service to docker-compose.yml that polls the command on an interval.

Environment

  • OS: Linux
  • PHP version: 8.5
  • Tenant: default (also affects multi-tenant)
  • Date found: 2026-07-11

Additional Context

  • Module(s) affected: Grocery, Meals
  • Severity: high (user-facing feature broken since 070542d)
  • Follow-ups (out of scope): re-enqueue on recipe edit (Recipe::updateDetails records no event); semantic ingredient consolidation beyond stemmed-name dedup; confirm worker tenant coverage (tenants.json, falls back to default).

Notes

  • Scenarios must pass once the fix is implemented.
  • Reported via Claude Code investigation.
## Description Creating a recipe does **not** add its ingredients to the grocery product catalog automatically. Ingredients silently accumulate in the `pending_taxonomy_items` queue and only reach the catalog if someone manually runs `bin/console grocery:process-taxonomy`. **Root cause:** commit `070542d` ("queue taxonomy suggestions in SQLite and process asynchronously via CLI worker") changed `RegisterRecipeIngredientsInCatalogListener` from a synchronous catalog write (`TaxonomySuggestor::resolveOrSuggest(...)`) to an enqueue-only `INSERT INTO pending_taxonomy_items`. The catalog insertion moved into the `grocery:process-taxonomy` worker — but **nothing runs that worker** (no cron, systemd timer, compose service, or in-request trigger anywhere in the repo), so the queue is never drained. The rest of the pipeline is healthy: `Recipe::create()` records `RecipeCreated`, the handler dispatches it, the listener is registered on the shared dispatcher, and enqueue/claim/save all resolve to the same per-tenant SQLite DB. ## Reproduction Steps 1. Ensure the `grocery:process-taxonomy` worker is not running. 2. Create a recipe with a new ingredient (e.g. "Tomato"). 3. Open the product catalog (`/grocery/catalog`). ## Current Behavior "Tomato" is absent from the catalog. The row sits unseen in `pending_taxonomy_items` indefinitely. ## Expected Behavior "Tomato" appears in the catalog immediately after the recipe is saved, and is later enriched (category, shelf life, canonical unit) by the background worker. ## Test Scenarios (Gherkin) ```gherkin Feature: Recipe ingredients are registered in the product catalog Scenario: Bug - ingredients never reach the catalog automatically Given the taxonomy enrichment worker is not running When I create a recipe containing the ingredient "Tomato" Then "Tomato" should appear in the product catalog # Currently fails: it only lands in pending_taxonomy_items Scenario: Fix - ingredient is in the catalog immediately on recipe creation Given AI enrichment is disabled or the worker has not yet run When I create a recipe containing the ingredient "Tomato" with unit "pcs" Then "Tomato" is immediately present in the product catalog with unit "pcs" Scenario: Fix - background worker enriches the existing product in place Given a minimal catalog product "Tomato" exists and is queued for enrichment And AI enrichment is enabled When the taxonomy worker runs Then the existing "Tomato" product is updated with a category and shelf life And no duplicate catalog product is created Scenario: Edge case - duplicate and already-catalogued ingredients Given "Garlic" already exists in the catalog When I create a recipe containing "Garlic" and two spellings of "Tomato" Then exactly one new "Tomato" product is created And "Garlic" is not duplicated ``` ## Acceptance Criteria (When Fixed) - [ ] Bug is resolved and cannot be reproduced - [ ] Ingredients appear in the catalog immediately on recipe creation, independent of the worker - [ ] The background worker enriches existing products in place (no duplicates) and requeues on failure - [ ] A runner executes `grocery:process-taxonomy` automatically in production - [ ] All Gherkin scenarios above pass ## Proposed Fix (hybrid) 1. **Synchronous** (listener): immediately persist a minimal `CatalogProduct` (name, stemmed name, recipe unit), deduped by stemmed name, then enqueue for enrichment. Catalog is correct instantly even if the worker/Gemini is down. 2. **Background** (worker): change `grocery:process-taxonomy` from *create* to *enrich* — load the existing product and update it in place via Gemini (category + shelf life + canonical unit). The pending queue is the "needs enrichment" marker. 3. **Runner**: add a dedicated `worker` service to `docker-compose.yml` that polls the command on an interval. ## Environment - OS: Linux - PHP version: 8.5 - Tenant: default (also affects multi-tenant) - Date found: 2026-07-11 ## Additional Context - Module(s) affected: Grocery, Meals - Severity: high (user-facing feature broken since `070542d`) - Follow-ups (out of scope): re-enqueue on recipe edit (`Recipe::updateDetails` records no event); semantic ingredient consolidation beyond stemmed-name dedup; confirm worker tenant coverage (`tenants.json`, falls back to `default`). ## Notes - Scenarios must pass once the fix is implemented. - Reported via Claude Code investigation.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
psycodepath/hrp#31
No description provided.