Aller au contenu principal
Version: 1.0.0

Compensations explicites et bornées

Une compensation demande au runtime d'appliquer l'opération inverse déclarée pour un effet externe précis. Elle est explicite : un échec, un fallback, une annulation ou un rollback de déploiement ne la déclenche jamais.

Quand l'utiliser​

Utilisez cette capacité quand un service externe a confirmé une écriture et que le package sait fournir une inverse métier précise, par exemple libérer une réservation précédemment créée.

L'effet original reste applied dans le journal. L'inverse possède son propre run, son propre effet, ses budgets, ses secrets et son autorité. Le résultat peut être :

  • completed : l'inverse déclarée a été appliquée ;
  • partial : l'inverse a été appliquée partiellement ;
  • unavailable : aucune inverse ou aucun reçu exploitable n'est disponible ;
  • failed : le handler prouve que l'inverse n'a pas été appliquée ;
  • unknown : l'issue ne peut pas être établie de façon sûre.

Déclarer l'inverse​

Le binding de l'écriture originale utilise un manifest V1 et désigne une stratégie existante :

touba.manifest.json
{
"manifest_version": "touba-manifest-v1",
"bindings": {
"tool:reservations/CreateReservation": {
"handler": {
"kind": "process",
"command": "node",
"args": ["handlers/create-reservation.mjs"]
},
"grants": ["network"],
"secrets": ["RESERVATION_API_TOKEN"],
"timeout_ms": 5000,
"max_payload_bytes": 65536,
"effect_contract": {
"kind": "external_write",
"retry_mode": "idempotency_key",
"compensation": {
"entry_ref": "strategy:reservations/ReleaseReservation"
}
}
}
}
}

La stratégie inverse reste volontairement petite : des bind purs, au plus une approbation humaine, exactement un appel de tool externe, puis give ou stop. Branches, boucles, fallback, appels imbriqués, interactions, deuxième effet et compensation récursive sont refusés.

Implémenter le handler inverse​

Le SDK expose le contexte de compensation uniquement pour l'appel inverse :

release-reservation.handler.mjs
import { createToolResult, defineTool } from "@touba/sdk";

await defineTool({
toolRef: "tool:reservations/ReleaseReservation",
inputSignature: [
{ name: "reservation_id", type_ref: "builtin:Text" },
],
outputType: "builtin:Text",
run: async ({ request: reservation_id, context }) => {
const response = await releaseReservation({
reservationId: reservation_id,
originalReceipt: context.compensation.original_effect_receipt,
idempotencyKey: context.effect.effect_id,
});

return createToolResult(response.message, {
effectReceipt: context.createEffectReceipt({
outcome: "applied",
receipt_ref: response.receiptRef,
}),
compensationReceipt: context.createCompensationReceipt({
result: response.complete ? "completed" : "partial",
}),
});
},
});

Cet extrait suppose un adaptateur métier releaseReservation fourni par votre package ; ce n'est pas un helper du SDK. L'appel inverse utilise operation: compensate dans le protocole handler V1.

Le reçu original est une donnée de contrôle. Ne le recopiez pas dans les arguments métier, les logs ou les traces.

Création, reprise et approbation​

L'hôte appelle séparément la création et la reprise. Chaque commande passe par une autorité de ciblage avant la lecture du journal. L'autorité est ensuite revalidée pour progresser et immédiatement avant un nouveau spawn.

Une reprise fournit la révision et le digest courants du record. Un rejeu exact déjà traité est une lecture pure : il ne relance pas le handler et ne produit pas de nouvelle trace. Une approbation expirée peut être renouvelée pour le même sujet et la même référence ; elle n'est pas héritée du run original.

Limites​

Le store SQLite fourni vérifie atomiquement révision et empreinte, y compris entre plusieurs processus locaux. L'hôte coordonne les exécutions et conserve les sauvegardes. Ce store ne fournit pas de lease multi-hôte ni d'historique inviolable. Les anciens fichiers JSON utilisent la migration explicite présentée dans Stockage et conservation.

Une compensation terminée prouve l'application de l'inverse déclarée, pas la restauration parfaite du monde extérieur. Cette capacité n'est ni une saga, ni une transaction distribuée, ni une garantie exactly-once.