« La télémétrie est morte. » — Réparée et prouvée, en 45 minutes

avant : 3 ports refusés après : 12/12 OK 0 événement perdu causes racines : 2, éliminées
Chaîne de présence des agents : hooks Claude → VFGate :5191 → relais → Chronos :4344 → Fil :4477. Chaque affirmation de cette page = une commande rejouable (datée 2026-10-05, 01:15→01:50 locales).
3/3ports muets au diagnostic (5191·4344·4477)
DOWNChronos : module natif incompatible Node 24
12/12checks doctor après réparation
0événement perdu (spool vide + journal actif)
2/2causes racines éliminées

La chaîne, avant / après

Hooks7 events + journal
→
VFGate :5191refus 01:15 → OK 01:44
→
Relais 500msspool forwarder actif
→
Chronos :4344ERR_DLOPEN → écoute
→
Fil :4477muette → 200
Seuls les hooks et le journal local ont survécu (fail-open par conception) : la trace de secours tourne, le réseau est mort.

La nuit de la panne, horodatée par les fichiers

JEU 03/10 · 19:08 (boot machine)
La chaîne démarre normalement
Tâche planifiée VFG-Agent-Presence (déclencheur logon) → doctor 12/12. Fil « Ready in 544ms », checkpoint Chronos avance.
SAM 04/10 · 00:06 (dernier log gate)
Tout meurt en même temps
La timeline vfgate.out.log s'arrête pile à l'heure de winget install OpenJS.NodeJS.LTS (v22.14.0 → v24.19.0, session précédente). L'installeur tue tous les node.exe — seul Ollama survit (process séparé).
SAM 04/10 · 20:20 (dernier checkpoint UTC)
Rien ne peut redémarrer
chronos.err.log : ERR_DLOPEN_FAILED — better-sqlite3 (compilé ABI Node 22, runtime ABI 24). Chronos ne peut pas se relancer.
DIM 05/10 · toute la journée
Panne silencieuse, 3 ports refusés
La tâche planifiée a fait son travail… au logon du 03/10. Sans intervalle de répétition, plus personne ne garde. Les hooks continuent d'écrire dans le spool/journal (fail-open), le réseau n'avale rien.
DIM 05/10 · 01:15 → 01:40 (cette session)
Mesure → 2 causes → 2 réparations
Doctor d'abord (répartition précise des DOWN), rebuild du module natif, relance par la tâche planifiée, intervalle 15 min posé.
DIM 05/10 · 01:44
Bout-en-bout prouvé + fail-open prouvé
Checkpoint 20:20:51Z → 23:43:50Z, session test visible dans le contrat live v2, spool→replay livré (23:44:23Z), Fil 200.

Les 2 causes racines, une par une

① Cause technique : ABI Node

Le module natif better-sqlite3 de Chronos, compilé pour Node 22, refuse de se charger sous Node 24 :

// chronos.err.log
Error: ERR_DLOPEN_FAILED
  at new Database (better-sqlite3/database.js:48)
  at initDb (src/db.js:20)
Node.js v24.19.0

Même un redémarrage parfait de la garde n'aurait rien changé : le process **mort** ne pouvait pas renaître. La panne avait deux verrous, pas un.

② Cause organisationnelle : garde sans répétition

La tâche planifiée qui relance toute la chaîne n'avait aucun intervalle :

PS> Get-ScheduledTask VFG-Agent-Presence | > triggers
Trigger type : MSFT_TaskLogonTrigger
Repetition   : Interval='' Duration=''

Une garde qui ne garde que le logon = une chaîne qui ne survit pas au premier accident de session. C'est la leçon la plus chère : la panne n'était pas « VFGate a gelé » (correctif du 02/10 toujours en place), c'était une seule mort sans filet.

Les réparations, une par une (commandes réelles)

✓ Réparation 1 — module natif

npm rebuild better-sqlite3
# rebuilt dependencies successfully
node src/server.js
# Server listening at http://127.0.0.1:4344

Le rebuild charge le prebuild pour l'ABI 24. Chronos réécoute.

✓ Réparation 2 — la garde devient une vraie garde

$t = Get-ScheduledTask 'VFG-Agent-Presence'
$t.Triggers[0].Repetition.Interval = 'PT15M'
$t.Triggers[0].Repetition.Duration = ''
Set-ScheduledTask -InputObject $t

Toutes les 15 min, le script idempotent vérifie chaque service (Test-Http) et ne relance que ce qui est mort. Plus de mort silencieuse possible.

La preuve, bout en bout (01:43 → 01:44)

PreuveCommande rejouableRésultat réel
Santé complètedoctor-agent-presence.ps112/12 OK — gate, Chronos, Fil, relais, spool, 4 configs hooks, tâche
Émission réellepipe JSON → emit-agent-event.mjs --event PostToolUsecheckpoint avance : 20:20:51Z → 23:43:50Z
Visible en avalcurl :4344/v1/query/agents/activesession e2e-presence-reparation-20261005 active, visible (contrat live v2)
Fil sertcurl :4477HTTP 200 (page « Fil — chronarbre de pensée »)
Fail-openémettre vers porte morte :5999, puis forward-spool.mjsspoulé puis livré au retour (checkpoint 23:44:23Z, spool purgé)
Zéro doublonGet-CimInstance node.exe · forward-spool1 seul daemon résident (PID 32772)
Trace de secours~/.claude/vfg-journal/journal-2026-10-04.jsonl92 lignes, journal actif pendant toute la panne

Leçon de méthode : l'auto-trace

Deux pièges de mesure ont failli fausser le diagnostic — les deux sont dans la méthode (règle 4 de l'audit par la mesure) :

Le checkpoint à 20:20:51Z semblait « fraîchement écrit » : en fait c'était la dernière écriture avant la mort (les tests E2E d'hier, le même soir qui a tué la chaîne)
Le « spool vide » semblait dire « aucun événement en attente » : en fait le vide venait du drain automatique du fail-open — le vrai fichier d'attente est spool.processing.jsonl, déplacé avant publication

La preuve n'est jamais le log d'un seul instant : c'est la réconciliation de 3 sources indépendantes (log gate, checkpoint Chronos, journal local) qui raconte la même histoire d'horloge.

Verdicts et restes (honnêteté de clôture)

ÉlémentVerdictPourquoi
Chaîne de présenceVIVANTEdoctor 12/12 + E2E visible, daté 01:44
Cause « mort en silence »ÉLIMINÉEgarde 15 min posée, script idempotent vérifié
2 adaptateurs gate (hero, whatsapp)HORS PÉRIMÈTREsanté 4/6, préexistant, impacte pas la présence
Push commit gate 121cee4DÉCISION HUMAINEdéclenche un code-deploy production
F06 « réactiver télémétrie »SATISFAITEhooks + E2E + fail-open prouvés — reste le marquage du protocole