Notes · architecture

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.

StockagePostgreSQL avec pgvector, ou SQLite avec sqlite-vec et FTS5 ; le même schéma memories des deux côtés4
Embeddingsall-MiniLM-L6-v2, 384 dimensions, sur CPU4
Outils52 outils MCP, 55 avec les intégrations amont optionnelles ; un test maintient le schéma client de chaque outil égal à celui de son handler20
RappelLongMemEval-S R@10 0,980, MRR 0,906 · LoCoMo R@10 0,889, MRR 0,78019

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.

Pièce 01 · architecture

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.

Session Claude Codevous, Claude,les sous-agentsPORTE 1 · AUTOMATIQUEHooks du pluginmcp_server/hooks/7 événements de sessionwire_composition_root()PORTE 2 · EXPLICITEServeur MCP cortexserver · tool_registry_*handlers/ · compositioncore/pur, zéro I/Oshared/stdlib seuleinfrastructure/pg_store · sqlite_storeembedding_engineMiniLM-L6-v2, 384 dEN OPTIONPostgreSQL cortexmemories · entitiesrelationshipswiki.* · checkpointspgvector, pg_trgmSQLite memory.dbdéfaut des installs pluginsqlite-vec · FTS5~/.claude/methodology/wiki/**/*.mdprofiles.jsonsession-log.jsonAuto-mémoire~/.claude/projects/*/memory/*.mdévénementsappelsimportecomposeSQLfichiersscanner : lu pour les profils, jamais copié dans memories
Backend. Une installation plugin tourne sur SQLite sauf si une URL PostgreSQL ou le marqueur de l’installeur dit le contraire ; un DATABASE_URL explicite mais injoignable lève une erreur au lieu de retomber en silence4. Le pointillé est une lecture seule : les fichiers mémoire de Claude Code nourrissent le profil cognitif et ne sont jamais copiés dans le store18.
Pièce 02 · hooks

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.

TEMPS DE LA SESSIONSessionStartsession_start1× AU DÉBUTUserPromptSubmitauto_recallCHAQUE PROMPTPreToolUsedecision_gateAVANT EDIT|WRITEPostToolUsepost_tool_captureAPRÈS CHAQUE OUTILSubagentStartagent_briefingCHAQUE SOUS-AGENTNotificationcompaction_checkpointÀ LA COMPACTIONSessionEndsession_lifecycle1× À LA FINinjecte : ancrés,décisions d'équipe,chauds ≥ 0,4 (8 max)plein texte rapide,protégés en tête« Cortex context »bloque (exit 2) si≥ 8 lignes decommentaireHORS BASEINSERT, classewrite_class=autotag auto-capturedbriefing : sonagent_context,puis décisionsINSERT danscheckpointsavant compactionprofil + journal,puis consolidationselon le nb de toursPostgreSQL cortexmemories · checkpoints · entities · relationships · prospective_memories · injection_receiptsle rappel écrit aussi : chaque souvenir injecté laisse un reçu d'injectionprofiles.jsonsession-log.jsonmethodology/
Capture automatique. post_tool_capture garde la sortie des outils Edit, Write, Bash, MultiEdit et NotebookEdit, et seulement les chemins de fichiers pour Read, Glob et Grep ; CORTEX_CAPTURE_MODE accepte full, writes-only ou off6. La sauvegarde avant compaction passe par la notification « compacted » ; SessionEnd consolide selon la longueur de la session, et SessionStart lance en arrière-plan un cycle qui entretient aussi le wiki7.
Pièce 03 · write

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.

contournement : deliberate (défaut) · décision · erreur · tag important · forceEntréeremember(…)contenu nettoyéclasse, originedomaine : racine gitSignauximportanceentités (regex, code)embedding MiniLM384 d, L2-normalisévalencePortail0,40 embedding0,25 entités0,20 temporel0,15 structurelstocke si score ≥ 0,4Curationtop 3 des voisins≥ 0,85 → fusion≥ 0,60 → liensinon → créationconflit → remplacePortée + INSERTstage = labileheat = h₀+0,3·scoreprotégé si décisionglobal si décisiond'équipe ou transverseAprès insertiongraphe d'entitésdéclencheurssynaptic taggingslot d'engrammepage wiki si classéescore < seuilreject below_thresholdquasi-doublonUPDATE en place
Le seuil n’est pas fixe. Après 20 échantillons, il dérive par domaine vers 50 % d’acceptation, borné entre 0,05 et 0,95. La nouveauté temporelle vaut 1 − e−h/24 ; celle de l’embedding, 1 moins la plus forte similarité parmi les 5 plus proches voisins8.
Pièce 04 · row

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é
labile0–1 h · décroissance ×2,0early_ltp1–6 h · décroissance ×1,2late_ltp6–24 h · ×0,8 · plancher 0,05consolidated> 24 h · ×0,5 · plancher 0,10CASCADECASCADECASCADEreconsolidatingplancher 0,05rappel en décalage≥ seuil + 0,3·stabilitérestabilisationCHALEUR EFFECTIVE00.05min_heat par défaut du rappel0.4« Hot Memories » au démarrage1.0anchor : heat_base 1,0 et no_decay
Autour de la ligne. entities et relationships forment le graphe de connaissances, reliés aux souvenirs par memory_entities ; les déclencheurs vivent dans prospective_memories, les règles dans memory_rules, les sauvegardes dans checkpoints. Sous SQLite, les vecteurs vivent dans une table virtuelle vec0 et le plein texte dans FTS511. Durées, multiplicateurs de décroissance et planchers sont dans la table de la cascade12.
Pièce 05 · read

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.

Requêterecall(query, …)ADR-NNNN ou id ?→ lookup exact,avant toute fusionIntentionclassement par regexvector 1,0fts 0,5heat 0,3ngram 0,3ajustés par intentionFusion SQLcosinus pgvectorts_rank_cdtrigrammes pg_trgmeffective_heat()récencesomme pondéréeReclassementsfamiliaritéHopfield · HDCactivation diffusanteémotion, humeurRRF k = 60FlashRankms-marco-MiniLM-L-12-v20,30·fusion+ 0,70·cross-enc.Réponserègles neuro-symboliquesordre « lost inthe middle »reçu d'injectionécritEFFETS DE BORD SUR LES LIGNES RAPPELÉESaccess_count +1replay_count +1reconsolidation de la chaleur :+0,02 · +0,05×émotion · −0,10co-activation hebbienne :arêtes d'entités renforcéesles lignes protégées ne sont pas reconsolidées
unified_search appelle ce même rappel, y ajoute les pages wiki et le code, puis fusionne par RRF k = 60 : il met donc lui aussi à jour les souvenirs qu’il rend.
Pièce 06 · decisions

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.

CHEMINOÙ ÇA VITCOMMENT ÇA REVIENTUne décision« on garde X,parce que Y »wiki_adr(…)numéro suivant, sous verrouécrit la page et réécritl'index des décisions~/.claude/methodology/wiki/adr/NNNN-slug.mdindex : ADR-NNNN → chemin du fichieravec project_root : <repo>/wiki/adr/<projet>/recall("ADR-0042")identité exacte d'abord :l'index donne le fichier,sans recherche floueremember(…)contenu de décision détectécontourne le portaildélibéré + agent_topicmemoriesis_protected = TRUEis_global = TRUE (équipe)ni décroissance ni compressionpointeur ADR : classe mechanicalTeam Decisionsau SessionStart, 3 au plus :protégé ∧ is_global∧ agent_context ≠ ''et non remplacéeÉdition de codedecision_gaterefuse ≥ 8 lignes decommentaire d'affiléeVotre dépôtle fichier source garde une ligne :# source: ADR-NNNNnon comptée dans la limite de 8Lecture du codele pointeur renvoie à la page ;la prose de décision restedans le wiki, pas dans le codepointeur
Team Decisions. Une décision stockée par remember devient protégée automatiquement. Depuis la v4.22.0, une décision délibérée écrite sous un agent_topic est aussi globale, ce qui est la condition que lit le bloc de SessionStart ; les lignes stockées avant le correctif sont rattrapées une fois10. Avec project_root, wiki_adr écrit l’ADR dans le dépôt et rien en base16.

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.

StoreEmplacementÉcrit parReluDécroissance
Cortex memoriesPostgreSQL ou SQLiteles hooks et rememberau démarrage, à chaque prompt, sur recalloui, calculée à la lecture ; lignes protégées exemptées
Wiki ADR~/.claude/methodology/wiki/adr/wiki_adrrecall exact sur ADR-NNNNnon, c’est un fichier
Auto-mémoire Claude Code~/.claude/projects/<projet>/memory/Claude, par écriture de fichiersà chaque session : Claude Code charge MEMORY.mdnon
CLAUDE.mdune section « Memory Insights » entre marqueurssync_instructionsà chaque session, avec CLAUDE.mdnon

Cortex · code sur GitHub · Démarrer un pilote