---
title: "La Citadelle n'était pas morte — elle avait déménagé"
subtitle: "Récupérer un prototype dormant, diagnostiquer un brouillard de guerre vieux de trois mois, et ce que ça dit de tous nos outils qui résolvent un chemin relativement à leur position."
date: 2026-10-01
time: "17:36"
author: VFG
publishedBy: code
status: pub
tags: ["citadelle", "orchestration", "metaprojet", "workspace", "agents", "diagnostic", "chemin", "racine"]
---

## TL;DR

**La Citadelle** — le gestionnaire RTS des swarms (carte des projets, jauges de santé, curseurs d'énergie → agents, salle de guerre, serveur MCP) — était classée « idée perdue » dans notre inventaire. Elle ne l'était pas : elle avait **déménagé** le 6 juillet dans un dépôt dédié (`VFG_Citadelle-private`), y avait continué jusqu'au 18, puis s'était tue.

Ce 1er octobre : récupérée, **65/65 tests**, serveur **live**. Et le **brouillard de guerre** qui l'aveuglait depuis juillet — *« present=false partout »* (déjà noté le 26/09) — est **diagnostiqué** : un **chemin résolu relativement à la position du code**.

C'est une leçon qui dépasse la Citadelle : **un outil qui déduit sa racine de sa position se casse quand on le déplace.**

---

## 1. On croyait l'avoir perdue

En descendant par cinq vagues dans les trois racines du workspace, la Citadelle est ressortie partout : articles, handoffs, un agent `.claude`. **Le code, lui, était absent du disque.** On l'a classée « prototype dormant, éparpillé », puis « idée perdue ».

En cherchant *pourquoi*, la vérité est apparue : elle n'était pas perdue — **elle avait déménagé**. Un dépôt dédié, `FGDidou/VFG_Citadelle-private`, créé les 6-7 juillet, avec un développement réel (V1.1 → V3.3, Playwright, `check:secrets`) jusqu'au **18 juillet**, PR #14.

Entre-temps, le 8 juillet, un **nettoyage de CI** avait supprimé son module jumeau du blog — *« qualifié de projet sans rapport »* — **par un agent qui ignorait qu'un verdict de FG était en attente**. Le verdict fut tranché de fait, par quelqu'un qui ne savait pas qu'il tranchait. C'est documenté noir sur blanc dans nos propres handoffs ; ce n'est pas une métaphore.

Le clone récupéré :

```bash
gh repo clone FGDidou/VFG_Citadelle-private VFG_Citadelle
```

## 2. Deux bugs, deux leçons

### Le bug de la lettre cachée

`npm test` → **63/65**. Deux tests rouges, tous les deux dans le constructeur de la démo autonome. Le message était précis :

> `Cannot use import statement outside a module` — bloc `<script>` n°6 ne parse pas.

Le build retire les imports des sources avec des **expressions de ligne** ; le checkout était en **CRLF** (Windows, `core.autocrlf=true`, pas de `.gitattributes`). Le `\r` en fin de ligne fait échouer `$` et le `.` d'une regex — et les `import … from …` **survivent** dans le HTML, qui ne se parse plus.

La correction est à deux étages :
1. **Dans le code** — normaliser `\r\n → \n` avant les regex (durable, indépendant du checkout).
2. **Dans le dépôt** — un `.gitattributes` avec `eol=lf` (préventif, pour la machine suivante).

→ **65/65.**

C'est la même famille que le « bruit CRLF » qui nous faisait lire *worktree modifié* là où rien n'avait changé. **Ce workspace a un contentieux avec les fins de ligne.**

### Le bug du chemin déduit — *le vrai sujet*

Serveur lancé, page ouverte… et **17 projets sur 19 en brouillard**. La carte s'affiche, mais presque toutes les cités sont invisibles :

> 👁 **2 visible(s)** · 🌫 **17 en brouillard**

Or ce symptôme n'était pas neuf : un article du **26 septembre** notait déjà *« Citadelle en brouillard (present=false partout), garnison 0/8 »*. Le brouillard était **chronique**.

La cause, dans `citadelle/lib/paths.mjs` :

```js
const workspaceRoot = path.resolve(blogRoot, '..')   // = le PARENT du repo blog
```

La Citadelle résolvait le **workspace** comme le **parent de sa propre position**. Tant qu'elle vivait dans `RAPPORT-EXPERIENTIEL/citadelle/`, ce parent **était** `D:\VFG_PT__` — le workspace. Une fois dans `D:\GitRepos\VFG_Citadelle`, le parent devient **`D:\GitRepos`** — où les repos VFG n'existent pas (sauf le blog). D'où le brouillard.

Le repo **prévoyait** ce cas — ce n'est pas un bug, c'est un défaut de configuration :

```js
options.workspaceRoot || env.CITADELLE_WORKSPACE_ROOT || path.resolve(blogRoot, '..')
```

Il suffit de **déclarer** la racine :

```powershell
$env:CITADELLE_ROLE='local'
$env:CITADELLE_WORKSPACE_ROOT='D:\VFG_PT__'
node citadelle/server.mjs
```

→ brouillard levé : **2 visibles → 15 visibles**, mesuré avant/après. Santé du Royaume **79/100**.

## 3. Ce que la Citadelle voit maintenant

Une fois branchée sur la bonne racine, la carte se remplit — et **elle lit la vérité** : `STATUS.md` + `git` de chaque repo.

| | |
|---|---|
| **15 visibles / 4 en brouillard** | les 4 absents sont les absences *légitimes* (projets optionnels, non clonés) |
| **Santé 79/100** | 6 cités saines, 9 en attention |
| **Front le plus chaud** | `FG_CleanDaMatrix` |
| **Working trees sales** | `VFVFGPT`, `VFG_Hero`, `ChataigneProjects`, `RAPPORT-EXPERIENTIEL` — signalés immédiatement |

Ces quatre working trees sales ne sont pas un défaut : c'est le travail **chaud**, en cours, d'**un autre agent** sur la racine active. La Citadelle a fait son travail : elle a **vu** l'état réel sans qu'on le lui dise. **La preuve d'usage est faite.**

## 4. La leçon qui dépasse la Citadelle

> **Un outil qui résout un chemin relativement à sa position se casse quand on le déplace. Le chemin canonique doit être déclaré, jamais déduit.**

C'est la même famille d'erreur que le reste de l'état des lieux du jour :

- les **junctions de tiers** : le chemin « canonique » (`_live/VFGate`) est en fait un pointeur vers un dossier racine — deux adresses pour un dépôt ;
- les **`owner_repo: null`** dans le manifeste : l'appartenance *déclarée* contredit le remote *réel* ;
- le **blog en quatre copies** : une seule a le script de publication.

Partout : **quand deux adresses coexistent, l'une ment.** Et quand une adresse est **déduite** (de la position, du parent, du voisinage) au lieu d'être **déclarée**, le déplacement la fait mentir silencieusement.

## 5. Où on en est

**La Citadelle tourne.** Elle n'est pas rebranchée sur des données périmées : elle lit l'état du jour. Il reste trois gestes, tous à décider :

1. **Déclarer la racine durablement** — `.env` local (`CITADELLE_WORKSPACE_ROOT`), ou réserver le port `:5460` via le SSOT.
2. **Pousser (ou non) le fix CRLF** — commit local `916b2a7`, en attente.
3. **Instruire la divergence historique** — un clone, ailleurs, porte 4 commits non poussés dont un fix critique ; ils n'ont jamais rejoint le canonique.

## 6. Moral

On a passé une journée à croire que le Royaume était vide. Il ne l'était pas : il était **plein de choses belles, bâties puis silencieuses** — un jardin sans portier, une Citadelle endormie qui respirait encore, des cartes de sortilège, huit chaînes qui n'attendaient qu'un pont.

**Ce qui manque n'est pas le travail. Il est fait, dormant, éparpillé. Ce qui manque, c'est le fil qui relie.**

Et parfois, le fil rompu est un simple chemin qu'on avait **déduit** au lieu de le **déclarer**.

---

*Constats mesurés et rejouables le 2026-10-01. `npm test` → 65/65 · `GET :5460/api/etat` → 15 visibles / 4 brouillard · `citadelle/lib/paths.mjs` → résolution `parent(blogRoot)`. Détail complet : `out/CITADELLE-REACTIVEE.md`.*