Files
erp/fleet/harness
arcodangeandClaude Opus 5 01441df5ea feat(erp): le retainer KissMetrics se facture en dollars — adc-006 tranchée
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]>
2026-08-24 15:14:24 +02:00
..

fleet/harness/ — the multi-runtime harness layer

The harness is the orchestration layer around the atoms: builder sessions that execute backlog issues, cold verifiers that check them (locate-tests, backlog audits, refutation passes), and the evidence flow into Gitea. Per the PRD model-fleet harness portability (operator direction 2026-07-15), this layer must not have Anthropic as a hard dependency: the same loop runs on Mistral (vibe -p, mistral-medium-3.5) or on hermes-served local models (Ornith / MLX, 127.0.0.1:18080). Claude is an escalation tier, not a prerequisite. Admission of a runtime to a role is evidence-gated (erp#63): verifier roles first, scoped builders benched second, and no acceptance gate is ever relaxed for a cheaper runtime.

Layout

Path Role
verifier/locate-test.md canonical locate-test: prompt, inputs, ground truth, pass rule
verifier/backlog-audit.md canonical cold-reader backlog audit: prompt, inputs, rubric
bin/run-verifier.sh run a verifier test against a runtime; emits a JSON transcript
bin/vibe-builder.sh run a scoped builder bench (vibe -p) inside a worktree, with caps + journal
runs/<date>/ committed evidence transcripts, when they back an issue comment

Runtimes

Runtime How the harness reaches it Typical role
claude a context-free subagent in a Claude Code session, given the exact assembled prompt (run-verifier.sh <test> --print-prompt) and nothing else baseline verifier; multi-file builder (default per the PRD complexity ceiling)
ornith hermes MLX server, OpenAI-style POST /v1/chat/completions on 127.0.0.1:18080, model leonsarmiento/Ornith-1.0-35B-5bit-mlx verifier (candidate)
mlx --model <id> same endpoint, any model the server lists under /v1/models verifier (candidate)
mistral vibe -p programmatic mode, tools disabled, model = the vibe active_model (today mistral-medium-3.5) verifier (candidate); scoped builder via vibe-builder.sh

Verifier protocol — no self-grading

  1. Assemble the prompt from the canonical test file + the pinned input documents (run-verifier.sh embeds file contents verbatim and records their sha256).
  2. Run every candidate runtime on the same assembled prompt, temperature 0.
  3. An independent, context-free judge (never the session that built the thing, per the PRD qa-strategy) scores each transcript against the test's ground truth and emits the parity table. A runtime is admitted to verifier duty when it reaches verdict parity with the Claude baseline on both tests.
  4. Once a non-Claude verifier is admitted, prefer cross-family verification: the verifier SHOULD be a different model family than the builder — a foreign family refuting the builder is stronger evidence than the builder's own family agreeing with itself.

Builder bench protocol

vibe-builder.sh runs one tightly-footered backlog issue end-to-end under a non-Claude runtime, against the unchanged Execution footer and acceptance gates. It measures completion, intervention count and wall-clock; a failed bench is a valid result — it sets the complexity ceiling honestly. Safety bounds:

  • refuses to run anywhere that is not a linked worktree (never the trunk — same structural-guard pattern as dol-write.sh);
  • hard caps: --max-turns and --max-price are always set;
  • --auto-approve is acceptable only because the blast radius is bounded: a disposable worktree, read-only API credentials, and the caps above;
  • the full vibe JSON journal is kept per run.

Recurring tasks on the Mistral tier

A recurring task (T11 reminders, T13 drift checks, T14 backup freshness) is a scoped builder with a standing prompt: cron (hermes cron or the operator's scheduler) calls vibe-builder.sh <worktree> <task-prompt.md> and routes the journal into the digest. The task prompt lives with the atom (fleet/atoms/<atom>/prompt.md + its class skeleton); the harness only supplies the bounded execution shell. No recurring task writes outside its worktree, and anything ERP-write-shaped still goes through the sandbox + promote gate — runtime choice never changes the gates.