Démarrer et reprendre
--durable et --record s’excluent : un journal durable s’exporte.
Le protocole d’une action
À la reprise, le programme est ré-exécuté depuis le tick 0 et chaque franchissement de frontière est servi par le journal : aucun effet, aucun appel au modèle, aucune question. Un point de contrôle qui ne correspond pas à l’état re-dérivé arrête tout. Au-delà du journal, l’exécution continue en direct.Une action restée sans résultat
Si le processus meurt entre l’intention et le résultat, l’effet a peut-être eu lieu. La reprise tranche :1
Outil idempotent : relancé avec la même clé
L’outil est déclaré
idempotent=True : l’hôte promet que son service ignore une seconde requête portant la même clé. L’action s’exécute exactement une fois.2
Réconciliateur : on demande au monde
Un réconciliateur dit si l’action a eu lieu. Si oui, son résultat est journalisé. S’il rend
NOT_EXECUTED, l’action est exécutée.3
Sinon : indéterminée
L’action n’est pas relancée (au plus une fois). Le runtime ne présume aucun
EFFECT, marque les SIDE_EFFECT déclarés comme sales, et pose tools.<outil>.in_doubt = true.Côté hôte
payments.py
Côté programme
Une action indéterminée est un fait, et la politique le lit :tools.<outil>.in_doubt vaut false au démarrage : la garde n’est pas indéterminée sur une exécution neuve.
Garanties
Refus de reprise
La chaîne du journal est un SHA-256 sans clé. Elle détecte la corruption et la réécriture naïve, pas un faussaire qui peut écrire le répertoire et recalculer toute la chaîne.
API Python
FileStore (fichier + fsync), SQLiteStore(path, run_id), MemoryStore(). Une société s’exécute sous un seul journal.
Noyau et permis
D’où viennent l’identifiant d’action et la clé d’idempotence.
Banc comparatif
Crash après un virement : AGENT-L, LangGraph, PydanticAI, CrewAI.