Où vont les souvenirs de Claude : Cortex, tracé dans son code
Chaque session Claude Code laisse deux sortes de traces dans Cortex. Les souvenirs vont dans une seule table, memories, chaque ligne portant un vecteur de 384 dimensions et une chaleur que la base recalcule à chaque lecture. Les décisions ont une identité à part : un fichier ADR numéroté sur disque, que la base se contente de pointer. Cette note suit les deux, du hook qui les capture jusqu’au rappel qui les ramène dans le contexte. Chaque affirmation renvoie aux lignes de la version v4.22.0 qui l’implémentent : vous pouvez les vérifier une à une plutôt que me croire sur parole.
Les chiffres de rappel viennent d’une exécution de benchmarks/reproduce.sh --no-regression le 15 septembre 2026, dans un conteneur PostgreSQL éphémère, sur le code publié en v4.22.0 ; ce sont des scores de récupération, pas d’exactitude des réponses.
Deux portes d’entrée, un seul store
Rien n’entre dans Cortex sans passer par l’une de deux portes. Les hooks du plugin se déclenchent seuls, à des moments fixes de la session, et parlent directement à la couche infrastructure3. Les outils MCP (remember, recall, wiki_adr et les autres) passent par le serveur, où seuls les handlers ont le droit de réunir le cœur pur et l’infrastructure1. Le cœur ne fait aucune entrée-sortie : les variables d’environnement, le cache du modèle de reranking et le système de fichiers du wiki lui arrivent par des points d’injection qui lèvent une erreur s’ils ne sont pas câblés, et une seule fonction les câble tous à chaque point d’entrée2.
Ce que les hooks lisent et écrivent
Vous ne voyez qu’une partie du travail : le bloc « Cortex Memory Context » en tête de session5. Six autres hooks lisent ou écrivent sans rien demander. Un seul, decision_gate, ne touche pas la base : il refuse une édition qui ajoute un long commentaire en prose, parce que la prose de décision appartient au wiki6.
Un souvenir franchit un portail de nouveauté
Le stockage a un gardien : il retient ce qui contredit ses attentes. Quatre signaux de nouveauté sont pondérés, puis comparés à un seuil de 0,48. Le portail filtre surtout la capture automatique : un remember délibéré, la classe par défaut, le contourne, comme les décisions, les erreurs et un tag important9. Une décision écrite délibérément sous un agent_topic, depuis une origine de confiance, est aussi marquée globale pour que toute l’équipe d’agents la voie ; une page téléchargée ou une capture automatique qui contient seulement un indice de décision ne l’est pas10.
Une ligne de la table memories
Une ligne porte bien plus que son texte. La colonne la plus trompeuse est la chaleur : aucune colonne heat n’est stockée. La base garde heat_base et l’heure où elle a été fixée ; la fonction SQL effective_heat() applique la décroissance à la lecture, et les lignes protégées ou ancrées y échappent11.
Contenu et vecteurs
- content
- le texte, durci à l’entrée
- embedding
- vector(384), index cosinus
- content_tsv
- plein texte généré
- tags
- JSONB : auto-captured, adr…
Portée
- domain
- projet, depuis la racine git
- directory_context
- répertoire de travail
- agent_context
- l’argument agent_topic
- is_global
- visible depuis tous les projets
Thermodynamique
- heat_base
- 0–1, départ de la décroissance
- heat_base_set_at
- horloge de la décroissance
- no_decay
- coupe la décroissance (ancres)
- surprise_score
- score de nouveauté du portail
Cycle de vie
- store_type
- episodic par défaut, ou semantic
- consolidation_stage
- labile à l’écriture
- access_count
- +1 à chaque rappel
- is_protected
- décision ou ancre
Provenance
- write_class
- auto · deliberate · derived · mechanical
- capture_origin
- deliberate · local_action · network · unknown
- source_attribution
- qui l’a dit
- tag prov:<grade>
- grade de provenance
Supersession
- supersedes_id
- la ligne remplacée
- superseded_by_id
- la ligne qui la remplace
- current_memories
- vue qui masque les remplacées
- compressed
- original_content conservé
Le rappel est aussi une écriture
Un recall cherche d’abord une identité exacte (ADR-0042, un id de souvenir), puis fusionne cinq signaux dans une seule fonction de la base17. Sous PostgreSQL, les signaux sont normalisés par leur maximum puis additionnés avec des poids propres à l’intention ; sous SQLite, la fusion se fait par rangs, w/(k + rang), sans le signal trigramme13. La complétion de Hopfield, la similarité hyperdimensionnelle et l’activation diffusante reclassent le résultat, puis un cross-encoder a le dernier mot14. Les souvenirs rendus ne sortent pas intacts : leur chaleur, leurs compteurs et les arêtes entre leurs entités changent au passage15.
Une décision a une identité : ADR-NNNN
L’identifiant d’une page wiki est le seul index des décisions. Le code ne garde qu’un pointeur, et le rappel résout ce pointeur avant toute recherche floue17. Une décision peut emprunter trois chemins ; chacun laisse une trace à un endroit différent et revient par un chemin différent16.
Quatre mémoires, côte à côte
Ce que Claude apprend pendant une session n’atterrit pas à un seul endroit. Claude Code a sa propre mémoire de fichiers, distincte de Cortex ; Cortex la lit pour le profil cognitif et ne la copie jamais dans le store18.
| Store | Emplacement | Écrit par | Relu | Décroissance |
|---|---|---|---|---|
| Cortex memories | PostgreSQL ou SQLite | les hooks et remember | au démarrage, à chaque prompt, sur recall | oui, calculée à la lecture ; lignes protégées exemptées |
| Wiki ADR | ~/.claude/methodology/wiki/adr/ | wiki_adr | recall exact sur ADR-NNNN | non, c’est un fichier |
| Auto-mémoire Claude Code | ~/.claude/projects/<projet>/memory/ | Claude, par écriture de fichiers | à chaque session : Claude Code charge MEMORY.md | non |
| CLAUDE.md | une section « Memory Insights » entre marqueurs | sync_instructions | à chaque session, avec CLAUDE.md | non |
Cortex · code sur GitHub · Démarrer un pilote