runward

RW™ · V0.26.0

Deux règles violées, la porte au vert.

Un retour de terrain a montré une limite que la porte n'avait jamais déclarée : une ligne verte ne voyage pas dans le temps. Elle le déclare maintenant, et une commande répond enfin à « quelles règles gouvernent ce fichier ? », en affichant le motif qui retient chacune.

Un retour de terrain, sur une mission construite avec runward : deux règles de craft écrites, violées en une après-midi, sur du code que l'agent venait lui-même d'écrire. runward check --strict au vert du début à la fin. Une règle HIGH sur les garde-fous de job de fond, une CRITICAL sur la frontière des secrets.

La porte s'est comportée exactement comme elle le déclarait. Le problème est que la déclaration était incomplète.

Ce qu'une ligne verte n'a jamais prouvé

Depuis ADR-0040, chaque verdict voyage avec sa zone aveugle déclarée : une ligne verte prouve qu'une décision a été tracée vers une preuve qui résout, jamais que cette preuve implémente la règle. La porte lit des octets au repos ; elle n'exécute rien et ne juge aucune sémantique. C'était écrit, et c'est un actif de crédibilité, pas un trou à cacher.

Ce qui n'était pas écrit : une ligne verte ne voyage pas dans le temps. Le jugement de l'opérateur portait sur le code qui existait quand la ligne a été écrite. Chaque exécution revérifie que les pointeurs cités résolvent et que la preuve scellée n'a pas dérivé, mais rien ne rejuge du code ajouté ensuite sous une règle déjà comptabilisée. Au standard d'ADR-0040 lui-même, « chaque porte nomme ce qu'elle ne peut pas vérifier », c'était un angle mort non déclaré. Il l'est maintenant.

La question qu'on ne pouvait pas poser

La base neutre écrite à l'init prescrit de confronter les règles de craft « au point d'action, pas de mémoire ». Rien ne rendait cette phrase mécanisable : rules filtrait par phase, et la seule phase govern en rend douze. Au point d'action, l'instruction se traduisait par « relis-en 64 et devine ».

runward rules --for <chemins> répond, et affiche le motif qui retient chaque règle, sur le modèle de git check-ignore -v, vieux de vingt ans et fait pour exactement ça :

async-job-guardrails      HIGH      appliesTo=**/cron/**    src/cron/runner.ts
config-secrets-boundary   CRITICAL  appliesTo=**/config/**  src/config/egress-key.ts

Le matching porte uniquement sur un territoire que la règle déclare, jamais sur ses tags : les tags sont thématiques, en déduire un chemin serait une heuristique sans motif auditable à montrer. Aucun accès disque, aucun git, aucun modèle : du texte comparé à du texte, donc un fichier peut ne pas exister encore. Et les chemins viennent de l'appelant : runward ne calcule jamais votre ensemble de changements, la composition reste la vôtre.

Le silence n'est pas une déclaration

Première version : une règle déclarait un territoire, ou ne disait rien. C'était le défaut. Une règle que personne n'avait encore examinée et une règle qui ne gouverne délibérément aucune classe de fichiers se lisaient exactement pareil : la confusion même qu'ADR-0040 existe pour refuser, réintroduite par le mécanisme censé l'appliquer.

Il y a donc trois états, comptés séparément : un territoire déclaré · une absence de territoire déclarée, avec sa raison · pas encore tranché. Une passe éditoriale a tranché les 64 règles : 14 déclarent un territoire, 50 déclarent ouvertement n'en avoir aucun, zéro reste muette. La raison de chaque absence se lit dans runward explain, et un test refuse désormais une règle qui ne déclarerait ni l'un ni l'autre.

Cinq règles ont été délibérément refusées à l'ancrage plutôt que d'inventer un territoire pour obtenir un zéro propre. Une règle sur le pooling de connexions aurait dicté l'arborescence de votre code. Une règle de gouvernance des contrats aurait sous-déclaré : un changement de DTO se serait lu « hors périmètre ». Un motif faux est pire qu'aucun motif, parce qu'il s'affiche comme un fait auditable.

Ce que la commande ne fait pas

Elle fait passer « relis 64 règles et devine » à « relis-en trois ». Elle ne vérifiera jamais que le code respecte la règle : ce jugement reste le vôtre, à la porte, et c'est une propriété assumée. Elle n'est pas non plus exhaustive par construction : cinquante règles ne déclarent aucun territoire et ne seront donc jamais matchées. La commande l'annonce à chaque exécution : non-matché ne veut pas dire inapplicable.

Ce que le framework peut faire, c'est poser la question au bon moment. Y répondre reste humain.


Mettre à jour : npm i -g runward@0.26.0 · dans une mission : runward update

ADR-0041 · ADR-0042 · Le changelog · Toutes les nouveautés

← Toutes les nouveautés