ADR-0037 — Capacités de déploiement déclarées par template, cible choisie sous contrainte¶
- Statut : Proposée — contient une contradiction assumée avec ADR-0009, à arbitrer
- Date : 2026-09-09
- Contexte amont : ADR-0009 (taxonomie des templates, cible
socle|poddérivée), ADR-0012 (Helm comme packaging K8s), ADR-0016 (cercles), ADR-0017 (couloir), R-236 (Forge propriétaire du vocabulaire structurel) - Ne décide rien pour l'exécution immédiate. Aucun code n'est modifié par cet ADR.
0. Ce que l'audit a trouvé avant tout arbitrage¶
Cet ADR n'ouvre pas un sujet neuf. ADR-0009 l'a déjà tranché en 2026-06-10, et l'audit montre trois écarts entre ce qui est écrit, ce qui est construit, et ce qui manque.
0.1 La décision existe déjà — et elle dit l'inverse de « choix explicite »¶
ADR-0009 §« Cible de déploiement par cercle » :
C'est la 5ᵉ propriété du profil, dérivée des 4 autres et de la sensibilité de l'app — pas une décision indépendante.
CODEX_DEPLOY_TARGET∈ {socle,pod} — résolu au manifeste, jamais saisi à la main.
La table de dérivation y est complète : paiement/fiscal → pod toujours ;
scenario ∈ {on-premise, cloud-client} → pod ; cercle client ou prod SaaS → pod ;
tenancy = dedicated → pod ; tout le reste → socle.
0.2 La dérivation n'a jamais été implémentée¶
Vérifié sur dynors-internal/applications/sirrat/backend/src/main :
grep '"pod"' | '"socle"' → 0 occurrence en code Java de production
Les seules mentions de socle/pod sont dans la Javadoc de SirratCodexConstants et un
faux positif (SirratHumanSessionService, « socle de lecture », sans rapport). L'en-tête
d'ADR-0009 annonce « Sprint d'implémentation : amorcé (CODEX_DEPLOY_TARGET déjà constant) » :
la constante existe, la règle non.
0.3 La clé porte aujourd'hui un tout autre concept¶
ValueResolutionService (ligne ~199) :
poser(valeurs, SirratCodexConstants.CODEX_DEPLOY_TARGET, stack.getDeploymentTarget());
Or stack.deploymentTarget vient de la migration 028, qui le définit ainsi :
deployment_target VARCHAR(120)— « Hôte ou groupe d'inventaire cible (ex.vps-prod-01). Null = cible par défaut du cercle. »
Donc CODEX_DEPLOY_TARGET est documenté comme socle|pod et alimenté avec un nom de
machine. Deux concepts, une clé :
ADR-0009 CODEX_DEPLOY_TARGET = socle | pod nature du placement (dérivée)
migr. 028 deployment_target = vps-prod-01 machine d'exécution (déclarée)
Un template qui écrirait {{ CODEX_DEPLOY_TARGET }} en attendant pod recevrait
vps-prod-01. Rien ne l'en avertit. À noter par ailleurs : setDeploymentTarget n'est appelé
nulle part en code de production — seulement dans deux tests. La colonne est donc, en
pratique, vide.
0.4 Le vrai manque : la dérivation ne vérifie aucune faisabilité¶
C'est le point que la proposition apporte réellement, et qu'aucun ADR ne couvre.
La table d'ADR-0009 dérive une cible depuis la politique — sensibilité, tenancy, scénario, cercle. Elle ne demande jamais si la forme technique de l'app peut être déployée ainsi. Conséquence mécanique :
app Flutter mobile publiée en store
+ tenancy = dedicated
→ la table dérive « pod »
« Pod » n'a aucun sens pour un binaire distribué en store. La même incohérence attend un Electron POS installé sur un poste, ou un front statique servi par un CDN. La règle produirait une cible que rien ne peut exécuter, et le refus n'arriverait qu'au moment du déploiement — au mieux.
Deux questions distinctes ont été confondues en une :
ce qui est POSSIBLE faisabilité propriété du TEMPLATE (forme technique)
ce qui est OBLIGATOIRE politique propriété du PROFIL (ADR-0009)
ADR-0009 répond à la seconde et suppose la première résolue.
1. Le conflit à arbitrer¶
| ADR-0009 (Acceptée) | Proposition 2026-09-09 | |
|---|---|---|
| Choix de la cible | dérivé, « jamais saisi à la main » | explicite, porté par SIRRAT |
| Contrainte | table de politique | capacités déclarées par le template |
| Risque évité | qu'on place FISCAL sur un socle mutualisé | qu'on demande un pod à une app mobile |
Les deux ne s'annulent pas — mais ADR-0009 dit « jamais saisi », et la proposition dit « explicitement choisi ». Cette phrase-là est en contradiction frontale, et je ne la tranche pas ici.
Trois lectures possibles, à trancher par le lead architecte :
A — la dérivation reste souveraine, les capacités ne sont qu'un garde-fou. Le template déclare ce qu'il supporte ; si la dérivation produit une cible non supportée, SIRRAT refuse au lieu de choisir autre chose. Aucune saisie n'apparaît. ADR-0009 est préservée mot pour mot ; on lui ajoute une garde de faisabilité. Coût : un profil légitime peut devenir indéployable sans recours, jusqu'à changement de profil.
B — capacités d'abord, politique ensuite, choix explicite quand il reste du jeu.
La cible retenue est l'intersection capacités(template) ∩ politique(profil). Si
l'intersection contient une seule valeur, elle s'impose (pas de saisie). Si elle en contient
plusieurs, SIRRAT exige un choix explicite — aucun défaut. C'est la proposition, et elle
amende ADR-0009 sur « jamais saisi à la main ».
Coût : amender une ADR Acceptée, et accepter qu'une même app puisse être placée différemment
selon le cercle sans que la politique l'impose.
C — statu quo documenté. On corrige seulement la collision de clé (§0.3) et on laisse la dérivation non implémentée jusqu'à ce qu'un besoin réel se présente. Coût : l'incohérence Flutter/pod reste latente ; elle se découvrira en déploiement.
Recommandation de rédaction, non de décision : A est la plus économe et ne rouvre rien ; B est la plus juste si l'on prévoit d'ajouter des backends (ECS, Nomad, OpenShift), parce qu'à ce moment-là plusieurs cibles deviennent simultanément possibles ET conformes, et la table de politique d'ADR-0009 ne saura pas départager.
2. Modèle proposé (sous réserve de l'arbitrage §1)¶
2.1 Quatre acteurs, quatre responsabilités¶
Forge catalogue des CAPACITÉS ce que cette forme technique sait faire
SIRRAT choix + gouvernance + manifeste ce qui est retenu, et pourquoi
dynors-ops exécution comment on le fait réellement
backend runtime Compose · Kubernetes · VM · store · CDN
C'est la même répartition que R-236 pour le vocabulaire : Forge possède la structure, SIRRAT remplit les emplacements. Une capacité de déploiement est une structure.
2.2 Capacités, pas « K8s-ready »¶
« K8s-ready » est un attribut binaire attaché à une technologie, et c'est précisément l'inférence à éviter :
Spring Boot ≠ Kubernetes
Spring Boot → compatible Kubernetes
→ si le template l'autorise
→ et si une cible pod est déclarée dans le cercle
→ alors K8s est possible
Vocabulaire proposé — un template déclare un ensemble, jamais une valeur :
CONTAINER · KUBERNETES · COMPOSE · STATIC_WEB · MOBILE_DISTRIBUTION · DESKTOP_PACKAGE · BATCH
Esquisse par forme technique (à valider avec le catalogue réel, non audité ici) :
dynors-spring-api: [COMPOSE, KUBERNETES]
dynors-worker: [COMPOSE, KUBERNETES, BATCH]
dynors-angular-runtime-web: [COMPOSE, STATIC_WEB, KUBERNETES]
dynors-flutter-public-mobile: [MOBILE_DISTRIBUTION]
dynors-electron-offline-pos: [DESKTOP_PACKAGE]
L'intérêt du modèle est là : ajouter ECS, Nomad, OpenShift ou un serverless demain n'oblige à toucher ni l'application, ni son profil — seulement le catalogue de capacités et le backend d'exécution.
2.3 Articulation avec l'existant¶
- ADR-0012 matérialise
pod → Helm/K8s. Dans ce modèle,podcesse d'être une valeur et devient la conjonction « capacitéKUBERNETESdisponible » + « cible pod déclarée dans le cercle ». ADR-0012 n'est pas contredite : elle décrit le backend, pas le choix. cibles.yml(dynors-ops) tient déjà le registre de ce qui est réellement déployable par(cercle, couloir). La vérification « la cible existe-t-elle physiquement ? » y a déjà son lieu — inutile d'en créer un second. La chaîne complète devient :
capacités (Forge) → ce que la forme technique sait faire
politique (ADR-0009) → ce que le profil impose
cible déclarée (Ops) → ce qui existe réellement ← déjà construit, P0.4
─────────────────────────────
refus si l'intersection est vide
3. Prérequis, quel que soit l'arbitrage¶
Ces deux points sont des défauts constatés, pas des options :
- Séparer les deux sens de
CODEX_DEPLOY_TARGET(§0.3). Une clé ne peut pas signifiersocle|poddans un ADR etvps-prod-01en base. Le nommage naturel :CODEX_DEPLOY_TARGET= nature du placement,CODEX_DEPLOY_HOST= machine — mais renommer une clé Codex touche les templates Forge, donc cela relève de R-236 et d'un slice dédié. - Décider du sort de
stack.deploymentTarget. Jamais écrit en production, jamais lu ailleurs que dans ceposer(...). Soit il est alimenté et devient la machine cible, soit il est retiré. Le laisser vide et branché produit aujourd'hui unCODEX_DEPLOY_TARGETsilencieusement absent.
4. Ce que cet ADR ne fait pas¶
- Aucune modification de code, de migration, de catalogue Forge.
- Aucun amendement d'ADR-0009 tant que §1 n'est pas arbitré.
- Aucune implémentation de la dérivation
socle|pod— elle reste non construite, et c'est désormais écrit plutôt que supposé acquis.