L'opérateur a demandé « on ne peut pas emettre de facture en dollars ? ». Si, et
c'est même la bonne réponse ici — au point que la question avait déjà son créneau
au registre : adc-006 était Proposed depuis juillet, sa première question ouverte
étant mot pour mot « USD multicurrency invoices vs EUR-at-settlement ? ».
LE DÉFAUT. Le contrat fixe un prix EN DOLLARS — 2 500 USD en part fixe, 3 000 en
part différée — mais les factures étaient libellées en euros, contre-valeur figée
au taux du jour d'émission. Leur propre note l'avouait : « le montant
effectivement dû en euros sera arrêté au taux du jour du règlement ». Une facture
dont le montant affiché n'est pas le montant dû.
Le coût était réel et mesuré : FAC004, FAC006 et FAC009 ont dû être reprises le
même jour parce que leur contre-valeur ne correspondait pas aux virements reçus.
51,13 EUR d'encaissement manquaient aux livres.
Le client paie d'ailleurs en dollars : les euros reçus varient de 2 147,00 à
2 195,97 pour une prestation à prix fixe, signature d'un virement en devise
converti à l'arrivée par Wise, qui ne détient qu'un solde en euros.
LE DROIT. Une facture peut être libellée dans toute monnaie (CGI art. 289, II,
transposant la directive 2006/112 art. 230) ; seule la TVA à payer doit être
déterminée en euros, et il n'y en a pas — autoliquidation par un preneur hors UE.
Les LIVRES, eux, restent tenus en euros (C. com. art. L.123-22) : la
contre-valeur inscrite n'a pas bougé d'un centime.
PÉRIMÈTRE. Les quatre factures NON RÉGLÉES, aucune n'ayant été transmise :
FAC005, FAC007, FAC010, FAC011, toutes portées à 3 000,00 USD. Les quatre réglées
restent en euros — leur montant est exactement ce qui a été encaissé et rapproché
avec la banque. Chaque facture dit ainsi sa propre vérité.
CE QUE adc-006 DOIT CONSIGNER, ET CONSIGNE. adc-002 pose qu'« une facture validée
n'est jamais ajustée pour raison de change ». Trois l'ont pourtant été le 24/08.
L'opérateur l'a arbitré au motif qu'aucune n'avait été transmise au client, et
adc-006 supprime la situation qui rendait l'arbitrage nécessaire : une facture en
dollars n'a aucune raison d'être ajustée, son montant ne dépendant d'aucun taux.
adc-002 s'en trouve restreinte, pas abrogée — les écarts de change continuent
d'aller en 766 et 666, et deviennent le cas normal au lieu de l'exception.
test/setInvoiceCurrency.ts. Deux pièges consignés. `PUT /invoices/{id}` ACCEPTE
`multicurrency_code`, répond 200, et n'applique rien : seule l'interface change
la devise. Et ce changement ABÎME la ligne — Dolibarr recalcule le montant en
devise depuis les euros et le taux du dictionnaire, puis le changement de taux
fige la devise et recalcule les euros, si bien que la contre-valeur dérive. Le
script réunit donc les deux opérations, réécrit la ligne ENTIÈRE (le PUT n'est
pas un PATCH) et refuse si la contre-valeur euro a bougé d'un centime.
Le juge a relevé, à raison, que le titre du change-set débordait de son contenu :
le passage en devise est fait par le script, en amont. Accepté et consigné au
gate ; le manifeste n'a pas été retouché pour ne pas rompre l'empreinte sur
laquelle le gate est scellé. Son residual risk — vérifier la multidevise en
production — est honoré. Juge post-gate PASS sans dérive.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
fleet/ — the atom registry
The fleet is Arcodange's AI back-office: narrow agents ("atoms") that operate the Dolibarr ERP's daily admin & accounting under the AI back-office PRD. This directory is the registry — the versioned source of truth for what the fleet may do. An atom absent from the registry does not run. Contract semantics come from the PRD atom contract; file syntax from the PRD document surface.
What an atom is
One narrow capability (classify, extract, validate, record, reconcile, report, remind) with a strict I/O contract and deterministic validators around it. The LLM proposes, code disposes: formats, arithmetic, checksums, dedupe and referential integrity are enforced by validators, and a model output that fails validation is quarantined, never auto-corrected. Workflows are compositions of atoms with explicit gates — never one prompt that "does the accounting".
Each atom lives in fleet/atoms/<atom>/:
| File | Role |
|---|---|
atom.yaml |
the registry entry — the contract (schema below) |
prompt.md |
thin runtime prompt, ≤ ~40 lines, extends exactly one class skeleton; no business rules (rules live in fleet/profile/ and in validators) |
scripts/ |
the deterministic implementation: runners, validators, scoring hooks |
Folder name = atom name = registry name — the house <app> join-key discipline
applied to atoms.
Layout
fleet/
├── README.md # this file: registry doc + atom.yaml schema
├── classes/ # the 7 prompt skeletons (PRD agent catalog)
│ ├── sentinel.md
│ ├── extractor.md
│ ├── erp-scribe.md
│ ├── deterministic-controller.md # no-LLM by design
│ ├── analyst-writer.md
│ ├── researcher.md
│ └── knowledge-archivist.md
├── atoms/
│ └── invoice-extract/ # T02 — the worked example (contract only; implementation = erp#40)
│ ├── atom.yaml
│ ├── prompt.md
│ └── scripts/
├── golden/ # per-atom golden sets — land with erp#39
├── profile/ # fiscal.yaml + calendar.yaml + ADC register + validator (profile/README.md)
└── harness/ # multi-runtime harness layer: verifier tests + builder bench (harness/README.md)
atom.yaml — the contract, field by field
Per the PRD atom contract:
| Field | Meaning |
|---|---|
name, version |
Identity. Folder name = name. version bumps on any behavioral change (prompt, model, validator) — a bump re-triggers the atom's golden-set evals. |
input_schema / output_schema |
JSON Schema for the atom's I/O; enforced at runtime (constrained decoding where the model tier supports it). |
invariants |
Deterministic post-conditions checked by code after every run (e.g. HT + TVA == TTC ± 0.01). A failed invariant quarantines the output — refuse, never repair. |
side_effect_class |
read · draft · write-sandbox · write-prod · outbound — drives which gates and credentials apply, per the PRD environment posture table. |
idempotency_key |
How a replay is recognized (e.g. supplier + ref_supplier + TTC) — a second run with the same key must be a no-op. |
autonomy |
The earned level (A0–A3 on the autonomy ladder) + a pointer to the eval evidence that justifies it. |
model_policy |
Preferred tier, fallbacks, escalation rule, per the PRD model fleet; closed per-atom by routing-bench evidence (erp#45 for the first atoms). |
eval_ref |
Where the golden set + scoring script live (fleet/golden/<atom>/). |
Two registry conveniences beyond the PRD contract fields bind the entry to the rest
of the surface: class (which fleet/classes/<class>.md skeleton the prompt
extends) and task (the PRD task-inventory
id the atom serves).
The worked example is atoms/invoice-extract/atom.yaml
(T02) — contract only; its implementation is
erp#40.
How an atom graduates
Autonomy is earned per atom, never assumed. The levels (A0 manual → A1 prepare
→ A2 rehearse + gate → A3 autonomous + audit) are defined on the PRD
autonomy ladder;
promotion and demotion are mechanical, per the PRD
autonomy promotion gates
(golden-set evals, unedited-approval streaks, incident demotion — the bars live
there, not here). The earned level and its evidence are recorded in the atom's
autonomy field: a promotion is a PR that changes that field with the evidence
linked, verified per the QA strategy's independent-verification rule.
Conventions
- English for all agent-facing files (house language policy).
- Same-change freshness: a change to an atom that leaves its
atom.yaml/prompt.mdstale is an incomplete change. - One capability per file; YAML/frontmatter over prose for anything a machine parses.
- Environment rules (trunk hygiene, read-only prod, sandbox-first writes, promote
gate) are the repo-wide ones:
AGENTS.mdoperating rules +dolibarr-sandbox-writeSKILL.md.