Assistant personnel · Offline-first · Privé

Mnemo remembers.

Un assistant local construit sur CrewAI + Ollama. Zéro cloud, zéro API externe — tout tourne sur votre machine. Il apprend de vous, raisonne avec vous, et agit pour vous.

10 Crews spécialisés
3 Couches mémoire
5 Routes ML
1076 Tests passants

Conçu pour durer.
Conçu pour vous.

Mnemo n'est pas un wrapper autour d'un LLM cloud. C'est une architecture locale pensée pour la confidentialité, la durabilité et l'autonomie.

Offline-first

Aucune donnée ne quitte votre machine. Les modèles LLM tournent via Ollama en local. Le seul réseau optionnel : la recherche web, avec confirmation explicite à chaque requête.

Mémoire hybride

Trois couches complémentaires : session JSON pour le court terme, Markdown lisible comme source de vérité, SQLite avec FTS5 + embeddings 768d pour la récupération sémantique.

Routing intelligent

Trois niveaux en cascade : keywords déterministes, classificateur ML (scikit-learn, seuils configurables), puis LLM en fallback. Le LLM n'est sollicité que quand c'est nécessaire.

Multi-crew

Chaque type de tâche a son crew dédié : conversation, shell, agenda, planification, briefing, curiosité. Chaque crew est testable et remplaçable indépendamment.

Trois couches.
Une source de vérité.

La mémoire de Mnemo est un système à trois niveaux avec fusion hybride. memory.md est la source de vérité — la base de données en est le dérivé indexé.

Court terme

Session JSON

Transcription complète de la session en cours. Chaque échange est horodaté et associé à une session_id. Conservé jusqu'à la consolidation en fin de session.

sessions/session_{id}.json

Long terme · lisible

Memory Markdown

Source de vérité humainement lisible. ConsolidationCrew extrait les faits importants et les écrit dans des sections structurées (identité, projets, préférences, décisions…).

memory.md

Long terme · indexé

SQLite FTS5 + Embeddings

Index bi-modal : Full-Text Search (FTS5) pour les mots-clés, vecteurs 768d (nomic-embed-text via Ollama) pour la sémantique. Dérivé de memory.md via sync_markdown_to_db().

memory.db

Retrieval par Reciprocal Rank Fusion
FTS5 keyword + cosine similarity RRF merge × poids catégorie × decay fraîcheur top-k chunks
Poids catégories

Priorité sémantique

identité 1.5× · décision 1.3× · projet 1.2× · préférence 1.1×
connaissance 1.0× · historique_session 0.7×

Fraîcheur

Decay temporel

Décroissance exponentielle — demi-vie 30 jours. Poids adaptatif : requête courte → keyword-heavy, requête longue → vector-heavy.

Trois niveaux.
Un seul chemin critique.

Chaque message traverse un pipeline en cascade. Le LLM (EvaluationCrew) n'est activé que si les niveaux rapides ne sont pas suffisamment confiants.

01

Pre-check ML

Keywords déterministes + classificateur ML (sklearn pipeline, joblib). Si confiance ≥ seuil, route directe sans LLM.

shell ≥ 80% scheduler ≥ 75% calendar ≥ 92%
02

EvaluationCrew

Analyse LLM complète : intent, entités, topics, besoin mémoire, besoin web, clarification, complexité, route cible.

JSON structuré fallback conversation
03

Interceptions

Trois garde-fous avant l'exécution : clarification utilisateur, confirmation web (query figée), confirmation shell (whitelist).

needs_clarification needs_web shell confirm
04

_route_message()

Dispatch vers le crew cible selon la route. web_context injecté dans tous les crews si la recherche a eu lieu.

5 routes actives fallback conv
05

Post-session

En fin de session : ConsolidationCrew extrait les faits, puis CuriosityCrew détecte les lacunes et pose des questions.

consolidation curiosité crash recovery

8 spécialistes.
Un seul orchestrateur.

Chaque crew est un ensemble d'agents et de tâches isolés, testable indépendamment. Le routing hybride décide lequel activer.

route : tout message

EvaluationCrew

Analyse l'intent de chaque message et produit un JSON de routing : route, needs_web, needs_clarification, memory_query, complexité…

route : conversation

ConversationCrew

Récupère la mémoire pertinente (RRF) et génère une réponse contextuelle. Reçoit web_context si une recherche a eu lieu.

fin de session

ConsolidationCrew

Extrait les faits importants de la session. Les écrit dans memory.md par section, puis re-synchronise la base de données.

post-consolidation

CuriosityCrew

Détecte les lacunes dans la mémoire (Python structurel + LLM contextuel). Pose jusqu'à 5 questions pertinentes à l'utilisateur.

route : shell

ShellCrew

Traduit les demandes en commandes shell. Validation par whitelist + confirmation explicite obligatoire avant toute exécution.

route : scheduler

SchedulerCrew

Traduit les demandes en tâches planifiées (format JSON multi-tâches). CRUD dans scheduled_tasks SQLite + miroir humain tasks.md.

scheduler · briefing matinal

BriefingCrew

Génère briefing.md (quotidien) et weekly.md (hebdo) depuis le calendrier ICS, les sessions récentes et la mémoire.

route : note

NoteWriterCrew

Écrit et modifie des notes directement dans memory.md via FileWriterTool. Modifications structurées par section.

route : calendar

CalendarWriteCrew

CRUD complet sur fichier ICS local : créer, modifier, supprimer. Indices numériques (#N) pour le ciblage LLM, résolution UID en Python. Confirmation obligatoire en CLI, bypass automatique en web.

De la mémoire
à l'autonomie.

Huit phases (0–7). Cinq complètes, une en cours, deux planifiées. L'objectif final : un assistant capable d'agir, d'apprendre et de s'interfacer avec n'importe quel support.

Complet
0

Mémoire hybride

  • JSON + Markdown + SQLite
  • FTS5 + embeddings 768d
  • Reciprocal Rank Fusion
  • Staleness detection
  • Decay temporel
Complet
1

Tests & qualité

  • Tests N1/N2/N3
  • CONTRIBUTING.md
  • ConsolidationCrew
  • CuriosityCrew
  • Crash recovery
Complet
2

Perception

  • PDF, DOCX, TXT, code
  • Calendrier ICS + RRULE
  • SearXNG + DuckDuckGo
  • Déduplication MD5
  • Audit log web
Complet
3

Action locale

  • ShellCrew + whitelist
  • SchedulerCrew multi-tâches
  • BriefingCrew + scheduler Docker
  • NoteWriterCrew
  • CalendarWriteCrew (ICS local)
Complet
4

API & Interfaces

  • REST API FastAPI + WebSocket
  • Dashboard web React
  • Streaming token-par-token
  • TTS Kokoro-82M (FR + JA)
  • STT faster-whisper
  • Pipeline voix RVC
En cours
5

Proactivité

  • Routing CoR refactorisé
  • Poids adaptatifs par profil
  • MemoryGapReport + WorldState
  • Détection de routines
  • Compression memory.md
  • Multi-profils
Complet
6

GOAP Planner

  • Backward chaining + tri topologique
  • ACTION_REGISTRY — 9 actions
  • PlanStore (plan.md persistant)
  • PlannerCrew + ReconnaissanceCrew
  • PlanRunner — arrêt au bloquant
  • Scheduler migré vers GOAP
Planifiée
7

Interfaces externes

  • Client Raspberry Pi (API REST)
  • Optimisation latence embarqué
  • Desktop pet Unity (C# client)
  • Streaming token → animation
  • Multi-device sync

Sessions du 17 au 18 mars 2026

v16 · 17-18-03-2026 GOAP Planner Phase 6 Poids adaptatifs
5 Bugs corrigés
7 Étapes implémentées
+129 Nouveaux tests
14 Fichiers créés
Feature majeure · Phase 6

GOAP Planner — planification par objectifs

Implémentation complète d'un planificateur GOAP (Goal-Oriented Action Planning) en backward chaining avec tri topologique. 9 actions dans l'ACTION_REGISTRY (FetchCalendar, SyncMemory, AssessMemoryGaps, FillBlockingGaps, ReconModule, CreatePlan, GenerateBriefing, GenerateWeekly, SendDeadlineAlert). Le scheduler est migré : les tâches système (briefing, weekly, deadline_alert) passent désormais par goap_dispatch(goal) — le planner calcule automatiquement la séquence d'actions optimale. Comportement émergent : user_online=False bloque FillBlockingGaps sans code spécifique.

goap/planner.py · scheduler.py — scheduler piloté par objectifs

Feature majeure · Phase 6

PlannerCrew + ReconnaissanceCrew

Deux nouveaux crews. ReconnaissanceCrew : lit les fichiers source en Python (sans LLM) à partir des hints extraits du message utilisateur, puis effectue une synthèse LLM unique → recon_context persisté dans world_state.json. Évite toute hallucination sur des symboles inexistants. PlannerCrew : charge les lacunes mémoire du WorldState, génère un plan structuré via LLM, et le persiste dans plan.md via PlanStore. Déclenché automatiquement quand la complexité est élevée.

crew.py · routing/dispatch.py — route "plan" opérationnelle

Feature · Phase 6

PlanStore + PlanRunner — plans persistants

PlanStore : I/O Markdown avec cases [ ]/[x], sections Étapes / Bloquants / Journal, statut structuré. Opérations : create(), get_next_step(), mark_done(), add_blocker(), append_log(). PlanRunner : exécute les steps via un dictionnaire d'executors (_STEP_EXECUTOR), s'arrête au premier bloquant (option B). check_active_plans() appelé au démarrage de chaque session pour reprendre les plans en cours.

tools/plan_tools.py · main.py — planification persistante inter-sessions

Feature · Phase 5.5-5.6

Poids adaptatifs par profil + MemoryGapReport

Phase 5.5 : poids appris par profil d'usage (learned_weights_{profile}.json), régression vers la moyenne (REGRESSION_RATE=0.05), audit trail (learned_weights_history.jsonl, purge > 90 jours). Priorité : statique → learned_global → learned_profil → overrides.

Phase 5.6 : MemoryGapReport structure les lacunes en bloquantes vs enrichissantes. to_world_state() génère les flags GOAP (memory_gaps_known, memory_blocking_gaps, memory_completeness). Persisté dans world_state.json.

memory_tools.py · main.py — mémoire auto-évaluée et GOAP-ready

Fix

Clamp WEIGHT_MAX — régression bloquée

Le test de régression vers la moyenne échouait : base["identité"]=2.8 produisait un poids brut de 3.08 — au-dessus de WEIGHT_MAX=3.0 avant ET après régression, les deux clampés à 3.0, delta=0. Fix : base["identité"]=2.5 → poids brut 2.75, régression mesurable.

test_active_learning.py — test régression valide

Fix

GOAP — effets négatifs non résolus

_actions_that_produce() ne gérait que les effets True. FillBlockingGaps a effects={"memory_blocking_gaps": False} → jamais sélectionné par le planner. Fix : signature étendue à _actions_that_produce(key, value, actions) pour matcher toute valeur d'effet.

goap/planner.py — effets booléens correctement résolus

Fix

Keywords plan — faux positifs par sous-chaîne

"implémenter" matchait le keyword strong "implémente" → route forcée vers plan sur n'importe quel verbe contenant la sous-chaîne. Fix : re.search(r"\b" + re.escape(kw) + r"\b") pour les keywords mono-mot sans espace ni tiret.

routing/handlers/keyword.py — détection plan précise

Sessions du 14 au 16 mars 2026

v15 · 14-16-03-2026 Kokoro-82M Interface Voix RVC à chaud
6 Bugs corrigés
3 Features ajoutées
1 Onglet dashboard
10 Fichiers modifiés
Feature majeure

Kokoro-82M — TTS multilingue unifié

Remplacement de Piper par Kokoro-82M (~327 MB pour la gestion des voix françaises et japonaises). Modèle HuggingFace GustavZ/kokoro-82m (fork de, hexgrad/Kokoro-82M). Téléchargement automatique au premier appel dans /data/models/ (volume persistant). Détection de langue par plage Unicode : hiragana / katakana / kanji → pipeline japonais, sinon → pipeline français. Double-checked locking pour les deux singletons (_kokoro_pipeline_fr, _kokoro_pipeline_ja) — thread-safe dès l'initialisation.

audio_tools.py — Piper supprimé, Kokoro-82M en production

Feature

Flux audio par phrases — prefetch-1 pool

Le frontend découpe la réponse en phrases avant d'appeler POST /api/tts. Seules 2 requêtes coexistent à tout moment : la phrase en lecture et la suivante en prefetch. Supprime les chevauchements audio et les race conditions sur le singleton Kokoro. Pattern : nextFetch lancé avant await fetchPromise, puis lu en séquence.

ChatPage.tsx — latence perçue réduite, zéro superposition

Feature

Onglet Voix — paramétrage RVC à chaud

Nouveau VoicePage.tsx dans le dashboard : choix de voix Kokoro (FR / JA), vitesse, toggle RVC on/off, 6 sliders RVC (f0_method, f0_up_key, index_rate, filter_radius, rms_mix_rate, protect), upload de modèle .pth + .index custom, bouton Tester (applique les réglages du formulaire au runtime sans sauvegarder) et bouton Sauvegarder (persiste dans /data/voice_settings.json).

VoicePage.tsx · api.py — réglages voix sans redémarrage

Fix critique

espeak-ng crash — tmpfs noexec

phonemizer copie libespeak-ng.so dans /tmp et la charge via ctypes. Le tmpfs Docker est noexec par défaut → OSError: failed to map segment from shared object. Fix : tmpfs: - /tmp:size=256m,mode=1777,exec dans docker-compose.yml.

docker-compose.yml — Kokoro japonais fonctionnel en container

Fix critique

HuggingFace cache — filesystem en lecture seule

L'image est read_only: true. HF Hub écrit des lock files dans HF_HOME. Tentatives en /tmp et /app/models → échecs. Fix : HF_HOME=/data/models (volume en écriture), suppression de HF_HUB_OFFLINE=1. Kokoro se télécharge une fois, est mis en cache de façon persistante.

dockerfile · docker-compose.yml — cache HF sur volume /data

Fix

MeCab / unidic — dictionnaire absent

misaki[ja] installe le package unidic mais pas ses données de dictionnaire. Premier appel TTS japonais → RuntimeError: Failed initializing MeCab. Fix : ajout de python -m unidic download dans le dockerfile après l'installation de requirements.audio.txt.

dockerfile — japonais opérationnel après rebuild

Fix

torch.Tensor.astype() + voix ff_emma inexistante

Kokoro retourne un torch.Tensor, le code supposait un np.ndarrayAttributeError: 'Tensor' has no attribute 'astype'. Fix : if hasattr(audio, "numpy"): audio = audio.numpy(). Voix ff_emma et fm_galvani retirées de KOKORO_VOICES_FR (inexistantes dans Kokoro-82M) — seul ff_siwis est disponible côté français.

audio_tools.py — TTS 500 résolu, liste voix exacte

Session du 11 mars 2026

v14 · 11-03-2026 WebSocket streaming Sessions web Calendrier UX
5 Bugs corrigés
3 Features ajoutées
1 Endpoint ajouté
8 Fichiers modifiés
Fix critique

Message streamé disparaissait après réception

Race condition dans le handler done du WebSocket : streamBufRef.current = '' s'exécutait immédiatement, avant que React traite l'updater de setMessages qui lisait le ref déjà vidé. Le message apparaissait pendant le streaming puis disparaissait au moment de la finalisation. Fix : capture de const finalContent = streamBufRef.current avant le reset, passage de la valeur snapshot à l'updater.

ChatPage.tsx — streaming → messages persistants

Fix critique

Sessions web jamais créées

Trois causes en cascade. (1) ingest_tools.py importait DB_PATH depuis memory_tools — symbole supprimé lors de la migration multi-utilisateurs : ImportError sur chaque message, update_session_memory jamais appelée. (2) main.py a un appel module-level _set_data_dir("/data") ; importé lazily dans user_context.run(), ce code écrasait le chemin utilisateur. (3) Fix : import anticipé de main.py au démarrage API (import Mnemo.main as _mnemo_main) + DB_PATH remplac�� par _db_path_default() dans ingest_tools.py.

api.py · ingest_tools.py — sessions écrites dans users/{username}/sessions/

Fix

Décalage d'une heure — vue Semaine calendrier

Le gutter des heures n'avait pas d'espaceur en haut alors que les colonnes de jours ont un header de 44 px : toutes les étiquettes horaires apparaissaient une ligne trop haut. Fix : ajout d'un <div className={styles.weekGutterSpacer} /> dans le gutter + CSS height: 44px associé.

CalendarPage.tsx — alignement heure exact

Fix

TypeScript — champ duration inexistant

Migration ModalState.durationendTime incomplète : openEdit, submit (×2) et le JSX référençaient encore modal.duration après la mise à jour du type. Les 4 occurrences corrigées. La durée est désormais calculée automatiquement via timeDiffMinutes(modal.time, modal.endTime) (minimum 15 min).

CalendarPage.tsx — build TypeScript propre

Feature

Import de fichier ICS

Bouton ↑ ICS dans le header du calendrier (visible si writable). Endpoint POST /api/calendar/import : parse le fichier uploadé, fusionne les VEVENTs dans le calendrier local, déduplique par UID existant. Retourne {"imported": N, "skipped": M} avec toast de confirmation.

Import sans doublon · feedback immédiat

Feature UX

Heure de fin à la place de Durée (min)

Le formulaire d'ajout / modification d'événement affiche désormais un champ Heure de fin (<input type="time">) plutôt qu'une durée en minutes. Plus naturel : "de 14h à 17h" au lieu de "180 minutes". La durée est calculée automatiquement à la soumission.

CalendarPage.tsx — UX alignée sur les habitudes utilisateurs

Feature

Consolidation automatique à la reconnexion

À chaque connexion WebSocket authentifiée, consolidate_orphan_sessions() s'exécute dans le contexte utilisateur via run_in_executor. Scanne sessions/*.json sans marker .done, lance ConsolidationCrew sur chacune → extraction des faits → memory.md + sync SQLite + marker .done. Parité avec le comportement CLI au démarrage (crash recovery).

api.py — mémoire consolidée même sans fermeture propre

Session du 10 mars 2026

v13 · 10-03-2026 CRUD mémoire Confirmation web interactive Rappels dashboard
1 Composant ConfirmModal
2 Endpoints ajoutés
1 Flow web two-step
4 Fixes & améliorations
Feature majeure

ConfirmModal — adieu window.confirm()

Nouveau composant React réutilisable en remplacement de tous les window.confirm() et alert(). Pattern useRef + Promise : await askConfirm() retourne true/false comme une confirmation synchrone. Props : message, confirmLabel, danger (fond rouge via var(--a4)). Utilisé dans MemoryPage, CalendarPage et ChatPage.

UX cohérente · aucun dialogue natif du navigateur

Feature

CRUD mémoire dans le dashboard

MemoryPage entièrement réécrite : sidebar de sections cliquables, éditeur titre + corps, guard dirty state (confirmation avant changement de section), ajout d'une nouvelle section, suppression avec confirmation danger. Sauvegarde → POST /api/memory → écriture memory.md + sync_markdown_to_db(). Préambule (contenu avant le premier ##) préservé à chaque round-trip.

Mémoire éditable et synchronisée sans CLI

Feature

Confirmation web interactive — recherche web

Fini le refus silencieux de needs_web en mode dashboard. Premier appel POST /api/message : si le LLM retourne needs_web=true, l'API répond {needs_web_confirm: true, web_query} au lieu de procéder. Le frontend affiche une ConfirmModal avec la requête figée. Deuxième appel avec web_confirmed: true/false : le backend lance (ou non) web_search() et injecte le contexte dans _route_message().

Parité CLI / dashboard sur la confirmation web

Feature

Rappels dashboard — GET /api/reminders

Nouveau endpoint qui parse les blocs ## 🔔 Rappel de briefing.md (généré par le scheduler). Chaque rappel reçoit un identifiant MD5 pour la déduplication côté client. App.tsx poll toutes les 60 secondes quand l'onglet est actif. Chaque rappel nouveau déclenche un toast react-toastify. Déduplication par date dans localStorage : un rappel vu aujourd'hui ne réapparaît pas.

Rappels planifiés visibles dans le dashboard sans action manuelle

Fix + Feature

Jours passés — vue semaine

get_events_with_uid reçoit un nouveau paramètre from_date : l'API passe le lundi de la semaine courante pour inclure les jours déjà écoulés. Côté frontend : classes CSS pastDay / pastHdr / pastEv grisent les colonnes passées sans les masquer.

Semaine complète visible, passé discret

Feature

[EVAL] — logs de routing

Toutes les décisions de routing sont désormais loguées avec leur source : [EVAL] (bypass kw), (bypass ML) ou (LLM), dans main.py et api.py. Le JSON d'évaluation complet est affiché à chaque message.

Routing transparent et debuggable

Fix

Coercion web_query / needs_web

Incohérence LLM corrigée à deux niveaux : règle absolue ajoutée au prompt ("si web_query est non-null, needs_web DOIT être true") + coercion code dans main.py après _parse_eval_json. Mots-clés "fouille", "fouille le net", "cherche sur internet" ajoutés. Règle scheduler simplifiée : heure précisée sans date → aujourd'hui, sinon → demain (exemples ISO concrets).

Routing web et scheduler fiables sur les petits modèles

Session du 09 mars 2026

v12 · 09-03-2026 8 bugs corrigés Migration React
8 Bugs corrigés
8 Composants React
3 Fixes CalendarWriteCrew
1 Commande CLI ajoutée
Feature majeure

Migration React + TypeScript

Le dashboard vanilla JS (1 200+ lignes) migré vers React + TypeScript + Vite avec CSS Modules. 8 composants créés : App, NavBar, MessageBubble, ChatPage, MemoryPage, SessionsPage, CalendarPage + wrapper api/index.ts typé. Proxy Vite→FastAPI en dev, build vers src/Mnemo/static/.

Dashboard scalable, état centralisé, zéro code spaghetti

Fix critique

SPA catch-all bloquant les routes /api/*

@app.get("/{full_path:path}") était enregistré avant toutes les routes API dans FastAPI. Après rebuild Docker, toutes les requêtes /api/* retournaient 404. Corrigé en déplaçant le catch-all SPA en toute fin de api.py.

Routes API fonctionnelles après rebuild

Fix critique

CalendarWriteCrew — UIDs Google (60+ chars)

Les UIDs Google Calendar (chj66opo6gsj...@google.com) étaient copiés de façon inexacte par le LLM, causant des erreurs "UID inconnu". Solution : format_events_with_uid passe des indices #N, CalendarWriteCrew.run() résout #N → UID complet en Python avant tout appel delete_event / update_event.

Suppression et modification fonctionnelles même avec des UIDs opaques

Fix

Résolution des dates (noms de jours)

Le LLM confondait "samedi" avec aujourd'hui ou calculait une date erronée, faute d'ancre explicite. Ajout de get_week_dates_for_prompt() : table samedi = 2026-03-14, dimanche = 2026-03-15… sur 14 jours, injectée en {week_dates} en tête du prompt CalendarWriteCrew. Contexte injecté réduit de 14 → 7 jours pour limiter le bruit.

Dates résolues par lookup direct, sans arithmétique LLM

Fix

Sessions illisibles

Type SessionMessage incorrect côté frontend (user_message/response vs role/content). Preview API vide (cherchait messages[0].user_message). Les deux corrigés.

Bulles de sessions peuplées correctement

Fix

_web_mode non propagé

CalendarWriteCrew recevait _web_mode=False en contexte web, déclenchant un input() bloquant la requête API. Corrigé dans base_inputs de _route_message() et _detect_calendar_write_intent ajouté au handler web.

Calendrier modifiable depuis le dashboard sans blocage

Fix · Feature

Label "Cette semaine" + rebuild

Lundi prochain tagué "Cette semaine" (diff flottant < 7 j). Corrigé avec les bornes calendaires réelles (lun–dim). Ajout de ./mnemo.sh rebuild : stop → build → up en une commande.

Labels d'urgence corrects · workflow rebuild simplifié

Session du 08 mars 2026

v11 · 08-03-2026 7 bugs corrigés 21 exemples ML
7 Bugs corrigés
21 Exemples ML ajoutés
2 Features ajoutées
30s Polling calendrier
Fix critique

Vue semaine décalée d'un jour

toISOString().slice(0,10) retourne la date UTC — en GMT+1, les événements du lundi apparaissaient le mardi. Corrigé par un helper _fmtDate(d) basé sur getFullYear / getMonth / getDate (heure locale).

Vue semaine alignée avec le fuseau horaire local

Fix critique

CalendarWriteCrew — date incorrecte

Le LLM résolvait les dates relatives sans ancre ISO explicite, causant des décalages. Injection de today_iso (YYYY-MM-DD) dans crew.py et dans le prompt YAML. Règle ajoutée : si le jour nommé correspond à aujourd'hui, utiliser today_iso directement sans dériver.

Dates résolues depuis une référence ISO absolue

Fix

Routing CalendarWriteCrew — formes directes

"Ajoute un événement…" était routé vers ConversationCrew. Ajout de _CALENDAR_WRITE_KEYWORDS et _detect_calendar_write_intent() dans main.py, bypass déterministe identique au pattern shell/scheduler.

Demandes calendrier (impératif) routées sans LLM

Fix

Routing — formes naturelles indirectes

"Tu peux me le supprimer du calendrier ?" n'était pas détecté (forme infinitive + lieu, sans impératif). Ajout d'une détection par co-occurrence : verbe d'action (supprimer, retire, annule…) + mot contexte (calendrier, agenda, événement…). 21 nouveaux exemples injectés dans training_data.jsonl + modèle ML ré-entraîné.

Toutes les formulations naturelles de suppression/création détectées

Fix + Feature

Refresh calendrier post-écriture IA

Cache Python par processus (CLI ≠ FastAPI) : l'API retournait des données stale jusqu'à 5 min après une écriture CLI. Fix : vérification mtime ICS à chaque GET + polling frontend 30s si l'onglet Calendrier est actif.

Événements créés par l'IA visibles en < 30s

Feature

Bouton ✕ en vue semaine

CRUD complet en vue semaine : bouton ✕ rouge au hover à côté du ✎, avec confirm() avant suppression. display:flex via onmouseenter pour les deux boutons côte à côte.

Delete accessible directement depuis la vue semaine

Feature

mnemo.sh — commande services

./mnemo.sh services démarre mnemo-scheduler et mnemo-api en une seule commande. stop arrête les deux. Ajout de logs-api et de la commande api seule.

Démarrage complet de l'environnement en 1 commande

Session du 07 mars 2026

v10 · 07-03-2026 38 nouveaux tests
38 Nouveaux tests
10 Routes API
2 Vues calendrier
1 Crew implémenté
Nouveau crew

CalendarWriteCrew

Implémentation complète du stub. Trois fonctions write ICS locales : add_event, update_event, delete_event. Confirmation interactive obligatoire avant toute modification ou suppression (commande figée après kickoff). Sources URL distantes refusées en écriture.

Nouveau

Dashboard FastAPI

api.py : 10 routes REST — health, message, memory, sessions, et calendrier CRUD (GET / POST / PUT / DELETE). Pipeline web sans stdin : shell bloqué, web auto-refusé, clarification ignorée. Service Docker mnemo-api exposé sur DASHBOARD_HOST:DASHBOARD_PORT.

Nouveau

Frontend SPA

Dashboard vanilla JS, 4 tabs : Chat, Mémoire, Sessions, Calendrier. Sessions affichées en bulles de conversation. Calendrier avec CRUD intégré (modal formulaire, boutons ✎ / ✕ au hover). Vue Liste et Vue Semaine (grille 7 j × 7h–22h) avec navigation ← Aujourd'hui →.

TODO

Refactorisation → React

La SPA vanilla approche 1 200 lignes de code imbriqué. Migration prévue vers React.js pour un scaling propre : composants réutilisables, état centralisé, fin du code spaghetti. Déclencheur : prochaine feature majeure frontend.

Fix critique

Durées calendrier

_make_event_dict calculait DTEND − ev_datetime (date de l'occurrence reconstruite), produisant des durées négatives pour les événements récurrents. Corrigé en DTEND − DTSTART depuis le composant original.

Vue semaine : hauteurs des blocs désormais correctes

Fix

Sessions : bulles vides

Le rendu utilisait m.user_message / m.agent_response alors que le JSON de session stocke des objets {role, content} individuels. Corrigé par un map basé sur m.role.

Bulles de conversation correctement peuplées

Tests · N1/N2/N3

test_calendar_write.py

38 tests couvrant les fonctions write ICS et CalendarWriteCrew.run(). N1 : fonctions pures. N2 : écriture réelle sur fichier ICS temporaire. N3 : crew avec kickoff() mocké (create, delete confirmé/annulé, update, cas dégradés).

38 / 38 · 55 s

Session du 06 mars 2026

v9 · 06-03-2026 787 tests passants
787 Tests passants
31 Régressions corrigées
5 Routes ML
2 Nouveaux crews
Nouveau

FileWriterTool

Outil d'écriture directe dans memory.md et dans les notes. Opérations supportées : append_section, replace_section, create_file. Validation de chemin intégrée pour éviter les écritures hors du répertoire /data.

Nouveau crew

NoteWriterCrew

Crew dédié à la prise de notes structurée. Utilise FileWriterTool pour écrire dans memory.md par section. Route note ajoutée au pipeline. Intégré dans le routing ML à 5 classes.

ML · routing

Modèle 5 routes

Le classificateur ML passe de 4 à 5 routes : conversation, shell, scheduler, calendar, note. Ré-entraînement du pipeline sklearn, mise à jour des seuils et du fichier router_model.joblib.

Tests · N1/N2/N3

Tour de tests complet

787 tests verts sur l'ensemble du projet. 31 régressions identifiées et corrigées (FileWriterTool, NoteWriterCrew, routes ML, mocks shell, calendar_tools). Nouveaux fichiers : test_note_writer.py, test_file_writer.py.

Fix

calendar_tools.py

Correction de l'expansion RRULE pour les règles WEEKLY avec BYDAY. Gestion des EXDATE et COUNT dans l'expansion manuelle.

Tests calendar : 100% pass

Fix

Shell mocks

Mise à jour des mocks ShellCrew.run() dans test_router.py et test_session_cycle.py pour le nouveau format d'inputs avec web_context.

Tests shell : 100% pass

Refactor

Landing page

Refactorisation CSS : de 349 lignes à ~270 lignes. Système de cards générique avec custom properties CSS (--cc, --cb) remplaçant 6 blocs dupliqués.

HTML réécrit comme présentation projet