Files
erp/fleet/harness/promote
arcodangeandClaude Opus 5 d4c8d0d68d 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]>
2026-08-13 20:36:45 +02:00
..

fleet/harness/promote/ — the gated pipeline

Rehearse on the sandbox → an independent agent judges → a human decides → production → a second agent verifies what actually landed.

Why this exists as code

The discipline was already written down — ADR-0003, the operating rules in AGENTS.md, the promote flow — and it 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. Nothing was wrong with the result; everything was wrong with the path. A rule an operator can skip is a recommendation.

So the stages here are chained by artefacts, not by good intentions. Each stage refuses to run until the previous one has produced its file, and the file has to say what the stage needs to hear:

Stage Produces Refuses unless
1 rehearse 01-rehearsal.json the target is the sandbox (host-guarded)
2 judge --pre 02-pre-verdict.json a rehearsal exists and its writes succeeded
3 gate 03-gate.json a pre-verdict exists; a human types the decision
4 apply 04-applied.json the gate says approved, by a named human, for this manifest
5 judge --post 05-post-verdict.json production was applied

The gate binds to a manifest digest: approving a change-set approves that change-set. Edit one amount afterwards and stage 4 refuses — the approval no longer matches what is about to be written.

The two judges

Both are context-free: they receive the manifest and the evidence, never the conversation that produced them. Per the PRD cross-family rule, a judge SHOULD be a different model family than whoever built the change-set — the admitted runtimes are Mistral (vibe -p) and Ornith 35B (hermes MLX), both proven at verdict parity in erp#63.

  • Pre-gate — prompted to refuse: find why this change-set is not safe to promote. It reads the rehearsal evidence, not a description of it. Its verdict goes to the human as an opinion, not a veto: a BLOCK still lets the operator approve, and the override is recorded in the gate file.
  • Post-gate — prompted to doubt the success: compare what production now holds against what the sandbox rehearsal predicted, and report drift. It runs after the writes, so it cannot prevent them — it exists so a silent discrepancy becomes a recorded finding instead of a surprise months later.

Judges are advisory by design. The blocking authority is the human gate and the host guards; an LLM verdict never silently stops or starts a production write.

Usage

P=fleet/harness/promote/pipeline.py
python3 $P rehearse  --manifest changeset.json --run-dir runs/2026-07-24-m3-deferred
python3 $P judge     --run-dir runs/... --stage pre  --runtime mistral
python3 $P gate      --run-dir runs/...            # interactive; records who and when
python3 $P apply     --run-dir runs/...            # needs ARCO_PROD_CONFIRM
python3 $P judge     --run-dir runs/... --stage post --runtime ornith

Every stage appends to journal.jsonl. The run directory is the evidence pack: it is what you keep, and what an auditor reads.

What this does not do

It does not replace the host guards (dol-write.sh refusing non-sandbox hosts, guard.ts requiring an explicit production opt-in, the chronology guard in invoice-create.sh). Those are structural and stay underneath. This pipeline adds sequence and evidence on top of them.