Architecture
Atelier des ouvrages — architecture, configuration et exploitation
Sur cette page
Module d'administration /{locale}/admin/book-workshop (story E14-S07). Espace privé de préparation d'un ouvrage
arabe et de sa traduction française : téléverser, structurer, traduire, corriger l'arabe, réviser, décider.
État. Code intégré, désactivé par défaut. La migration est une candidate non appliquée (
supabase/candidates/book-workshop/). Aucun ouvrage n'est chargé, aucun fournisseur n'a été appelé, rien n'est publié. L'activation, le chargement d'un ouvrage et l'import public sont trois décisions séparées.
1. Principes#
- L'arabe documentaire est la référence ; le texte brut n'est jamais réécrit. Une correction de l'arabe est journalisée (motif obligatoire) et rend périmée la traduction liée.
- Une traduction est une donnée dérivée et versionnée ; les versions sont en ajout seul.
- Une relecture par IA n'est jamais une validation humaine. Une validation humaine est une décision : explicite, attribuée à l'administrateur de la session, journalisée, et elle crée une nouvelle version.
- Préparer un ouvrage n'est pas le publier.
2. Architecture#
| Couche | Contenu |
|---|---|
src/app/api/admin/book-workshop/** | routes minces : elles délèguent à bookWorkshopApi() |
src/app/[locale]/(site)/admin/book-workshop/** | pages (client) ; garde de session et de drapeau dans layout.tsx |
src/features/book-workshop/** | interface FR/AR (i18n existante), client HTTP authentifié |
src/domain/book-workshop/** | logique pure : segmentation, contrôles, filiation des unités, contrat du chargement d'état, profils supportés, erreurs |
src/server/book-workshop/** | services : configuration, cas d'usage HTTP (api.ts), boucle d'exécution des lots (worker.ts), composition |
src/infrastructure/book-workshop/** | adaptateurs : SQL (postgres/), connexion propre (postgres/pool.ts), fournisseurs de traduction |
Les services serveur ne contiennent aucun SQL. Le navigateur n'appelle que /api/admin/book-workshop/* avec le jeton
de session ; il n'accède ni à la base ni au fournisseur.
Chaque requête suit l'ordre : drapeau → administrateur → contrat d'entrée → magasin. Une requête refusée n'atteint ni le stockage de l'atelier ni un fournisseur.
3. Variables d'environnement (serveur uniquement)#
| Variable | Rôle | Défaut |
|---|---|---|
BOOK_WORKSHOP_ENABLED | active le module ; seule la valeur littérale true active | désactivé |
BOOK_WORKSHOP_DATABASE_URL | connexion propre à l'atelier ; aucun repli sur une autre variable | — |
BOOK_WORKSHOP_DB_CA_CERT | autorité de certification (PEM) quand le certificat du serveur n'est pas signé par une autorité publique | autorités du système |
BOOK_WORKSHOP_PUBLIC_IMPORT_ENABLED | autorise « Importer dans ISNAD » ; indépendant de l'activation | désactivé |
OPENAI_API_KEY, ISNAD_TRANSLATION_MODEL_NORMAL, ISNAD_TRANSLATION_MODEL_ESCALATED, BOOK_WORKSHOP_MODELS | moteur de traduction E14 existant et modèles proposés | — |
BOOK_WORKSHOP_PRICING_JSON | tarif par modèle ; sans lui le coût est « inconnu », jamais inventé | inconnu |
BOOK_WORKSHOP_PROVIDER=simulation | fournisseur simulé pour les tests ; interdit en production sauf BOOK_WORKSHOP_ALLOW_SIMULATION=true | — |
BOOK_WORKSHOP_PROVIDER_TIMEOUT_MS | délai d'un appel fournisseur | 90 000 |
BOOK_WORKSHOP_MARKDOWN_MAX_BYTES, BOOK_WORKSHOP_LAYOUT_MAX_BYTES, BOOK_WORKSHOP_PDF_MAX_BYTES | limites de téléversement | 4 Mo / 3 Mo / 3 Mo |
BOOK_WORKSHOP_LEASE_SECONDS, BOOK_WORKSHOP_WORKER_TIME_BUDGET_MS | bail d'un lot ; durée d'un passage | 180 ; 240 000 |
Aucune valeur de secret n'est renvoyée au navigateur ni écrite dans un journal.
Connexion. La vérification du certificat TLS est toujours active pour un hôte distant ; elle n'est jamais
désactivée pour faire fonctionner une connexion. Le rôle de cette connexion n'est pas présumé limité : son choix et ses
privilèges minimaux sont décrits dans supabase/candidates/book-workshop/README.md et décidés au gate d'activation.
4. Drapeau désactivé#
- Les routes de l'atelier répondent
503 DISABLEDà un administrateur, sans ouvrir de connexion. - Les pages de l'atelier affichent « module désactivé » ; le lien de l'administration est masqué.
- Le cockpit
/admin/translationsest exactement le cockpit des notices. - L'absence du schéma n'empêche rien de se charger.
5. Cockpit des traductions#
Quand l'atelier est activé, ses ouvrages apparaissent dans Administration → Traductions à côté des notices. Le
magasin de l'atelier est secondaire : s'il échoue (schéma absent, base injoignable), les notices restent servies,
la réponse porte degraded et l'interface dit que les ouvrages privés sont indisponibles et que les comptages ne les
incluent pas. Une panne n'est jamais transformée en liste vide ni en « introuvable », et une décision portant sur un
ouvrage n'est jamais redirigée vers les notices.
6. Décisions de revue#
Un seul service de décision pour l'écran de révision de l'atelier et pour le cockpit (executeReviewCommand →
BookWorkshopCockpitPort.recordReview) : acteur issu de la session, contrôle de la version retenue, décision
journalisée (review_decisions), nouvelle version, transaction unique, clé d'idempotence (une demande répétée est
rejouée, jamais enregistrée deux fois).
- Valider : possible seulement sur une traduction retenue, à jour, déclarée complète, non simulée.
- Corriger : qualité de la version remplacée et motif obligatoires.
- Une unité sans traduction peut recevoir une première rédaction humaine ; elle est attribuée et non validée.
7. Profil documentaire#
La liste des profils pris en charge est fermée (SupportedBookProfiles.ts). Le profil d'un ouvrage est déclaré
explicitement par un administrateur, à la première traduction ou au premier chargement ; il n'y a pas de profil par
défaut, et un profil non pris en charge est refusé.
8. Restructuration#
Une nouvelle segmentation (autre mise en page OCR, autre version du segmenteur) ne reporte rien sur la seule foi d'un
identifiant. La filiation est décidée d'après l'empreinte du texte brut (UnitLineage.ts) :
| État | Condition | Reporté |
|---|---|---|
UNCHANGED | même identifiant, même type, même texte brut, unique des deux côtés | identité de revue, corrections de l'arabe, traduction retenue, validation humaine |
SOURCE_CHANGED | même identifiant et type, arabe différent | identité et traduction (épinglée sur l'ancien arabe : périmée) ; pas la validation humaine |
AMBIGUOUS | correspondance non établie (texte partagé, identifiant réattribué, autre type) | rien |
NEW | aucune unité antérieure | rien |
Une unité divisée ou fusionnée ne reçoit jamais de validation humaine. Les segmentations précédentes restent intactes et consultables (versions, corrections, décisions) dans l'historique de l'unité.
9. Charger un état de traduction produit hors site#
Tableau de bord de l'ouvrage → « Charger un état de traduction » : corpus isnad-book-corpus-v1 (JSONL) et, s'il
existe, journal des corrections OCR. Les fichiers sont compressés par le navigateur (4 Mo au plus compressés, 40 Mo au
plus décompressés).
Refusé avant toute écriture : format inconnu, identifiants en double, identifiants absents de la segmentation
active, statut de relecture inconnu, validation ou origine humaine déclarée dans le fichier. Une complétude absente
est enregistrée unknown : la traduction ne peut pas être validée telle quelle.
Le chargement est une transaction unique, sérialisée par ouvrage : en cas d'erreur rien n'est conservé. « Vérifier » exécute le même traitement et l'annule. Un chargement dont une partie a été écartée (correction inapplicable, traduction épinglée sur un autre arabe) est déclaré incomplet. Recharger le même état ne crée ni version ni décision. Une version humaine retenue n'est jamais remplacée.
10. Exécution des lots de traduction#
POST /api/admin/book-workshop/worker par un administrateur, un passage à la fois. Aucun planificateur, aucun secret
partagé, aucun GET : rien n'est traduit sans demande explicite. L'état durable (travaux, lots, tentatives, budget)
vit dans le schéma privé.
11. Import vers ISNAD (désactivé par défaut)#
« Vérifier l'import » et « Importer dans ISNAD » écrivent dans le modèle commentary_*, dont les sources et éditions
sont lisibles publiquement : importer rend visibles le titre et les métadonnées de l'ouvrage. L'action est donc
refusée par le serveur tant que BOOK_WORKSHOP_PUBLIC_IMPORT_ENABLED n'est pas true, et l'interface ne l'affiche
pas. PUBLISHED n'est jamais écrit par l'atelier.
Constats de la certification T03 (base isolée) — à résoudre avant toute autorisation de l'import public :
- l'import écrit dans
public.commentary_*par la connexion de l'atelier : le rôle applicatif limité (book_workshop_app) n'y a aucun droit, l'import échoue donc (503 STORE_UNAVAILABLE) ; lui accorder ces droits élargirait la posture privée de cette connexion ; - l'import n'est pas atomique : une erreur en cours laisse dans les tables publiques la source, l'édition et une partie des passages et traductions (une nouvelle exécution complète l'import, mais l'état intermédiaire est public) ;
- les métadonnées publiées de la source et de l'édition contiennent des identifiants internes de l'atelier (ouvrage, segmentation, empreinte du Markdown) et une clé dérivée du nom du fichier téléversé ;
anonlit la source (titres, auteur, statuts) et l'édition ; il ne lit ni les passages ni les traductions.
12. Tests#
npm run test:book-workshop:unit # domaine, fournisseur, configuration
npm run test:book-workshop # intégration PGlite : téléversement → décisions → chargement d'état
npm run test:book-workshop-t02 # régressions : drapeau, autorisations, panne, import public, filiation, contrats
npm run test:book-workshop-t03 # certification : filiation (arabe changé, texte déplacé, fusion), notes, rechargement des corrections
npm run test:book-workshop-pg # PostgreSQL réel : rôles et droits effectifs, RLS, ajout seul, concurrence, atomicité, retour arrière (CI)
La fixture est synthétique (scripts/validation/fixtures/book-workshop/build-synthetic-fixture.mjs) ; le fournisseur
est simulé. Aucun secret ni appel réseau n'est nécessaire.
13. Avant toute activation#
- Certification T03, puis promotion et application de la migration candidate (acte autorisé séparément).
- Choix du rôle de connexion et de l'autorité de certification. Ni le rôle
postgres(il contourne la RLS) niservice_rolene conviennent ;book_workshop_appn'existe pas encore en production. Lu en production le 2026-10-03 : la chaîne de certificats du pooler Supabase n'est pas vérifiable avec les autorités du système, doncBOOK_WORKSHOP_DB_CA_CERTsera nécessaire. - Approbation du schéma
book_workshopau regard de la taxonomie (ADR-0002). - Activation du drapeau ; le chargement d'un ouvrage et l'import public restent des décisions distinctes.