Aller au contenu principal
Version: 1.0.0

Projet Touba et prepare

Choisir le processus d'un handler​

Une décision hôte peut fournir le processus complet d'un binding : kind, command, args et, si nécessaire, cwd.

host-decisions.json
{
"bindings": {
"tool:orders.review/ReadOrder": {
"handler": {
"kind": "process",
"command": ".venv/bin/python",
"args": ["-B", "handlers/read_order.py"],
"cwd": "."
}
}
}
}
touba prepare --mode plan --host-decisions host-decisions.json
touba prepare --mode write --host-decisions host-decisions.json

Sous Windows, utilisez .venv/Scripts/python.exe. Le processus est remplacé comme un ensemble : les anciens arguments ne sont pas conservés. Choisissez handler ou package_handler pour un binding. Un binding géré par fragment se modifie dans son fragment. Grants, secrets et contrat d'effet restent des décisions explicites distinctes.

Ce guide explique comment organiser un petit projet Touba sans gérer manuellement les artefacts dérivés.

Les commandes de ce guide supposent que la CLI Touba est disponible dans le terminal. Installer seulement l’extension VS Code via VSIX ne suffit pas pour obtenir la commande touba.

Rôle​

Un projet Touba relie les éléments suivants :

ÉlémentRôle
fichier .tbsource lisible par le développeur
moduleidentité logique déclarée dans le fichier
configuration projetentrée, profils et références explicites vers les configurations d’autorité
projection de buildAIR, manifest assemblé et run plan dérivés pour un profil
manifestcontrat effectif d’exécution des tools, dérivé des autorités déclarées
handlercode hôte appelé par un tool
ownershipresponsabilité équipe, owners et review gates

Un fichier .tb n’a pas toujours besoin d’un manifest ou d’un handler. Un projet en a besoin lorsque l’AIR préparé contient un tool à exécuter, que ce tool soit déclaré dans le fichier courant ou importé depuis un autre module du même projet.

Décider explicitement des droits hôte​

Pour éviter un script de modification du manifest, fournissez les décisions à prepare. Exemple pour le tool ReadOrder du module orders.review :

Terminal
touba prepare --mode plan --host-decisions-json '{"bindings":{"tool:orders.review/ReadOrder":{"grants":["network"],"effect_contract":{"kind":"none"},"timeout_ms":5000}}}'

Le rapport montre le changement et les besoins source. Après revue, reprenez la même commande avec --mode write, puis avec --mode check. Seul write écrit. Remplacez la référence et les décisions par celles de votre projet ; kind: "none" convient uniquement à un handler sans écriture externe.

Champs possibles : grants, secrets (handles, jamais valeurs), memories, effect_contract, timeout_ms, max_payload_bytes, sandbox. La clé de premier niveau agent_secret_scopes permet de sélectionner les handles autorisés par agent. Les sélections doivent correspondre à des besoins déclarés par le source. Les champs omis sont conservés ; une liste vide révoque le champ sélectionné. Une permission manquante reste signalée, sans complément automatique.

Répéter les mêmes décisions ne change rien. Un objet invalide empêche toutes les écritures. Un binding fourni par fragment doit être modifié dans son fragment, pas avec cette option ; toute divergence avec le manifest courant est refusée et demande une réconciliation explicite. Le manifest reste l'autorité d'exécution ; aucun second registre n'est créé. Un lock existant doit être vérifié et renouvelé explicitement après une modification d'autorité. Ne passez aucune valeur secrète sur la ligne de commande.

Convention de module​

Les clés de touba.project.json et de son objet configuration sont fermées : une clé inconnue est refusée avec ProjectConfigFieldUnknown. Placez les contextes dans configuration.contexts, jamais dans contexts à la racine.

Dans un projet, la convention est :

Structure
src/examples/search.tb

avec :

source.tb
module examples.search

La partie dossier donne le domaine logique. Le nom du fichier donne le dernier segment du module.

Initialiser un projet​

Syntaxe :

Terminal
touba init

Effet :

  • crée les dossiers de projet attendus ;
  • crée touba.project.json avec le langage hôte JavaScript, le SDK @touba/sdk, le layout touba-artifacts-v1 et les profils pure / dev / prod ;
  • sépare les dérivés dans .touba/build/, l’état dans .touba/state/ et les preuves locales dans .touba/evidence/ ;
  • prépare les exclusions de projet ;
  • ne crée pas de manifest tant qu’aucun tool n’existe.

Pour les handlers Python, utilisez touba init --language python, puis touba sdk install python. Pour JavaScript, utilisez touba sdk install javascript. Les deux SDK suivent les mêmes concepts ; seul le langage hôte change.

Autorité explicite de l’hôte​

prepare expose les besoins du source dans manifest_requirements. Les manifests générés ne donnent aucun grant, secret ou scope mémoire ; les triggers restent désactivés. Relisez ces besoins, implémentez les handlers et configurez les permissions hôte explicitement. needs_changes signifie que des actions restent à faire, pas que l’exécution est prête.

Passez --manifest touba.manifest.json pour conserver le manifest relu hors du build, ou configurez son chemin dans le projet. La préparation préserve les décisions hôte et les handlers métier. Un nouveau besoin du source n’élargit jamais les droits silencieusement.

Seul touba-artifacts-v1 est accepté : .touba/build/<profil>/ pour les builds, .touba/state/ pour l’état. La stratégie unique, ou à défaut l’entrée exécutable unique, est choisie sans ambiguïté. Sinon utilisez --entry <ref>. Aucun repli vers un ancien layout ni migration implicite n’est effectué.

Entrée par défaut d'un projet​

Avec plusieurs stratégies, ajoutez à la racine de touba.project.json la propriété "entry": "strategy:app.support/Run" en remplaçant la référence par une entrée réelle. Conservez les autres champs. Cette entrée est partagée par les préparations write et check.

Pour un choix ponctuel, passez le même --entry <ref> aux deux commandes. Un write avec cette option suivi d'un check sans elle ne vérifie pas le même projet effectif : EffectiveProjectDrift ou select_entry est attendu. Aucune stratégie n'est choisie arbitrairement.

Créer un module​

Syntaxe :

Terminal
touba new module examples.hello

Effet :

Structure
src/examples/hello.tb

avec :

source.tb
module examples.hello

Un nom de module projet doit contenir au moins deux segments en minuscules, par exemple examples.hello.

Créer un package contextuel​

touba package new acme.review --context --yes crée le manifeste auteur, la procédure et le README. Utilisez touba package check review, puis touba package add ./review pour l'enregistrer. Voir les packages et l'activation du contexte.

Préparer un projet sans tool​

Exemple :

source.tb
module examples.hello

strategy RunHello
gives Text
do
show
with message as "Touba ready"
give "done"

Commande :

Terminal
touba prepare --mode plan

Résultat attendu :

  • le rapport indique l’AIR qui serait produit ;
  • aucun manifest n’est demandé ;
  • aucun handler n’est généré.

Pour écrire les artefacts dérivés :

Terminal
touba prepare --mode write --profile pure

Le projet obtient l’AIR sous .touba/build/pure/air/. Aucun manifest n’est produit puisque le programme ne déclare pas de tool.

Préparer un projet avec tool​

Exemple :

source.tb
module examples.search

type SearchRequest
query is Text

type SearchResult
title is Text

tool SearchDatasets
takes request as SearchRequest
gives SearchResult
requires network

Commande :

Terminal
touba prepare --mode write --profile dev

Résultat attendu :

  • un AIR est produit ;
  • touba.project.json fixe le langage hôte, le SDK, l’entrée et les références de configuration ;
  • .touba/build/dev/touba.manifest.json reçoit le binding effectif pour SearchDatasets ;
  • .touba/build/dev/touba.run-plan.json compose l’entrée et les dérivés si entry est déclarée ;
  • .touba/build/dev/touba.effective-project.json explique le contrat résolu et la provenance de chaque section ;
  • un stub de handler est créé sous handlers/examples/search/.

Le handler reste du code métier à compléter. Le stub ne doit pas être traité comme une implémentation fonctionnelle.

Le manifest ne contient pas de valeur de secret. Il est régénérable, mais doit rester relisible comme projection des contrats d’exécution du projet.

Composer un fragment de binding​

Un fragment de binding décrit une intention explicite de wiring. Il peut venir d’un module, d’un package local ou d’un outil de génération, mais il n’est pas un artefact runtime.

bindings/search.json
{
"fragment_schema_version": "touba-binding-fragment-v1",
"source": {
"kind": "module",
"module": "examples.search"
},
"bindings": {
"tool:examples.search/SearchDatasets": {
"handler": {
"kind": "process",
"command": "/absolute/path/to/node",
"args": ["handlers/examples/search/search_datasets.handler.mjs"],
"cwd": "."
},
"grants": ["network"],
"secrets": [],
"timeout_ms": 10000,
"max_payload_bytes": 1048576,
"payload_constraints": {
"input": {
"request.query": {
"non_empty": true,
"max_length": 80
}
}
}
}
}
}

Composition :

Terminal
touba prepare --binding-fragment bindings/search.json --mode write

prepare vérifie que le tool existe dans l’AIR assemblé, que les grants et secrets requis sont présents, que les contraintes de payload sont valides, puis écrit le manifest assemblé du profil. touba run reçoit ce manifest dérivé via un run plan explicite, jamais le fragment.

Pour éviter de répéter le flag, référencez le fragment dans le projet :

touba.project.json (extrait)
{
"entry": "strategy:examples.search/RunSearch",
"configuration": {
"binding_fragments": ["bindings/search.json"],
"resource_bindings": ["touba.resources.json"],
"ownership": "touba.ownership.json"
}
}

Ces chemins sont explicites. Leur présence sur disque ne suffit jamais à les activer.

Valider des resource bindings​

Un resource binding relie une ressource logique Touba à un câblage hôte explicite. Pour les resource bindings, Touba valide le contrat tandis que le backend reste fourni par l’hôte. Avec la transmission des resource bindings (RB-2), touba run --resource-bindings transmet aussi au handler les seuls bindings associés aux ressources effectivement utilisées par le tool appelé.

touba.resources.json
{
"resource_bindings_schema_version": "touba-resource-bindings-v1",
"resources": {
"memory:examples.memory/Notes": {
"kind": "memory_backend",
"provider": "host.project_memory",
"config": {
"endpoint_url": "https://memory.example.internal/notes"
},
"grants": ["memory.read"],
"secrets": ["DATABASE_URL"],
"required_secrets": ["DATABASE_URL"],
"limits": {
"max_payload_bytes": 1048576
}
}
}
}

Validation projet :

Terminal
touba prepare \
--resource-bindings touba.resources.json \
--require-resource-bindings \
--mode check

Validation runtime :

Terminal
touba run --project . \
--entry strategy:examples.memory/Run \
--profile dev

Touba refuse une ressource inconnue, une famille non supportée, un binding inactif, un grant ou secret requis absent, un scope mémoire manquant, ou un champ qui contient un payload brut ou une valeur secrète.

Binding transmis au handler​

Dans le protocole handler, le binding transmis ressemble à ceci :

Extrait de requête handler
{
"resource_bindings": [
{
"resource_ref": "memory:examples.memory/Notes",
"kind": "memory_backend",
"provider": "host.project_memory",
"grants": ["memory.read"],
"secret_handles": ["DATABASE_URL"],
"config": {
"endpoint_url": "https://memory.example.internal/notes"
},
"limits": {
"max_payload_bytes": 1048576
}
}
]
}

Le handler utilise ces informations comme câblage hôte. Il ne décide pas si la ressource est autorisée : les vérifications AIR, manifest, scopes, grants, secrets et resource bindings restent faites avant dispatch par Touba.

Préparer un projet avec imports purs​

La tranche d’imports purs (IM-1) autorise des imports explicites de type, define non sensible et function pure.

Module commun :

src/examples/common.tb
module examples.common

type SearchRequest
query is Text
rows is Int

define DefaultRows as Int = 5

function NormalizeQuery
takes query as Text
gives Text
do
give query

Module importeur :

src/examples/search.tb
module examples.search

import type SearchRequest from examples.common
import define DefaultRows from examples.common
import function NormalizeQuery from examples.common

strategy RunSearch
gives Text
do
show
with message as "search project ready"
give "done"

Commande :

Terminal
touba prepare --mode write

Résultat attendu :

  • les modules du projet sont compilés explicitement ;
  • l’AIR préparé de examples.search contient les imports purs résolus ;
  • aucun manifest ou handler n’est créé si aucun tool n’est déclaré ;
  • les surfaces Touba peuvent inspecter les imports depuis l’AIR préparé.

Préparer un projet avec imports effectifs locaux​

Touba autorise les imports locaux de tool, skill, strategy, agent et memory déclarés dans un autre module du même projet. L’import rend la déclaration disponible pour composition, mais il ne déplace pas l’autorité d’exécution.

Module déclarant :

src/examples/data_tools.tb
module examples.data_tools

type DatasetSearchRequest
query is Text
rows is Int

type DatasetSearchResult
title is Text
url is Text

tool SearchDatasets
takes request as DatasetSearchRequest
gives DatasetSearchResult
requires network

Module importeur :

src/examples/research_app.tb
module examples.research_app

import type DatasetSearchRequest from examples.data_tools
import type DatasetSearchResult from examples.data_tools
import tool SearchDatasets from examples.data_tools

skill BuildDatasetBrief
takes request as DatasetSearchRequest
gives DatasetSearchResult
uses SearchDatasets
do
SearchDatasets
with request as request
into result
give result

Commande :

Terminal
touba prepare --mode write

Résultat attendu :

  • l’AIR préparé de examples.research_app contient l’import tool résolu ;
  • le tool garde sa référence canonique tool:examples.data_tools/SearchDatasets ;
  • touba.manifest.json reçoit ou vérifie le binding de cette référence canonique ;
  • le stub de handler est créé sous handlers/examples/data_tools/, pas sous le module importeur ;
  • les grants, secrets, limites, contraintes payload et lock supply chain restent explicites.

Les autres imports effectifs suivent la même règle :

  • une skill ou une strategy importée conserve son graphe et ses dépendances dans l’AIR préparé ;
  • un agent importé reste fermé tant que son callable_by ne nomme pas explicitement l’appelant, y compris sous forme inter-module ;
  • une memory importée reste une ressource logique ; un owner doit déclarer le scope read ou write requis avant d’appeler un tool d’accès.

Préparer un projet avec package local​

Un package local Touba rend des déclarations disponibles entre projets. Il ne donne pas d’autorité d’exécution.

Structure possible :

Structure
mon-projet/
src/examples/package_app.tb
authors/research/
touba.package.json
src/org/example/research.tb

Manifeste de package :

authors/research/touba.package.json
{
"package_schema_version": "touba-package-v1",
"name": "org.example.research",
"version": "0.1.0",
"root": ".",
"touba": {
"air_profile": "touba-air-json-canonical-v1",
"runtime_compatibility": ">=1.0 <2.0"
},
"exports": {
"types": [
"type:org.example.research/DatasetSearchRequest",
"type:org.example.research/DatasetSearchResult"
],
"defines": [],
"functions": [],
"tools": [
"tool:org.example.research/SearchDatasets"
],
"skills": [],
"strategies": [],
"agents": [],
"memories": []
},
"provenance": {
"kind": "local_path",
"source": "."
}
}

Configuration du projet :

touba.project.json
{
"project_version": "touba-project-v1",
"language": "javascript",
"sdk": "@touba/sdk",
"packages": [
{
"manifest": "authors/research/touba.package.json",
"editable": true,
"version": "0.1.0"
}
],
"run_profiles": {
"dev": {
"require_supply_chain_lock": false
},
"prod": {
"require_supply_chain_lock": true,
"supply_chain_lock": "touba.lock.json"
}
}
}

Module fourni par le package :

authors/research/src/org/example/research.tb
module org.example.research

type DatasetSearchRequest
query is Text
rows is Int

type DatasetSearchResult
title is Text
url is Text

tool SearchDatasets
takes request as DatasetSearchRequest
gives DatasetSearchResult
requires network

Module du projet :

src/examples/package_app.tb
module examples.package_app

import type DatasetSearchRequest from org.example.research
import type DatasetSearchResult from org.example.research
import tool SearchDatasets from org.example.research

skill BuildDatasetBrief
takes request as DatasetSearchRequest
gives DatasetSearchResult
uses SearchDatasets
do
SearchDatasets
with request as request
into result
give result

Commande :

Terminal
touba prepare --mode write

Résultat attendu :

  • le package est chargé parce qu’il est déclaré explicitement ;
  • seuls les refs listés dans exports sont importables ;
  • l’AIR préparé du projet contient les déclarations importées utiles ;
  • si un tool exporté est réellement importé, prepare produit le binding attendu dans touba.manifest.json et le stub sous le module déclarant ;
  • le package ne peut pas accorder un grant, un secret, un handler, un scope mémoire, un caller d’agent ou un trigger actif.

Option équivalente sans modifier touba.project.json :

Terminal
touba prepare --package authors/research/touba.package.json --mode write

Préparer un run plan​

Un run plan regroupe les chemins d’exécution du projet dans un artefact explicite. Il pointe vers l’AIR préparé, le manifest si des tools existent, le profil de secrets si des secrets sont requis, et le lock si le profil de run l’exige.

Pour un projet simple :

Terminal
touba prepare \
--run-plan touba.run-plan.json \
--entry skill:examples.package_app/BuildDatasetBrief \
--mode write

Forme produite :

touba.run-plan.json
{
"run_plan_schema_version": "touba-run-plan-v1",
"entry_ref": "skill:examples.package_app/BuildDatasetBrief",
"air": ".touba/air/examples/package_app.air.json",
"manifest": "touba.manifest.json",
"secrets_profile": "touba.secrets.dev.json",
"profile": "dev",
"outputs": {
"mode": "show"
}
}

Le run plan ne contient pas d’inputs métier, de prompt brut ou de valeur secrète. Les entrées d’exécution restent fournies au moment du run.

Pour un profil strict :

Terminal
touba prepare \
--run-plan touba.run-plan.json \
--entry skill:examples.package_app/BuildDatasetBrief \
--profile prod \
--mode write

Dans ce cas, si le profil prod exige un lock supply chain, le plan pointe aussi vers touba.lock.json.

Valider l’ownership multi-équipe​

Un artefact touba.ownership.json décrit les équipes responsables, les owners, les review gates et les reviews fournies pour un profil.

touba.ownership.json
{
"ownership_schema_version": "touba-ownership-v1",
"teams": {
"team:platform": {
"display_name": "Platform"
},
"team:security": {
"display_name": "Security"
}
},
"owners": {
"tool:examples.search/SearchDatasets": ["team:platform"]
},
"review_gates": {
"prod": {
"requires_review_for": ["tool_binding", "required_capability"],
"reviewers": ["team:security"]
}
},
"reviews": [
{
"review_id": "review:search/security-001",
"profile": "prod",
"target_ref": "tool:examples.search/SearchDatasets",
"reviewer": "team:security",
"decision": "approved"
}
]
}

Validation :

Terminal
touba prepare \
--ownership touba.ownership.json \
--profile prod \
--mode check

Avec --require-ownership, Touba refuse si le fichier d’ownership est absent.

touba.ownership.json ne donne aucune permission runtime. Il ne crée pas de handler, grant, secret, scope mémoire, caller d’agent, provider, lock ou manifest. Il permet de vérifier que la responsabilité et les reviews attendues sont présentes avant d’avancer vers un profil sensible.

Vérifier la cohérence​

Syntaxe :

Terminal
touba prepare --mode check

Cette commande échoue si :

  • le manifest attendu manque ;
  • un binding de tool manque ;
  • un handler attendu manque ;
  • un grant, secret ou accès mémoire attendu n’est pas accordé ;
  • le run plan attendu manque, dérive ou contient un champ sensible ;
  • une review gate attendue manque pour le profil demandé ;
  • l’artefact d’ownership tente d’introduire une autorité runtime ;
  • le chemin du fichier source ne correspond pas au nom de module.

Exécuter ensuite​

prepare ne lance pas le runtime.

Pour exécuter un programme avec tool, le manifest reste explicite :

Terminal
touba run program.air.json \
--manifest touba.manifest.json

Si un run plan a été préparé :

Terminal
touba run --run-plan touba.run-plan.json \
--inputs-json '{"request":{"query":"transport"}}'

Le runtime refuse avant exécution si le plan pointe vers un AIR absent, une entrée inconnue, un manifest ou profil secrets requis absent, ou un lock supply chain strict incohérent.

Pour un run de profil prod, le projet force aussi le lock supply chain déclaré dans touba.project.json :

Terminal
touba run program.air.json \
--profile prod \
--manifest touba.manifest.json

Pour un programme sans tool, aucun manifest n’est nécessaire :

Terminal
touba run program.air.json

Contrat Projet​

prepare coordonne le contrat projet :

  • imports purs (IM-1) : type, define non sensible et function pure ;
  • imports effectifs (IM-2) : tool, skill, strategy, agent et memory, sans autorité implicite ;
  • packages explicites (PKG-1) via touba.project.json ou --package, avec installation explicite et sans autorité implicite ;
  • run plan (RP-1) via touba prepare --run-plan et touba run --run-plan, sans payload sensible ni découverte automatique ;
  • fragments de bindings (BF-1) via touba prepare --binding-fragment, composés dans le manifest assemblé sans exécution directe du fragment ;
  • resource bindings (RB-1 / RB-2) via touba prepare --resource-bindings et touba run --resource-bindings, puis transmis aux handlers concernés avec backends fournis par l’hôte.

Le contrat projet reste explicite : pas d’alias, wildcard ou re-export, pas de handler inline dans .tb, pas de découverte automatique de manifest par le runtime, pas de lecture automatique de .env, pas de grant ou secret ajouté silencieusement.

Ces frontières évitent qu’un fichier importé ou préparé ajoute une autorité d’exécution invisible. Pour les tools, l’import rend le tool disponible, tandis que manifest, handler, grants, secrets, contraintes, lock supply chain et refus runtime restent explicites. Pour les agents et les mémoires, les règles callable_by et les scopes restent vérifiées par l’assemblage projet.

Décisions hôte en fichier et par module​

touba prepare --mode plan --host-decisions host-decisions.json
touba prepare --mode write --host-decisions host-decisions.json
touba prepare --mode check --host-decisions host-decisions.json
host-decisions.json
{
"modules": {
"orders.review": { "grants": ["network"], "timeout_ms": 5000 }
},
"bindings": {
"tool:orders.review/ReadOrder": {
"effect_contract": { "kind": "none" }
}
}
}

Un module correspond exactement à son nom déclaré. Ses décisions portent sur les grants, secrets et limites ; chaque tool reçoit uniquement les grants et handles qu’il demande. Une décision de binding remplace la décision correspondante du module, y compris avec une liste vide pour retirer un grant. Le contrat d’effet reste explicite pour chaque binding.

Pour plusieurs parcours, conservez entry comme défaut et ajoutez entrypoints dans touba.project.json. Le serveur projet et touba run --project . --entry <référence> utilisent cette même liste.

Retirer un binding devenu inutile​

Après suppression d’un tool dans les sources, demandez explicitement le retrait de son ancien binding :

host-decisions.json
{"remove_bindings":["tool:orders.review/OldTool"]}
touba prepare --mode plan --host-decisions host-decisions.json
touba prepare --mode write --host-decisions host-decisions.json
touba prepare --mode check

Le plan affiche remove_binding. Un tool encore déclaré est protégé ; un binding fourni par fragment se retire dans ce fragment. Les fichiers handlers restent en place pour votre revue. Les imports du rapport sont regroupés par relation source, sans répéter une même relation lors de la préparation de plusieurs entrées.