R4 avait délibérément fermé le mobile au Technicien. Le référent demande l'ouverture à tous les rôles avec les mêmes droits qu'au web — nouvelle release R6 (R4 est close, R4.1/4.2/4.3 déjà pris par le socle mobile technicien d'origine, pas de réouverture ni de collision de numérotation). Maquette (docs/02-design/maquettes/maquette-mobile-tous-roles.html, 7 écrans) validée par le référent : D1 barre d'onglets adaptative par rôle (Technicien/Technicien limité inchangés + onglet Menu ajouté) ; D2 le Menu reprend à l'identique les 4 groupes du web, même matrice de permissions ; D3 groupe sans lien visible masqué en entier ; D4 le mobile porte les actions courantes par famille, pas les flux de gestion les plus denses ; D5 aucune logique de permission propre au mobile. R6.1 — socle : usePermissions() mobile calqué sur le web ; barre d'onglets adaptative (ongletsVisibles par rôle, href:null masque sans retirer du navigateur) ; Accueil (dashboard réel pour Administrateur/Gestionnaire/ Dispatcher/Vue seule, aucun chiffre inventé) ; OT (liste complète, viewOther déjà géré serveur, réutilise la fiche OT R4 telle quelle) ; Menu (groupes filtrés, vide masqué) ; Demandes (PanneauDemandes — un seul composant pour tous les rôles : création/suivi Demandeur, approbation/ rejet Gestionnaire/Dispatcher/Administrateur, lecture Vue seule). Familles pas encore portées : écran « à venir » plutôt qu'un lien mort. Typecheck propre, 17 tests Jest, lint 5/5 paquets. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
23 KiB
SIOP V2 — GMAO ascenseurs (refondation design-first)
Contexte
Projet réel (SPELEV, maintenance d'ascenseurs, Maroc) mené par le Pr. Daaif (ENSET) avec ses étudiants. V2 = refondation de siop-spelev/siop (v1, gelée, référence de lecture seulement) décidée le 15 juillet 2026 : 100 % nouveau code, design en premier, documentation en « playbook » pédagogique. Double objectif : produit complet déployé (web + mobile + IA, Dokploy) et playbook du cycle de vie logiciel (cours ENSET + méthode de la future entreprise du référent).
Langue de travail : français (docs, échanges, commits descriptifs). Code et identifiants en anglais.
Principes non négociables (V2)
- Design-first : aucune ligne de code applicatif avant validation par le référent de la charte graphique, des tokens et des maquettes HD (
docs/02-design/). Chaque release subit une revue « pixel » écrans ↔ maquettes. - Playbook vivant : chaque phase du cycle de vie a son dossier
docs/0X-*/(template réutilisable + artefacts réels). Le journal quotidien vit dansdocs/journal/— jamais dans le livre. - Périmètre fermé par release : R0 Fondations → R1 Référentiel → R2 Exploitation → R3 Gestion → R4 Mobile → R5 IA → R6 Mobile tous rôles. On n'ouvre pas Rn+1 avant recette et déploiement de Rn.
- Déployer tôt : chaque release part sur le serveur de production (Dokploy) dès sa recette.
- Le développeur est le premier utilisateur :
DEMO_MODE=trueactive un sélecteur de compte démo (connexion 1 clic sur les comptes seedés, endpointPOST /auth/demo-loginstrictement absent si l'env ne l'active pas). Critère : changer de rôle en < 3 s sans mot de passe.
Stack (ADR-001 — reconduite de v1 car éprouvée, code neuf)
pnpm + Turborepo. apps/api : NestJS, Prisma, PostgreSQL (pgvector + PostGIS), Redis, MinIO derrière l'interface FileStorage (jamais d'import direct du SDK). apps/web : React 19, Vite, TanStack Query/Table, shadcn/ui + Tailwind v4 thématisés par les tokens de docs/02-design/tokens.css, Lucide. apps/mobile : Expo (Expo Go). apps/ai : FastAPI (Python, uv). Règle d'or du contrat : schémas Zod dans packages/shared → OpenAPI committée → clients typés régénérés dans le même commit (CI bloquante).
Conventions
- Journal de bord : toute session se clôt par une entrée en tête de
docs/journal/journal.md(date — auteur — release/tâche ; Actions / Décisions / Prochaine étape). - Mode solo temporaire : commits directs sur
maintolérés ; à l'arrivée des étudiants → PR + CI obligatoires. - Commits conventionnels ; branches courtes
feat/…,fix/…. - Tests écrits avec le code : Jest (api), Vitest (web), jest-expo (mobile), pytest (ai) ; couverture backend ≥ 70 % bloquante ; parcours Playwright par release.
- API fermée par défaut (guard JWT global,
@Public()explicite) ; matrice de permissions en base. - Conformité loi 09-08 : géolocalisation limitée au service, purge des audios, consentements.
- IA : l'IA propose, un humain valide (sauf urgence « personne bloquée ») ; toute réponse RAG cite sa source.
Conventions d'infrastructure
- Docker/Dokploy : tous les services et conteneurs sont préfixés
siop2-(demande du référent, leçon v1 : sur le réseau partagé Dokploy, un service nommépostgres/apicollisionne avec les autres projets).
État d'avancement
- ✅ R0 (1/2) : playbook 00-vision, 01-cadrage, 02-design — charte + tokens + maquettes HD VALIDÉES par le référent le 15/07/2026 (artefact : maquette-web.html ; 7 écrans, bi-thème). 03-architecture rédigé (ADR-001 stack, ADR-002 démo-login, C4, modèle R0). Racine monorepo posée (package.json/pnpm-workspace/turbo/.nvmrc) +
infra/(compose PostgreSQL pgvector+PostGIS, Redis, MinIO — services préfixéssiop2-). - ✅ R0.10
apps/api: NestJS + Prisma (migrationr0_identity), guard JWT global fermé par défaut +@Public(), PermissionsGuard (matrice en base, cache 60 s), démo-login ADR-002 (module conditionnel, 404 sinon, double verrou prod), seed idempotent (7 rôles, 70 lignes de matrice, 7 comptes démo),FileStorage/health ;packages/shared(Zod) +pnpm contract→docs/openapi.jsoncommittée ; 19 tests Jest verts + smoke test build prod. - ✅ R0.11
apps/web: React 19 + Vite + Tailwind v4,tokens.csscopié tel quel + classes extraites de la maquette validée, Manrope auto-hébergée ; connexion + sélecteur démo (masqué si 404 — une seule source de vérité : l'API), coquille sidebar (rail safran, écrans futurs marqués R1-R3) / topbar (thème, chip DÉMO, sélecteur de rôle), page /design ; client typéschema.d.tsgénéré depuisdocs/openapi.jsonet committé ; vérifié en navigateur réel (bascule de rôle 101 ms, bi-thème, 0 erreur console). - ✅ R0.12 — tests + CI (5 jobs verts) :
lint(ESLint 10 flat config racine, règle ADR-001 anti-import MinIO codée et vérifiée),ci-contract(régénération spec+client, diff bloquant),api(PostgreSQL 18 + Redis, migrate deploy, Jest couverture ≥ 70 % bloquante — mesurée 97,5 %),web(typecheck + vitest + build),e2e(Playwright : parcours démo, bascule < 3 s chronométrée, déconnexion). Badge au README. - ✅ R0.13 — Dockerfiles + runbook Dokploy : image api (multi-stage pnpm deploy,
prisma migrate deployau boot, seed optionnelSEED_ON_START, non-root, healthcheck ;binaryTargetsexplicites) ; image web (nginx, proxy/apirésolu à la requête, fallback SPA, cache assets) ;infra/docker-compose.dokploy.yml(5 servicessiop2-, seul le web surdokploy-network) ; runbookdocs/06-production/runbook-dokploy.md. Répétition locale complète validée (migrate+seed au boot, parcours via nginx conteneurisé, double verrou ADR-002 observé). Le déploiement réel attend les accès au serveur du partenaire. - ✅ R0 CLOSE (tag
release/r0) — en production :https://siop2.apps.enset.top(Dokploy ENSET, profil démo, vérifiée en ligne). Restes non bloquants : secretDOKPLOY_WEBHOOK_URL(CD), sauvegardes PostgreSQL Dokploy. Production client SPELEV : attend les accès au serveur du partenaire. - ✅ R1 — maquettes validées (16/07) :
maquette-r1.html, 7 écrans + 4 décisions (statuts d'équipement ≠ statuts OT ; organe sans emplacement par construction ; invitation par lien 7 j ; catégories jamais supprimées si utilisées). - ✅ R1.1 — socle backend : migration
r1_referentiel(Category, Location site→zone + PostGIS générée, Asset, AssetComponent, Team, invitation User), 21 nouvelles opérations au contrat (26 total), 4 modules API + invitations/activation sous@RequirePermission, seed parc maquette (5 sites, 8 appareils, organes, 2 équipes), 36 tests (96 %/85 %), CI surpostgis/postgis:18-3.6. - ✅ R1.2 — écrans web du référentiel : 7 écrans fidèles à maquette-r1.html (Sites + carte Leaflet/OSM, fiche site, ascenseurs, fiche appareil + QR réel, création, étiquette A6 imprimable, personnes & équipes avec lien d'activation à copier, catégories) + page /activation ; navigation et actions pilotées par la matrice (
usePermissions) ; recette R1 rejouée en e2e Playwright (dont activation d'un invité) ; alias Vite@siop/shared→ source TS (leçon CJS/workspace). - 🏁 R1 CLOSE (16/07/2026, tag
release/r1) : recettée par le référent, déployée et vérifiée en ligne (migration + seed au boot, 5 sites / 8 appareils sur l'instance). - ✅ R2 — maquettes validées (16/07) :
maquette-r2.html(demandes/approbation, nouvel OT, préventif, checklist, compteurs) + 4 décisions actées. - ✅ R2.1 — socle backend exploitation : migration
r2_exploitation(10 tables), 15 opérations au contrat (41 total, transitions + champs requis du bilan dans@siop/shared), machine à états stricte avec garde de clôture (bilan 3 champs + checklist), demande→OT 1-1, rejet à motif, scoping « voir autre » (listes + accès directs), seed maquette (31 valeurs de bilan, 8 gabarits, OT/demandes/compteurs), 45 tests (95 %/79 %). - ✅ R2.2 — préventif + compteurs :
underContract+periodKey(unicité[assetId, periodKey]= idempotence EN BASE), génération mensuelle (ancrage mise en service, premier contrôle, gabarits administrables, statut du mois), compteurs strictement croissants ; +7 opérations (48), 50 tests (94,7 %/78 %). Automatisation cron/BullMQ notée pour le durcissement production. - ✅ R2.3 — écrans web exploitation : liste/fiche OT (transitions via
allowedTransitions, garde visible, bilan codé, checklist cliquable, activité), demandes+approbation/rejet motivé, nouvel OT (interrupteur urgence), préventif (tuiles+générer+gabarits), compteurs, tableau de bord réel, urgence traversante (chip topbar, badges, bandeau) ;GET /assets/options(trou Demandeur corrigé) ; retry sur collision de référence dans la génération ; 9 Playwright verts (recette R2 complète), 50 tests API. - 🏁 R2 CLOSE (16/07/2026, tag
release/r2) : recettée (1 anomalie corrigée en recette : tri « Interventions récentes »), déployée et vérifiée en ligne (portail/q/A1, urgence en tête, préventif de juillet généré). - ✅ R3 — maquettes validées (16/07) + R3.1 socle backend gestion : migration
r3_gestion(Partner, Part sans colonne de quantité, StockMovement signé/tracé/PU figé, PurchaseOrder, LaborTime taux figé, Document, User.hourlyRate) ; 66 opérations ; stock = Σ mouvements (jamais négatif, en transaction), réception BC → RECEIPT + lastUnitPrice, conso/MO à prix/taux FIGÉS,WorkOrderDetail.costsimmuable après clôture ; seed maquette (OT-0341 = 505 MAD, testé) ; 55 tests (92 %/74,9 %). Durcissement : références par séquences Postgres (fin des courses max+1). - ✅ R3.2 — bibliothèque + analytics :
FileStoragecomplet (put/stream/remove, bucket auto), upload multipart typé (20 Mo, rattachement requis, permission sur la cible), download streamé par l'API, octets vérifiés à l'identique en e2e ;GET /analytics/summarydérivé du réel (pannes par organe via bilans codés, coûts figés, taux préventif, top équipements) ; MinIO en CI ; 71 opérations, 58 tests (6 runs consécutifs verts). - ✅ R3.3 : 7 écrans web fidèles à maquette-r3 (stock/alertes, fiche pièce, BC + réception, création BC préremplie depuis l'alerte, tiers, bibliothèque, statistiques) + fiche OT complète (coûts réels : consommer à prix figé / saisir temps à taux figé ; carte Documents) + fiche ascenseur (Documents) + taux horaire dans Personnes + nav Ressources/Statistiques activée par la matrice. Recette R3 automatisée (13/13 e2e Playwright).
- ✅ R3.4 — corrections de recette + recherche globale (17/07) : revue pixel (artefact) → 8 écarts corrigés sur arbitrage du référent (filtre fournisseur + tri sous-seuil + entrée de stock en liste, fournisseur cliquable, sélecteur de période 3/6/12 sur les stats, rattachements syndic→site via
Location.partnerId(migrationr3_recette_fixes), filtre « Rattaché à » + glisser-déposer en bibliothèque, aperçus/ouverture des documents) ; recherche globale ⌘K (GET /search, 73 opérations, familles filtrées par la matrice, « voir autre » respecté). 74 tests API, 14/14 Playwright. - 🏁 R3 CLOSE (17/07/2026, tag
release/r3) : recettée par le référent (revue pixel + 8 corrections + recherche globale, CI verte). Reste : redéploiement Dokploy (manuel, webhook CD absent) et vérification en ligne. - ✅ R4 — maquettes rédigées (17/07) :
maquette-r4.html, 7 écrans mobile technicien offline-first (Ma journée bi-état, fiche OT, clôture terrain avec garde, scan QR des étiquettes R1, fiche ascenseur, checklist en file, synchro & conflits à verrou optimiste) + 5 décisions à acter (offline-first en file, verrou optimiste tranché par l'humain, périmètre fermé technicien, scan local, photos en file — ni audio ni géoloc en R4). - ✅ R4 — maquettes + décisions D1-D5 VALIDÉES par le référent (17/07) ; R4.1 socle mobile :
apps/mobile(Expo SDK 57, TS strict), connexion + sélecteur démo ADR-002, tabbar (onglets futurs marqués), « Ma journée » triée priorité/échéance, cache TanStack persisté (lecture hors-ligne D1), jeton SecureStore, client typé du contrat,CORS_ORIGINSopt-in côté API (Expo web/debug seulement), 6 tests jest-expo + job CImobile. Vérifié 10/10 en Expo web (connexion démo → Ma journée → hors-ligne/retour). - ✅ R4.2 — terrain en ligne : scanner QR réel (expo-camera,
analyseScantestée, résolution locale D4, QR étrangers refusés), fiche ascenseur (D3, historique scopé), fiche OT (machine à états, coûts figés, garde parclosureBlockersAPI), clôture terrain (bilan 6 champs au pouce), préventif → grille cochable (appui long = N/A,aria-checked). Écritures en ligne assumées (bandeaux) — la file est R4.3. Vérifié 12/12 en Expo web (dont clôture complète d'un OT de test), 12 tests jest-expo. - ✅ R4.3 — file & verrou : verrou optimiste serveur (
baseUpdatedAt→ 409 contextualisé, toute écriture avance la version, web inchangé, ADR-003 sécurité/routage mobile) ; file persistée rejouée dans l'ordre (propagation de version intra-lot, arrêt sur conflit, patchs optimistes, photos D5 compressées en file), écran Synchro & conflits (rejouer sur version à jour / abandonner), préchargement parc+référentiels, purge complète à la déconnexion. Recette « mode avion » 13/13 en Expo web (conflit tranché par l'humain, serveur DONE + bilan), 17 tests jest-expo, 74 tests API. - 🏁 R4 CLOSE (17/07/2026, tag
release/r4) : posé sur décision du référent avec la recette « mode avion » validée 13/13 en Expo web piloté ; la recette sur téléphone (Expo Go — vrai mode avion, scan caméra) est reportée et reste due avant toute production client mobile. - ✅ R5 — maquettes rédigées (17/07) :
maquette-r5.html, 6 écrans (assistant RAG à citations sources document/page/extrait, refus explicite hors corpus, suggestion de bilan web+mobile à validation humaine, corpus & ingestion administrable, voix opt-in avec purge) + 5 décisions à acter (aucune écriture automatique, sourcé ou silencieux, corpus fermé, anonymisation 09-08, voix purgée). - ✅ R5 — maquettes + décisions D1-D5 VALIDÉES par le référent (17/07) ; R5.1 socle
apps/ai: ADR-004 (embeddings locaux fastembed 384d, pgvector via migration Prismar5_ia(RagChunk+ corpus sur Document), génération opt-in — mode extractif par défaut, service jamais exposé joint par l'API seule), pipeline d'ingestion anonymisé D4 (fonction pure testée, PDF paginés + bilans codés),/internal/reindex+/internal/searchsous jeton de service, 14 pytest + ruff + job CIai(embeddeur déterministe en CI). Vérifié en réel : corpus seedé indexé en 7 s, recherche sémantique concluante, 0 identité dans les chunks. - ✅ R5.1+ génération opt-in :
GenerateurADR-004 §3 — extractif par défaut,AI_GENERATION=api+AI_API_KEY(exigée au boot, jamais loguée ; Dokploy secrets) +AI_MODEL(défaut claude-opus-4-8), SDK anthropic en extra optionnel, citations [n] obligatoires, repli extractif sur tout échec (dontstop_reason=refusal). R5.2 assistant au contrat :POST /assistant/ask(WORK_ORDERS.view) +POST /assistant/suggest-bilan(WORK_ORDERS.edit) proxifiés par NestJS (traduction dialecte interne → contrat, 503 propre), suggestion = codes EXISTANTS seulement (confiance + « N bilans similaires »), normalisation fastembed corrigée (débusquée en chaîne réelle), 23 pytest, tests API sur stub HTTP. - ✅ R5.3 — écrans IA : page web Assistant (chat sourcé, refus honnête chiffré, « Ouvrir » vers PDF/OT), Bibliothèque = corpus administrable (statut d'indexation par document, interrupteur d'exclusion, « Réindexer tout », bandeau 09-08), suggestions fiche OT (« Appliquer » = geste humain, liseré « suggéré » retiré au choix manuel) et clôture mobile (chips, « réseau requis » hors-ligne) ; contrat 76 opérations (corpus sur Document,
PATCH /documents/{id}/corpus,POST /assistant/reindex— ci-contract vérifie aussi le client mobile) ; seuilsAI_SEUIL_*par env ; job e2e CI avecsiop2-ai(embeddeur déterministe, seuils calibrés sur mesures réelles), 16/16 Playwright, 78 tests API ; chaîne vérifiée au vrai modèle ONNX (web 7/7, mobile Expo web 6/6, 0 erreur console). - ✅ Recette R5 sans clé API + durcissement (17/07) : la recette a invalidé MiniLM-384 (page-réponse classée derrière des passages sans rapport, 0,24 vs 0,41) → bascule mesurée vers
paraphrase-multilingual-mpnet-base-v2768 d (ADR-004 amendé, migrationr5_embeddings_mpnet, découpage ~350 car., seuils 0,45/0,40/0,55) ; recette type ✓ (réponse sourcée p. 2, refus honnête, D1-D5, tout en extractif) ; revue pixel publiée (artefact, 3 arbitrages) ; Dockerfilesiop2-ai(modèle au build, non-root), compose Dokploy (service interne,AI_SERVICE_TOKENrequis, génération opt-in), runbook §5-6 (service IA, calibrage seuils client, réindexation post-déploiement). - ✅ Revue pixel R5 validée par le référent (17/07) — 1 correction appliquée sur arbitrage : la Bibliothèque-corpus passe en tableau (Document/Rattaché à/Indexation/Corpus + actions ; interrupteur éteint pour les non-indexables), vignettes conservées sur les cartes Documents des fiches ; « Appliquer » direct et calibrage au runbook validés tels quels. 16/16 Playwright rejoués.
- 🏁 R5 CLOSE (17/07/2026, tag
release/r5) : recettée par le référent (revue pixel + arbitrage tableau), recette passée SANS clé API, durcissement répété en local conteneurisé. R0 → R5 : périmètre v1 couvert. - ✅ Recette terrain mobile sur iPhone 15 Pro — 7/7 validés (19/07, ADR-005) : Expo Go bloqué par le retard d'approbation Apple (SDK 54 vs notre SDK 57) → décision : builds natifs locaux (Xcode/Android Studio, signature gratuite) pour la vraie recette, Expo Go conservé pour l'aperçu sans installation. 5 obstacles techniques réels corrigés et documentés (UDID plutôt que nom, ne jamais contourner
expo run:*par un xcodebuild manuel, modules Expo/RN précompilés SDK 56/57 incompatibles avec la liaison statique du projet,fetchglobal incompatible avec l'upload multipart natif — fix permanentEXPO_PUBLIC_USE_RN_FETCH=1dansapps/mobile/.env, découverte réseau du dev client peu fiable). Recette iOS : connexion, scan caméra réel (résolution + rejet QR étranger), vrai mode avion, conflit D2 tranché par l'humain avec photo réellement téléversée, persistance à travers fermeture/reconstruction de l'app, suggestions R5 au vrai modèle, purge de sécurité au changement de compte — tout validé. Android : build natif sur émulateur, passage santé complet. - ✅ Dictée implémentée (22/07, ADR-004 §5) : écran Voix R5 (jamais construit) —
faster-whisperlocal (CTranslate2, CPU, opt-inAI_TRANSCRIPTION=off|locale, défaut off), audio jamais persisté (purge serveur ET mobile quoi qu'il arrive), transcription → relecture humaine → suggestion immédiate et/ou sauvegarde dansInterventionReport.note(champ existant depuis R2, jamais eu d'écran jusqu'ici) →noterejoint le corpus à la clôture comme les bilans codés. Mobile :expo-audio/expo-file-system, bouton dicter sur l'écran de clôture. Dockersiop2-aiembarque le modèle (1,54→2,19 Go). Vérifié réellement (voix de synthèse → texte fidèle, bout en bout API, conteneur Docker construit, chaîne corpus complète note→clôture→réindexation→recherche). 80 tests API, 29 pytest ai, mobile vert. - ✅ Dictée validée sur iPhone physique (01/08, ADR-004 §5) : le blocage USB du 22/07 était un faux négatif de
system_profiler(outil de diagnostic en panne, pas le câble). Deux bugs réels trouvés et corrigés :expo-audioiOS exigesetAudioModeAsync({ allowsRecording: true })avantrecord()(absent, jamais détecté hors appareil réel) ; le correctif des modules précompilés (ADR-005) n'avait jamais été rendu permanent dansapps/mobile/.env— corrigé. Chaîne complète vérifiée en vrai : micro → dictée → transcription fidèle → note → clôture → réindexation → retrouvé par la recherche sémantique. - fix(mobile) déconnexion accessible depuis tous les onglets (02/08) : bug remonté par le référent (une fois authentifié, aucun moyen de revenir en arrière ou de se déconnecter — seule « Ma journée » portait ce contrôle, via un
onLongPressnon découvrable).EnteteTabs(composants/ui.tsx) factorise une entête commune aux 4 onglets terrain, tap simple + confirmation, rôle affiché dynamique. - ✅ R6 — maquette validée (02/08) :
maquette-mobile-tous-roles.html, 7 écrans (synthèse par rôle, Accueil adaptatif, Menu groupé, OT côté Dispatcher, Demandes, Stock, Personnes & statistiques) + 5 décisions actées — D1 barre d'onglets adaptative (Technicien/Technicien limité inchangés + onglet Menu) ; D2 le Menu reprend à l'identique les 4 groupes du web (même matrice, aucune règle nouvelle) ; D3 groupe sans lien visible masqué en entier ; D4 le mobile porte les actions courantes par famille, pas les flux de gestion les plus denses (réservés au web) ; D5 aucune logique de permission propre au mobile. Nouvelle release (R4 est close et R4.1/4.2/4.3 déjà pris par le socle mobile technicien d'origine — pas de réouverture, pas de collision de numérotation). - ✅ R6.1 — socle :
usePermissions()mobile (calqué sur le web, litme.permissions) ; barre d'onglets adaptative par rôle (ongletsVisibles,href: nullmasque sans retirer du navigateur) ; écrans Accueil (dashboard réel pour Administrateur/Gestionnaire/Dispatcher/Vue seule, aucun chiffre inventé), OT (liste complèteviewOther, réutilise la fiche OT R4 telle quelle), Menu (groupes filtrés par la matrice, groupe vide masqué), Demandes (PanneauDemandes— un seul composant pour tous les rôles : création/suivi pour le Demandeur, approbation/rejet à motif pour Gestionnaire/Dispatcher/Administrateur, lecture seule pour Vue seule ;GET /assets/options, déjà ouvert à tout rôle authentifié depuis R2.3, réutilisé pour le sélecteur d'équipement). Familles pas encore portées (Sites, Ascenseurs, Stock, Tiers, Fichiers, Statistiques, Assistant, Personnes) : écran « à venir » honnête plutôt qu'un lien mort. Typecheck propre, 17 tests Jest verts, lint 5/5 paquets — pas de vérification visuelle en navigateur de mon côté (aucun outil de ce type dans cet environnement), à confirmer par le référent sur l'iPhone déjà connecté au serveur Metro. - 🔄 Reprise ici : confirmation du référent sur iPhone (nav adaptative R6.1 + correctif déconnexion) ; puis R6.2 (Parc — Sites, Ascenseurs), R6.3 (Ressources — Stock, Tiers, Fichiers), R6.4 (Pilotage — Statistiques, Personnes, Assistant), sur le modèle des sous-releases R4.1→R4.3. Restes non bloquants inchangés : redéploiement Dokploy de l'instance ENSET (
AI_SERVICE_TOKENà créer — runbook §2 — puis « Réindexer tout »), recette Android sur appareil physique, calibrageAI_SEUIL_*et qualité darija sur corpus SPELEV réel, secretDOKPLOY_WEBHOOK_URL, production client SPELEV (attend les accès serveur du partenaire). - Détail quotidien :
docs/journal/journal.md. Dépôt :siop-spelev/siop2(privé), jalons R0→R5 (v1) + R6 en cours.