Les planches d’ancrage · série II · feuille 10 / 19

Les mémoires : séparer

L'AXE DES DONNÉES — classer, et séparer. Ce qui trie, ce qui sépare, et ce qui ne sépare rien.

Planche 10 · Les mémoires : séparer
29 pièces · 14 bâties · 12 partielles · 2 absentes · 1 non établies
Planche 10 — Les mémoires : séparer

ouvrir la planche en plein écran → · 29 pièces · 16 traverses · 16 marges

La thèse une ligne, écrite dans la matière

La CLASSIFICATION est bâtie et vérifiable (2 axes orthogonaux, 34 libellés sur 3 faces, 16 canaux en énum fermée, 8 branches tenues par 12 épreuves) ; la COMPARTIMENTATION, elle, est bâtie du côté de l'ÉCRITURE et creuse du côté de la LECTURE — et la correction décisive est que les deux « portes à 5 » ne sont pas la même porte : celle des 5 DROITS mémoriels a bien un appelant de production, qui lui passe un tuple VIDE ; celle des 5 AXES a sept appelants applicatifs, mais son axe nº3 (womb_depth) n'est alimenté par AUCUN d'eux, et la seule fonction qui le dériverait de la rive durable a zéro appelant.

Les marges 16 arbitrages ouverts, entiers
  • ⚑ LES DEUX « PORTES À CINQ » NE SONT PAS LA MÊME PORTE, ET LA THÈSE LES A FUSIONNÉES. Il y a cinq DROITS mémoriels (read · write · propose_promotion · derive · purge — instance_passport.py:51) pesés par compartiment dans p0p2_admission_gate.py:507-517 ; et cinq AXES de membrane (localisation · identité · womb_depth · engagement · rôle — membrane_service.py:9-20) pesés par authorize_axes. La thèse disait « la porte des 5 droits n'a aucun appelant applicatif ». Mesuré : la porte des 5 DROITS a UN appelant de production qui lui passe `memory_access=()` (supervisor_p0p2_admission_gate.py:446) — appelée, mais avec rien à peser. La porte des 5 AXES a SEPT appelants applicatifs, dont un routeur enregistré (coquille_router, main.py:2342) — donc BÂTIE et atteinte. Deux défauts réels, deux formes différentes, et l'un n'excuse pas l'autre.
  • ⚑ LE VRAI TROU QUE LA THÈSE A MANQUÉ : L'AXE Nº3 EST MORT DANS LES SEPT CHEMINS. J'ai ouvert les sept sites d'appel un par un. Cinq passent `engagement_level` (bureau_service.py:151, knowledge_gate/porte.py:210, mcp_carte_tools.py:76, mcp_live_organs.py:44, mcp_repo_tools.py:70). ZÉRO passe `womb_depth`. Il reste donc au défaut `0` (membrane_service.py:231) dans toute la production, et le seuil `womb_depth < required_womb_depth` (:352) ne peut jamais refuser. La fonction qui le remplirait depuis la rive durable — `aderive_womb_depth` (:493), avec sa règle nommée « un point = un couple DISTINCT (event_type, jour) » — a zéro appelant hors du module, comme `aauthorize` (:529) qui l'enveloppe. La membrane a cinq axes écrits et quatre axes vivants ; et celui qui manque est précisément celui du MÉRITE GESTATIONNEL, c'est-à-dire la doctrine entière de « l'information se mérite ».
  • ⚑ CORRECTION DU POOL ACMI : CE N'EST PAS « LA MÊME LIGNE SANS JOINTURE », C'EST PIRE. La thèse annonçait les mémoires et les compartiments dans la même ligne. Mesuré : DEUX dictionnaires séparés sur le même objet — `self.local_memory` (canon_aware_memory_agent.py:121) et `self._compartments` (:130) — et `recall(fractal_address)` (:402-404) lit le premier sans jamais ouvrir le second. Le défaut n'est pas l'absence de jointure entre deux colonnes : c'est que le chemin de lecture et le registre d'isolement ne se rencontrent NULLE PART. L'unique endroit où les deux cohabitent vraiment est une photo durable : `acmi_agent_pool_records.local_memory` (JSONB, :25) à côté de `.compartments` (JSONB, :26) — et une photo ne garde rien.
  • ⚑ LE MUR DE LA LECTURE N'EST PAS ABSENT : IL EST BINAIRE ET COLLECTIF. La thèse disait « pas de mur du côté de la lecture ». Précision mesurée : tout `/api/v2/acmi` est derrière `require_internal_token`, posé en dépendance de l'APIRouter entier (acmi_router.py:72-81). `/recall` n'est donc pas ouvert au public. Mais c'est UN secret partagé, pas un droit nommé : qui le porte rappelle la mémoire de n'importe quel agent à n'importe quelle adresse fractale. La différence compte, parce qu'on ne corrige pas un mur absent et un mur sans portes de la même façon.
  • ⚑ LE « CHECK EN BASE » DE LA THÈSE N'EST PAS CELUI QU'ELLE CROYAIT. `memory_layer_events` n'a AUCUNE contrainte CHECK — `grep -i check` sur v82_086_memory_contract.py rend 0 ; la colonne `layer` n'a qu'un `DEFAULT 'hot'` (:31). La discipline hot/warm/cold vit uniquement en Python (memory.py:191). Les vrais CHECK de portée mémorielle sont ailleurs, et ils sont bons : `acmi_memory_deposits.scope IN ('indiv','commu','civ')` (models/acmi_memory_deposit.py:89-92) et `acmi_agent_pool_records.default_scope IN ('unknown','indiv','commu','civ')` (:32-35).
  • ⚑ ET `memory_layer_events` N'A PAS DE PROPRIÉTAIRE. Aucune colonne d'usager dans la table ; `created_by` est rempli avec `thread_id` par la migration elle-même (v82_086:78). La propriété est reconstituée à la main, en Python, par un `SELECT owner_id FROM threads` (memory.py:658-661). Conséquence exacte : la séparation des mémoires de fil tient entièrement à ce que ces treize appels soient là. Ils y sont, 13/13 — mais rien dans la base ne les remplacerait si un quatorzième endpoint naissait sans son garde. Et c'est exactement ce qui s'est produit le 2026-08-06, consigné dans le code même (memory.py:600-612) : la porte gardée, six fenêtres nues, 200 sans jeton.
  • ⚑ UN POINT OÙ LE DÉPÔT FAIT MIEUX QUE SA RÉPUTATION. Le womb écrit en FAIL-CLOSE réel : `db is None → 503` avec sa raison (womb.py:104-108), et le commentaire garde la trace du défaut corrigé — l'ancien repli rendait `status: ok` pour un événement qui disparaissait au redémarrage. Même honnêteté à la lecture : `/status` rend `source` et `degraded` (:200), et refuse de traiter « zéro événement » comme une panne (:191-196 — « Personne n'a rien fait n'est pas la base est tombée »). La mémoire de fil, elle, n'a PAS cette franchise : `_events_for_thread` (memory.py:580-594) sert un magasin local quand la base rend `None`, sans un champ pour le dire.
  • ⚑ LA PHASE DU WOMB EST UNE DÉDUCTION, PAS UN ÉTAT. L'INSERT ne nomme que quatre colonnes — user_id, event_type, data, recorded_at (womb.py:119-120) ; le `phase` du modèle part dans le JSON `data` (_envelope, :75-84). Et `/status` rend `"phase": "active" if recent else "dormant"` (:199-201) : la gestation « active » signifie seulement qu'il existe des événements dans les dix derniers. Un champ annoncé n'est pas un chemin implémenté.
  • ⚑ TROIS FAÇONS DONT L'ISOLEMENT MÉMORIEL CANON NE TIENT PAS, et aucune n'est un oubli de code — chacune est une décision visible. (1) Le demandeur déclare sa propre portée : `observer_scope` vient du corps de la requête (canon_compartment_router.py:58), pendant que `Depends(get_current_user)` (:92) authentifie sans jamais alimenter le verdict. (2) Le gate des dimensions ne se déclenche que si l'observateur en déclare : `if observer_dimensions:` (compartment.py:130) — ne rien déclarer le saute. (3) Un pont désarme la portée pour TOUT LE MONDE : `if observer_scope != compartment.scope and not compartment.bridged_compartments` (:117-121) teste si la liste de ponts est non vide, jamais si le demandeur est au bout d'un pont.
  • ⚑ LES COMPARTIMENTS CANON N'ONT PAS DE TABLE. `_COMPARTMENTS` est un dict de module dans canon_compartment_router.py ; `self._compartments` un dict par agent (canon_aware_memory_agent.py:130). Créer, sceller ou mettre en quarantaine un compartiment est un geste qui ne survit pas au prochain déploiement — et le déploiement redémarre le backend en une minute. Les deux seules persistances de compartiment du dépôt portent d'autres noms : `instance_memory_grants.memory_compartment` (les droits) et `acmi_memory_deposits.memory_compartment` (les dépôts).
  • ⚑ `civ` PAR DÉFAUT : CONFIRMÉ, MAIS IL FALLAIT DIRE OÙ. Le défaut est celui de la mémoire fractale BABEL — `DEFAULT_TREE_SCOPE = "civ"` (fractal_pipeline.py:94, fractal_dataseed.py:23, babel_macro_decoder.py:81), avec sa raison écrite à côté : « rétro-compatibilité V1 ». Ni la membrane ni le passeport ne font ça. Et le contre-exemple existe, bâti exprès : `acmi_agent_pool_records.default_scope` vaut `'unknown'` (:22, migration v82_280), jamais promu à la plus large. ⚠ Mais la divergence entre les deux CHECK n'est gardée par rien : le pool admet `unknown`, `acmi_memory_deposits` ne l'admet pas (3 valeurs contre 4). Un agent `unknown` sera refusé par la base, sans refus nommé pour le dire.
  • ⚑ LE CHIFFRE « 13 COUCHES » N'EST PAS ÉTABLI, ET JE DIS POURQUOI. J'en ai vérifié ONZE, chacune située : womb_events · memory_layer_events · memory_layer_snapshots · PersistentDict(memory_events) · PersistentDict(memory_snapshots) · instance_memory_grants · acmi_memory_deposits · acmi_agent_pool_records · _acmi_pool (RAM) · _COMPARTMENTS (RAM) · les trois paliers navigateur. On arrive à 13 en comptant hot/warm/cold séparément — c'est très probablement l'origine du chiffre, mais c'est une hypothèse, pas une mesure. Tant que la règle de comptage n'est pas écrite, le compte est une rumeur.
  • ⚑ CE QUE LA CLASSIFICATION, ELLE, TIENT — et il faut le dire avec la même précision que les trous. Les quatre chiffres de CLAUDE.md sur cet axe ont été RECOMPTÉS à HEAD et sont justes : 34 libellés (16 opérationnels + 11 données + 7 sectoriels, recomptés sphère par sphère sur le dict LIBELLES de spheres_libelles.py:66-141) ; 16 canaux (event_bus.py:289-294) ; 8 branches + 1 tronc (arbre_canon.py:70, :73) tenus par 12 épreuves (hf-atom-tools/atom_canon/tests/test_arbre_canon.py, 12 `def test_`) ; 27 = 3 × (8+1) (arbre_canon.py:155-157). La décision 34 — « la taxonomie de CLAUDE.md EST le canon » — se vérifie.
  • ⚑ LE PIÈGE DES DEUX RÉPONSES EST RÉEL ET NON GARDÉ. `sphere_de('TRONC')` rend S0 (spheres_libelles.py:71 — « TRONC » enregistré comme face DONNÉES de S0) pendant que `arbre_canon.TRONC` vaut S9 (arbre_canon.py:73). Deux modules du MÊME paquet, deux réponses, aucun test qui les croise. Ce n'est pas une erreur — c'est une homonymie de FACE, et CLAUDE.md la documente — mais rien dans le code n'empêche une session future de la lire comme une erreur et de « corriger » le bon module.
  • ⚑ LA RÉSOLUTION PAR LE SENS EXISTE ET PERSONNE NE S'EN SERT. `sphere_de()` ferme exactement le piège qui a coûté une session (lire la 6ᵉ clé de ATOM_MAPPING et conclure « S6 = communication », alors que Communication est S0). Mais les trois listes divergentes que sa propre docstring dénonce (:21-30) sont toujours là, inchangées : `routers/spheres.py` CANONICAL_SPHERES, `atom-mapping-data.js` ATOM_MAPPING.spheres, `atom-manifest-format.js` SPHERE_RULES. L'organe de réconciliation est bâti ; aucun classeur ne l'appelle. C'est le même motif que la myéline et que `aderive_womb_depth` : la bonne pièce, posée à côté de la chaîne.
  • ⚑ PÉRIMÈTRE DE MES « ZÉRO APPELANT », parce qu'un zéro sans son périmètre est une rumeur. Fouillé : `backend/app/` ET `backend/` hors app/ (le second arbre, ~278 .py), `agents/`, `frontend/`, `scripts/`, `hf-atom-tools/` — hors `archives/`, `transit/`, `backend/tests/`, `__pycache__`, `.venv-tests`. Mesuré au COMMIT (`git show HEAD:` / `git grep HEAD`), pas dans l'arbre de travail, parce que 60+ fichiers backend sont sales sur cette branche (`integration/fondateur-p0`, HEAD = 8addf1c9481559a8e9dbc97b1fc0bcc85e10a0e1). Les routeurs cités sont vérifiés enregistrés dans `git show HEAD:backend/app/main.py` — mais `register_router` avale les ImportError : « enregistré » n'est pas « monté sans erreur » tant qu'on n'a pas interrogé le serveur vivant, ce que cette planche n'a pas fait (lecture seule).
Les blancs déclarés ce que la mesure n’a pas pu voir

Mesuré en lecture seule au commit 8addf1c9481559a8e9dbc97b1fc0bcc85e10a0e1 (branche integration/fondateur-p0), par `git show HEAD:` et `git grep HEAD` — jamais dans l'arbre de travail, qui est sale sur 60+ fichiers backend. Aucune base interrogée, aucune production touchée, aucun commit. CE QUI N'EST DONC PAS ÉTABLI ICI : (1) qu'un routeur « enregistré dans main.py » soit effectivement monté — `register_router` avale les ImportError, et seule une interrogation du serveur vivant le dirait ; (2) le contenu réel des tables nommées — `womb_events`, `memory_layer_events`, `instance_memory_grants`, `acmi_memory_deposits`, `acmi_agent_pool_records` : la planche décrit leur STRUCTURE et leurs contraintes, pas leur peuplement (et la mémoire du dépôt porte « 429 des 448 tables sont vides », non revérifié ici) ; (3) le compte des couches de mémoire, faute de règle de comptage écrite — onze vérifiées et nommées, pas treize. Les chiffres de CLAUDE.md sur l'axe des données (34 libellés / 16 canaux / 8+1 branches / 12 épreuves / 27 cellules) ont tous été RECOMPTÉS à HEAD et sont confirmés. Le périmètre des « zéro appelant » est nommé en marge ; hors de ce périmètre, aucun de mes zéros ne vaut.

ne pas dessiner

  • Les 7 planches cosmogrammes existantes (frontend/planches/*.svg) — QG, poumons, Metatron, grille 27, urbanisme, contenu, anatomie des lieux
  • Les planches techniques déjà faites : bilatéral, respiration, layers/scopes, portes-fenêtres, meshmap, emboîtement, étoile-semence
  • Les planches institutionnelles déjà faites : économie, armure, politique, institutions, entreprise
  • La grille 27 comme sujet principal — elle a sa planche ; ici elle n'apparaît que comme pièce calculée (cellules_grille_27), parce que la question posée est la CLASSIFICATION et la COMPARTIMENTATION, pas la topologie
  • Les 2 axes comme simple rappel doctrinal — ils sont dessinés ici avec leurs compteurs recomptés, sinon la planche répète CLAUDE.md au lieu de le vérifier
  • Un 12ᵉ inventaire de la dormance : les « zéro appelant » de cette planche sont SITUÉS et leur périmètre est nommé, ils ne rouvrent pas le dossier dormance
Les traverses 16 relations, de source à source
Axe 1 — CONTENU (sphères, 3 faces)
orthogonalité — deux axes qui ne se substituent jamais ; un événement porte une sphère ET un canal
→Axe 2 — CANAL (énum fermée de 16)CLAUDE.md §Classification Événements (décision 34 : la taxonomie EST le canon) ; event_bus.py:91-95 (sphere_primary + canal dans la MÊME enveloppe Event)
Axe 2 — CANAL
un canal hors énum est forcé à 'bus' ET marqué red — le seul axe du système qui refuse en le disant
→le refus honnête (status=red)event_bus.py:289-294 (VALID_CANALS) + :79 (status red = classification échouée)
womb_events
la rive durable devrait alimenter la profondeur — un point = un couple distinct (event_type, jour)
→Axe nº3 de la membrane (womb_depth)membrane_service.py:493-526 existe ; 0 appelant hors du module ; et 0 des 7 appelants d'authorize_axes ne passe womb_depth
supervisor_p0p2_admission_gate
le seul chemin de production vers la porte — il lui passe memory_access=()
→la boucle des 5 droits mémorielssupervisor_p0p2_admission_gate.py:439-447 (:446 = memory_access=()) → p0p2_admission_gate.py:507-517
instance_memory_grants (5 droits en base)
la table des droits par compartiment devrait gouverner les lectures
→toute lecture mémorielle applicativeInstanceMemoryGrantRecord (models/instance_passport.py:186-202) n'est lu que via passport.memory_grants dans p0p2_admission_gate.py:506 — la seule boucle qui s'en sert tourne à vide
CanonAwareMemoryAgent.recall()
la lecture devrait franchir l'isolement mémoriel
→self._compartments / can_access_compartmentcanon_aware_memory_agent.py:402-404 lit local_memory et ne touche jamais _compartments (:130) ; can_access_compartment n'est appelé que par les 2 routes consultatives (acmi_router.py:694, canon_compartment_router.py:103)
acmi_memory_repository.deposit()
le mur de l'ÉCRITURE : portée contrainte en base, immutabilité par trigger, montée de portée refusée sans reçu
→acmi_memory_deposits (CHECK + trigger + rang de portée)acmi_memory_repository.py:196-416 ; _SCOPE_RANK :31 ; refus :307-310 ; CHECK models/acmi_memory_deposit.py:89-92
acmi_memory_deposits
le côté LECTURE du même mur
→une lecture par compartimentACMIMemoryRepository (acmi_memory_repository.py:193) n'a qu'une méthode publique, `deposit` (:196) — aucune fonction de lecture n'existe
_fil_a_moi_ou_404
séparateur par propriété, fail-close, 404 indiscernable de l'inexistence
→les 13 routes /threads/ de memory.pymemory.py:633-671, appelé 13 fois (:690, 709, 722, 743, 767, 805, 824, 868, 896, 932, 974, 1008, 1052) pour 13 routes de fil
memory_layer_events
la table devrait porter qui possède l'événement
→un propriétaire en basev82_086_memory_contract.py:26-38 et :55-62 — aucune colonne de propriétaire ; created_by est rempli avec thread_id (:78). La propriété est reconstituée à la main en Python par une requête sur threads.owner_id (memory.py:658-661)
memory_layer_events.layer
la discipline des 3 couches devrait être tenue par la base
→une contrainte CHECKgrep -i check sur v82_086_memory_contract.py → 0 occurrence ; seul DEFAULT 'hot' (:31). La règle vit en Python (memory.py:191)
canon_compartment_router / can-access
la portée de l'observateur devrait se déduire du jeton
→l'identité du demandeurcanon_compartment_router.py:58 (observer_scope dans CanAccessReq, donc dans le CORPS) et :92 (Depends(get_current_user) authentifie mais n'alimente jamais le verdict)
frontend memory-hot/warm/cold
la consolidation navigateur devrait remonter
→une persistance côté serveurmemory-manager.js:372-394 consolide dans le navigateur ; governance-init.js:309 étiquette encore cold comme « IndexedDB + Supabase » alors que la base de production est un PostgreSQL auto-hébergé (CLAUDE.md §DB)
myelination-master.js
la troisième lecture devrait déposer ce qu'elle mesure
→tout organe backendgit grep -il myelin sur backend/ + hf-atom-tools/ → 0 fichier ; 8 hits, tous sous frontend/
arbre_canon (8 branches + tronc)
3 portées × (8+1) = 27, calculé à la demande, jamais stocké
→la grille des 27 cellulesarbre_canon.py:155-157 ; servi par fractale_communautaire.py (enregistré main.py:2007) avec fail-close 503 canon_absent (:108-121)
spheres_libelles.sphere_de()
la résolution par le sens devrait remplacer les trois listes divergentes
→les classeurs du backend et du frontendles trois listes nommées dans la docstring de spheres_libelles.py:21-30 (routers/spheres.py CANONICAL_SPHERES · atom-mapping-data.js ATOM_MAPPING.spheres · atom-manifest-format.js SPHERE_RULES) existent toujours ; aucun appelant de sphere_de mesuré hors hf-atom-tools
La nomenclature 29 pièces, chacune avec sa source
1Axe 1 — CONTENU : 9 sphères + S0 sève, et TROIS faces par sphère34 libellés — opérationnelle 16 · données 11 · sectorielle 7CLAUDE.md §Classification Événements ; hf-atom-tools/atom_canon/primitives/spheres_libelles.py:51-55 (enum Face) et :66-141 (dict LIBELLES) · confirmé dans le codeRecomptés à la main à HEAD sur le dict LIBELLES : S0=5, S1=3, S2=3, S3=5, S4=3, S5=2, S6=4, S7=2, S8=3, S9=4 → 34 ; par face 16+11+7=34. Les trois chiffres de CLAUDE.md sont JUSTES.
2Résolution par le SENS, jamais par le rang — sphere_de()1 index, normalisation NFKDspheres_libelles.py:159-170 (_INDEX construit sur _normaliser) et :172-183 (sphere_de) · confirmé dans le codeAppelé par : le module lui-même (meme_sphere, libelle_de, est_une_branche). Aucun appelant backend mesuré hors hf-atom-tools — la résolution existe, le backend classe encore par ses propres listes (routers/spheres.py, atom-mapping-data.js).
3Le piège des deux réponses : sphere_de('TRONC')=S0 vs arbre_canon.TRONC=S92 modules du même paquet, 2 réponsesspheres_libelles.py:71 (Libelle("TRONC", Face.DONNEES) sous S0) vs hf-atom-tools/atom_canon/primitives/arbre_canon.py:73 (TRONC: str = "S9") · confirmé dans le codeAucun test ne croise les deux modules. La contradiction est réelle et non gardée — c'est une homonymie de FACE, pas une erreur, mais rien ne l'empêche d'être lue comme une erreur.
4Axe 2 — CANAL : énum FERMÉE de 16, et le seul axe qui refuse honnêtement16 canaux ; défaut canal='bus' (event_bus.py:94), status='green' (:95)backend/app/services/event_bus.py:289-294 (VALID_CANALS frozenset) · confirmé dans le codeRecomptés à HEAD : bus·http·ws·sse·email·sms·voice (7) + chat·stream·push·blockchain (4) + xr·voxel·hologram (3) + dom·none (2) = 16. CONFIRMÉ. Un canal hors énum est forcé à 'bus' ET marqué status=red.
5Arbre canon — 8 branches + 1 tronc, source unique tenue par des épreuves12 testsarbre_canon.py:70 (BRANCHES = S1..S8), :73 (TRONC = S9) ; épreuves : hf-atom-tools/atom_canon/tests/test_arbre_canon.py · confirmé dans le codeLe chiffre 12 de CLAUDE.md est CONFIRMÉ (12 `def test_` comptés à HEAD). Le chemin annoncé dans la docstring du module (`tests/test_arbre_canon.py`) est relatif au paquet : le fichier réel est `hf-atom-tools/atom_canon/tests/test_arbre_canon.py`.
6Womb — l'écriture de la gestation (womb_events) : FAIL-CLOSE réel1 seule porte d'écriture, 0 repli mémoirebackend/app/routers/womb.py:104-108 (db is None → 503) et :115-134 (INSERT + commit, rollback + 503 nommé) ; routeur enregistré main.py:2531 ; table : backend/alembic/versions/v82_054_womb_events_contract.py · confirmé dans le codeLe repli en mémoire a été RETIRÉ et le commentaire dit pourquoi : il rendait `status: ok` pour un événement qui disparaissait au redémarrage. Conforme à la loi « ce qui ENGAGE est en base, ou l'opération échoue ».
7Le séparateur du womb — WHERE user_id = sujet du JWT1 clause WHERE, liée au jeton, sur la seule lecture du routeurwomb.py:188-190 (SELECT ... FROM womb_events WHERE user_id = :uid), avec caller_id = str(user_id) depuis Depends(get_current_user_id) (:179-181) · confirmé dans le codeAucun. Le womb n'a qu'UNE lecture (/status) et elle est liée. C'est le séparateur le plus propre de la planche.
8Le séparateur du fil — _fil_a_moi_ou_404, fail-close, 404 muet13 appels pour 13 routes /threads/ (la 14ᵉ route, /health, n'est pas liée à un fil)backend/app/routers/memory.py:633-671 (def), appelé à :690, 709, 722, 743, 767, 805, 824, 868, 896, 932, 974, 1008, 1052 · confirmé dans le codeAucun trou mesuré. 13/13 couvertes, dépendance get_current_user_id présente sur chacune, et db=None refuse (:650-657) : « une lecture qu'on ne peut pas gouverner ne se laisse pas passer en attendant ». Le commentaire :600-612 garde la trace du cas inverse mesuré en prod le 2026-08-06 — porte gardée, six fenêtres nues.
9Membrane lisible S9 — authorize_axes : 5 axes, 3 verdicts5 axes · 3 verdicts (permeable/gestating/sealed) · 3 présentations (ouvert/visible_eteint/absent)backend/app/services/membrane_service.py:223-240 (signature), :9-20 (les 5 axes nommés), :22-25 et :173-182 (les 3 verdicts + leurs 3 présentations) · confirmé dans le code⚑ CORRECTION DE LA THÈSE : elle n'a PAS « aucun appelant applicatif ». Sept appelants à HEAD, hors tests : routers/coquille_router.py:119 · services/bureau_service.py:146 · services/community_trunk_service.py:295 · services/knowledge_gate/porte.py:206 · services/mcp_carte_tools.py:71 · services/mcp_live_organs.py:41 · services/mcp_repo_tools.py:65. Et coquille_router est enregistré à main.py:2342. La membrane est ATTEINTE.
10Les 5 DROITS mémoriels par compartiment — l'énum fermée et sa table5 droits · 1 ligne par (passeport, compartiment) · 5 colonnes booléennes NOT NULL DEFAULT falsebackend/app/core/instance_passport.py:51 (MEMORY_RIGHTS = read, write, propose_promotion, derive, purge) ; table : backend/app/models/instance_passport.py:186-202 (InstanceMemoryGrantRecord : memory_compartment PK + 5 booléens can_*) ; migration v82_268_instance_passport_registry.py · confirmé dans le codeRien ne manque à la STRUCTURE : défaut fermé, exhaustivité assertée (p0p2_admission_gate.py:97 — `assert set(_RIGHT_FLAGS) == set(MEMORY_RIGHTS)`). C'est la meilleure séparation du dépôt.
11acmi_memory_deposits — le CHECK en base, l'append-only, et la montée de portée1 CHECK à 3 valeurs · 1 trigger d'immutabilité · 1 refus nommé de montée de portéebackend/app/models/acmi_memory_deposit.py:29 (table), :53 (memory_compartment), :54 (scope) et :89-92 (CheckConstraint scope IN ('indiv','commu','civ')) ; garde de montée : backend/app/services/acmi_memory_repository.py:31 (_SCOPE_RANK) et :292-310 (acmi_scope_rise_receipt_required 409) ; immutabilité par trigger v82_279 · confirmé dans le codeRien côté écriture : c'est le mur, et il est réel. Il refuse qu'un même contenu (content_sha256) déjà déposé en `indiv` ressorte en `commu` ou `civ` sans reçu.
12Les trois paliers du navigateur — le seul organe qui consolide vraiment3 paliers + 1 transitionneur (memory-womb.js) · 1285 lignes · 1168 lignes pour womb-core.jsfrontend/js/memory-manager.js:8-11 (HOT RAM · WARM localStorage · COLD IndexedDB) ; instanciation :293-296 ; routage auto :336-361 ; remontée hot←warm←cold :372-394 ; chargés par frontend/js/governance-init.js:307-311 (+ memory-manager.js:312, womb-core.js:266 et :1110) · confirmé dans le codeRien au mécanisme : la cascade de promotion existe et tourne. Mais LE MÉDIUM EST LE SÉPARATEUR — c'est le navigateur de la personne qui sépare, pas une règle du système. Rien de ce qui est séparé là ne survit à un autre appareil.
13`civ` comme valeur par DÉFAUT de qui ne dit rien≥5 sites où l'absence de portée devient `civ` · et `civ` = « la membrane la plus large » (membrane_service.py:10-11)backend/app/services/babel/fractal_pipeline.py:93-94 (DEFAULT_TREE_SCOPE = "civ", avec le commentaire « Défaut = civ pour rétro-compatibilité V1 ») et :131 ; backend/app/services/babel/fractal_dataseed.py:23 ; backend/app/services/babel/babel_macro_decoder.py:81 ; babel_fractal_treasury_consumer.py:266 et :531 · confirmé dans le code⚑ CONFIRMÉ, mais il faut NOMMER OÙ : ce défaut est celui de la mémoire fractale BABEL, pas de la membrane ni du passeport. Le défaut y est même écrit avec sa raison (rétro-compatibilité V1), ce qui est honnête — et reste le choix de la membrane la plus large pour qui n'a rien déclaré.
14Le contre-exemple qui sauve l'honneur — `unknown` plutôt que `civ`4 valeurs ici, 3 dans acmi_memory_deposits (:89-92)backend/app/models/acmi_agent_pool_contract.py:22 (default_scope server_default 'unknown') et :32-35 (CHECK IN ('unknown','indiv','commu','civ')) ; migration v82_280_acmi_unknown_scope.py ; côté agent : canon_aware_memory_agent.py:77 (class _UnknownScope) · confirmé dans le codeRien — c'est le bon geste : qui n'a pas dit sa portée est marqué `unknown`, jamais promu à la plus large. ⚑ Mais la DIVERGENCE est réelle et non gardée : le pool admet `unknown`, le dépôt ne l'admet pas. Un agent `unknown` ne peut rien déposer, et aucun refus nommé ne le dit — le CHECK de la base le refusera sans phrase.
15Grille 27 — 3 portées × (8 branches + 1 tronc), CALCULÉE, pas stockée27 cellules, en RAM à chaque appelarbre_canon.py:155-157 (cellules_grille_27 → 3 * (len(BRANCHES) + 1)) ; servie par backend/app/routers/fractale_communautaire.py, enregistré main.py:2007 · confirmé dans le codeLes 27 cellules ne sont nulle part une TABLE : c'est une fonction qui rend un entier. Le routeur fail-close honnêtement (503 'canon_absent', fractale_communautaire.py:108-121) si l'arbre canon n'est pas importable — « la grille 27 ne se sert jamais depuis une copie ». Ce qui manque : aucune persistance d'une cellule, donc aucune mémoire ne porte d'adresse de cellule durable.
16La PHASE de gestation — annoncée dans le champ, jamais persistée1 champ calculé sur « les 10 derniers événements existent-ils »womb.py:199-201 ("phase": "active" if recent else "dormant") vs :119-120 (l'INSERT ne nomme que user_id, event_type, data, recorded_at) ; le `phase` du modèle entre dans le JSON `data` via _envelope (:75-84) · confirmé dans le codeLa colonne. `phase` n'est pas un état du womb : c'est une déduction de volume récent. Un champ « actif » annoncé n'est pas un chemin implémenté.
17aderive_womb_depth — la profondeur dérivée de la RIVE DURABLE1 point de profondeur = 1 couple DISTINCT (type, jour)backend/app/services/membrane_service.py:493-526 (COUNT(DISTINCT (event_type, DATE(recorded_at))) FROM womb_events WHERE user_id = :uid) · confirmé dans le codeL'APPELANT. Périmètre fouillé : backend/app/ + backend/ hors app/ + agents/ + frontend/ + scripts/, hors tests. Hors de membrane_service.py lui-même (:571, dans `aauthorize`), ZÉRO appelant. Et `aauthorize` a lui aussi zéro appelant hors du module. La règle est bonne, nommée, honnête sur l'absence de base — et personne ne la pose jamais.
18Mémoire de fil tri-couche hot/warm/cold (memory_layer_events)3 couches ; colonne layer VARCHAR(16) NOT NULL DEFAULT 'hot'backend/app/routers/memory.py:48-50 (enum MemoryLayer) et :191 (_get_layer_for_event) ; table : backend/alembic/versions/v82_086_memory_contract.py:26-38 ; routeur enregistré main.py:1694 · confirmé dans le codeLE CHECK. `git show HEAD:backend/alembic/versions/v82_086_memory_contract.py | grep -i check` → **0 occurrence**. Il n'y a AUCUNE contrainte CHECK sur `layer` : la discipline des trois couches vit uniquement en Python. Et la table n'a aucune colonne de PROPRIÉTAIRE — `created_by` est rempli avec `thread_id` (:78 de la migration), pas avec un usager.
19Le repli local des événements de fil — PersistentDict, non déclaré dans la réponse2 magasins locaux ; 0 champ de source dans la réponsememory.py:122-123 (_memory_events / _snapshots = PersistentDict) et :580-594 (_events_for_thread : si _load_events_db rend None, on sert _thread_events) · confirmé dans le codeLA DÉCLARATION. Le garde de propriété fail-close, donc le repli n'est atteint qu'avec une base VIVANTE qui ne rend rien (table absente, requête None) — et là, la réponse sert des événements d'un magasin local SANS dire qu'elle est dégradée. Le womb, lui, rend `source` et `degraded` (womb.py:200) ; la mémoire de fil non.
20La boucle qui pèse ces 5 droits — p0p2, un appelant, un tuple VIDE1 appelant de production · memory_access=() · 0 itérationbackend/app/services/p0p2_admission_gate.py:504-517 (for compartment, right in request.memory_access → droit_memoriel_inconnu 422 / memory_scope_denied 403) ; le SEUL appelant : backend/app/services/supervisor_p0p2_admission_gate.py:439-447, avec memory_access=() à la ligne 446 · confirmé dans le code⚑ CORRECTION DE LA THÈSE : la porte n'est pas SANS appelant — elle est appelée avec RIEN à peser. `git grep AdmissionRequest( ` hors tests : 1 seul site, et il passe un tuple vide. La loi « UN droit mémoriel refusé refuse TOUT l'acte » (:26-27, :505-506) est implémentée, testée, et tourne à vide à chaque admission réelle.
21Pool d'agents ACMI — DEUX registres en RAM, pas une seule ligne2 dicts distincts par agent + 1 dict de pool au niveau modulehf-atom-tools/atom_canon/acmi/canon_aware_memory_agent.py:121 (self.local_memory = {}) et :130 (self._compartments: dict[str, Compartment] = {}) ; pool du routeur : backend/app/routers/acmi_router.py:83 (_acmi_pool: dict[str, CanonAwareMemoryAgent]) · confirmé dans le code⚑ CORRECTION DE LA THÈSE : ce ne sont PAS « les mémoires et les compartiments dans la MÊME ligne ». Ce sont deux dictionnaires SÉPARÉS — et c'est pire que l'absence de jointure : `recall(fractal_address)` (:402-404) lit `self.local_memory.get(...)` et **ne consulte jamais `self._compartments`**. L'isolement mémoriel et la lecture mémorielle ne se croisent nulle part. Le seul JSONB qui mélange les deux est une PHOTO durable : backend/app/models/acmi_agent_pool_contract.py:26 (compartments JSONB) à côté de :25 (local_memory JSONB).
22can_access_compartment — 4 gates, 5 refus nommés, verdict CONSULTATIF4 gates → 5 motifs de refus ; 2 routes l'exposent ; 0 lecture le consultehf-atom-tools/atom_canon/memory/compartment.py:100-141 (scope_mismatch_*_no_bridge · canon_level_blocked_* · dimensions_empty_intersection · compartment_sealed · compartment_quarantined) ; exposé par backend/app/routers/canon_compartment_router.py:92-114, enregistré main.py:2399, et par acmi_router.py:694 · confirmé dans le codeTROIS maillons, chacun nommé. (1) Le verdict n'ARRÊTE rien : les deux routes le RENDENT dans un corps JSON, aucune lecture mémorielle ne l'interroge. (2) Le demandeur déclare sa propre portée — `CanAccessReq.observer_scope` (canon_compartment_router.py:58) vient du CORPS de la requête, pas du jeton. (3) Le gate des dimensions ne se déclenche que `if observer_dimensions:` (compartment.py:130) : qui n'en déclare aucune le saute entièrement.
23Le pont qui désarme la portée pour TOUT LE MONDE1 pont ouvert → le gate de portée ne refuse plus personnehf-atom-tools/atom_canon/memory/compartment.py:117-121 — `if observer_scope != compartment.scope and not compartment.bridged_compartments:` · confirmé dans le codeLa liaison au PARTENAIRE. La condition teste si la liste de ponts est non vide, jamais si l'observateur est au bout de l'un d'eux. Un pont entre A et B annule le contrôle de portée de A pour un observateur de n'importe quelle portée.
24Les compartiments eux-mêmes — un dict de module, perdu au redémarrage0 table pour les compartiments canon ; 0 migrationbackend/app/routers/canon_compartment_router.py (_COMPARTMENTS, lu à :80, :92-96, :101-102) ; même patron par agent à canon_aware_memory_agent.py:130, 584, 601 · confirmé dans le codeLA BASE. `create_compartment` écrit dans un dict de module. Le compartiment créé, mis en quarantaine ou scellé disparaît au prochain déploiement. La seule persistance de compartiment du dépôt est ailleurs et porte un autre nom : `instance_memory_grants` (les droits) et `acmi_memory_deposits.memory_compartment` (les dépôts).
25Ce qui garde quand même /recall — un jeton interne, pas un droit1 secret partagé pour toutes les routes ACMI · 0 vérification par usagerbackend/app/routers/acmi_router.py:72-81 — _require_acmi_authority → require_internal_token, posé en dependencies=[...] sur l'APIRouter entier (prefix /api/v2/acmi) · confirmé dans le codeLa GRADUATION. Le mur existe mais il est binaire et collectif : qui porte le jeton interne peut rappeler la mémoire de n'importe quel agent à n'importe quelle adresse fractale. Ce n'est pas « aucun mur » — c'est un mur sans portes nommées.
26Myéline — la troisième lecture : bâtie, chargée, et enfermée547 lignes · 0 fichier backend ne contient le motfrontend/js/myelination-master.js (547 lignes), chargé par governance-init.js:773 ; son propre en-tête, :22 : « Mecanique d'organisme uniquement: aucune persistance backend » · confirmé dans le codeLA SORTIE. `git grep -il myelin` sur backend/ + hf-atom-tools/ → 0 fichier ; les 8 hits du dépôt sont tous sous frontend/. La troisième lecture mesure quelles voies sont chaudes, émet `myelin.sheath.updated` et `myelin.fastpath.ranked` sur le bus — et aucune de ces mesures ne quitte l'onglet. ⚑ CORRECTION : « enfermée » est juste, « bâtie » aussi ; l'état honnête est PARTIEL, pas BATI, parce qu'un organe dont la mesure ne sort jamais n'a pas son maillon de sortie.
27Axe nº3 des cinq — WOMB_DEPTH : jamais alimenté en production0 des 7 appelants · 5 des 7 passent engagement_levelmembrane_service.py:231 (womb_depth: int = 0, défaut) et :352-358 (le seuil) ; les 7 sites d'appel vérifiés un par un : 0 ne passe womb_depth · confirmé dans le code⚑ C'EST LE TROU DÉCISIF DE LA PLANCHE. `womb_depth` reste au défaut 0 dans chaque chemin de production, donc `womb_depth < required_womb_depth` ne peut jamais refuser (sauf si un appelant posait un required, ce que personne ne fait non plus). Et la seule fonction qui le remplirait depuis `womb_events` — `aderive_womb_depth`/`aauthorize` — a zéro appelant. La membrane n'a pas 5 axes vivants : elle en a 4, dont l'identité et la localisation font presque tout le travail.
28Le même dépôt côté LECTURE — il n'y a pas de fonction1 méthode publique, et c'est une écriturebackend/app/services/acmi_memory_repository.py — la classe ACMIMemoryRepository (:193) n'expose qu'UNE méthode publique : `async def deposit` (:196). Aucune méthode de lecture, de liste ou de filtre par compartiment. · confirmé dans le code⚑ LE CŒUR DE LA THÈSE, CONFIRMÉ ET PRÉCISÉ. Le mur est bâti du côté de l'écriture parce que de l'autre côté il n'y a pas de porte du tout. La lecture ACMI passe par ailleurs : `/recall` (acmi_router.py:538-544) appelle `acmi.recall(req.fractal_address)` sur un agent pris dans `_acmi_pool` — sans propriétaire, sans compartiment, sans portée.
29Le compte des « treize couches de mémoire »11 vérifiées, pas 13aucune énumération canon trouvée à HEAD ; j'en ai VÉRIFIÉ onze, nommées, chacune avec sa source dans cette planche · non confirmé dans le codeLA RÈGLE DE COMPTAGE. Les onze que je peux nommer et situer : womb_events · memory_layer_events · memory_layer_snapshots · PersistentDict(memory_events) · PersistentDict(memory_snapshots) · instance_memory_grants · acmi_memory_deposits · acmi_agent_pool_records · _acmi_pool (RAM) · _COMPARTMENTS (RAM) · les 3 paliers navigateur (comptés 1, ou 3 si on les sépare, ce qui donne 13 — et c'est probablement d'où vient le 13). Tant que la règle n'est pas écrite, le chiffre est une rumeur : un compte sans sa règle de normalisation ne compte pas.
Le lexique de cette planche 28 termes à deux registres · 11 avec un jumeau

On ne remplace pas le nom canon, on le double : le terme reste l’autorité, la glose est une poignée. Le lexique entier est sur sa propre page.

portée (axe POSITION — ind / com / civ)Jusqu'où une chose compte — pour moi seul, pour un groupe, ou pour tous — et c'est l'endroit où l'on agit qui le décide, pas une case à cocher.trinity_scope (la portée-LENTILLE) — La mesure de combien une même chose pèse à la fois pour une personne, pour son groupe et pour tous — trois nombres ensemble, pas un choix…
scope / tree_scopeLe champ qui enregistre la portée d'une chose, et qui lui donne un contenant différent dans chacun des trois classements empilés.
layerUn étage du système qui existe en même temps que les autres et qui a son propre réseau de liens.RenderLayer — La liste fermée des sept formes sous lesquelles une vue peut sortir, du simple texte à la carte ou au son.
Face (d'une portée)Le nom sous lequel une même portée se présente selon qui la regarde : la base, l'écran, l'agent, ou le livre du lecteur.
membrane (membrane_service, S9 lisible)L'organe qui décide si une lecture passe, pas encore, ou jamais — en croisant où l'on lit, qui lit, son cheminement, son rang et son rôle.membranes A · B · C (conduit_membranes) — Les trois seuls passages déclarés du système : ce qui entre, ce qui remonte vers le plus large, et ce qui sort vers un lecteur.
agent (type de nœud)Le genre de place qu'occupe un agent qui agit au nom de quelqu'un, avec l'endroit auquel il est rattaché.
sphère (S0 à S9)Elle dit de quoi un message parle : chaque message reçoit un numéro de 0 à 9 qui nomme son sujet.
sève (Position.SEVE)C'est la position qui dit : cette sphère n'occupe aucun endroit de la structure, elle passe par tous.
TRONC (arbre_canon.TRONC)C'est la position centrale de la structure, celle où ce que les huit autres produisent est compilé et gardé ; S9 l'occupe.TRONC (libellé, face données de S0) — C'est le nom sous lequel S0 apparaît quand on la regarde du côté de ce qui est enregistré.face (enum Face) — Elle dit sous quel angle on nomme une sphère : le même objet reçoit un nom différent selon l'angle choisi.
TRONC (libellé, face données de S0)C'est le nom sous lequel S0 apparaît quand on la regarde du côté de ce qui est enregistré.TRONC (arbre_canon.TRONC) — C'est la position centrale de la structure, celle où ce que les huit autres produisent est compilé et gardé ; S9 l'occupe.
branche (BRANCHES)C'est la position des huit sujets qui produisent du contenu : S1 à S8, pas une de plus ni une de moins.
face (enum Face)Elle dit sous quel angle on nomme une sphère : le même objet reçoit un nom différent selon l'angle choisi.
canalC'est par quel moyen un message voyage : le réseau interne, le web, le courriel, le SMS, l'écran, et onze autres.
status (green / yellow / red)C'est la note de confiance mise sur l'étiquetage d'un message : vert, le classement est bon ; rouge, il a échoué.
bus (valeur de canal)C'est la valeur qui dit : ce message ne sort pas de la machine, il passe d'un module à l'autre dans le même programme.bus (l'organe de distribution) — C'est la pièce de code qui reçoit un message et le remet à tous ceux qui s'y sont abonnés.
bus (l'organe de distribution)C'est la pièce de code qui reçoit un message et le remet à tous ceux qui s'y sont abonnés.canal — C'est par quel moyen un message voyage : le réseau interne, le web, le courriel, le SMS, l'écran, et onze autres.bus (valeur de canal) — C'est la valeur qui dit : ce message ne sort pas de la machine, il passe d'un module à l'autre dans le même programme.
portée (VALID_PORTEES : indiv | commu | civ)C'est la taille de l'espace qu'un message concerne : une personne, un groupe, ou tout le monde.
mériteLe fait de comprendre vraiment par quoi une chose arrive — ce qui ne s'imite pas, parce qu'imiter la compréhension c'est ne pas l'avoir.
fail-closeQuand l'outil ne peut pas faire le travail pour de vrai, il refuse et dit pourquoi, au lieu de repondre « c'est fait ».
porteUn passage qu'on traverse vraiment : il faut une permission pour le franchir, et il laisse une trace de ce qui est passe.porte (Porte de la Connaissance, knowledge_gate/porte.py) — Elle decide combien d'un document on montre a quelqu'un — de rien du tout jusqu'au texte complet — en six crans, et par defaut elle ne…
porte (Porte de la Connaissance, knowledge_gate/porte.py)Elle decide combien d'un document on montre a quelqu'un — de rien du tout jusqu'au texte complet — en six crans, et par defaut elle ne montre rien.porte — Un passage qu'on traverse vraiment : il faut une permission pour le franchir, et il laisse une trace de ce qui est passe.fenetre — Une vue sur ce qui se passe ailleurs : on voit, on ne prend rien, et rien ne change de main.
gardeUn controle pose sur le chemin d'une action, qui la laisse passer ou la refuse.
membrane (membrane lisible, S9)registre spirituelElle decide, pour chaque lecture, si la personne peut voir la chose maintenant, pas encore, ou jamais — et elle ne rend pas un simple oui/non : elle dit lequel des trois.MONDES — La liste fermée des sept espaces où une chose peut avoir une adresse, et deux façons d'écrire le même nom y comptent pour deux espaces…membrane (routeur /api/v1/membrane — passages entre les 3 mondes) — Elle enregistre les transferts d'un contenu d'un espace vers un autre — du prive au collectif, du collectif au public — et permet de les…
membrane (routeur /api/v1/membrane — passages entre les 3 mondes)Elle enregistre les transferts d'un contenu d'un espace vers un autre — du prive au collectif, du collectif au public — et permet de les annuler.membrane (membrane lisible, S9) — Elle decide, pour chaque lecture, si la personne peut voir la chose maintenant, pas encore, ou jamais — et elle ne rend pas un simple…
engagement (niveaux d'engagement citoyen)Quatre paliers de responsabilite choisis librement : plus on en prend, plus on peut poser de gestes qui touchent le collectif.
ce qui ENGAGE (regle fail-close)La liste courte des choses qui creent un droit ou une obligation — argent, vote, appartenance, accord, marque d'authenticite, transmission — et qui n'ont pas le droit de reussir sans etre ecrites pour de bon.
Babel (Décodeur de Babel)L'outil qui mesure comment les mots ont été retournés, et qui refuse de répondre plutôt que de deviner quand sa base manque.
ACTIF / PONT / PARTIEL_ENERGIE / LATENT / VIDECinq étiquettes qui disent, pour un site, ce qui est présent de chaque côté et à quel point les deux s'accordent.

Matière : PLANCHES-MATIERE-3-DONNEES-2026-10-03.json (sha256 634d4e52ceb9) — planche de matière « L'AXE DES DONNÉES — classer, et séparer. Ce qui trie, ce qui sépare, et ce qui ne sépare rien. ». Générée par scripts/generer-planches-ancrage.py ; la version « avant » (avant le passage du sceptique) que la matière conserve n’est pas dessinée — c’est la version dégradée au doute qui l’est, et les marges portent les dégradations.