{
  "version": "3.0",
  "format": "sprint-contracts",
  "format_spec": {
    "description": "Chaque tâche suit le pattern Sprint Contract (Anthropic Harness Design). verification_steps = critères testables négociés AVANT implémentation. Un agent ne peut marquer 'completed' que si TOUS les steps passent.",
    "source": "https://www.anthropic.com/engineering/harness-design-long-running-apps",
    "rules": [
      "Toute nouvelle tâche DOIT avoir verification_steps AVANT de commencer",
      "verification_steps sont des actions concrètes (curl, test, check), pas des descriptions",
      "status = completed UNIQUEMENT si tous les verification_steps.passes = true",
      "1 feature par session Claude Code max (anti context-anxiety)"
    ]
  },
  "phase": "PHASE_1B — Agents specialises + awareness multi-sessions",
  "date_approved": "2026-03-21T14:35:00Z",
  "approval_status": "✅ APPROVED BY GILLES — Je veux go",
  "tasks": [
    {
      "id": "s3-3",
      "title": "Profil joueur cross-games",
      "description": "Dashboard/endpoint agrégé montrant stats joueur across tous les jeux Laludora.",
      "priority": "haute",
      "effort": "L",
      "status": "pending",
      "assignee": "claude",
      "plan": ".planning/PLAN-SPRINT3.md",
      "current_subtask": "1.2-GATEWAY",
      "verification_steps": [
        {
          "step": "curl http://100.116.248.8:8088/api/profile/<player_id> retourne stats agrégées",
          "passes": false
        },
        {
          "step": "Chaque jeu (Semantique, Litere, Footix) contribue au profil",
          "passes": false
        },
        {
          "step": "Page /profil accessible depuis le Portail (100.116.248.8:8090)",
          "passes": false
        },
        {
          "step": "Page /leaderboard affiche top 100 cross-games",
          "passes": false
        },
        {
          "step": "Test depuis Mac (100.77.179.8) — pas localhost",
          "passes": false
        }
      ],
      "notes": " [2026-04-11 cleanup: repasse pending, pas de session active]"
    },
    {
      "id": "s3-4",
      "title": "Design system audit complet (Tailwind consistency)",
      "description": "Audit exhaustif Tailwind CSS — couleurs, spacing, typographie homogènes across Portail + Jeux.",
      "priority": "moyenne",
      "effort": "M",
      "status": "pending",
      "assignee": "claude",
      "verification_steps": [
        {
          "step": "Fichier design-tokens.json généré avec palette + spacing + fonts",
          "passes": false
        },
        {
          "step": "Audit report HTML généré par quality-engine/agents/design.py",
          "passes": false
        },
        {
          "step": "Tous les jeux utilisent les mêmes tokens Tailwind",
          "passes": false
        },
        {
          "step": "Aucune divergence couleur > 10% détectée entre jeux",
          "passes": false
        }
      ]
    },
    {
      "id": "s3-5",
      "title": "Leaderboard global platform-wide",
      "description": "Leaderboard cross-games agrégé (tous les jeux Laludora).",
      "priority": "basse",
      "effort": "L",
      "status": "pending",
      "assignee": "claude",
      "verification_steps": [
        {
          "step": "curl http://100.116.248.8:8088/api/leaderboard retourne top 100 JSON",
          "passes": false
        },
        {
          "step": "Scores pondérés par jeu (pas simple addition)",
          "passes": false
        },
        {
          "step": "Page /leaderboard responsive mobile + desktop",
          "passes": false
        },
        {
          "step": "Test depuis Mac (100.77.179.8) — pas localhost",
          "passes": false
        }
      ]
    },
    {
      "id": "rack-1",
      "title": "Commander ORICO 8848RC3 + Inter-Tech X-3531",
      "description": "ORICO 8848RC3 : $119 livraison gratuite sur oricotechs.com (boîtier 4 bays 2.5\", USB-C 10Gbps, JBOD). Inter-Tech X-3531 : 173 RON sur eMAG.ro (pour futur boîtier 3D quand imprimante dispo).",
      "priority": "haute",
      "effort": "S",
      "status": "pending",
      "assignee": "gilles",
      "tags": [
        "nas",
        "hardware",
        "rack"
      ],
      "verification_steps": [
        {
          "step": "ORICO 8848RC3 commandé sur oricotechs.com",
          "passes": true
        },
        {
          "step": "ORICO 8848RC3 reçu et branché USB-C sur Lenovo M920Q",
          "passes": false
        },
        {
          "step": "4 bays visibles dans TrueNAS (lsblk ou disques UI)",
          "passes": false
        }
      ]
    },
    {
      "id": "rack-2",
      "title": "Shuck les disques Seagate Extension",
      "description": "Retirer les 2 disques Seagate 1To de leurs boîtiers USB externes pour récupérer les SATA nus.",
      "priority": "haute",
      "effort": "S",
      "status": "pending",
      "assignee": "gilles",
      "depends_on": [
        "rack-1"
      ],
      "tags": [
        "nas",
        "hardware"
      ],
      "verification_steps": [
        {
          "step": "2 disques SATA nus extraits sans dommage",
          "passes": false
        },
        {
          "step": "SMART check OK sur chaque disque (smartctl -a)",
          "passes": false
        }
      ]
    },
    {
      "id": "rack-4",
      "title": "TrueNAS — créer pool ZFS data RAID-Z1 (3 HDD rack)",
      "description": "Connecter le rack via USB. Créer pool RAID-Z1 avec WD 1To + Seagate 1To + Seagate 1To. 2To utilisables, tolère 1 panne.",
      "priority": "haute",
      "effort": "M",
      "status": "pending",
      "assignee": "claude",
      "depends_on": [
        "rack-3"
      ],
      "tags": [
        "nas",
        "zfs",
        "truenas",
        "raid"
      ],
      "verification_steps": [
        {
          "step": "Rack connecté USB, 4 disques visibles dans TrueNAS",
          "passes": false
        },
        {
          "step": "Pool data RAID-Z1 créé — zpool status = ONLINE",
          "passes": false
        },
        {
          "step": "Capacité utile ~2To confirmée",
          "passes": false
        },
        {
          "step": "Test resilience : zpool offline 1 disque → toujours DEGRADED pas FAULTED",
          "passes": false
        }
      ]
    },
    {
      "id": "rack-5",
      "title": "TrueNAS — créer pool fast-backup (Kingston rack Bay 1)",
      "description": "Configurer le Kingston 480Go en Bay 1 du rack comme destination de snapshots du pool fast.",
      "priority": "moyenne",
      "effort": "S",
      "status": "pending",
      "assignee": "claude",
      "depends_on": [
        "rack-4"
      ],
      "tags": [
        "nas",
        "zfs",
        "backup"
      ],
      "verification_steps": [
        {
          "step": "Pool fast-backup créé et visible",
          "passes": false
        },
        {
          "step": "Snapshot manuel fast → fast-backup OK",
          "passes": false
        }
      ]
    },
    {
      "id": "rack-6",
      "title": "Migrer usb-data vers pool data",
      "description": "Déplacer les données actuelles de /mnt/usb-data (114Go) vers le pool data RAID-Z1. Reconfigurer tous les containers Docker pour pointer vers le nouveau chemin.",
      "priority": "haute",
      "effort": "L",
      "status": "pending",
      "assignee": "claude",
      "depends_on": [
        "rack-4"
      ],
      "tags": [
        "nas",
        "migration",
        "docker"
      ],
      "verification_steps": [
        {
          "step": "rsync usb-data → /mnt/data terminé sans erreur",
          "passes": false
        },
        {
          "step": "Tous les containers Docker redémarrés avec nouveaux chemins",
          "passes": false
        },
        {
          "step": "N8N, Paris, Laludora, Jellyfin fonctionnels post-migration",
          "passes": false
        },
        {
          "step": "Ancien WD USB débranché — aucun service ne plante",
          "passes": false
        }
      ]
    },
    {
      "id": "rack-7",
      "title": "Migrer volumes Docker vers pool fast",
      "description": "Déplacer les volumes Docker critiques (PostgreSQL gilles_os_db, paris_db, Redis) vers /mnt/fast pour bénéficier du SSD.",
      "priority": "moyenne",
      "effort": "M",
      "status": "pending",
      "assignee": "claude",
      "depends_on": [
        "rack-6"
      ],
      "tags": [
        "nas",
        "docker",
        "performance"
      ],
      "verification_steps": [
        {
          "step": "gilles_os_db sur SSD — chemin /mnt/fast/postgres",
          "passes": false
        },
        {
          "step": "paris_db sur SSD — chemin /mnt/fast/paris-db",
          "passes": false
        },
        {
          "step": "Temps de réponse DB amélioré (baseline avant / après)",
          "passes": false
        }
      ]
    },
    {
      "id": "rack-8",
      "title": "Configurer snapshots ZFS automatiques",
      "description": "Activer snapshots ZFS automatiques : fast → fast-backup quotidien, fast → data hebdo. Via TrueNAS Periodic Snapshot Tasks.",
      "priority": "moyenne",
      "effort": "S",
      "status": "pending",
      "assignee": "claude",
      "depends_on": [
        "rack-5",
        "rack-7"
      ],
      "tags": [
        "nas",
        "backup",
        "automation"
      ],
      "verification_steps": [
        {
          "step": "Periodic Snapshot Task quotidienne active sur pool fast",
          "passes": false
        },
        {
          "step": "Replication Task fast → fast-backup configurée",
          "passes": false
        },
        {
          "step": "Test : snapshot s est déclenché et visible",
          "passes": false
        }
      ]
    },
    {
      "id": "p1b-1",
      "title": "N8N — Reconnecter credentials Telegram",
      "description": "Après réinstallation N8N (2026-04-09), les credentials Telegram sont perdus. Workflows Surveillance NAS et Paris ont besoin d'être reconnectés via l'UI N8N. Le Push Matinal (OotI6FGkhA7qBjKS) utilise HTTP direct avec TELEGRAM_BOT_TOKEN env, pas de credentials.",
      "priority": "haute",
      "effort": "S",
      "status": "pending",
      "assignee": "gilles",
      "tags": [
        "n8n",
        "telegram",
        "infra"
      ],
      "verification_steps": [
        {
          "step": "Ouvrir N8N UI http://192.168.1.132:30109 → Settings → Credentials → recréer 'Telegram NAS Bot'",
          "passes": false
        },
        {
          "step": "Workflow Surveillance Services NAS : nœud Telegram rebranché sur nouveau credential",
          "passes": false
        }
      ],
      "notes": " [2026-04-11 cleanup: credential @GillesOS_bot deja fixe via n8n-tg-gillesos-fix. Verifier si cette tache concerne un bot different (Surveillance NAS / Paris) avant re-execution]"
    },
    {
      "id": "p1b-2",
      "title": "Tailscale — Nettoyer ancien nœud + stocker clé en vault",
      "description": "Après wipé Tailscale 2026-04-09 : nouvelle IP 100.116.248.8. Ancien nœud 'nas-domicile-1' (100.96.99.10) toujours présent dans admin tailscale.com → le supprimer. Stocker nouvelle auth key tskey-auth-kjxZn33q6A21CNTRL dans Vaultwarden.",
      "priority": "moyenne",
      "effort": "S",
      "status": "pending",
      "assignee": "gilles",
      "tags": [
        "tailscale",
        "security",
        "infra"
      ],
      "verification_steps": [
        {
          "step": "tailscale.com admin → supprimer nœud 100.96.99.10 / nas-domicile (ancien)",
          "passes": false
        },
        {
          "step": "Vaultwarden : stocker tskey-auth-kjxZn33q6A21CNTRL + date expiry Jul 2026",
          "passes": false
        }
      ]
    },
    {
      "id": "infra-1",
      "title": "Installer Lidarr via TrueNAS Apps",
      "description": "Lidarr absent du NAS. Installer comme TrueNAS App (pas Docker manuel). Connecter à slskd pour téléchargement auto musique.",
      "priority": "moyenne",
      "effort": "S",
      "status": "pending",
      "assignee": "claude",
      "verification_steps": [
        {
          "step": "curl http://100.116.248.8:8686/health retourne 200",
          "passes": false
        },
        {
          "step": "Lidarr visible dans TrueNAS Apps UI",
          "passes": false
        },
        {
          "step": "Lidarr connecté à slskd (settings > download clients)",
          "passes": false
        }
      ]
    },
    {
      "id": "infra-2",
      "title": "Migration services standard vers TrueNAS Apps",
      "description": "Migrer Radarr, Sonarr, qBittorrent, Vaultwarden depuis Docker manuel vers TrueNAS Apps. Services custom (paris, laludora, gilles-os) restent Docker manuel.",
      "priority": "basse",
      "effort": "L",
      "status": "pending",
      "assignee": "claude",
      "verification_steps": [
        {
          "step": "Radarr accessible via TrueNAS App, données migrées",
          "passes": false
        },
        {
          "step": "Sonarr accessible via TrueNAS App, données migrées",
          "passes": false
        },
        {
          "step": "qBittorrent accessible via TrueNAS App, données migrées",
          "passes": false
        },
        {
          "step": "Vaultwarden accessible via TrueNAS App, données migrées",
          "passes": false
        },
        {
          "step": "Anciens containers Docker manuels supprimés",
          "passes": false
        }
      ]
    },
    {
      "id": "infra-3",
      "title": "Ajouter appareils au système (télé Mi Stick, Pixel Tailscale)",
      "description": "Enregistrer tous les appareils dans config.json. Installer Jellyfin + Symfonium sur Mi Stick. Reconnecter Pixel Tailscale (offline 3j).",
      "priority": "moyenne",
      "effort": "S",
      "status": "pending",
      "assignee": "gilles",
      "verification_steps": [
        {
          "step": "config.json contient section 'devices' avec Mac, Pixel, Mi Stick",
          "passes": false
        },
        {
          "step": "Jellyfin accessible depuis Mi Stick sur http://100.116.248.8:30013",
          "passes": false
        },
        {
          "step": "Pixel visible dans tailscale status",
          "passes": false
        }
      ]
    },
    {
      "id": "infra-4",
      "title": "Jellyfin — scan bibliothèque + cover art",
      "description": "Activer chercheurs d'images (MusicBrainz, TheAudioDB), lancer scan complet, vérifier pochettes téléchargées.",
      "priority": "haute",
      "effort": "S",
      "status": "pending",
      "assignee": "gilles",
      "verification_steps": [
        {
          "step": "Chercheurs MusicBrainz + TheAudioDB activés dans settings bibliothèque Musiques",
          "passes": false
        },
        {
          "step": "Scan bibliothèque lancé et terminé sans erreur FileNotFoundException",
          "passes": false
        },
        {
          "step": "Pochettes visibles dans Feishin/Symfonium",
          "passes": false
        }
      ]
    },
    {
      "id": "infra-5",
      "title": "Vaultwarden — ajouter VAULTWARDEN_ADMIN_TOKEN dans .env.secrets",
      "description": "Admin panel désactivé faute de token. Générer token, l'ajouter dans /mnt/usb-data/.env.secrets, redémarrer container.",
      "priority": "moyenne",
      "effort": "XS",
      "status": "pending",
      "assignee": "claude",
      "verification_steps": [
        {
          "step": "curl http://100.116.248.8:8087/admin retourne 200 (pas 302 vers /)",
          "passes": false
        },
        {
          "step": "VAULTWARDEN_ADMIN_TOKEN présent dans .env.secrets",
          "passes": false
        }
      ],
      "notes": " [2026-04-11 cleanup: Vaultwarden restaure hier (1101 ciphers). Verifier si admin token deja present avant action]"
    },
    {
      "id": "infra-7",
      "title": "paris_scheduler — diagnostiquer et corriger Restarting",
      "description": "Container paris_scheduler en Restarting. Identifier cause (credentials expirés ? config manquante ?) et corriger.",
      "priority": "haute",
      "effort": "S",
      "status": "pending",
      "assignee": "claude",
      "verification_steps": [
        {
          "step": "docker ps | grep paris_scheduler affiche 'Up' (pas Restarting)",
          "passes": false
        },
        {
          "step": "docker logs paris_scheduler --tail 20 sans erreur fatale",
          "passes": false
        }
      ]
    },
    {
      "id": "p1b-3",
      "title": "Phase 1B — Créer agents Stratège, Inspecteur, Vie-perso",
      "description": "3 agents référencés dans le système mais sans code. Créer chacun avec structure minimale (FastAPI health check, prompt système, port dédié).",
      "priority": "haute",
      "effort": "L",
      "status": "pending",
      "assignee": "claude",
      "verification_steps": [
        {
          "step": "curl http://100.116.248.8:8098/health retourne 200 (stratège)",
          "passes": false
        },
        {
          "step": "curl http://100.116.248.8:8097/health retourne 200 (vie-perso)",
          "passes": false
        },
        {
          "step": "curl http://100.116.248.8:8096/health retourne 200 (inspecteur)",
          "passes": false
        }
      ]
    },
    {
      "id": "p1b-4",
      "title": "Phase 1B — Connecter agents à log_metric()",
      "description": "Les agents ne loggent pas leurs métriques. Table metrics vide depuis Phase 1A. Connecter tous les agents actifs à log_metric() pour alimenter le dashboard.",
      "priority": "haute",
      "effort": "M",
      "status": "pending",
      "assignee": "claude",
      "verification_steps": [
        {
          "step": "SELECT count(*) FROM metrics WHERE created_at > now()-interval '1 day' retourne > 0",
          "passes": false
        },
        {
          "step": "Dashboard 8100 affiche métriques en temps réel",
          "passes": false
        }
      ]
    },
    {
      "id": "p1b-5",
      "title": "Ollama — diagnostiquer crash + plan migration Legion Y530",
      "description": "Ollama crashé (Exited 2). Court terme : redémarrer sur NAS. Long terme : migrer sur Legion Y530 (GPU) quand réparé.",
      "priority": "moyenne",
      "effort": "S",
      "status": "pending",
      "assignee": "claude",
      "verification_steps": [
        {
          "step": "curl http://100.116.248.8:11434/api/tags retourne liste modèles",
          "passes": false
        },
        {
          "step": "Test génération qwen2.5:1.5b OK",
          "passes": false
        }
      ]
    },
    {
      "id": "nas-3d-1",
      "title": "Commander composants AliExpress pour boîtier 3D NAS",
      "description": "HBA LSI Inspur 9211-8i, riser PCIe Lenovo, 4x adaptateurs SATA, alim 12V 8A, contrôleur ventilateur PWM, visserie M3, aimants. ~70€ total.",
      "priority": "moyenne",
      "effort": "XS",
      "status": "pending",
      "assignee": "gilles",
      "verification_steps": [
        {
          "step": "Commande AliExpress passée (HBA + riser + adaptateurs SATA + alim + ventilateur)",
          "passes": false
        },
        {
          "step": "Commande Amazon.de passée (ventilateur 120mm Arctic P12 + répartiteur alim)",
          "passes": false
        }
      ]
    },
    {
      "id": "nas-3d-2",
      "title": "Imprimer boîtier 3D NAS M920Q (PETG obligatoire)",
      "description": "Imprimer makerworld.com/fr/models/1979199. PETG obligatoire pour résistance thermique. + grille ventilateur 120mm + carénage HBA.",
      "priority": "basse",
      "effort": "M",
      "status": "pending",
      "assignee": "gilles",
      "depends_on": "nas-3d-1",
      "verification_steps": [
        {
          "step": "Boîtier principal imprimé en PETG",
          "passes": false
        },
        {
          "step": "Grille ventilateur 120mm imprimée",
          "passes": false
        },
        {
          "step": "Carénage HBA imprimé (optionnel mais recommandé)",
          "passes": false
        }
      ]
    },
    {
      "id": "nas-3d-3",
      "title": "Assembler boîtier 3D + installer HBA + migrer disques depuis ORICO",
      "description": "Installer HBA dans M920Q via riser PCIe. Connecter 3x HDD (RAID-Z1 pool data). SSD Kingston dans ORICO (pool fast-backup). Valider ZFS.",
      "priority": "basse",
      "effort": "L",
      "status": "pending",
      "assignee": "gilles+claude",
      "depends_on": "nas-3d-2",
      "verification_steps": [
        {
          "step": "zpool status data affiche ONLINE RAID-Z1 (3x HDD)",
          "passes": false
        },
        {
          "step": "zpool status fast-backup affiche ONLINE (SSD ORICO)",
          "passes": false
        },
        {
          "step": "HBA visible dans TrueNAS (Storage > Disks)",
          "passes": false
        },
        {
          "step": "SMART actif sur les 3 HDD (pas 'SMART not supported')",
          "passes": false
        }
      ]
    },
    {
      "id": "appli-1",
      "title": "Reconfigurer Feishin Mac + Symfonium Pixel + Symfonium Mi Stick",
      "description": "Changer URL serveur Jellyfin vers 100.116.248.8:30013 dans toutes les applis. Installer Symfonium sur Mi Stick (Android TV).",
      "priority": "haute",
      "effort": "XS",
      "status": "pending",
      "assignee": "gilles",
      "verification_steps": [
        {
          "step": "Feishin Mac connecté et bibliothèque musicale visible",
          "passes": false
        },
        {
          "step": "Symfonium Pixel connecté à 100.116.248.8:30013",
          "passes": false
        },
        {
          "step": "Symfonium Mi Stick installé et connecté",
          "passes": false
        }
      ]
    },
    {
      "id": "vocal-1",
      "title": "Phase 2 Vocal — Auto-stop + auto-envoi SuperWhisper",
      "description": "Resoudre le flux vocal complet avec outil GRATUIT. Priorite : macOS Dictation (zero install, Fn Fn toggle, illimite) ou whisper.cpp/Vosk. SuperWhisper free trop limite (15min/mois). Flux cible : demarrer, parler, auto-stop silence, auto-coller, auto-envoyer via Hammerspoon.",
      "priority": "haute",
      "effort": "M",
      "status": "pending",
      "assignee": "claude",
      "tags": [
        "vocal",
        "superwhisper",
        "hammerspoon"
      ],
      "verification_steps": [
        {
          "step": "Parler dans le terminal → texte colle automatiquement sans Escape",
          "passes": false
        },
        {
          "step": "Dire 'go go' en fin de phrase → Entree automatique (Hammerspoon)",
          "passes": false
        },
        {
          "step": "Flux complet sans toucher le clavier sauf 1 geste (Fn toggle)",
          "passes": false
        }
      ]
    },
    {
      "id": "vocal-2",
      "title": "Phase 2 Vocal — Wake word 'Hey Gilles'",
      "description": "Implementer un wake word local (Vosk ou Porcupine) pour demarrer l'enregistrement sans toucher le clavier. Vosk installe, script a ecrire. Flux : Hey Gilles → enregistre → go go → arrete + colle + envoie.",
      "priority": "moyenne",
      "effort": "L",
      "status": "pending",
      "assignee": "claude",
      "depends_on": [
        "vocal-1"
      ],
      "tags": [
        "vocal",
        "wake-word",
        "vosk"
      ],
      "verification_steps": [
        {
          "step": "Dire 'Hey Gilles' → enregistrement demarre automatiquement",
          "passes": false
        },
        {
          "step": "Flux zero-clavier complet fonctionnel",
          "passes": false
        },
        {
          "step": "CPU < 5% en mode ecoute permanente",
          "passes": false
        }
      ]
    },
    {
      "id": "vocal-3",
      "title": "Phase 3 Vocal — Box connectee DIY (smart speaker)",
      "description": "Construire un smart speaker maison pour parler a Claude depuis n'importe quelle piece. Materiel dispo : JBL GO 2 (speaker BT), micros cravate, Pixel 9a. Option Raspberry Pi Zero (ref tweet Sajeel_Purewal 2026-04-09). Peut aussi utiliser le Mac M4 avec micro externe.",
      "priority": "basse",
      "effort": "XL",
      "status": "pending",
      "assignee": "gilles+claude",
      "depends_on": [
        "vocal-2"
      ],
      "tags": [
        "vocal",
        "hardware",
        "smart-speaker"
      ],
      "verification_steps": [
        {
          "step": "Parler a Claude depuis la cuisine sans etre devant le Mac",
          "passes": false
        },
        {
          "step": "Claude repond a voix haute via speaker",
          "passes": false
        },
        {
          "step": "Latence < 10 secondes entre fin de phrase et debut de reponse vocale",
          "passes": false
        }
      ]
    },
    {
      "id": "vocal-4",
      "title": "Commandes vocales navigation terminaux",
      "description": "Hammerspoon configure pour detecter commandes vocales dans le texte SuperWhisper : ouvre paris, ouvre laludora, terminal suivant, ferme terminal. VS Code keybindings deja configures (Ctrl+Alt+0-5). A tester et stabiliser.",
      "priority": "moyenne",
      "effort": "S",
      "status": "pending",
      "assignee": "claude",
      "tags": [
        "vocal",
        "hammerspoon",
        "vscode"
      ],
      "verification_steps": [
        {
          "step": "Dire 'ouvre paris' → nouveau terminal Claude Code dans projets/paris",
          "passes": false
        },
        {
          "step": "Dire 'terminal suivant' → bascule au terminal suivant",
          "passes": false
        },
        {
          "step": "Dire 'ferme terminal' → ferme le terminal courant",
          "passes": false
        }
      ],
      "notes": " [2026-04-11 cleanup: repasse pending, pas de session active]"
    },
    {
      "id": "achats-1",
      "title": "Bot Achats G — Assistant d'achat personnel multi-sources",
      "description": "Bot Telegram @achats_g_bot : chasseur de prix et d'opportunites pour achats personnels (neuf + occasion). Distinct de VintedBot (qui reste dedie au resell/arbitrage). Sources cibles : Vinted.ro/OLX/Lajumate/FB Marketplace (seconde main), eMAG/eExpress/Altex (neuf RO), Amazon/AliExpress (international), comparateurs Compari.ro/Price.ro. Tracker de prix avec alertes quand produit passe sous seuil. Gere wishlist + historique prix. Token bot deja dans .env.secrets (TELEGRAM_PERSONAL_BOT_TOKEN).",
      "priority": "moyenne",
      "effort": "XL",
      "status": "pending",
      "assignee": "claude",
      "tags": [
        "achats",
        "telegram",
        "scraping",
        "personnel"
      ],
      "verification_steps": [
        {
          "step": "Architecture decidee : reutiliser vintedbot ou projet separe",
          "passes": false
        },
        {
          "step": "Au moins 3 sources branchees et testees (Vinted.ro, OLX.ro, eMAG)",
          "passes": false
        },
        {
          "step": "Wishlist persistante (SQLite) avec seuil de prix par item",
          "passes": false
        },
        {
          "step": "Alerte Telegram @achats_g_bot quand prix passe sous seuil",
          "passes": false
        },
        {
          "step": "Historique prix garde sur 90 jours minimum",
          "passes": false
        },
        {
          "step": "Bot pilotable depuis N8N (start/stop/status)",
          "passes": false
        }
      ]
    },
    {
      "id": "medias-1",
      "title": "Jellyfin wake-on-request (economie RAM)",
      "description": "Mettre Jellyfin en demarrage a la demande via Sablier ou lazytainer. Gain estime : ~530 Mo RAM quand inactif. Jellyfin n'est utilise que ponctuellement pour films/series, la musique passe maintenant par Navidrome (leger, toujours ON). Le proxy doit detecter les requetes entrantes, demarrer le container Jellyfin, attendre le healthcheck, router la requete, puis arreter apres X minutes d'inactivite.",
      "priority": "moyenne",
      "effort": "M",
      "status": "pending",
      "assignee": "claude",
      "tags": [
        "jellyfin",
        "ram-optimization",
        "wake-on-request",
        "medias"
      ],
      "context": {
        "current_ram_jellyfin_mb": 532,
        "nas_ram_used_gb": 26,
        "nas_ram_total_gb": 31,
        "navidrome_deployed": "2026-04-11",
        "tools_suggested": [
          "acouvreur/sablier",
          "sablier-app/sablier",
          "LazyTainer"
        ]
      },
      "verification_steps": [
        {
          "step": "Container Jellyfin stop par defaut (docker ps ne le montre pas)",
          "passes": false
        },
        {
          "step": "Ouvrir http://192.168.1.132:30013 depuis navigateur declenche le demarrage automatique",
          "passes": false
        },
        {
          "step": "Jellyfin healthy dans les 90 secondes apres premiere requete",
          "passes": false
        },
        {
          "step": "Apres 30 min sans activite, Jellyfin est stoppe automatiquement",
          "passes": false
        },
        {
          "step": "RAM NAS diminue de ~500 Mo quand Jellyfin est stoppe (verifiable via free -h)",
          "passes": false
        }
      ]
    },
    {
      "id": "medias-2",
      "title": "Developper l'IPTV sur Jellyfin",
      "description": "Ajouter la gestion de chaines IPTV en direct dans Jellyfin (Live TV). Jellyfin supporte nativement les playlists M3U et les EPG (XMLTV). Permet de centraliser films + series + TV en direct dans une seule interface, accessible depuis Feishin et Symfonium sur tous les appareils (Mac, Pixel, TV). Ca rejoint la vision souverainete numerique (plus besoin d'app IPTV tierce).",
      "priority": "moyenne",
      "effort": "M",
      "status": "pending",
      "assignee": "claude",
      "tags": [
        "jellyfin",
        "iptv",
        "live-tv",
        "medias"
      ],
      "context": {
        "jellyfin_port": 30013,
        "jellyfin_path": "/mnt/.ix-apps/app_mounts/jellyfin",
        "approach_options": [
          "Playlist M3U directe dans Jellyfin > Live TV > Tuners TV",
          "xTeVe (proxy M3U + EPG) devant Jellyfin",
          "TVHeadend (DVR complet) + plugin Jellyfin"
        ],
        "interdependance": "medias-1 (wake-on-request) - si Jellyfin devient on-demand, l'IPTV doit pouvoir declencher le reveil"
      },
      "verification_steps": [
        {
          "step": "Source IPTV identifiee et credentials securises dans .env.secrets",
          "passes": false
        },
        {
          "step": "Live TV configure dans Jellyfin avec au moins 1 chaine fonctionnelle",
          "passes": false
        },
        {
          "step": "EPG (guide des programmes) affiche dans l'interface Jellyfin",
          "passes": false
        },
        {
          "step": "Lecture d'une chaine IPTV depuis Symfonium (Pixel et TV) fonctionne",
          "passes": false
        },
        {
          "step": "Enregistrement d'un programme (DVR) fonctionne si supporte",
          "passes": false
        }
      ]
    },
    {
      "id": "infra-cron-nas-illegal",
      "title": "Migrer cron root NAS monitoring_containers_pg vers N8N",
      "description": "Decouvert 2026-04-11: un cron root NAS lance `/mnt/usb-data/monitoring/monitoring_containers_pg.py` toutes les 5 min. Viole la regle absolue #5 (aucun cron hors N8N/Prefect). Simultanement le workflow N8N `surveillance-nas.json` reference un chemin mort (`/mnt/usb-data/systeme/monitoring_containers.py`), donc tout le monde croit que c'est N8N qui fait le monitoring alors que c'est un cron cache. Source unique perdue. ATTENTION: aussi impacte par l'incident /mnt vs /mnt/mnt — le vrai script peut etre dans /mnt/mnt/usb-data/monitoring/, a verifier avant migration.",
      "priority": "haute",
      "effort": "M",
      "status": "pending",
      "assignee": "claude",
      "verification_steps": [
        {
          "step": "crontab -l sur NAS ne contient plus de ligne monitoring_containers_pg.py",
          "passes": false
        },
        {
          "step": "Workflow N8N 'surveillance-nas' ou equivalent actif, execute toutes les 5 min, logs OK",
          "passes": false
        },
        {
          "step": "Table Postgres gilles_os_db contient des entrees monitoring recentes (< 6 min)",
          "passes": false
        },
        {
          "step": "Dashboard 8100 affiche l'etat des containers a jour",
          "passes": false
        },
        {
          "step": "Verifier qu'aucun autre cron root NAS illegal n'existe (crontab -l, /etc/cron.*, systemd timers)",
          "passes": false
        }
      ],
      "created_at": "2026-04-11T02:11:31.739470+00:00",
      "origin": "dedup-audit-2026-04-11"
    },
    {
      "id": "infra-audit-paths-morts",
      "title": "Audit exhaustif des paths morts et composants casses silencieux",
      "description": "Audit DEDUP 2026-04-11 a revele que chaque dossier audite contient au moins un composant casse silencieusement (paths hardcoded obsoletes, workflows N8N pointant dans le vide, containers tournant avec code desynchronise, scripts important des chemins inexistants). Le systeme ne detecte pas ses propres pannes. Voir memoire memory/composants_casses_silencieux.md. Couvre aussi l'incident /mnt vs /mnt/mnt (voir memoire incident_mnt_path_split.md).",
      "priority": "haute",
      "effort": "L",
      "status": "pending",
      "assignee": "claude",
      "verification_steps": [
        {
          "step": "Liste complete des crons root NAS (crontab -l, /etc/cron.*, systemd timers) documentee dans memoire/decisions/",
          "passes": false
        },
        {
          "step": "Chaque workflow N8N a ete teste et ses paths references verifies",
          "passes": false
        },
        {
          "step": "grep -rn '/mnt/usb-data\\|Path.home.*Projets' dans tout gillesOS verifie chaque hit",
          "passes": false
        },
        {
          "step": "Chaque deploy.sh a sa source rsync validee comme existante",
          "passes": false
        },
        {
          "step": "Chaque container a ses bind mounts valides (source existe, bon type fichier/dossier)",
          "passes": false
        },
        {
          "step": "Rapport final dans memoire/decisions/audit-paths-morts-2026-04.md",
          "passes": false
        }
      ],
      "created_at": "2026-04-11T02:11:31.739470+00:00",
      "origin": "dedup-audit-2026-04-11",
      "depends_on": [
        "infra-cron-nas-illegal"
      ]
    },
    {
      "id": "quality-engine-refactor-crossprojects",
      "title": "Refactor quality-engine en auditeur cross-projets",
      "description": "Actuellement quality-engine a un dict PROJECTS en dur et 10 agents tous appliques aveuglement. Le generaliser pour auditer TOUS les projets du systeme selon leur fonction et visee. Architecture cible: (1) manifeste .qa.json par projet declarant type+visee+agents+thresholds, (2) registre d'agents etendu (data-integrity, pnl-accuracy, source-reliability, extraction-accuracy, seo-content, accessibility, system-integrity, etc.), (3) discovery automatique des projets et leurs .qa.json, (4) integration N8N (workflow hebdomadaire + rapports Telegram). PREREQUIS: DEDUP Mac termine (quality-engine doit d'abord etre migre vers intelligence/quality-engine/).",
      "priority": "moyenne",
      "effort": "XL",
      "status": "pending",
      "assignee": "claude",
      "verification_steps": [
        {
          "step": "intelligence/quality-engine/ existe (dedup fait)",
          "passes": false
        },
        {
          "step": "engine.py ne contient plus PROJECTS hardcode — lit depuis projets/*/.qa.json",
          "passes": false
        },
        {
          "step": "Au moins 3 nouveaux types d'agents ajoutes (hors games web)",
          "passes": false
        },
        {
          "step": "quality-engine --all tourne sans erreur sur tous les projets instrumentes",
          "passes": false
        },
        {
          "step": "Workflow N8N hebdomadaire cree et actif",
          "passes": false
        },
        {
          "step": "Rapport Telegram recu avec findings par projet",
          "passes": false
        }
      ],
      "created_at": "2026-04-11T02:22:39.225351+00:00",
      "origin": "dedup-audit-2026-04-11",
      "depends_on": [
        "dedup-mac-projets-systeme"
      ]
    },
    {
      "id": "quality-engine-instrumentation-projets",
      "title": "Instrumenter chaque projet du systeme avec un .qa.json",
      "description": "Une fois quality-engine refactore, creer un .qa.json dans chaque projet de projets/* declarant: type, visee, agents applicables, endpoints/urls, thresholds. Projets concernes: paris, laludora (portail + 5 sites), recruitOS, journalisme, foot-data, La-France-de-Gilles, lycee, border-games, vintedbot, vie-perso. Aussi: .qa.json pour le systeme lui-meme (agent system-integrity qui verifie composants casses silencieux, crons caches, paths morts, bind mounts invalides).",
      "priority": "moyenne",
      "effort": "L",
      "status": "pending",
      "assignee": "claude",
      "verification_steps": [
        {
          "step": "Chaque dossier projets/* a un .qa.json valide contre le schema",
          "passes": false
        },
        {
          "step": "Le systeme lui-meme a son .qa.json avec agent system-integrity",
          "passes": false
        },
        {
          "step": "quality-engine --all produit un rapport non-vide pour chaque projet",
          "passes": false
        },
        {
          "step": "Aucun projet en etat 'non instrumente' dans le rapport global",
          "passes": false
        }
      ],
      "created_at": "2026-04-11T02:22:39.225351+00:00",
      "origin": "dedup-audit-2026-04-11",
      "depends_on": [
        "quality-engine-refactor-crossprojects"
      ]
    },
    {
      "id": "sys-dedup-projets-systeme",
      "title": "DEDUP: dissoudre projets/systeme/ vers noyau/intelligence/memoire/docs",
      "description": "Chantier central identifie par l'audit queue 2026-04-11 (memoire/audits/queue_2026-04-11.md) et confirme par l'audit DEDUP de la nuit du 2026-04-11. projets/systeme/ contient 628 fichiers dont 65 avec paths hardcoded `~/Projets/systeme/` casses. Selon la nouvelle archi (systeme > projets, pas dedans), projets/systeme/ doit disparaitre en tant que concept. Dissolution en 6 buckets: intelligence/ (services), memoire/ (etat+decisions), noyau/ (config), docs/ (VISION/ARCHITECTURE/INFRASTRUCTURE), racine (session_*.sh), archives (morts). PREREQUIS: session /mnt/mnt terminee (une autre session s'en occupe), fix chef-de-projet (container actuel deploye depuis projets/systeme/chef-de-projet/). Plan detaille dans memoire/decisions/dedup-mac-plan-20260411.md (a creer). Zero suppression au premier pass — tout passe par memoire/archives/projets-systeme-2026-04-11/ pour quarantaine avant suppression vraie.",
      "priority": "haute",
      "effort": "L",
      "status": "pending",
      "assignee": "claude",
      "verification_steps": [
        {
          "step": "projets/systeme/ n'existe plus (ou ne contient que un README pointant vers l'archive)",
          "passes": false
        },
        {
          "step": "grep -rn '~/Projets/systeme\\|Projets/systeme' ~/gillesOS/intelligence ~/gillesOS/noyau ~/gillesOS/memoire ~/gillesOS/cockpit retourne vide (hors __pycache__)",
          "passes": false
        },
        {
          "step": "Tous les containers NAS qui dependaient de projets/systeme/ ont ete redeployes depuis leur nouvel emplacement et fonctionnent (chef-de-projet, dashboard, etc.)",
          "passes": false
        },
        {
          "step": "Les 2 crons Mac qui referencaient projets/systeme/ ont ete migres (ou supprimes si morts)",
          "passes": false
        },
        {
          "step": "docs/VISION.md, docs/ARCHITECTURE.md, docs/INFRASTRUCTURE.md existent et sont la source de verite",
          "passes": false
        },
        {
          "step": "memoire/archives/projets-systeme-2026-04-11/ contient la quarantaine complete avec structure d'origine preservee",
          "passes": false
        },
        {
          "step": "Aucun container, cron, N8N workflow, ou script ne reference plus projets/systeme/",
          "passes": false
        }
      ],
      "created_at": "2026-04-11T02:24:45.741849+00:00",
      "origin": "dedup-audit-2026-04-11",
      "depends_on": [
        "infra-mnt-mnt-migration",
        "sys-fix-chef-de-projet"
      ],
      "plan": "memoire/decisions/plan-D-dedup-mac-20260411.md"
    },
    {
      "id": "sys-fix-crontab-mac",
      "title": "Migrer/supprimer les 2 crons Mac vers N8N (regle #5)",
      "description": "crontab -l sur Mac contient 2 entrees qui violent la regle absolue #5 (aucun cron hors N8N/Prefect):\n1. `3 8 1 * * /Users/gillesaugereau/Projets/vie-perso/backend/scrapers/run_monthly.sh` (scrapers cuisine mensuel)\n2. `0 9 * * * /Users/gillesaugereau/Projets/systeme/quality-engine/run-cycle.sh` (cycle quality-engine quotidien)\nLes deux passent par le symlink ~/Projets → ~/gillesOS donc resolvent encore, mais quality-engine/cycle.log s'arrete le 7/04 suggerant que le cron 2 ne tourne plus (Mac eteint la nuit ? run-cycle.sh fail silencieux ?). Audit fonctionnel a faire avant migration. Le cron 2 devient obsolete avec le refactor quality-engine cross-projets (quality-engine-refactor-crossprojects).",
      "priority": "moyenne",
      "effort": "M",
      "status": "pending",
      "assignee": "claude",
      "verification_steps": [
        {
          "step": "Diagnostiquer pourquoi quality-engine/cycle.log s'arrete le 7/04 (Mac sleep ? script fail ?)",
          "passes": false
        },
        {
          "step": "Verifier si run_monthly.sh vie-perso est encore necessaire ou obsolete",
          "passes": false
        },
        {
          "step": "Si necessaire: workflow N8N equivalent cree (cron expression + execute command)",
          "passes": false
        },
        {
          "step": "crontab -l sur Mac ne contient plus ces 2 entrees",
          "passes": false
        },
        {
          "step": "Aucun cron sur Mac hors N8N (verifie par audit)",
          "passes": false
        }
      ],
      "created_at": "2026-04-11T02:24:45.741849+00:00",
      "origin": "audit-queue-2026-04-11"
    },
    {
      "id": "sys-fix-chef-de-projet",
      "title": "Fix chef-de-projet: container desynchronise + paths casses + mount config.json",
      "description": "Container NAS chef-de-projet tourne mais est casse silencieusement : (1) code deploye depuis projets/systeme/chef-de-projet/ avec paths hardcoded `~/Projets/systeme/` casses depuis la migration 2026-04-10, (2) version corrigee existe dans intelligence/agents/chef-projet/ mais jamais deployee, (3) bind mount /mnt/usb-data/config.json pointe vers un dossier vide (incident /mnt vs /mnt/mnt), (4) est en categorie A de l'audit /mnt/mnt (sera migre par la session /mnt/mnt Phase 2). Plan detaille dans memoire/decisions/plan-C-fix-chef-de-projet-20260411.md. PREREQUIS: session /mnt/mnt Phase 2 terminee. [FUSION 2026-04-11: absorbe infra-6 — ajout TELEGRAM_TOKEN chef-de-projet fait partie de ce fix]",
      "priority": "haute",
      "effort": "M",
      "status": "completed",
      "assignee": "claude",
      "verification_steps": [
        {
          "step": "md5sum chef.py deploye = md5sum intelligence/agents/chef-projet/chef.py (7a66d1dd9714ebe623ff06d0d4882afb)",
          "passes": true
        },
        {
          "step": "Bind mount /mnt/usb-data/config.json pointe un vrai JSON (resolu par fix /mnt/mnt)",
          "passes": true
        },
        {
          "step": "Container Up, health=ok, restarts=0",
          "passes": true
        },
        {
          "step": "collect_system() fonctionne (docker-cli + procps installes dans Dockerfile)",
          "passes": true
        },
        {
          "step": "SSH Mac fonctionnel (vision.md + CLAUDE.md + git log collectes avec succes)",
          "passes": true
        },
        {
          "step": "queue-worker + Obsidian lus avec succes",
          "passes": true
        },
        {
          "step": "Cycle echoue proprement sur LLM indisponible (tunnel Mac unreachable) — blocage externe, pas code",
          "passes": true
        },
        {
          "step": "Ancienne version quarantainee dans memoire/archives/projets-systeme-2026-04-11/chef-de-projet",
          "passes": true
        }
      ],
      "created_at": "2026-04-11T02:48:41.674816+00:00",
      "origin": "dedup-audit-2026-04-11",
      "plan": "memoire/decisions/plan-C-fix-chef-de-projet-20260411.md",
      "depends_on": [
        "infra-mnt-mnt-migration"
      ],
      "completed_at": "2026-04-11T20:10:00+00:00",
      "completed_by": "session fix-chef-de-projet-20260411"
    },
    {
      "id": "cockpit-port-paris",
      "title": "Déplacer paris_frontend vers un autre port (cockpit homepage garde :3000)",
      "status": "pending",
      "priority": "moyenne",
      "assignee": "claude",
      "effort": "S",
      "scope": "homepage (gethomepage.dev) est le Cockpit officiel de G — catégorisé Cockpit/Infra/Projets/Medias/Systeme/Intelligence. Il garde le port 3000. paris_frontend doit être déplacé sur un autre port (proposition : 3010). Inclus : edit docker-compose paris, update refs Cloudflare Tunnel si exposition publique, update GUIDE.md + config.json. Decouverte Wave 1 reparation 2026-04-11.",
      "verification_steps": [
        {
          "step": "paris_frontend UP et healthy",
          "passes": false
        },
        {
          "step": "homepage UP et accessible",
          "passes": false
        },
        {
          "step": "docker-compose paris + homepage sans conflit de ports",
          "passes": false
        },
        {
          "step": "Refs Cloudflare/Tailscale/docs a jour si port paris_frontend change",
          "passes": false
        }
      ],
      "created": "2026-04-11",
      "source": "Wave 1 reparation — decouverte pendant restart paris stack"
    },
    {
      "id": "infra-post-mnt-verif",
      "title": "Verifier backups/snapshots post-incident /mnt/mnt",
      "description": "Apres resolution split /mnt vs /mnt/mnt (2026-04-11) et zfs set mountpoint, verifier : (1) snapshots ZFS auto encore actifs sur usb-data, (2) backups Vaultwarden/PostgreSQL recents, (3) replication externe si existante, (4) aucun path casse silencieux ne persiste dans les jobs de backup.",
      "priority": "haute",
      "effort": "S",
      "status": "pending",
      "assignee": "claude",
      "tags": [
        "infra",
        "backup",
        "zfs"
      ],
      "origin": "cleanup-2026-04-11",
      "verification_steps": [
        {
          "step": "zfs list -t snapshot usb-data | tail -5 montre snapshots recents",
          "passes": false
        },
        {
          "step": "Vaultwarden: dernier backup < 24h verifiable",
          "passes": false
        },
        {
          "step": "PostgreSQL gilles_os_db: dernier dump < 24h verifiable",
          "passes": false
        },
        {
          "step": "Aucun job de backup en erreur dans les logs NAS 48h",
          "passes": false
        }
      ]
    },
    {
      "id": "infra-llm-tunnel-mac",
      "title": "Retablir LLM tunnel Mac ou alternative",
      "description": "Le chef-de-projet echoue sur le seul appel restant : plan_with_llm. Tunnel Mac http://100.77.179.8:9876 (Python ~/.claude-server/server.py, PID 73569) listen sur IP Tailscale mais unreachable meme depuis localhost Mac (Tailscale MagicSock cassé). Fallback Ollama inexistant sur NAS (p1b-5). Options : (A) redemarrer tunnel Mac + Tailscale, (B) bind tunnel sur 0.0.0.0 au lieu de 100.77.179.8, (C) installer Ollama sur NAS (bloque par RAM: 3.8G libres / 31G), (D) migrer Ollama sur Legion Y530.",
      "priority": "haute",
      "effort": "M",
      "status": "pending",
      "assignee": "claude",
      "tags": [
        "llm",
        "tailscale",
        "chef-de-projet"
      ],
      "origin": "fix-chef-de-projet-20260411",
      "verification_steps": [
        {
          "step": "curl http://100.77.179.8:9876/llm POST depuis NAS retourne 200",
          "passes": false
        },
        {
          "step": "chef-de-projet POST /run produit un plan JSON complet",
          "passes": false
        },
        {
          "step": "Plan Telegram recu par G apres /run",
          "passes": false
        }
      ]
    },
    {
      "id": "auto-improve-scripts-20260411",
      "title": "automation_master.py + contradiction_scanner.py (phase Act + Verify)",
      "description": "Cree les 2 scripts manquants de la boucle d'auto-amelioration. automation_master = orchestrateur dry-run safe enchainant planner_supervisor, apply_approved_rules (review), contradiction_scanner. Permissions auto/review/forbidden alignees matrice CLAUDE.md, log JSONL atomique dans memoire/metrics/, rollback git pour CLAUDE.md. contradiction_scanner = scanner READ-ONLY stdlib pure, 6 types (dup C1, annulation C2, forbidden C3, dead-path C4, ports C5, cutoff C6). 14/14 tests unittest OK. Smoke run reel a trouve 1 contradiction critique : ~/.claude/CLAUDE.md:106 pointe vers ~/gillesOS/noyau/INFRASTRUCTURE.md qui n'existe pas (vrai bug doc).",
      "priority": "moyenne",
      "effort": "M",
      "status": "completed",
      "assignee": "claude",
      "created_at": "2026-04-11T20:40:55+00:00",
      "completed_at": "2026-04-11T20:40:55+00:00",
      "session_id": "automation-contradiction-scripts",
      "files_created": [
        "intelligence/automation_master.py",
        "intelligence/contradiction_scanner.py",
        "intelligence/tests/test_automation_master.py",
        "intelligence/tests/test_contradiction_scanner.py"
      ],
      "files_modified": [
        "projets/systeme/ARCHITECTURE.md (section Boucle auto-amelioration)"
      ],
      "spec": "/tmp/spec-automation-contradiction.md",
      "verification_steps": [
        {
          "step": "python3 -m unittest tests.test_automation_master tests.test_contradiction_scanner (14 tests)",
          "passes": true
        },
        {
          "step": "python3 intelligence/automation_master.py dry-run sur vrai repo",
          "passes": true
        },
        {
          "step": "python3 intelligence/contradiction_scanner.py --since 7d genere rapport",
          "passes": true
        },
        {
          "step": "Smoke: scanner trouve contradiction C4 reelle sur INFRASTRUCTURE.md absent",
          "passes": true
        }
      ],
      "next_decisions_G": [
        "Valider le branchement N8N (workflow automation-master-cycle toutes les 30 min ?)",
        "Ou cron NAS : */30 * * * * python3 /mnt/usb-data/automation/automation_master.py --apply",
        "Corriger la contradiction C4 trouvee : ~/.claude/CLAUDE.md:106 reference ~/gillesOS/noyau/INFRASTRUCTURE.md qui n'existe pas (le vrai fichier est projets/systeme/INFRASTRUCTURE.md)"
      ],
      "notes": "Spec validee par G avant code. Aucun branchement auto, tout est dry-run par defaut."
    },
    {
      "id": "tablette-dns-adguard",
      "title": "Tailscale admin — pointer nameserver global vers AdGuard NAS",
      "description": "Sur login.tailscale.com/admin/dns, ajouter 100.116.248.8 comme nameserver custom et cocher 'Override local DNS'. Debloquera le filtre pub AdGuard niveau OS pour tablette, Mac, Pixel et tout le tailnet d'un coup.",
      "priority": "moyenne",
      "effort": "XS",
      "status": "pending",
      "assignee": "g",
      "verification_steps": [
        {
          "step": "adb shell ping -c 2 doubleclick.net (depuis tablette) renvoie 0.0.0.0 ou fail resolve",
          "passes": false
        },
        {
          "step": "AdGuard Home dashboard montre des requetes DNS depuis 100.93.200.24 (tablette)",
          "passes": false
        }
      ],
      "created_at": "2026-04-12T01:30:00+03:00",
      "context": "Session tablette Lenovo 2026-04-12. DNS AdGuard pas automatisable via ADB car tailscale CLI n'a pas de commande pour set les tailnet nameservers (seulement --accept-dns). Requiert admin console web."
    },
    {
      "id": "tablette-n8n-webhooks",
      "title": "N8N — creer webhooks add-film et add-idea pour HTTP Shortcuts tablette",
      "description": "Creer 2 workflows N8N avec webhook trigger : 1) /webhook/add-film qui recoit {title} et POST vers Jellyseerr /api/v1/request. 2) /webhook/add-idea qui recoit {idea} et append dans intelligence/incubateur/idees/captures.md avec timestamp. Les deux endpoints sont deja referenced dans cockpit/tablette-lenovo/http_shortcuts_import.json.",
      "priority": "moyenne",
      "effort": "S",
      "status": "pending",
      "assignee": "claude",
      "verification_steps": [
        {
          "step": "curl -X POST http://100.116.248.8:30109/webhook/add-film -d '{\"title\":\"Inception\"}' cree une requete dans Jellyseerr",
          "passes": false
        },
        {
          "step": "curl -X POST http://100.116.248.8:30109/webhook/add-idea -d '{\"idea\":\"test\"}' ajoute une ligne dans intelligence/incubateur/idees/captures.md",
          "passes": false
        },
        {
          "step": "Depuis HTTP Shortcuts tablette, le raccourci Ajouter idee marche avec toast de confirmation",
          "passes": false
        }
      ],
      "created_at": "2026-04-12T01:30:00+03:00",
      "dependencies": [],
      "context": "Session tablette Lenovo 2026-04-12. Prerequis pour HTTP Shortcuts cockpit tactile operationnel."
    },
    {
      "id": "tablette-follow-up-manuel",
      "title": "Tablette Lenovo — finir logins et PWA Homepage",
      "description": "Actions manuelles qui ne peuvent pas etre automatisees via ADB : 1) Bitwarden → email gillux.agr@gmail.com + master password. 2) Chrome → finir login Nextcloud + grant access (URL deja configuree). 3) Fennec → menu ⋮ → Installer → PWA Homepage sur home screen. 4) AccuBattery (Play Store) → plafond charge 80%.",
      "priority": "haute",
      "effort": "XS",
      "status": "pending",
      "assignee": "g",
      "verification_steps": [
        {
          "step": "Bitwarden coffre deverrouille et liste les ciphers",
          "passes": false
        },
        {
          "step": "Nextcloud client synchronise au moins 1 fichier depuis le NAS",
          "passes": false
        },
        {
          "step": "Icone PWA 'Gilles OS - Cockpit' presente sur home screen tablette",
          "passes": false
        }
      ],
      "created_at": "2026-04-12T01:30:00+03:00",
      "context": "Session tablette Lenovo 2026-04-12. Setup ADB fait, URLs configurees via tap automation, il reste juste les credentials personnels et 2-3 taps UI."
    },
    {
      "id": "llm-routing-bootstrap",
      "title": "LLM routing — bootstrap infrastructure (strategie_llm_routing.md)",
      "description": "Mettre en place l'infrastructure de routing LLM selon la strategie decidee 2026-04-12. Point d'entree : noyau/strategie_llm_routing.md (doc de reference, 4 dimensions de decision, 6 regles hardcodees, hierarchie T0-T5, architecture cible budget_planner+policy_engine+LiteLLM). Chaque etape est un sprint contract independant — faire une seule etape par session Claude Code.",
      "priority": "haute",
      "effort": "XL",
      "status": "pending",
      "assignee": "claude",
      "reference_doc": "noyau/strategie_llm_routing.md",
      "verification_steps": [
        {
          "step": "Etape 1 — Diagnostic infrastructure : RAM+CPU NAS verifies (free -h via SSH local ou gilles-os agent), plan Claude reel avril 2026 confirme via WebSearch anthropic.com/pricing, etat SSH NAS Tailscale diagnostique. Resultats ecrits dans intelligence/veille/ai_landscape.md section Claude pricing et dans un nouveau memoire/decisions/2026-04-XX_diagnostic_infra_llm.md",
          "passes": false
        },
        {
          "step": "Etape 2 — Tunnel Mac : diagnostic du process PID 73569 qui ne repond plus (silence depuis 2026-04-02, 586 logs historiques). Soit restart+fix (binding 0.0.0.0 au lieu de 100.77.179.8 seul, PROJECTS_PATH actualise vers ~/gillesOS, secret hardcode sorti dans .env.secrets), soit deprecier au profit d'une architecture LiteLLM directe. curl http://100.77.179.8:9876/health retourne 200.",
          "passes": false
        },
        {
          "step": "Etape 3 — Ollama + Gemma 4 E4B deployes sur NAS. curl http://100.116.248.8:11434/api/tags liste gemma4:e4b. Test d'inference sur 3 prompts FR reels retourne une reponse coherente en < 15s.",
          "passes": false
        },
        {
          "step": "Etape 4 — LiteLLM proxy deploye sur NAS (Docker). Config avec au moins 2 deployments : ollama-gemma-e4b (local) et claude-tunnel (via Mac si repare). curl http://100.116.248.8:4000/v1/chat/completions retourne une reponse via chaque deployment.",
          "passes": false
        },
        {
          "step": "Etape 5 — memoire/decisions/llm_usage.jsonl cree et instrumente. Chaque appel via LiteLLM y logue une ligne structuree (task_id, task_type, tier, provider, tokens_in/out, cost_eur, status, latency_ms, parent_task_id). Verifie avec 10+ appels reels.",
          "passes": false
        },
        {
          "step": "Etape 6 — noyau/llm_router/policy_engine.py ecrit (Python, ~100 lignes). Lit budget_state.json (vide OK au debut) et llm_routing_rules.yaml. Applique les 6 regles hardcodees R1-R6 de strategie_llm_routing.md. Expose POST /llm qui forward vers LiteLLM. Test : un appel marque sensitivity=high doit aller sur local force meme si Claude dispo.",
          "passes": false
        },
        {
          "step": "Etape 7 — Premier consommateur backend migre. Choisir une tache simple (resume RSS ou classification d'article). La basculer du code direct vers policy_engine.py. Mesurer cost-to-success sur 30 appels. Comparer qualite Ollama vs Claude. Ecrire le resultat dans metrics/cost_to_success.json.",
          "passes": false
        },
        {
          "step": "Etape 8 — noyau/llm_router/budget_planner.py ecrit. Lit queue.json + llm_usage.jsonl. Publie memoire/budget_state.json avec {dispo_reel, mode, previsions}. Cron 10 min. Champ estimated_cost ajoute au schema queue.json en optionnel.",
          "passes": false
        },
        {
          "step": "Etape 9 — Test de bascule souverainete : simuler disparition tunnel Mac (stop le process), verifier que tous les consommateurs backend continuent de tourner sur les tiers locaux T0-T2 sans erreur visible. Document le resultat dans memoire/decisions/.",
          "passes": false
        }
      ],
      "created_at": "2026-04-12T00:00:00+03:00",
      "dependencies": [],
      "context": "Session G + Claude Opus 4.6 2026-04-12. Decantation complete de la strategie routing LLM : souverainete avant performance, opportunisme controle, 4 dimensions de decision (cout/budget previsionnel/cost-to-success/risque silencieux), zero lock-in SDK vendor, regles hardcodees pour fichiers critiques et donnees sensibles. Etat des lieux : noyau/llm_router/ vide, decisions/llm_usage.jsonl inexistant, queue 44 pending / 2 completed, tunnel Mac silencieux depuis 2026-04-02, Ollama non installe, NAS SSH Tailscale en timeout. Chaque etape de ce sprint contract doit etre faite dans UNE session Claude Code dediee qui lit d'abord noyau/strategie_llm_routing.md."
    },
    {
      "id": "finder-nas-smb-full",
      "status": "pending",
      "priority": 3,
      "title": "Accès Finder complet au NAS via SMB",
      "description": "Partager /mnt/usb-data/ entier en SMB pour accès Finder Mac. Bloqué par conflit ACL : datasets n8n (usb-data/n8n, usb-data/n8n/data, usb-data/n8n/db, usb-data/n8n/obsidian) sont en NFSv4, root usb-data en POSIX. Plan : 1) changer acltype nfsv4→posix sur les 4 datasets n8n (risque faible, Docker utilise uid/gid pas ACL), 2) midclt call sharing.smb.create avec name=nas path=/mnt/usb-data, 3) tester smb://192.168.1.132 dans Finder Mac. Port SMB 445 confirmé ouvert depuis Mac.",
      "created_at": "2026-04-12",
      "tags": [
        "nas",
        "smb",
        "finder",
        "infra"
      ]
    }
  ],
  "phase_previous": "PHASE_1A — Autonomous + Transparent + Self-Aware System (completed 2026-03-31)",
  "date_updated": "2026-04-12T00:00:00+03:00"
}