feat(erp): indemnité d'occupation janv→juil 2026 — change-set gated, au gate

La convention annexée aux statuts (annexe 3) et la décision n°1 de l'associé
unique du 09/01/2026 fixent une indemnité d'occupation de 220 EUR/mois pour un
bureau de 10 m² dans le domicile du gérant. Elle n'a JAMAIS été ni versée ni
comptabilisée : aucun tiers, aucune des 15 factures fournisseur, aucune ligne au
grand livre bancaire.

Change-set : un tiers « Radureau Gabriel » (FO0012) puis 7 factures fournisseur
validées et réglées par inscription au compte courant d'associé (compte 3), sans
mouvement de trésorerie — la voie adc-005. Janvier au PRORATA : la convention
prend effet à sa signature, 23 jours sur 31 → 163,23 EUR. Total 1 483,23 EUR et
non 1 540 : un mois plein aurait été légèrement généreux.

Chaque facture porte en note le fondement complet et le calcul qui justifie le
forfait — loyer 1 100 EUR, quote-part de surface 16,67 % → 183,33, charges
réelles 12,53 au prorata, soit 195,87 de prorata strict contre 220 retenus
(+12,3 %). La justification vit ainsi avec l'écriture, pas dans une note à part.

Répétition sandbox : 8 opérations, 14 suites, toutes ok. Dates vérifiées en
Europe/Paris — lues en UTC elles semblent reculées d'un jour, Dolibarr tronquant
à minuit heure serveur. Compte courant : -429,75 → -1 912,98.

Corrige un vrai défaut du pipeline découvert en route : quand une op échoue,
{id} était remplacé par le DICTIONNAIRE D'ERREUR, l'URL devenait un JSON
multiligne, urllib levait InvalidURL et l'étape mourait AVANT d'écrire son
artefact — on perdait la preuve de l'échec qu'on venait de produire. Corrigé
dans rehearse et dans apply, où --keep-going exposait la même faille en pleine
écriture de production.

Verdict pré-gate : BLOCK (mistral), au motif d'une numérotation non chronologique
contraire au CGI art. 289. FAUX POSITIF, démontré sur la production : les
factures FOURNISSEUR portent déjà 5 ruptures de chronologie, leur séquence
suivant l'ordre d'enregistrement et non la date du document ; les factures
ÉMISES, seules visées par l'art. 289, en comptent 0. Le juge a appliqué au
registre des factures reçues une règle qui ne gouverne que celles émises. Sa
recommandation de renumérotation casserait la piste d'audit fiable.

Le gate humain n'est pas franchi : rien n'est écrit en production.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
This commit is contained in:
2026-08-13 20:36:45 +02:00
co-authored by Claude Opus 5
parent 216dc0213b
commit d4c8d0d68d
5 changed files with 13800 additions and 0 deletions
+20
View File
@@ -112,6 +112,17 @@ def stage_rehearse(args) -> None:
results.append({"label": op.get("label", ""), "method": call["method"], "path": call["path"],
"rc": 1 if failed else 0, "result": r})
print(f" [{op.get('label','op')}] {call['method']} {call['path']} -> {'ok' if not failed else r}")
if failed:
# Sans ce garde-fou, {id} est remplacé par le DICTIONNAIRE D'ERREUR : l'URL
# devient un JSON multiligne, urllib lève InvalidURL, et la répétition meurt
# AVANT d'écrire son artefact — on perd la preuve de l'échec qu'on venait de
# produire. Une op ratée doit être consignée, pas faire tomber l'étape.
for follow in op.get("then", []):
results.append({"label": f"{op.get('label','')} :: {follow.get('label','follow-up')}",
"method": follow["method"], "path": follow["path"],
"rc": 1, "result": {"_skipped": "op parente en échec"}})
print(f"{follow.get('label','follow-up')} -> ignoré (op parente en échec)")
continue
for follow in op.get("then", []):
fr = api(base, skey, follow["method"], follow["path"].replace("{id}", str(r)), follow.get("body"))
ffailed = isinstance(fr, dict) and "_error" in fr
@@ -259,6 +270,15 @@ def stage_apply(args) -> None:
print(f" [{op.get('label')}] {call['method']} {call['path']} -> {'ok' if not failed else r}")
if failed and not args.keep_going:
break
if failed:
# --keep-going poursuit la boucle : sans ce garde-fou on retomberait sur le
# même InvalidURL qu'en répétition, en pleine écriture de production.
for follow in op.get("then", []):
results.append({"label": f"{op.get('label')} :: {follow.get('label','follow-up')}",
"method": follow["method"], "path": follow["path"],
"result": {"_skipped": "op parente en échec"}, "ok": False})
print(f"{follow.get('label','follow-up')} -> ignoré (op parente en échec)")
continue
for follow in op.get("then", []):
fr = api(base, key, follow["method"], follow["path"].replace("{id}", str(r)), follow.get("body"))
ffailed = isinstance(fr, dict) and "_error" in fr