diff --git a/docs/OPS-LOCALHOST-AUTO-SYNC.md b/docs/OPS-LOCALHOST-AUTO-SYNC.md
index c426481..bc67fe0 100644
--- a/docs/OPS-LOCALHOST-AUTO-SYNC.md
+++ b/docs/OPS-LOCALHOST-AUTO-SYNC.md
@@ -1,964 +1,1393 @@
-# OPS — Localhost auto-sync
+# OPS — Localhost auto-sync, cockpit local et supervision NAS
## Objet
-Ce document fige l’architecture locale qui permet de garder le **localhost éditorial** aligné sur `origin/main`, sans dépendre de la mémoire de l’opérateur.
+Ce document remplace et prolonge la première version de l’architecture `localhost auto-sync`.
+Il fige désormais **l’architecture opératoire complète** obtenue à l’issue de la session de stabilisation du cockpit local d’exploitation.
-But recherché :
+Le périmètre ne se limite plus à :
-- quand une PR est mergée sur `main`, le **localhost** doit pouvoir se réaligner automatiquement ;
-- le serveur local Astro doit tourner depuis un **worktree dédié** ;
-- le dépôt de développement principal ne doit pas être pollué par ce mécanisme ;
-- l’exploitation doit rester simple à diagnostiquer ;
-- l’installation doit pouvoir être **réappliquée proprement** après oubli, incident, ou redémarrage machine.
+- garder `localhost` aligné sur `origin/main` ;
+- séparer correctement le repo canonique de développement et le miroir local.
+
+Il couvre maintenant aussi :
+
+- le **deuxième environnement local de travail** sur `4322` ;
+- le **cockpit local d’exploitation** sur `4310/4311` ;
+- la **supervision NAS** via `stack-health.json` ;
+- l’**instrumentation distante** de staging et prod via `__ops/health.json` ;
+- la **lecture causale** des conteneurs critiques ;
+- la **gradation des alertes** ;
+- la **fraîcheur temporelle** du cache NAS.
---
-## Principes
+## 1. Architecture finale retenue
-L’architecture locale repose sur **trois espaces distincts** :
+L’architecture locale repose désormais sur **cinq espaces / rôles distincts**.
-1. **Repo canonique de développement**
- - Chemin :
- `/Volumes/FunIA/dev/archicratie-edition/site`
- - Usage :
- développement normal, nouvelles fonctionnalités, corrections manuelles, branches de travail, commits, PR.
+### A. Repo canonique de développement
-2. **Worktree localhost dédié**
- - Chemin :
- `~/ops-local/archicratie/localhost-worktree`
- - Branche locale :
- `localhost-sync`
- - Usage :
- exécuter `astro dev` sur une copie locale réalignée automatiquement sur `origin/main`.
-
-3. **Ops local hors repo**
- - Chemin :
- `~/ops-local/archicratie`
- - Usage :
- scripts d’exploitation, logs, état, automatisation LaunchAgent.
-
----
-
-## Pourquoi cette séparation
-
-Il ne faut pas utiliser le dépôt de développement principal comme serveur localhost permanent.
-
-Sinon on mélange :
-
-- travail en cours ;
-- commits non poussés ;
-- essais temporaires ;
-- état réellement publié sur `main`.
-
-Le résultat devient ambigu.
-
-Le **worktree localhost** sert donc de **miroir exécutable de `main`**, tandis que le repo canonique reste l’espace de développement.
-
----
-
-## Vue d’ensemble
-
-Flux logique :
-
-1. `origin/main` avance après merge d’une PR.
-2. Un agent local de sync :
- - fetch `origin/main` depuis le repo canonique ;
- - réaligne le worktree localhost ;
- - exécute `npm ci` si nécessaire ;
- - déclenche le redémarrage de l’agent Astro local.
-3. Un agent Astro local :
- - démarre `astro dev` depuis le worktree localhost ;
- - écoute sur `127.0.0.1:4321`.
-4. Le site local accessible sur `http://127.0.0.1:4321` reflète alors `main`.
-
----
-
-## Référence des chemins
-
-### Repo canonique
+Chemin :
```text
/Volumes/FunIA/dev/archicratie-edition/site
```
-### Worktree localhost
+Usage :
-```text
-/Users/s-funia/ops-local/archicratie/localhost-worktree
-```
-
-### Ops local
-
-```text
-/Users/s-funia/ops-local/archicratie
-```
-
-### Scripts principaux
-
-```text
-/Users/s-funia/ops-local/archicratie/auto-sync-localhost.sh
-/Users/s-funia/ops-local/archicratie/run-astro-localhost.sh
-/Users/s-funia/ops-local/archicratie/doctor-localhost.sh
-/Users/s-funia/ops-local/archicratie/install-localhost-sync.sh
-```
-
-### Logs ops
-
-```text
-/Users/s-funia/ops-local/archicratie/logs/auto-sync-localhost.log
-/Users/s-funia/ops-local/archicratie/logs/astro-localhost.log
-```
-
-### État du dernier sync
-
-```text
-/Users/s-funia/ops-local/archicratie/last-sync.env
-```
-
-### LaunchAgents
-
-```text
-~/Library/LaunchAgents/me.archicratie.localhost-sync.plist
-~/Library/LaunchAgents/me.archicratie.localhost-astro.plist
-```
-
-### Logs launchd
-
-```text
-~/Library/Logs/archicratie-localhost-sync.out.log
-~/Library/Logs/archicratie-localhost-sync.err.log
-~/Library/Logs/archicratie-localhost-astro.out.log
-~/Library/Logs/archicratie-localhost-astro.err.log
-```
-
----
-
-## Architecture retenue
-
-### 1. Le repo canonique
-
-Le repo canonique reste la source locale de travail :
-
-```bash
-cd /Volumes/FunIA/dev/archicratie-edition/site
-```
-
-C’est ici qu’on fait :
-
-- création de branches ;
-- développement ;
-- tests manuels de nouvelles fonctionnalités ;
+- développement normal ;
+- branches ;
- commits ;
-- pushes ;
-- PR.
+- PR ;
+- tests manuels de nouvelles fonctionnalités ;
+- exécution du serveur de dev local sur `4322`.
-### 2. Le worktree localhost
+### B. Worktree miroir local de `main`
-Le localhost ne tourne **pas** depuis le repo canonique.
-Il tourne depuis un worktree dédié :
+Chemin :
-```bash
-cd ~/ops-local/archicratie/localhost-worktree
-git branch --show-current
+```text
+~/ops-local/archicratie/localhost-worktree
```
-Branche attendue :
+Branche locale :
```text
localhost-sync
```
-Ce worktree suit `origin/main` via le script d’auto-sync.
+Usage :
-### 3. Le dossier ops local
+- refléter automatiquement `origin/main` ;
+- servir le **miroir local de vérité** sur `http://127.0.0.1:4321` ;
+- ne jamais servir de lieu de développement de fond.
-Les scripts d’exploitation ne doivent pas être mis dans le repo, ni sur un volume externe soumis à des restrictions de lancement.
-Ils sont placés sous `HOME` :
+### C. Dossier d’ops local hors repo
+
+Chemin :
```text
~/ops-local/archicratie
```
-Motif :
+Usage :
-- compatibilité avec `launchd` ;
-- logs persistants ;
-- visibilité claire ;
-- indépendance du dépôt.
+- scripts ;
+- logs ;
+- fichiers d’état ;
+- cache NAS ;
+- artefacts du cockpit ;
+- LaunchAgents ;
+- PID files.
----
+### D. Cockpit local d’exploitation
-## Pourquoi les scripts et le worktree localhost ne sont plus sur `/Volumes/...`
-
-Deux difficultés réelles ont été rencontrées.
-
-### 1. Exécution LaunchAgent depuis un volume externe
-
-Erreur observée :
+Chemin :
```text
-/bin/bash: /Volumes/FunIA/dev/_ops-local/archicratie/auto-sync-localhost.sh: Operation not permitted
+~/ops-local/archicratie/cockpit
```
-Conclusion :
+Usage :
-- le LaunchAgent peut échouer si le script est lancé depuis un chemin sur volume externe ;
-- le plus robuste est de placer les scripts ops sous `HOME`.
+- UI locale en `http://127.0.0.1:4310` ;
+- API locale en `http://127.0.0.1:4311` ;
+- lecture unifiée des états Git, LaunchAgents, serveurs Astro, staging, prod et stack NAS ;
+- déclenchement d’actions unitaires et composées.
-### 2. Exécution Astro depuis l’ancien localhost sous `/Volumes/...`
+### E. NAS DSM 7.3
-Erreur observée :
+Chemins clés :
```text
-Error: EPERM: operation not permitted, open '/Volumes/FunIA/dev/archicratie-localhost/node_modules/astro/bin/astro.mjs'
+/volume2/docker/archicratie_ops
+/volume2/docker/archicratie_ops/public/stack-health.json
+/volume2/docker/archicratie_ops/generate-stack-health.sh
```
-Conclusion :
+Usage :
-- l’ancien localhost situé sur `/Volumes/FunIA/dev/archicratie-localhost` n’est **plus** une base fiable pour l’exploitation automatisée ;
-- le worktree localhost doit lui aussi être placé sous `HOME`.
-
-Donc :
-
-- **bon choix** : `~/ops-local/archicratie`
-- **mauvais choix** : `/Volumes/FunIA/dev/_ops-local/...`
-- **ancien chemin obsolète** : `/Volumes/FunIA/dev/archicratie-localhost`
+- produire l’état de santé des conteneurs critiques ;
+- exposer un JSON simple rapatrié ensuite sur le Mac ;
+- rester la source de vérité côté Docker Synology.
---
-## Rôle exact du script `auto-sync-localhost.sh`
+## 2. Vue d’ensemble opératoire
-Le script a quatre responsabilités :
+### 2.1 Miroir local `4321`
-1. **Vérifier l’environnement**
- - `git`
- - `bash`
- - `node`
- - `npm`
+Le miroir local `4321` est la **vérité de `main`**.
-2. **Garantir le runtime Node**
- - Node `22.x`
- - npm `10.x`
+Flux :
-3. **Réaligner le worktree localhost**
- - fetch du repo canonique ;
- - s’assurer que le worktree localhost existe ;
- - checkout `localhost-sync` ;
- - reset hard sur `origin/main` ;
- - clean si nécessaire ;
- - `npm ci` si nécessaire.
+1. `origin/main` avance.
+2. Un script d’auto-sync local fetch le repo canonique.
+3. Le worktree `localhost-sync` est réaligné.
+4. `astro dev` tourne depuis `~/ops-local/archicratie/localhost-worktree`.
+5. `http://127.0.0.1:4321` reflète alors `main`.
-4. **Déclencher le redémarrage d’Astro**
- - relancer l’agent LaunchAgent Astro ;
- - laisser `run-astro-localhost.sh` porter le démarrage réel du serveur.
+### 2.2 Atelier local `4322`
----
+Le port `4322` est le **vrai atelier de développement**.
-## Rôle exact du script `run-astro-localhost.sh`
+Flux :
-Ce script a une responsabilité unique :
+1. on travaille dans `/Volumes/FunIA/dev/archicratie-edition/site` ;
+2. le serveur de dev local est lancé manuellement depuis ce repo ;
+3. le cockpit peut le démarrer / l’arrêter ;
+4. un agent local de sync peut réaligner le repo canonique sur `origin/main` quand la branche est `main` et propre.
-- démarrer `astro dev` **depuis le worktree localhost** ;
-- sur `127.0.0.1:4321` ;
-- avec le bon runtime Node ;
-- en produisant les logs Astro dédiés.
+### 2.3 Cockpit `4310/4311`
-Il ne doit jamais démarrer depuis le repo canonique.
+Le cockpit lit en continu :
----
+- l’état Git du repo canonique ;
+- l’état Git du miroir `localhost-sync` ;
+- les réponses HTTP de `4321` et `4322` ;
+- les réponses distantes staging / prod ;
+- les LaunchAgents locaux ;
+- les logs récents ;
+- les événements opératoires ;
+- le cache NAS rapatrié via SSH.
-## Rôle exact du script `doctor-localhost.sh`
+### 2.4 Supervision NAS
-Le doctor sert à produire un **diagnostic lisible et opératoire** sur :
-
-- le runtime Node ;
-- les LaunchAgents ;
-- le worktree localhost ;
-- l’alignement sur `origin/main` ;
-- l’état du serveur Astro ;
-- la réponse HTTP de `localhost:4321` ;
-- les fichiers d’état et les logs.
-
-C’est la **commande de référence** pour savoir si l’installation est saine.
-
----
-
-## Rôle exact du script `install-localhost-sync.sh`
-
-Le script d’installation sert à **reconstruire proprement toute l’architecture locale** :
-
-- écrire ou réécrire `auto-sync-localhost.sh` ;
-- écrire ou réécrire `run-astro-localhost.sh` ;
-- écrire ou réécrire les deux LaunchAgents ;
-- recharger les LaunchAgents ;
-- exécuter `doctor-localhost.sh` en fin d’installation.
-
-C’est la **commande de réinstallation standard** après oubli, casse, dérive ou changement de configuration.
-
----
-
-## Runtime Node requis
-
-Le projet exige :
+Le NAS génère périodiquement :
```json
-{
- "node": ">=22 <23",
- "npm": ">=10 <11"
-}
+stack-health.json
```
-Le runtime retenu localement est donc :
-
-```text
-/opt/homebrew/opt/node@22/bin/node
-/opt/homebrew/opt/node@22/bin/npm
-```
-
-Vérification :
-
-```bash
-"$(brew --prefix node@22)/bin/node" -v
-"$(brew --prefix node@22)/bin/npm" -v
-```
-
-Résultat attendu :
-
-```text
-v22.x
-10.x
-```
+Ce fichier est ensuite rapatrié sur le Mac, mis en cache localement, puis injecté dans le cockpit.
---
-## LaunchAgents
+## 3. Distinction conceptuelle consolidée
-L’architecture finale repose sur **deux LaunchAgents**, avec des responsabilités distinctes.
+### 3.1 `4321 = miroir`
+- automatique ;
+- worktree dédié ;
+- vérité de `main` ;
+- ne pas utiliser pour le développement normal.
-### 1. LaunchAgent sync
+### 3.2 `4322 = atelier`
+- manuel ;
+- repo canonique ;
+- branches et travail vivant ;
+- peut être aligné automatiquement avec `origin/main` **uniquement** si le repo est propre et sur `main`.
-Fichier :
+### 3.3 `4310/4311 = cockpit`
+- interface d’exploitation ;
+- ne sert pas le site éditorial ;
+- supervise, diagnostique, orchestre et documente l’état global.
+
+---
+
+## 4. Scripts locaux stabilisés
+
+Le dossier `~/ops-local/archicratie` contient maintenant un ensemble cohérent de scripts.
+
+### 4.1 Scripts du miroir `4321`
```text
-~/Library/LaunchAgents/me.archicratie.localhost-sync.plist
+~/ops-local/archicratie/auto-sync-localhost.sh
+~/ops-local/archicratie/run-astro-localhost.sh
+~/ops-local/archicratie/doctor-localhost.sh
+~/ops-local/archicratie/install-localhost-sync.sh
+```
+
+Rôles :
+
+- vérifier runtime et dépendances ;
+- fetch `origin/main` ;
+- réaligner le worktree ;
+- exécuter `npm ci` si besoin ;
+- redémarrer le serveur miroir ;
+- produire un diagnostic global.
+
+### 4.2 Scripts de l’atelier `4322`
+
+```text
+~/ops-local/archicratie/auto-sync-dev-main.sh
+~/ops-local/archicratie/run-dev-4322.sh
+~/ops-local/archicratie/stop-dev-4322.sh
+```
+
+Rôles :
+
+- fetch du repo canonique ;
+- réalignement automatique **seulement** si branche `main` + repo propre ;
+- démarrage manuel du dev sur `127.0.0.1:4322` ;
+- arrêt manuel du dev.
+
+Principe important :
+
+- si le repo canonique est sale, le script note `skip_dirty_main` et ne casse rien ;
+- si le repo canonique est déjà aligné, le script note `noop_main_aligned`.
+
+### 4.3 Scripts du cockpit
+
+```text
+~/ops-local/archicratie/run-cockpit.sh
+~/ops-local/archicratie/stop-cockpit.sh
```
Rôle :
-- réaligner périodiquement le worktree localhost sur `origin/main` ;
-- relancer l’agent Astro si nécessaire.
+- démarrer / arrêter le cockpit avec son UI Vite et son API Node ;
+- standardiser l’exploitation pour éviter les commandes manuelles dispersées.
-### 2. LaunchAgent Astro
-
-Fichier :
+### 4.4 Script de rapatriement NAS
```text
-~/Library/LaunchAgents/me.archicratie.localhost-astro.plist
+~/ops-local/archicratie/pull-stack-health.sh
```
Rôle :
-- exécuter `run-astro-localhost.sh` ;
-- lancer `astro dev` depuis le worktree localhost.
-
----
-
-## Contenu de référence du LaunchAgent sync
-
-```xml
-
-
-
-
- Label
- me.archicratie.localhost-sync
-
- ProgramArguments
-
- /bin/bash
- /Users/s-funia/ops-local/archicratie/auto-sync-localhost.sh
-
-
- RunAtLoad
-
-
- StartInterval
- 60
-
- WorkingDirectory
- /Users/s-funia
-
- EnvironmentVariables
-
- HOME
- /Users/s-funia
- PATH
- /opt/homebrew/opt/node@22/bin:/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
- LANG
- fr_FR.UTF-8
- LC_ALL
- fr_FR.UTF-8
-
-
- StandardOutPath
- /Users/s-funia/Library/Logs/archicratie-localhost-sync.out.log
-
- StandardErrorPath
- /Users/s-funia/Library/Logs/archicratie-localhost-sync.err.log
-
-
-```
-
----
-
-## Contenu de référence du LaunchAgent Astro
-
-```xml
-
-
-
-
- Label
- me.archicratie.localhost-astro
-
- ProgramArguments
-
- /bin/bash
- /Users/s-funia/ops-local/archicratie/run-astro-localhost.sh
-
-
- RunAtLoad
-
-
- KeepAlive
-
-
- WorkingDirectory
- /Users/s-funia
-
- EnvironmentVariables
-
- HOME
- /Users/s-funia
- PATH
- /opt/homebrew/opt/node@22/bin:/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
- LANG
- fr_FR.UTF-8
- LC_ALL
- fr_FR.UTF-8
-
-
- StandardOutPath
- /Users/s-funia/Library/Logs/archicratie-localhost-astro.out.log
-
- StandardErrorPath
- /Users/s-funia/Library/Logs/archicratie-localhost-astro.err.log
-
-
-```
-
----
-
-## Installation / réinstallation des LaunchAgents
-
-### Vérifier les plists
-
-```bash
-plutil -lint ~/Library/LaunchAgents/me.archicratie.localhost-sync.plist
-plutil -lint ~/Library/LaunchAgents/me.archicratie.localhost-astro.plist
-```
-
-### Recharger proprement
-
-```bash
-launchctl bootout "gui/$(id -u)" ~/Library/LaunchAgents/me.archicratie.localhost-sync.plist 2>/dev/null || true
-launchctl bootout "gui/$(id -u)" ~/Library/LaunchAgents/me.archicratie.localhost-astro.plist 2>/dev/null || true
-
-launchctl bootstrap "gui/$(id -u)" ~/Library/LaunchAgents/me.archicratie.localhost-sync.plist
-launchctl bootstrap "gui/$(id -u)" ~/Library/LaunchAgents/me.archicratie.localhost-astro.plist
-
-launchctl kickstart -k "gui/$(id -u)/me.archicratie.localhost-sync"
-launchctl kickstart -k "gui/$(id -u)/me.archicratie.localhost-astro"
-```
-
-### Inspecter l’état
-
-```bash
-launchctl print "gui/$(id -u)/me.archicratie.localhost-sync" | sed -n '1,160p'
-launchctl print "gui/$(id -u)/me.archicratie.localhost-astro" | sed -n '1,160p'
-```
-
-État attendu :
-
-- pas d’erreur `Operation not permitted` ;
-- pas d’erreur `EX_CONFIG` ;
-- le LaunchAgent sync tourne périodiquement ;
-- le LaunchAgent Astro peut être vu comme `running`, `spawn scheduled` ou `not running` selon le moment d’observation ;
-- l’état réel de vérité reste le **doctor** et la **réponse HTTP sur 4321**.
-
----
-
-## Vérifications de bon fonctionnement
-
-### 1. Vérifier que le worktree suit bien `origin/main`
-
-```bash
-git -C ~/ops-local/archicratie/localhost-worktree rev-parse HEAD
-git -C /Volumes/FunIA/dev/archicratie-edition/site ls-remote origin refs/heads/main
-```
-
-Les deux SHA doivent être identiques.
-
-### 2. Vérifier la branche du worktree localhost
-
-```bash
-git -C ~/ops-local/archicratie/localhost-worktree branch --show-current
-```
-
-Résultat attendu :
+- ouvrir une session SSH vers le NAS ;
+- lire le fichier `/volume2/docker/archicratie_ops/public/stack-health.json` ;
+- le recopier localement dans :
```text
-localhost-sync
+~/ops-local/archicratie/cache/stack-health.json
```
-### 3. Vérifier le dernier état sync
+- écrire un log d’exploitation.
-```bash
-cat ~/ops-local/archicratie/last-sync.env
-```
+---
-Exemple attendu :
+## 5. Fichiers d’état et journaux locaux
+
+### 5.1 Dossier ops
```text
-LAST_SYNC_AT="2026-03-16 20:29:43"
-LAST_SYNC_SHA="a1bfbf4405d1342c635caec5b219c14b83b36d5e"
-LAST_SYNC_STATUS="noop"
+~/ops-local/archicratie
```
-### 4. Vérifier les logs de sync
+Contient notamment :
+
+```text
+astro-localhost.pid
+dev-4322.pid
+dev-main-4322.pid
+cockpit.pid
+last-sync.env
+last-dev-main-sync.env
+cockpit-events.jsonl
+```
+
+### 5.2 Logs utiles
+
+```text
+~/ops-local/archicratie/logs/auto-sync-localhost.log
+~/ops-local/archicratie/logs/astro-localhost.log
+~/ops-local/archicratie/logs/pull-stack-health.log
+```
+
+Et côté LaunchAgents :
+
+```text
+~/Library/Logs/archicratie-pull-stack-health.out.log
+~/Library/Logs/archicratie-pull-stack-health.err.log
+```
+
+### 5.3 Cache NAS local
+
+```text
+~/ops-local/archicratie/cache/stack-health.json
+```
+
+Ce fichier est devenu un **artefact stratégique** :
+
+- il permet au cockpit de lire la stack NAS sans dépendre d’un appel live direct au NAS ;
+- il permet une supervision plus robuste ;
+- il rend possible une **alerte de fraîcheur**.
+
+---
+
+## 6. LaunchAgents et automatisation locale
+
+L’automatisation locale repose désormais sur **trois moteurs indépendants**.
+
+### 6.1 Sync miroir
+
+Rôle :
+
+- tourner périodiquement ;
+- réaligner le worktree `localhost-sync` ;
+- maintenir `4321` fidèle à `origin/main`.
+
+Cadence constatée :
+
+- environ **toutes les 60 secondes**.
+
+### 6.2 Sync dev-main
+
+Rôle :
+
+- surveiller le repo canonique ;
+- le fetch régulièrement ;
+- le réaligner si, et seulement si :
+ - la branche est `main` ;
+ - l’arbre de travail est propre.
+
+Cadence constatée :
+
+- environ **toutes les 75 secondes**.
+
+### 6.3 Pull stack-health NAS
+
+LaunchAgent :
+
+```text
+me.archicratie.pull-stack-health
+```
+
+Rôle :
+
+- rapatrier le cache NAS via SSH ;
+- mettre à jour le fichier local ;
+- nourrir la supervision causale.
+
+Cadence constatée :
+
+- environ **toutes les 60 secondes**.
+
+Pilotage manuel du LaunchAgent :
+
+### Désactiver temporairement le rapatriement local
```bash
-tail -n 120 ~/ops-local/archicratie/logs/auto-sync-localhost.log
+launchctl bootout "gui/$(id -u)" ~/Library/LaunchAgents/me.archicratie.pull-stack-health.plist
```
-### 5. Vérifier les logs Astro
+### Réactiver le rapatriement local
```bash
-tail -n 120 ~/ops-local/archicratie/logs/astro-localhost.log
+launchctl bootstrap "gui/$(id -u)" ~/Library/LaunchAgents/me.archicratie.pull-stack-health.plist
```
-### 6. Vérifier qu’Astro écoute bien sur 4321
+### Interprétation :
+
+- bootout retire volontairement l’agent du domaine utilisateur courant ;
+
+- bootstrap recharge ce même agent dans le domaine utilisateur courant ;
+
+- en fonctionnement nominal, cet agent doit rester actif ;
+
+- après bootstrap, le fichier ~/ops-local/archicratie/cache/stack-health.json doit recommencer à être rafraîchi ;
+
+- dans le cockpit, launchAgents.pullStackHealth.loaded doit redevenir true.
+
+### Point d’attention :
+
+- bootout sert au test contrôlé, au diagnostic ciblé ou à une maintenance volontaire ;
+
+- bootout ne constitue pas une commande d’exploitation normale ;
+
+- après tout usage de bootout, il faut restaurer l’état nominal avec bootstrap.
+---
+
+## 7. SSH Mac ↔ NAS
+
+### 7.1 Situation initiale
+
+La copie du fichier NAS vers le Mac a rencontré plusieurs problèmes :
+
+- nom d’hôte local non résolu ;
+- port SSH non standard ;
+- `scp` indisponible / mal supporté dans la configuration DSM observée ;
+- authentification par mot de passe trop lourde.
+
+### 7.2 Paramètres réels retenus
+
+- IP locale NAS : `192.168.1.20`
+- port SSH : `44322`
+- utilisateur : `archicratia`
+
+### 7.3 Méthode de copie finalement retenue
+
+Plutôt que `scp`, la méthode robuste retenue a été :
+
+- `ssh ... 'cat /chemin/fichier' > fichier_local`
+
+Cela a permis :
+
+- de s’affranchir du problème `scp subsystem request failed`;
+- de garder un protocole simple ;
+- d’intégrer facilement le mécanisme dans `pull-stack-health.sh`.
+
+### 7.4 Authentification par clé
+
+Une clé dédiée a été générée côté Mac et installée côté NAS.
+Le point bloquant n’était pas la clé elle-même, mais les permissions du `HOME` du compte Synology.
+
+Correctif déterminant côté NAS :
+
+- retirer les permissions `go-w` sur le `HOME` ;
+- conserver :
+ - `~/.ssh` en `700`
+ - `authorized_keys` en `600`
+
+Après cette correction :
+
+- l’authentification par clé est devenue fonctionnelle ;
+- le LaunchAgent de pull a pu fonctionner sans mot de passe.
+
+### 7.5 Alias SSH local
+
+Une entrée `~/.ssh/config` a ensuite permis d’utiliser un alias opératoire du type :
+
+```text
+transhands-ops
+```
+
+ce qui a simplifié les scripts de pull.
+
+---
+
+## 8. Supervision NAS : génération de `stack-health.json`
+
+### 8.1 Script côté NAS
+
+Fichier :
+
+```text
+/volume2/docker/archicratie_ops/generate-stack-health.sh
+```
+
+Rôle :
+
+- inspecter plusieurs conteneurs Docker clés ;
+- écrire un JSON simple ;
+- exposer cet état dans `public/stack-health.json`.
+
+### 8.2 Exécution et droits
+
+Constat clé :
+
+- sans privilèges suffisants, le script peut échouer ou signaler `docker_unavailable` ;
+- via le planificateur DSM 7.3, exécuté correctement, le fichier est bien régénéré.
+
+Le comportement retenu :
+
+- exécution via tâche planifiée DSM avec les droits nécessaires ;
+- sortie du JSON écrite dans :
+
+```text
+/volume2/docker/archicratie_ops/public/stack-health.json
+```
+
+### 8.3 Problème rencontré
+
+Un comportement bloquant est apparu :
+
+- fichier existant possédé par `root` ;
+- exécution interactive sans privilège ;
+- `mv` demandant confirmation ou échouant ;
+- `chmod` impossible.
+
+Conclusion :
+
+- la génération doit être pensée comme **opération root planifiée**, pas comme simple commande utilisateur interactive.
+
+### 8.4 Horodatage local
+
+Le champ `generatedAt` a d’abord été produit en UTC (`...Z`), ce qui déplaisait à l’exploitation locale.
+
+Le script a été ajusté pour produire désormais une heure locale explicite, par exemple :
+
+```text
+2026-03-18T13:26:32+01:00
+```
+
+---
+
+## 9. Périmètre de la stack NAS désormais supervisée
+
+Le JSON NAS couvre désormais au moins les conteneurs suivants :
+
+- `edge-traefik`
+- `auth-authelia`
+- `auth-lldap`
+- `auth-redis`
+- `archicratie-web-blue`
+- `archicratie-web-green`
+- `gitea-old-2026-02-09-105211`
+- `gitea-act-runner`
+
+Le cockpit les remappe en :
+
+- `edgeTraefik`
+- `authelia`
+- `lldap`
+- `authRedis`
+- `webBlue`
+- `webGreen`
+- `gitea`
+- `giteaActRunner`
+
+---
+
+## 10. Déploiement staging / prod : instrumentation `__ops/health.json`
+
+### 10.1 Problème initial
+
+Le cockpit savait tester :
+
+- le HTTP public staging ;
+- le HTTP public prod.
+
+Mais il ne savait pas lire proprement **le build réellement servi** par `web_blue` et `web_green`.
+
+### 10.2 Cause
+
+Les conteneurs Nginx servaient bien le site, mais pas de manifeste d’état applicatif dédié.
+Les tentatives d’accès à :
+
+```text
+/__ops/health.json
+```
+
+retournaient d’abord :
+
+- `404` côté conteneurs ;
+- puis `302` via Authelia côté domaine public.
+
+### 10.3 Solution produite
+
+Deux modifications complémentaires ont été apportées.
+
+#### A. Côté build / déploiement
+
+Un script :
+
+```text
+scripts/write-ops-health.mjs
+```
+
+écrit désormais un manifeste :
+
+```text
+public/__ops/health.json
+```
+
+avec des champs du type :
+
+- `service`
+- `env`
+- `upstream`
+- `buildSha`
+- `builtAt`
+
+Ce manifeste est produit pendant le workflow de déploiement pour :
+
+- staging / `web_blue`
+- prod / `web_green`
+
+#### B. Côté edge / Traefik / Authelia
+
+Le routage a été ajusté pour que :
+
+```text
+https://staging.../__ops/health.json
+https://archicratie.../__ops/health.json
+```
+
+restent **lisibles sans redirection Authelia**, tout en laissant le reste du site inchangé.
+
+### 10.4 Correction de l’heure `builtAt`
+
+Comme pour `generatedAt`, le champ `builtAt` a été réaligné sur l’heure locale France via :
+
+```text
+TZ=Europe/Paris
+```
+
+dans le workflow de déploiement.
+
+---
+
+## 11. Cockpit local d’exploitation
+
+### 11.1 Emplacement
+
+```text
+~/ops-local/archicratie/cockpit
+```
+
+### 11.2 Ports
+
+- UI : `http://127.0.0.1:4310`
+- API : `http://127.0.0.1:4311`
+
+### 11.3 Démarrage
+
+Le cockpit est lancé via :
```bash
-lsof -nP -iTCP:4321 -sTCP:LISTEN
+~/ops-local/archicratie/run-cockpit.sh
```
-### 7. Vérifier le processus Astro exact
+et arrêté via :
```bash
-PID="$(lsof -tiTCP:4321 -sTCP:LISTEN | head -n 1)"
-ps -p "$PID" -o pid=,command=
-lsof -a -p "$PID" -d cwd
+~/ops-local/archicratie/stop-cockpit.sh
+```
+### 11.3 bis Mode opératoire recommandé
+
+Le pilotage normal du cockpit doit se faire via les scripts bash d’exploitation :
+
+```bash
+~/ops-local/archicratie/run-cockpit.sh
+~/ops-local/archicratie/stop-cockpit.sh
+```
+et non via un lancement manuel dispersé du type npm run dev ou node server/index.mjs exécuté séparément.
+
+### Justification :
+
+- run-cockpit.sh démarre correctement l’ensemble du cockpit ;
+
+- l’UI et l’API sont lancées ensemble dans le bon ordre logique ;
+
+- l’exploitation reste homogène et rejouable ;
+
+- stop-cockpit.sh fournit un arrêt propre, cohérent avec les PID et l’usage opératoire retenu.
+
+### Autrement dit :
+
+- 4310 = façade visuelle ;
+
+- 4311 = moteur d’état ;
+
+- les scripts bash = méthode normale d’exploitation.
+
+### 11.4 Architecture interne
+
+- UI React / Vite
+- API Node locale
+- lecture des états système via scripts, logs, fichiers JSON et `launchctl`
+
+### 11.4 bis Source de vérité interne
+
+Le cockpit ne repose pas sur une simple interface décorative.
+
+La source de vérité opérationnelle est l’API locale :
+
+```text
+http://127.0.0.1:4311/api/status
```
-Attendu :
+### L’UI affichée sur :
-- commande contenant `astro dev`
-- cwd = `~/ops-local/archicratie/localhost-worktree`
+```text
+http://127.0.0.1:4310
+```
-### 8. Vérifier le contenu servi
+n’est qu’une projection visuelle de cet état consolidé.
+### Conséquence pratique :
+
+- si un doute existe sur l’exactitude de l’interface, il faut interroger directement GET /api/status ;
+
+- c’est cette réponse JSON qui fait foi pour :
+
+- l’état Git ;
+
+ - les LaunchAgents ;
+
+ - les serveurs 4321 et 4322 ;
+
+ - staging / prod ;
+
+ - la stack NAS ;
+
+ - les alertes ;
+
+ - les événements.
+
+Cette distinction est importante : l’UI montre, l’API atteste.
+
+### 11.5 Endpoints utiles
+
+L’API du cockpit expose au moins :
+
+- `GET /api/health`
+- `GET /api/status`
+- `POST /api/action`
+- `POST /api/event`
+- `POST /api/events/clear`
+
+### Remarques d’exploitation :
+
+- `GET /api/health` vérifie simplement que l’API locale répond ;
+- `GET /api/status` est l’endpoint central de supervision ;
+- `POST /api/action` déclenche une action opératoire unitaire ;
+- `POST /api/event` permet d’écrire un événement dans l’historique local ;
+- `POST /api/events/clear` vide explicitement l’historique des événements.
+
+En cas de diagnostic, `GET /api/status` doit être considéré comme la lecture machine de référence.
+---
+
+## 12. Ce que le cockpit sait lire
+
+Le cockpit agrège désormais :
+
+### 12.1 Git
+- branche du repo canonique ;
+- SHA local ;
+- `origin/main` ;
+- état de propreté (`clean`, `modified`, `untracked`, etc.).
+
+### 12.2 Miroir 4321
+- port HTTP ;
+- PID ;
+- cwd du process ;
+- dernier sync ;
+- SHA du miroir.
+
+### 12.3 Atelier 4322
+- port HTTP ;
+- PID ;
+- branche du repo canonique ;
+- dernier sync dev-main ;
+- statut `noop_main_aligned` ou `skip_dirty_main`.
+
+### 12.4 Staging et prod
+- HTTP public ;
+- lecture de `__ops/health.json` ;
+- `env` ;
+- `upstream` ;
+- `buildSha` ;
+- `builtAt`.
+
+### 12.5 LaunchAgents
+- état chargé / non chargé ;
+- exit code ;
+- compteur de runs.
+
+### 12.6 Logs récents
+- sync miroir ;
+- sync dev-main ;
+- Astro miroir ;
+- Astro dev ;
+- pull stack-health.
+
+### 12.7 Stack NAS
+- chemin du cache local ;
+- `generatedAt` ;
+- `dockerAccessMode` ;
+- services détaillés.
+
+---
+
+## 13. Centre d’alertes et hiérarchie de gravité
+
+La supervision n’est pas seulement binaire ; elle est maintenant **qualifiée**.
+
+### 13.1 Niveau `info`
Exemple :
-```bash
-curl -s http://127.0.0.1:4321/archicrat-ia/prologue/ | grep -n "taxe Zucman"
-```
+- `giteaActRunner` indisponible
-Le texte renvoyé doit correspondre à la version attendue sur `main`.
+Justification :
-### 9. Vérifier globalement toute l’installation
+- cela dégrade la chaîne de pipeline / déploiement ;
+- cela ne casse pas automatiquement le service public.
+
+### 13.2 Niveau `warn`
+Exemples :
+
+- `authRedis` indisponible
+- `gitea` indisponible
+
+Justification :
+
+- impact opératoire réel ;
+- système encore potentiellement utilisable ;
+- attention nécessaire.
+
+### 13.3 Niveau `critical`
+Exemples :
+
+- cache NAS trop ancien
+- services cœur réellement cassés
+- rupture forte de la lecture de confiance
+
+### 13.4 Exemple d’alerte informative
+
+Quand `gitea-act-runner` a été arrêté, le cockpit a correctement affiché :
+
+- une alerte **Info**
+- sans transformer à tort l’état global du service public en catastrophe.
+
+### 13.5 Exemple d’alerte `warn`
+
+Quand `auth-redis` a été arrêté, le cockpit a affiché :
+
+- `Redis d’authentification indisponible`
+- niveau **Attention**
+- avec la stack causale montrant le conteneur `exited / unhealthy`.
+
+### 13.6 Exemple d’alerte `warn` sur la forge
+
+Quand Gitea a été arrêté, le cockpit a affiché :
+
+- `Forge Gitea indisponible`
+- niveau **Attention**
+- avec staging / prod encore sains.
+
+---
+
+## 14. Fraîcheur du cache NAS
+
+### 14.1 Problème conceptuel identifié
+
+Même avec un bon `stack-health.json`, le cockpit pouvait mentir par omission si :
+
+- le scheduler DSM était cassé ;
+- ou le pull SSH Mac était stoppé ;
+- ou le cache local restait figé.
+
+### 14.2 Solution
+
+Une lecture d’âge du cache a été ajoutée dans `buildAlerts(...)`.
+
+Règles de seuil :
+
+- pas d’alerte si le cache est frais ;
+- **warn** au-delà d’environ 3 minutes ;
+- **critical** au-delà d’environ 6 minutes.
+
+### 14.3 Test réel effectué
+
+Le LaunchAgent `pull-stack-health` a été volontairement stoppé.
+Résultats observés :
+
+- dans une fenêtre intermédiaire : le cockpit a bien produit une alerte **Attention** ;
+- au-delà du seuil haut : le cockpit a bien produit **Cache NAS trop ancien** avec niveau **Critique**.
+
+Les commandes utilisées pour le test contrôlé de vieillissement du cache sont les suivantes.
+
+### Interrompre volontairement le rapatriement local
```bash
-~/ops-local/archicratie/doctor-localhost.sh
+launchctl bootout "gui/$(id -u)" ~/Library/LaunchAgents/me.archicratie.pull-stack-health.plist
```
+### Restaurer le fonctionnement nominal
-Verdict attendu :
+```bash
+launchctl bootstrap "gui/$(id -u)" ~/Library/LaunchAgents/me.archicratie.pull-stack-health.plist
+```
+### Lecture correcte du test
+
+### Pendant ce test :
+
+- launchAgents.pullStackHealth.loaded peut passer à false ;
+
+- ce n’est pas une panne mystérieuse du cockpit ;
+
+- c’est l’effet recherché, puisque le LaunchAgent a été volontairement déchargé ;
+
+- le fichier ~/ops-local/archicratie/cache/stack-health.json reste alors figé à sa dernière valeur connue.
+
+### Point d’interprétation essentiel
+
+Le cockpit peut encore afficher une stack NAS apparemment OK tout en levant une alerte de fraîcheur warn puis critical.
+
+Ce comportement est correct.
+
+Il signifie :
+
+- que le dernier cache disponible décrivait un état sain ;
+
+- mais que ce cache est désormais trop ancien pour constituer une vérité d’exploitation suffisamment fiable.
+
+Autrement dit, le cockpit ne dit pas :
+
+“la stack est saine avec certitude”
+
+Il dit :
+
+“le dernier état connu de la stack était sain, mais cette information est devenue trop ancienne pour être pleinement digne de confiance”.
+
+### Conclusion :
+
+- la supervision ne dépend plus uniquement de l’existence du cache ;
+- elle qualifie aussi sa **fraîcheur temporelle**.
+
+---
+
+## 15. Actions opératoires disponibles dans le cockpit
+
+Le cockpit fournit maintenant :
+
+### 15.1 Actions composées
+- réparer le miroir ;
+- préparer l’atelier dev.
+
+### 15.1 bis Composition réelle
+
+Les actions composées ne sont pas des actions magiques indépendantes.
+Elles orchestrent plusieurs actions unitaires dans un ordre déterminé.
+
+Composition retenue :
+
+- **réparer le miroir** :
+ 1. exécuter `doctor-localhost`
+ 2. exécuter le sync miroir `4321`
+
+- **préparer l’atelier dev** :
+ 1. exécuter le sync `dev-main`
+ 2. démarrer le serveur local `4322`
+
+Règle d’exécution :
+
+- si la première étape échoue, la chaîne s’arrête ;
+- la deuxième étape n’est pas lancée si la première n’est pas valide.
+
+Règle de traçabilité :
+
+- les actions composées écrivent un **événement composite** synthétique dans l’historique ;
+- les sous-actions internes peuvent être exécutées en mode silencieux pour éviter un doublonnage inutile dans le journal.
+
+### 15.2 Actions unitaires
+- lancer `doctor-localhost` ;
+- forcer le sync miroir `4321` ;
+- forcer le sync dev-main ;
+- démarrer `4322` ;
+- arrêter `4322`.
+
+### 15.2 bis Périmètre et garde-fous
+
+Les actions unitaires du cockpit pilotent avant tout **l’exploitation locale sur le Mac**.
+
+Elles agissent notamment sur :
+
+- les scripts d’ops sous `~/ops-local/archicratie` ;
+- le miroir local `4321` ;
+- le serveur de dev local `4322` ;
+- les états et journaux locaux.
+
+Elles ne constituent pas un mécanisme de mutation directe de staging ou prod.
+Staging et prod sont principalement :
+
+- lus ;
+- sondés ;
+- comparés ;
+- diagnostiqués.
+
+Points importants :
+
+- `démarrer 4322` et `arrêter 4322` n’affectent que l’atelier local ;
+- `sync dev-main` ne doit pas casser le repo canonique : le réalignement automatique n’a lieu que si le repo est sur `main` et propre ;
+- certaines actions sont volontairement désactivées dans l’UI selon l’état courant ;
+- une confirmation utilisateur peut être demandée avant déclenchement.
+
+### 15.3 Journal d’événements
+
+Les actions déclenchées depuis le cockpit peuvent être journalisées dans :
```text
-✅ aucun problème bloquant détecté
-✅ aucun avertissement
+~/ops-local/archicratie/cockpit-events.jsonl
```
+### Ce journal peut contenir :
+
+- des événements unitaires ;
+
+- des événements composites.
+
+### Chaque événement conserve au moins des informations du type :
+
+- horodatage ;
+
+- type d’action ;
+
+- libellé ;
+
+- succès / échec ;
+
+- code de sortie ;
+
+- durée ;
+
+- script concerné ;
+
+- résumé opératoire.
+
+### Ce journal constitue une mémoire locale d’exploitation utile pour :
+
+- relire les actions effectuées ;
+
+- comprendre un enchaînement opératoire ;
+
+- garder une trace minimale des gestes réalisés depuis le cockpit.
+
+### Le cockpit sait aussi vider cet historique via l’API dédiée :
+
+- POST /api/events/clear
+
+### Point important :
+
+- vider l’historique des événements ne supprime pas les logs techniques ;
+
+- cela ne modifie ni les LaunchAgents, ni les PID, ni les fichiers d’état, ni les repos Git ;
+
+- cela efface uniquement la mémoire locale des événements cockpit.
---
-## Interprétation des statuts
+## 16. Règles d’exploitation consolidées
-### `status=updated`
+### 16.1 Règle d’or
+Le repo canonique n’est pas le miroir local.
-Le worktree localhost a été réaligné sur un nouveau SHA ou nettoyé, puis l’agent Astro a été relancé.
+### 16.2 Règle miroir
+`4321` doit raconter la vérité de `main`, pas l’état du travail en cours.
-### `status=noop`
+### 16.3 Règle atelier
+`4322` est le lieu du travail vivant, du test manuel et des branches.
-Le worktree localhost était déjà aligné et le serveur local tournait correctement.
-Aucun changement de contenu n’était nécessaire.
+### 16.4 Règle d’ops
+Les scripts d’ops et LaunchAgents vivent sous `HOME`, pas sur `/Volumes/...`.
+
+### 16.5 Règle de vérité
+Un état n’est réellement fiable que si l’on connaît :
+
+- le SHA ;
+- le cwd ;
+- le process réel ;
+- la fraîcheur de la donnée ;
+- la causalité de la panne.
---
-## Usage normal au quotidien
+## 17. Commandes de contrôle essentielles
-### Cas A — travail produit / publié
-
-Quand on veut simplement consulter localement l’état réel de `main` :
-
-- on laisse tourner les LaunchAgents ;
-- on consulte `http://127.0.0.1:4321`.
-
-Dans ce mode, le localhost doit être considéré comme :
-
-**un miroir local exécutable de `origin/main`**.
-
-### Cas B — développement de nouvelles fonctionnalités
-
-Quand on veut modifier du code, tester un comportement, créer une branche :
-
-```bash
-cd /Volumes/FunIA/dev/archicratie-edition/site
-git switch -c feat/ma-branche
-npm run dev
-```
-
-Dans ce mode, on travaille dans le **repo canonique**, pas dans le worktree localhost.
-
----
-
-## Règle d’or
-
-Il existe désormais **deux usages distincts** :
-
-### 1. Voir ce qui est réellement sur `main`
-
-Utiliser :
-
-```text
-http://127.0.0.1:4321
-```
-
-qui sert depuis :
-
-```text
-~/ops-local/archicratie/localhost-worktree
-```
-
-### 2. Développer / tester du neuf
-
-Utiliser :
-
-```text
-/Volumes/FunIA/dev/archicratie-edition/site
-```
-
-avec branche locale, commandes manuelles, tests de dev.
-
----
-
-## Ce qu’il ne faut pas faire
-
-### Ne pas développer dans le worktree localhost
-
-Le worktree localhost est un miroir piloté.
-Il peut être reset automatiquement.
-
-Donc :
-
-- pas de commits dedans ;
-- pas de modifications de fond dedans ;
-- pas de dev feature dedans.
-
-### Ne pas utiliser le repo canonique comme miroir auto-sync
-
-Sinon on mélange :
-
-- environnement de dev ;
-- état publié ;
-- serveur local permanent.
-
-### Ne pas conserver l’ancien chemin localhost comme référence
-
-Le chemin :
-
-```text
-/Volumes/FunIA/dev/archicratie-localhost
-```
-
-n’est plus la référence opérationnelle.
-Il est obsolète pour cette architecture.
-
-### Ne pas déplacer les scripts ops sur un volume externe
-
-Sinon `launchd` peut échouer avec :
-
-```text
-Operation not permitted
-```
-
----
-
-## Procédure de redémarrage machine
-
-Après reboot, le comportement attendu est :
-
-1. les LaunchAgents se rechargent ;
-2. le script d’auto-sync s’exécute ;
-3. le worktree localhost est réaligné ;
-4. Astro redémarre sur `127.0.0.1:4321`.
-
-### Vérification rapide après reboot
-
-```bash
-launchctl print "gui/$(id -u)/me.archicratie.localhost-sync" | sed -n '1,120p'
-launchctl print "gui/$(id -u)/me.archicratie.localhost-astro" | sed -n '1,120p'
-tail -n 80 ~/ops-local/archicratie/logs/auto-sync-localhost.log
-lsof -nP -iTCP:4321 -sTCP:LISTEN
-```
-
----
-
-## Procédure de secours manuelle
-
-### Forcer un resync
-
-```bash
-~/ops-local/archicratie/auto-sync-localhost.sh
-```
-
-### Forcer un diagnostic complet
-
-```bash
-~/ops-local/archicratie/doctor-localhost.sh
-```
-
-### Réinstaller tout le dispositif
-
-```bash
-~/ops-local/archicratie/install-localhost-sync.sh
-```
-
----
-
-## Symptômes fréquents et diagnostic
-
-### Symptôme 1 — localhost ne montre pas les dernières modifs
-
-Vérifier :
-
-```bash
-git -C ~/ops-local/archicratie/localhost-worktree rev-parse HEAD
-git -C /Volumes/FunIA/dev/archicratie-edition/site ls-remote origin refs/heads/main
-```
-
-Si les SHA diffèrent :
-- le sync n’a pas tourné ;
-- ou le LaunchAgent sync ne s’exécute pas.
-
-### Symptôme 2 — SHA bon, mais contenu web pas à jour
-
-Vérifier :
-
-```bash
-lsof -nP -iTCP:4321 -sTCP:LISTEN
-PID="$(lsof -tiTCP:4321 -sTCP:LISTEN | head -n 1)"
-ps -p "$PID" -o pid=,command=
-lsof -a -p "$PID" -d cwd
-```
-
-Cause probable :
-- Astro tourne depuis le mauvais dossier ;
-- ou un ancien `astro dev` manuel tourne encore ailleurs.
-
-### Symptôme 3 — LaunchAgent présent, mais rien ne tourne
-
-Vérifier :
-
-```bash
-tail -n 80 ~/Library/Logs/archicratie-localhost-sync.err.log
-tail -n 80 ~/Library/Logs/archicratie-localhost-astro.err.log
-tail -n 120 ~/ops-local/archicratie/logs/auto-sync-localhost.log
-tail -n 120 ~/ops-local/archicratie/logs/astro-localhost.log
-```
-
-Causes possibles :
-- mauvais PATH ;
-- Node 22 absent ;
-- script non exécutable ;
-- erreur dans un plist ;
-- ancien chemin encore référencé ;
-- worktree localhost absent ;
-- `node_modules` non installés dans le worktree.
-
-### Symptôme 4 — erreur `Operation not permitted`
-
-Cause probable :
-- script ops ou agent lancé depuis un chemin sous `/Volumes/...`.
-
-Résolution :
-- conserver les scripts sous `~/ops-local/archicratie`.
-
-### Symptôme 5 — erreur `EBADENGINE`
-
-Cause probable :
-- Node 23 utilisé à la place de Node 22.
-
-Résolution :
-- forcer PATH avec `node@22` dans les LaunchAgents et dans les scripts.
-
-### Symptôme 6 — erreur `EPERM` sur `astro.mjs`
-
-Cause probable :
-- Astro essaie encore de démarrer depuis l’ancien emplacement localhost ;
-- ou une incohérence persiste dans les chemins des scripts.
-
-Résolution :
-- vérifier que **tous** les scripts pointent vers `~/ops-local/archicratie/localhost-worktree` ;
-- vérifier que `doctor-localhost.sh` confirme le bon cwd ;
-- réinstaller via `install-localhost-sync.sh`.
-
----
-
-## Commandes de contrôle essentielles
-
-### État Git
+### 17.1 SHA et branches
```bash
git -C ~/ops-local/archicratie/localhost-worktree rev-parse HEAD
+git -C /Volumes/FunIA/dev/archicratie-edition/site rev-parse HEAD
git -C /Volumes/FunIA/dev/archicratie-edition/site ls-remote origin refs/heads/main
git -C ~/ops-local/archicratie/localhost-worktree branch --show-current
```
-### État LaunchAgents
+### 17.2 Cockpit
```bash
-launchctl print "gui/$(id -u)/me.archicratie.localhost-sync" | sed -n '1,160p'
-launchctl print "gui/$(id -u)/me.archicratie.localhost-astro" | sed -n '1,160p'
+~/ops-local/archicratie/run-cockpit.sh
+~/ops-local/archicratie/stop-cockpit.sh
+curl -s http://127.0.0.1:4311/api/status | python3 -m json.tool | sed -n '1,260p'
```
-### État logs
+### 17.3 Dev 4322
```bash
-tail -n 120 ~/ops-local/archicratie/logs/auto-sync-localhost.log
-tail -n 120 ~/ops-local/archicratie/logs/astro-localhost.log
-tail -n 80 ~/Library/Logs/archicratie-localhost-sync.err.log
-tail -n 80 ~/Library/Logs/archicratie-localhost-astro.err.log
+~/ops-local/archicratie/run-dev-4322.sh
+~/ops-local/archicratie/stop-dev-4322.sh
```
-### État serveur
+### 17.4 Pull stack-health
```bash
-lsof -nP -iTCP:4321 -sTCP:LISTEN
-PID="$(lsof -tiTCP:4321 -sTCP:LISTEN | head -n 1)"
-ps -p "$PID" -o pid=,command=
-lsof -a -p "$PID" -d cwd
-curl -I -s http://127.0.0.1:4321/ | head -n 5
+~/ops-local/archicratie/pull-stack-health.sh
+launchctl print "gui/$(id -u)/me.archicratie.pull-stack-health" | sed -n '1,120p'
+cat ~/ops-local/archicratie/cache/stack-health.json
+tail -n 20 ~/ops-local/archicratie/logs/pull-stack-health.log
```
-### Vérification contenu
+# Commandes complémentaires de test contrôlé :
```bash
-curl -s http://127.0.0.1:4321/archicrat-ia/prologue/ | grep -n "taxe Zucman"
+launchctl bootout "gui/$(id -u)" ~/Library/LaunchAgents/me.archicratie.pull-stack-health.plist
+launchctl bootstrap "gui/$(id -u)" ~/Library/LaunchAgents/me.archicratie.pull-stack-health.plist
+```
+
+### 17.5 Miroir et doctor
+
+```bash
+~/ops-local/archicratie/auto-sync-localhost.sh
+~/ops-local/archicratie/doctor-localhost.sh
```
---
-## Décision d’exploitation finale
+## 18. Procédures de secours
-La politique retenue est la suivante :
+### 18.1 Si `4321` ment
+Vérifier :
-- **repo canonique** = espace de développement ;
-- **worktree localhost** = miroir automatique de `main` ;
-- **ops sous HOME** = scripts, logs, automation ;
-- **LaunchAgent sync** = réalignement périodique ;
-- **LaunchAgent astro** = exécution d’Astro ;
-- **Astro local** = lancé uniquement depuis le worktree localhost.
+- SHA du worktree ;
+- process Astro ;
+- cwd réel ;
+- logs du miroir ;
+- doctor.
-Cette séparation rend le dispositif plus :
+### 18.1 bis Lecture correcte du symptôme
-- lisible ;
-- robuste ;
-- opérable ;
-- antifragile.
+Quand `4321` “ment”, il faut d’abord déterminer de quel mensonge il s’agit réellement :
+
+- le worktree n’est pas aligné sur `origin/main` ;
+- le bon worktree existe, mais le mauvais process sert le port `4321` ;
+- le sync a bien eu lieu, mais l’interface observée ne reflète pas encore le bon état.
+
+Ordre de lecture recommandé :
+
+1. vérifier le SHA du worktree miroir ;
+2. vérifier le SHA attendu de `origin/main` ;
+3. vérifier le process réellement attaché au port `4321` ;
+4. vérifier le `cwd` réel de ce process ;
+5. relire les logs du sync miroir et du serveur Astro ;
+6. terminer par `doctor-localhost` si le doute persiste.
+
+Principe :
+
+- un `4321` trompeur est rarement un problème “front” ;
+- c’est presque toujours un problème de **SHA**, de **process**, de **cwd**, ou de **chaîne de sync**.
+
+### 18.2 Si `4322` ne suit plus `main`
+Vérifier :
+
+- branche du repo canonique ;
+- propreté de l’arbre ;
+- logs `auto-sync-dev-main.sh`.
+
+### 18.2 bis Ne pas confondre dérive normale et panne
+
+Le port `4322` n’a pas vocation à refléter automatiquement `main` dans tous les cas.
+
+Deux situations sont normales et ne doivent pas être interprétées comme des pannes :
+
+- le repo canonique est sur une autre branche que `main` ;
+- le repo canonique est sur `main` mais l’arbre est localement modifié.
+
+Dans ces cas, le cockpit ou les scripts peuvent produire des états du type :
+
+- `skip_dirty_main`
+- ou absence de réalignement automatique
+
+Ce comportement est volontaire.
+
+Principe :
+
+- `4322` est un **atelier de travail vivant** ;
+- il ne doit pas être réaligné de force si cela risque d’écraser ou de perturber le travail local.
+
+La bonne question n’est donc pas seulement :
+
+- “est-ce que `4322` suit `main` ?”
+
+mais :
+
+- “avait-il vocation à suivre `main` dans l’état courant du repo ?”
+
+### 18.3 Si le cockpit dit n’importe quoi sur le NAS
+Vérifier :
+
+- `stack-health.json` sur le NAS ;
+- le planificateur DSM ;
+- le LaunchAgent `pull-stack-health` ;
+- la fraîcheur du cache local.
+- si nécessaire, couper puis relancer explicitement le LaunchAgent avec `launchctl bootout ...` puis `launchctl bootstrap ...`.
+
+### 18.3 bis Distinguer panne NAS, panne de rapatriement et cache périmé
+
+Si la lecture NAS semble incohérente, il faut distinguer trois cas très différents :
+
+#### A. Le NAS produit mal son état
+Indices possibles :
+
+- `stack-health.json` absent ou invalide côté NAS ;
+- `dockerAccessMode` incohérent ;
+- conteneurs signalés `docker_unavailable` ;
+- problème de tâche planifiée DSM ou de privilèges côté génération.
+
+#### B. Le NAS produit bien son état, mais le Mac ne le rapatrie plus
+Indices possibles :
+
+- `stack-health.json` correct sur le NAS ;
+- cache local figé sur le Mac ;
+- `launchAgents.pullStackHealth.loaded = false` ;
+- logs de `pull-stack-health` arrêtés.
+
+#### C. Le dernier cache local est encore lisible, mais il est trop ancien
+Indices possibles :
+
+- services encore affichés comme `OK` ;
+- alerte de fraîcheur `warn` ou `critical` ;
+- `generatedAt` ancien ;
+- aucun rafraîchissement récent du cache.
+
+La bonne méthode n’est donc pas seulement de “regarder la stack”, mais de répondre successivement à trois questions :
+
+1. le NAS génère-t-il correctement le fichier ?
+2. le Mac le rapatrie-t-il encore ?
+3. la donnée affichée est-elle encore fraîche ?
+
+### 18.4 Si staging / prod n’exposent plus `__ops/health.json`
+Vérifier :
+
+- le workflow de déploiement ;
+- `scripts/write-ops-health.mjs` ;
+- le routage Traefik / Authelia ;
+- les endpoints internes `8081` / `8082`.
+
+### 18.4 bis Méthode de tri diagnostique
+
+Quand `__ops/health.json` n’est plus lisible, il faut d’abord identifier **à quel étage** se situe la panne.
+
+#### Cas 1 — l’endpoint interne est déjà en erreur
+Si :
+
+- `http://127.0.0.1:8081/__ops/health.json`
+- ou `http://127.0.0.1:8082/__ops/health.json`
+
+retourne `404` ou une autre erreur, alors le problème est plutôt :
+
+- côté build ;
+- côté contenu servi par Nginx ;
+- ou côté déploiement du manifeste.
+
+#### Cas 2 — l’endpoint interne fonctionne mais le domaine public redirige ou bloque
+Si l’interne fonctionne, mais que le domaine public renvoie `302`, `401` ou un comportement auth inattendu, alors le problème est plutôt :
+
+- côté Traefik ;
+- côté règles de routage ;
+- côté Authelia / exemptions ;
+- côté exposition publique de `__ops/health.json`.
+
+#### Cas 3 — l’endpoint répond `200`, mais le contenu est incohérent
+Si `__ops/health.json` existe et répond, mais que `env`, `upstream`, `buildSha` ou `builtAt` sont faux ou obsolètes, alors le problème est plutôt :
+
+- dans la génération du manifeste ;
+- dans le workflow de déploiement ;
+- ou dans le moment précis où le manifeste est écrit dans l’image livrée.
+
+Principe :
+
+- `404` interne → problème de build / image / contenu servi ;
+- `302` ou `401` public → problème de routage / auth ;
+- `200` mais mauvais contenu → problème de génération ou d’injection du manifeste.
---
-## Résumé opératoire
+## 19. Tests réels désormais qualifiés
+
+Les validations suivantes ont été observées pendant la session :
+
+- staging / prod exposent bien `__ops/health.json` en public ;
+- `buildSha` et `builtAt` sont lisibles ;
+- `builtAt` est revenu à l’heure locale souhaitée ;
+- la stack NAS est rapatriée côté Mac ;
+- le cockpit sait afficher les services :
+ - `authRedis`
+ - `gitea`
+ - `giteaActRunner`
+- arrêt du runner Gitea → alerte `info`
+- arrêt de Redis auth → alerte `warn`
+- arrêt de Gitea → alerte `warn`
+- vieillissement artificiel du cache NAS → `warn` puis `critical`
+
+---
+
+## 20. État final de référence
+
+Quand tout fonctionne correctement, l’état attendu est :
+
+- `4321` = miroir fidèle de `origin/main`
+- `4322` = atelier local maîtrisé
+- `4310/4311` = cockpit lisible et opératoire
+- staging = build lisible via `__ops/health.json`
+- prod = build lisible via `__ops/health.json`
+- stack NAS = lisible via cache local frais
+- alertes = hiérarchisées et causales
+- actions cockpit = pilotage rapide sans confusion
+
+---
+
+## 21. Ce qu’il ne faut pas oublier
+
+Cette architecture n’est plus seulement une astuce de localhost.
+C’est une **micro-ingénierie d’exploitation locale complète**.
+
+Elle repose sur :
+
+- séparation des rôles ;
+- automatisation légère mais fiable ;
+- lecture causale ;
+- instrumentation des sites ;
+- cache NAS rapatrié ;
+- cockpit local ;
+- mémoire documentaire durable.
+
+---
+
+## 22. Résumé ultra court
### Pour voir la vérité de `main`
-
-Ouvrir :
-
```text
http://127.0.0.1:4321
```
-Le serveur doit provenir de :
-
-```text
-~/ops-local/archicratie/localhost-worktree
-```
-
-### Pour développer
-
-Travailler dans :
-
+### Pour travailler vraiment
```text
/Volumes/FunIA/dev/archicratie-edition/site
```
-avec tes commandes habituelles.
+### Pour superviser et piloter
+```text
+http://127.0.0.1:4310
+```
+
+### Pour lire l’état machine consolidé
+```text
+http://127.0.0.1:4311/api/status
+```
+
+### Pour vérifier la stack NAS
+```text
+~/ops-local/archicratie/cache/stack-health.json
+```
### Pour réparer vite
-
```bash
~/ops-local/archicratie/doctor-localhost.sh
~/ops-local/archicratie/auto-sync-localhost.sh
-```
-
-### Pour tout réinstaller proprement
-
-```bash
-~/ops-local/archicratie/install-localhost-sync.sh
+~/ops-local/archicratie/pull-stack-health.sh
```
---
-## Mémoire courte
+## 23. Pistes futures à forte valeur
-Si un jour plus rien n’est clair, repartir de ces six commandes :
+Les prochaines améliorations les plus pertinentes seraient :
-```bash
-git -C ~/ops-local/archicratie/localhost-worktree rev-parse HEAD
-git -C /Volumes/FunIA/dev/archicratie-edition/site ls-remote origin refs/heads/main
-launchctl print "gui/$(id -u)/me.archicratie.localhost-sync" | sed -n '1,120p'
-launchctl print "gui/$(id -u)/me.archicratie.localhost-astro" | sed -n '1,120p'
-lsof -nP -iTCP:4321 -sTCP:LISTEN
-~/ops-local/archicratie/doctor-localhost.sh
-```
-
-Et lire :
-
-```bash
-tail -n 120 ~/ops-local/archicratie/logs/auto-sync-localhost.log
-tail -n 120 ~/ops-local/archicratie/logs/astro-localhost.log
-```
+- journal automatique des incidents détectés / résolus ;
+- diagnostic synthétique de cause probable dans le cockpit ;
+- regroupement visuel des services NAS par familles :
+ - service public
+ - authentification
+ - forge / déploiement ;
+- raffinement documentaire dans `START-HERE.md` et doc d’exploitation.
---
-## Statut actuel visé
+## Conclusion
-Quand tout fonctionne correctement :
+L’architecture locale est désormais passée :
-- `~/ops-local/archicratie/localhost-worktree` pointe sur le même SHA que `origin/main` ;
-- `astro dev` écoute sur `127.0.0.1:4321` ;
-- son cwd est `~/ops-local/archicratie/localhost-worktree` ;
-- le contenu servi correspond au contenu mergé sur `main`.
+- d’un simple `localhost auto-sync`
+- à un **dispositif d’exploitation local complet**.
-C’est l’état de référence à préserver.
\ No newline at end of file
+Le résultat essentiel est le suivant :
+
+- on sait **où** se trouve la vérité ;
+- on sait **quel** serveur sert quoi ;
+- on sait **pourquoi** une alerte remonte ;
+- on sait **comment** diagnostiquer et réparer ;
+- on sait **rejouer** l’installation ;
+- on a désormais une mémoire technique durable de l’ensemble.