Ta suite de tests est verte. 142 tests passent. Tu tags, tu déploies. Et l’appli ne démarre pas — parce que les tests mockaient le store, et que le vrai boot rejoue la WAL, et qu’un segment manque. Aucun test n’a touché ce chemin-là.

Ce trou — entre « les tests passent » et « ça tient debout pour de vrai » — c’est toute la raison d’être de probatum.

Ce que probatum vérifie (que les tests ne regardent pas)

Un fichier, probatum.yaml. Des checks à plat, sans logique. Et le curl, le grep, la supervision de processus sont embarqués — tu déclares juste les règles qui font passer ou échouer un check.

# démarre le vrai service, attends qu'il réponde, garde-le vivant pour la suite
- name: api boots
  run: ./target/debug/myapp --port 8080
  ready: http://127.0.0.1:8080/healthz
  timeout: 15

# curl embarqué
- get: http://127.0.0.1:8080/api/version
  expect: 200
  contains: ['"version"']

# grep embarqué — seulement les lignes écrites PENDANT ce run
- name: app log is clean
  log: /var/log/myapp/app.log
  absent: ["ERROR", "panic"]

probatum démarre ton système réel, l’interroge, lit ses logs — puis tue tout l’arbre de processus sur chaque sortie : fin normale, panic de probatum lui-même, Ctrl-C, SIGTERM. Si probatum meurt, rien de ce qu’il a lancé ne survit. Pas de port zombie entre deux runs, pas de serveur orphelin qui traîne.

Et le contrat qui compte le plus, celui qui manque à tous les scripts bash maison :

échoué (exit 1) ≠ pas pu observer (exit 2).

Un binaire absent, une URL injoignable, un environnement sale — ce n’est pas un bug dans ton code, c’est « je n’ai pas pu regarder ». Confondre les deux, c’est te faire courir après des fantômes. probatum refuse un environnement déjà sale (le port répond avant même de démarrer le service) au lieu de le détruire : il ne purge jamais ce qu’il ne possède pas.

Le reste, c’est le filtre de bruit : panic, traceback, FATAL détectés par défaut ; et humble quand il n’est pas sûr — il montre la fin de la sortie et dit « exit 1 », il n’invente jamais une cause. Une fausse cause est pire que pas de cause.

Pourquoi pas juste une étape cidx ?

La vraie question — celle qu’on m’a posée. Je maintiens déjà cidx, un runner CI/CD déclaratif : il lance des outils contre mon code (trivy, gitleaks, go-test), à l’identique en local et en CI, groupés en phases et pipelines. Alors pourquoi un deuxième outil ?

L’aveu honnête d’abord : les deux savent lancer une commande arbitraire. Une étape cidx pourrait scripter les mêmes checks. Mais ils ne vérifient pas le même objet :

  cidx  ──▶  le CODE, statiquement
            (quels scanners/linters/tests tournent, local == CI)

  probatum ──▶ le SYSTÈME QUI TOURNE, dynamiquement
            (boot, readiness, comportement HTTP, fenêtre de log, teardown)
            ← cidx n'a rien de cette machinerie

La règle de partage tient en une ligne : lancer un outil contre mes sources → étape cidx. Démarrer mon système et observer son comportement → probatum.

Du coup, pas de fusion. probatum rejoint le roster d’outils que cidx orchestre — comme trivy, comme go-test — relié par un simple preset. Une seule source de vérité, deux lanceurs :

  probatum.yaml  ← un seul fichier

      ├── boucle interne (dev ou agent, en secondes) : probatum run
      └── boucle externe (CI, pipeline)              : cidx run test

Et le garde-fou, qui est la vraie décision de conception : si probatum se met à vouloir des phases et des pipelines, il est en train de devenir cidx → on refuse. Si cidx se met à vouloir la readiness et le process-ownership, il devient probatum → même chose. La frontière n’est pas un accident, elle est tenue exprès. Deux petits outils qui font chacun une chose et gardent leur bord.

Le moteur de preuve qui n’avait rien à prouver

Confession : probatum n’a pas commencé comme ça. Il est né « moteur de preuve » — des promesses, des oracles, un registre de preuves, tout le vocabulaire de la vérification formelle. Impressionnant sur le papier.

Je l’ai coupé. Ce vocabulaire était de la surcouche, pas de la valeur — de la grandiloquence autour d’un besoin simple : une ligne, un fichier, lance mes checks, ne me montre que ce qui compte. Un moteur de preuve qui, au fond, n’avait rien à prouver — juste quelque chose à rendre utile. (Oui, le jeu de mots est facile. Il est aussi exactement ce qui s’est passé.)

Supprimer le mot « preuve », c’est la même discipline que partout ailleurs dans ces projets : nommer la limite, jeter le vocabulaire qui gonfle. Un outil gagne ses mots ; il ne les emprunte pas à la théorie pour se donner de l’allure.

Le vrai premier lecteur, c’est l’agent

Ce qui a guidé la coupe : le premier lecteur de probatum n’est pas un humain qui scanne un terminal. C’est un agent (et l’humain par-dessus son épaule).

Un agent qui enchaîne cinquante runs bash ad-hoc se noie dans la sortie et empile les serveurs orphelins sur le port 8080. probatum lui rend un verdict (probatum run --json) et possède tout ce qu’il lance — donc on peut le boucler serré sans nettoyer derrière. C’est ça, le différenciateur face à « lance juste du bash » : la propriété des processus, le verdict qui se replie en une ligne, et la trace d’evidence pour rejouer. Une clé mal orthographiée dans le YAML est une erreur, jamais un check silencieusement sauté — parce qu’un agent qui croit avoir testé quelque chose qu’il n’a pas testé, c’est le pire des mensonges.

Où ça en est, honnêtement

probatum a douze jours. v0.1.0, 21 commits, un binaire statique d’environ 1 Mo, une image de 14 Mo, aucun contributeur externe, aucun utilisateur à part moi. Il n’est pas sur crates.io — je n’ai pas encore décidé d’y poser le nom.

Ce qui est solide aujourd’hui : le contrat est gelé (sources × règles à plat, exit 0/1/2, propriété des processus sur tous les chemins de sortie), il se teste lui-même — le probatum.yaml à sa racine construit, lint, lance la démo de bout en bout et vérifie que les scénarios négatifs sont bien attrapés — et il tourne dans cidx 2.1.0 comme preset de phase de test. Les deux se dogfoodent l’un l’autre : le pipeline cidx tourne dans probatum, probatum sort en preset cidx. Chacun a déjà trouvé des bugs chez l’autre.

Ce qui n’est pas fait : tout le reste. C’est un v0.1.0, et ce texte parle de sa frontière, pas de sa maturité.

L’écosystème que je vise

La carte, alors : cidx orchestre la boucle externe, probatum vérifie le système qui tourne dans la boucle interne. Ils se dogfoodent l’un l’autre — le pipeline de cidx tourne dans probatum, probatum sort en preset cidx — ils sont tous les deux pensés pour l’agent, et chacun refuse de devenir l’autre.

Ce n’est pas une plateforme. C’est une chaîne d’outils où chaque maillon fait une chose, reste remplaçable, et est honnête sur son bord. Le pari n’est pas dans la taille de chaque outil — il est dans les frontières entre eux, tenues exprès. probatum, c’est un maillon de plus : celui qui répond, quand les tests sont verts, à la seule question qui reste — oui, mais est-ce que ça tient debout ?