De l'actuelle à la cible
Ce qui change dans le fichier
| actuelle | cible | pourquoi |
|---|---|---|
tenant_id, store_name, domain | station: {id, kind, name} | une organisation possède plusieurs stations |
global: {language, currency, locale} | global.timezone en plus | les horaires d'une sous-identité, les échéances des boîtes |
agent: {name, persona, tone, rules, emoji, model} | agent.mode en plus ; les rules qui valent pour tout agent deviennent des politiques | agent.rules atteignent encore les deux portes ; seule la façon de parler de notre agent doit y rester |
agent.portals.web.widget: {origins, theme} | la même forme ; identity.auth en plus | le site du tenant est un portail de l'agent, pas la station |
| — | agent.portals: {web, teams, slack, whatsapp} avec identity.auth | de nouveaux portails, et la preuve de qui parle |
| — | station.identity.providers (otp, oidc, assertion, chacun avec verifies:) et identifies: true sur un champ de formulaire | une room identifiée (déclarée), puis authentifiée (vérifiée). identifies: true existe déjà dans l'actuelle |
apis[].operations: [{id, access, reversible, exposed}] | risk et audience en plus | le risque, ce qui sort. access, reversible et exposed existent déjà dans l'actuelle |
skills[].invoke: {method, args} ou {native}, params: {x: {default}} (default facultatif) | invoke.guards ; set_by et type sur un paramètre | qui peut fixer un paramètre, de quel type est sa valeur, et une lecture a ses gardes. La déclaration unique (invoke, kinds, present, select, submit, render), invoke.native, invoke.args, $params lu par invoke.args et par les appels du submit, un paramètre qui vaut son default ou la liaison d'une tâche existent déjà dans l'actuelle |
catalog: {from_skill, mode, query_arg} au niveau de la station | — : la config n'a pas de catalogue ; un item se résout par ce qu'un invoke a montré dans la room, et se lit par le present de la skill qui l'a montré | l'invoke dit déjà ce que la station propose ; une seconde déclaration pourrait le contredire, et une copie indexée vieillirait |
present.fields d'une skill : {from, type, role, template} | le même bloc ; le rôle headline et la class en plus | les champs typés, leurs rôles (title, billed, link) et la table des types, commune aux formulaires, existent déjà dans l'actuelle ; un item se lit par le present de la skill qui l'a montré |
kinds: {x: {when, select, submit}} | kinds.<x>.present en plus (une permission sur ce qu'un profil voit) ; kinds.<x>.fields, les champs de ligne que la personne remplit ligne par ligne | un kind se reconnaît par une condition sur un champ présenté ; cela existe déjà dans l'actuelle. Un choix qui appartient à une ligne (l'accompagnement d'un plat) n'avait nulle part où s'écrire |
select: {max_items, guards: [form, require, check, update]}, chaque garde avec when | permission, policy, approval en plus dans guards ; name, effect, mode, reason sur toute garde ; unique, valid_for ; quantity: {min, max, step}, le plus souvent par kind | une liste ordonnée, ouverte à d'autres gardes. La liste, require, when et le langage des conditions existent déjà dans l'actuelle |
submit: [{name, mode, tool, args, output, line_fields, line_shape}], un seul appel par skill ou par kind | plusieurs appels, exécutés dans l'ordre ; chaque sortie typée : output: {x: {from, type}} ; line: remplace line_fields et line_shape ($line.ref, $line.quantity, $line.price, $line.fields.<champ>) ; extra_lines | enchaîner des écritures dans une même skill ; une seule table de types pour tout ce qui porte une valeur. Les appels nommés, leurs sorties et $action.key existent déjà dans l'actuelle |
$submitted[tache.appel].sortie : la tâche d'un playbook | $result[tache], $read[tache] en plus | ce qu'une tâche a lu ou rendu. $submitted[tache.appel] existe déjà dans l'actuelle |
playbooks[]: {name, goal, tasks: {nom: {skill, kind, one_of, choose, all_of, when, required, label, after, params}}} | one_of: {choose, fallback, methods: {nom: {skill, kind, when, label, params}}} : choose dans le one_of, un label par méthode ; plus d'all_of, after: [a, b] attend les deux ; when sur $read (pas sur $memory ni $submitted) ; choose: clm ; params liés à $read ou $result ; depends_on, on_open, guards sur une tâche | la condition sur les données, la donnée, le nom d'une méthode tel que la personne le lit. Les tâches nommées, goal, label, after, le choix (one_of, choose: customer|rule, écrit à côté de one_of), when sur $selection.kinds, les params d'une tâche (et de chaque méthode) liés à $submitted[tache.appel] avec l'after qu'ils impliquent, et l'ordre appliqué au choix existent déjà dans l'actuelle |
— (l'agent ouvre le playbook : auto_trigger, start_playbook) | activation: {when, intent, exclusive_skills} | un playbook qui s'ouvre et fait loi |
submit_guard de la station | submit_guard par playbook, avec submitted_by | qui soumet, et sous quelle règle |
| une commande déclarée sur chaque skill dont elle envoie les lignes, regroupée par opération au submit | submit sur le playbook : l'appel qui envoie les lignes de plusieurs tâches, déclaré une fois, ses lignes prises par $selection[task=…] ; cité $submitted[<appel>] | deux copies d'un même appel pouvaient diverger, et le corps dépendait alors de l'ordre des lignes |
| — | profiles avec grants | obligatoire dès qu'il y a une skill : la migration écrit un profil anonyme qui reçoit les opérations qu'appellent les skills existantes, une par une |
agent.portals.web.widget.labels, qui ne touche pas les refus | texts: {fr: {<clé>: "…"}} au niveau de la station, pour les phrases de refus et de retenue | le modèle lit aussi ces phrases : elles valent sur toutes les portes |
| — | policies, inboxes, events, automations, tests, evals | nouveaux, tous facultatifs |
La forme précédente des playbooks n'est pas lue : l'outil la réécrit (python -m convoy_core.config.migrate). Chaque élément de steps devient une tâche nommée d'après sa skill,
sans after : l'ordre d'une liste ne dit pas une dépendance, l'auteur l'écrit s'il la veut.
description devient goal ; trigger disparaît, et son intent devient le goal d'un playbook
qui n'avait pas de description. $submitted[<skill>.<appel>] devient
$submitted[<tâche>.<appel>] quand la skill est l'action — ou celle d'une méthode d'un one_of —
de tâches d'un seul nom. Un all_of disparaît : chaque tâche dont l'after le nomme attend
directement ses membres. choose et fallback entrent dans le one_of, et ses méthodes passent
sous methods.
Le catalogue. L'outil retire catalog. Rien ne le remplace : la recherche passe par une skill
dont l'invoke est l'opération de recherche du tenant (la question sous un paramètre, à la place
de query_arg), et une référence se résout par ce que la room a déjà vu. mode: indexed n'a pas
d'équivalent : Convoy n'indexe pas le catalogue d'un tenant (le cas d'un tenant sans opération de recherche reste ouvert).
Le submit aussi. line_fields: {ref: a, quantity: b} devient line: {a: "$line.ref", b: "$line.quantity"} (une clé absente garde slug ou qty) ; line_shape: ref devient
line: "$line.ref" ; sans l'un ni l'autre, rien n'est écrit : la forme par défaut est la même. Les
appels d'une même opération portés par les skills de plusieurs tâches d'un playbook deviennent un
seul appel dans le submit de ce playbook, quand leurs args et leur forme de ligne sont les
mêmes : $selection[kind=k] y devient $selection[task=…], et les références
$submitted[<tâche>.<appel>] vers lui deviennent $submitted[<appel>]. Des args différents, ou
une skill aussi utilisée hors de tout playbook, sont laissés à l'auteur, avec la raison. Une config
migrée doit envoyer au tenant les mêmes requêtes, octet pour octet.
L'ordre de construction
Une étape n'est livrée que quand ses exemples de cette version passent dans la version actuelle.
- Les états d'action (en attente, lue) et les traces de lecture, dans la room et le reçu.
- L'ordre appliqué au choix ; les tâches (
after,one_of,when) ; le planificateur (les tâches nommées,goal,label,after,one_ofavecchoose: customerourule,whensur les kinds choisis et l'ordre appliqué au choix,step_open, sont construits ;chooseécrit dans leone_of, lelabeld'une méthode, le retrait d'all_of,whensur$read,choose: clmet le guidage du modèle vers les tâches ouvertes suivent). - La nouvelle forme des skills, l'ancienne lue pendant une version ; les six configs migrées (la liste ordonnée des gardes de
select,require,whenet le langage des conditions sont construits ; la déclaration unique de la skill est construite, avecparamset leurdefault,invoke.args,invoke.nativeetkinds.<k>(when,select,submit), et lesparamsd'une tâche liés à$submitted[…]). - Le submit guard par playbook, avec l'appel de commande du playbook (
submit,line:,extra_lines) et les champs de ligne ; puis le graphe d'états de chaque playbook, compilé au chargement, avant le guidage ; les sous-playbooks ; puisresultet$result, qui lisent un sous-playbook. - Le parcours attaché à la room ; les approbations liées à une version de l'action ;
valid_for. - Les profils,
per_profile, les permissions ; l'identité par personne (le plane d'identité,identifieset le fournisseurotpvia le tenant sont construits ;via: convoy,oidc,assertion, le choix par portail, les demandes d'authentification etsubmitted_by: ownersuivent). approval, quand une room accueille plusieurs personnes avec des rôles.submitted_by, quand les acteurs sont authentifiés ; puis les sous-identités déléguées.set_by, les classes de données, les audiences ; les gardes de lecture.- L'activation déterministe,
seek_guidancesans modèle, la règle de la première écriture. - L'idempotence par action,
unique,reversible,budget,inboxes($action.keyest construite ;reversibleest déclaré et rend le submit MCP destructif ; l'écran d'approbation et l'ordre au submit suivent). - Les automatisations, avec leur profil et la vérification des boucles ; la section
agentet ses portails (la sectionagentetagent.portals.web.widget: {origins, theme}sont construits ; Teams, Slack, WhatsApp à côté du widget et de la PWA suivent). - Les versions de station, le staging, les tests de contrat, le rejeu ; les
evalsde l'agent. - Le modèle de classement en ombre, puis en SUGGEST, puis en ENFORCE, station par station.
D'où viennent ces décisions
- ADR-028 §6 — un playbook comme ensemble de tâches et quatre relations.
- ADR-029 — tout est une station ; l'action ; la sélection comme ordre d'actions ; les sous-playbooks ; qui soumet.
- ADR-030 — le point de décision, le planificateur, le modèle de classement ; les profils ;
seek_guidance; la provenance, les classes de données, le temps, l'exécution, le Human In The Loop, les automatisations.
Certaines clés de ce guide sont des propositions que les ADR n'ont pas encore figées : le bloc
station, global.timezone, auth.on_behalf_of, station.delegation, agent.portals et
identity.auth, la syntaxe des tests et des evals. Elles
pourront changer à l'implémentation ; le sens, non.