# Specification : `BOM.yaml` v1 — sourcier.shop

> Format standard de Bill of Materials pour projets makers / open hardware.
> Lu par sourcier.shop pour générer automatiquement un widget de panier
> avec liens affiliés et optimisation multi-vendeurs.

## Usage

Tout maker open hardware publie un fichier `BOM.yaml` à la racine de son
repo public GitHub. Sourcier.shop le fetch automatiquement via l'endpoint :

```
https://sourcier.shop/api/bom/{github_user}/{repo}
https://sourcier.shop/bom/{github_user}/{repo}        ← widget HTML
```

Aucun setup côté maker. Aucun account sourcier nécessaire. La publication
du `BOM.yaml` dans le repo public **est** l'inscription au service.

## Format minimal (1 item)

```yaml
project: mon-projet
items:
  - name: Roulement 608ZZ
    qty: 4
```

C'est suffisant : sourcier canonicalise par le `name`, trouve les vendors
par défaut, et génère le widget. Pas de friction.

## Format complet recommandé

```yaml
# Métadonnées du projet
project: forcair
version: "1.5"               # version / phase du BOM
description:                 # multilingue facultatif
  fr: Robot autonome de désherbage de pavés open hardware
  en: Open hardware autonomous paver weeding robot
url: https://github.com/SebE585/forcair
license: CERN-OHL-S-2.0
budget_target_eur: 75
budget_realistic_eur: 150
lifecycle: phase_1_achat     # idee | phase_0 | phase_1_achat | phase_1_assemblage | phase_2 | done | pause

# Items — tous les champs sont optionnels sauf `name` (au moins 1 langue)
items:
  # Item minimal monolingue
  - name: Roulement 608ZZ
    qty: 4

  # Item monolingue avec ref sourcier
  - ref: ELE-010
    name: L298N pont H double
    qty: 2
    notes: 4 moteurs traction (2 canaux × 2)

  # Item multilingue complet
  - ref: MEC-001
    name:
      fr: Roulement 608ZZ ABEC-7
      en: 608ZZ ABEC-7 ball bearing
    description:
      fr: Roulement à billes 8x22x7mm pour roues
      en: 8x22x7mm ball bearing for wheels
    qty: 4
    unit: pc                 # pc | lot | kg | m | rouleau | kit (default: pc)
    category: Mécanique - Roulements
    notes:
      fr: Pour les 4 hubs de roue Forcair
      en: For the 4 wheel hubs of Forcair

  # Item sans ref sourcier (toléré, fallback canonicalize sur le name)
  - name:
      fr: Brosse nylon perceuse 50mm
      en: Drill nylon brush 50mm
    qty: 1
    category: Brosse
    notes:
      fr: Test phase 0 manuel
```

## Champs

### Niveau projet (racine)

| Champ | Type | Obligatoire | Description |
|---|---|---|---|
| `project` | str | ✅ | slug du projet (snake_case ou kebab-case) |
| `version` / `phase` | str | non | version ou phase du BOM (ex `"1.5"`, `"phase_1"`) |
| `description` | str ou dict langue | non | description courte du projet |
| `url` | str | non | URL du repo source ou page projet |
| `license` | str | non | SPDX identifier de la licence (ex `CERN-OHL-S-2.0`, `MIT`, `CC-BY-SA-4.0`) |
| `budget_target_eur` | float | non | budget cible / annoncé |
| `budget_realistic_eur` | float | non | budget réaliste après audit |
| `lifecycle` | str | non | enum lifecycle (voir liste ci-dessus) |
| `items` | list | ✅ | liste des items (au moins 1) |

### Niveau item

| Champ | Type | Obligatoire | Description |
|---|---|---|---|
| `name` | str ou dict langue | ✅ | nom humain de l'item (au moins 1 langue) |
| `qty` | float | non (défaut 1) | quantité nominale pour le projet |
| `unit` | str | non (défaut `pc`) | pc / lot / kg / m / rouleau / kit |
| `ref` | str | non | référence interne projet (libre) |
| `sourcier_ref` | str | non | référence sourcier (`MEC-001`, `ELE-010`...) |
| `description` | str ou dict langue | non | description longue / spec |
| `category` | str | non | catégorie d'affichage |
| `notes` | str ou dict langue | non | notes libres |
| `spec` | str | non | specs techniques (ex `8x22x7mm`) |

### Multilingue

Tous les champs textuels (`name`, `description`, `notes`) acceptent **deux formats** :

**Format simple** (str monolingue) :
```yaml
name: Roulement 608ZZ
```

**Format dict langue** (multilingue) :
```yaml
name:
  fr: Roulement 608ZZ
  en: 608ZZ ball bearing
  de: 608ZZ Kugellager
```

L'API sourcier accepte un paramètre `?lang=fr|en|de` et renvoie la langue
demandée. Si la langue n'est pas dispo, fallback vers `en` puis `fr` puis
la première langue trouvée.

## Endpoints API sourcier

```
GET /api/bom/{user}/{repo}
GET /api/bom/{user}/{repo}?lang=en
GET /api/bom/{user}/{repo}?branch=main          (default: main, fallback master)
GET /api/bom/{user}/{repo}?country=fr           (vendors selon pays)

GET /bom/{user}/{repo}                          ← widget HTML

# Compatibilité legacy (BOM hardcodés)
GET /api/bom/{slug}                             (forcair, etc.)
GET /bom/{slug}
```

## Cache

Sourcier cache les `BOM.yaml` fetched depuis GitHub pendant **1 heure**.
Pour forcer un refresh : `?refresh=1`. Le cache évite de hammer l'API
GitHub (60 req/h sans token, 5000 req/h avec token).

## Versioning

Cette spec est versionnée. Le champ `version` du `BOM.yaml` ne référence
**PAS** la version de la spec, mais celle du projet. La version de la spec
suivie par sourcier est dans le User-Agent et dans les release notes
sourcier.

Spec actuelle : **v1.0** (2026-04-11).
