LA CLASSIFICATION : deux axes orthogonaux, trois faces par sphère, et le troisième champ qui ne refuse pas
ouvrir la planche en plein écran → · 25 pièces · 14 traverses · 16 marges
Les deux axes sont indépendants et l'Event les porte côte à côte — mais le canon de l'axe 1 n'est tenu qu'en Python : son miroir JS, chargé en production, compte 1·2·3·4·5·7·8·9 et ignore S6 ; et la classification des mémoires nomme des compartiments que rien ne persiste, pendant que le seul compartiment qui EST une colonne en base n'a aucune porte.
La planche ne doit PAS dessiner l'axe 1 comme un canon à deux moitiés accordées : écrire explicitement que la moitié JS, celle qui est chargée dans le navigateur en production, compte 1·2·3·4·5·7·8·9 et ignore S6, et qu'aucune garde ne la tient. Elle ne doit PAS dessiner une mémoire compartimentée : deux registres en mémoire de processus qui ne se voient pas, un « consolidé: true » qui n'écrit rien, un backend_hint qui ne branche rien, et la seule colonne memory_compartment réellement en base derrière une porte qui lève 503 inconditionnellement. Elle ne doit PAS dessiner quatre portes d'accès égales : deux sont conditionnelles, la porte SCOPE se désarme sur un pont quelconque, et la quarantaine se lève par pontage. Elle ne doit PAS dire « polysémie assumée » : rien ne réconcilie 'eta_stricte' et {S1..S4} — c'est une homonymie non réconciliée. Elle ne doit PAS dessiner l'ACL du bus comme une compartimentation des topics : 7 topics exacts mordent, les 22 préfixes attendent une variable absente du déploiement. Et elle doit porter, en clair, que la prod n'a pas été mesurée aujourd'hui et que la dernière révision nommée est v82_260 sur atom-db/atom_db au 2026-08-08.
ne pas dessiner
| arbre_canon.py (BRANCHES = S1..S8) contradiction frontale non gardée — le miroir déclaré dit l'inverse de la source | → | frontend/js/arbre-spheres.js (BRANCHES = 1,2,3,4,5,7,8,9)arbre_canon.py:90 vs arbre-spheres.js:298,324,352,376,400,424,448,473 ; aucune garde en CI (audit_s9_s6_packaging_axis.py absent de ci.yml) | |
| arbre_canon.Empaqueteur renvoi nominatif — le canon Python nomme le module JS comme implémentation | → | arbre-spheres.js S6_PACKAGING_LAYERarbre_canon.py (champ implementation) → arbre-spheres.js:732 | |
| spheres_libelles.sphere_de homonymie inter-modules du même paquet : 'TRONC' rend S0 (face DONNÉES) là où la constante vaut S9 | → | arbre_canon.TRONCspheres_libelles.py LIBELLES['S0'] vs arbre_canon.py:92 | |
| Event.sphere_primary côte à côte, jamais l'un dans l'autre — deux résolveurs sans lecture croisée | → | Event.canalevent_bus.py:91-95 ; _resolve_sphere_primary :324+ ; _resolve_canal :378-387 | |
| _with_canon_payload_defaults tampon de traitement posé par setdefault — contournable, et sauté si le payload n'est pas un dict | → | payload métier (clé data)event_bus.py:308-324, posé :472 | |
| event_bus discipline_flag='eta_stricte' deux domaines de valeurs pour une même clé, sans pont ; coercition silencieuse à S4 si les chemins se rencontrent | → | measure_tier3.VALID_DISCIPLINE_FLAGS={S1..S4}event_bus.py:301 vs measure_tier3.py:55 et :137-142 | |
| bridge_compartments désarmement collatéral — un pont quelconque annule le test de portée pour tous | → | can_access_compartment porte 1 (SCOPE)compartment.py:170-176 → :117-122 | |
| bridge_compartments écrasement de statut — le pont lève la quarantaine sans la lire | → | quarantine_compartmentcompartment.py:175-176 vs :195 | |
| route_memory_to_tier → backend_hint indication, pas branchement — aucune écriture | → | les 7 backends nommés (hedera_dlt, filesystem_markdown, bayon…)hierarchy.py:54-62, :100 | |
| POST /api/v2/acmi/remember porte fermée en amont — refus 503 inconditionnel avant l'autorité | → | ACMIMemoryAuthority.authorize (colonne memory_compartment)acmi_router.py:517-536 ; authorize acmi_memory_authority.py:97 | |
| get_acmi_operation_authority / get_acmi_deposit_repository fournisseurs de dépendance définis, cités dans aucun Depends() | → | les endpoints ACMIacmi_router.py:90, :98 — zéro occurrence en Depends dans le fichier | |
| check_publish garde réellement appelée, import au point de décision, fail-close sur blocage | → | event_bus.publishevent_bus.py:510-528 | |
| PRIVILEGED_PREFIXES (22) blocage conditionné à une variable absente du déploiement | → | BUS_ACL_ENFORCEbus_acl.py:37-69, :134-136 ; grep deploy/ infrastructure/ = 0 | |
| canon_compartment_router._COMPARTMENTS deux registres de compartiments en mémoire de processus, qui ne se voient pas | → | CanonAwareMemoryAgent._compartments (acmi_router._acmi_pool)canon_compartment_router.py:41 vs acmi_router.py:83 et canon_aware_memory_agent.py:130 |
| 1 | AXE 1 — arbre canon (8 branches + tronc + sève)8 branches + 1 tronc + 1 sèvehf-atom-tools/atom_canon/primitives/arbre_canon.py:84-96 (BRANCHES=S1..S8, TRONC=S9, SEVE=S0, RACINES=(0,S9)) ; NOMS :98-109 · confirmé dans le code | |
| 2 | Les 12 épreuves qui tiennent l'arbre12hf-atom-tools/atom_canon/tests/test_arbre_canon.py — 12 `def test` (:32,:51,:56,:64,:71,:81,:88,:102,:112,:117,:124,:133) ; lancées par le job CI `canon-tests` .github/workflows/ci.yml:459-474 · confirmé dans le codele chemin annoncé dans arbre_canon.py:27 (`tests/test_arbre_canon.py`) n'existe pas ; le vrai est hf-atom-tools/atom_canon/tests/ | |
| 3 | L'empaqueteur n'est pas S6arbre_canon.py — dataclass Empaqueteur (est_une_sphere=False, est_une_branche=False, implementation='frontend/js/arbre-spheres.js — S6_PACKAGING_LAYER') ; tenu par test_arbre_canon.py:81 et :88 · confirmé dans le code | |
| 4 | AXE 2 — VALID_CANALS, énum fermée de 1616 (bus, http, ws, sse, email, sms, voice, chat, stream, push, blockchain, xr, voxel, hologram, dom, none)backend/app/services/event_bus.py:289-295 · confirmé dans le code | |
| 5 | Le refus honnête du canal — trois gestesevent_bus.py:467-470 — logger.error('Invalid canal …') puis canal='bus' puis status='red' · confirmé dans le codela garde vit UNIQUEMENT dans publish() ; le dataclass Event (event_bus.py:94 `canal: str = "bus"`) n'a aucune validation — un Event construit directement porte n'importe quel canal | |
| 6 | Le repli nommé de la sphèreevent_bus.py:445-461 — pipeline 5 étapes puis repli '0' ; si la sphère a été INFÉRÉE et vaut '0', status='red' sauf si master_hash_registry.get_descriptor(source).sphere_id == '0' · confirmé dans le code | |
| 7 | L'orthogonalité des deux axesevent_bus.py:91-95 — sphere_primary / sphere_affluent / sphere_ratio ET canal / status, champs frères du même dataclass ; deux résolveurs indépendants : _resolve_sphere_primary (:324+) et _resolve_canal (:378-387). Aucun des deux ne lit la sortie de l'autre · confirmé dans le code | |
| 8 | LES TROIS FACES — Face (3 membres)3hf-atom-tools/atom_canon/primitives/spheres_libelles.py — class Face : OPERATIONNELLE, DONNEES, SECTORIELLE · confirmé dans le code | |
| 9 | Les 34 libellés et leur répartition34 = 16 + 11 + 7spheres_libelles.py — LIBELLES ; comptés au commit : 34 `Libelle(` = 16 OPERATIONNELLE + 11 DONNEES + 7 SECTORIELLE · confirmé dans le code | |
| 10 | Le piège de la face — sphere_de('TRONC') rend S0spheres_libelles.py — LIBELLES['S0'] contient Libelle('TRONC', Face.DONNEES, 'MASTER-INDEX §0.6') ; arbre_canon.py:92 TRONC='S9'. Deux modules du même paquet, deux réponses · confirmé dans le code | |
| 11 | Les chaînes vivantes de spheres_libelles6 chaînes5 consommateurs backend + 1 lint : backend/app/data/agents_fallback.py:31,37 · backend/app/services/community_branches.py:77 (BRANCHES) · backend/app/services/fractal_position_resolver.py:708-712 · backend/app/services/routing_engine.py:39,247,312 · hf-atom-tools/atom_canon/mathematiques/coordonnee_articulatoire.py:125 (_normaliser) · scripts/lint_proposal_contract.py:27-31,114 · confirmé dans le codetoutes les importations sont locales et gardées (`_sphere_de is not None` dans agents_fallback.py:37 et routing_engine.py) : si le paquet manque, la chaîne se dégrade EN SILENCE au lieu de refuser | |
| 12 | Le clamp η, lui, est appliqué0.3backend/app/services/babel/babel_macro_decoder.py:621-622 et :672-673 — `if discipline_flag != "S1" and confidence > MAX_INTERPRETIVE_CONFIDENCE: confidence = MAX_INTERPRETIVE_CONFIDENCE` (0.3) ; même geste dans crop_circle_treasury_consumer.py:335-336 · confirmé dans le codela condition teste la clé η contre une valeur de STATUT ÉPISTÉMIQUE ('S1') ; avec la valeur canon 'eta_stricte' du bus, le test est toujours vrai et le clamp toujours actif. Conservateur — mais juste par accident, pas par dessein | |
| 13 | L'isolement par défaut du compartimenthf-atom-tools/atom_canon/memory/compartment.py:59-61 is_isolated() = default_isolation ET pas de bridge ; create_compartment force default_isolation=True · confirmé dans le codeprédicat juste — mais sur un objet qui ne vit qu'en mémoire de processus (voir « Les routeurs canon de la mémoire ») | |
| 14 | bridge_compartments — pas d'élévation automatiquecompartment.py:160-169 — actual_chan_level = min(c1_level, c2_level, chan_level), puis inversion en libellé · confirmé dans le codetient par coïncidence d'énum, pas par garde : :169 `inverted[actual_chan_level]` lèverait KeyError si un niveau tombait hors de la table de 4 (`levels.get(..., 0)` → 0 ∉ inverted). Inatteignable aujourd'hui seulement parce que canon_level.py:31-34 a exactement ces 4 membres | |
| 15 | Le tronc JS porte encore sphereId=6frontend/js/arbre-spheres.js:513-516 (LEGACY M14 : sphereId=6, name='Communication', frequency=639) vs :519-522 (canonical_sphereId=9, canonical_name='AT·OM Mapping (S9)', canonical_frequency=174) · confirmé dans le codeles deux jeux de champs coexistent ; rien n'empêche un lecteur de prendre les legacy — la dérive M14 annoncée dans CLAUDE.md est toujours au commit | |
| 16 | Le trou déclaré de S6 en face DONNÉES1 sphère sans face DONNÉESspheres_libelles.py — spheres_absentes_du_mapping() ; S6 ne porte que 1 libellé OPERATIONNELLE + 3 SECTORIELLE, aucun DONNEES → la fonction rend ('S6',) · confirmé dans le codezéro appelant. Périmètre fouillé : backend/ (les deux arbres) + hf-atom-tools/, hors tests → aucune importation de `spheres_absentes_du_mapping`. Le trou est déclaré à personne | |
| 17 | LE 3ᵉ CHAMP — le tampon de traitementevent_bus.py:300-306 CANON_PAYLOAD_DEFAULTS (discipline_flag='eta_stricte', statut_epistemique='S2', MAX_INTERPRETIVE_CONFIDENCE=0.30, exposition_externe=False) ; appliqué par _with_canon_payload_defaults :308-324 et posé sur le payload à :472 · confirmé dans le codedeux fuites : (1) :316 `if not isinstance(data, dict): return data` — un payload non-dict repart SANS tampon, sans status='red', sans log ; (2) le tampon est posé par `setdefault` (:321-322) donc un émetteur qui déclare un discipline_flag quelconque le garde. Le 3ᵉ champ ne refuse jamais | |
| 18 | MemoryTier — la classification des mémoires7 tiershf-atom-tools/atom_canon/memory/hierarchy.py:35-44 (7 membres) ; _TIER_BACKEND_HINT :54-62 ; route_memory_to_tier :74-103 ; exposé par backend/app/routers/canon_memory_hierarchy_router.py (monté main.py:2403) et acmi_router.py:/route-tier · confirmé dans le codele backend n'est jamais ouvert : :100 rend `backend_hint` — une CHAÎNE qui NOMME un backend ('hedera_dlt', 'filesystem_markdown', 'bayon_canon_sentinel'…). Pire, consolidate_memory :105-139 rend `"consolidated": True` (:133) sans écrire nulle part : un « consolidé » qui n'a rien consolidé | |
| 19 | Gate 1 — SCOPEcompartment.py:117-122 — `if observer_scope != compartment.scope and not compartment.bridged_compartments:` · confirmé dans le codeUN SEUL pont désarme la porte POUR TOUT LE MONDE. Dès que `bridged_compartments` est non vide — un pont vers n'importe quel partenaire — le test de portée ne se déclenche plus, pour AUCUN observateur, d'AUCUNE portée. La condition ne vérifie jamais que l'observateur est de l'autre côté de CE pont | |
| 20 | Gate 4 — STATUScompartment.py:136-139 — SEALED et QUARANTINED ajoutent bien un motif de refus · confirmé dans le codebridge_compartments :175-176 écrase `c1.status`/`c2.status` à BRIDGED SANS lire le statut courant : ponter un compartiment en quarantaine lève sa quarantaine en silence, et la porte 4 le laisse alors passer. La quarantaine (:195) n'est pas protégée contre le pont | |
| 21 | Les routeurs canon de la mémoirecanon_compartment_router monté main.py:2399 · canon_memory_hierarchy_router monté main.py:2403 (tous deux non-core : register_router main.py:1637-1663 journalise et continue sur ImportError, REGISTER_ROUTER_STRICT OFF par défaut). atom_canon est bien importable dans l'image : backend/Dockerfile:43-44 (COPY hf-atom-tools /opt/hf-atom-tools + pip install -e). Auth réelle : dependencies=[Depends(get_current_user)] sur les 4 endpoints compartiment (:74, :94, :118, :134) · confirmé dans le codele registre est un dict de module : canon_compartment_router.py:41 `_COMPARTMENTS: dict[str, Compartment] = {}` (son propre en-tête l'écrit : « Stockage in-process (P0) - persistance Hedera prevue P1+ »). La prod tourne 4 workers : un compartiment créé sur l'un est 404 sur les trois autres, et tout meurt au redémarrage. Et /health (:142) est OUVERT et annonce `p0_status: 'operational'` + `compartments_count` — une première page qui dit « opérationnel » d'un registre qui ne survit pas à un redémarrage. Un contrôle d'accès ENGAGE : il doit être en base ou refuser | |
| 22 | L'autre compartiment, celui qui EST une colonneColonne réelle : memory_compartment VARCHAR(128) — composant de PRIMARY KEY dans backend/alembic/versions/v82_268_instance_passport_registry.py:97,103 et dans v82_279_acmi_memory_deposit_authority.py:74 ; ORM backend/app/models/instance_passport.py:195 et models/acmi_memory_deposit.py:53. Autorité fail-close vraie : backend/app/services/acmi_memory_authority.py:111-113 (`if db is None: raise ... 503`) et refus nommé `acmi_memory_compartment_not_granted` (403) sur un octroi absent · confirmé dans le codeAUCUNE PORTE. `authorize` n'est appelée que depuis son propre module (:242 dans issue_remember_capability, :298 dans verify_remember_capability) et ces deux méthodes ont ZÉRO appelant dans tout backend/ hors leur fichier. L'unique porte HTTP, POST /api/v2/acmi/remember (acmi_router.py:517-536), lève `acmi_capability_consumption_not_atomic` 503 INCONDITIONNELLEMENT, avant de toucher l'autorité. Et les deux fournisseurs de dépendance écrits pour elle, get_acmi_operation_authority (:90) et get_acmi_deposit_repository (:98), n'apparaissent dans AUCUN Depends(...) | |
| 23 | L'ACL du bus — la compartimentation des topics7 topics fail-close (dont 4 à frozenset() vide) + 22 PRIVILEGED_PREFIXES en mode observeRéellement appelée : event_bus.py:510-512 (import AU point de décision, non avalé) ; fail-close sur blocage :513-528 (status='red', handlers_called=0, block_reason='bus_acl:privileged_topic_untrusted_source'). Table toujours fail-close : bus_acl.py:78-88 EXACT_TOPIC_SOURCES, 7 topics ; check_publish :165-224 · confirmé dans le codeles 22 préfixes privilégiés (bus_acl.py:37-69) ne mordent pas : _enforce_enabled() :134-136 lit BUS_ACL_ENFORCE, défaut OFF, et `grep -rn BUS_ACL_ENFORCE deploy/ infrastructure/` → 0. Elle sait dire non ; elle a le droit de ne pas le dire. À noter aussi : 4 des 7 topics exacts portent frozenset() VIDE (babel.temple.energy.generated, mission.rewarded, forge.product.distributed, forge.pierre.philosophale) → bloqués pour toute source, par dessein assumé (:73-77) | |
| 24 | Miroir JS de l'axe 1sphereId = 1,2,3,4,5,7,8,9 — S6 absente, S9 comptée comme branche⚠ Suite datée 2026-10-03 (brief d’assemblage §4 ③) : ce qui est absent ici, c’est S6 dans une LISTE — les branches du JS, une face, un catalogue. La sphère S6 Divertissement elle-même est massivement bâtie (276 fichiers) ; la confusion vient de l’empaqueteur, un module de présentation nommé d’après S6.frontend/js/arbre-spheres.js:292-472 — BRANCHES a 8 clés : :297 personnel(sphereId 1) · :323 entreprise(2) · :351 institutions(3) · :375 creation(4) · :399 communaute(5) · :423 formation(7) · :447 monEquipe(8) · :472 durabilite(9). `grep -c divertissement` sur le fichier = 0. Chargé en prod : governance-init.js:672 · confirmé dans le codece n'est pas un miroir, c'est la composition PÉRIMÉE que arbre_canon.py existe pour tuer (S1-S5+S7-S9). Aucune garde : scripts/audit_s9_s6_packaging_axis.py n'est PAS dans ci.yml (grep = 0) et ne vérifie que des phrases de tronc obsolètes + les renderers, jamais la composition. Les 12 épreuves ne tiennent que le Python. | |
| 25 | Une clé polysémique, assumée au canonDeux domaines incompatibles pour la MÊME clé : event_bus.py:301 `discipline_flag: 'eta_stricte'` vs backend/app/services/babel/measure_tier3.py:55 `VALID_DISCIPLINE_FLAGS = {'S1','S2','S3','S4'}`, avec coercition à 'S4' (spéculation) en :137-142 et pondération DISCIPLINE_WEIGHTS :61-66 (S4 = 0.3) · confirmé dans le code« assumée » n'est pas établi : je n'ai trouvé AUCUN module qui réconcilie les deux domaines, ni dans CLAUDE.md §4.4.1 ni en code. Ça ne mord pas aujourd'hui (DISCIPLINE_WEIGHTS n'est lu qu'en measure_tier3.py:376, sur des lectures tier-3 LLM, jamais sur un payload de bus) — c'est une homonymie non réconciliée, pas une polysémie assumée. Il manque soit un domaine unique déclaré, soit un pont écrit |
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.
Matière : PLANCHES-MATIERE-3-DONNEES-2026-10-03.json (sha256 634d4e52ceb9) — planche de matière « PLANCHE VIII — LA CLASSIFICATION : deux axes orthogonaux, trois faces par sphère, et le troisième champ qui ne refuse pas ». 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.