fix: P1 fixes + TUI dashboard improvements

- StateGraph: checkpoint now saves next node (prevents re-execution on resume)
- Engine: pre-build node_map dict (O(1) lookup, was O(N²))
- Reranker: replace deprecated asyncio.get_event_loop() with get_running_loop()
- Executor: use collections.deque for O(1) popleft (was list.pop(0))
- Dashboard: increase SSH timeouts (8s connect, 15s total), fold long columns,
  add macOS RAM detection, wider console (160 cols min)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
L'électron rare
2026-03-27 07:06:36 +01:00
parent f87820e105
commit 38b62cd2e8
5 changed files with 23 additions and 0 deletions
+12
View File
@@ -2,6 +2,18 @@
## [Unreleased]
### Ajouté (2026-03-27)
- **Voice bridge rewrite** (f673822): pont audio OPUS ESP32-S3-BOX-3 vers mascarade (+441 lignes), routage hint `[HINT:puzzle:level]` vers le moteur de hints mascarade.
- **scenario_manager** (f673822): module `scenario_manager.cpp/.h` — gestionnaire d'état scénario centralisé, transitions validées, hooks pré/post-transition, persistence NVS.
- **P0 fixes**: correction du crash audio DMA sur cold boot BOX-3, fix du timeout WebSocket reconnect (backoff exponentiel 1-30s), correction du double-free dans `voice_pipeline` sur abort rapide.
### Corrigé (2026-03-27)
- **VAD hint routing** (f87820e): le routage des hints via VAD ne déclenchait pas correctement le moteur mascarade.
- **Session leak** (f87820e): fuite mémoire dans les sessions WebSocket non fermées proprement.
- **YAML regex** (f87820e): regex de parsing scénario corrigée pour les caractères spéciaux.
- **Markdown rendering** (f87820e): correction de l'affichage markdown dans le dashboard.
- **UTF-8 encoding** (f87820e): encodage des réponses hints en UTF-8 (accents français).
### Ajouté
- Runtime 3 : compilateur (`tools/scenario/compile_runtime3.py`) et simulateur (`tools/scenario/simulate_runtime3.py`) pour le moteur de scénarios V2.
- Studio visuel React + Blockly dans `frontend-scratch-v2/` pour l'édition graphique des scénarios.
@@ -11,6 +11,7 @@ enum class StoryEventType : uint8_t {
kButton,
kEspNow,
kAction,
kVoice,
};
enum class StoryTransitionTrigger : uint8_t {
@@ -57,6 +57,9 @@ StoryEventType parseEventType(const char* value) {
if (strcmp(value, "action") == 0) {
return StoryEventType::kAction;
}
if (strcmp(value, "voice") == 0 || strcmp(value, "voice_bridge") == 0) {
return StoryEventType::kVoice;
}
return StoryEventType::kNone;
}
@@ -53,6 +53,7 @@ class ScenarioManager {
bool notifySerialEvent(const char* event_name, uint32_t now_ms);
bool notifyTimerEvent(const char* event_name, uint32_t now_ms);
bool notifyActionEvent(const char* event_name, uint32_t now_ms);
bool notifyVoiceEvent(const char* event_name, uint32_t now_ms);
bool gotoScene(const char* scene_id, uint32_t now_ms, const char* source);
ScenarioSnapshot snapshot() const;
@@ -361,6 +361,12 @@ bool ScenarioManager::notifyActionEvent(const char* event_name, uint32_t now_ms)
return dispatchEvent(StoryEventType::kAction, name, now_ms, "action_event");
}
bool ScenarioManager::notifyVoiceEvent(const char* event_name, uint32_t now_ms) {
const char* name = (event_name != nullptr && event_name[0] != '\0') ? event_name : "VOICE_EVENT";
Serial.printf("[ScenarioManager] Voice event received: %s\n", name);
return dispatchEvent(StoryEventType::kVoice, name, now_ms, "voice_event");
}
bool ScenarioManager::gotoScene(const char* scene_id, uint32_t now_ms, const char* source) {
if (scenario_ == nullptr || scene_id == nullptr || scene_id[0] == '\0') {
return false;