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

L’emboîtement fractal

L'emboîtement fractal : appareils × assemblages de sorties

Planche 6 · L’emboîtement fractal
35 pièces · 10 bâties · 23 partielles · 2 absentes · 0 non établies
Planche 6 — L’emboîtement fractal

ouvrir la planche en plein écran → · 35 pièces · 13 traverses · 15 marges

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

Les deux bouts sont bâtis — 7 sorties fermées et gardées au serveur, 6 moteurs et 6 afficheurs au client — mais le MILIEU n'existe que sur UNE page : le seul choisisseur de sortie n'est dans aucun des deux tableaux du chargeur central, l'attestation de l'appareil arrive au noyau APRÈS que la branche de compilation soit déjà choisie, et les deux moitiés du joint appareil→assemblage ne se rencontrent sur AUCUNE page.

Les marges 15 arbitrages ouverts, entiers
  • ⚑ LA CORRECTION PRINCIPALE — « LE JOINT appareil → assemblage, BÂTI DE BOUT EN BOUT » est FAUX, et de deux façons indépendantes. (1) Les deux moitiés clientes ne coexistent sur AUCUNE page : `device-master.js` n'est chargé que par l'entrée `'institutions'` de SPHERE_MODULE_MAP (governance-init.js:95) ; `view-mode-manager.js` n'est dans aucun des deux tableaux `scripts` et n'arrive que par balise directe sur desktop.html, meshmap.html, simulation-dashboard.html. Intersection = 0. (2) Là où le plafond atteint bien un organe chargé — le noyau lui-même — il arrive à la ligne 1326, soit APRÈS le `if/else` de la ligne 1286 qui a déjà choisi le moteur : le noyau ANNOTE la requête et son propre commentaire l'avoue (« Le plafond qualifie seulement la lecture graphique »).
  • ⚑ LE PIÈGE QUI M'A PRESQUE FAIT DIRE LE CONTRAIRE : `grep -c view-mode-manager governance-init.js` rend 2, et on croit le module chargé. Les deux occurrences sont (a) un commentaire à la ligne 667 et (b) la ligne 1389, DANS `TIER3_PATTERNS` — une liste de noms qui ne CHARGE rien : `matchesTier` (:1433-1436) dérive le nom d'un script déjà présent dans le tableau pour le classer en étage 3. Même piège pour `atom-meshmap` (:1320) et `env-customizer`. ⛔ Ne jamais conclure « chargé » d'un nom trouvé dans governance-init : il faut la chaîne `'/js/<nom>.js'` DANS un des deux tableaux `scripts` (bornes : :232-1078 FULL, :1079-1200 LIGHT, plus SPHERE_MODULE_MAP :71-138 et PAGE_MODULE_MAP :139-224 qui sont page-conditionnels).
  • ⚑ LE CHIFFRE « 166 PAGES » N'EST PAS REPRODUCTIBLE. Mes mesures, avec leurs règles : 215 fichiers .html suivis par git sous frontend/ (`git ls-files 'frontend/*.html' | wc -l`) ; 109 chargent governance-init.js ; 27 sont des GOVERNANCE_PAGES et reçoivent le tableau FULL (governance-init.js:51-65). Les 82 autres reçoivent le tableau nommé « LIGHTWEIGHT » — qui, malgré son commentaire « ~15 scripts instead of ~130 », en contient plus de cent, dont le noyau de projection. Ce commentaire est lui-même une dérive à signaler.
  • ⚑ J'AI DÛ CORRIGER LA FICHE DANS L'AUTRE SENS AUSSI : « compileVillage2D / compileXR — zéro demandeur » est trop fort. Le select du panneau studio offre bel et bien `2d` et `xr` (atom-projection-studio.js:1278-1280) et ces valeurs traversent `_resolveMedium` jusqu'au noyau. La vérité exacte : 0 demandeur en CODE, 1 demandeur par geste humain, sur la SEULE page qui porte `#projection-studio-root` (frontend/design-center.html). Et pour `xr` c'est pire que l'absence : design-center est une page LIGHT, donc `xr-layer.js` (tableau FULL seul, governance-init.js:913) n'y est pas — on peut y demander une sortie XR que rien n'affiche.
  • ⚑ `compileText` : la fiche avait raison, mais pas pour la raison annoncée. La thèse disait « aucun des 17 types ne peut atteindre la sortie text » — or 8 des 17 types DÉCLARENT `text` (mobile, ar_headset, station, baton_360, glasses, xr_headset, console, watch — re-mesuré par appel). Le vrai verrou est un ORDRE : `_deviceMediumCeiling` teste `outputs.includes('2d')` (view-mode-manager.js:951) avant `outputs.includes('text')` (:958), et AUCUN des 8 types ne porte `text` sans porter `2d` (re-mesuré : liste vide). La ligne `return 'text'`, écrite le 2026-09-30 avec un long commentaire qui la justifie, est morte à la naissance pour les 17 types.
  • ⚑ ET SON COMMENTAIRE SE TROMPE DE COMPTE : governance-init.js:476 et view-mode-manager.js:952 annoncent « text pour SEPT familles d'appareils sur huit ». Il y a 7 familles de runtime au total (RuntimeFamily, device_taxonomy.py:23-31), pas 8 ; et 6 d'entre elles portent `text` — robot est la seule qui n'en porte pas. Un commentaire qui dit « 7 sur 8 » là où le code dit « 6 sur 7 » fabrique une fausse assurance sur l'organe le plus fragile de la chaîne.
  • ⚑ LA DOUBLE SOURCE DU VOCABULAIRE CANON : `trinity_geo_router.py:53-58` importe `RenderLayer` dans un `try/except Exception` dont le repli est un CLONE LITTÉRAL des sept valeurs. J'ai vérifié que l'import RÉUSSIT (backend/.venv-tests → 7 valeurs identiques) et que le Dockerfile installe bien le paquet (`backend/Dockerfile:43-44`, `pip install -e /opt/hf-atom-tools`). Mais AUCUN test ne compare `device_taxonomy.RENDER_OUTPUTS` à `RenderLayer` : trois copies des sept valeurs peuvent diverger sans qu'une garde bronche. C'est un gisement, pas encore un défaut.
  • ⚑ `render_layer` par nœud : je ne reproduis pas « 13 valeurs ». Sous ma règle (`git grep -ohE "render_layer[\"']?\s*[:=]\s*[\"'][A-Za-z0-9_.-]+[\"']" -- backend frontend/js hf-atom-tools agents`, hors __tests__) j'en compte 12 distinctes. Le nombre dépend entièrement de la forme cherchée, donc il ne doit JAMAIS voyager sans sa règle. Ce qui est solide et indépendant du compte : 8 de ces valeurs sortent des 7 sorties canon, et la colonne (world_canon_projection.py:57) est un `Text` sans enum ni CheckConstraint.
  • ⚑ « MONTÉ DANS main.py » N'EST PAS UNE MESURE. `register_router` avale l'`ImportError` sauf si `core=True` ou `REGISTER_ROUTER_STRICT` — et aucun des 5 routeurs de cette planche n'est `core`. J'ai donc re-mesuré autrement : import réel dans `backend/.venv-tests` → les 5 importent, avec 10/9/10/6/6 routes. ⚠ C'est l'interpréteur LOCAL, pas le conteneur de production : je n'ai pas interrogé la prod.
  • ⚑ LA PORTE REST DE LA FRACTALE COMMUNAUTAIRE EST UNE SURFACE SANS CLIENT. 6 routes, importables, montées — et 0 appelant dans le dépôt hors docs et scripts de déploiement. Un conseil antérieur l'avait déjà mesuré (data/urbanisme/projets-services/.../conseil_3.json:86 : « personne ne s'y présente ») ; je le confirme indépendamment. Je dégrade donc BATI → PARTIEL : la porte est atteignable, la chaîne est coupée.
  • ⚑ LE MODULE QUI PORTE LE NOM NE FAIT PAS LA CHOSE. `frontend/js/fractal-nested-windows.js` est chargé par les DEUX tableaux (governance-init.js:485, :1172) et son nom promet l'emboîtement fenêtre-dans-fenêtre. Son en-tête (:3-7) dit la vérité : 3 niveaux FIXES (L1 tronc / L2 feuille / L3 flux), lisant /api/v2/fractal-code. Aucune récursion. On pourrait croire la primitive rachetée côté client : elle ne l'est pas.
  • ARBITRAGE QUI ATTEND JONATHAN nº1 — où doit vivre le choisisseur ? Deux options exclusives, et le code porte déjà les deux moitiés : (a) charger `view-mode-manager.js` dans les deux tableaux et charger `device-master.js` hors de la seule page institutions — le joint se ferme, mais 1114 modules héritent d'un choisisseur actif ; (b) déplacer la décision DANS le noyau (qui lit déjà le plafond à :92-113) et faire du ViewModeManager un simple lecteur. ⛔ Ne pas trancher cela dans une planche : la planche doit MONTRER les deux moitiés et le vide entre elles.
  • ARBITRAGE nº2 — `meshmap` est le défaut du noyau et n'est PAS une sortie canon. Soit on l'ajoute aux 7 (et les 4 vocabulaires doivent bouger ensemble), soit le `else` de la ligne 1303 doit REFUSER au lieu de glisser. Aujourd'hui le système a un défaut silencieux qui porte un nom hors canon.
  • ARBITRAGE nº3 — `dom` comme dernier barreau du repli (view-mode-manager.js:314) sort du vocabulaire et n'affiche pas le monde. Et ces 4 marches sont FIGÉES par un test (commentaire :975-977) : c'est exactement le cas « un test qui fige une dérive reste vert ». Corriger l'échelle demandera de toucher au banc.
  • CE QUE LA PLANCHE DOIT FAIRE VOIR, GRAPHIQUEMENT : ne pas dessiner les 7 sorties, les 6 moteurs et les 6 afficheurs du MÊME trait. Les deux bouts sont pleins ; le milieu est une bande vide traversée par trois fils seulement — et deux de ces trois fils n'existent que sur une seule page. Le trait du choisisseur doit être pointillé et porter « 0 / 109 pages par le chargeur central ».
Les blancs déclarés ce que la mesure n’a pas pu voir

Ce que cette collecte N'A PAS pu voir. (1) LE RUNTIME : tout est mesuré sur le commit 8addf1c9481559a8e9dbc97b1fc0bcc85e10a0e1 et sur l'arbre de travail, jamais sur un navigateur ouvert. Je n'ai chargé aucune page, donc je n'ai pas vu un seul `window.ViewModeManager` réel, aucun appel HTTP, aucune projection compilée. « Chargé » signifie ici « présent dans un tableau de scripts ou une balise <script> », pas « exécuté sans erreur ». (2) LA PRODUCTION : je n'ai pas interrogé le VPS DigitalOcean (138.197.132.63) ni la base atom_db. Je ne sais donc pas combien d'appareils sont réellement enregistrés dans le registre, ni si `scope_membership` / `projection_nodes` contiennent une seule ligne, ni quels routeurs le processus prod a réellement montés (`register_router` avale l'ImportError, et les listes `_registered_ok` / `_registered_broken` ne vivent qu'en mémoire du processus). L'importabilité des 5 routeurs est mesurée dans backend/.venv-tests, en local. (3) LE DEUXIÈME ARBRE : mes grep ont bien couvert `backend/app/` ET les ~278 .py hors `app/` (le motif `-- backend` les prend tous les deux), mais je n'ai PAS fouillé `archives/`, `transit/` ni `AT-OM-999-DEPLOY-ZAMA-ALLELUYA/` — un appelant historique y vivrait sans que je le voie, et ce serait un appelant mort de toute façon. (4) LES BALISES DYNAMIQUES : je n'ai cherché les chargements que dans governance-init.js et dans les balises `<script src>` statiques des .html. Un module injecté par un autre chargeur JS (window-manager, module-orchestrator, un import dynamique) m'échapperait : mes comptes « 0 / N pages » sont donc des MINORANTS de chargement, et c'est dans ce sens-là qu'ils peuvent être faux. (5) LE COMPTE DES VALEURS `render_layer` dépend de la forme cherchée : ma règle ne voit que les littéraux `render_layer = \"x\"` / `render_layer: 'x'` ; une valeur construite, lue en base ou passée par variable n'y figure pas. (6) LES SORTIES `2.5d` ET `sonore` : je conclus ABSENT sur l'absence de moteur dans le noyau de projection ; je n'ai pas cherché un organe sonore ailleurs dans le dépôt (bioacoustic, audio_generation existent et n'ont pas été examinés pour cette question).

ne pas dessiner

  • Les 27 cellules de la grille, l'urbanisme du village, le contenu du village, l'anatomie des lieux, les poumons, le Metatron et le QG — les 7 cosmogrammes existants (frontend/planches/*.svg, galerie frontend/planches-atelier.html) les portent déjà.
  • La topologie 8 branches + tronc + sève. Elle est tranchée et scellée (CLAUDE.md §0, arbre_canon.py tenu par 12 tests) ; la reprendre ici rouvrirait un arbitrage fermé. Cette planche ne touche à l'arbre que par UN point : le registre S6_PACKAGING_LAYER et ses 14 renderers déclarés.
  • Le vocabulaire étoile/pétale et le yin-yang des services : scellés par ADR le 2026-08-08 (« tout existe déjà »), hors sujet du joint appareil→sortie.
  • La chaîne de position / point 0 / covenant. Elle touche cette planche en un seul endroit (le focus dérivé de nav-state qui écrase le medium choisi, atom-projection-studio.js:966-971) — ce seul trait suffit ; le reste a ses propres planches.
  • Les 7 mondes et les 3 portées de trinity_geo. Ils partagent le même routeur que la garde des sorties, mais ce sont trois axes DIFFÉRENTS : les mêler sur une planche ferait croire que la sortie est une portée. Ne tirer de trinity_geo QUE le validateur `_sortie_connue`.
  • Un schéma qui montrerait la chaîne complète comme fonctionnelle. Les deux bouts bâtis et le milieu vide doivent se distinguer au premier regard — trait plein / trait pointillé / absence de trait, et chaque pointillé annoté de son compte de pages.
Les traverses 13 relations, de source à source
device_registry (POST /api/v2/devices/register)
alimente
→device-master.js (_registryState)backend/app/routers/device_registry.py:658 → frontend/js/device-master.js:410-411, enregistrement auto :1549-1552
device-master.js (getDeviceProfile)
alimente
→ViewModeManager (_getDeviceCapabilities)frontend/js/device-master.js:1570-1590 → frontend/js/view-mode-manager.js:823-862 — mais governance-init.js:95 ne charge device-master que sur institutions.html, et view-mode-manager n'est dans aucun tableau : intersection de pages = 0
device-master.js (getDeviceProfile)
alimente
→atom-projection-kernel (device_render_ceiling)frontend/js/atom-projection-kernel.js:92-113, appelée à :1326 — APRÈS le choix de branche à :1286-1303 : le plafond annote, il ne décide pas
ViewModeManager.selectMedium
appelle
→atom-projection-studio (_resolveMedium)frontend/js/atom-projection-studio.js:695-718 — appel conditionnel à `global.ViewModeManager`, absent du chargeur central ; le repli déclaré est `defaut_sans_selecteur` → meshmap (:719-722)
select du panneau studio
appelle
→atom-projection-kernel (compile*)frontend/js/atom-projection-studio.js:1275-1280 → :686-693 → :956 → noyau :1286-1303 ; monté seulement là où existe #projection-studio-root : frontend/design-center.html, 1 page sur 215
nav-state (position dérivée)
autorise
→medium de la requête studiofrontend/js/atom-projection-studio.js:966-971 puis :1570-1572 — quand un focus est dérivé, `request.medium` est FORCÉ à 'meshmap' et le choix explicite de l'opérateur est écrasé ; nav-state-machine.js n'est chargé que dans le tableau FULL (governance-init.js:293), donc pas sur design-center
atom-projection-kernel
alimente
→les six afficheurstopic `projection.compile.completed` publié à frontend/js/atom-projection-kernel.js:1356 ; abonnés : mirror-earth.js:626, projection-2d-window.js:98, projection-text-window.js:163, scene-viewer-3d.js:509, xr-layer.js:1238 — 2 des 6 hors du tableau LIGHT, 1 hors des deux
visual-layer-composer (MEDIUM_PRESET_MAP)
appelle
→renderers de calquesfrontend/js/visual-layer-composer.js:184 via RENDERER_COMMAND_TOPICS:65-67 — 1 fil (geo) pour 5 calques
arbre-spheres (S6_PACKAGING_LAYER / render_plan)
alimente
→ViewModeManager (_wireFruitRenderPlan)frontend/js/arbre-spheres.js:693-711 → frontend/js/view-mode-manager.js:320-330 — consommateur non chargé
nested_window_fractal.RenderLayer
autorise
→trinity_geo_router (garde des 7 sorties)hf-atom-tools/atom_canon/primitives/nested_window_fractal.py:20-29 → backend/app/routers/trinity_geo_router.py:53-58, :123-134 ; import canon re-vérifié OK dans backend/.venv-tests
FractalAddress (plafond 12)
contient
→NestedWindowFractal (depth)hf-atom-tools/atom_canon/primitives/nested_window_fractal.py:44-51 — la profondeur est héritée et bornée, mais la classe qui l'hérite n'a aucun appelant
device_taxonomy (RENDER_OUTPUTS)
alimente
→les services backend1 seul consommateur des SORTIES : backend/app/routers/device_registry.py:36-42 ; les 4 autres importateurs (atom_package_vocab.py:17, continuity_engine.py:41, device_capability_registry.py:43, runtime_adapter_registry.py:47) n'importent que RUNTIME_FAMILY_VALUES
fractale_communautaire (/api/v2/fractale)
alimente
→un clientbackend/app/routers/fractale_communautaire.py:79 monté main.py:2007 ; 0 appelant hors docs/tests/scripts de déploiement
La nomenclature 35 pièces, chacune avec sa source
1Noyau de projection — SIX moteurs de compilation6 méthodes compile* — règle : `git show HEAD:frontend/js/atom-projection-kernel.js | grep -n '^ compile[A-Za-z0-9]*:'`, 6 lignesfrontend/js/atom-projection-kernel.js:1385 (compileMeshMap), :1449 (compileGeoMirror), :1507 (compileText), :1558 (compileVillage2D), :1586 (compileScene3D), :1638 (compileXR) · confirmé dans le code
2Le noyau est dans LES DEUX tableaux du chargeur central109 pages sur 215 chargent governance-init.js (règle : `git grep -l governance-init.js -- 'frontend/*.html' | wc -l`, fichiers suivis par git)frontend/js/governance-init.js:474 (tableau FULL) et :1164 (tableau LIGHT) · confirmé dans le code
3compileMeshMap — branché, et HORS du vocabulaire fermé du serveur`meshmap` ∉ RENDER_OUTPUTS (7 valeurs : text, 2d, 2.5d, 3d, xr, geo, sonore — re-mesuré par appel)frontend/js/atom-projection-kernel.js:1385 ; demandeurs : frontend/js/environment-projection-door.js:97,226 · frontend/js/hitl-panel.js:627 · frontend/js/atom-projection-studio.js:720 (défaut) ; absence côté serveur : backend/app/core/device_taxonomy.py:62-70 · confirmé dans le code
4compileGeoMirror — BÂTI ET APPELÉ (la fiche le disait dormant : FAUX)3 demandeurs `medium: 'geo'` hors noyau et hors tests (règle : `git grep -n "medium: 'geo'" -- frontend/js`, moins le noyau, moins __tests__)frontend/js/atom-projection-kernel.js:1449 ; demandeurs : frontend/js/mirror-earth.js:547 · frontend/js/projection-spine-bridge.js:355 · frontend/js/temple-flow-bridge.js:436 · confirmé dans le code
5compileScene3D — branché, et atteint par deux portes réelles2 demandeurs `medium: '3d'` hors noyau/tests ; 2 pages où demandeur ET afficheur coexistent (creation.html, meshmap.html)frontend/js/atom-projection-kernel.js:1586 ; demandeurs : frontend/js/env-customizer.js:926 (creation.html) · frontend/js/environment-projection-door.js:339 (creation.html, meshmap.html) ; afficheur frontend/js/scene-viewer-3d.js:509 (chargé governance-init.js:477 et :1167) · confirmé dans le code
6RENDER_OUTPUTS — le vocabulaire FERMÉ du serveur, validé à l'import7 sorties — re-mesuré par appel : `len(RENDER_OUTPUTS) == 7`, ('text','2d','2.5d','3d','xr','geo','sonore')backend/app/core/device_taxonomy.py:62-70 ; garde à l'import :232-238 (RuntimeError si une sortie sort de l'énumération) · confirmé dans le code
7Les 17 types d'appareil et leurs sorties maximales17 types — re-mesuré par appel : `len(DEVICE_TYPE_VALUES) == 17`backend/app/core/device_taxonomy.py:34-57 (DeviceType), :98-140 (DEVICE_TYPE_DEFINITIONS), garde d'unicité :230-231 · confirmé dans le code
8L'honnêteté de la taxonomie — 9 types refusent d'inventer une sortie9 types à sorties vides — re-mesuré par appel : drone, sensor, robot, iot, vehicle, gateway, camera_360, seed_key, energy_node ; chacun porte son `no_runtime_reason`backend/app/core/device_taxonomy.py:96 (_NO_SCREEN), :101-140 · confirmé dans le code
9Le registre d'appareils — importable et enregistré10 routes — mesuré par import réel dans backend/.venv-tests (`import app.routers.device_registry` → len(router.routes)==10)backend/app/routers/device_registry.py:36-42 (seul consommateur backend des sorties), prefix /api/v2/devices :46-48 ; montage backend/app/main.py:2548 · confirmé dans le code
10FractalAddress — la profondeur, bornée et qui refuseplafond 12 ; 2 appelants de production hors tests/docs (règle : `git grep -ln 'fractal_address import'` moins docs/, moins tests/)hf-atom-tools/atom_canon/primitives/fractal_address.py:20 (MAX_FRACTAL_DEPTH = 12), :32 (FractalDepthError), :130-133 (le refus) ; appelants de production : backend/app/services/trinity_geo/locator.py et backend/app/services/bioacoustic/fractalizer.py · confirmé dans le code
11Le répartiteur du noyau — 5 media nommés, le 6e est le DÉFAUT5 `if` nommés (geo, 2d, 3d, xr, text) + 1 `else` → compileMeshMapfrontend/js/atom-projection-kernel.js:1286-1303 · confirmé dans le code`meshmap` n'est jamais nommé : tout medium inconnu (dom, uefn, cymatique, sonore, 2.5d) reçoit une meshmap sans refus ni trace. Le commentaire :1297-1299 le reconnaît et laisse le geste à plus tard.
12compileText — bâti de bout en bout, et STRUCTURELLEMENT INATTEIGNABLE8 des 17 types déclarent `text` ; 0 des 8 déclare `text` SANS `2d` (re-mesuré par appel sur DEVICE_TYPE_DEFINITIONS)frontend/js/atom-projection-kernel.js:1507 ; afficheur frontend/js/projection-text-window.js:163 ; échelle frontend/js/view-mode-manager.js:951-958 ; taxonomie backend/app/core/device_taxonomy.py:98-140 · confirmé dans le codeDeux verrous, et je les ai vérifiés moi-même. (a) `_deviceMediumCeiling` teste `outputs.includes('2d')` AVANT `outputs.includes('text')` (view-mode-manager.js:951 puis :958) : comme AUCUN type canon ne porte `text` sans porter `2d`, la ligne `return 'text'` est morte pour les 17 types. (b) Le select du studio n'offre pas `text` (atom-projection-studio.js:1275-1280 : auto, meshmap, geo, 2d, 3d, xr). Aucun des deux chemins ne peut produire `text`.
13compileVillage2D — bâti, afficheur bâti, UN SEUL demandeur : un select sur une page0 demandeur en code (`medium: '2d'` littéral hors noyau/tests = 0) ; 1 demandeur par geste humain (option du select) sur 1 page sur 215frontend/js/atom-projection-kernel.js:1558 ; afficheur frontend/js/projection-2d-window.js:98 (chargé governance-init.js:475 et :1165) ; demandeur frontend/js/atom-projection-studio.js:1278 → :686-693 · confirmé dans le codeLa fiche disait « zéro demandeur » : c'est trop fort. L'option `2d` EXISTE dans le panneau et traverse `_resolveMedium` (medium_source='panel_explicit'). Mais le panneau ne se monte que là où vit `#projection-studio-root` — `frontend/design-center.html`, SEULE page du dépôt (règle : `git grep -l projection-studio-root -- 'frontend/*.html'` → 1). Aucun code ne demande jamais `2d`.
14compileXR — bâti, afficheur bâti, UN SEUL demandeur : un select sur une page0 demandeur en code ; 1 option de select sur design-center.html — qui est une page LIGHT, donc xr-layer.js n'y est PAS chargéfrontend/js/atom-projection-kernel.js:1638 ; afficheur frontend/js/xr-layer.js:1238 (chargé governance-init.js:913, tableau FULL SEUL) ; demandeur frontend/js/atom-projection-studio.js:1280 · confirmé dans le codePire que « zéro demandeur » : le seul demandeur et le seul afficheur sont sur des pages DISJOINTES. `design-center` n'est pas dans GOVERNANCE_PAGES (governance-init.js:51-65, 27 pages) → tableau LIGHT → `xr-layer.js` absent. Demander `xr` depuis le studio compile une projection que rien n'affiche sur cette page.
15RenderLayer canon — la MÊME énumération, mais importée dans un try/except7 valeurs identiques aux 7 de RENDER_OUTPUTS — re-mesuré par appel dans backend/.venv-tests : l'import canon RÉUSSIT et rend exactement ('text','2d','2.5d','3d','xr','geo','sonore')hf-atom-tools/atom_canon/primitives/nested_window_fractal.py:20-29 ; import backend/app/routers/trinity_geo_router.py:53-58 · confirmé dans le codeLa fiche disait « en autorité canon-aware ». L'import est enveloppé d'un `except Exception` qui retombe sur un CLONE LITTÉRAL des sept valeurs (trinity_geo_router.py:58). Les deux listes sont aujourd'hui égales — mais AUCUN test ne les compare (règle : `git grep -rn RENDER_OUTPUTS -- backend/tests hf-atom-tools` → 2 occurrences, toutes deux internes à device_taxonomy). Trois copies du même vocabulaire peuvent diverger en silence.
16⚑ LE JOINT appareil → assemblage — les deux moitiés existent, elles ne se rencontrent sur AUCUNE pagedevice-master.js : 1 page (institutions) · view-mode-manager.js : 3 pages (desktop, meshmap, simulation-dashboard) · intersection : 0serveur : backend/app/routers/device_registry.py:658 (rend possible_render_outputs) · client-émetteur : frontend/js/device-master.js:1549-1552 (enregistrement AUTO au chargement), :1570-1590 (getDeviceProfile pose le plafond) · client-consommateur : frontend/js/view-mode-manager.js:823-862, :925-960 · chargement : governance-init.js:95 (device-master.js, SPHERE_MODULE_MAP['institutions'] UNIQUEMENT) · confirmé dans le codeLA CORRECTION LA PLUS LOURDE DE CETTE PASSE. La fiche annonçait « BÂTI DE BOUT EN BOUT ». Mesuré : `device-master.js` n'apparaît qu'UNE fois dans governance-init.js, à la ligne 95, dans l'entrée `'institutions'` de SPHERE_MODULE_MAP — il ne se charge donc que sur institutions.html. `view-mode-manager.js` n'est dans AUCUN des deux tableaux de scripts ; il n'arrive que par balise directe sur desktop.html, meshmap.html, simulation-dashboard.html. AUCUNE page ne porte les deux. Le plafond attesté est produit là où personne ne choisit, et le choisisseur vit là où aucun plafond n'arrive.
17⚑ L'attestation de l'appareil arrive au noyau APRÈS le choix de la branche1 appelant, à la ligne 1326 — soit 23 lignes APRÈS le `if/else if/else` qui a déjà choisi le moteurfrontend/js/atom-projection-kernel.js:92-113 (_requestWithConfirmedDeviceRenderCeiling), appelée une seule fois à :1326 ; branche de compilation choisie à :1286-1303 · confirmé dans le codeLe noyau SAIT lire le plafond serveur et ne s'en sert pas pour choisir. Son propre commentaire le dit (:101-102) : « Le plafond qualifie seulement la lecture graphique ». C'est le second trou du joint, et il est indépendant du premier : même si device-master et un choisisseur se trouvaient un jour sur la même page, le noyau continuerait d'annoter sans décider.
18⚑⚑⚑ LE CHOISISSEUR — ViewModeManager, absent des DEUX tableaux du chargeur central0 occurrence de `/js/view-mode-manager.js` dans les tableaux `scripts` ; 3 pages sur 215 le chargent par balise directefrontend/js/view-mode-manager.js:24 (objet), :293-310 (selectMedium), :1147 et :1150 (exposition globale) ; governance-init.js:1389 — 'view-mode-manager' n'y figure QUE dans TIER3_PATTERNS (une liste de CLASSEMENT par étage, pas de chargement : matchesTier :1433-1436 dérive le nom d'un script DÉJÀ présent dans le tableau) · confirmé dans le codeLe piège est le nom dans TIER3_PATTERNS : il donne l'illusion d'un chargement. Il ne fait que dire « si ce script est chargé, défère-le ». Le module qui décide quelle sortie l'appareil reçoit n'est atteint par le chargeur central sur AUCUNE page. ⚑ La fiche disait « 3 des 166 pages » : je ne reproduis pas 166. Mesure : 215 fichiers .html suivis par git sous frontend/, 109 chargent governance-init.js, 27 sont des GOVERNANCE_PAGES (governance-init.js:51-65).
19Le composeur d'assemblage — MEDIUM_PRESET_MAP5 clés (meshmap, geo, 2d, 3d, xr) pour 7 sorties canon — `text`, `2.5d` et `sonore` sont absentsfrontend/js/visual-layer-composer.js:80-86 (la table), :303 (`MEDIUM_PRESET_MAP[normalized] || 'explorer'`) ; chargé governance-init.js:466 et :1156 · confirmé dans le codeUne sortie hors table ne provoque aucun refus : elle reçoit `explorer` (geo + pings). Un appareil texte se verrait donc composer des calques géographiques. Le repli par défaut déguise l'absence en succès.
20RENDERER_COMMAND_TOPICS — 5 calques déclarés, 1 fil vers un renderer5 calques nommés dans PRESETS (geo, pings, semi-xr, xr-full, gaming) · 1 topic de commande (geo)frontend/js/visual-layer-composer.js:65-67 (la table : une seule entrée 'geo'), :69-76 (PRESETS nomment 5 calques), :184 (`RENDERER_COMMAND_TOPICS[layerId]`) · confirmé dans le code4 calques sur 5 s'activent dans l'état du composeur sans qu'aucune commande ne parte vers un renderer. Le commentaire du code l'assume (:63-64 : « Une preference active n'est pas une preuve qu'un renderer existe »).
21S6_PACKAGING_LAYER — l'assemblage par BRANCHE, 14 renderers déclarés14 renderers déclarés ; 14/14 fichiers existent ; 3/14 seulement sont chargés par governance-init.js (atom-encyclopedia-view, env-renderer-village [page 'desktop' seule], scene-viewer-3d)frontend/js/arbre-spheres.js:732-757 (le registre), :693 (sélection du renderer), :1641 et :1775 (exposition) · confirmé dans le code11 des 14 renderers déclarés n'ont AUCUNE entrée dans les tableaux de scripts : battlefield-view, chronoscope-renderer, dreamcatcher-renderer, env-renderer, isometric-renderer, lys-mirror-renderer, narrative-notes, provenance-viewer, sankey-renderer, world-renderer — et `view-mode-manager`, qui y est pourtant déclaré « orchestrator / router S6 ». Le registre nomme son propre routeur, et ce routeur n'est pas chargé.
22La branche xr/3d de la sélection de renderer — inatteignable par le chemin automatique4 marches de repli, dont 1 (`dom`) hors des 7 sorties canon ; `text`, `geo`, `meshmap`, `2.5d`, `sonore` absents de l'échellefrontend/js/view-mode-manager.js:311-317 (fallbackMedium, échelle ['xr','3d','2d','dom']) · confirmé dans le codeLe module qui porte cette sélection n'est de toute façon pas chargé centralement (voir LE CHOISISSEUR). La branche existe ; rien ne l'atteint par un chemin automatique.
23⚑⚑⚑ NestedWindowFractal — LA primitive d'emboîtement fenêtre-dans-fenêtre, orpheline0 appelant de `add_child` / `children_ids` — règle : `git grep -rn 'NestedWindowFractal\|nested_window_fractal'` sur TOUT le dépôt (donc les DEUX arbres backend : backend/app/ ET les 278 .py hors app/), hors hf-atom-tools/atom_canon/tests → 2 occurrences seulement, et aucune n'est un appel : trinity_geo_router.py:54 importe `RenderLayer` (l'énum, pas la classe) et temple_canonical_extractor.py:807 la cite en commentairehf-atom-tools/atom_canon/primitives/nested_window_fractal.py:32-77 (parent_id, children_ids, add_child, path) · confirmé dans le codeEt le module frontend qui porte presque son nom ne la sauve pas : `frontend/js/fractal-nested-windows.js` (chargé, governance-init.js:485 et :1172) est une charpente FIXE de 3 étages (L1 tronc / L2 feuille / L3 flux) lisant /api/v2/fractal-code — en-tête :3-7. Aucune récursion fenêtre-dans-fenêtre. La seule primitive récursive du dépôt reste sans appelant ; sa propriété `path` (:52-57) ne rend d'ailleurs que 2 maillons.
24render_layer — le champ PAR NŒUD, chaîne libre12 valeurs littérales distinctes en circulation — MA mesure, avec sa règle : `git grep -ohE "render_layer[\"']?\s*[:=]\s*[\"'][A-Za-z0-9_.-]+[\"']" -- backend frontend/js hf-atom-tools agents`, hors __tests__ → geo(18), 3d(7), 2d(7), meshmap(5), data(4), GEO_DUAL_MIRROR(4), root(2), geo_voids(2), geo_filaments(2), mesh_core(1), geo_overlay(1), geo_hubs(1)backend/app/models/world_canon_projection.py:57 — `Mapped[str] = mapped_column(Text, nullable=False, server_default=text("'2d'"))` · confirmé dans le codeLa fiche annonçait 13 : je ne le reproduis pas sous ma règle, j'en compte 12 — et la règle d'extraction change le compte, donc ce nombre doit toujours voyager avec elle. Ce qui compte n'est pas 12 ou 13 : c'est que 8 de ces 12 (meshmap, data, root, mesh_core, geo_voids, geo_filaments, geo_overlay, geo_hubs) sortent des 7 sorties canon, et que la colonne n'a NI enum NI CheckConstraint pour les refuser.
25La garde des 7 sorties — côté API, sur une seule porte6 routes importables (mesuré par import réel dans backend/.venv-tests) ; 1 champ gardé : `LocateRequest.render_layer`backend/app/routers/trinity_geo_router.py:123-134 (_sortie_connue, field_validator, refus nommé) ; montage backend/app/main.py:2303 · confirmé dans le codeLa garde existe, elle refuse honnêtement, et elle ne couvre QUE le corps de POST /api/v2/trinity-geo/locate. Le `render_layer` PAR NŒUD de world_canon_projection.py:57 n'est gardé par rien. La même notion est fermée d'un côté de l'API et libre de l'autre.
26QUATRE vocabulaires de sortie, disjoints4 énumérations ; `2d`/`3d`/`xr` sont les 3 SEULES valeurs communes aux quatre ; `meshmap`, `dom`, `uefn`, `pings`, `gaming` n'existent dans aucune autre(1) backend/app/core/device_taxonomy.py:62-70 — 7 valeurs · (2) frontend/js/atom-projection-kernel.js:1286-1303 — 6 media (meshmap, geo, 2d, 3d, xr, text) · (3) frontend/js/view-mode-manager.js:1033 (_mediumRank) — 6 rangs (dom, text, 2d, 3d, xr, uefn) · (4) frontend/js/visual-layer-composer.js:69-76 — 5 calques (geo, pings, semi-xr, xr-full, gaming) · confirmé dans le codeAucune table de correspondance entre les quatre, et aucun test qui les compare. Chaque traversée du joint est une traduction implicite.
27L'écouteur de changement de medium du studio — jette geo et meshmap en silence5 media acceptés ; `geo` et `meshmap` retombent sur un `return` nu — sans recompilation ni refus publiéfrontend/js/atom-projection-studio.js:1554-1579, le filtre à :1566 — `if (['dom','2d','3d','xr','text'].indexOf(rawMedium) === -1) return;` · confirmé dans le codeLe commentaire :1563-1565 reconnaît le trou pour les autres media (« ce silence-là est un geste séparé »). Et l'écouteur ne vaut que pour une requête dont `medium_source` est `view_mode_selected` ou `viewmode_medium_changed` (:1559-1561) — or le choisisseur qui produit ces sources n'est pas chargé.
28La chaîne de repli du medium — ne se replie pas vers le canon4 marches : xr → 3d → 2d → dom ; `dom` ∉ les 7 sorties canonfrontend/js/view-mode-manager.js:311-317 · confirmé dans le codeLe dernier barreau sort du vocabulaire : un appareil qui se replie jusqu'au bout atterrit sur `dom`, qui n'est pas une sortie canon et n'affiche pas le monde. Le commentaire :975-977 dit que ces quatre marches sont FIGÉES par un test — c'est le cas du « test qui fige une dérive et reste vert ».
29Les six afficheurs — tous bâtis, tous filtrants, mais pas tous chargés6 afficheurs · 3 dans LES DEUX tableaux (2d, text, scene-viewer-3d) · 2 dans le tableau FULL seul (mirror-earth, xr-layer → 27 pages) · 1 dans AUCUN tableau (atom-meshmap, 2 pages par balise : design-center.html, meshmap.html)frontend/js/mirror-earth.js:626-631 (geo, filtre `medium !== 'geo'`) · frontend/js/projection-2d-window.js:98 · frontend/js/projection-text-window.js:163 · frontend/js/scene-viewer-3d.js:509 · frontend/js/xr-layer.js:1238 · frontend/js/atom-meshmap.js (abonné via son propre pont, :670) ; chargement : governance-init.js:475, :476, :477, :913, :196 · confirmé dans le codeLa fiche disait BÂTI. Je dégrade : l'afficheur de la sortie PAR DÉFAUT du noyau — `atom-meshmap.js`, celle que reçoit tout medium inconnu — n'est dans aucun des deux tableaux du chargeur. Sur 107 des 109 pages qui chargent governance-init, le noyau peut compiler une meshmap que rien n'affiche.
30La porte d'environnement — bâtie, commitée, hors du chargeur central0 dans les tableaux de scripts · 2 pages sur 215frontend/js/environment-projection-door.js:97, :216, :226, :329, :339 ; 0 occurrence dans governance-init.js ; 2 pages : frontend/creation.html, frontend/meshmap.html · confirmé dans le codeElle porte les deux seuls appels `medium: 'meshmap'` et `medium: '3d'` faits par du CODE (et non par un select). Hors de ces 2 pages, ces deux portes n'existent pas.
31La porte REST de la fractale communautaire — montée, et personne ne s'y présente6 routes importables (mesuré par import réel dans backend/.venv-tests) · 0 appelant client — règle : `git grep -rn 'api/v2/fractale'` sur TOUT le dépôt, hors docs/, hors tests, hors frontend/content : il ne reste que la déclaration du préfixe, un script de déploiement (deploy/deploy-from-archive.sh:92, qui teste un 404) et des index de cartographiebackend/app/routers/fractale_communautaire.py:79 (prefix /api/v2/fractale), :193, :250, :279, :330, :406, :426 ; montage backend/app/main.py:2007 · confirmé dans le codeLa fiche disait BÂTI. L'organe est là, il est atteignable — mais aucune page ni aucun module ne l'appelle. `GET /api/v2/fractale/grille-27` et `/communaute/{id}/tronc` sont la seule porte REST de l'emboîtement communautaire, et elle n'a pas de client.
32Le plan de rendu des fruits — la jonction branche → sortie1 émetteur, 1 consommateur, 0 page où les deux sont chargés par le chargeur centralémetteur frontend/js/arbre-spheres.js:693-711 (topic `arbre.fruits.render_plan.updated`) ; consommateur frontend/js/view-mode-manager.js:320-330 (_wireFruitRenderPlan) · confirmé dans le codeLe seul consommateur du plan de rendu des fruits vit dans le module non chargé. Le plan est publié dans le vide.
33Le montage des routeurs — une DÉCLARATION, que j'ai dû vérifier autrement5 routeurs re-vérifiés par import réel dans backend/.venv-tests : device_registry 10 routes · device_capability_router 9 · scene_renderer 10 · fractale_communautaire 6 · trinity_geo_router 6 — tous importent sans erreurbackend/app/main.py, fonction register_router : `except ImportError` qui journalise et CONTINUE, sauf si `core=True` ou `REGISTER_ROUTER_STRICT` ; aucun des routeurs de cette planche n'est déclaré `core=True` (main.py:1839, 1871, 2007, 2303, 2548) · confirmé dans le code« Monté dans main.py » ne prouve rien par soi-même : l'ImportError est avalée. J'ai donc mesuré l'importabilité moi-même — dans l'interpréteur LOCAL backend/.venv-tests, PAS dans le conteneur de production. Ce que je n'ai pas vu : l'état réel du processus en prod.
34Moteur de la sortie `2.5d`1 sortie canon déclarée, 0 moteur, 0 afficheurbackend/app/core/device_taxonomy.py:64 (la sortie est déclarée) ; aucune méthode `compile*` correspondante dans frontend/js/atom-projection-kernel.js (6 compile*, aucune 2.5d) · confirmé dans le code
35Moteur de la sortie `sonore`1 sortie canon déclarée (portée par 8 types d'appareil), 0 moteurbackend/app/core/device_taxonomy.py:68 ; hf-atom-tools/atom_canon/primitives/nested_window_fractal.py:28 ; aucun moteur dans le noyau · confirmé dans le code
Le lexique de cette planche 15 termes à deux registres · 6 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…
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.
RenderLayerLa liste fermée des sept formes sous lesquelles une vue peut sortir, du simple texte à la carte ou au son.layer — Un étage du système qui existe en même temps que les autres et qui a son propre réseau de liens.
emboîtement fractalLe fait que chaque centre est contenu dans un centre plus large, de proche en proche, jusqu'au centre de tous.
mode (fenêtre / porte)La distinction entre regarder depuis chez soi et passer de l'autre côté : passer demande une permission et laisse une trace, regarder ne change rien.
lieu (type de nœud)Le genre de place qu'occupe un endroit ; le dépôt en compte déjà plus de cinq mille, tous géographiques.
communaute (type de nœud)Le genre de place qu'occupe un groupe, entre les personnes et le centre de tous.
scope_membershipLa table qui dit qui est contenu par qui, avec un contenant différent par portée, et c'est elle qui tranche quand l'adresse dit autre chose.
branche (BRANCHES)C'est la position des huit sujets qui produisent du contenu : S1 à S8, pas une de plus ni une de moins.
Creation (sacred_name de S7)registre spirituelC'est le nom de registre sacré que porte la sphère de la formation.Création (S4) — C'est le sujet de ce qu'on fabrique soi-même.
topicC'est le nom exact d'un message, celui qui décide qui le recevra ; il s'écrit en trois morceaux séparés par des points.
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.
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.

Matière : PLANCHES-MATIERE-1-TECHNIQUE-2026-10-03.json (sha256 b29a3dc78614) — planche de matière « L'emboîtement fractal : appareils × assemblages de sorties ». 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.