# Reprendre un système existant La plupart du travail réel ne démarre pas sur un dépôt vide : c'est du brownfield. Vous héritez d'un service à moitié construit, vous reprenez une mission mise en pause, ou vous faites face à un système qui n'a jamais été bâti sur le moindre principe. La réponse de runward est une seule commande en lecture seule, runward characterize, qui transforme une base de code non documentée en point de départ factuel d'une mission gouvernée, ainsi qu'un workflow qui vous dit quoi en faire. ## Ce que fait `runward characterize` (et ce qu'il ne fait pas) `runward characterize` produit un inventaire factuel d'une base de code existante et l'écrit dans `runward/characterization.md`. Il est délibérément, structurellement limité : - **Lecture seule.** Il ouvre des fichiers et analyse des artefacts au repos. Il n'installe jamais, ne construit jamais, n'exécute jamais et n'instrumente jamais la cible, et ne lui adresse aucun appel réseau (ADR-0014). - **N'écrit que dans `runward/`.** Il n'écrit jamais dans votre arbre source et ne touche jamais à `.git/`. La lecture de l'historique git se fait via des commandes git en lecture seule comme `git rev-list`, `git log` et `git shortlog`, jamais une écriture. - **Zéro-LLM, déterministe.** Toute la commande est du pur parsing ; deux exécutions sur le même commit produisent la même sortie. Aucun modèle n'est appelé et aucun n'est embarqué. Notez que cela concerne la *commande* : le LLM qui affine les candidats en décisions argumentées vit dans votre harness, en aval, jamais dans runward. - **Des faits, pas des décisions.** L'en-tête de sortie porte l'estampille `confidence: high` et indique clairement que rien dedans n'explique *pourquoi* le système existe. Il n'émet jamais « voici votre architecture » ou « voici votre posture de conformité » : cette tentation est précisément ce que le contrat interdit par construction. ### Codes de sortie - `0` quand l'inventaire est produit. - `2` quand il n'y a pas de répertoire cible lisible. ## Ce qu'il lit Le balayage parcourt l'arborescence (borné à une profondeur de 6, en ignorant les répertoires lourds comme `.git`, `node_modules`, `dist`, `build`, `.venv`, `target`) et collecte : - **Écosystèmes de dépendances.** Node/JS/TS est analysé intégralement depuis `package.json` : nombre de dépendances runtime, nombre de dépendances dev, et les noms réels des dépendances. Python (`pyproject.toml`, `requirements.txt`, `setup.py`, `Pipfile`), Go (`go.mod`, en comptant les lignes du bloc require) et Rust/Java/Ruby/PHP sont détectés par présence du manifeste plus lockfile ; pour ceux-ci, les *comptes* de dépendances sont partiels (seuls `requirements.txt` et `go.mod` sont comptés à la ligne ; le reste ne rapporte que la présence). Un lockfile manquant est rapporté comme **`none` (unpinned)** dans la table rendue. Un `package.json` malformé est tout de même enregistré, avec des dépendances laissées inconnues plutôt que de faire planter la commande. - **Points d'entrée.** `main`/`bin` déclarés dans `package.json`, plus les fichiers de convention comme `index.ts`, `src/index.ts`, `src/main.ts`, `main.py`, `app.py`, `src/main.rs`, `main.go`. - **CI / pipelines.** Chaque `.yml`/`.yaml` sous `.github/workflows/`, plus `.gitlab-ci.yml`, `azure-pipelines.yml`, `.circleci/config.yml`, `Jenkinsfile`, `.drone.yml`. - **Conteneurs / déploiement.** `Dockerfile`, `docker-compose*`, `compose.yaml`, `vercel.json`, `netlify.toml`, `fly.toml`, `Procfile`, `Chart.yaml`. - **Tests.** Présence des répertoires `test/`, `tests/`, `__tests__/`, `spec/`, et un décompte des fichiers correspondant aux conventions de nommage de tests pour JS/TS, Go (`_test.go`), Python (`test_*.py`) et Java (`*Test.java`). Aucun chiffre de couverture n'est lu ni rendu : seulement la présence de répertoires et un décompte de fichiers. - **Forme git.** Des comptes uniquement : total des commits, dates du premier et du dernier commit (l'intervalle), et nombre d'auteurs distincts. La section rendue indique franchement que le log vous dit *ce qui* a changé et *quand*, rarement *pourquoi* : et le *pourquoi* est une décision à reconstituer, pas un fait à lire. Si la cible n'est pas un dépôt git (ou si git est absent), ceci vaut `null` et est rapporté comme tel. Le fichier rendu rapporte aussi un décompte approximatif des fichiers balayés. ## Optionnel : extraction d'ADR candidats avec `--mine` ### Ce qu'il propose Ajoutez `--mine` pour proposer en plus des décisions rétroactives *candidates*. C'est toujours de l'archéologie git déterministe, avec **aucun appel de modèle**. Cela propose : - un candidat par stack détectée ; - un pour la cible de déploiement (s'il y en a une) ; - un pour le pipeline CI (s'il y en a un) ; - jusqu'à huit pour les familles de dépendances notables (framework web / UI, base de données / ORM, file / messagerie, IA / fournisseur de modèle, SDK de service externe). Pour chaque candidat de stack, il recherche la date de première apparition du manifeste via `git log --diff-filter=A --reverse`. ### Comment les candidats sont écrits Chaque candidat est écrit sous la forme `runward/adr/DRAFT-.md` avec `Status: hypothesis`, les pointeurs de preuve, `why: UNKNOWN — the operator must supply the rationale`, et un déclencheur de réévaluation obligatoire (vide). - Les DRAFTs sont écrits avec `force:false`, de sorte qu'un DRAFT que vous avez déjà commencé à éditer n'est **jamais** écrasé. - Les fichiers `ADR-NNNN-*.md` ratifiés par l'opérateur ne sont jamais ciblés ni écrasés. - `characterization.md` lui-même est toujours rafraîchi (idempotent, `force:true`). ### Rien de tout cela ne peut passer la garde Rien de ce que `--mine` émet ne peut passer la garde : un DRAFT porte `Status: hypothesis`, et `runward check --strict` reste au rouge tant que vous n'avez pas réécrit le *pourquoi*, ajouté un déclencheur, mis `Status: accepted` et renommé le fichier en `ADR-NNNN-.md`. Ratifier un candidat est votre décision, pas celle de votre agent. ## Le workflow brownfield recommandé La commande imprime ses propres étapes suivantes, qui mènent vers `runward/workflows/brownfield.md`. La règle du workflow est : **caractériser avant de toucher, ne jamais réécrire d'un bloc**. ### Quatre modes d'entrée Il identifie quatre modes d'entrée et vous dit de vous caler sur le plus exigeant lorsqu'ils se recoupent : - **M1 : Reprendre votre propre mission.** Des artefacts existent. Relisez-les, reconstruisez l'état à partir des artefacts persistés (jamais la mémoire), trouvez la phase atteinte, reprenez là. - **M2 : Rejoindre un projet en cours.** Inventoriez le système, puis produisez les livrables amont manquants : un léger recadrage rétroactif, une note d'architecture reconstituée par observation, et des ADR rétroactifs. Réintégrez la chaîne, généralement à `architect`. - **M3 : Auditer puis reconstruire.** D'abord un audit rigoureux et une analyse d'écarts section par section, puis une reconstruction par étapes, jamais d'un bloc. - **M4 : Dériver un nouveau système à partir d'un ancien.** Traitez l'ancien système comme des exigences et des contraintes, pas comme une base à copier ; réintégrez en greenfield à `frame`. ### Reconstructions (M3/M4) Pour les reconstructions (M3/M4), le workflow prescrit trois mouvements dans l'ordre, jamais sautés : 1. Caractériser le comportement avec des tests de caractérisation avant tout changement, en notant que *ces tests d'exécution vous appartiennent, dans votre propre harness, pas à une commande runward*. 2. Installer une couche anticorruption à la frontière. 3. Remplacer progressivement, à la manière du strangler, chaque étape gardée et réversible. Chaque choix structurant que vous conservez ou prenez est verrouillé dans un ADR, puis vous réintégrez la chaîne `method` standard avec les mêmes gardes qu'en greenfield. ## Pilotable par un agent, frontière de câblage intacte Un agent peut piloter tout cela de bout en bout : lancer `runward characterize --mine`, lire `runward/characterization.md` et les fichiers `DRAFT-*.md`, et vous aider à reconstituer chaque *pourquoi*. Ce qu'il ne peut pas faire, c'est blanchir de lui-même une hypothèse en décision acceptée : la garde reste au rouge tant que *vous* n'avez pas ratifié. Et `characterize` ne câble jamais rien dans votre dépôt : il écrit uniquement sous `runward/`, jamais dans `.git/`, conformément à la règle de runward selon laquelle les adaptateurs sont des échantillons inertes que l'opérateur câble (ADR-0012). La frontière en lecture seule est ici un choix d'autorisation délibéré, pas une fonctionnalité manquante. ## Référence rapide ``` runward characterize # read-only inventory → runward/characterization.md runward characterize --path ./svc # target a subdirectory runward characterize --mine # also propose DRAFT retroactive ADRs (deterministic) RUNWARD_DRY_RUN=1 runward characterize --mine # print intended writes, write nothing ``` Le dry-run fonctionne de deux façons : le flag global `--dry-run` (`runward --dry-run characterize --mine`) ou la variable d'environnement `RUNWARD_DRY_RUN=1` ; les deux affichent les écritures prévues sans rien toucher sur le disque. Les options propres à la commande sont `--path` et `--mine`. Sortie `0` = inventaire produit ; sortie `2` = pas de répertoire cible lisible. `--mine` ne change jamais le code de sortie : c'est indicatif. ## Pourquoi ce choix La commande est délibérément en lecture seule et zéro-LLM par construction, pas par convention. ADR-0014 consigne les alternatives rejetées : 1. Laisser characterize EXÉCUTER/instrumenter le système pour observer le comportement réel, rejeté car cela viole le principe never-a-runtime et exécuterait du code non fiable ; les tests de caractérisation à l'exécution appartiennent au propre harness de l'opérateur. 2. Émettre des ADR directement ou auto-accepter les décisions extraites, rejeté car cela blanchit une hypothèse en fait validé et laisse une supposition machine satisfaire la garde, brisant le principe operator-owns-the-gate. 3. Mettre l'extraction dans le chemin par défaut, rejeté pour que l'inventaire déterministe tienne seul, hors ligne et reproductible. 4. Un artefact combiné unique, rejeté pour garder les faits confidence:high (characterization.md) nettement séparés des suppositions hypothesis (DRAFT-*.md). L'objectif est de rendre structurellement impossible, pour un agent qui lit une vraie base de code, l'émission de conclusions qui atteindraient la garde. ## Ce qui vient ensuite Le déclencheur de réévaluation d'ADR-0014 surveille l'usage terrain de `characterize --mine`. Deux signaux comptent : - des opérateurs qui promeuvent des DRAFTs sans les lire (en faisant confiance à des propositions sans ratification) ; - l'inventaire déterministe sollicité pour porter des jugements au-delà du parsing. La réponse est d'ajouter de la friction à la promotion des DRAFTs (une confirmation explicite par ADR) ou de restreindre l'inventaire, jamais de laisser characterize dériver vers l'affirmation de conclusions. ## Voir aussi - [Quickstart](https://runward.dev/docs/getting-started/quickstart/) - [Les six phases](https://runward.dev/docs/concepts/six-phases/) - [Depuis un agent IA](https://runward.dev/docs/operating/from-an-agent/) - [Maintenir & diagnostiquer](https://runward.dev/docs/operating/maintain/)