# LEANMAPPER V4 — ARCHITECTURE & PLAN DE LOTS
> Vidéo téléphone → nuage de points métrique → mesh STL/OBJ → entités éditables → .leanplan.json → LeanHub
> Version 1.0 — 2026-07-17 — Kerbau

---

## 1. DÉCISION AMONT À TRANCHER (avant tout lot)

Les notes de release que tu as collées mentionnent **ggml, Vulkan, C API** — ce n'est PAS le repo officiel
Robbyant (PyTorch/CUDA 12.8). C'est un **port C++ communautaire** (type lingbot.cpp).

| Critère | Officiel (PyTorch) | Port ggml/Vulkan |
|---|---|---|
| Maturité | Repo Robbyant, maintenu | Communautaire, "C API may need review" (leurs mots) |
| VRAM | ~13.3 Go CUDA | Potentiellement moins, Vulkan |
| Intégration Python | Native (Open3D direct) | Via bindings C → friction |
| De-ghosting/TSDF/métrique | À vérifier version | Présent dans les notes (surface fusion, ICP, loop-closure, TSDF) |
| Risque | Faible | Moyen (API instable) |

**Recommandation** : partir sur l'**officiel PyTorch** pour la V4.0. L'architecture ci-dessous rend le moteur
**interchangeable** : il communique uniquement par fichiers (`.ply` + `poses.json` en sortie). Si le port ggml
mûrit, on le branche sans toucher au reste.

---

## 2. ARCHITECTURE GLOBALE

```
[P: téléphone]
  vidéo MP4 (720p, mouvements lents)
      │  upload (PWA ou scp)
      ▼
┌─────────────────────────────── M (Ubuntu, RTX 5070 Ti) ───────────────────────────────┐
│                                                                                        │
│  ÉTAGE 1 — RECONSTRUCTION (env conda isolé "lingbot-map", GPU exclusif*)               │
│  reconstruct.py : MP4 → LingBot-Map → scene_raw.ply (métrique) + poses.json            │
│                                                                                        │
│  ÉTAGE 2 — MESH (Open3D, CPU ok)                                                       │
│  mesh_pipeline.py : scene_raw.ply → TSDF/Poisson → scene.obj / scene.stl               │
│  clean_scene.py   : RANSAC sol+murs → floor.obj, walls.obj, objects.ply                │
│                                                                                        │
│  ÉTAGE 3 — SEGMENTATION ENTITÉS (Open3D, CPU)                                          │
│  segment.py : DBSCAN sur objects.ply → entity_001.obj … + entities.json                │
│               (bbox, dimensions, position, classe heuristique machine/table/divers)    │
│                                                                                        │
│  ÉTAGE 4 — API (FastAPI, port 8120 "leanmapper-api")                                   │
│  POST /jobs (upload vidéo) · GET /jobs/{id}/status · GET /jobs/{id}/scene              │
│  GET/PUT /scenes/{id}/entities · POST /scenes/{id}/export (obj|stl|leanplan)           │
│                                                                                        │
│  ÉTAGE 5 — STUDIO (PWA three.js, servie par l'API, studio.kerbau.fr éventuel)          │
│  Viewer 3D + sélection entité + gizmos translate/rotate/scale + snap sol               │
│  Export : .leanplan.json (projection top-down 2D) / scene modifiée .obj/.stl           │
│                                                                                        │
│  ÉTAGE 6 — LEANHUB (port 8090)                                                         │
│  Reçoit les .leanplan.json — consommés ensuite par LeanSpaghetti                       │
└────────────────────────────────────────────────────────────────────────────────────────┘

* VRAM : LingBot-Map ~13.3 Go → arrêter Ollama pendant une reconstruction (pattern connu :
  contention VRAM → `systemctl stop ollama` avant, restart après). Jamais les deux en même temps.
```

### Principes verrouillés
1. **Couplage par fichiers uniquement** entre étages (PLY/OBJ/JSON). Chaque étage testable seul en CLI.
2. **Édition non destructive** : `entities.json` porte les transforms (translation, rotation, scale) ;
   les OBJ sources ne sont jamais réécrits. L'export applique les transforms à la volée.
3. **Échelle métrique** dès l'étage 1 (mode métrique LingBot-Map) → pas de calibrage manuel comme en v3.
4. **STL = géométrie seule** (pas de couleur) → pour impression 3D / CAO. **OBJ+MTL = couleur** → pour
   le studio et Blender. Les deux exports depuis le même mesh.

### Format `entities.json` (contrat central — à figer au lot C1)
```json
{
  "_type": "leanscene", "_version": "1.0",
  "name": "Poste assemblage L2", "unit": "m",
  "floor": {"mesh": "floor.obj", "z": 0.0},
  "entities": [
    {
      "id": "ent_001", "mesh": "entity_001.obj",
      "class": "machine", "label": "Presse",
      "bbox": {"w": 1.2, "d": 0.8, "h": 1.9},
      "transform": {"t": [0,0,0], "r_z_deg": 0, "s": 1.0},
      "locked": false
    }
  ]
}
```

---

## 3. PLAN DE LOTS — À DISTRIBUER AUX MODÈLES

Chaque lot = 1 session, 1 prompt, livrable testable seul. Fournir au modèle : ce document (section 2)
+ le contrat `entities.json` + les fichiers d'entrée du lot. Rien d'autre.

Légende modèles (du moins cher au plus cher) :
- **QL** = Qwen2.5-14b local via Bob (coût zéro)
- **H** = Claude Haiku 4.5, sans réflexion
- **S** = Claude Sonnet 4.6, sans réflexion
- **S+** = Claude Sonnet 4.6, réflexion étendue
- **O+** = Opus/Fable, réflexion — à réserver, cher

### PHASE A — Moteur de reconstruction
| Lot | Contenu | Livrable | Modèle |
|---|---|---|---|
| A1 | Installation LingBot-Map officiel sur M : conda py3.10, torch 2.9.1 cu128, FlashInfer, Kaolin (build source), download checkpoint, smoke test sur vidéo demo | Env fonctionnel + `INSTALL_LOG.md` | **S** (debug install = erreurs concrètes, pas de raisonnement profond) |
| A2 | `reconstruct.py` : MP4 → frames (ffmpeg) → LingBot-Map mode métrique + de-ghosting → `scene_raw.ply` + `poses.json` ; options `--keyframe_interval`, `--mask_sky` | Script CLI | **S** |
| A3 | Wrapper `run_reconstruction.sh` : stop ollama → job → restart ollama, logs, codes retour | Script bash | **H** ou **QL** |

### PHASE B — Mesh & exports
| Lot | Contenu | Livrable | Modèle |
|---|---|---|---|
| B1 | `mesh_pipeline.py` (Open3D) : PLY → outlier removal → normales → Poisson OU TSDF → décimation cible 200k faces → `scene.obj` + `scene.stl` | Script CLI | **S** |
| B2 | `clean_scene.py` : RANSAC itératif → extraction sol (plan z min) + murs (plans verticaux) → `floor.obj`, `walls.obj`, `objects.ply` ; remise à plat du sol (z=0) | Script CLI | **S+** (choix des seuils géométriques, cas dégénérés) |

### PHASE C — Segmentation entités
| Lot | Contenu | Livrable | Modèle |
|---|---|---|---|
| C1 | `segment.py` : DBSCAN sur `objects.ply` → 1 mesh par cluster (`entity_NNN.obj`) + génération `entities.json` (contrat §2) avec bbox métriques | Script + contrat figé | **S+** (paramètres eps/min_points dépendants de la densité — itératif) |
| C2 | Classification heuristique : règles dimensionnelles bbox → machine / table / rack / divers (éditable ensuite dans le studio, pas besoin d'IA) | Fonction dans segment.py | **H** |

### PHASE D — Studio d'édition (PWA three.js, single-file, charte Kerbau)
| Lot | Contenu | Livrable | Modèle |
|---|---|---|---|
| D1 | Viewer : charge `entities.json` + OBJs via l'API, OrbitControls, grille sol, liste entités latérale | `studio.html` v1 | **S** |
| D2 | Édition : clic → sélection, TransformControls (translate XY, rotate Z, scale uniforme), snap sol, verrouillage, undo simple, PUT vers l'API | studio v2 | **S+** (interactions 3D + état) |
| D3 | Exports : projection top-down → `.leanplan.json` (format v3 existant, compatible LeanHub) ; export OBJ/STL avec transforms appliqués (côté API, trimesh) | studio v3 + endpoint | **S** |
| D4 | Habillage charte Kerbau (Space Mono/Sora, thème terminal), PWA manifest, QR local | studio v4 | **H** |

### PHASE E — API & intégration
| Lot | Contenu | Livrable | Modèle |
|---|---|---|---|
| E1 | `leanmapper_api.py` FastAPI port 8120 : jobs (upload vidéo, statut, lancement pipeline A→C en tâche de fond), CRUD scènes/entités, service du studio | API v1 + systemd | **S** |
| E2 | LeanHub minimal port 8090 : POST/GET/LIST `.leanplan.json`, page index listant les plans | `leanhub_api.py` + systemd | **H** |
| E3 | Bout-en-bout : vidéo test réelle (ton poste de travail) → plan dans LeanHub ; doc `LEANMAPPER_V4_TEST.md` | Validation | toi + **S** si blocage |

### Lots à escalader en **O+** uniquement si :
- A1 échoue de façon non triviale (conflits CUDA/Kaolin/FlashInfer croisés)
- La reconstruction sort une géométrie incohérente et il faut arbitrer entre moteurs (officiel vs port ggml, review du C API)
- D2 : bug d'état 3D irrésoluble après 2 sessions S+

---

## 4. ORDRE D'EXÉCUTION & JALONS

```
A1 → A2 → B1  ......... JALON 1 : "je vois mon atelier en 3D dans Blender" (STL/OBJ ✓)
     └→ A3 (parallèle)
B2 → C1 → C2  ......... JALON 2 : "mes machines sont des objets séparés"
E1 → D1 → D2  ......... JALON 3 : "je déplace une machine à l'écran"
D3 → E2 → D4 → E3 ..... JALON 4 : "le plan arrive dans LeanHub" — V4.0 livrée
```

Le besoin STL/OBJ est couvert dès le **JALON 1** (3 lots). Tout le reste est de l'édition/confort.

## 5. RISQUES CONNUS
1. **VRAM** : jamais Ollama + LingBot-Map simultanés (lot A3 le garantit).
2. **Repo jeune** : figer le commit LingBot-Map dans `INSTALL_LOG.md` (pattern lingbot-desktop-mac : checkout d'un commit précis).
3. **Kaolin sans wheel torch 2.9** : build source, prévoir CUDA toolkit local — c'est LE point dur de A1.
4. **Segmentation imparfaite** : DBSCAN fusionnera parfois 2 machines collées → prévoir dans D2 un outil "scinder" en V4.1, pas en V4.0.
5. **Vidéo téléphone** : mouvements lents, pas de zoom, 720p — écrire une consigne de capture d'1 page pour les clients (réutilisable SRP).
