ADR-0180 : Socle frontend commun des agents sectoriels, paramétré par un manifeste produit
- Statut : proposé le 2026-11-04, en attente de validation de l'utilisateur (voir « Points à valider »)
- Date : 2026-11-04
- Portée :
navin-enterprise-banking-backoffice/frontend(le socle),navin-enterprise-banking-backoffice/product.manifest.json(premier consommateur, UC-00), contratcontracts/v1/agent-product-manifest.schema.json(figé, non modifié ici), testscontracts/tests/test_product_manifests.py - Documents liés : ADR-0175 (Navin Enterprise RAG Agent), ADR-0176 (Navin Enterprise Banking Back-Office Automation Agent),
ADR-0177 (Compliance & Audit Agent),
.navin/continuity/DECISIONS.md(entrées du 2026-11-04 « Un seul frontend pour les 16 agents sectoriels » et « Agents UC-01 à UC-05 développés en parallèle ») - Numérotation : les numéros 0178 et 0179 ont été pris le même jour par d'autres chantiers ; cette décision porte le numéro 0180, premier numéro libre au moment de l'écriture.
Contexte
L'utilisateur a tranché le 2026-11-04 : les seize agents sectoriels de agent_uses_cases/ (UC-00 à UC-15) utilisent le
même frontend ; seuls changent le nom, le logo, les couleurs, le préfixe d'API, les menus et les entités. Le frontend
d'UC-00 (navin-enterprise-banking-backoffice/frontend, Next.js 16.4.4, MUI 9.5.0, framer-motion, BFF OIDC Keycloak
avec jetons côté serveur, fr/en/ar RTL) est le plus complet : 23 menus, écrans de liste à pagination par curseur,
dialogues d'écriture avec Idempotency-Key et If-Match, confirmation d'action sensible (annexe E), copilote SSE.
Avant cette décision, le nom, les couleurs par défaut, le préfixe /api/v1/banking-backoffice, la route de
readiness, les 23 menus (src/components/shell/navigation.ts) et les sept invites du copilote étaient écrits en dur
dans le code. Cinq produits d'assurance (UC-01 à UC-05) se construisent en parallèle et livrent chacun un
product.manifest.json : il faut un contrat figé et un socle qui le lit, sans qu'aucun produit ne copie un écran.
Décision
- Un seul shell. Le frontend de
navin-enterprise-banking-backoffice/frontendest le socle de tous les agents sectoriels. Aucun autre frontend n'est créé par produit ; un produit livre un manifeste, un logo, éventuellement des fichiers de surcharge i18n, et son backend. - Contrat du manifeste.
contracts/v1/agent-product-manifest.schema.json(version 1.0) est le contrat entre le socle et les produits. Le socle le lit au démarrage parNAVIN_PRODUCT_MANIFEST(par défaut../product.manifest.json, relatif au dossier du frontend), le valide par une garde de type écrite à la main (src/lib/product-manifest.ts, aucun paquet npm ajouté ; la validation JSON Schema complète est le test de contratcontracts/tests/test_product_manifests.py), le gèle, et refuse de démarrer sur un fichier absent ou invalide (jamais de valeurs par défaut silencieuses). - Sous-ensemble public. Seuls
product_id,use_case,sector,name,short_name,description,branding,locales,roles,navigation,entitiesetcopilotatteignent les composants client (publicManifestOf). Les ports, le préfixe d'API, le chemin OpenAPI, les fichiers de surcharge et l'identité du gabarit restent côté serveur. - BFF piloté par le manifeste. Le proxy
/api/backoffice/*, la lecture du branding d'instance, la route de readiness et le flux SSE du copilote utilisentapi.prefixetapi.healthdu manifeste. Les variables d'environnement existantes continuent de gagner (NAVIN_BACKOFFICE_API_PREFIX,NAVIN_BACKOFFICE_READINESS_PATH) ; le manifeste fournit la valeur par défaut. - Branding d'instance au-dessus des défauts produit. Le nom, le logo (
branding.logo_path, servi depuispublic/products/<product_id>/) et les couleurs du manifeste sont les défauts, affichés avec la mention « identité par défaut ». La réponseGET <prefix>/settings/brandingdu backend, quand elle existe, les remplace ; l'éditeur d'administration du branding est inchangé. - Navigation dérivée. Les menus viennent de
manifest.navigation: identifiant, href, libellé fr/en/ar, icône nommée parmi un ensemble borné d'icônes@mui/icons-materialdéjà embarquées (NAV_ICONS), rôles d'affichage, écran générique. Une icône inconnue retombe sur une icône générique et le test du manifeste UC-00 échoue si une icône n'est pas mappée. Le filtrage par rôle est inchangé (affichage seulement ; le backend décide sur chaque route). - Écrans génériques. Les pages dédiées existantes restent prioritaires dans Next ; une route générique
src/app/(app)/[...screen]/page.tsxrend toute entrée du manifeste dont le href n'a pas de page dédiée, par l'écran qu'elle nomme :dashboard,work_queue,entity_list,approvals,copilot,documents,eod,controls,reports,connectors,audit,governance,admin,settings.entity_listconstruit la table depuisentity.columns(montants par devise depuis des chaînes décimales, jamais sommés ; statuts en mots ; références en monospace), le tiroir de détail depuisdetail_fields(avec l'ETag), et les actions depuisentity.actions:WriteDialogavecIdempotency-Key,If-Matchquandif_match,ConfirmSensitiveActionquandrisk_levelvautside_effect, champs depuisaction.fieldsavec la validation décimale et devise desrc/lib/money.ts. Rien de bancaire n'est écrit dans ce chemin générique ; un second manifeste (sinistres assurance) le prouve en test. - i18n. Les messages du shell restent dans
src/i18n/messages/*; un produit ajoute son vocabulaire pari18n_overrides(un fichier JSON par langue, lu côté serveur, clés du shell uniquement, aucun U+2013 ni U+2014). Les libellés des menus, entités, colonnes, actions et invites du copilote viennent du manifeste, en fr, en et ar. - Emplacement physique. Le socle reste dans
navin-enterprise-banking-backoffice/frontendpendant la session UC-00. Son déplacement vers un dossier neutre est ungit mvaprès la clôture d'UC-00, jamais pendant, afin que les neuf chantiers en cours ne s'écrasent pas. Un dossiernavin-enterprise-common/frontend-shellapparu le même jour n'est pas le socle décrit ici ; sa consolidation (consommer ce socle ou disparaître) se tranche à la clôture d'UC-00 avec cette ADR.
Ce qu'un produit peut personnaliser
name, short_name, description, branding (logo, couleurs), api.prefix, api.health, api.openapi,
ports, locales.default, roles, navigation (menus, ordre, icônes du jeu borné, rôles, écran), entities
(ressources, colonnes, champs de détail, actions et leurs champs), copilot.quick_prompts, i18n_overrides,
template.service_id.
Ce qu'un produit ne peut pas personnaliser
La pile (Next.js, MUI, framer-motion), le BFF et sa politique de cookies, la session, le CSRF, l'en-tête
Idempotency-Key sur chaque écriture, le dialogue de confirmation d'action sensible, la pagination par curseur, la
règle « jamais de somme entre devises », les composants d'écran, les langues supportées (fr, en, ar) et le sens
d'écriture, le jeu d'icônes, les ports réservés de la plateforme, template.published (toujours false tant que
l'utilisateur n'a pas publié).
Alternatives écartées
- Un frontend par agent (copie du dossier UC-00) : seize copies à maintenir, refusé par l'utilisateur.
- Un frontend générique sans écrans métier : refusé (interface creuse).
- Extraire d'abord un paquet npm partagé pendant UC-00 : impossible à stabiliser pendant que neuf chantiers modifient le socle ; reporté après la clôture d'UC-00 (point 9).
- Valider le manifeste côté frontend avec une bibliothèque JSON Schema : paquet supplémentaire refusé ; la garde de type suffit au shell et le test de contrat fait la validation complète.
Conséquences
- UC-00 rend exactement les 23 menus d'avant (mêmes identifiants, hrefs, icônes, rôles, ordre) : test
tests/product-manifest.test.tsx. - Les tests de navigation et de parité i18n lisent le manifeste au lieu de
NAV_ITEMS. - Les cinq produits d'assurance livrent un manifeste et un logo ; aucun écran n'est cloné.
- Le manifeste UC-00 compte 23 menus, 19 entités et 36 actions d'écriture, validé par le test de contrat.
- Une entité dont la ressource dépend d'un parent (
/reconciliations/{id}/breaks,/connectors/{id}/sync-runs) n'entre pas dans le manifeste 1.0 (le motifresourcerefuse{id}) : ces écrans restent des pages dédiées ; une évolution du contrat pourra les décrire. - L'action différée « Relancer l'extraction » des documents reste différée :
docs/api/openapi.backend.jsonn'expose pasPOST /documents/{id}/reextractau moment de l'écriture.
Sécurité
- Aucun port, aucune URL de backend, aucun chemin de fichier n'atteint le navigateur : le sous-ensemble public est
calculé côté serveur et testé (
JSON.stringify(public)ne contient ni8471ni/api/v1/). - Le logo du manifeste passe par
safeLogoUrl(chemin même origine ou https seulement). - Les chemins d'actions du manifeste sont relatifs au préfixe et refusés s'ils contiennent
..; l'identifiant de ligne est encodé ; le BFF garde ses contrôles de segments. - Les fichiers
i18n_overridesne peuvent pas sortir du dossier du produit.
Points à valider
- Confirmer que le socle reste dans
navin-enterprise-banking-backoffice/frontendjusqu'à la clôture d'UC-00 et le nom du dossier neutre cible dugit mv. - Décider du sort de
navin-enterprise-common/frontend-shell(consommer ce socle ou le retirer). - Valider la liste des écrans génériques et l'ensemble borné d'icônes.