Signaler une technique (tech step) absente du catalogue lors d'une correction d'étape #130

Closed
opened 2026-09-09 13:18:52 +02:00 by kyuno · 0 comments
Owner

Contexte

Origine : ticket #3, puce 3 — « Permettre d'ajouter des tech steps inconnus pour ceux qui manquent, afin d'aider à enrichir le catalogue ».

Le catalogue TechStep est de la donnée de référence : key @unique, seedé depuis reference-seed-data.ts's TECH_STEPS (schema.prisma:729-745). Aucun endpoint de création.

Le seul point d'entrée « édition de technique » est le TechStepCorrectionPopover, ouvert depuis StepDescription en mode editable sur le panneau de détail d'une recette dont on est l'auteur (RecipeDetailPanel, showActions — RecipeDetailPanel.tsx:230-235, StepDescription.tsx:84-109). Là, le CatalogSearchPicker ne propose que des TechStep existants (TechStepCorrectionPopover.tsx:672-686), et submitTechStepCorrectionSchema.correctedTechStepId doit matcher une ligne réelle (assertTechStepsExist → 404 TECH_STEP_NOT_FOUND, recipe-tech-step-correction.service.ts:68-88).

⇒ Impossible de dire « la technique ici n'existe pas encore dans le catalogue ».

Il existe déjà une boucle de feedback : StepTechStepCorrection → services/tech-step-llm-worker → TechStepTrainingSuggestion → revue manuelle (schema.prisma:978-1017). Mais elle ne sait relier une correction qu'à une technique connue.

Modèle de référence : les ingrédients hors-catalogue

Le pattern à répliquer existe déjà pour les ingrédients (schema.prisma:548-612, Ingredient.isPlaceholder) : ligne libre displayName + createdById + createdAt + reviewedAt, exclue des listes browsables (reference.service.ts:143-146), regroupée par nom normalisé dans /admin/catalogue (CatalogPage + catalog.ts) où un mainteneur la marque « traitée » ou purge les orphelines. L'ajout réel au catalogue reste une édition manuelle de reference-seed-data.ts + locales.

Comportement souhaité

Dans le TechStepCorrectionPopover, sous le CatalogSearchPicker de techniques, une échappatoire « Ma technique n'est pas dans la liste » ouvrant un champ texte libre. À la validation, la correction est enregistrée avec une technique placeholder (nommée par l'utilisateur), visible dans un onglet « techniques manquantes » de /admin/catalogue, à côté des ingrédients hors-catalogue.

Modèle de données (proposition)

TechStep (apps/api/prisma/schema.prisma) — colonnes additives calquées sur Ingredient :

model TechStep {
  id            Int       @id @default(autoincrement())
  key           String    @unique   // placeholder ⇒ `placeholder:<uuid>`
  /// NEW — true = technique libre saisie par un utilisateur en corrigeant
  /// une étape, absente du catalogue seedé. displayName non-null, exclue de
  /// `GET /reference/tech-steps` et du classifieur.
  isPlaceholder Boolean   @default(false) @map("is_placeholder")
  displayName   String?   @map("display_name")
  createdById   Int?      @map("created_by_id")
  createdAt     DateTime? @map("created_at")
  reviewedAt    DateTime? @map("reviewed_at")
  createdBy     UserProfile? @relation("TechStepPlaceholderCreator", fields: [createdById], references: [id], onDelete: SetNull)
  ...
  @@index([isPlaceholder])
}

Migration purement additive : colonnes nullable + @default(false), aucune ligne seedée touchée.

Alternative (à trancher) : ne pas toucher TechStep et modéliser un ProposedTechStep séparé — mais réutiliser isPlaceholder colle mieux au précédent ingrédient et garde StepTechStep.techStepId (FK) inchangée.

Backend

  • submitTechStepCorrectionSchema (packages/shared/src/schemas/recipe.ts:252-284) : accepter correctedTechStepName?: string en plus de correctedTechStepId?, mutuellement exclusifs (même forme que recipeIngredientInputSchema's ingredientId xor placeholderName).
  • submitTechStepCorrection (recipe-tech-step-correction.service.ts:413) : si correctedTechStepName, créer la ligne TechStep placeholder dans la transaction (comme resolveIngredientLines), puis continuer avec son id.
  • getTechSteps (reference.service.ts:82-88) : where: { isPlaceholder: false }.
  • toStepTechStepViews (recipe.service.ts:175-241) : TechStepView porte displayName/isPlaceholder (label = displayName ?? t(catalog.techSteps.<key>), même helper que les ingrédients).
  • Nouvelles routes admin /admin/api/catalog/tech-step-placeholders (list reviewed=, mark-reviewed, prune-orphans) — miroir de celles des ingrédients.
  • Event analytics tech_step.placeholder_created.
  • services/tech-step-llm-worker : hors scope ici, mais une technique placeholder est un signal de plus haute valeur qu'une correction ordinaire — à noter pour un ticket suivant.

Frontend

  • TechStepCorrectionPopover (TechStepCorrectionPopover.tsx:672) : bouton « pas dans la liste » sous le CatalogSearchPicker, champ texte, envoie correctedTechStepName. Pill « à compléter » sur une technique placeholder posée (comme ingredient-row__placeholder-badge).
  • StepDescription : highlight de technique placeholder au style distinct.
  • CatalogPage (/admin/catalogue) : onglet « Techniques manquantes » réutilisant PlaceholderGroupCard / le regroupement par nom normalisé.
  • i18n recipes.techStepCorrection.notInListButton, admin.catalog.techStepTab, etc.

Découpage proposé

  1. Modèle + contrat + création — migration TechStep.isPlaceholder, schéma packages/shared, submitTechStepCorrection crée la ligne, getTechSteps/toStepTechStepViews filtrent/exposent. Pas d'UI encore.
  2. UI correction — bouton « pas dans la liste » + champ + badge dans TechStepCorrectionPopover/StepDescription, e2e Gherkin.
  3. Revue admin — routes /admin/api/catalog/tech-step-placeholders, onglet dans CatalogPage.

Tests (obligatoires, cf. specs/dev-conventions.md)

  • Mocha : submitTechStepCorrection avec correctedTechStepName crée une TechStep isPlaceholder + la StepTechStep "manual" associée ; getTechSteps exclut les placeholders ; routes admin list/mark-reviewed/prune.
  • Cypress : mode layout TechStepCorrectionPopover (échappatoire visible, champ, validation) ; Gherkin détail recette → corriger une étape → « pas dans la liste » → badge visible ; mode layout onglet admin.

Documentation à mettre à jour

  • specs/batch-cooking-modele.md + specs/backend-architecture.md — tech_step (colonnes placeholder), section corrections tech-step, admin catalogue.

Points restant à trancher en revue

  • Réutiliser TechStep.isPlaceholder vs table ProposedTechStep séparée (voir plus haut).
  • Une technique placeholder est-elle proposée au CatalogSearchPicker des autres utilisateurs (mutualiser une même technique manquante entre recettes), ou strictement invisible jusqu'à promotion ? (invisible = aligné sur les ingrédients.)
  • Garde anti-doublon (nom normalisé déjà proposé) à la création, ou seulement au regroupement admin (comme les ingrédients) ?

🤖 Filed by Claude Code

## Contexte Origine : ticket #3, puce 3 — *« Permettre d'ajouter des tech steps inconnus pour ceux qui manquent, afin d'aider à enrichir le catalogue »*. Le catalogue `TechStep` est de la **donnée de référence** : `key @unique`, seedé depuis `reference-seed-data.ts`'s `TECH_STEPS` ([schema.prisma:729-745](apps/api/prisma/schema.prisma#L729-L745)). Aucun endpoint de création. Le seul point d'entrée « édition de technique » est le `TechStepCorrectionPopover`, ouvert depuis `StepDescription` en mode `editable` sur le **panneau de détail** d'une recette dont on est l'auteur (`RecipeDetailPanel`, `showActions` — [RecipeDetailPanel.tsx:230-235](apps/web/src/features/recipes/RecipeDetailPanel.tsx#L230-L235), [StepDescription.tsx:84-109](apps/web/src/features/recipes/steps/StepDescription.tsx#L84-L109)). Là, le `CatalogSearchPicker` ne propose **que** des `TechStep` existants ([TechStepCorrectionPopover.tsx:672-686](apps/web/src/features/recipes/steps/TechStepCorrectionPopover.tsx#L672-L686)), et `submitTechStepCorrectionSchema.correctedTechStepId` doit matcher une ligne réelle (`assertTechStepsExist` → 404 `TECH_STEP_NOT_FOUND`, [recipe-tech-step-correction.service.ts:68-88](apps/api/src/modules/recipe/recipe-tech-step-correction.service.ts#L68-L88)). ⇒ Impossible de dire « la technique ici n'existe pas encore dans le catalogue ». Il existe déjà une boucle de feedback : `StepTechStepCorrection` → `services/tech-step-llm-worker` → `TechStepTrainingSuggestion` → revue manuelle ([schema.prisma:978-1017](apps/api/prisma/schema.prisma#L978-L1017)). Mais elle ne sait relier une correction qu'à une technique **connue**. ## Modèle de référence : les ingrédients hors-catalogue Le pattern à répliquer existe déjà pour les ingrédients ([schema.prisma:548-612](apps/api/prisma/schema.prisma#L548-L612), `Ingredient.isPlaceholder`) : ligne libre `displayName` + `createdById` + `createdAt` + `reviewedAt`, exclue des listes browsables ([reference.service.ts:143-146](apps/api/src/modules/reference/reference.service.ts#L143-L146)), regroupée par nom normalisé dans `/admin/catalogue` (`CatalogPage` + `catalog.ts`) où un mainteneur la marque « traitée » ou purge les orphelines. L'ajout réel au catalogue reste une édition manuelle de `reference-seed-data.ts` + locales. ## Comportement souhaité Dans le `TechStepCorrectionPopover`, sous le `CatalogSearchPicker` de techniques, une échappatoire « **Ma technique n'est pas dans la liste** » ouvrant un champ texte libre. À la validation, la correction est enregistrée avec une **technique placeholder** (nommée par l'utilisateur), visible dans un onglet « techniques manquantes » de `/admin/catalogue`, à côté des ingrédients hors-catalogue. ## Modèle de données (proposition) `TechStep` (`apps/api/prisma/schema.prisma`) — colonnes additives calquées sur `Ingredient` : ```prisma model TechStep { id Int @id @default(autoincrement()) key String @unique // placeholder ⇒ `placeholder:<uuid>` /// NEW — true = technique libre saisie par un utilisateur en corrigeant /// une étape, absente du catalogue seedé. displayName non-null, exclue de /// `GET /reference/tech-steps` et du classifieur. isPlaceholder Boolean @default(false) @map("is_placeholder") displayName String? @map("display_name") createdById Int? @map("created_by_id") createdAt DateTime? @map("created_at") reviewedAt DateTime? @map("reviewed_at") createdBy UserProfile? @relation("TechStepPlaceholderCreator", fields: [createdById], references: [id], onDelete: SetNull) ... @@index([isPlaceholder]) } ``` Migration purement additive : colonnes nullable + `@default(false)`, aucune ligne seedée touchée. Alternative (à trancher) : ne pas toucher `TechStep` et modéliser un `ProposedTechStep` séparé — mais réutiliser `isPlaceholder` colle mieux au précédent ingrédient et garde `StepTechStep.techStepId` (FK) inchangée. ## Backend - `submitTechStepCorrectionSchema` ([packages/shared/src/schemas/recipe.ts:252-284](packages/shared/src/schemas/recipe.ts#L252-L284)) : accepter `correctedTechStepName?: string` en plus de `correctedTechStepId?`, mutuellement exclusifs (même forme que `recipeIngredientInputSchema`'s `ingredientId` xor `placeholderName`). - `submitTechStepCorrection` ([recipe-tech-step-correction.service.ts:413](apps/api/src/modules/recipe/recipe-tech-step-correction.service.ts#L413)) : si `correctedTechStepName`, créer la ligne `TechStep` placeholder dans la transaction (comme `resolveIngredientLines`), puis continuer avec son id. - `getTechSteps` ([reference.service.ts:82-88](apps/api/src/modules/reference/reference.service.ts#L82-L88)) : `where: { isPlaceholder: false }`. - `toStepTechStepViews` ([recipe.service.ts:175-241](apps/api/src/modules/recipe/recipe.service.ts#L175-L241)) : `TechStepView` porte `displayName`/`isPlaceholder` (label = `displayName ?? t(catalog.techSteps.<key>)`, même helper que les ingrédients). - Nouvelles routes admin `/admin/api/catalog/tech-step-placeholders` (list `reviewed=`, `mark-reviewed`, `prune-orphans`) — miroir de celles des ingrédients. - Event analytics `tech_step.placeholder_created`. - `services/tech-step-llm-worker` : hors scope ici, mais une technique placeholder est un signal de plus haute valeur qu'une correction ordinaire — à noter pour un ticket suivant. ## Frontend - `TechStepCorrectionPopover` ([TechStepCorrectionPopover.tsx:672](apps/web/src/features/recipes/steps/TechStepCorrectionPopover.tsx#L672)) : bouton « pas dans la liste » sous le `CatalogSearchPicker`, champ texte, envoie `correctedTechStepName`. Pill « à compléter » sur une technique placeholder posée (comme `ingredient-row__placeholder-badge`). - `StepDescription` : highlight de technique placeholder au style distinct. - `CatalogPage` (`/admin/catalogue`) : onglet « Techniques manquantes » réutilisant `PlaceholderGroupCard` / le regroupement par nom normalisé. - i18n `recipes.techStepCorrection.notInListButton`, `admin.catalog.techStepTab`, etc. ## Découpage proposé 1. **Modèle + contrat + création** — migration `TechStep.isPlaceholder`, schéma `packages/shared`, `submitTechStepCorrection` crée la ligne, `getTechSteps`/`toStepTechStepViews` filtrent/exposent. Pas d'UI encore. 2. **UI correction** — bouton « pas dans la liste » + champ + badge dans `TechStepCorrectionPopover`/`StepDescription`, e2e Gherkin. 3. **Revue admin** — routes `/admin/api/catalog/tech-step-placeholders`, onglet dans `CatalogPage`. ## Tests (obligatoires, cf. `specs/dev-conventions.md`) - **Mocha** : `submitTechStepCorrection` avec `correctedTechStepName` crée une `TechStep` `isPlaceholder` + la `StepTechStep` `"manual"` associée ; `getTechSteps` exclut les placeholders ; routes admin list/mark-reviewed/prune. - **Cypress** : mode layout `TechStepCorrectionPopover` (échappatoire visible, champ, validation) ; Gherkin détail recette → corriger une étape → « pas dans la liste » → badge visible ; mode layout onglet admin. ## Documentation à mettre à jour - `specs/batch-cooking-modele.md` + `specs/backend-architecture.md` — `tech_step` (colonnes placeholder), section corrections tech-step, admin catalogue. ## Points restant à trancher en revue - Réutiliser `TechStep.isPlaceholder` vs table `ProposedTechStep` séparée (voir plus haut). - Une technique placeholder est-elle proposée au `CatalogSearchPicker` des **autres** utilisateurs (mutualiser une même technique manquante entre recettes), ou strictement invisible jusqu'à promotion ? (invisible = aligné sur les ingrédients.) - Garde anti-doublon (nom normalisé déjà proposé) à la création, ou seulement au regroupement admin (comme les ingrédients) ? --- 🤖 Filed by [Claude Code](https://claude.com/claude-code)
kyuno added the
enhancement
planned
labels 2026-09-09 13:18:52 +02:00
kyuno added the
High
label 2026-09-09 13:25:26 +02:00
kyuno closed this issue 2026-09-09 16:25:29 +02:00
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: kyuno/batchCooking#130
No description provided.