Commit Graph

15 Commits

Author SHA1 Message Date
Tallagrand Thierry
26bdabf979 P12 — la voiture se charge sur le solaire d hier, pas sur le reseau
Idee du proprietaire, apres la nuit du 14 au 15/09 : 8 kWh achetes au reseau, la
batterie encore a 69 % a 5 h du matin, et une prevision de 38,7 kWh le lendemain
dont 10,5 auraient ete ecretes faute de place. Payer du reseau la nuit pour
ecreter le solaire le lendemain, c est perdre deux fois.

Le mode `battery_assist_night` existait pour exactement ca, et etait MORT depuis
toujours : il exige `batteryCurrentA >= plancher de decharge` (0 A), impossible
la nuit puisque la maison tire sur la batterie. On ne cree donc pas un chemin,
on rend viable celui qui existe — en remplacant un test de COURANT impossible
par un test d ENERGIE.

Le plancher n est PAS une constante nouvelle : c est `required_soc_prudent`, que
le bilan publie deja en profil p80. La voiture descend la batterie jusqu au
niveau qui lui permet encore de tenir jusqu au croisement PV, et pas plus bas.
La bascule vers le reseau est automatique quand le surplus est epuise — la
soupape decrite par le proprietaire, sans aucune regle horaire.

⚠️ Verrou anti-oscillateur, et il n est pas decoratif. Le plancher descend
pendant la nuit et le SOC aussi : voiture en charge le SOC perd 7,1 pt/h contre
3,7 pour le plancher, mais voiture ARRETEE il n en perd que 2,6. Le surplus
reapparaitrait tout seul et la charge repartirait. Une seule passe par session
de nuit, comme la recovery batterie depuis le 27/08.

Les gardes de courant restent : commonSafety, l arret dur, et la garde souple
avec sa tolerance temporelle — c est elle qui arretera la charge quand la bande
de SOC deviendra trop basse pour le courant tire. Ce qu on retire est uniquement
le plancher de PROFIL, qui interdisait de demarrer des que la batterie n etait
pas a l equilibre.

Cinq scenarios, six mutations qui mordent chacune sur un jeu different. Deux
d entre elles ont trouve de vraies faiblesses : les attendus ne verifiaient que
le choix du mode et pas que la voiture charge, et la garde de fraicheur du bilan
n etait couverte par aucun scenario.

Rien n est deploye et le toggle est a off.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GwpFpy2aQ2rJok6tNQSbVj
2026-09-15 05:05:17 +02:00
Tallagrand Thierry
4fb401be96 Le conseiller ne fixe plus la cible du matin
Le premier cablage reel (§20, 09/08) faisait suivre a la cible SOC du matin
`input_number.evcs_advisor_night_target_soc`, derriere quatre gardes :
toggle, statut ok, fraicheur de 120 minutes, bornage [22, 90].

Depuis que le conseiller est devenu observateur le 12/09, il n emet plus de
cible et ce helper est fige a 55 %. Les quatre gardes continuaient pourtant
de passer toutes les quatre, parce que la garde de fraicheur date le RUN et
non la VALEUR : le conseiller tourne toujours, donc son horodatage est
frais. Activer le toggle aurait applique un chiffre perime sans qu aucune
garde ne s y oppose.

Retire : les quatre parametres d INPUT_PARSE, le bloc de WINDOWS_SOURCEV2,
le passe-plat de BATTERY_CLOUD_LOCKS, la trace (ADVISOR) de DECISION et les
quatre champs du gabarit de snapshot. Le cablage du BILAN (§39) est intact.
`parseAdvisorRunToMs` devient `parseRunStampToMs` : le bilan publie le meme
format et reste son seul appelant.

Golden 98 → 96. Les trois scenarios du cablage sont remplaces par un seul,
`advisor-cible-ignoree`, qui pinne l ABSENCE de cablage : toutes les
conditions de l ancienne regle reunies, et la cible doit rester celle des
regles de prevision. Mutation : cablage restaure → il tombe, avec
« target=22% (ADVISOR) » dans la raison ; retire → 96/96.

Gabarit re-rendu contre Home Assistant : 196 champs, JSON valide, aucun
null. Pousse en production, cycle suivant en succes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GwpFpy2aQ2rJok6tNQSbVj
2026-09-13 12:14:38 +02:00
Tallagrand Thierry
914ef021b2 Session reseau unique de la nuit : engagement sur le bilan, sortie sur la fenetre
§26 avait pose l objectif et designe le critere. Avant d ecrire quoi que ce
soit, j ai mesure les kWh reellement sortis de la batterie par point de SOC sur
cinq fenetres et trois nuits : 0,164 a 0,171 kWh/point au-dessus de 42 %, soit
environ 17 kWh, mais 0,121 entre 45 et 22 % et 0,082 entre 33 et 20 %. En bas
d echelle un point de SOC vaut moitie moins, alors que BALANCE.js convertit avec
une constante unique de 15 kWh. required_soc y est donc systematiquement trop
bas — c est le facteur 1,75 mesure la nuit du 02-03/09, ou le SOC descendait
bien plus vite que la ligne.

Cette mesure a dicte la conception : le relachement de la session ne peut pas
etre un seuil de SOC, puisqu il porterait sur une grandeur fausse et que tout
seuil que le SOC repasse ensuite rouvre l oscillateur du 27/08. Les deux
variantes evidentes — relacher sur required_soc, ou sur required_soc_prudent
avec la bande p50/p80 — ont ete ecartees pour cette raison.

Retenu : engagement sur le bilan (batterie sous la cible), relachement a la fin
de la fenetre. L oscillation devient structurellement impossible, la condition
de sortie etant monotone : une fenetre ne se ferme qu une fois. Un nouveau
helper input_boolean.victron_evcs_night_grid_session_committed porte
l engagement, cree par input_boolean.reload sans redemarrage HA et ecrit
seulement sur changement reel ; sans cette persistance la garde redeviendrait
une bande, donc un oscillateur.

Effet : l ACM recharge jusqu a la cible puis tient la batterie a 2 A, la maison
finit la nuit sur le reseau en heures creuses au lieu de vider la reserve, et la
batterie arrive a l aube pres de sa cible avec de la place pour le solaire. La
coupure immediate E2 de l hysteresis exige l absence d override et le helper
takeover reste ON : aucune modification du workflow d hysteresis.

Golden 75 -> 78. Le scenario epingle quelques heures plus tot comme limite
connue devient celui du correctif, attendu mis a jour dans le meme commit.
Quatre mutations ; la plus parlante retire la garde bilan et fait tomber les
trois scenarios anti-oscillateur du 27/08 — les tests de l ancien correctif
protegent le nouveau code.

Prerequis identifie et non traite : la conversion kWh vers points de SOC est une
constante la ou la realite est une courbe. Consequence, un sous-declenchement
possible en bas d echelle, jamais une oscillation. Prochain chantier du bilan.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KKzkkVTG6KXnGXFFC7X9XJ
2026-09-03 23:14:49 +02:00
Tallagrand Thierry
05bdf019c2 La cible du matin devient une energie a couvrir, plus un palier
La regle classait le lendemain en trois paliers a partir de son TOTAL (>= 18 kWh
-> 35 %, >= 10 -> 55 %, sinon 85 %) et ignorait donc l heure d arrivee de
l energie. Le 03/09 demain annoncait 33,9 kWh — donc "fort", donc 35 % — avec un
matin creux (0,03 kWh a 7 h, 0,77 a 8 h) et un croisement a 9 h. Meme defaut que
le plancher du soir, a l autre bout de la nuit.

Releve de la nuit 02-03/09, requis contre reel : 00:30 62,4 / 53 ; 03:00
50,4 / 45 ; 04:30 41,7 / 37 ; 06:00 37,1 / 22 — cible fixe a 35 tout du long.
Le required_soc decroit proprement avec la nuit qui se consomme (6,1 -> 0 kWh a
couvrir), la batterie est restee sous la ligne toute la nuit, et le bilan le
savait des 00:30. A 03:00 EDF a ete relache cinq points sous la ligne ; le
reseau est revenu de 07:24 a 10:33 au plein tarif, 1,49 kWh.

La cible vaut desormais min(90, max(palier ou advisor, required_soc)). Sens
unique delibere : la direction corrigee est celle de l echec observe, et le
principe unificateur des deux cablages est que le bilan ne pousse jamais dans le
sens "couvre moins bien la nuit". Plafond 90 parce qu au-dela la batterie cesse
d etre un puits et evince la production du matin, et parce que le bilan publie
sans ecretage des valeurs superieures a 100 — sans ce plafond la cible serait
inatteignable et EDF resterait allume toute la nuit. C est deja la borne du
chemin advisor, aucune constante nouvelle.

Pas d oscillateur : required_soc decroit de facon monotone pendant la nuit et la
passe unique de recovery reste en place. Gardes et repli identiques au plancher
du soir, kill-switch existant.

Golden 70 -> 75. Quatre mutations font tomber chacune exactement un scenario
different. Un cinquieme scenario fige une LIMITE CONNUE, mesuree par sonde : la
passe unique de recovery latche le relachement pour toute la session, donc la
cible dynamique n ameliore que la premiere passe. §26 avait deja designe le bon
critere de re-engagement — un budget d energie restant a couvrir, qui appartient
au bilan ; la suite touche la machine a etats de la nuit et fera l objet d un
chantier propre.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KKzkkVTG6KXnGXFFC7X9XJ
2026-09-03 22:51:09 +02:00
Tallagrand Thierry
6f3f98e8b6 Le plancher du soir vient de l energie a couvrir, plus d un niveau fixe
grep evcs_balance sur les deux workflows de decision renvoyait 0 depuis la mise
en service du bilan le 26/08 : il calculait l heure de croisement, l energie a
couvrir et le SOC requis, et personne ne l ecoutait. Le 03/09 a 19:44 les deux
nombres coexistaient dans le meme systeme — evening_soc_start_min a 85 %, un
niveau qui ne sait rien de la nuit, contre evcs_balance_required_soc a 99,7 %,
l energie a couvrir jusqu au croisement de 9 h. A SOC 94 le premier autorisait
la charge, le second disait que la batterie ne passerait pas la nuit seule.

Le plancher de demarrage du soir vaut desormais max(regle, bilan). Sens unique
delibere : le bilan peut durcir, jamais relacher. L assouplir — il dira souvent
moins de 85 % par forte prevision d ete — autoriserait des charges qui n ont pas
lieu aujourd hui, decision distincte a valider pour elle-meme. Le max rend au
passage la garde robuste sans borne ni ecretage : une valeur nulle, negative ou
aberrante ne peut pas relacher le plancher.

Portee demarrage : le plancher de maintien reste min(...), donc a 80 %, et
aucune charge en cours n est coupee. L abondance P6 continue de primer, etant
une preuve mesuree que la ressource depasse ce que le systeme absorbe.

Gardes : fraicheur inferieure a 30 min (six cycles du bilan), horodatage futur
rejete, statut ok, valeur finie. Sinon regle historique. Aucun nouveau toggle :
evcs_balance_enabled a OFF arrete les ecritures du bilan, balance_last_run se
fige, la fraicheur expire et le plancher retombe seul sur 85 % en une
demi-heure — un helper de plus aurait fait doublon.

Gabarit complet re-rendu contre HA : 195 champs, JSON valide, aucun null.
parseAdvisorRunToMs reutilise, l advisor et le bilan publiant le meme format.

Golden 66 -> 70. Quatre mutations — bilan ignore, Math.max remplace par une
affectation directe, garde de fraicheur supprimee, garde de statut supprimee —
font tomber chacune exactement un scenario, et un different a chaque fois.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KKzkkVTG6KXnGXFFC7X9XJ
2026-09-03 22:19:33 +02:00
Tallagrand Thierry
7a26078e73 La provocation solaire se demande aux chargeurs, pas au SOC
Le correctif precedent bloque l incident de 19:44, mais un excedent proche de
zero est ambigu : c est la signature de la censure ET celle de l equilibre de
fin de journee. Le releve du 03/09 contient les deux avec le meme excedent —
18:50 : +14 W, ressource refusee ; 19:41 : +1 W, plus rien a donner. Rejeu du
19:41 : batteryAssistSolarStartOk etait encore vrai. Trois minutes avant
l incident, le systeme aurait commis la meme faute.

Le plan annoncait de lire evcs_balance_pv_censored. Lecture de CENSORSHIP.js :
ce drapeau est un OU de motifs dont l un est exactement le proxy qu on voulait
remplacer (socPct >= 90 et batterie sans puits). Et il est reste ON jusqu au
calcul de 19:50, soit cinq minutes APRES le demarrage de la charge — ce qui l a
fait basculer n est pas la censure mais sun_elevation passant sous 10 degres.
Il n aurait pas bloque l incident.

Le signal juste, ce sont les chargeurs eux-memes : sept entites
sensor.*_mppt_operation_mode, jamais lues par aucun workflow. mppt_active = je
suis a mon point de puissance maximale, je donne tout ; voltage_current_limited
ou off = je me retiens faute de preneur. Le 03/09 elles ont bascule a 19:20,
soit 24 minutes avant pv_censored, sans aucun proxy et a la cadence de la
boucle. Contre-verification le 01/09 a 14:22 : les trackers restaient limites
PENDANT une charge de 3,4 kW sous 6,9 kW de PV, donc le signal ne s auto-annule
pas a midi.

Collecte sans aucun entity_id code en dur (selectattr sur mppt et
operation_mode), expression testee sur /api/template puis gabarit complet
re-rendu : 191 champs, JSON valide, aucun null. Lectures inexploitables
ecartees, et repli sur le comportement anterieur quand il n en reste aucune.

Garde de DEMARRAGE uniquement, structurellement : la lier au sourceMode ou au
maintien ferait que la charge, en donnant un preneur au MPPT, detruirait sa
propre condition d autorisation — le bistable de l abondance P6 corrige le
24/08.

Golden 64 -> 66. Mutation dans trois directions : garde neutralisee -> 1 echec
cible, provocation toujours refusee -> 3 echecs, lecture illisible traitee
comme "a fond" -> 1 echec sur le scenario de repli.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KKzkkVTG6KXnGXFFC7X9XJ
2026-09-03 21:41:31 +02:00
Tallagrand Thierry
ef3d7ed7b2 Un demarrage se decide sur l excedent, pas sur la production
Le 03/09 a 19:44, voiture branchee : PV 758 W, maison 1202 W, batterie deja
a -12 A, nuit qui tombe. Le controleur a lance 1,6 kW de charge parce que
pv_start_min_w (700 W) est une puissance PV absolue : "758 >= 700, donc il y
a du soleil". L excedent valait -444 W. En 33 minutes la voiture a pris
0,897 kWh et la batterie en a rendu 1,479 kWh (SOC 94 -> 85), la veille d une
nuit que le bilan energetique annoncait deja deficitaire.

Rejeu du releve dans la vraie chaine : rawSolarStartOk ET
batteryAssistSolarStartOk etaient tous deux vrais. Corriger le seul seuil PV
n aurait donc rien change ; les deux chemins sont traites ensemble.

La grandeur juste est ce qui reste une fois la maison servie. Sans injection
et reseau absent, PV - maison(hors EVCS) vaut la puissance batterie au signe
pres, et separe les deux situations que la mesure PV seule confond : sous
censure la production se cale sur la maison, l excedent vaut ~0 et il y a de
la ressource refusee ; en deficit reel l excedent est negatif et la batterie
porte deja la maison. Zero est la frontiere physique entre les deux, verifiee
sur le releve du 03/09 (excedent positif sur toute la plage ou pv_censored
etait ON, negatif ensuite).

Aucune constante nouvelle, aucun champ de snapshot, aucun helper :
derived.houseNonEvcsW existait deja. Le chemin "soleil reel" reutilise
pv_start_min_w tel quel, applique a la bonne grandeur ; le chemin
"provocation" exige seulement excedent >= 0. Demarrage uniquement : les
conditions de maintien gardent leur plancher et leur tolerance temporelle.

Golden 61 -> 64, autour d une paire de releves reels pris a 54 minutes d ecart
dont les lectures PV ne different que de 11 W (769 puis 758) : le premier
demarre, le second non. apresmidi-provocation-p3 est corrige : il portait
PV 300 W, maison 600 W, batterie +2 A reseau ouvert, un trou de 300 W que rien
n alimente, et passait quand meme parce que le demarrage ne regardait jamais
le bilan. Mutation dans les deux sens : 2 echecs cibles a chaque fois,
excedent-censure-demarre restant vert (il tient par le chemin assistance).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KKzkkVTG6KXnGXFFC7X9XJ
2026-09-03 20:53:50 +02:00
Tallagrand Thierry
10987a04d8 Un depassement souple de la batterie doit durer pour compter
La table des bandes SOC declarait depuis toujours un courant souple tolere
pendant des minutes et un courant dur tolere quelques secondes. Les deux
tolerances etaient calculees, exportees dans le debug, et lues par aucune
decision : le controleur comparait un echantillon instantane a un plancher,
toutes les 30 s.

A SOC 94 la batterie est en float et son courant oscille entre +1,8 et -2,6 A
d'un echantillon a l'autre. Le test devenait un tirage a pile ou face et la
charge du vehicule s'arretait sur du bruit : deux fois le 01/09, quatre a six
fois par jour la semaine precedente. Chaque arret coutait environ 0,45 kWh,
la batterie etant pleine et le vehicule le seul preneur.

Le correctif est purement temporel : aucun seuil n'est deplace, aucune
constante ajoutee. La tolerance de la bande est branchee sur les trois
conditions de MAINTIEN, via un helper de persistance qui porte le debut du
depassement en cours. Le plancher de demarrage et le garde dur restent
instantanes : la tolerance est du confort, jamais du risque.

Golden 56 -> 61. Preuve par mutation dans les deux sens : tolerance supprimee
-> 2 echecs cibles, tolerance infinie -> 3 echecs cibles, l'arret dur restant
vert dans les deux cas.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KKzkkVTG6KXnGXFFC7X9XJ
2026-09-01 20:10:28 +02:00
Tallagrand Thierry
f0479945c4 Fenetre d etablissement : ne plus decider sur les mesures du transitoire
A la fermeture du contacteur, PV, import et courant batterie ne decrivent aucun
regime permanent pendant ~150 s (mesure du 31/08). La boucle de 30 s y prenait
cinq decisions, et la moyenne PV 10 min en gardait la trace dix minutes de plus
-- le systeme lisait "plus de soleil", en concluait qu il avait besoin du
reseau, et confirmait sa propre premisse.

Aucun seuil nouveau : le systeme publiait deja l horodatage de la transition et
personne ne le lisait. Pendant la fenetre, on ne demarre pas et on ne laisse pas
une charge s arreter sur ces mesures ; les step-down restent libres.

Deux asymetries voulues : la fenetre ne s ouvre qu a l ARRIVEE du reseau (a sa
disparition les onduleurs reprennent tout, les gardes doivent agir plus vite),
et elle suspend le confort, jamais le risque.

Golden 42 -> 48, chaque cas apparie a son contrefactuel hors fenetre. Mutation :
gridSettling force a false -> 2 echecs cibles, restauration -> 48/48.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KKzkkVTG6KXnGXFFC7X9XJ
2026-08-31 18:18:03 +02:00
Tallagrand Thierry
057e0f7d8f Corrige l'inversion plancher de demarrage / plancher de maintien (EVCS)
Incident du 24/08 12:24-12:28 : bascule de profil vers midday_hc_or_solar
avec SOC 58 %. Plancher de demarrage = min(midday_soc_min 75, abondance P6
40) = 40 ; plancher de maintien = max(soc_stop_min 29, 75-15) = 60. Le
systeme avait donc le droit de DEMARRER ce qu'il n'avait pas le droit de
MAINTENIR : cycle limite a la periode de min_run (3 min) sur toute la bande
[40 %, 60 %], et ~2,6 kW de PV ecretes a chaque creux (PV 10,24 kW -> 7,64 kW
quand la borne s'arrete, ESS sans injection).

Deux volets indissociables dans BATTERY_CLOUD_LOCKS.js :
- effectiveSocHoldFloor = min(regle du profil, plancher de demarrage
  effectif). Sans abondance, comportement inchange.
- l'abondance P6 se prouve au demarrage (batterie >= +20 A) mais se maintient
  en charge (batterie >= 0) : sinon la charge du vehicule, qui detourne
  precisement le surplus, detruit sa propre condition d'autorisation.

Golden 39 -> 40. Preuve par mutation sur midday-inversion-start-hold : code
d'avant -> stop / "Conditions de maintien perdues", apres -> step_up 19 A.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KKzkkVTG6KXnGXFFC7X9XJ
2026-08-24 12:46:18 +02:00
Tallagrand Thierry
819eafd4d4 P10: mémoire 'véhicule plein' en journée (collant jusqu'au débranchement)
- WINDOWS: dayVehicleFullIdle (statut 3 ou VDF, héritages jour<->nuit),
  sourceMode blocked même face au solaire
- DECISION: branche collante day_vehicle_full_idle au-dessus des gardes
- échappatoires force_charge_now/force_grid_now (golden)
- golden 33->36, validé live 10:04:30 (latch en 16 s sur la VW pleine)
- HANDOFF §22 + MANUEL états collants
2026-08-09 10:06:25 +02:00
Tallagrand Thierry
0419c741de P9: charge réseau avant départ (hors HC) + sélecteur véhicule VW/JEEP
- helpers: input_select.victron_evcs_vehicle, departure_time_jeep (06:30),
  toggle allow_grid_departure_urgent (OFF) — package victron_evcs_vehicles.yaml
- INPUT_PARSE: departureTimeMin par véhicule sélectionné
- WINDOWS: fenêtre départ ≤120min hors HC → grid_hc_allowed (solar_only prioritaire),
  état collant departure_done_idle à la fin de charge
- BCL: gridAllowedBySoc += departureUrgentActive
- DECISION: branche collante departure_done_idle au-dessus des gardes
- HA_SERVICE_CALLS: takeover helper tenu pendant P9 (ACM + hystérésis)
- hystérésis: cut immédiat étendu à departure_done_idle (patch rejouable)
- golden 29→33, HANDOFF §21
2026-08-09 08:27:24 +02:00
Tallagrand Thierry
98c9202bad Advisor: premier câblage réel — cible SOC matin (toggle, bornes, fraîcheur 120min)
- nouveau toggle input_boolean.evcs_advisor_apply_night_target (package + carte, OFF par défaut)
- snapshot/INPUT_PARSE: advisor_apply/target/status/last_run (+parseAdvisorRunToMs DD/MM HH:mm)
- WINDOWS: batteryMorningTargetPct = advisor si ON+ok+frais, clamp [max(22,hardFloor),90],
  sinon règles forecast; traçabilité '(ADVISOR)' dans la reason DECISION
- golden 26→29 (appliqué / stale / toggle off)
- HANDOFF §20 (surveillance + rollback 3 niveaux)
2026-08-09 07:32:46 +02:00
Tallagrand Thierry
9bc4af0ea1 Fixes A/B/C/D incident rebond EDF nuit 07-08/08 + GRAFCET doc
- A: état collant night_recovery_done_idle + bande cible−2 (anti-rebond takeover)
- B: snapshot relit night_session_start_soc/at (fin des réécritures 30s)
- C: hystérésis grid — turn_off_vehicle_unplugged (coupure immédiate, bypass min_on)
- D: evcs_status_raw==3 (charged) = fin de charge immédiate
- golden 19→23 scénarios (2 attendus mis à jour, 4 nouveaux)
- MANUEL_COMPLET §11.6 GRAFCET cycle nuit, HANDOFF §17, vision-notes incident
2026-08-08 14:20:21 +02:00
Tallagrand Thierry
1ae8129658 R2: git = source de vérité — src/nodes/ + build/extract intégrés au push
- src/nodes/*.js (6 nodes + template snapshot) = source de vérité unique.
- n8n-push.mjs reconstruit le JSON depuis src/ avant syntaxe+golden+PUT
  (src gagne toujours; n8n-extract.mjs pour resynchroniser si édition UI).
- n8n-build.mjs --check pour vérifier la synchro.
- Copies historiques déplacées dans archive/ (mortes, dérivées de la prod).
- .gitignore: backups locaux de rollback.
- AGENTS.md: règle R2 pour toutes les futures sessions (dont Qwen local).
- Validé: idempotence, mutation/restauration, pipeline push complet en réel.
2026-08-07 21:03:52 +02:00