id: ADR-016 title: Les exceptions pour holding-crate sont bornées, consignées et accordées par la Core Team date: 2026-09-02 status: proposé relates-to:
- ADR-007
- crates/zeroclaw-runtime/AGENTS.md
- docs/book/src/foundations/fnd-001-intentional-architecture.md
- https://github.com/zeroclaw-labs/zeroclaw/pull/10557
- https://github.com/zeroclaw-labs/zeroclaw/pull/10410
- https://github.com/zeroclaw-labs/zeroclaw/pull/10179
ADR-016 : les exceptions au crate de maintien sont bornées, consignées et accordées par l’équipe cœur
Contexte
crates/zeroclaw-runtime/AGENTS.md déclare que ce crate est une zone de transition, demande aux contributeurs de ne pas y ajouter de nouvelles fonctionnalités et nomme les sous-systèmes en attente d’extraction. L’instruction est sans condition et ne prévoit aucune procédure d’exception.
Cette combinaison n’a pas de réponse pour le cas ordinaire : un travail accepté atterrit sur un sous-système qui réside encore dans le crate de rétention, et le crate vers lequel il est censé être déplacé n’a pas encore été créé. Le contrat interdit le seul emplacement disponible, et la destination n’existe pas encore. Trois pull requests ont atteint cet état et l’ont résolu de trois manières différentes.
Le mécanisme de garde de précondition cron a d’abord été intégré via #10220, puis une exception ponctuelle a été proposée, et enfin l’extraction complète a été réalisée dans #10557. L’extraction était le bon résultat, mais elle a été obtenue en construisant deux alternatives complètes puis en abandonnant l’une d’elles. Un contributeur doit pouvoir déterminer si l’extraction est nécessaire avant de l’implémenter deux fois.
#10410 a conservé la coordination de la configuration partagée et du cycle de vie des agents dans le runtime plutôt que de créer un crate de cycle de vie avant l’extraction prévue du daemon. La justification avancée pour ce placement est que déplacer le code vers zeroclaw-infra inverserait une dépendance existante, puisque config dépend déjà d’infra, et qu’une extraction anticipée établirait une frontière que la feuille de route ne prévoit pas. C’est l’argument en faveur d’une exception à cet endroit, et non une conclusion arrêtée : ce placement n’a pas été accepté, et #10410 nécessite sa propre décision explicite selon le processus que cet enregistrement établit.
#10179 s’est heurtée à la même règle, mais son transport n’avait aucun appelant côté réception. Le retrait, ou une décision explicite de responsabilité, était la meilleure réponse dans ce cas, plutôt qu’une exception.
Ces trois cas diffèrent par leur nature, et pas seulement par leur taille. L’extraction était proportionnée pour l’un, aurait produit le mauvais périmètre pour un autre, et était hors de propos pour le troisième. Une seule instruction inconditionnelle ne peut pas les distinguer, et le silence sur les exceptions a fait que chaque contributeur doit deviner.
Les alternatives sont de maintenir l’instruction inconditionnelle et d’exiger l’extraction dans tous les cas, d’assouplir celle-ci de manière générale, ou de conserver l’extraction comme comportement par défaut et de définir la manière dont une exception bornée est accordée.
Décision
L’extraction reste la valeur par défaut. L’équipe principale peut approuver une exception limitée lorsqu’une extraction immédiate nécessiterait un refactoring disproportionné, ou établirait une frontière de crate que la feuille de route ne prévoit pas.
Ce que doit indiquer une exception
Une exception approuvée désigne les quatre éléments suivants :
- Périmètre autorisé. Les chemins précis que couvre l’exception. Pas un sous-système en général.
- Destination prévue. Le crate vers lequel le code doit migrer, de sorte que l’exception décrive un report plutôt qu’un retour en arrière.
- Autorité d’approbation. Qui l’a accordée.
- Condition d’expiration ou de réexamen. Ce qui y met fin, ou le moment où elle est réexaminée. L’enregistrement doit indiquer lequel des deux c’est, car ils ne se comportent pas de la même manière (voir ci-dessous).
Ce que signifie l’expiration
Une exception expire ou fait l’objet d’une revue, et l’enregistrement indique lequel.
Une expiration met fin à l’autorisation. Tout ajout supplémentaire au périmètre couvert nécessite alors un renouvellement par la Core Team. Elle n’exige pas de retirer le code déjà intégré au titre de l’exception : l’autorisation cesse de s’appliquer vers l’avant, et non rétroactivement. Défaire ce qui a déjà été accepté relève du travail d’extraction, et non d’une conséquence du passage d’une date.
Une condition de révision ne met rien fin par elle-même. Elle oblige l’équipe centrale à reconsidérer la situation, et l’issue de cette reconsidération est soit un renouvellement, soit une expiration, soit une extraction.
Comment il est accordé
L’enregistrement est créé avant la fusion de la fonctionnalité, et séparément de celle-ci. Une pull request de fonctionnalité ne peut pas s’accorder elle-même une exception, car le contrat qu’elle voudrait faire lever est précisément celui qui la contraint.
Une exception nécessite un cas d’utilisation concret et pris en charge. Le code sans appelant réel ne remplit pas cette condition ; dans ce cas, la mise hors service ou une décision explicite de responsabilité est la bonne réponse.
Comment un token d’exception individuel est enregistré
Une exception est accordée de la même manière que toute autre décision du dépôt : par une revue d’une pull request qui ajoute l’entrée au tableau des exceptions actives dans le fichier AGENTS.md du crate responsable, approuvée par la Core Team. Aucun mécanisme distinct, et aucune approbation en dehors des règles de revue habituelles.
L’entrée est l’enregistrement. Une décision qui n’existe que dans un fil de revue n’a pas été prise, car rien de ce qui lira ensuite le contrat ne la retrouverait.
Ce qu’est une exception n’est pas
Une exception autorise la poursuite du travail sur un sous-système que la crate concernée contient déjà. Elle n’autorise jamais l’introduction d’un nouveau sous-système dans cette crate, et ne se généralise pas d’un sous-système à un autre. Accorder une exception pour cron ne dit rien au sujet du daemon.
Adoption
Cet enregistrement transforme une interdiction inconditionnelle en autorisation soumise à des conditions énoncées, ce qui en fait une modification de gouvernance et de processus de contribution plutôt qu’une simple modification de documentation. FND-003 §8 régit cette voie.
Adopter cela relève donc de la décision de la Core Team, à prendre et à consigner explicitement, y compris la voie applicable au titre de FND-003 §8. Cette pull request constitue la proposition concrète, et non l’adoption. Tant que cette décision n’est pas consignée, l’instruction inconditionnelle reste en vigueur et aucune exception n’a été accordée.
Conséquences
Les contributeurs disposent d’un chemin de décision qui peut être résolu avant l’implémentation plutôt qu’après. Le cas du cron a coûté deux implémentations complètes pour répondre à une question qu’un bref enregistrement aurait permis de trancher en amont.
Chaque exception est visible et datée. Le tableau des exceptions actives dans le contrat de la caisse de rétention rend la dette accumulée lisible, et chaque entrée nomme ce qui y met fin, de sorte qu’une exception devenue discrètement permanente apparaît au grand jour au lieu d’être enfouie.
Le Core Team se charge d’apprécier la proportionnalité au cas par cas. C’est un choix délibéré : les trois cas ci-dessus montrent que ce jugement ne peut pas se réduire à un seuil de taille, car « refactorisation disproportionnée » et « mauvaise frontière » sont des motifs différents qui partagent simplement un même symptôme.
L’instruction relative à la caisse de rétention conserve toute sa force. Cela ne l’affaiblit pas : cela fournit la procédure que l’instruction supposait sans jamais la définir.
Acceptation
ADR-016 reste proposé jusqu’à ce que :
crates/zeroclaw-runtime/AGENTS.mdénonce la règle d’exception et contient un tableau des exceptions actives ;- Au moins une exception a été accordée ou refusée par ce processus, ce qui démontre qu’il est utilisable et non pas seulement consigné par écrit.