Les flows¶
Besoins d'exécution : un flow déclare, le projet résout¶
Un node de flow ne référence pas pytest ni npm test en dur : son
acceptance peut déclarer un besoin ({"run_need": "test-runner"}) plutôt
qu'une commande (issue #205). grimoire flow run/resume résolvent ce
besoin à la commande réelle du projet au chargement du blueprint — en deux
temps, jamais un troisième : une déclaration explicite
(needs.commands dans project-context.yaml) l'emporte toujours sur la
détection par marqueur de projet (pyproject.toml, package.json,
Cargo.toml, go.mod). Un besoin qui ne se résout ni par l'un ni par
l'autre refuse le chargement du blueprint en le nommant, avant que le
premier node soit présenté à l'hôte — jamais une installation qui
échouerait au troisième node.
| Commande | Description |
|---|---|
grimoire needs resolve [--project-root .] [--json] |
Le verdict de résolution de chaque besoin du catalogue pour ce projet, et sa source (declared, detected, unresolved) |
Rétrocompatible : un blueprint à commandes en dur ({"run": "pytest -q"})
reste valide sans aucun changement. grimoire flow run affiche un
avertissement (jamais un refus) quand une commande en dur correspond mot
pour mot à un besoin résolu pour ce projet — une invitation à migrer vers
run_need, pas une obligation.
Blueprints et flows¶
Un blueprint décrit un flow agentique comme un graphe de nodes typés. Ces commandes le manipulent sans passer par l'atelier web. Détail du format et de l'éditeur : Mode local et blueprints.
| Commande | Description |
|---|---|
grimoire blueprint new <id> |
Scaffolder un .blueprint.json valide depuis un modèle embarqué |
grimoire blueprint validate <fichier> |
Valider : JSON Schema, puis contrôles structurels de compilation |
grimoire blueprint compile <fichier> |
Compiler en mission pack, avec les mêmes règles fail-closed que l'atelier |
grimoire blueprint evals <fichier> --record <relevé> |
Rejouer les évals déclarées contre un relevé d'exécution |
Le rejeu ne fabrique aucun verdict : un cas absent du relevé est rapporté comme non exécuté, jamais comme échoué.
validate et compile ont deux couches de contrôle, dont la première demande
le paquet jsonschema. Quand il manque, la couche ne s'exécute pas et les deux
commandes refusent : un contrôle qui n'a pas eu lieu ne peut pas conclure à
un succès. --allow-skipped-schema accepte explicitement le contrôle partiel.
grimoire flow — le moteur conduit, l'hôte exécute un node¶
blueprint compile aplatit un blueprint en un unique prompt markdown : le
modèle improvise l'ordre. grimoire flow inverse cela (#204) : le
RuntimeKernel conduit un blueprint node par node, chaque node reçoit un
contrat borné (entrées, frontière d'outils, critères d'acceptation, contrat
de sortie), et l'hôte — jamais le kit — exécute le node. compile reste
un repli valide pour les hôtes sans exécuteur de node ; flow ajoute une
seconde sortie, il ne retire rien.
| Commande | Description |
|---|---|
grimoire flow run <fichier> |
Démarrer un run et présenter le contrat du premier node ; sans argument, liste les runs |
grimoire flow status <run-id> |
État du run : node courant, nodes faits, dernier refus, contrat courant |
grimoire flow next <run-id> [--wave] |
Le node courant ; avec --wave, tous les nodes prêts (non faits, prédécesseurs du DAG faits). Lecture seule |
grimoire flow resume <run-id> --result <fichier> |
Vérifier la sortie du node courant contre son contrat ; avance ou suspend |
grimoire flow abort <run-id> [--reason] |
Abandonner un run — terminal, jamais repris |
grimoire flow extract <run-id> [--out <fichier>] |
Extraire un blueprint brouillon de la séquence réellement exécutée par ce run |
grimoire flow list [--require-measure <blueprint-id>] |
Le registre local des flows : runs connus par blueprint, avec leur mesure de dispatch |
Un checkpoint par node : un run interrompu reprend exactement au node courant, jamais du début. Une sortie non conforme au contrat de sortie du node suspend le run en nommant le node et la pin fautifs, avec un code de sortie non nul — jamais un échec muet.
flow extract — un flow s'extrait d'un run, il ne se dessine pas (#210)¶
Créer un flow devient éditer quelque chose qui a déjà fonctionné plutôt que
partir d'un canevas vide. grimoire flow extract <run-id> joint les
métadonnées du run (ordre topologique, blueprint source) au Mission Ledger
(commande réellement exécutée par node, via --executor dispatch) pour
produire un blueprint brouillon dans le même format .blueprint.json — texte
d'abord, lisible et diffable, que le Studio peut ensuite éditer.
Par node, trois choses observées, jamais devinées : l'acceptance déclarée
d'un node complété par la cascade est remplacée par la commande
réellement exécutée ({"run": "..."}), réinférée en {"run_need": "..."}
quand elle correspond caractère pour caractère à un besoin résolu pour ce
projet (issue #205) ; un node jamais dispatché (classe V2, ou exécuté par
un hôte interactif sans passer par la cascade) garde son acceptance d'origine
verbatim ; un node jamais atteint (run incomplet ou abandonné) est marqué
not_reached, sans qu'aucune commande n'y soit inventée. Chaque node du
brouillon porte un objet "extraction" (status, et note sur un refus)
qui dit lequel de ces trois cas s'est produit.
Le brouillon extrait est rejouable : flow run/resume le chargent comme
n'importe quel blueprint, et reproduisent la même séquence de nodes que le
run source sur le même projet.
kind: "composite" — un flow est un node (lot 3, issue #206)¶
Le schéma réservait déjà kind: "composite" pour un sous-flow (ref =
use-case:<id> ou un chemin .blueprint.json), mais seul le compilateur
Studio le validait — le moteur de flows ne l'exécutait pas. Depuis cette
issue, un node composite expose des pins comme n'importe quel node, et son
exécution (sous --executor dispatch) lance la ref comme un run à part
entière : son propre run_id, ses propres checkpoints, son propre Mission
Ledger — lié au parent par FlowRunMeta.parent_run_id/parent_node_id.
ref reconnaît trois formes, jamais une quatrième inventée :
- un chemin se terminant par
.blueprint.json, résolu relativement au blueprint qui le référence puis à la racine du projet ; - un id de flow nu, résolu contre le registre local
(
registry/blueprints/<id>.blueprint.json) ; use-case:<id>— réservé à l'expansion Studio/catalogue : le moteur de flows refuse cette forme au chargement, il ne l'exécute pas.
Trois refus, tous au chargement du blueprint parent, jamais quand le node composite serait présenté à un exécuteur :
- référence introuvable (fichier absent, id absent du registre) ;
- cycle de composition — un blueprint qui se référence lui-même, directement ou via un intermédiaire ;
- profondeur de composition supérieure à 3 (un flow racine qui référence un sous-flow qui en référence un troisième est la limite).
Le coût du sous-flow est remonté au node composite : flow status et le
rapport de --executor dispatch montrent son cost_usd comme la somme des
coûts de tous les nodes de l'enfant. Le plafond du pilote (issue #209,
max_cost_usd_per_node) s'applique à ce total, vérifié entre chaque node
de l'enfant — jamais après un node déjà vert : un dépassement abandonne le
run enfant (flow status <child-run-id> le montre aborted) et ferme le
node composite en cost_capped, comme un node simple dont la cascade aurait
renoncé au palier suivant.
Sous l'exécuteur interactive (par défaut), un node composite n'est pas
auto-lancé — son contrat affiche composite:<ref> comme frontière d'outils,
et l'hôte lance lui-même grimoire flow run <ref> avant de soumettre un
résultat conforme aux pins déclarées, comme pour tout autre node interactif.
flow extract (#210) ne l'aplatit jamais : le node composite reste
kind: "composite" avec sa ref d'origine dans le brouillon, et son
extraction porte le child_run_id du sous-flow qu'il a lancé quand il y en
a un — le sous-flow s'extrait séparément, sur son propre run_id.
grimoire.flows.registry.describe_flow (utilisé par flow list, ci-dessous)
lit, en plus, version/kitMin/kitMax au niveau du blueprint (tous
optionnels — version retombe sur 0.0.0) et l'union des run_need
déclarés par le flow et par tous ses sous-flows composite.
Les sept genres de node (lot 4, issue #207)¶
composite (ci-dessus) est le cas dégénéré : un sous-flow, une tentative,
aucune agrégation. Sept genres de plus généralisent le même mécanisme —
lancer un ou plusieurs sous-flows enfants — avec chacun sa propre politique
d'agrégation. Aucun n'est exprimable dans un document de consignes statique ;
tous deviennent directs dès que le moteur tient l'état. Chacun n'existe que
sous --executor dispatch (grimoire.flows.genres, fonctions
_execute_<genre> — module compagnon de grimoire.flows.dispatch_executor,
extrait de lui pour rester sous le seuil du cliquet de lignes) ; sous
interactive, le contrat affiche le genre et sa
ref comme frontière d'outils, l'hôte orchestre lui-même.
Paramétrage refusé nommément au chargement du blueprint
(grimoire.flows.blueprint_loader._validate_genre_config) — jamais un
TypeError/KeyError anonyme à l'exécution :
| Genre | ref |
config.<genre> requis |
|---|---|---|
fanout |
sous-flow modèle | — (voir ci-dessous) |
verify-panel |
sous-flow vérificateur | k (entier ≥ 2), angles (liste de k chaînes) ; class optionnelle (V1 ou V2, défaut V2) |
loop-until-dry |
sous-flow | maxRounds (entier ≥ 1) |
judge |
sous-flow candidat | n (entier ≥ 2), angles (liste de n chaînes) |
checkpoint |
ignorée (chaîne vide par convention) | — |
budget |
ignorée — voir config.budget.passes |
maxCostUsd (nombre > 0), passes (liste non vide de refs) |
replay-diff |
sous-flow | — |
fanout — un node produit N éléments, N sous-flows instanciés, les
résultats sont recollés. Deux temps : le node est d'abord dispatché
normalement (comme un node ordinaire), sa sortie doit porter, en plus de ses
pins déclarées, une clé fanout_items (liste non vide) ; le moteur instancie
alors un sous-flow (ref) par élément. N est borné par
pilot.max_fanout_n (_grimoire/standard/pilot.yaml) — absent, le
fan-out est refusé : jamais une politique manquante ne vaut un plafond
infini implicite. Vert seulement si les N enfants finissent tous verts ;
recoller un résultat partiel comme s'il était le tout serait l'invention que
l'issue refuse.
pilot.max_parallel (défaut 1, séquentiel) borne le nombre d'enfants actifs en
même temps. Au-delà de 1, le plafond max_cost_usd_per_node est relu avant
chaque lancement et un coût inconnu bloque les lancements suivants, comme un
enfant suspendu à l'hôte (aucun lancement après la suspension) ; les
enfants déjà en vol ne sont pas rétractés (dépassement borné par
max_parallel - 1 enfants). Les enfants partagent le même répertoire : à
n'activer que pour des enfants qui n'écrivent pas les mêmes fichiers.
verify-panel — k vérificateurs indépendants (config.verifyPanel.k),
chacun sous un angle distinct (.angles, instruit de chercher à réfuter,
jamais de confirmer par défaut). Chaque vérificateur vote par un verdict
structuré (READY ou NOT-READY) dans sa sortie ; sans verdict, il
s'abstient (finir n'est pas approuver). config.verifyPanel.class : V1
exige la majorité stricte des votes exprimés, V2 (défaut) leur unanimité.
Si les votes exprimés ne sont pas une majorité de k, le node rend
inconclusive ; des tree_fingerprint différentes parmi les votants le
rendent invalid. Coût = k tentatives,
toujours — jamais une cascade qui s'arrête au premier consensus
apparent : un plafond de coût qui empêche de compléter les k tentatives
referme le node en cost_capped, jamais sur un vote partiel (« jamais un
vert sur 1/k »).
loop-until-dry — relance jusqu'à config.loopUntilDry.maxRounds tours
sans nouveauté. Chaque tour doit porter une clé novelty_key (chaîne) dans
sa sortie ; la déduplication porte contre tout le vu depuis le premier
tour (un ensemble cumulatif), jamais seulement contre le dernier tour
retenu. maxRounds est une borne dure, distincte d'un séchage réel
(loop_until_dry_stop_reason : "dry" / "max_rounds" / "cost_capped").
judge — N tentatives sous angles imposés (config.judge.n/.angles),
puis le node lui-même est dispatché une seconde fois comme juge (V1 —
« un humain choisit »), avec le résumé des N tentatives dans son contexte de
tâche ; sa sortie doit porter judge_winner (index 0..N-1). La trace des N
tentatives survit dans leurs propres runs (Mission Ledger de chaque enfant),
jamais seulement dans un champ éphémère du node.
checkpoint — approuver, refuser, amender. Toujours une décision
d'hôte forcée, jamais auto-décidée par la cascade (même sous
--executor dispatch, ce genre pend systématiquement, comme un node V2).
La reprise (flow resume --result, forme interactive, quel que soit
l'exécuteur qui a conduit le reste du run) doit porter
checkpoint_decision (approve/reject/amend) ; reject bloque le run
en nommant le motif (checkpoint_reason) dans l'événement de refus du
kernel — « le motif dans le graphe de décision » — une reprise ultérieure
rouvre exactement ce même node. amend (W2-10) demande une révision : le
node reste ouvert, la décision et son motif sont inscrits dans le journal du
run (relu par flow status) et comptés. accept_as_is n'est accepté qu'après
config.checkpoint.max_amendments amendements (entier >= 1, 3 par défaut) ; il
exige checkpoint_reason (le risque nommé), inscrit accepted_risk, et pose
task.risk_accepted sur la tâche du ledger liée au run (--task-id) si elle
existe ; le ledger le rejoue dans la tâche (accepted_risks) et classify la remonte
au moins en V1 pour toute surface (task show, MCP task_show/task_claim,
dispatch_advice, transition needs_verification de task dispatch).
Limite : le compteur est celui du journal du run, amend non attesté compris.
config.checkpoint.plan (W2-11, fournisseur connu : upgrade-removals) lie la
décision à la liste projetée de ce qu'elle engage : hachée à la présentation
(inscrite dans le challenge), recalculée à la décision. approve et
accept_as_is dont le plan a changé — ou dont la projection est impossible —
sont refusés en plan_drifted avec les écarts, sans consommer le tour humain ;
le challenge suivant porte le plan courant et exige un nouveau tour. Limite :
l'empreinte couvre la projection du fournisseur, pas ce qu'un exécuteur ferait
après la décision. grimoire upgrade-flow review affiche le plan présenté.
Octroi d'autonomie (grimoire autonomy, W5-06) — la seule voie pour
faire franchir un checkpoint destructif sans humain (CI, banc) ;
GRIMOIRE_UNATTENDED=1 seul n'ouvre rien.
| Commande | Effet |
|---|---|
grimoire autonomy grant --scope <sel> --max-cost-usd <usd> --until <échéance> |
Demander un octroi. <sel> : run:<id> ou blueprint:<id>, /<node> en option, répétable, aucun joker ; <échéance> : +30m, +2h, +1d ou un instant ISO 8601 avec fuseau. Les trois bornes sont obligatoires. |
grimoire autonomy grant … --attest-from <projet> |
Même demande, attestée par le journal des tours de <projet> quand la session tourne dans un autre projet que la cible (--project-root). L'octroi inscrit attest_from ; le tour est consommé dans le registre de <projet>. Mêmes règles que autonomy mandate grant --attest-from (Tâches). |
grimoire autonomy status |
Chaque octroi réévalué maintenant : actif ou révoqué (motif), coût dépensé, échéance ; -o json ajoute les événements. |
grimoire autonomy revoke <grant-id> [--reason] |
Révoquer à la main, toujours permis. |
L'octroi est lui-même une décision attestée : la première demande émet un
challenge lié à la session et sort en code 3 (décision humaine requise) ; un
tour humain de cette session, puis la même commande relancée, l'inscrit et
consomme ce tour. Seul un challenge émis par la session courante est repris : un
challenge ouvert sans session ou par une autre session n'empêche pas la demande
d'une session, qui reçoit alors le sien. Un /<node> restreint le seul run que
désigne le sélecteur, pas ses sous-flows ni les nodes homonymes d'un autre
blueprint ; sans /<node>, les sous-flows sont couverts. Tant qu'il est valide, un
approve ou accept_as_is non attesté sur un checkpoint du périmètre est accepté avec
attestation=granted:<grant-id>, distinct d'attested. Il est révoqué, une
fois pour toutes, à l'échéance (expired), quand le coût atteint le plafond
(cost_reached), au premier coût inconnu (cost_unknown, politique stop
sans alternative), et au premier gate rouge du périmètre (gate_red : une
sortie refusée par son contrat, ou une tentative de dispatch red ou
unrunnable). Le checkpoint suivant repasse en awaiting_human et le refus
nomme l'octroi révoqué. Chaque challenge, octroi et révocation est une ligne de
_grimoire-runtime-output/flows/authority/@autonomy-grants.authority.jsonl,
journal que la garde d'autorité interdit d'écrire à l'agent.
Limite : le coût mesuré est celui des tentatives task.dispatched des runs
couverts, postérieures à l'octroi ; le coût de la session hôte n'est mesuré
nulle part dans le kit et n'y entre pas (cost_scope dans status).
budget — plafond de coût déclaré (config.budget.maxCostUsd),
abandon des passes optionnelles avant dépassement. passes est une liste
de refs de sous-flows ; la première est obligatoire (son échec fait échouer
le node) ; chacune des suivantes n'est lancée que si le coût cumulé reste
sous le plafond — sinon abandonnée, jamais tentée, sans faire échouer le
node. Distinct de pilot.max_cost_usd_per_node : ce plafond-ci est déclaré
par le blueprint, propre à ce node.
replay-diff — rejoue le même flow (ref) sur la même entrée (deux
runs enfants, sans variation d'un à l'autre) et compare les traces.
replay_diverged (booléen, dans la sortie) dit si les deux traces
(statut + nodes complétés) divergent — une divergence est un signal,
jamais un défaut : le verdict reste vert tant que les deux relectures
finissent, qu'elles divergent ou non.
Coût et pilote, pour les sept genres : pilot.max_cost_usd_per_node
s'applique au total de chaque genre, jamais à une seule tentative —
vérifié avant chaque nouveau sous-flow lancé, jamais après un enfant déjà
vert (même garantie que pour composite). Extraction : flow extract
ne les aplatit jamais — kind, ref et config.<genre> survivent
verbatim dans le brouillon, exactement comme composite. Rust : aucun
changement — les sept genres sont de l'orchestration Python (E/S, lancement
de runs enfants), hors périmètre des fonctions pures déjà portées par
grimoire-flows-core ; la grille de transitions et les contrats de node
n'ont pas changé.
--executor dispatch — la cascade par classe de vérifiabilité (#311)¶
run et resume acceptent --executor dispatch (défaut : interactive,
inchangé). Au lieu d'attendre un --result de l'hôte, chaque node est confié
à grimoire.flows.dispatch_executor : une tâche de mission est créée (ou
retrouvée) au ledger, liée au run et au node, avec pour critères
d'acceptation ceux du node et pour preuve attendue son contrat de sortie ;
elle est dispatchée en cascade (run_dispatch, #323) — l'ouvrier écrit sa
sortie dans un fichier JSON que le kit vérifie contre les pins du node, avec
la même fonction que flow resume, et exécute l'acceptance structurée du
node quand le blueprint en déclare une (voir « Acceptance structurée d'un
node », ci-dessous, issue #428) : une enveloppe conforme ne suffit plus seule
à fermer un node V0 dont l'acceptance déclare une commande.
La chaîne suit la classe de vérifiabilité du node (#309) :
- V0 :
cheap→mid→strong. - V1 : démarre à
mid; un vert marque le node à relire (needs_verificationcôté ledger), jamais une fermeture. - V2 : refusé avant tout appel — le node revient à l'hôte, exactement
comme
flow run/flow resumeinteractifs : le contrat est présenté,flow resume --resultreprend la main.
run --executor dispatch enchaîne les nodes tant que la cascade reste
verte, checkpointe après chacun, et s'arrête au premier node rouge (chaîne
épuisée), acceptance inexécutable, ou V2, avec un rapport par node
(fournisseur, tentatives, verdict, coût, escalades, statut d'acceptance)
plutôt qu'un contrat. resume --executor dispatch (sans --result) reprend
de la même façon le node que le run avait laissé ouvert — utile après un
crash comme après une suspension V2 corrigée autrement. flow status
affiche, pour tout node dispatché, fournisseur, tentatives, verdict,
relecture requise, incertitudes déclarées et statut d'acceptance
(exécutée/inexécutable/jugée) — lus depuis l'événement
task.dispatched du Mission Ledger, valables même dans un process qui n'a
pas fait le dispatch. --max-tier et --timeout bornent la cascade comme
sur grimoire task dispatch.
Acceptance structurée d'un node (issue #428)¶
Un node V0 est « vérifiable mécaniquement » : par défaut, le vocabulaire de
son acceptance textuel (« la suite de tests passe », …) sert uniquement à
dériver sa classe de vérifiabilité (#309) — le gate de la cascade ne vérifie
que la conformité de l'enveloppe JSON au contrat de sortie du node, jamais le
texte lui-même. Sur un flow réel rejoué le 2026-09-11 (épic #307, lot 3),
cela a laissé fermer vert un node dont la vraie suite pytest ne pouvait pas
même être collectée (dépendance absente) : l'enveloppe était conforme, rien
d'autre n'avait tourné.
node.acceptance accepte désormais, en plus d'une chaîne libre
(rétrocompatible, comportement inchangé), une entrée structurée que le gate
exécute réellement après la réponse de l'ouvrier — dans le projet, en
sous-processus, délai borné :
{"run": "pytest -q tests/test_x.py", "expect_exit": 0, "cwd": ".", "timeout_s": 120, "expect_stdout_contains": "passed"}
Seul run est requis ; expect_exit (0 par défaut), cwd (. par défaut),
timeout_s (120 par défaut) et expect_stdout_contains (aucun par défaut)
sont optionnels. Deux formes d'evidence mécanique existent aussi, pour éviter
d'écrire une commande à la main :
{"path_exists": "dist/rapport.json"}— un fichier doit exister.{"test": "tests/test_x.py::test_y"}— un test nommé doit passer (traduit en une invocationpytestdu seul identifiant déclaré).
acceptance peut mélanger texte libre et entrées structurées dans la même
liste. Une forme structurée invalide (clé inconnue, plusieurs clés sur la
même entrée, type incorrect) est un refus nommé au chargement du
blueprint (GrimoireRuntimeError), jamais un gate qui échouerait ouvert
plus tard — et le kit n'infère jamais de commande depuis la prose : sans
entrée structurée, un node V0 garde le comportement historique (enveloppe
seule), documenté ci-dessous comme limite connue.
Le verdict d'un node dont l'acceptance est exécutée suit trois issues, jamais une quatrième :
- exécutée, verte — toutes les commandes déclarées ont rendu le code de
sortie attendu (et la sous-chaîne attendue, si
expect_stdout_containsest posé). Le node ferme normalement. - exécutée, rouge — au moins une commande a rendu un code de sortie inattendu, sans qu'aucun signal d'inexécutable (ci-dessous) n'ait été détecté. Suit la cascade normale : réessai au fournisseur suivant du même palier, puis escalade au palier suivant, comme un check d'enveloppe rouge.
- acceptance inexécutable — refus nommé, distinct d'un échec, jamais un
succès : la commande n'a pas pu rendre de verdict exploitable (binaire
introuvable — code de sortie 127 ;
pytestqui rend 5 « no tests collected » ou 4 « usage error » ; une erreur d'import détectée dans la sortie, quel que soit le code de sortie). La cascade s'arrête net sur ce node — changer de fournisseur ou de palier ne répare pas un environnement cassé — etflow statusnomme le node en faute.
La classe V0 exige une acceptance structurée¶
Un node dont le texte seul suffirait à le classer V0 (#309 — « la suite de
tests passe », par exemple) mais qui ne déclare aucune entrée structurée
est rétrogradé en V1 : la cascade démarre à mid (pas cheap), et un
vert ne ferme jamais le node — il le marque à relire
(needs_verification), exactement comme un vrai V1 déclaré. Sans cette
règle, le texte mécanique seul aurait rouvert le fossé même que l'issue #428
corrige : le gate n'aurait toujours que le check d'enveloppe à faire tourner
pour ce node, et fermerait vert sur la seule foi de l'ouvrier — la
rétrogradation, pas seulement le libellé du rapport, est ce qui l'empêche.
Cette dérivation est appliquée une seule fois, au chargement du
blueprint, et transmise à la fois au démarrage de la cascade (run_dispatch)
et au rapport (DispatchExecutor) — flow status et flow run s'accordent
donc sur la même classe pour le même node, jamais recalculée deux fois de
façon divergente. Un avertissement nommé accompagne la rétrogradation, à la
fois dans flow status/le rapport de dispatch et dans les logs :
Le kit n'infère jamais de commande depuis la prose pour éviter cet
avertissement : la seule sortie est de déclarer une entrée structurée
(run/path_exists/test) sur le node.
Un node dont l'acceptance est V1 déclarée (vocabulaire de revue, « revue humaine avant fusion ») n'est pas concerné par cette règle — il n'a jamais prétendu être V0 — et garde le comportement actuel du kit, documenté ici sans être étendu : un vert V1 ferme la cascade en marquant le node à relire, sans qu'aucun juge automatisé n'intervienne. Faire arbitrer une classe V1 par un palier supérieur plutôt que par une revue humaine est hors périmètre de l'issue #428 (voir son texte : « ce que ce ticket refuse »), à traiter par un ticket séparé s'il devient nécessaire.
Dans les deux cas (V1 déclaré, ou V0 rétrogradé), flow status rapporte le
statut d'acceptance « jugée » : le mode conservateur qui ne prétend
jamais à une exécution qui n'a pas eu lieu.
La trace du node (visible dans flow status --output json et le rapport de
flow run --executor dispatch) porte, pour chaque commande exécutée, le code
de sortie et la sortie tronquée à 2 Ko (stdout puis stderr) — de quoi lire un
message d'erreur pytest sans relancer la commande à la main.
Workflows¶
Un workflow a une nature. Les commandes d'hygiène (command) doublent un
diagnostic CLI ; les orchestrations (orchestration) coordonnent plusieurs
agents sur plusieurs tours et déclarent parfois l'équipe avec laquelle elles
tournent. Les deux familles vivent dans des répertoires différents — les
premières sous .github/prompts/, les secondes sous le tier kit — et le
catalogue indexe les deux.
| Commande | Description |
|---|---|
grimoire workflows list |
Lister les workflows — nature, agents, provenance |
grimoire workflows list -k orchestration |
Ne garder que les workflows multi-agents |
grimoire workflows list --all |
Inclure ceux qu'une commande CLI remplace |
grimoire workflows search <terme> |
Chercher par slug, description, et optionnellement contenu |
grimoire workflows show <slug> |
Afficher un workflow, ce qu'il déclare, et son contenu |
grimoire workflows install <slug> |
Installer un workflow du framework — la destination suit sa nature |
grimoire workflows sync |
Synchroniser les prompts du framework vers le projet |
grimoire workflows diff |
Différences entre framework et projet |
grimoire workflows doctor |
Auditer les prompts du projet contre les défauts du framework |
grimoire workflows prune |
Retirer les prompts propres au projet absents du framework |
Workflows remplacés par une commande¶
Quatre des sept prompts livrés redisaient une commande du SDK :
| Workflow | Commande qui le remplace |
|---|---|
/grimoire-status |
grimoire status |
/grimoire-health-check |
grimoire doctor |
/grimoire-self-heal |
grimoire doctor --fix |
/grimoire-pre-push |
grimoire check |
Ils occupaient la moitié du catalogue. Ils restent livrés et installables — un
projet qui les a ne les perd pas — mais ne sont plus déployés dans les projets
neufs ni listés par défaut : --all les montre, avec la commande à utiliser.
Les trois autres restent de plein droit : /grimoire-changelog,
/grimoire-dream et /grimoire-session-bootstrap lisent l'historique et la
mémoire pour en tirer une synthèse, ce qu'aucune commande ne fait.
Se déclarer pour entrer au catalogue¶
Un workflow d'orchestration entre au catalogue en le disant, dans son
frontmatter — pas en étant deviné depuis son emplacement, parce que sous
workflows/ vivent aussi des gabarits de rapport rendus à chaque run :
---
kind: orchestration
description: "Ce que fait ce workflow"
agents: [sm, architect, dev, qa]
triggers:
- la situation qui appelle ce workflow
---
agents et patterns sont facultatifs ; description ne l'est pas en
pratique, c'est ce que le catalogue affiche. Un fichier sans frontmatter reste
sur disque et n'est pas listé.
patterns cite les identifiants du catalogue de patterns (ORC-01, KNO-02…)
que le workflow instancie, et memory les couches du Memory OS qu'il lit ou
écrit. Aucun workflow livré ne déclare de couche mémoire : leurs fichiers ne le
disent pas, et l'inventer contredirait la règle du dépôt — ce qui décrit un
artefact doit en être dérivé. Le champ existe pour les workflows que vous
écrivez.
Un prompt qui redit une commande du SDK le déclare avec deprecated_by:,
suivi de la commande.