c96deb5ad6ba1c2c394b26a49017b80bc9a2c8b1
8
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
c96deb5ad6 |
feat(erp): les deux comptes bancaires tombent au centime — reprise FAC004/FAC006, réparation FAC009, cashbacks
Suite de la réconciliation ouverte par la question de l'opérateur. Après ce
passage, l'ERP et la banque disent le même chiffre sur les deux comptes :
Qonto 2 013,30 EUR et Wise 11 863,85 EUR, écart 0,00.
CE QUI A DÉBLOQUÉ LE RESTE. L'opérateur a établi que KissMetrics n'a JAMAIS reçu
la moindre facture — le client payait le forfait de 2 500 USD sans pièce. Rien
ne s'opposait donc à corriger des factures qu'on croyait entre ses mains.
FAC004 et FAC006 portaient toutes deux 2 145,92 EUR, le même montant repris tel
quel d'un cycle à l'autre, alors que la banque a reçu 2 147,00 EUR le 29/05 et
2 195,97 EUR le 25/06 : 2 500 USD à chaque fois, au taux du jour du paiement.
51,13 EUR d'encaissement manquaient aux livres. Leurs notes qualifiaient déjà
l'écart d'« écart de change » — défendable tant qu'on ne pouvait pas toucher aux
factures ; plus vrai maintenant qu'on le peut. Une facture, un règlement, une
ligne bancaire, le même chiffre.
RÉPARATION DE FAC009. `PUT /invoices/{id}/lines/{lid}` n'est PAS un PATCH : tout
champ absent du corps est remis à zéro. La correction appliquée ce matin ne
passait que subprice, pu_ht, qty et desc — `product_type` est donc retombé de 1
(service) à 0 (produit), faisant de FAC009 la seule ligne du registre typée
« produit » dans une société qui ne vend que des prestations et facture hors UE
sous l'art. 259-1° du CGI. Rétabli.
C'est le juge pré-gate qui l'a vu, sur la répétition de FAC004 et FAC006 — où le
même appel avait en plus effacé `desc` ENTIÈREMENT. Premier finding de la
journée qui apporte quelque chose de réel, et il aurait envoyé au client deux
factures sans désignation. Les charges de ligne sont désormais complètes.
test/deleteInvoicePayment.ts. Corriger une facture encaissée suppose de refaire
son règlement, et l'API REST ne sait pas le supprimer : `PUT
/invoices/{id}/payments` ne met à jour que son NUMÉRO. Deux verrous dans
l'interface, consignés dans le fichier : tant que la facture est marquée payée
le lien de suppression est simplement ABSENT — pas d'erreur, rien — il faut
d'abord la rouvrir ; et la confirmation est une boîte MODALE jQuery dont les
boutons n'ont ni name ni value, si bien qu'un sélecteur sur input[type=submit]
ne trouve rien et que la suppression n'a pas lieu, silencieusement. Sans
--payment, le script retrouve le règlement sur la fiche : coder en dur un
identifiant relevé en bac à sable est une façon commode de supprimer le mauvais.
ABONNEMENT ANTHROPIC D'AOÛT. 90,00 EUR du 19/08, reçu 2330-6710-2536 arrivé dans
books@ le 24/08. Second abonnement à usage interne, distinct du budget IA client.
CASHBACKS WISE — compte 768. Wise parle de « remise », mais le montant suit le
SOLDE et non les frais : 0,19 EUR sur ~510 EUR en mars, 4,97 EUR sur ~9 700 EUR
en août, soit ~0,5-0,6 % l'an, stable. C'est la rémunération d'un solde, donc un
produit financier. Le seul frais Wise de l'exercice est le forfait de 50 EUR du
26/01, sans rapport. Ni 763, qui vise les revenus de créances commerciales, ni
764, réservé aux valeurs mobilières de placement : 768.
RISTOURNE QONTO — compte 627 au crédit. L'opération est typée `qonto_fee` par la
banque elle-même, côté crédit : une remise sur un service reçu vient en
diminution de la charge, elle ne crée pas un produit.
PIÈGE CATALOGUÉ. bank-match.sh continuera d'afficher ces sept écritures en
BANK-ONLY : il ne rapproche que les RÈGLEMENTS de factures et ignore les
paiements divers. Les voir listées ne veut donc PAS dire qu'elles manquent —
c'est écrit dans known-patterns.json, sans quoi le prochain agent les
ressaisirait.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
|
||
|
|
b745fdcb43 |
feat(erp): encaissement KissMetrics du 17/08, deux échéances URSSAF, règlement des charges sociales
L'opérateur a demandé si le versement de la semaine passée avait été pris en compte. Il ne l'était pas — et la réconciliation menée en réponse a fait sortir trois autres trous. L'ENCAISSEMENT. KissMetrics a viré 2 164,75 EUR le 17/08 (Wise, réf. VENDOR:DEV), soit 2 500,00 USD au taux du jour : la part fixe du cycle M4. Le mouvement n'était pas enregistré, si bien que FAC009 a été émise le matin même au taux du 24/08 (2 140,45 EUR) alors que le contrat arrête le montant dû « au taux du jour du règlement ». La facture était fausse de 24,30 EUR, et déjà payée. Le précédent commandait la méthode : FAC008 avait été libellée 2 185,00 EUR, exactement la somme reçue le 20/07, même schéma de règlement anticipé. Sur arbitrage de l'opérateur, FAC009 est repassée en brouillon, portée à 2 164,75 EUR, revalidée sous le même numéro et la même date, puis adossée au virement. Sa note reprend la rédaction de FAC008. Le juge a bloqué en relevant que `last_main_doc` était présent — donc, selon lui, facture émise. Il confond PDF PRODUIT et facture TRANSMISE : le PDF avait été produit deux heures plus tôt par l'agent lui-même pour assembler le dossier, et le message d'envoi est un brouillon jamais envoyé. Mais il a raison sur la conséquence, et c'est le seul de ses findings de la journée qui apporte quelque chose : le PDF sur disque devenait faux. Il a été régénéré aussitôt. Son second finding — `emetteur` null — tombe : le champ est null sur les huit lignes d'encaissement Wise antérieures ; le renseigner sur la seule ligne d'août romprait la permanence des méthodes au lieu de la servir. LES DEUX ÉCHÉANCES URSSAF. 493,00 EUR prélevés le 22/05 et 1 215,00 EUR le 17/08 n'étaient enregistrés ni l'un ni l'autre. Créés comme charges sociales — pas comme factures fournisseur : l'URSSAF n'est pas un fournisseur — puis réglés depuis Qonto. test/paySocialCharge.ts comble un trou du chemin gated : recordSocialCharge.ts crée la charge et la laisse impayée, et rien n'enregistrait le règlement. Tant qu'il manque, le mouvement bancaire reste orphelin et le solde de l'ERP diverge de celui de la banque — c'est ce qui laissait ces deux échéances invisibles. Dolibarr n'expose aucune route REST pour les charges sociales, donc le pipeline ne peut pas porter l'opération ; le script garde ce qu'il peut de sa discipline. Trois pièges consignés dans le fichier, dont un qui a coûté une fausse alerte : NE PAS chercher « payée » dans la page — « ImPAYÉE » contient « payée ». Une première version déclarait ÉCHEC sur un règlement qui venait d'aboutir, ce qui pousse à rejouer, donc à créer un doublon. On lit le montant restant dû. LE RUNBOOK disait 646. C'est faux depuis adc-009 : dans une SARL à l'IS, ce que la société verse à l'URSSAF pour son gérant majoritaire est une charge de personnel, donc 641. Corrigé, avec la mention explicite que toute version portant 646 est périmée. Ajouté aussi ce que la fiche de charge confirme à l'écran — « Code comptable: Inconnu » : le type de charge ne pilote PAS le compte sur ce déploiement, contrairement à ce que le runbook laissait croire. Enfin l'exemple `--period 2026` du runbook était invalide : le script découpe la valeur comme une date ISO et produisait `undefined/undefined/2026`, ce qui fait échouer la création sans message. Reste au tableau, documenté et non traité ici : six cashbacks Wise (11,13 EUR), un remboursement Qonto (5,22 EUR), l'abonnement Anthropic du 19/08 (90,00 EUR, reçu pas encore arrivé), et deux encaissements client sous-enregistrés — FAC004 et FAC006, 51,13 EUR reçus de plus que ce que l'ERP porte. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> |
||
|
|
bcea21f96b |
feat(erp): cycle M4 KissMetrics, comblement des écarts bancaires, clause de pénalités rétablie
Deux passages gated en production, et l'outil qui manquait pour clore le dossier client. CYCLE M4 (2026-08-24-m4-et-ecarts). Cinq opérations : le tiers Anthropic PBC — entité américaine, distincte d'Anthropic Ireland Limited — et sa facture de 90 EUR réglée le 19/07 ; le règlement Darnis de 262,20 EUR ; les deux factures du cycle M4 (part fixe 2 500 USD → 2 140,45 EUR échéance 22/09, part différée 3 000 USD → 2 568,54 EUR échéance 23/11). Les trois écarts bancaires relevés sont comblés ; Qonto tombe à 3 806,08 EUR. Le juge pré-gate a bloqué : le règlement Darnis du 14/08 précède la facture datée du 31/08. C'est un fait bancaire, pas une erreur de saisie — Darnis facture en fin de mois — et adc-008 avait EXPRESSÉMENT prévu ce blocage et autorisé le passage outre tracé. L'art. 289 du CGI qu'invoque le juge régit la numérotation des factures ÉMISES, non l'ordre entre un paiement et sa facture ; c'est la troisième fois de la session qu'il l'étend hors de son champ. CLAUSE DE PÉNALITÉS (2026-08-24-clause-bilingue). Les factures M4 sont sorties avec une clause AMPUTÉE : sans la traduction anglaise, sans la mention « Ces stipulations sont des minima légaux d'ordre public auxquels il ne peut être renoncé ». Détecté par le contrôle des mentions obligatoires sur les PDF — avant tout envoi. La note publique est rétablie dans sa rédaction de référence, à droit constant : aucun montant, aucune date, aucune ligne touchés. Deux raisons de corriger plutôt que de laisser courir. La permanence des méthodes (PCG art. 121-5, adc-011) : une clause légale identique doit être rédigée identiquement, sans quoi la variation se lit comme une intention. Et le fond : le destinataire est américain, c'est la version anglaise qui lui rend la clause opposable en fait — et c'est sur elle que s'appuie la relance. Le juge a bloqué là aussi, en supposant que la référence portait « ce montant ». Il ne l'avait pas vérifié, et son propre residual risk demandait de le faire. Vérification faite et consignée (02b-preuve-reference.txt) : FAC005 à FAC008 portent toutes « ce forfait », et la clause proposée leur est identique au caractère près — 1283 car. Le finding était inversé. test/buildInvoicePdf.ts. Valider une facture par l'API ne produit AUCUN PDF : le fichier n'existe que si quelqu'un l'a demandé. `PUT /documents/builddoc` le ferait mais répond 403 — ce droit n'est accordé à aucun scope agent. Le script emprunte donc l'interface, puis relit le fichier par l'API : on ne croit pas la page de retour, on vérifie que le PDF est déposé et lisible par un tiers. Les deux juges post-gate passent sans dérive. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> |
||
|
|
480888937f |
feat(erp): indemnité d'occupation janv→juil 2026 (1 483,23 €) — paiements divers, sans tiers (#89)
Co-authored-by: Gabriel Radureau <[email protected]> |
||
|
|
1cba032512 |
fix(security): production read agent is now actually read-only
Migration applied to production, in the only order that does not break the promote flow: 1. Created `ai_agent_prod_prod_write` (id=5) with the narrow `prod-write` scope — invoices, payments, and document submission (needed by builddoc to regenerate a modified invoice's PDF). Verified functionally: reads pass, DELETE on an invoice returns 403. 2. Repointed the promote pipeline at that user's key. It no longer borrows the read skills' credential; if the key is absent it dies with the provisioning command rather than silently falling back. 3. Revoked 12 write/delete rights from `ai_agent` (id=3), the credential every read skill holds: create/modify on customer AND supplier invoices, thirdparties, contacts, thirdparty payment details, proposals, exports, accounting links — and delete on proposals, events, and GED documents. Verified after: invoices, thirdparties, contacts, products, proposals, supplier invoices and bank accounts all still read; creating an invoice returns `403 Forbidden: Insuffisant rights`. The documented posture and the real one finally agree. scopes.ts corrected against the live instance: 262 is NOT "créer/modifier les produits" as the first catalogue guessed but the `voir_tous` ACL extension — a READ right the skills depend on (without it, list endpoints return empty arrays instead of 403). Revoking it would have silently blinded every read skill. This is why the audit reads labels off /user/perms.php rather than trusting ids in code. The READ_ONLY baseline is now the audited read surface (35 rights), not a guess. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_01VRShc4QhLLU73FLHx9vskh |
||
|
|
a2cafc0d6b |
feat(harness): gated promote pipeline — rehearse, judge, human gate, apply, judge
The discipline (ADR-0003, the promote flow, the operating rules) was written down and still depended on whoever was driving choosing to follow it. On 2026-07-25 an agent session wrote five documents into the production ledger through direct API calls, bypassing the promote flow entirely — a correct result reached by a path nobody could audit. A rule an operator can skip is a recommendation. The five stages are now chained by artefacts on disk. Each refuses to run until the previous produced its file, and the file says what it needs to hear: rehearse (sandbox, host-guarded) -> judge --pre -> gate (human) -> apply (prod) -> judge --post. The gate binds to a manifest digest, so approving a change-set approves THAT change-set. An op is defined ONCE, as an API call, and replayed on the sandbox then on production — because the first design described each write twice (a sandbox script input and a prod API body) and the pre-gate judge immediately caught them diverging: the rehearsal was creating a EUR invoice with no due date while production would have received a USD one at 60 days. Two descriptions of the same write are two things that can disagree. Judges are context-free, cross-family per the PRD qa-strategy rule, and advisory: a BLOCK still lets the operator approve, and the override is recorded with their name. Blocking authority stays with the human gate and the host guards — an LLM verdict never silently starts or stops a production write. Verified end to end against the real 24/08 change-set (M3 deferred, USD 3,000): - pre-gate judge (Mistral) returned BLOCK twice, correctly — first on the sandbox/prod divergence, then on a duplicate left by a repeated rehearsal; - apply refuses after a rejected gate; - apply refuses without ARCO_PROD_CONFIRM; - editing an amount after approval invalidates the gate on digest mismatch. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_01VRShc4QhLLU73FLHx9vskh |
||
|
|
ceb4321224 |
chore(fleet): erp#63 evidence — verifier parity + builder bench transcripts
- runs/2026-07-18/: the 8 sha256-pinned verifier transcripts (4 runtimes × 2 tests), blind-judging verdicts (2 independent judges per cell, unanimous), the erp#56 builder-bench journal + prompt + caps, and the evidence README with the parity table. - run-verifier.sh: mistral runtime drops the tool-filter flag (--enabled-tools with a no-match pattern hangs vibe 2.21.0); plain -p with --max-turns 1. Verdicts: Mistral (vibe -p, mistral-medium-3.5) and Ornith 35B (hermes MLX) reach verdict parity with the Claude baseline on both tests → admitted to verifier duty. Qwen2.5-7B-4bit fails both → the honest small-model floor. Builder bench: erp#56 completed by the Mistral runtime, 0 code corrections, 261 s, acceptance run clean (0 bank-UNKNOWN) → merged as PR #68. Closes #63 (with the paired factory qa-strategy PR). Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01VRShc4QhLLU73FLHx9vskh |
||
|
|
f2a60817e2 |
feat(fleet): multi-runtime harness — verifier tests + capped builder shell
The harness layer (builder sessions, cold verifiers, evidence flow) gets a committable home, per the PRD model-fleet § harness portability and erp#63: - fleet/harness/verifier/: the two canonical verifier tests (locate-test, cold-reader backlog audit) with pinned inputs, verbatim prompts, ground truth and pass rules — judged context-free, never self-graded. - fleet/harness/bin/run-verifier.sh: runs a test against any OpenAI-style local endpoint (Ornith/MLX) or vibe -p (Mistral); emits sha256-pinned JSON transcripts. - fleet/harness/bin/vibe-builder.sh: the bounded shell for scoped builders and recurring tasks — refuses the trunk (linked-worktree guard), hard --max-turns/--max-price caps, full JSON journal per run. - fleet/README.md layout + AGENTS.md Fleet section updated in the same change (same-change freshness rule). Part of erp#63 (harness portability spike, D2). Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01VRShc4QhLLU73FLHx9vskh |