DEUX CHOSES.
1. RÉCONCILIATION. Le trunk accusait 49 commits de retard, 0 d'avance : tout son
delta vivait dans l'arbre de travail, mélange d'améliorations réelles et d'état
périmé. Il est remis sur origin/main, et son état est préservé sur la branche
trunk-snapshot-20260814. Ce qui méritait d'être porté l'est ici :
- test/main.ts : chemin Work/ au lieu de Desktop/ (l'ancien n'existe plus) et
module TIERS activé. L'URL par défaut reste corrigée par la garde d'hôte ;
- ansible README : la commande du playbook recurrentBackup ;
- company.json : siteWeb arcodange.fr et email @arcodange.fr — le trunk était
à jour sur ces deux champs quand main portait encore duckdns et gmail.
NE SONT PAS PORTÉS, délibérément :
- le .gitignore du trunk, qui avait PERDU les règles replay-packs ;
- company.json SAS / 6201Z, périmé — main porte SARL / 6201Z depuis le PR #77 ;
- un console.log de débogage dans forms.ts ;
- createEphemeralAgent + permissions.ts + orchestratorExample.ts : travail
cohérent mais qui DOUBLE le modèle de scopes durables (scopes.ts +
provisionAiUser.ts) effectivement livré. Deux conceptions concurrentes de la
même chose ne se portent pas en silence — arbitrage opérateur.
2. LA NOTE DU MODE VIERGE ÉTAIT FAUSSE. Elle annonçait qu'il manquait un « golden
empty ». Rien ne manquait : la chaîne existe en entier dans test/, et c'est
main.ts qui pilote l'installeur. J'avais proposé de construire ce qui était déjà
là, faute d'avoir regardé — exactement le contrôle inscrit dans AGENTS.md le
matin même. La note décrit désormais la chaîne réelle :
blank --yes -> main.ts -> install.lock -> provisionSandbox.ts
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
main.ts pilote l'ASSISTANT D'INSTALLATION de Dolibarr (initialSetup.ts va sur
/install/). C'est le script le plus destructeur du dépôt, et il était le seul
des scripts UI à n'avoir aucune garde d'hôte — alors que la docstring de
guard.ts nomme précisément le danger : « test/.env ships DOLIBARR_ADDRESS
pointing at PRODUCTION, so a script run with the ambient environment would
drive the real ERP ».
Pointé sur la production avec l'environnement ambiant, il relançait l'installeur
sur l'ERP réel. assertSandbox() n'autorise désormais que le bac à sable, sauf
double opt-in explicite (ARCO_ALLOW_PRODUCTION + ARCO_PROD_CONFIRM) — que
l'installation initiale d'un nouvel environnement justifie, et qui reste un
geste d'opérateur.
Vérifié : DOLIBARR_ADDRESS=https://erp.arcodange.lab est refusé.
Ceci clôt le mode vierge, qui n'avait rien à inventer. La chaîne existait déjà
en entier :
sandbox-lifecycle.sh blank --yes purge la base, retire install.lock
test/main.ts pilote l'installeur, société, affichage, modules
touch install.lock reverrouille (procédure du README)
test/provisionSandbox.ts agent d'écriture, droits, clé API
Le « golden empty » proposé au commit précédent était une invention pour un
problème résolu : je n'avais pas regardé test/. C'est exactement le contrôle
inscrit dans AGENTS.md le matin même — lire l'existant avant de modéliser.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Bash lit ses scripts au fil de l'eau. Éditer le fichier en cours de route décale
les offsets et corrompt l'exécution : un refresh-from-prod en cours s'est mis à
afficher les messages de blank_sandbox, puis a fini sur « syntax error ». Le
travail avait bien abouti — sandbox restaurée, 295 tables, 10 factures, 12 tiers
— mais la trace mentait sur la fonction qui tournait. Sur un script qui purge des
bases, une trace qui ment est inacceptable.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
DEUX CHOSES, dont une inachevée et dite comme telle.
1. LA SAUVEGARDE N'AVAIT AUCUNE GARDE DE CLUSTER. 24 appels kubectl nus, aucun
contexte épinglé — alors que le script lit des secrets, crée des Jobs et sait
RESTAURER une base. Lancé sur le contexte courant d'une station de travail, il
serait parti chercher les secrets Longhorn du cluster d'un client. Même patron
que ops/sandbox : ERP_KUBE_CONTEXT, wrapper K(), empreinte positive vérifiée
avant tout dispatch. Testé contre le cluster client et contre un contexte
inexistant : il refuse les deux.
Sauvegarde de production passée dans la foulée. La base a été dédupliquée à
juste titre — rien n'avait été écrit depuis la sauvegarde automatique de 01:00.
Au passage, le CronJob quotidien tourne bien depuis juillet ; il était encore
noté comme à faire.
2. LE MODE VIERGE PURGE MAIS NE RECONSTRUIT PAS. `blank --yes` vide la base du
bac à sable (295 tables -> 0, vérifié) avec trois protections empilées : refus
sans --yes, garde de cluster, et une relecture de current_database() DANS le Job
lui-même — une purge sur la mauvaise base ne se rattrape pas par un refresh,
contrairement à la bonne.
Mais l'instance ne se reconstruit pas. Premier essai : install.lock vit sur le
volume documents, que la purge ne touche pas, si bien que Dolibarr servait un
login sur un schéma inexistant. Correctif appliqué — retrait du verrou puis
redémarrage. Second essai : le verrou reste absent, et le schéma reste à ZÉRO
table. L'entrypoint de l'image ne lance aucune installation automatique.
CE QU'IL MANQUE est donc nommé dans le script : un « golden empty », pg_dump
d'une instance fraîchement installée — schéma et données de référence, aucune
donnée métier. `blank` le restaurerait au lieu de laisser la base vide, comme
refresh-from-prod restaure le dump de production. Seule la source change.
En l'état `blank` laisse le bac à sable inutilisable, et le script le dit. Le
bac à sable a été remis iso-prod avant de rendre la main.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
37/37 mouvements appariés à la date et au montant sur les trois comptes. Le
manifeste décrit l'exercice exactement comme la banque le connaît, et la banque
est le seul référent qu'on ne peut pas réécrire : Qonto et Wise sont hors de
Dolibarr. C'est le test qui compte, et il passe.
L'appariement se fait sur (date, montant) et SUR RIEN D'AUTRE. Le libellé n'y
participe pas — c'est précisément ce que le rejeu corrige, il ne peut pas servir
à valider.
Les corrections sont DÉCLARATIVES : chacune nomme le défaut, sa cause, son
remède et sa portée. Une correction sans cause écrite est une réécriture
silencieuse, et c'est ce qu'un repreneur lira pour comprendre pourquoi le livre
reconstruit diffère de l'ancien.
- 20 des 37 mouvements portent un libellé Dolibarr générique, illisible au
grand livre ;
- les 7 indemnités ne se journalisent pas faute de compte auxiliaire — le
correctif (455110) est vérifié en sandbox ;
- la TVA d'OVH est réclamée en déduction alors que la facture est au nom
personnel du fondateur : 7,30 TTC au taux 0, déductible 353,76 → 352,54.
Aucune déclaration déposée (CA12 annuelle), la correction est gratuite.
CE QUI RESTE BLOQUÉ, et ce n'est pas un détail d'implémentation : un rejeu EN
ÉCRITURE exige une instance VIERGE. sandbox-lifecycle.sh ne sait que
refresh-from-prod — un re-seed iso-prod, qui réimporte précisément les défauts
qu'on veut corriger. Écrire le rejeu sans cette capacité produirait des
doublons, pas une reconstruction. Le mode vierge est le préalable.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Un exercice OUVERT n'a d'engagement envers personne : aucun compte déposé,
aucune liasse, aucun FEC remis. Tant qu'il en va ainsi, une erreur de
MODÉLISATION se corrige mieux en rejouant depuis les pièces qu'en la
contournant — la piste d'audit fiable demande que l'écriture reflète sa pièce,
et rejouer resserre ce lien là où une rustine le distend.
extractExercise.ts ne fait que LIRE. Il produit la description ordonnée des
faits : tiers, factures client et fournisseur avec leurs lignes et règlements,
et l'intégralité des mouvements bancaires.
Les mouvements bancaires sont le RÉFÉRENT : Qonto et Wise sont hors de
Dolibarr, on ne peut pas les réécrire. Le test d'acceptation d'un rejeu est
donc qu'ils soient retrouvés au centime et à la date.
La ligne de partage entre ce qui se rejoue et ce qui est figé n'est pas
technique mais juridique : est figé tout ce qu'un tiers détient — facture
client ENVOYÉE, dépôt au greffe, déclaration transmise — et tout exercice clos.
Les factures client sont donc extraites avec delivered:true : un rejeu les
reproduit à l'identique, il ne les recalcule pas.
Les exclusions sont DÉCLARATIVES ET MOTIVÉES, jamais codées en dur : un
auditeur doit lire pourquoi une référence manque sans avoir à le deviner. Deux
pièces sont écartées — FAC001-CL00001, dont la référence était malformée par un
défaut de masque, et l'avoir qui l'annulait. L'opérateur a établi qu'elle n'a
jamais quitté la société : KissMetrics a été facturé via Wise en février, et le
document qu'il détient référence FAC001-CL0001001. L'avoir ne documentait donc
qu'un faux pas interne. La séquence FAC reste continue (FAC001 à FAC008), la
réémission portant elle aussi FAC001 — rien à renuméroter.
Deux constats que l'extraction met au jour :
- 20 des 37 mouvements bancaires portent un libellé Dolibarr générique
(« SupplierInvoicePayment », « CustomerInvoicePayment ») qui traverse le grand
livre tel quel et y est illisible pour un repreneur comme pour un inspecteur ;
- FAF2025001 (OVH, 7,30 EUR) est datée du 24/10/2025, trois mois avant
l'immatriculation, et NE FIGURE PAS à l'annexe 1 des statuts qui recense les
actes accomplis pour le compte de la société en formation. Une dépense
antérieure à l'existence de la société ne peut être supportée par elle que si
elle est reprise par cet état ou ratifiée ensuite.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>