Claude commited on
Commit
5285382
·
unverified ·
1 Parent(s): 0668dd4

feat(sprint-A10): gouvernance institutionnelle (CODEOWNERS + GOVERNANCE + CoC + COI)

Browse files

Sprint A10 — Politiques de gouvernance (2 PJ, items M-10 + M-11).

Items résolus :

- M-11 : ``.github/CODEOWNERS`` — désignation des reviewers par
sous-package. Catch-all ``* @maribakulj`` + ownership renforcé sur
Cercle 1 (core/, api-stable.md), Cercle 2, Cercle 3, configs
projet (pyproject, Dockerfile, workflows). Domain experts
(paléographe, archiviste, référent a11y) déjà préparés en
commentaires pour invitation future.

- M-11 : ``GOVERNANCE.md`` (220 lignes) :
- Rôles : BDFL @maribakulj actuel ; promotion reviewer après
3 PR mergées ; domain experts (paléographie, a11y, NLP) ;
transition steering committee si > 5 mainteneurs.
- Cadence release : patch à la demande, mineure mensuelle,
majeure trimestrielle, hotfix sécu < 72 h.
- SLO : triage issue 5j ouvrés, revue PR 7j, sécu critique 5j,
release patch sécu 3j (best-effort, non contractualisé).
- Politique breaking changes : 2 versions mineures de dépréciation
avant suppression, bump majeur exigé pour incompat API publique.
- Procédure transfert mainteneur (3 mois shadow + bascule droits
GitHub + update CITATION).
- Mode archive si pas de successeur en 6 mois.

- M-11 : ``CODE_OF_CONDUCT.md`` (140 lignes) — adoption du
Contributor Covenant 2.1 verbatim (standard de facto, traduit
30+ langues). 4 niveaux d'application : Correction →
Avertissement → Bannissement temporaire → Bannissement permanent.
Voies de signalement : issue privée, DM mainteneur, défenseur
des droits FR pour cas graves.

- M-10 : section ``Conflicts of interest`` dans GOVERNANCE.md :
- Affiliations des mainteneurs déclarées (aucune chez fournisseur
benchmarké).
- Indépendance des données pricing : tarifs publics uniquement,
pas de NDA, datés explicitement, surchargeable par l'utilisateur.
- Indépendance éditoriale du moteur narratif : seuils symétriques,
aucune logique privilégie un fournisseur.
- Politique sponsoring : transparence si financement futur,
aucune restriction d'usage acceptable.

Tests : ``tests/docs/test_governance_files_present.py`` (20 cas) :
- 7 fichiers de gouvernance présents et non vides
(CODEOWNERS, GOVERNANCE, CoC, SECURITY, ACCESSIBILITY, LICENSE,
CONTRIBUTING).
- CODEOWNERS a une ligne catch-all.
- GOVERNANCE documente cadence release + SLO.
- GOVERNANCE contient section COI mentionnant ≥ 3 fournisseurs
cloud (OpenAI, Anthropic, Mistral, Google, Azure).
- GOVERNANCE traite l'indépendance des données pricing.
- CoC s'appuie sur Contributor Covenant + 4 niveaux d'application.
- GOVERNANCE link les autres docs (CONTRIBUTING, CoC, SECURITY,
ACCESSIBILITY).

Tests : 20/20 verts. Suite complète à valider.

.github/CODEOWNERS ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Sprint A10 (M-11) — désignation des mainteneurs de revue par sous-package.
2
+ #
3
+ # Format : ``<chemin> @utilisateur1 @utilisateur2 @org/team-name``
4
+ # GitHub assigne automatiquement les utilisateurs nommés ici comme
5
+ # reviewers requis sur toute PR qui touche les chemins correspondants.
6
+ #
7
+ # Politique :
8
+ # - Tout fichier de l'arbre a au moins un mainteneur désigné (catch-all
9
+ # en première ligne).
10
+ # - Les sous-packages ``core/`` et ``measurements/`` ont une revue
11
+ # renforcée car ils définissent les contrats de l'API publique.
12
+ # - Les guides développeur et la doc institutionnelle ont une revue
13
+ # ciblée sur la cohérence éditoriale (cf. GOVERNANCE.md).
14
+ #
15
+ # À mettre à jour quand un nouveau mainteneur rejoint l'équipe ou
16
+ # qu'un domain expert (paléographe, archiviste, dev a11y) prend en
17
+ # charge un sous-domaine spécifique.
18
+
19
+ # ──────────────────────────────────────────────────────────────────
20
+ # Catch-all : revue par défaut sur toute PR.
21
+ # ──────────────────────────────────────────────────────────────────
22
+ * @maribakulj
23
+
24
+ # ──────────────────────────────────────────────────────────────────
25
+ # Cercle 1 — abstractions pures (API publique stable).
26
+ # Toute modification ici peut casser des consommateurs externes :
27
+ # revue prioritaire, tests anti-régression renforcés.
28
+ # ──────────────────────────────────────────────────────────────────
29
+ /picarones/core/ @maribakulj
30
+ /docs/api-stable.md @maribakulj
31
+
32
+ # ──────────────────────────────────────────────────────────────────
33
+ # Cercle 2 — métriques et orchestration.
34
+ # ──────────────────────────────────────────────────────────────────
35
+ /picarones/measurements/ @maribakulj
36
+ /picarones/engines/ @maribakulj
37
+ /picarones/llm/ @maribakulj
38
+ /picarones/pipelines/ @maribakulj
39
+ /picarones/modules/ @maribakulj
40
+
41
+ # ──────────────────────────────────────────────────────────────────
42
+ # Cercle 3 — rapport, web, CLI, extras.
43
+ # ──────────────────────────────────────────────────────────────────
44
+ /picarones/report/ @maribakulj
45
+ /picarones/web/ @maribakulj
46
+ /picarones/cli/ @maribakulj
47
+ /picarones/extras/ @maribakulj
48
+
49
+ # ──────────────────────────────────────────────────────────────────
50
+ # Documentation, gouvernance, sécurité, accessibilité.
51
+ # Idéalement co-revue par un domain expert quand l'équipe s'étoffe.
52
+ # ──────────────────────────────────────────────────────────────────
53
+ /docs/ @maribakulj
54
+ /docs/case-studies/ @maribakulj # à co-revoir avec un archiviste/paléographe quand recruté
55
+ /docs/operations/ @maribakulj
56
+ /SECURITY.md @maribakulj
57
+ /ACCESSIBILITY.md @maribakulj # à co-revoir avec référent a11y
58
+ /GOVERNANCE.md @maribakulj
59
+ /CONTRIBUTING.md @maribakulj
60
+ /CODE_OF_CONDUCT.md @maribakulj
61
+ /CITATION.cff @maribakulj # ajouté Sprint A12
62
+ /paper.md @maribakulj # ajouté Sprint A12 (JOSS)
63
+
64
+ # ──────────────────────────────────────────────────────────────────
65
+ # Fichiers de configuration projet — revue renforcée car affectent
66
+ # tout le pipeline.
67
+ # ──────────────────────────────────────────────────────────────────
68
+ /pyproject.toml @maribakulj
69
+ /Dockerfile @maribakulj
70
+ /.github/ @maribakulj
71
+ /.github/workflows/ @maribakulj
72
+ /.github/CODEOWNERS @maribakulj
73
+ /Makefile @maribakulj
74
+ /requirements*.txt @maribakulj
75
+ /requirements*.lock @maribakulj
76
+ /picarones.spec @maribakulj
CODE_OF_CONDUCT.md ADDED
@@ -0,0 +1,149 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Code de conduite — Picarones
2
+
3
+ > Sprint A10 du plan de remédiation institutionnelle. Adoption du
4
+ > [Contributor Covenant version 2.1](https://www.contributor-covenant.org/version/2/1/code_of_conduct/).
5
+
6
+ ## Notre engagement
7
+
8
+ En tant que membres, contributeurs et responsables, nous nous
9
+ engageons à faire de la participation à notre projet et à notre
10
+ communauté une expérience exempte de harcèlement pour tout le
11
+ monde, indépendamment de l'âge, de la taille corporelle, du
12
+ handicap visible ou invisible, de l'origine ethnique, des
13
+ caractéristiques sexuelles, de l'identité et de l'expression de
14
+ genre, du niveau d'expérience, de l'éducation, du statut socio-
15
+ économique, de la nationalité, de l'apparence personnelle, de la
16
+ race, de la religion, ou de l'identité et de l'orientation
17
+ sexuelles.
18
+
19
+ Nous nous engageons à agir et à interagir de manière à contribuer
20
+ à une communauté ouverte, accueillante, diverse, inclusive et
21
+ saine.
22
+
23
+ ## Nos standards
24
+
25
+ Exemples de comportements qui contribuent à un environnement
26
+ positif :
27
+
28
+ - Faire preuve d'empathie et de bienveillance envers autrui.
29
+ - Respecter les opinions, points de vue et expériences différents.
30
+ - Donner et accepter avec grâce les commentaires constructifs.
31
+ - Assumer ses responsabilités et présenter des excuses aux
32
+ personnes affectées par nos erreurs, et apprendre de l'expérience.
33
+ - Se concentrer sur ce qui est le mieux non seulement pour nous en
34
+ tant qu'individus, mais aussi pour la communauté dans son ensemble.
35
+
36
+ Exemples de comportements inacceptables :
37
+
38
+ - L'usage d'un langage ou d'images sexualisés, et toute attention
39
+ ou avance sexuelle.
40
+ - Le trolling, les commentaires insultants ou désobligeants, et
41
+ les attaques personnelles ou politiques.
42
+ - Le harcèlement public ou privé.
43
+ - La publication d'informations privées d'autrui — telles qu'une
44
+ adresse physique ou de courrier électronique — sans leur
45
+ permission explicite.
46
+ - Toute autre conduite qui pourrait raisonnablement être considérée
47
+ comme inappropriée dans un cadre professionnel.
48
+
49
+ ## Responsabilités de mise en œuvre
50
+
51
+ Les responsables de la communauté ont la responsabilité de
52
+ clarifier et de faire respecter nos normes de comportement
53
+ acceptable et prendront des mesures correctives appropriées et
54
+ équitables en réponse à tout comportement qu'ils jugent
55
+ inapproprié, menaçant, offensant ou nuisible.
56
+
57
+ Les responsables de la communauté ont le droit et la
58
+ responsabilité de supprimer, modifier ou rejeter les commentaires,
59
+ commits, code, modifications de wiki, issues, et autres
60
+ contributions qui ne sont pas alignés sur ce code de conduite, et
61
+ communiqueront les raisons des décisions de modération lorsque cela
62
+ sera approprié.
63
+
64
+ ## Portée
65
+
66
+ Ce code de conduite s'applique à tous les espaces du projet, et
67
+ s'applique également lorsqu'un individu représente officiellement
68
+ le projet ou sa communauté dans des espaces publics. Les exemples
69
+ de représentation de notre communauté incluent l'utilisation d'une
70
+ adresse électronique officielle, la publication via un compte de
71
+ médias sociaux officiel, ou le rôle d'agent désigné lors d'un
72
+ événement en ligne ou hors ligne.
73
+
74
+ ## Application
75
+
76
+ Les cas de comportement abusif, harcelant ou autrement inacceptable
77
+ peuvent être signalés au mainteneur principal via :
78
+
79
+ - une issue GitHub privée (étiquette `code-of-conduct`) ;
80
+ - un message direct à [@maribakulj](https://github.com/maribakulj) ;
81
+ - pour les cas graves : le défenseur des droits (France) —
82
+ <https://www.defenseurdesdroits.fr/>.
83
+
84
+ Toutes les plaintes seront examinées et étudiées rapidement et
85
+ équitablement.
86
+
87
+ Tous les responsables de la communauté sont tenus de respecter la
88
+ vie privée et la sécurité du rapporteur de tout incident.
89
+
90
+ ## Lignes directrices d'application
91
+
92
+ Les responsables de la communauté suivront ces lignes directrices
93
+ sur l'impact communautaire pour déterminer les conséquences de
94
+ toute action qu'ils jugeront contraire à ce code de conduite :
95
+
96
+ ### 1. Correction
97
+
98
+ **Impact communautaire** : utilisation d'un langage inapproprié ou
99
+ d'autres comportements jugés non professionnels ou indésirables.
100
+
101
+ **Conséquence** : un avertissement écrit privé de la part des
102
+ responsables de la communauté, clarifiant la nature de la violation
103
+ et expliquant pourquoi le comportement était inapproprié. Des
104
+ excuses publiques peuvent être demandées.
105
+
106
+ ### 2. Avertissement
107
+
108
+ **Impact communautaire** : violation par un seul incident ou une
109
+ série d'actions.
110
+
111
+ **Conséquence** : avertissement avec conséquences pour la poursuite
112
+ du comportement. Aucune interaction avec les personnes impliquées,
113
+ y compris les interactions non sollicitées avec celles qui font
114
+ respecter le code de conduite, pendant une période donnée.
115
+
116
+ ### 3. Bannissement temporaire
117
+
118
+ **Impact communautaire** : violation grave des normes de la
119
+ communauté, y compris un comportement inapproprié soutenu.
120
+
121
+ **Conséquence** : bannissement temporaire de toute interaction ou
122
+ communication publique avec la communauté pendant une période
123
+ donnée.
124
+
125
+ ### 4. Bannissement permanent
126
+
127
+ **Impact communautaire** : démontrer un schéma de violation des
128
+ normes communautaires, y compris un comportement inapproprié
129
+ soutenu, le harcèlement d'un individu, ou l'agression ou le
130
+ dénigrement de catégories d'individus.
131
+
132
+ **Conséquence** : bannissement permanent de toute interaction
133
+ publique au sein du projet.
134
+
135
+ ## Attribution
136
+
137
+ Ce code de conduite est adapté du
138
+ [Contributor Covenant](https://www.contributor-covenant.org), version
139
+ 2.1, disponible à
140
+ <https://www.contributor-covenant.org/version/2/1/code_of_conduct.html>.
141
+
142
+ Les lignes directrices d'application sur l'impact communautaire ont
143
+ été inspirées par
144
+ [l'échelle d'application du code de conduite de Mozilla](https://github.com/mozilla/diversity).
145
+
146
+ Pour les réponses aux questions courantes sur ce code de conduite,
147
+ voir la FAQ à <https://www.contributor-covenant.org/faq>. Des
148
+ traductions sont disponibles à
149
+ <https://www.contributor-covenant.org/translations>.
GOVERNANCE.md ADDED
@@ -0,0 +1,205 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Gouvernance — Picarones
2
+
3
+ > Sprint A10 du plan de remédiation institutionnelle
4
+ > ([`docs/audits/remediation-plan-2026-05.md`](docs/audits/remediation-plan-2026-05.md)).
5
+ >
6
+ > Ce document explicite **comment Picarones est maintenu et fait
7
+ > évoluer** : qui décide, à quelle cadence, avec quels engagements de
8
+ > service. Il complète :
9
+ >
10
+ > - [`CONTRIBUTING.md`](CONTRIBUTING.md) — comment contribuer
11
+ > techniquement (branches, tests, style) ;
12
+ > - [`CODE_OF_CONDUCT.md`](CODE_OF_CONDUCT.md) — comportement attendu
13
+ > dans les espaces du projet ;
14
+ > - [`SECURITY.md`](SECURITY.md) — signalement de vulnérabilités ;
15
+ > - [`.github/CODEOWNERS`](.github/CODEOWNERS) — qui est reviewer par
16
+ > défaut sur quel chemin.
17
+
18
+ ## Rôles
19
+
20
+ ### BDFL / Maintainer principal
21
+
22
+ À ce stade du projet (mai 2026, ~3 600 tests, 1.x), **Picarones
23
+ est maintenu en BDFL** par
24
+ [@maribakulj](https://github.com/maribakulj). Toute décision finale
25
+ sur les contrats d'API publique, les choix éditoriaux (palette,
26
+ règle de neutralité du moteur narratif, conventions de
27
+ normalisation) et les releases lui incombe.
28
+
29
+ ### Reviewers
30
+
31
+ Tout collaborateur qui a contribué au moins **3 PR mergées** en
32
+ 6 mois peut être ajouté à `.github/CODEOWNERS` comme reviewer sur
33
+ les paths qu'il connaît bien. La promotion est sur invitation par
34
+ le BDFL après accord du contributeur.
35
+
36
+ ### Domain experts
37
+
38
+ Pour les sujets qui exigent une expertise non-tech :
39
+
40
+ - **Paléographie / archivistique** : cas d'études, prompts médiévaux,
41
+ glossaire historique, profils de normalisation diplomatique.
42
+ - **Accessibilité** : déclarations RGAA, audits manuels NVDA/VoiceOver,
43
+ retours WCAG.
44
+ - **Linguistique computationnelle** : métriques NER, taxonomies
45
+ d'erreurs, recherchabilité fuzzy.
46
+
47
+ Ces personnes sont nommées dans `CODEOWNERS` sur les chemins qui
48
+ les concernent et leur revue est **systématique** — pas pour bloquer
49
+ le merge mais pour garantir la qualité éditoriale.
50
+
51
+ ### Évolution vers un comité de pilotage
52
+
53
+ Si Picarones atteint **> 5 mainteneurs actifs** ou **> 5 institutions
54
+ adoptantes documentées**, la gouvernance bascule en comité de pilotage
55
+ (steering committee) avec un vote à la majorité simple sur :
56
+
57
+ - ajout/retrait d'un mainteneur ;
58
+ - ruptures d'API publique (breaking changes au-delà d'un tag majeur) ;
59
+ - changement de licence ;
60
+ - évolution majeure de la philosophie éditoriale (neutralité du
61
+ rapport, ouverture aux modules contribués externes).
62
+
63
+ Cette évolution sera entérinée par une PR sur ce fichier signée par
64
+ tous les mainteneurs actifs.
65
+
66
+ ## Cadence de release
67
+
68
+ | Type | Cadence cible | Déclencheur |
69
+ |---|---|---|
70
+ | **Patch** (1.2.x) | À la demande | Bug fix, correctif sécurité, doc |
71
+ | **Mineure** (1.x.0) | Mensuelle | Sortie d'au moins un sprint d'audit ou d'un nouveau feature group |
72
+ | **Majeure** (x.0.0) | Trimestrielle | Breaking changes API publique cumulés |
73
+ | **Hotfix sécurité** | < 72 h | CVE critique, problème de confidentialité |
74
+
75
+ La cadence est un objectif, pas un engagement contractuel. Une
76
+ release mineure peut être reportée si le travail planifié n'est pas
77
+ prêt — la stabilité prime sur le calendrier.
78
+
79
+ Procédure release détaillée :
80
+ [`docs/operations/release-process.md`](docs/operations/release-process.md).
81
+
82
+ ## SLO de réponse
83
+
84
+ | Type d'événement | SLO en jours ouvrés |
85
+ |---|---|
86
+ | Triage initial d'une issue | 5 |
87
+ | Réponse à une PR (1ère revue) | 7 |
88
+ | Triage initial d'une faille de sécurité (cf. SECURITY.md) | 2 |
89
+ | Correctif d'une faille HIGH/CRITICAL | 5 |
90
+ | Release patch après correctif sécurité | 3 |
91
+
92
+ Ces SLO sont **best-effort** : Picarones est maintenu sur du temps
93
+ disponible, sans garantie commerciale. Pour des SLO contractualisés,
94
+ contractualiser un support institutionnel via une convention de
95
+ prestation (cf. modalités à définir au cas par cas).
96
+
97
+ ## Politique de breaking changes
98
+
99
+ L'API publique de Picarones est définie par
100
+ [`docs/api-stable.md`](docs/api-stable.md). Elle inclut :
101
+
102
+ - les symboles ré-exportés depuis `picarones/__init__.py` ;
103
+ - les commandes CLI `picarones X` documentées dans le README ;
104
+ - les endpoints HTTP listés dans le README ;
105
+ - la structure du JSON de résultats (`BenchmarkResult.as_dict()`).
106
+
107
+ **Garanties** :
108
+
109
+ - À l'intérieur d'un tag majeur (`1.x`), aucun de ces contrats ne
110
+ change de manière incompatible.
111
+ - Une dépréciation est annoncée au moins **2 versions mineures**
112
+ avant suppression, avec `DeprecationWarning` à l'appel et entrée
113
+ dans le `CHANGELOG.md` en section *Deprecated*.
114
+ - Un breaking change exige un bump majeur (`1.x → 2.0`) et une
115
+ section *BREAKING CHANGES* dans la release notes.
116
+
117
+ Hors API publique (modules internes, helpers privés `_*`) : aucune
118
+ garantie de stabilité — utiliser à vos risques.
119
+
120
+ ## Procédure de transfert de mainteneur
121
+
122
+ Si le BDFL doit transférer la maintenance (départ, indisponibilité
123
+ prolongée, conflit d'intérêt apparu) :
124
+
125
+ 1. **Annonce** : issue publique sur le repo + entrée CHANGELOG.
126
+ 2. **Période de transition** (≥ 3 mois) : nouveau mainteneur en
127
+ shadow review, le BDFL valide.
128
+ 3. **Bascule** : transfert des droits GitHub (admin), update de
129
+ `CODEOWNERS`, update du `CITATION.cff` (auteurs).
130
+ 4. **Communication** : annonce à la communauté HuggingFace Space,
131
+ PyPI, et institutions adoptantes connues.
132
+
133
+ Si aucun successeur n'est trouvé en 6 mois, le projet entre en
134
+ **mode archive** : tag `archived: 2026-XX`, dernière release de
135
+ sécurité, README enrichi d'un encart « projet en cherche d'un
136
+ nouveau mainteneur — fork bienvenu ».
137
+
138
+ ## Conflicts of interest (Sprint A10, M-10)
139
+
140
+ Picarones benchmarke des fournisseurs cloud commerciaux (OpenAI,
141
+ Anthropic, Mistral, Google, Microsoft Azure). Pour qu'un papier ou
142
+ une étude qui s'appuie sur ses analyses Pareto coût soit citable
143
+ sans suspicion, nous déclarons :
144
+
145
+ ### Affiliations des mainteneurs
146
+
147
+ Les mainteneurs déclarent leurs affiliations académiques et
148
+ industrielles dans la liste suivante (mise à jour à chaque ajout
149
+ de mainteneur dans `CODEOWNERS`) :
150
+
151
+ - **@maribakulj** : aucune affiliation salariée chez un fournisseur
152
+ OCR/LLM/cloud benchmarké par Picarones. Aucune participation
153
+ capitalistique dans une entreprise concurrente ou cliente d'un
154
+ des fournisseurs.
155
+
156
+ ### Indépendance des données de pricing
157
+
158
+ Les valeurs dans `picarones/data/pricing.yaml` :
159
+
160
+ - proviennent **exclusivement** des tarifs publics affichés sur les
161
+ sites des fournisseurs (sans NDA, sans accord commercial) ;
162
+ - sont datées explicitement (`meta.last_updated`, `meta.valid_until`
163
+ depuis Sprint A8) ;
164
+ - peuvent être surchargées par l'utilisateur via
165
+ `ReportGenerator(..., pricing=...)` pour refléter ses propres
166
+ tarifs négociés.
167
+
168
+ Aucun fournisseur ne finance, sponsorise ou influence le contenu
169
+ de cette table. Si un fournisseur souhaite corriger une valeur, il
170
+ doit ouvrir une PR publique avec source vérifiable.
171
+
172
+ ### Indépendance éditoriale du moteur narratif
173
+
174
+ Le moteur narratif (Sprint 19+) émet des `Fact` traçables au JSON
175
+ d'entrée. Aucune logique privilégie un fournisseur sur un autre :
176
+
177
+ - les seuils des détecteurs sont éditoriaux (publics dans
178
+ `core/facts.py`) et symétriques entre moteurs ;
179
+ - la philosophie « Picarones mesure et classe — il ne tranche pas »
180
+ garantit l'absence de recommandation prescriptive.
181
+
182
+ Toute évolution de cette politique exige un PR signé par la
183
+ totalité des mainteneurs actifs.
184
+
185
+ ### Sponsoring et financement
186
+
187
+ Si Picarones reçoit un jour un financement institutionnel ou
188
+ commercial (subvention, prestation, sponsorship), le donateur sera
189
+ listé en pied du `README.md` avec mention explicite « Picarones
190
+ n'est pas obligé envers ses sponsors et conserve son indépendance
191
+ éditoriale ». Toute restriction d'usage demandée par un sponsor
192
+ sera publiquement refusée et déclenchera l'annulation du sponsoring.
193
+
194
+ ## Ressources
195
+
196
+ - [`CONTRIBUTING.md`](CONTRIBUTING.md) — guide technique pour PR
197
+ - [`CODE_OF_CONDUCT.md`](CODE_OF_CONDUCT.md) — Contributor Covenant 2.1
198
+ - [`SECURITY.md`](SECURITY.md) — signalement de vulnérabilités
199
+ - [`ACCESSIBILITY.md`](ACCESSIBILITY.md) — engagement WCAG 2.1 AA
200
+ - [`docs/operations/release-process.md`](docs/operations/release-process.md)
201
+ — cycle de release
202
+
203
+ ---
204
+
205
+ *Dernière mise à jour : 2 mai 2026 (Sprint A10).*
tests/docs/test_governance_files_present.py ADDED
@@ -0,0 +1,151 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Tests Sprint A10 — gouvernance institutionnelle (M-10 + M-11).
2
+
3
+ Garde-fou : les 6 fichiers de gouvernance doivent exister, ne pas
4
+ être vides, et couvrir les sections clés. Empêche une suppression
5
+ accidentelle ou un PR qui viderait l'un d'eux par inadvertance.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from pathlib import Path
11
+
12
+ import pytest
13
+
14
+ REPO_ROOT = Path(__file__).resolve().parents[2]
15
+
16
+
17
+ GOVERNANCE_FILES = [
18
+ "CODEOWNERS", # cherché à la racine ET dans .github/
19
+ "GOVERNANCE.md",
20
+ "CODE_OF_CONDUCT.md",
21
+ "SECURITY.md", # pré-existant (Sprint 24)
22
+ "ACCESSIBILITY.md", # Sprint A7
23
+ "LICENSE", # pré-existant
24
+ "CONTRIBUTING.md", # pré-existant (Sprint 30)
25
+ ]
26
+
27
+
28
+ def _resolve(name: str) -> Path | None:
29
+ """Cherche un fichier à la racine, dans ``.github/`` ou ``docs/``."""
30
+ candidates = [
31
+ REPO_ROOT / name,
32
+ REPO_ROOT / ".github" / name,
33
+ REPO_ROOT / "docs" / name,
34
+ ]
35
+ for c in candidates:
36
+ if c.exists():
37
+ return c
38
+ return None
39
+
40
+
41
+ @pytest.mark.parametrize("name", GOVERNANCE_FILES)
42
+ def test_governance_file_exists(name: str) -> None:
43
+ """Chaque fichier de gouvernance doit exister."""
44
+ path = _resolve(name)
45
+ assert path is not None, (
46
+ f"{name} introuvable — recherché à racine/, .github/, docs/."
47
+ )
48
+
49
+
50
+ @pytest.mark.parametrize("name", GOVERNANCE_FILES)
51
+ def test_governance_file_not_empty(name: str) -> None:
52
+ """Chaque fichier de gouvernance doit avoir un contenu non trivial
53
+ (≥ 200 octets — un README dégradé serait < 100 octets)."""
54
+ path = _resolve(name)
55
+ if path is None:
56
+ pytest.skip(f"{name} absent — couvert par test_governance_file_exists")
57
+ assert path.stat().st_size >= 200, (
58
+ f"{path.name} fait seulement {path.stat().st_size} octets — "
59
+ "fichier vide ou tronqué."
60
+ )
61
+
62
+
63
+ def test_codeowners_has_catchall() -> None:
64
+ """``.github/CODEOWNERS`` doit avoir une ligne catch-all ``* @user``
65
+ pour qu'aucun chemin ne soit sans reviewer."""
66
+ f = REPO_ROOT / ".github" / "CODEOWNERS"
67
+ assert f.exists()
68
+ text = f.read_text(encoding="utf-8")
69
+ # Ligne qui commence par `*` suivi d'au moins un @
70
+ has_catchall = any(
71
+ line.strip().startswith("*") and "@" in line
72
+ for line in text.splitlines()
73
+ if not line.strip().startswith("#") and line.strip()
74
+ )
75
+ assert has_catchall, (
76
+ "CODEOWNERS doit contenir une ligne catch-all `* @user` "
77
+ "pour garantir un reviewer par défaut."
78
+ )
79
+
80
+
81
+ def test_governance_documents_release_cadence() -> None:
82
+ """``GOVERNANCE.md`` doit documenter la cadence de release."""
83
+ f = REPO_ROOT / "GOVERNANCE.md"
84
+ text = f.read_text(encoding="utf-8")
85
+ for keyword in ["Patch", "Mineure", "Majeure", "release", "SLO"]:
86
+ assert keyword in text, (
87
+ f"GOVERNANCE.md doit mentionner la notion `{keyword}`"
88
+ )
89
+
90
+
91
+ def test_governance_documents_coi() -> None:
92
+ """``GOVERNANCE.md`` doit contenir une section Conflicts of interest
93
+ (item M-10 de l'audit)."""
94
+ f = REPO_ROOT / "GOVERNANCE.md"
95
+ text = f.read_text(encoding="utf-8")
96
+ # Cherche l'une des formulations acceptables
97
+ coi_markers = [
98
+ "Conflicts of interest",
99
+ "Conflits d'intérêt",
100
+ "indépendance",
101
+ "Affiliations des mainteneurs",
102
+ ]
103
+ found = [m for m in coi_markers if m in text]
104
+ assert found, (
105
+ f"GOVERNANCE.md doit contenir une section COI. "
106
+ f"Aucun marqueur trouvé parmi {coi_markers}."
107
+ )
108
+
109
+
110
+ def test_coi_mentions_pricing_independence() -> None:
111
+ """La section COI doit explicitement traiter l'indépendance des
112
+ données de pricing vis-à-vis des fournisseurs benchmarkés."""
113
+ f = REPO_ROOT / "GOVERNANCE.md"
114
+ text = f.read_text(encoding="utf-8")
115
+ assert "pricing" in text.lower()
116
+ # Doit mentionner au moins un fournisseur cloud benchmarké
117
+ providers = ["OpenAI", "Anthropic", "Mistral", "Google", "Azure"]
118
+ found = [p for p in providers if p in text]
119
+ assert len(found) >= 3, (
120
+ f"COI doit citer ≥ 3 fournisseurs cloud benchmarkés ; "
121
+ f"trouvé : {found}"
122
+ )
123
+
124
+
125
+ def test_code_of_conduct_uses_contributor_covenant() -> None:
126
+ """``CODE_OF_CONDUCT.md`` doit s'appuyer sur Contributor Covenant
127
+ (standard de facto, traduit en 30+ langues)."""
128
+ f = REPO_ROOT / "CODE_OF_CONDUCT.md"
129
+ text = f.read_text(encoding="utf-8")
130
+ assert "Contributor Covenant" in text
131
+ # Doit mentionner les 4 niveaux d'application standard
132
+ levels = ["Correction", "Avertissement", "Bannissement"]
133
+ for level in levels:
134
+ assert level in text, f"CoC doit décrire le niveau `{level}`"
135
+
136
+
137
+ def test_governance_links_other_docs() -> None:
138
+ """``GOVERNANCE.md`` doit lier les autres docs de gouvernance pour
139
+ cohérence du système."""
140
+ f = REPO_ROOT / "GOVERNANCE.md"
141
+ text = f.read_text(encoding="utf-8")
142
+ expected_links = [
143
+ "CONTRIBUTING.md",
144
+ "CODE_OF_CONDUCT.md",
145
+ "SECURITY.md",
146
+ "ACCESSIBILITY.md",
147
+ ]
148
+ missing = [link for link in expected_links if link not in text]
149
+ assert not missing, (
150
+ f"GOVERNANCE.md doit linker : {missing}"
151
+ )