# Le contrat machine : check --json et wire Piloter runward sur des données : la sortie JSON de check, la détection wire en lecture seule, la non-interactivité durcie, et la référence rapide pour un agent. ## Étape 3 : `check --json`, le contrat machine Pour un pilotage autonome, utilisez `runward check --json` afin que l'agent se branche sur des données, et non sur du texte coloré capté à l'écran (ADR-0030). En mode `--json`, chaque ligne destinée à l'humain est supprimée et la seule sortie stdout est un unique objet JSON déterministe ; le code de sortie est inchangé et reste le signal primaire. ### Aucune mission trouvée, code de sortie 2 ```json { "runward": "", "mission": null, "verdict": "no-mission", "exitCode": 2 } ``` ### Exécution normale Ajoutez `--strict` pour obtenir aussi le tableau `conformance` : ```json { "runward": "", "mission": "", "currentGate": "", "adrCount": 0, "strict": true, "verdict": "clean", // or "gaps" "exitCode": 0, // 0 clean, 1 gaps "gaps": { "deliverables": 0, "conformance": 0, "hooks": 0 }, "deliverables": [ { "phase": "...", "artifact": "...", "relPath": "...", "state": "filled" } ], "conformance": [ { "scope": "...", "rule": "...", "problem": "..." } ] } ``` La clé `conformance` n'est présente que sous `--strict`. ### Ce que `--strict` vérifie `--strict` vérifie les manifestes de conformité aux règles de manière déterministe : lignes présentes, pointeurs typés se résolvant vers un contenu réel, signatures qui correspondent, aucune dérive, sceau intact. Il vérifie la présence et la forme d'une décision tracée, jamais la qualité du code : cela reste le jugement de l'opérateur à la garde. Au-dessus d'une garde stricte au vert, runward fait apparaître deux preuves *consultatives* qu'il n'exécute ni ne lit jamais : - la preuve comportementale (votre suite de tests) - le workflow de vérification (une passe contradictoire cite-contre-applique) Les deux sont consultatives, aucune ne bloque. ### Hooks Si vous passez aussi `--hooks`, les commandes fournies par l'opérateur depuis `runward/hooks.json` s'exécutent autour de l'audit ; sous `--json`, leur stdout est redirigé vers stderr (fd 2) pour ne pas corrompre le contrat de l'objet JSON unique. Les hooks sont uniquement optionnels : la garde propre de runward ne les exécute jamais, si bien qu'un clone ne peut rien exécuter par surprise. ### Couverture `--json` aujourd'hui Note pour un agent pilote : `--json` est actuellement livré sur `check` (le chemin porteur), sur `wire`, et sur `rules` et `explain`. `status` et `doctor` n'exposent **pas** encore `--json` ; ADR-0030 diffère leur sortie machine comme un complément additif et non cassant. Pilotez sur `check --json` et son code de sortie ; lisez `status`/`doctor` en texte pour l'instant. ## Étape 4 : `runward wire`, détection au mieux, en lecture seule `runward wire` répond à une seule question : quel harnais IA exécute cette commande, afin que l'agent puisse proposer le canal de déclenchement automatique correspondant. Il est en lecture seule et n'invite jamais, si bien qu'une exécution pilotée par un agent ne se bloque jamais ; son code de sortie est toujours 0. ### La détection a deux forces Essayées dans l'ordre : 1. **Signal d'exécution** : un marqueur d'environnement que le harnais injecte dans le processus enfant. Fort : il atteste *cette* exécution. Seuls des marqueurs vérifiés sont utilisés : `CLAUDECODE=1` (Claude Code / Cowork), `GEMINI_CLI=1` (Gemini CLI), `CURSOR_AGENT=1` (mode agent Cursor). Le statut devient `detected`. 2. **Fichier de configuration** : un marqueur de profil d'outil déjà présent dans le dépôt (par ex. `.cursor/rules/runward.mdc`, `GEMINI.md`, `.kiro/steering/`). Plus faible : un dossier `.cursor/` ne prouve pas que Cursor exécute *cette* commande, il est donc présenté distinctement comme `config-detected`. ### Quand ni l'un ni l'autre ne se résout Quand ni l'un ni l'autre ne se résout, le statut est `undetermined`, ce n'est pas une erreur. C'est attendu pour Copilot CLI, Windsurf, Kiro, Continue, Aider, Trae, car aucun signal d'exécution fiable n'existe pour eux et il n'y a pas de convention `AI_AGENT=1` transversale aux harnais. Le comportement correct est de *demander à l'opérateur*, en langage clair, quel outil IA il utilise, puis de câbler l'échantillon correspondant : ne jamais deviner. ### Le contrat `--json` `runward wire --json` émet la détection comme un contrat stable. La forme `HarnessDetection` : ```json { "runward": "", "mission": "", "schemaVersion": 1, "status": "detected", // detected | config-detected | undetermined "harness": "cursor", // stable id, or null "label": "Cursor (agent mode)", "family": "cursor", "detectedVia": "runtime-signal", // runtime-signal | config-file | null "signal": "CURSOR_AGENT", // env var name, or null "recommendedChannel": { "channel": "advisory-hook", "sample": null, "note": "..." }, "candidateChannels": [ { "channel": "pre-commit", "sample": "runward/adapters/pre-commit" }, { "channel": "ci-required-check", "sample": "runward/adapters/github-actions.yml" } ], "wires": false, "operatorAction": "offer-to-wire-sample" // or "ask-operator-which-harness" } ``` ### `wires` est toujours false `wires` est **toujours `false`** : l'invariant ADR-0012 rendu vérifiable par machine. `candidateChannels` sont les canaux durs universels présents dans chaque mission après `init` et valables quel que soit le harnais : le `pre-commit` git et un contrôle requis en CI. Là où runward livre un échantillon spécifique à un harnais, il pointe vers un adaptateur inerte (par ex. `runward/adapters/claude-code-settings.json` pour le hook de fin de tour Claude) ; là où il n'en livre aucun (gemini/cursor/copilot), `sample` vaut `null` avec une note pointant vers le packaging de distribution. ## Non-interactivité durcie (pour qu'une exécution autonome ne se bloque jamais) ### Quand une exécution est traitée comme non interactive Un agent autonome ne doit jamais se bloquer sur une invite à laquelle il ne peut répondre. `isNonInteractive()` renvoie vrai quand : - `RUNWARD_YES=1` (le drapeau `--yes` le positionne) - `CI` a une valeur vraie - l'un des flux standard n'est pas un TTY La dernière condition est le correctif issu d'ADR-0030 : un agent dans un pty alloué, ou avec un stdin redirigé, est traité comme non interactif même sans `--yes`, si bien que l'assistant d'`init` retombe sur les défauts au lieu de se bloquer sur stdin. Toute commande autre qu'`init` est déjà non interactive ; `wire` n'invite jamais par conception. ### Sortie datée déterministe Pour une sortie datée déterministe dans les exécutions non interactives (par ex. les packs OSCAL, [le sceau de preuve](https://runward.dev/docs/concepts/evidence/)), `RUNWARD_NOW` remplace « aujourd'hui » mais est validé comme un vrai `YYYY-MM-DD` ; une valeur mal formée retombe sur aujourd'hui plutôt que de produire une sortie invalide au regard du schéma. ## Référence rapide pour un agent en exploitation - `runward init --yes` : base neutre (`AGENTS.md` + `.agents/skills/`), aucun profil privilégié. - `runward check --json` : verdict + garde + états des livrables ; sortie 0 propre / 1 manques / 2 aucune mission. - `runward check --strict --json` : ajoute le tableau `conformance` ; l'autorité déterministe. - `runward wire --json` : détection + canal recommandé ; `wires:false` toujours ; sortie 0 toujours. - Sur un statut `wire` `detected`/`config-detected` : proposez l'échantillon recommandé ; sur `undetermined` : demandez à l'opérateur quel outil, puis câblez l'échantillon correspondant. N'agissez que sur approbation explicite. - `runward explain --json`, `runward rules --json` : texte des règles lisible par machine. - Vocabulaire de conformité : `runward compliance ` assemble une **preuve d'appui prête pour l'audit** (OSCAL) qui alimente un programme ; l'acceptation relève de l'auditeur, jamais une revendication de certification. ## Voir aussi - [Depuis un agent IA](https://runward.dev/docs/operating/from-an-agent/) - [La frontière : runward ne se câble jamais tout seul](https://runward.dev/docs/operating/from-an-agent/the-wiring-boundary/) - [Brancher la porte](https://runward.dev/docs/operating/wire-the-gate/) - [Commandes CLI](https://runward.dev/docs/reference/cli/)