fix(recipes): persiste les métadonnées de clause des tech-steps à l'édition #26

Merged
kyuno merged 1 commit from fix/recipe-tech-step-metadata-on-update into main 2026-09-04 18:19:21 +02:00
Owner

Le bug

recipe.service.ts a deux chemins d'écriture des StepTechStep :

technique (span) ingredients utensils temperatures
createRecipeInternal (import + création) ✅ ✅ ✅ ✅
updateRecipe (PATCH /recipes/:id) ✅ ❌ ❌ ❌
scripts/backfill-tech-steps.ts ✅ ❌ ❌ ❌

matchStepsTechSteps calcule pourtant les trois pour les deux chemins — updateRecipe et le backfill les ignoraient simplement. La divergence existe depuis 5506279 (#75, ingrédients/ustensiles) ; 189df73 (#22, températures) l'a reproduite.

Conséquence : toute recette ré-enregistrée via l'UI perd les ingrédients/ustensiles/températures de ses techniques ; toute recette importée avant le 29/08/2026 n'a jamais eu de températures (la migration 20260829120000 ne crée que la table). Résultat concret sur la prod : cooking-optimizer.ts ne voit jamais de CookingTemperatureView, donc detectApplianceTemperatureConflicts() ne peut pas signaler « four requis à 150 °C et 200 °C » — le préchauffage des recettes concernées est fusionné sans température ni note d'enchaînement. (Constaté sur batch.dev.kyuno.fr : gratin dauphinois Préchauffer … 150 °C + moussaka préchauffer … 200 °C fusionnés en une tâche sans température, zéro note de conflit.)

Changements

  1. toStepTechStepCreateInputs (lib/recipe-matching/tech-step-persistence.ts) — seule source de vérité du payload techSteps.create (technique + ingredients/utensils/temperatures). createRecipeInternal, updateRecipe et backfill-tech-steps.ts l'utilisent tous les trois : ils ne peuvent plus diverger. createRecipeInternal passe de ~40 lignes inline à un appel.
  2. backfill-tech-steps.ts — createMany → create par ligne (createMany ne fait pas les écritures imbriquées) + doc à jour.
  3. Rattrapage automatique au déploiement (choix « Auto, complet ») :
    • model BackfillState + migration backfill_state (registre d'idempotence).
    • scripts/backfill-runtime.ts ajouté au CMD Docker après seed-runtime. Gardé par un marqueur → tourne une seule fois ; une panne du backfill est loggée sans faire échouer le boot (le serveur démarre, retente au prochain start).
    • reset-db.ts : backfill_state ajouté au TRUNCATE.
  4. Test PATCH /recipes/:id — une température de clause survit à l'édition (mêmes assertions que le test POST « extracts a step's temperature mentions », via PATCH).

Compromis assumé du rattrapage

backfill-runtime.ts lance backfillTechSteps() entier : il supprime/recrée tous les StepTechStep source:"auto" contre le modèle NLP actuellement déployé. Il ne re-fait donc pas que les métadonnées — des tags de technique peuvent bouger sur des recettes existantes si le modèle a évolué depuis leur dernier matching (ex. les confusions Préchauffer→bake, Enfourner→melt déjà connues). C'est le prix d'une remise à niveau complète et automatique ; pas de garde-fou F1 ici (contrairement à retrain-tech-steps.ts) — l'objectif est d'aligner l'existant sur ce que produisent déjà les nouveaux imports. Premier boot post-déploiement : un appel intent-service par Step (quelques minutes, une fois ; le service est healthy avant le CMD via depends_on).

Vérification

  • ✅ pnpm --filter api exec tsc --noEmit
  • ✅ pnpm --filter api build (les 2 nouveaux scripts compilent dans dist/)
  • ✅ pnpm lint (biome, 311 fichiers)
  • ✅ migration = SQL généré par Prisma pour le modèle, à l'identique
  • ❌ Mocha recipe.test.ts non lancé — Postgres + tech-step-intent-service requis, indisponibles ici. À jouer en CI / sur la machine de dev.

Le job test de la CI est déjà rouge sur main (bug pré-existant resetDatabase / seedReferenceData : Unique constraint failed on (key)), sans rapport avec cette PR. lint et build doivent passer.

🤖 Generated with Claude Code

## Le bug `recipe.service.ts` a deux chemins d'écriture des `StepTechStep` : | | technique (span) | `ingredients` | `utensils` | `temperatures` | |---|:---:|:---:|:---:|:---:| | `createRecipeInternal` (import + création) | ✅ | ✅ | ✅ | ✅ | | `updateRecipe` (PATCH /recipes/:id) | ✅ | ❌ | ❌ | ❌ | | `scripts/backfill-tech-steps.ts` | ✅ | ❌ | ❌ | ❌ | `matchStepsTechSteps` calcule pourtant les trois pour les deux chemins — `updateRecipe` et le backfill les ignoraient simplement. La divergence existe depuis `5506279` (#75, ingrédients/ustensiles) ; `189df73` (#22, températures) l'a reproduite. **Conséquence :** toute recette **ré-enregistrée via l'UI** perd les ingrédients/ustensiles/températures de ses techniques ; toute recette **importée avant le 29/08/2026** n'a jamais eu de températures (la migration `20260829120000` ne crée que la table). Résultat concret sur la prod : `cooking-optimizer.ts` ne voit jamais de `CookingTemperatureView`, donc `detectApplianceTemperatureConflicts()` ne peut pas signaler « four requis à 150 °C **et** 200 °C » — le préchauffage des recettes concernées est fusionné sans température ni note d'enchaînement. (Constaté sur `batch.dev.kyuno.fr` : gratin dauphinois `Préchauffer … 150 °C` + moussaka `préchauffer … 200 °C` fusionnés en une tâche sans température, zéro note de conflit.) ## Changements 1. **`toStepTechStepCreateInputs`** (`lib/recipe-matching/tech-step-persistence.ts`) — seule source de vérité du payload `techSteps.create` (technique + `ingredients`/`utensils`/`temperatures`). `createRecipeInternal`, `updateRecipe` et `backfill-tech-steps.ts` l'utilisent tous les trois : ils ne peuvent plus diverger. `createRecipeInternal` passe de ~40 lignes inline à un appel. 2. **`backfill-tech-steps.ts`** — `createMany` → `create` par ligne (`createMany` ne fait pas les écritures imbriquées) + doc à jour. 3. **Rattrapage automatique au déploiement** (choix « Auto, complet ») : - `model BackfillState` + migration `backfill_state` (registre d'idempotence). - `scripts/backfill-runtime.ts` ajouté au `CMD` Docker après `seed-runtime`. Gardé par un marqueur → tourne **une seule fois** ; une panne du backfill est loggée sans faire échouer le boot (le serveur démarre, retente au prochain start). - `reset-db.ts` : `backfill_state` ajouté au `TRUNCATE`. 4. **Test** `PATCH /recipes/:id` — une température de clause survit à l'édition (mêmes assertions que le test POST « extracts a step's temperature mentions », via PATCH). ## Compromis assumé du rattrapage `backfill-runtime.ts` lance `backfillTechSteps()` **entier** : il supprime/recrée tous les `StepTechStep` `source:"auto"` contre le modèle NLP **actuellement déployé**. Il ne re-fait donc pas que les métadonnées — des tags de technique peuvent bouger sur des recettes existantes si le modèle a évolué depuis leur dernier matching (ex. les confusions `Préchauffer`→`bake`, `Enfourner`→`melt` déjà connues). C'est le prix d'une remise à niveau complète et automatique ; pas de garde-fou F1 ici (contrairement à `retrain-tech-steps.ts`) — l'objectif est d'aligner l'existant sur ce que produisent déjà les nouveaux imports. Premier boot post-déploiement : un appel `intent-service` par `Step` (quelques minutes, une fois ; le service est `healthy` avant le `CMD` via `depends_on`). ## Vérification - ✅ `pnpm --filter api exec tsc --noEmit` - ✅ `pnpm --filter api build` (les 2 nouveaux scripts compilent dans `dist/`) - ✅ `pnpm lint` (biome, 311 fichiers) - ✅ migration = SQL généré par Prisma pour le modèle, à l'identique - ❌ **Mocha `recipe.test.ts` non lancé** — Postgres + `tech-step-intent-service` requis, indisponibles ici. À jouer en CI / sur la machine de dev. > Le job `test` de la CI est **déjà rouge sur `main`** (bug pré-existant `resetDatabase` / `seedReferenceData` : `Unique constraint failed on (key)`), sans rapport avec cette PR. `lint` et `build` doivent passer. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
kyuno added 1 commit 2026-09-04 09:08:59 +02:00
fix(recipes): persiste les métadonnées de clause des tech-steps à l'édition
All checks were successful
CI / build (push) Successful in 2m44s
CI / lint (push) Successful in 2m50s
CI / intent-service-test (push) Successful in 4m36s
CI / e2e (push) Successful in 8m12s
CI / test (push) Successful in 14m57s
ec01fc0107
`createRecipeInternal` écrivait, pour chaque technique détectée, ses
ingrédients / ustensiles / températures de clause ; `updateRecipe` et le
script `backfill-tech-steps.ts` n'écrivaient que le span de la technique.
Toute recette ré-enregistrée via l'UI (ou touchée par le backfill) perdait
donc silencieusement ces métadonnées — et `cooking-optimizer.ts` ne pouvait
plus détecter de conflit de four entre deux préchauffages dès qu'une recette
avait été éditée une fois (ni sur les imports antérieurs à la détection des
températures, jamais ré-enregistrés).

- `toStepTechStepCreateInputs` (nouveau, `lib/recipe-matching/`) : seule
  source de vérité du payload `techSteps.create`, utilisée par les trois
  appelants pour qu'ils ne divergent plus.
- `backfill-tech-steps.ts` : `createMany` -> `create` par ligne (createMany
  ne fait pas les écritures imbriquées) + doc à jour.
- Rattrapage automatique au déploiement : `model BackfillState` + migration
  `backfill_state`, script `backfill-runtime.ts` ajouté au CMD Docker après
  `seed-runtime`. Gardé par un marqueur (tourne une seule fois), n'échoue
  jamais le boot (une panne du backfill est loggée, le serveur démarre
  quand même, retente au prochain start).
- Test `PATCH /recipes/:id` : une température de clause survit à l'édition.
- `reset-db.ts` : `backfill_state` ajouté au TRUNCATE.

Vérifié : `tsc --noEmit` + `pnpm --filter api build` + `pnpm lint` OK ;
migration = SQL généré par Prisma pour le modèle, à l'identique. Mocha
`recipe.test.ts` non lancé (Postgres + intent-service requis, indisponibles
ici) — à jouer en CI / sur la machine de dev.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
kyuno merged commit 4c0a72a1e3 into main 2026-09-04 18:19:21 +02:00
Sign in to join this conversation.
No reviewers
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: kyuno/batchCooking#26
No description provided.