Aller au contenu principal

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), contrat contracts/v1/agent-product-manifest.schema.json (figé, non modifié ici), tests contracts/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​

  1. Un seul shell. Le frontend de navin-enterprise-banking-backoffice/frontend est 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.
  2. 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 par NAVIN_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 contrat contracts/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).
  3. Sous-ensemble public. Seuls product_id, use_case, sector, name, short_name, description, branding, locales, roles, navigation, entities et copilot atteignent 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.
  4. 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 utilisent api.prefix et api.health du 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.
  5. Branding d'instance au-dessus des défauts produit. Le nom, le logo (branding.logo_path, servi depuis public/products/<product_id>/) et les couleurs du manifeste sont les défauts, affichés avec la mention « identité par défaut ». La réponse GET <prefix>/settings/branding du backend, quand elle existe, les remplace ; l'éditeur d'administration du branding est inchangé.
  6. 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-material dé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).
  7. Écrans génériques. Les pages dédiées existantes restent prioritaires dans Next ; une route générique src/app/(app)/[...screen]/page.tsx rend 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_list construit la table depuis entity.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 depuis detail_fields (avec l'ETag), et les actions depuis entity.actions : WriteDialog avec Idempotency-Key, If-Match quand if_match, ConfirmSensitiveAction quand risk_level vaut side_effect, champs depuis action.fields avec la validation décimale et devise de src/lib/money.ts. Rien de bancaire n'est écrit dans ce chemin générique ; un second manifeste (sinistres assurance) le prouve en test.
  8. i18n. Les messages du shell restent dans src/i18n/messages/* ; un produit ajoute son vocabulaire par i18n_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.
  9. Emplacement physique. Le socle reste dans navin-enterprise-banking-backoffice/frontend pendant la session UC-00. Son déplacement vers un dossier neutre est un git mv après la clôture d'UC-00, jamais pendant, afin que les neuf chantiers en cours ne s'écrasent pas. Un dossier navin-enterprise-common/frontend-shell apparu 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 motif resource refuse {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.json n'expose pas POST /documents/{id}/reextract au 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 ni 8471 ni /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_overrides ne peuvent pas sortir du dossier du produit.

Points à valider​

  1. Confirmer que le socle reste dans navin-enterprise-banking-backoffice/frontend jusqu'à la clôture d'UC-00 et le nom du dossier neutre cible du git mv.
  2. Décider du sort de navin-enterprise-common/frontend-shell (consommer ce socle ou le retirer).
  3. Valider la liste des écrans génériques et l'ensemble borné d'icônes.