# OPS — Localhost auto-sync
## 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.
But recherché :
- 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.
---
## Principes
L’architecture locale repose sur **trois espaces 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.
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
```text
/Volumes/FunIA/dev/archicratie-edition/site
```
### Worktree localhost
```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 ;
- commits ;
- pushes ;
- PR.
### 2. Le worktree localhost
Le localhost ne tourne **pas** depuis le repo canonique.
Il tourne depuis un worktree dédié :
```bash
cd ~/ops-local/archicratie/localhost-worktree
git branch --show-current
```
Branche attendue :
```text
localhost-sync
```
Ce worktree suit `origin/main` via le script d’auto-sync.
### 3. Le dossier ops local
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` :
```text
~/ops-local/archicratie
```
Motif :
- compatibilité avec `launchd` ;
- logs persistants ;
- visibilité claire ;
- indépendance du dépôt.
---
## 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 :
```text
/bin/bash: /Volumes/FunIA/dev/_ops-local/archicratie/auto-sync-localhost.sh: Operation not permitted
```
Conclusion :
- 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`.
### 2. Exécution Astro depuis l’ancien localhost sous `/Volumes/...`
Erreur observée :
```text
Error: EPERM: operation not permitted, open '/Volumes/FunIA/dev/archicratie-localhost/node_modules/astro/bin/astro.mjs'
```
Conclusion :
- 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`
---
## Rôle exact du script `auto-sync-localhost.sh`
Le script a quatre responsabilités :
1. **Vérifier l’environnement**
- `git`
- `bash`
- `node`
- `npm`
2. **Garantir le runtime Node**
- Node `22.x`
- npm `10.x`
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.
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.
---
## Rôle exact du script `run-astro-localhost.sh`
Ce script a une responsabilité unique :
- 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.
Il ne doit jamais démarrer depuis le repo canonique.
---
## Rôle exact du script `doctor-localhost.sh`
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 :
```json
{
"node": ">=22 <23",
"npm": ">=10 <11"
}
```
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
```
---
## LaunchAgents
L’architecture finale repose sur **deux LaunchAgents**, avec des responsabilités distinctes.
### 1. LaunchAgent sync
Fichier :
```text
~/Library/LaunchAgents/me.archicratie.localhost-sync.plist
```
Rôle :
- réaligner périodiquement le worktree localhost sur `origin/main` ;
- relancer l’agent Astro si nécessaire.
### 2. LaunchAgent Astro
Fichier :
```text
~/Library/LaunchAgents/me.archicratie.localhost-astro.plist
```
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 :
```text
localhost-sync
```
### 3. Vérifier le dernier état sync
```bash
cat ~/ops-local/archicratie/last-sync.env
```
Exemple attendu :
```text
LAST_SYNC_AT="2026-03-16 20:29:43"
LAST_SYNC_SHA="a1bfbf4405d1342c635caec5b219c14b83b36d5e"
LAST_SYNC_STATUS="noop"
```
### 4. Vérifier les logs de sync
```bash
tail -n 120 ~/ops-local/archicratie/logs/auto-sync-localhost.log
```
### 5. Vérifier les logs Astro
```bash
tail -n 120 ~/ops-local/archicratie/logs/astro-localhost.log
```
### 6. Vérifier qu’Astro écoute bien sur 4321
```bash
lsof -nP -iTCP:4321 -sTCP:LISTEN
```
### 7. Vérifier le processus Astro exact
```bash
PID="$(lsof -tiTCP:4321 -sTCP:LISTEN | head -n 1)"
ps -p "$PID" -o pid=,command=
lsof -a -p "$PID" -d cwd
```
Attendu :
- commande contenant `astro dev`
- cwd = `~/ops-local/archicratie/localhost-worktree`
### 8. Vérifier le contenu servi
Exemple :
```bash
curl -s http://127.0.0.1:4321/archicrat-ia/prologue/ | grep -n "taxe Zucman"
```
Le texte renvoyé doit correspondre à la version attendue sur `main`.
### 9. Vérifier globalement toute l’installation
```bash
~/ops-local/archicratie/doctor-localhost.sh
```
Verdict attendu :
```text
✅ aucun problème bloquant détecté
✅ aucun avertissement
```
---
## Interprétation des statuts
### `status=updated`
Le worktree localhost a été réaligné sur un nouveau SHA ou nettoyé, puis l’agent Astro a été relancé.
### `status=noop`
Le worktree localhost était déjà aligné et le serveur local tournait correctement.
Aucun changement de contenu n’était nécessaire.
---
## Usage normal au quotidien
### 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
```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
git -C ~/ops-local/archicratie/localhost-worktree branch --show-current
```
### État LaunchAgents
```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 logs
```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
```
### État serveur
```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
```
### Vérification contenu
```bash
curl -s http://127.0.0.1:4321/archicrat-ia/prologue/ | grep -n "taxe Zucman"
```
---
## Décision d’exploitation finale
La politique retenue est la suivante :
- **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.
Cette séparation rend le dispositif plus :
- lisible ;
- robuste ;
- opérable ;
- antifragile.
---
## Résumé opératoire
### 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 :
```text
/Volumes/FunIA/dev/archicratie-edition/site
```
avec tes commandes habituelles.
### 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
```
---
## Mémoire courte
Si un jour plus rien n’est clair, repartir de ces six commandes :
```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
```
---
## Statut actuel visé
Quand tout fonctionne correctement :
- `~/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`.
C’est l’état de référence à préserver.