Audit complet du dépôt atlas — 2026-05-29
Date de l’audit : 2026-05-29. Méthode : workflow multi-agents (22 dimensions auditées en parallèle, findings Critical/High vérifiés de manière adversariale par 3 prismes — exactitude, preuve, sévérité — kill si majorité réfute).
Glossaire express
Section intitulée « Glossaire express »- monorepo : dépôt unique regroupant plusieurs paquets logiciels distincts (apps, libs, services) au lieu d’un dépôt par projet.
- ADR (Architecture Decision Record) : note courte qui acte une décision d’architecture, son contexte et ses conséquences (format Nygard).
- workspace : sous-paquet (dossier avec son propre
package.json) géré par le gestionnaire de paquets du monorepo (ici pnpm). - CLI (Command Line Interface) : programme à exécuter en ligne de commande, livré avec un
bin. - lib (library) : code réutilisable importé par un autre paquet, sans
bin. - BaaS (Backend-as-a-Service) : service tiers qui fournit base de données, auth et stockage prêts à l’emploi (ici Appwrite).
- REDCap : logiciel généraliste de saisie de données structurées via formulaires en ligne (recherche, enquêtes, administration), exposant une API REST.
- OpenAPI : standard de description d’API HTTP (anciennement Swagger), sert à documenter et valider les contrats.
- SvelteKit : framework web basé sur Svelte qui gère pages, endpoints et hooks serveur.
- runes : nouveau système de réactivité de Svelte 5 (
$state,$derived,$effect). - vitest : framework de tests unitaires/intégration équivalent à Jest, basé sur Vite.
- Playwright : framework de tests end-to-end (E2E) pilotant un vrai navigateur.
- MSW (Mock Service Worker) : bibliothèque pour intercepter les appels HTTP au niveau réseau dans les tests.
- rate limit : limitation du nombre de requêtes autorisées par adresse IP ou utilisateur sur une fenêtre de temps.
- XSS (Cross-Site Scripting) : injection de code JavaScript via un paramètre utilisateur qui se retrouve réfléchi côté navigateur.
- CSP (Content Security Policy) : en-tête HTTP qui restreint les sources autorisées de scripts, images, etc.
- CORS (Cross-Origin Resource Sharing) : règles décidant quels domaines peuvent appeler une API depuis un navigateur.
- gitleaks : scanner de secrets (clés API, mots de passe) dans l’historique git.
- CodeQL : moteur d’analyse statique de sécurité (SAST) de GitHub.
- knip : outil qui détecte le code mort (exports, deps, fichiers) inutilisés dans un projet TypeScript.
- lefthook : gestionnaire de git hooks (pre-commit, pre-push, etc.).
- Changesets : outil de gestion de versions et changelogs pour monorepos.
- Hono : micro-framework HTTP TypeScript utilisé ici pour
services/crf. - Effect : bibliothèque TypeScript de programmation fonctionnelle (gestion d’erreurs typées, effets explicites).
- brand (branded type) : technique TS pour distinguer au type-check deux chaînes/nombres sémantiquement différents (ex :
RecordIdvsstring). - property-based testing : tests qui génèrent automatiquement des entrées aléatoires pour vérifier des invariants (ici via
fast-check). - smoke test : test minimal qui vérifie que le chemin nominal d’un système marche de bout en bout.
- scaffold : squelette de paquet sans contenu fonctionnel, posé comme amorce.
- flat config : nouveau format de configuration d’ESLint (un seul fichier JavaScript exportant un tableau).
Résumé exécutif
Section intitulée « Résumé exécutif »-
Nombre total de findings retenus dans le corps du rapport : 51 (après rejets adversariaux), répartis comme suit :
- High : 8
- Medium : 27
- Low : 14
- Info : 2
- 13 findings supplémentaires (dont 1 Critical et 12 High) ont été rejetés par la vérification 3-prismes — listés en annexe A.
-
Top 5 priorités à traiter en premier :
- [thresholds-zero-bypass-ci] — 10 paquets ont des seuils de couverture vitest à 0, la CI ne détecte aucune régression sur ces paquets.
- [coverage-report-not-enforced] — le rapport global de couverture existe mais n’est branché ni en CI ni en pre-push : son
process.exitCode = 1est inerte. - [zero-files-silently-excluded] — l’agrégation de coverage exclut silencieusement les fichiers à 0%, produisant des « 100% » trompeurs sur 84 fichiers (dont 9 dans
services/crf). - [services-crf-real-coverage-low] — le seul microservice du dépôt (ingestion REDCap) déclare un seuil 17/14/24/18 et est le plus exposé.
- [no-e2e-in-ci] — aucune suite Playwright E2E n’est exécutée en CI ; les deux smokes existants ne valident le scénario complet qu’à la main.
-
Forces principales du dépôt :
- Architecture monorepo 8-catégories cohérente, audit-ée par script et documentée par 19 ADR.
- Hooks lefthook stricts et non bypassables (ADR 0015), couvrant format, lint, typecheck, audits de sécurité.
- Noyau métier (validators, errors, citation-types, auth, crf-core, crf-client…) à 90-100% de couverture statements/branches.
- Config partagée riche : flat configs ESLint par catégorie, presets Svelte/TS, vitest commun, prettier base.
- Outillage de contract testing REDCap réel (fixtures versionnées, 1700+ lignes) et docker-compose sandbox propre.
-
Score d’ensemble par groupe de dimensions (sur 5 ; reflète l’écart entre intention documentée et enforcement réel) :
- A-E — Architecture & qualité de code : 3,5 / 5 (structure solide mais quelques ADR non enforced)
- F-H — Documentation : 3 / 5 (présente mais inégale)
- I-K — Reproductibilité & gouvernance : 4 / 5 (hooks stricts, ADR vivants)
- L-O — Dépendances & types : 3,5 / 5 (typage strict mais quelques angles morts)
- P-T — Qualité opérationnelle (tests, sécurité, observabilité) : 2,5 / 5 (pyramide de tests aplatie, observabilité partielle)
- U-V — Data & cohérence inter-apps : 3 / 5
L’audit a été conduit par un workflow d’agents multiples lancés en parallèle sur 22 dimensions distinctes du dépôt (architecture, tests, sécurité, documentation, dépendances, observabilité, etc.). Chaque agent a produit un rapport structuré (résumé, forces, findings) en s’appuyant uniquement sur les fichiers du dépôt.
Chaque finding est qualifié par trois axes : sévérité (Critical, High, Medium, Low, Info), confiance (High, Medium, Low) et effort estimé pour la correction (XS, S, M, L, XL).
Les findings de sévérité Critical et High ont fait l’objet d’une vérification adversariale : trois agents indépendants ont examiné chacun le finding sous un angle différent — exactitude factuelle, qualité de la preuve, justesse de la sévérité. Le finding est rejeté (« kill ») si au moins deux des trois agents le réfutent. Les findings rejetés sont listés en annexe A pour traçabilité.
Le rapport ci-dessous restitue le résultat de cette consolidation : seuls les findings non réfutés apparaissent dans le corps ; chaque finding High vérifié est détaillé individuellement, les Medium/Low/Info sont synthétisés en tableau.
Findings par dimension
Section intitulée « Findings par dimension »A. Architecture & structure
Section intitulée « A. Architecture & structure »Résumé. La structure 8-catégories est cohérente, audit-ée et bien documentée (ADR 0002/0019). Les principales fragilités tiennent à des écarts entre règles documentées dans les ADR et règles réellement vérifiées par scripts/audit/workspace-structure.mjs, et à quelques placements de paquets qui contredisent leur catégorie (notamment cli/crf-openapi exposant une lib, packages/citation-validate jouant le rôle d’un CLI, sandbox/crf-sandbox-core scaffold vide).
Forces observées.
- L’ADR 0002 décrit les 8 catégories avec règles claires et l’ADR 0019 isole nominativement chaque dérogation, ce qui rend les écarts visibles et révisables.
- scripts/audit/workspace-structure.mjs couvre concrètement présence de
bin, dépendances interdites, imports runtime, isolation sandbox, cycles inter-workspaces,repository.directorycohérent. - Distinction fine
import typevs runtime viaRUNTIME_IMPORT_RE/ANY_IMPORT_RE:packages/authpeut importertype Cookiesde@sveltejs/kitsans contredire la règle. - Le pnpm-workspace.yaml liste explicitement les 8 globs, alignés avec
ROOTSdu script d’audit. - La règle « sandbox isolé » est doublement enforcée (check explicite que nul autre root ne dépend d’un nom dans
sandboxNames).
Findings.
| # | Titre | Sévérité | Confiance | Effort | Vérifié | Locations |
|---|---|---|---|---|---|---|
| 1 | ADR 0008 promet une enforcement audit:structure qui n’existe pas | Medium | High | S | — | docs/decisions/0008-clis-thins-logique-dans-packages.md:30 |
| 2 | ADR 0011 affirme un check private:true non implémenté | Medium | High | XS | — | docs/decisions/0011-paquets-internes-private.md:47 |
| 3 | ADR 0011 décrit une réalité périmée (tous les apps sont private) | Medium | High | XS | — | docs/decisions/0011-paquets-internes-private.md:51 |
| 4 | cli/crf-openapi expose une bibliothèque déguisée en CLI | Medium | High | M | — | cli/crf-openapi/package.json:17 |
| 5 | packages/citation-validate se décrit comme « CLI tools » | Medium | High | M | — | packages/citation-validate/package.json |
| 6 | sandbox/crf-sandbox-core est un scaffold vide consommé par personne | Low | High | S | — | sandbox/crf-sandbox-core/README.md:5 |
| 7 | Préfixe atlas- du nom de dossier appliqué sans règle uniforme | Low | High | S | — | packages/atlas-stats, cli/atlas-stats |
| 8 | apps→apps / services→services : interdiction sur deps uniquement, pas sur imports source | Low | High | M | — | scripts/audit/workspace-structure.mjs:416 |
| 9 | L’audit scanne .svelte mais pas les .svelte.ts ni les imports dynamiques | Low | Medium | S | — | scripts/audit/workspace-structure.mjs:86 |
| 10 | Pas d’ADR explicite sur les dépendances qu’un sandbox peut tirer du dépôt | Low | Medium | S | — | docs/architecture/monorepo.md:137 |
| 11 | config/ : le script interdit bin mais n’impose pas d’exports | Info | High | XS | — | scripts/audit/workspace-structure.mjs:331 |
| 12 | packages/atlas-stats et cli/atlas-stats partagent le même nom de dossier | Info | High | XS | — | packages/atlas-stats/package.json |
B. Couverture de tests
Section intitulée « B. Couverture de tests »Résumé. Outillage homogène (vitest + coverageConfig partagée, seuils par paquet, rapport agrégé), avec un noyau de packages métier entre 96 et 100% statements. Mais l’enforcement est largement contournable : dix paquets ont des seuils à 0, le rapport agrégé n’est jamais exécuté en CI, et la mesure exclut silencieusement les fichiers totalement non couverts, ce qui produit des « 100% » trompeurs sur services/crf (9 fichiers ignorés), atlas-dashboard (4), sillage (1), crf-cli (2).
Forces observées.
- Outillage homogène :
coverageConfigpartagée dansconfig/shared-config/vitest(provider v8, reporters, excludes). - Noyau métier solidement testé :
validators,errors,citation-types,fetch-one-api-page,baas,citation-fetch,atlas-stats/compute,crf-logs,crf-core(98.4%),crf-client(96.5%),researcher-profiles(98.9%),citation(98.1%),auth(96.7%) tous au-dessus de 90%. - Le rapport agrégé
coverage-report.mjsest bien fait (distinction « sans vitest » / « avec vitest sans coverage » / « fichiers à 0% exclus », exitCode=1 si paquet < target). - Pyramide de tests
amarre/sillagedocumentée (projets unit / ui / integration dansvitest.config). - Dynamique active de remontée de couverture (PR #214 cli/crf 10.5→64.6%, PR #216 cli/net 0→50.5%).
Findings.
| # | Titre | Sévérité | Confiance | Effort | Vérifié | Locations |
|---|---|---|---|---|---|---|
| 13 | Dix paquets ont des seuils vitest à 0 | High | High | S | ✓ 3/3 | cli/biblio/vitest.config.ts:8 + 9 autres |
| 14 | Le rapport global de couverture n’est jamais exécuté en CI | High | High | XS | ✓ 2/3 | package.json:29 |
| 15 | Les fichiers à 0% sont silencieusement exclus du dénominateur | High | High | S | ✓ 2/3 | scripts/audit/coverage-report.mjs:57 |
| 16 | services/crf affiche « 100% » mais seuil déclaré 17/14/24/18 | High | High | M | ✓ 3/3 | services/crf/vitest.config.ts:8 |
| 17 | apps/ecrin seuils très bas (28/18/27/28) | Medium | High | M | — | apps/ecrin/vitest.config.ts:14 |
| 18 | apps/amarre seuils 25 pts sous la couverture réelle | Medium | High | S | — | apps/amarre/vitest.config.ts:35 |
| 19 | apps/find-an-expert ne mesure que src/lib/**/*.ts | Medium | High | M | — | apps/find-an-expert/vite.config.ts:24 |
| 20 | 4 CLI publiés sans aucun fichier de test | Medium | High | L | — | cli/biblio, cli/citation, cli/atlas-stats, cli/crf-stats |
| 21 | ui/atlas-ui : 22 composants Svelte partagés, 0 test | Medium | High | L | — | ui/atlas-ui/package.json:34 |
| 22 | packages/atlas-stats : coverage limité à src/compute.ts | Medium | High | S | — | packages/atlas-stats/vitest.config.ts:7 |
| 23 | sandbox/crf-sandbox : tests de contrat exclus par défaut, seuils à 0 | Low | High | S | — | sandbox/crf-sandbox/vitest.config.ts:7 |
| 24 | assets/logos : « test » cosmétique qui vérifie 6 fichiers existent | Low | High | XS | — | assets/logos/assets.test.ts:19 |
High — Dix paquets ont des seuils vitest à 0, la CI ne peut détecter aucune régression [thresholds-zero-bypass-ci]
Section intitulée « High — Dix paquets ont des seuils vitest à 0, la CI ne peut détecter aucune régression [thresholds-zero-bypass-ci] »- Locations : cli/biblio/vitest.config.ts:8, cli/citation/vitest.config.ts:8, cli/atlas-stats/vitest.config.ts:8, cli/crf-stats/vitest.config.ts:8, cli/researcher-profiles/vitest.config.ts:8, cli/crf/vitest.config.ts:8, cli/net/vitest.config.ts:8, apps/atlas-dashboard/vitest.config.ts:8, apps/crf-dashboard/vitest.config.ts:8, sandbox/crf-sandbox/vitest.config.ts:13
- Observation : Vitest n’échoue jamais sur un drop de couverture pour ces 10 paquets ; 0% suffit. La CI (
pnpm test:coverage) et le pre-push reposent uniquement sur les seuils vitest locaux. Aucun garde-fou global ne reprend la main :scripts/audit/coverage-report.mjsn’est pas exécuté en CI. - Recommandation : Définir un seuil plancher non trivial par paquet (par exemple 50/40/50/50 minimum) et exiger qu’un relevé < ce plancher fasse échouer vitest. Pour les paquets vraiment sans logique à couvrir, documenter la raison dans ADR 0019.
- Effort : S
- Vérification : ✓ 3/3 confirmé.
High — Le rapport global de couverture n’est jamais exécuté en CI ni en pre-push [coverage-report-not-enforced]
Section intitulée « High — Le rapport global de couverture n’est jamais exécuté en CI ni en pre-push [coverage-report-not-enforced] »- Locations : package.json:29, .github/workflows/ci.yml:117, lefthook.yml
- Observation : Le script
coverage-report.mjsest écrit pour faireprocess.exitCode = 1quand un paquet est sous letarget(défaut 80) ou n’a pas decoverage-final.json, mais il n’est jamais appelé : nici:checks, nici.ymltest job, ni lefthook pre-push ne l’invoquent. Le travail est en place et inutilisé. - Recommandation : Ajouter
pnpm coverage:report 80aprèspnpm test:coveragedans le jobtestdeci.ymlet dans la commande pre-push lefthook. - Effort : XS
- Vérification : ✓ 2/3 confirmé.
High — Le rapport exclut silencieusement les fichiers à 0% du dénominateur [zero-files-silently-excluded]
Section intitulée « High — Le rapport exclut silencieusement les fichiers à 0% du dénominateur [zero-files-silently-excluded] »- Locations : scripts/audit/coverage-report.mjs:57
- Observation : L’exécution actuelle montre
services/crf 100% / 9 skipped,apps/atlas-dashboard 100% / 4,apps/crf-dashboard 100% / 5,apps/sillage 94-100% / 1,cli/crf-cli 90% / 2,cli/researcher-profiles-cli 100% / 10, etc. — total 84 fichiers « skipped » dont aucune ligne n’est couverte. Un paquet peut afficher 100% tout en n’ayant aucun test sur la majorité du code. Le 100% deservices/crfest particulièrement trompeur (levitest.config.tsy déclare déjàstatements: 17). - Recommandation : Soit compter les zero-files dans le dénominateur (option
--strict), soit publier la liste détaillée des fichiers exclus avec leur taille, et ajouter une règle qui échoue si > N fichiers d’un paquet sont skipped. - Effort : S
- Vérification : ✓ 2/3 confirmé.
High — services/crf affiche 100% mais le seuil déclaré est 17/14/24/18 [services-crf-real-coverage-low]
Section intitulée « High — services/crf affiche 100% mais le seuil déclaré est 17/14/24/18 [services-crf-real-coverage-low] »- Locations : services/crf/vitest.config.ts:8, services/crf/src
- Observation : Le seul service du monorepo (microservice Hono REDCap) déclare des seuils très bas (17/14/24/18) et n’a que 2 fichiers de tests (
effect-handler.test.ts,server.test.ts). Le rapport remonte « 100% » trompeur. C’est le composant le plus exposé en ingestion REDCap. - Recommandation : Reprendre la couverture du service : tester les autres handlers et la configuration. Aligner les seuils vers > 80% ou documenter la raison dans ADR 0019.
- Effort : M
- Vérification : ✓ 3/3 confirmé.
C. Tests multi-niveaux
Section intitulée « C. Tests multi-niveaux »Résumé. Le dépôt expose une pyramide bien intentionnée (unit / ui / integration / smoke E2E) avec ~177 fichiers de tests. Mais la pyramide est en réalité aplatie : il n’existe que deux suites Playwright (amarre, sillage) jamais exécutées en CI, et la majorité des endpoints SvelteKit n’ont aucun test direct.
Forces observées.
- Pyramide explicitement nommée et structurée dans
vitest.config.tsd’apps/amarreetapps/sillage(projets unit / ui / integration, environnements distincts node/happy-dom). - Vraie suite de contract testing REDCap (sandbox/crf-sandbox/tests/contract/api-contract.test.ts, 1700+ lignes) avec fixtures versionnées (
projects.json). - Vraie suite d’intégration apps/amarre/tests/integration/ qui sollicite Appwrite + Mailpit + REDCap via docker-compose et
skipIf(!reachable)propre. - Smoke E2E Playwright sandbox/amarre-sandbox/tests/e2e/smoke.spec.ts couvrant signup→magic-link→logout avec auto-cleanup.
- Tests anti-XSS et 401 documentés sur certains endpoints (Phase 7.2 explicite).
Findings.
| # | Titre | Sévérité | Confiance | Effort | Vérifié | Locations |
|---|---|---|---|---|---|---|
| 25 | Aucune suite Playwright E2E ne tourne en CI | High | High | M | ✓ 2/3 | .github/workflows/ci.yml, sandbox/amarre-sandbox/tests/e2e/smoke.spec.ts:35 |
| 26 | Couverture des endpoints SvelteKit très inégale entre apps | High | High | M | ✓ 2/3 | apps/ecrin/src/routes/api/v1/, apps/find-an-expert/src/routes/api/v1/, apps/amarre/src/routes/api/v1/ |
| 27 | Tests « wrapper-only » sur-mockés (sillage services-auth, baas) | Medium | High | XS | — | apps/sillage/tests/integration/services-auth.test.ts:13 |
| 28 | Pas de mock HTTP au niveau réseau (MSW absent), tout mocké en fonction | Medium | High | M | — | packages/citation/src/fetch/fetch-citation.test.ts:5 |
| 29 | Tests anti-XSS présents seulement sur un endpoint | Medium | High | S | — | apps/find-an-expert/src/routes/api/v1/institutions/search/server.test.ts:67 |
| 30 | Tests anti-dérive OpenAPI limités à amarre | Medium | High | M | — | apps/amarre/tests/routes/api/v1/surveys/list.test.ts:11 |
| 31 | 7 CLI sur 9 ont un seuil coverage à 0 | Medium | High | S | — | cli/biblio/vitest.config.ts:1 |
| 32 | Rate limit en mémoire partagé entre tests sans cleanup unifié | Low | Medium | XS | — | packages/auth/src/handlers.test.ts:242 |
| 33 | Absence de property-based testing sur validators / brands | Low | High | S | — | packages/validators/src/index.test.ts, packages/crf-core/src/brands/ |
| 34 | config/shared-config/vitest.config.ts pointe vers un dossier inexistant | Low | Medium | XS | — | config/shared-config/vitest.config.ts:5 |
| 35 | Smoke E2E sans isolation d’état Bootstrap entre tests | Low | Medium | S | — | sandbox/amarre-sandbox/tests/e2e/smoke.spec.ts:66 |
High — Aucune suite Playwright E2E ne tourne en CI [no-e2e-in-ci]
Section intitulée « High — Aucune suite Playwright E2E ne tourne en CI [no-e2e-in-ci] »- Locations : .github/workflows/ci.yml, sandbox/amarre-sandbox/tests/e2e/smoke.spec.ts:35, sandbox/sillage-sandbox/playwright.config.ts
- Observation : Les deux seuls smoke E2E (
amarre-sandbox,sillage-sandbox) sont rédigés correctement mais ne sont déclenchés par aucun workflow GitHub Actions. Le scénario complet signup→magic-link→survey→logout n’est validé qu’à la main par le développeur qui sait monter la stack docker. Toute régression sur l’orchestration (cookie, redirection, modal Bootstrap) ne sera détectée qu’en aval. - Recommandation : Ajouter un job CI nightly (ou matrix sur PR concernant
apps/amarre|sillage) qui démarre la docker-compose sandbox viapnpm -F @univ-lehavre/atlas-amarre-sandbox start, attend la disponibilité (Mailpit + Appwrite + REDCap) puis exécutepnpm test:smoke. Conservertest.skippour les runs locaux sans docker. - Effort : M
- Vérification : ✓ 2/3 confirmé.
High — Couverture des endpoints SvelteKit très inégale entre apps [endpoint-coverage-uneven]
Section intitulée « High — Couverture des endpoints SvelteKit très inégale entre apps [endpoint-coverage-uneven] »- Locations : apps/ecrin/src/routes/api/v1/, apps/find-an-expert/src/routes/api/v1/, apps/amarre/src/routes/api/v1/
- Observation : Comptages observés :
ecrin14 endpoints / 4 tests,find-an-expert17 / 3,amarre9 / 5. Les endpoints sensibles non testés incluentsurveys/url,account/push,repositories/[id]/contributors,/pulls,/issues,/analysis. La promesse de la Phase 7.2 reste à étendre aux endpoints métier. - Recommandation : Établir un inventaire
+server.ts→ test associé dans chaque app, puis fixer un seuil minimal (chaque endpoint a au moins un test 401 + un test 200 + un test payload malformé). Faire émerger un helper partagécreateRouteEvent({ locals, body, ip }). - Effort : M
- Vérification : ✓ 2/3 confirmé.
D. Lint, format et style
Section intitulée « D. Lint, format et style »Résumé. Le socle (flat configs partagés, presets par catégorie, lefthook strict, ESLint security/unicorn/functional/typescript-strict) est riche et bien pensé. Mais l’usage réel est en décalage : aucun preset Svelte strict en apps, des paquets sandbox sans script lint, des warnings ESLint jamais bloquants, et un répertoire src exclusif qui laisse scripts/tests/fixtures hors format:check.
Forces observées.
- Architecture du config partagé claire et factorisée :
config/shared-config/eslint/expose 4 presets (typescript/svelte/svelteRelaxed/scripts) avecarchitectureCategoryenforcement. - Hooks lefthook complets et non bypassables (ADR 0015).
- Durcissement sécurité Svelte explicitement listé et commenté dans config/shared-config/eslint/svelte.js pour compenser la non-couverture .svelte par CodeQL.
- Règles strictes activées pour les libs TS :
strict-boolean-expressions,no-floating-promises,consistent-type-imports,switch-exhaustiveness-check. - Le script scripts/lint-staged.mjs groupe les fichiers stagés par package root pour préserver
tsconfigRootDir.
Findings.
| # | Titre | Sévérité | Confiance | Effort | Vérifié | Locations |
|---|---|---|---|---|---|---|
| 36 | Le preset Svelte strict (Phase 1.1 DevSecOps) n’est utilisé par aucune app | High | High | S | ✓ 2/3 | config/shared-config/eslint/svelte.js:226, apps/amarre/eslint.config.js:1 |
| 37 | format:check restreint à src laisse tests, scripts et configs hors contrôle | Medium | High | S | — | packages/auth/package.json, packages/atlas-stats/package.json |
| 38 | 3 sandboxes sur 4 n’ont aucun script lint malgré du code TypeScript | Medium | High | S | — | sandbox/amarre-sandbox/package.json, sandbox/sillage-sandbox/package.json, sandbox/crf-sandbox-core/package.json |
| 39 | Aucun --max-warnings 0 : règles warn silencieuses en CI | Medium | High | XS | — | config/shared-config/eslint/base.js:113 |
| 40 | eslint-comments/require-description désactivé : disables sans justification | Medium | High | XS | — | config/shared-config/eslint/base.js:9 |
High — Le preset Svelte strict n’est utilisé par aucune app [svelte-strict-preset-unused]
Section intitulée « High — Le preset Svelte strict n’est utilisé par aucune app [svelte-strict-preset-unused] »- Locations : config/shared-config/eslint/svelte.js:226, apps/amarre/eslint.config.js:1, apps/atlas-dashboard/eslint.config.js, apps/crf-dashboard/eslint.config.js, apps/ecrin/eslint.config.js, apps/find-an-expert/eslint.config.js, apps/sillage/eslint.config.js, ui/atlas-ui/eslint.config.js
- Observation : Les 6 apps et
ui/atlas-uiconsomment toutessvelteRelaxed. Le presetsveltestrict — qui activesvelte/no-target-blank,svelte/button-has-type,svelte/no-svelte-internal,svelte/no-reactive-functions, etc., et qui est documenté comme la mesure compensatoire pour CodeQL — n’est appliqué nulle part. - Recommandation : Soit migrer chaque app vers
svelte()(avec exceptions documentées), soit fusionner le bloc DevSecOps Phase 1.1 danssvelteRelaxed. Documenter le choix dans un ADR. - Effort : S
- Vérification : ✓ 2/3 confirmé.
E. Workspaces et builds
Section intitulée « E. Workspaces et builds »Résumé. Les builds des paquets publiables reposent sur tsc ou svelte-package, déclenchés via turborepo. Les pipelines sont fonctionnels mais quelques paquets n’exposent pas de script build cohérent, et le cache turbo n’inclut pas tous les inputs pertinents.
Forces observées.
- Turbo configure les dépendances de tâche (
build^,test^,lint) de manière cohérente. - Les paquets publiables exposent
exports,files,typesselon les bonnes pratiques.
Findings.
| # | Titre | Sévérité | Confiance | Effort | Vérifié | Locations |
|---|---|---|---|---|---|---|
| 41 | Inputs turbo.json n’incluent pas tous les configs (vitest, eslint) | Low | Medium | S | — | turbo.json |
F. Documentation dans le code et paquets
Section intitulée « F. Documentation dans le code et paquets »Résumé. Les commentaires JSDoc sont présents mais inégaux. Les README de paquets publics ont des longueurs variables ; le finding « squelettes » a été rejeté par vérification (cf annexe A).
Forces observées.
- Certains paquets exposent une vraie doc inline (auth, validators, crf-core).
- ADR vivants et systématiquement référencés dans le code.
Findings.
| # | Titre | Sévérité | Confiance | Effort | Vérifié | Locations |
|---|---|---|---|---|---|---|
| 42 | Commentaires TODO sans owner ni date | Low | Medium | S | — | sources multiples |
G. Documentation VitePress (site)
Section intitulée « G. Documentation VitePress (site) »Résumé. Le site VitePress est en place, structuré et publié. Quelques pages restent à étoffer mais le runbook incident-response (initialement signalé comme vide pour les contacts admins) a été rejeté en vérification.
Forces observées.
- Navigation cohérente, ADR indexés.
Findings.
| # | Titre | Sévérité | Confiance | Effort | Vérifié | Locations |
|---|---|---|---|---|---|---|
| 43 | Quelques pages « TBD » dans le menu | Low | Medium | S | — | docs/ |
H. Documentation racine et onboarding
Section intitulée « H. Documentation racine et onboarding »Résumé. Le README racine et les CLAUDE.md sont à jour. Les prérequis système exhaustifs et la remédiation knip ont été rejetés en vérification (cf annexe A).
Forces observées.
- README racine pointe clairement vers la doc VitePress.
Findings.
| # | Titre | Sévérité | Confiance | Effort | Vérifié | Locations |
|---|---|---|---|---|---|---|
| 44 | Quickstart contributeur dispersé entre README et docs/ | Low | Medium | S | — | README.md |
I. Reproductibilité (lockfile, versions Node, Docker)
Section intitulée « I. Reproductibilité (lockfile, versions Node, Docker) »Résumé. pnpm-lock.yaml versionné, .nvmrc présent, Dockerfiles reproductibles, sandboxes utilisent docker-compose figés.
Forces observées.
- ADR 0004 sur volumes anonymes sillage-sandbox documenté.
Findings.
| # | Titre | Sévérité | Confiance | Effort | Vérifié | Locations |
|---|---|---|---|---|---|---|
| 45 | Pas de pin SHA pour les actions GitHub | Low | Medium | S | — | .github/workflows/ |
J. Gouvernance et processus (ADR, Changesets, conventional commits)
Section intitulée « J. Gouvernance et processus (ADR, Changesets, conventional commits) »Résumé. 19 ADR vivants, Changesets configuré, commit lints actifs via lefthook.
Forces observées.
- ADR systématiquement référencé dans le code et les hooks d’audit.
Findings.
| # | Titre | Sévérité | Confiance | Effort | Vérifié | Locations |
|---|---|---|---|---|---|---|
| 46 | Quelques ADR décrivent un état historique périmé (cf finding 3) | Medium | High | XS | — | docs/decisions/0011-paquets-internes-private.md |
K. CI/CD et hooks git
Section intitulée « K. CI/CD et hooks git »Résumé. Lefthook strict (ADR 0015), workflows GitHub Actions structurés. Mais le rapport coverage n’est pas branché (finding 14), aucun job E2E (finding 25).
Forces observées.
- Hooks non bypassables, audits security/licenses/lockfile/test/cpd/knip en pre-push.
Findings.
| # | Titre | Sévérité | Confiance | Effort | Vérifié | Locations |
|---|---|---|---|---|---|---|
| 47 | Caching CI Node/pnpm non optimisé | Low | Medium | S | — | .github/workflows/ci.yml |
L. Dépendances et licences
Section intitulée « L. Dépendances et licences »Résumé. Licences validées en pre-push, deps régulièrement mises à jour. Quelques dérogations documentées (ADR 0019).
Forces observées.
pnpm auditetlicense-checkeractifs.
Findings.
| # | Titre | Sévérité | Confiance | Effort | Vérifié | Locations |
|---|---|---|---|---|---|---|
| 48 | Quelques deps en ^ larges sur paquets publiables | Low | Medium | S | — | sources multiples |
M. Langages, TS strictness et Svelte 5 runes
Section intitulée « M. Langages, TS strictness et Svelte 5 runes »Résumé. TypeScript strict activé, runes Svelte 5 utilisées. Le finding sur exactOptionalPropertyTypes / noUncheckedIndexedAccess a été rejeté en vérification.
Forces observées.
- Migration Svelte 5 effective dans toutes les apps.
Findings.
| # | Titre | Sévérité | Confiance | Effort | Vérifié | Locations |
|---|---|---|---|---|---|---|
| 49 | Quelques any résiduels dans paquets historiques | Low | Medium | S | — | sources multiples |
N. Architecture frontend et UI partagée
Section intitulée « N. Architecture frontend et UI partagée »Résumé. ui/atlas-ui partage 22 composants Svelte mais sans tests (cf finding 21).
Forces observées.
- Storybook en place pour preview.
Findings.
| # | Titre | Sévérité | Confiance | Effort | Vérifié | Locations |
|---|---|---|---|---|---|---|
| 50 | Theming dispersé entre apps consommatrices | Low | Medium | M | — | ui/atlas-ui |
O. Architecture backend (services, handlers, Effect)
Section intitulée « O. Architecture backend (services, handlers, Effect) »Résumé. services/crf (Hono + Effect) bien structuré mais sous-couvert en tests (cf finding 16).
Forces observées.
- Pattern Effect-handler factorisé et réutilisé.
Findings.
| # | Titre | Sévérité | Confiance | Effort | Vérifié | Locations |
|---|---|---|---|---|---|---|
| 51 | Pas de standardisation handler→app entre amarre/ecrin/find-an-expert | Medium | Medium | M | — | sources multiples |
P. Sécurité applicative
Section intitulée « P. Sécurité applicative »Résumé. Rate-limit, validation Zod, anti-XSS partiel. Le finding « microservice CRF sans authentification ni CORS restreint » a été rejeté en vérification (auth gérée en amont).
Forces observées.
- Gitleaks en pre-commit, CodeQL configuré,
secrets:scanen CI.
Findings.
| # | Titre | Sévérité | Confiance | Effort | Vérifié | Locations |
|---|---|---|---|---|---|---|
| 52 | Anti-XSS testé sur 1 endpoint (cf finding 29) | Medium | High | S | — | apps/find-an-expert/src/routes/api/v1/institutions/search/server.test.ts |
| 53 | CSP headers non systématiques entre apps | Medium | Medium | M | — | sources multiples |
Q. Performance et poids
Section intitulée « Q. Performance et poids »Résumé. Bundles raisonnables, code splitting effectif. Le finding « test files shipped to npm » a été rejeté en vérification.
Forces observées.
filescorrectement défini dans lespackage.jsonpubliables.
Findings.
| # | Titre | Sévérité | Confiance | Effort | Vérifié | Locations |
|---|---|---|---|---|---|---|
| 54 | Pas de budget bundle ni de regression check | Low | Medium | M | — | .github/workflows/ |
R. Observabilité
Section intitulée « R. Observabilité »Résumé. Logs structurés via crf-logs. Le finding « no audit log on auth actions » a été rejeté en vérification.
Forces observées.
crf-logsfactorisé et testé à 100%.
Findings.
| # | Titre | Sévérité | Confiance | Effort | Vérifié | Locations |
|---|---|---|---|---|---|---|
| 55 | Pas de tracing distribué entre services/apps | Low | Medium | L | — | sources multiples |
| 56 | Métriques d’erreur non agrégées côté ops | Low | Medium | M | — | sources multiples |
S. Accessibilité et i18n
Section intitulée « S. Accessibilité et i18n »Résumé. A11y partielle (composants Bootstrap), i18n non systématique.
Forces observées.
- Quelques helpers ARIA dans
ui/atlas-ui.
Findings.
| # | Titre | Sévérité | Confiance | Effort | Vérifié | Locations |
|---|---|---|---|---|---|---|
| 57 | Pas de test a11y automatisé (axe-core absent) | Medium | High | M | — | ui/atlas-ui |
| 58 | i18n non décidée (pas d’ADR, pas de framework) | Low | Medium | L | — | sources multiples |
T. DX et onboarding contributeur
Section intitulée « T. DX et onboarding contributeur »Résumé. README et CLAUDE.md à jour. Les findings « prérequis système non documentés » et « knip casse main » ont été rejetés en vérification.
Forces observées.
- Scripts pnpm cohérents et turbo bien configuré.
Findings.
| # | Titre | Sévérité | Confiance | Effort | Vérifié | Locations |
|---|---|---|---|---|---|---|
| 59 | Pas de devcontainer / GitHub Codespaces config | Low | Medium | M | — | .devcontainer |
U. Schémas et migrations (Appwrite, REDCap)
Section intitulée « U. Schémas et migrations (Appwrite, REDCap) »Résumé. Bootstrap Appwrite et REDCap automatisés via sandbox. Le finding « data dictionaries gitignored » a été rejeté en vérification (fixtures versionnées).
Forces observées.
sandbox/crf-sandbox/tests/contract/fixtures versionnées (projects.json).
Findings.
| # | Titre | Sévérité | Confiance | Effort | Vérifié | Locations |
|---|---|---|---|---|---|---|
| 60 | Migrations Appwrite : pas de versioning explicite côté repo | Medium | Medium | M | — | sandbox/amarre-sandbox |
V. Cohérence inter-apps déployées
Section intitulée « V. Cohérence inter-apps déployées »Résumé. Les apps partagent packages/auth et ui/atlas-ui, mais quelques divergences subsistent. Le finding « auth response envelope divergent » a été rejeté en vérification.
Forces observées.
- Factory
createAuthServicepartagée entre amarre, ecrin, sillage, find-an-expert.
Findings.
| # | Titre | Sévérité | Confiance | Effort | Vérifié | Locations |
|---|---|---|---|---|---|---|
| 61 | Schémas OpenAPI non testés contre les implémentations (cf finding 30) | Medium | High | M | — | sources multiples |
Plan de remédiation
Section intitulée « Plan de remédiation »1. Quick wins (XS / S) — à intégrer dans la prochaine PR
Section intitulée « 1. Quick wins (XS / S) — à intégrer dans la prochaine PR »- A. Architecture : findings 1 (ajouter règle cli↔cli), 2 (check
private:true), 3 (mettre à jour ADR 0011), 6 (supprimer ou complétercrf-sandbox-core), 7 (trancher convention nommage), 9 (élargir scanner aux.svelte.ts), 10 (ADR sandbox deps), 11 (check exports config/), 12 (nommageatlas-stats). - B. Couverture : findings 13 (seuils plancher), 14 (brancher
coverage:reporten CI), 15 (mode--strict), 18 (resserrer seuils amarre), 22 (étendre include atlas-stats), 23 (sandbox seuils), 24 (exclure logos). - C. Tests : findings 27 (supprimer wrapper-only), 29 (helper anti-XSS), 31 (seuils CLI), 32 (
__resetRateLimitForTests), 33 (introduire fast-check), 34 (config vitest mort), 35 (describe.configure({ retries: 1 })). - D. Lint : findings 36 (preset Svelte strict), 37 (
format:checkracine), 38 (lint scripts sandboxes), 39 (--max-warnings 0), 40 (require-description). - J. Gouvernance : finding 46 (ADR 0011 mise à jour).
2. Chantiers moyens (M) — à planifier dans le mois
Section intitulée « 2. Chantiers moyens (M) — à planifier dans le mois »- A. Architecture : findings 4 (extraire
crf-openapien lib), 5 (migrercitation-validateprompts verscli/biblio), 8 (scanner imports relatifs cross-workspace). - B. Couverture : findings 16 (couverture
services/crf), 17 (seuils ecrin), 19 (élargir include find-an-expert). - C. Tests : findings 25 (job CI E2E nightly), 26 (inventaire endpoint→test), 28 (introduire MSW), 30 (étendre pattern
_openapi). - O. Backend : finding 51 (standardiser handler→app).
- P. Sécurité : finding 53 (CSP systématique).
- S. A11y : finding 57 (axe-core).
- U. Schémas : finding 60 (versioning Appwrite).
- V. Cohérence : finding 61 (validation OpenAPI cross-app).
3. Chantiers lourds (L / XL) — à arbitrer ou laisser sine die
Section intitulée « 3. Chantiers lourds (L / XL) — à arbitrer ou laisser sine die »- B. Couverture : findings 20 (tests pour 4 CLI sans test), 21 (tests pour
ui/atlas-ui). - N. UI : finding 50 (consolidation theming).
- R. Observabilité : finding 55 (tracing distribué).
- S. A11y/i18n : finding 58 (décision i18n).
- T. DX : finding 59 (devcontainer).
A. Findings rejetés par vérification
Section intitulée « A. Findings rejetés par vérification »| Dimension | id | Titre | Sévérité initiale | Rejet |
|---|---|---|---|---|
| Tests multi-niveaux | integration-tests-are-mocked-units | Le projet integration de sillage est presque entièrement composé d’unit tests mockés | High | 2/3 |
| Lint, format et style | prettier-root-config-ignore-shared-base | Le .prettierrc racine n’hérite pas du shared base | High | 3/3 |
| Documentation paquets | readmes-squelettes-paquets-publics | 14 README de paquets publics réduits à 5 lignes | High | 2/3 |
| Documentation paquets | absence-tsdoc-paquets-publies | Plusieurs paquets publics ont 0 commentaire JSDoc | High | 2/3 |
| Documentation VitePress | contacts-admins-infra-vide | Runbook incident-response : contacts admins infra non renseignés | High | 2/3 |
| Sécurité applicative | crf-service-no-authentication | Microservice services/crf exposé sans authentification ni CORS restreint | Critical | 2/3 |
| Langages / TS | base-tsconfig-missing-strict-extras | base.json n’active ni exactOptionalPropertyTypes ni noUncheckedIndexedAccess | High | 3/3 |
| Performance | test-files-shipped-to-npm | Fichiers *.test.js et *.test.d.ts publiés dans le tarball npm | High | 2/3 |
| Observabilité | no-audit-log-on-auth-actions | Aucun audit log des actions auth sensibles | High | 2/3 |
| DX onboarding | prerequis-systeme-non-documentes | Prérequis système (Node 24, pnpm, gitleaks, Docker) non documentés | High | 2/3 |
| DX onboarding | knip-casse-main-bloque-push | knip échoue sur main : pre-push bloque sans piste de remédiation | High | 2/3 |
| Schémas et migrations | data-dictionaries-gitignored-no-canonical-source | Dictionnaires REDCap hors Git, pas de référence canonique | High | 3/3 |
| Cohérence inter-apps | auth-response-envelope-divergent | Enveloppe de réponse JSON divergente entre find-an-expert et amarre/ecrin | High | 2/3 |
B. Métriques observées
Section intitulée « B. Métriques observées »A. Architecture & structure
- Workspaces totaux : 38
- Catégories : 8 (apps, assets, packages, services, cli, ui, config, sandbox)
- Résultat
audit:structure: passed - ADR : 19
- Apps
private:true: 6 / Sandboxprivate:true: 4 - Dérogations connues :
atlas-crf-openapi(no -cli suffix),atlas-citation-validate(@clack/promptsen deps),ui/atlas-ui(private + peerDeps svelte) - Règles non checkées :
private:true(ADR 0011), cli↔cli deps (ADR 0008), structure thin CLI, exportsconfig/, imports dynamiques, imports relatifs cross-workspace
B. Couverture de tests
- Paquets avec vitest config : 33
- Paquets sans test : 5
- Paquets avec seuils à 0 : 10
- Couverture agrégée : 90 / 82,7 / 90,4 / 90,4 (statements/branches/functions/lines)
- Fichiers à 0% silencieusement exclus : 84
- Apps avec couverture réelle la plus basse :
find-an-expert62/42/44/62,ecrin75,5/63,8/67,9/75,8,amarre74,9/74,6/58,8/75,4 - CLI sans aucun fichier de test :
biblio,citation,atlas-stats,crf-stats - Paquets sans vitest :
ui/atlas-ui,cli/logos,sandbox/amarre-sandbox,sandbox/crf-sandbox-core,sandbox/sillage-sandbox services/crfseuils déclarés : 17 / 14 / 24 / 18coverage:reportinvoqué en CI : non
C. Tests multi-niveaux
- Fichiers de tests : 177
- Fichiers avec
vi.mock: 60 - Configs Playwright : 2 (E2E en CI : 0)
- Configs vitest : 30
- Endpoints ecrin : 14 (4 testés)
- Endpoints find-an-expert : 17 (3 testés)
- Endpoints amarre : 9 (5 testés)
- CLI avec seuil à 0 : 7 / 9
- Usage MSW : 0
- Tests XSS : 1 fichier
C. Pour aller plus loin (ADR pertinents)
Section intitulée « C. Pour aller plus loin (ADR pertinents) »- A. Architecture : docs/decisions/0002-monorepo-huit-categories.md, docs/decisions/0008-clis-thins-logique-dans-packages.md, docs/decisions/0011-paquets-internes-private.md, docs/decisions/0019-derogations-workspace-audit.md
- I. Reproductibilité : docs/decisions/0004-volumes-anonymes-sillage-sandbox.md
- K. CI/CD : docs/decisions/0015-hooks-git-lefthook-jamais-bypass.md
- Index complet : docs/decisions/