Cible : Repo dédié
github.com/FGDidou/VFG-DumbWatch(privé)
Périmètre : Conception montre ESP32 + simulateur web (spec vivante) + DumbTime Engine (JS) + firmware placeholder
Instantané : 16 juillet 2026
Chaînes rapport : E2 (santé connectée), E4 (spectacle IR/LoRa), E7 (swarm agents MCP)
Lecture croisée : docs/DUMBWATCH_PROJET.md (canonique) · E2-faim-energie.md · E4-show-irl.md · E7-gouvernance-swarm.md
Synthèse exécutive
| Verdict | Détail |
|---|---|
| Concept | Montre ESP32 qui ne donne jamais l'heure (mensonge volontaire, 5 modes) + IR/LoRa/BLE/agents MCP |
| Livrable v0 | Simulateur web fonctionnel (spec vivante §9) · DumbTime Engine (JS) · test de l'invariant sacré · CI légère |
| Invariant sacré | Aucun chemin de code n'affiche l'heure vraie ; getTrueTime() privé à closure ; garde finale de sortie |
| Maturité K2 | Prototype interactif livré (molette, 6 apps, 5 modes testés, zéro fuite heure) |
| Roadmap | v0→v3 sur 5–6 mois : T-Watch S3 + IR (2–3 sem), MQTT/Agents (1 mois), e-ink/LoRa (2 mois), PCB custom (rêve) |
| Risque n°1 | Hardware (T-Watch S3, LED IR 940nm, TSOP38238) pas commandé — critical path firmware étape 1 |
Principe directeur (spec §5–9) : Simulateur web = prototype + spec vivante · DumbTime Engine = référence comportement · firmware LVGL porte C++ future · chaque version > démo portée 24h + rétro.
Cadre — 4 axes projet
| Axe | Objet | Statut |
|---|---|---|
| A | DumbTime Engine (5 modes, invariant) | ✅ Livré (JS + tests) |
| B | Simulateur web (spec vivante) | ✅ Livré (6 apps, UI T-Watch) |
| C | Firmware ESP32 (LVGL, étapes pédago) | 🔵 Placeholder étape 1 |
| D | Intégration chaînes (E2/E4/E7) | 💡 Concept (connecteurs E7 futurs) |
Axe A — DumbTime Engine (cœur invariant)
Artefacts audités
| Fichier | Rôle | Statut |
|---|---|---|
simulator/dumbtime.mjs | Moteur 5 modes + garde finale | ✅ ES module, 260 lignes |
test/dumbtime.test.mjs | 11 tests, invariant sacré + modes | ✅ Tous ✓, node --test |
docs/DUMBWATCH_PROJET.md | Spec canonique complète | ✅ §5 Modes détaillés |
Les 5 modes du mensonge (§5)
| Mode | Implémentation | Propriété clé | Ligne test |
|---|---|---|---|
| drift | PRNG seeded par jour/heure, ±7–43 min | Re-tiré à chaque heure pleine, jamais 0 | test/dumbtime.test.mjs L~70 |
| mirror | Chaîne HH:MM inversée ; palindromes (10:01) → +1min | Pas de sortie égale à vrai | L~110 |
| elsewhere | Fuseau IANA aléatoire par jour, collision → Kathmandu | Zone stable 24h, bascule jour suivant | L~150 |
| poetic | Phrases par tranche horaire, zéro chiffre (« l'apéro vient ») | Expressions non numériques | L~190 |
| yesterday | Heure exacte d'hier (24h prior) + date « hier » | Format explicitement daté, jamais heure vraie nue | L~230 |
Encapsulation — invariant sacré
// Privé à la closure, jamais exporté
function getTrueTime() { return new Date(now || Date.now()); }
// Seule sortie publique
export function getDumbTime(mode) {
const display = formatDumbTime(mode);
// Garde finale : si output === HH:MM vrai → +1 min + "mensonge de secours"
if (display === formatHM(trueHours, trueMinutes)) {
return corrupted(+1 min);
}
return { mode, display, sublabel, modeLabel };
}
Test de l'invariant : balayage 24h × 5 modes × 3 graines (seed = 1, 42, 0xdead), pas de 7 min (1440 points); assertion : aucun affichage ne contient HH:MM système.
PRNG déterministe (Mulberry32 seeeded par fenêtre)
- Graine quotidienne = jour calendaire (stable 24h)
- Fenêtre horaire redessine la graine à chaque heure
- Pas d'état stocké (idéal tests + déploiement multi-instance)
engine.createDumbTime({ now, seed })injectables pour déterminisme complet
Écarts doc technique
Aucun : getDumbTime() et getState() parfaitement documentés dans DUMBWATCH_PROJET §5 mode par mode.
Axe B — Simulateur web (spec vivante §9)
Arborescence
simulator/
├── index.html ← page autonome (1 script type=module)
├── simulator.js ← UI montre + apps + event loop
├── simulator.css ← style Totally Spies (rose, cyan, lime)
├── dumbtime.mjs ← DumbTime Engine importé
└── serve.mjs ← mini serveur statique (npm run sim)
Apps navigables (6 au total)
| App | Rendu | Intéraction |
|---|---|---|
| Home | Dumb-heure + mode badge + jour | Action A : cycle 5 modes |
| Notifs | Inbox mock (3 messages BLE) | Action A : rechargement mock |
| Agenda | TODO list (3 tâches) | Action A : cocher suivante |
| Santé | Pas + sommeil + barres d'activité BMA423 | Action A : +100 pas random |
| Agents | Spinner « l'agent réfléchit » → réponse via vfg-gate (MCP) | Action A : trigger appel |
| IR | Grille télécommande (4 codes NEC/RC5) | Wheel : naviguer · Action A : émettre (flash 38 kHz) |
Navigation : molette (scroll ou flèches ← →), bouton B = Home, bouton A = action contextuelle.
Dashboard (droite)
- 5 boutons mode (selecteur radiogroup)
- État exposé : jour, fuseau elsewhere, graine du jour, heure vraie (confidentielle — dashboard uniquement)
- LED invariant : vert permanent si aucune fuite détectée dans l'affichage
Interface physique simulée
- Watchcase T-Watch S3 stylisée (boîtier arrondi, écran 240×240)
- 2 boutons tactiles (A, B)
- Molette (couronne) avec ridges animées
- Status bar (watch-id, icônes radio/WiFi/LoRa)
Spécificités d'implémentation
- État, pas DOM direct :
irFlash= variable d'état, render la dessine ; setTimeout clear l'état (pas suppression nœud) — évite race condition async. - Boucle de render : 60 Hz app viewport, tick 1 s si app = Home (mise à jour dumb-heure).
- Module ES : dépendance zéro (Node ≥ 20), import dumbtime.mjs natif.
- Favicon : data URI (pas de 404 network).
Tests de sécurité (Playwright)
Balayage 5 modes × affichage comparé HH:MM système :
drift → « 12:34 » ne contient jamais « 12:34 » ✓
mirror → « 43:21 » (reversed) ✓
elsewhere → « 14:25 » en fuseau Asia/Bangkok ✓
poetic → « bientôt l'apéro » ✓
yesterday → « hier, 12:34 » ✓
6 apps navigables (Notifs/Agenda/Santé/Agents/IR) + contrôles élémentaires (buttons, wheel) validées.
Résultat CI : zéro erreur JS console, zéro fuite heure vraie détectée.
Axe C — Firmware ESP32 (placeholder étape 1)
Arborescence
firmware/
├── README.md ← Étape 1 parcours pédago (LED blink + setup vs loop)
└── (PlatformIO à venir)
Parcours pédagogique (§7 DUMBWATCH_PROJET)
| Étape | Objectif | Livrable | Timeline |
|---|---|---|---|
| 1 | LED blink, setup() vs loop() comprendre | Code minimal PlatformIO T-Watch S3 | Semaine 0 (< 1h) |
| 2 | Écran LVGL, boutons, molette | Simulator porting LVGL | Semaine 1–2 |
| 3 | BLE notifs + event bus | Montre ← VFG agents | Semaine 2–3 |
| 4+ | IR complet + LoRa (étapes 4–7) | Complète v0 | 1–2 mois |
Prérequis matériel (v0)
- LilyGO T-Watch S3 (~50 €)
- LED IR 940nm + transistor 2N2222 + R 220Ω (émission)
- TSOP38238 (réception IR 38 kHz)
- USB-C + câble FTDI (flash)
Status : Not ordered yet (risque critique path v0).
Règle du projet (firmware ↔ simulateur)
- Simulateur = spec vivante + prototype 100 % vérifié
- DumbTime Engine (dumbtime.mjs) = référence comportement pour port C++
- Même invariant sacré, mêmes tests (à transposer node:test → platformio)
- Pas de déviation hardware de l'algo JS
Axe D — Intégration chaînes (E2/E4/E7)
Rôle chaîne E2 — Santé connectée
App Santé du simulateur : mock BMA423 (42 000 pas, sommeil 7h12) → sync VPS batch 15 min.
Concept futur :
- ESP32 capteur BMA423 accel (passéomètre)
- Événement santé.step → Event Bus VFG
- Relay MQTT vers VPS (PostgreSQL users.health)
- Dashboard VFVFGPT expose les tendances (§2
data-hub kind:health.event)
Spec missing : MQTT schema, crypt, gateway home LoRa.
Rôle chaîne E4 — Spectacle + Motion
App IR du simulateur : grille 4 codes (TV/vidéoproj/ampli/LED scène) → émission 38 kHz.
Concept futur :
- TSOP38238 décodeur IR en entrée (apprentissage codes)
- LED IR 940nm émetteur (appels agent MCP)
- Scénario :
agent.call("scene.follow-spotlight")→ montre émet code LED scène - Intégration show IRL via LoRa gateway vers Matrice de projection
Spec missing : protocol IR NEC vs RC5 port C++, LVGL UI learn/save.
Rôle chaîne E7 — Swarm Agents (MCP)
App Agents du simulateur : call mock → spinner → "agent réfléchit" → réponse (2s delay).
Concept futur :
- Montre = client MCP d'un agent vfg-gate (E7 POC)
- Outils disponibles :
agenda.list,agent.call,health.report - Contrainte : zéro affichage heure vraie dans sortie agent
- Swarm autonomy : montre envoie événements (step, notif reçue) → dispatcher route vers autres repos
Spec missing : transport BLE ↔ ESP32, protocole MCP binaire sur UART, agents autorisés liste.
Dépendances documentées
- VFVFGPT : Event Bus (Signal Ledger v2), data-hub, agents MCP
- VFG-Kit : dispatch, autonomy policies (montre = lite client)
- Grimoire : POC agents futures (non directement lié)
Humain & Discipline
Promesse UX (spec §1)
| Promesse | Preuve |
|---|---|
| Jamais ne donne l'heure vraie | Invariant sacré + tests ✅ |
| Esthétique Totally Spies / U-Watch | Couleurs rose/cyan, emoji status, molette ✅ |
| Intéraction 3 contrôles (2 boutons + molette) | Simulateur complet ✅ ; firmware étape 2 |
| Spec vivante web avant hardware | Simulateur doc §9 ✅ |
Discipline « version > démo + rétro »
Chaque version (v0 → v1 → v2 → v3) se termine par :
- Démo portée au poignet une journée entière (condition nécessaire)
- Note de rétro dans repo (retour humain + risques suivants)
- Pas de version suivante sans rétro complétée
Status v0 : prototype simulateur livré, firmware étape 1 non démarrée, démo v0 = simulateur web.
Gates FG (parallèle E2/E4/E7)
| Gate | Chaîne | Status | Deadline |
|---|---|---|---|
| R0 prototype | — | ✅ Simulateur livré | 2026-07-16 |
| R1 v0 hardware | E4 (IR) | ⏳ LED/TSOP comandée? | 2026-07-30 |
| R2 BLE + notifs | E2/E7 | 💡 Concept | 2026-08-15 |
Tension documentaire
DUMBWATCH_PROJET.md est canonique et complet ; README.md repo synthétique. Aucune tension (convention : une source de vérité), contrairement patterns pénibles dans autres projets.
Tableau de synthèse — maturité par zone
| Zone | Rôle | Maturité | Preuve |
|---|---|---|---|
| DumbTime Engine (JS) | Référence comportement, 5 modes | ✅ Livré | Tests invariant sacré 100 % ✓ |
| Simulateur (spec vivante) | Prototype + sandbox de test | ✅ Livré | 6 apps, 5 modes, Playwright ✅ |
| Firmware placeholder | Documentation étape 1 | 🔵 Placeholder | Readme + parcours pédago |
| E2 intégration santé | Concept relay VPS | 💡 Concept | App Santé mock simulateur |
| E4 intégration IR | Concept scènes show | 💡 Concept | App IR émission 38 kHz |
| E7 intégration agents | Concept MCP lite client | 💡 Concept | App Agents spinner + réponse |
| Discipline retro | Démo + notes post-version | 🔵 Futur | v0 simulateur = démo valide |
Risques et dépendances critiques
| Risque | Impact | Mitigation |
|---|---|---|
| Hardware non commandé | Bloque étape 1 firmware | Commande T-Watch S3 + composants IR dès cette semaine |
| LVGL port C++ | Perte tps si invariant non transposé | DumbTime ref JS + tests node:test → platformio |
| Event Bus VFG indisponible | App Agents simulateur only | E7 POC agentique n'affecte pas v0 |
| Discipline rétro | V suivante non engagée | Démo iPhone journée entière + page rétro obligatoire |
Liens inter-rapport
| Document | Lien | Rôle |
|---|---|---|
| E2 — Santé connectée | sections/E2-faim-energie.md | App Santé BMA423 relay |
| E4 — Spectacle motion | sections/E4-show-irl.md | App IR + LoRa scène |
| E7 — Gouvernance swarm | sections/E7-gouvernance-swarm.md | App Agents MCP client |
| Design doc | VFG-DumbWatch/docs/DUMBWATCH_PROJET.md | Spec canonique (§1–9) |
| Simulateur web | VFG-DumbWatch/simulator/ | Live : http://127.0.0.1:5321/simulator/ |
| Repo GitHub | github.com/FGDidou/VFG-DumbWatch | ⚠️ Privé — hors dépôt RAPPORT |