designwithai-mcp 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +153 -0
- package/dist/connectors.js +76 -0
- package/dist/decisions.js +67 -0
- package/dist/embeddings.js +98 -0
- package/dist/evals.js +172 -0
- package/dist/finding-identity.js +229 -0
- package/dist/handbook.js +98 -0
- package/dist/paths.js +62 -0
- package/dist/profile.js +79 -0
- package/dist/reviews.js +99 -0
- package/dist/server.js +1136 -0
- package/dist/skills.js +57 -0
- package/dist/state.js +48 -0
- package/handbook-embeddings.json +1 -0
- package/package.json +46 -0
- package/skills/EVALS.md +122 -0
- package/skills/README.md +59 -0
- package/skills/_shared/delivery-modes.md +12 -0
- package/skills/_shared/execution-contract.md +43 -0
- package/skills/_shared/issue-schema.md +25 -0
- package/skills/_shared/severity-model.md +16 -0
- package/skills/analyze-figma/evals/cases.json +140 -0
- package/skills/analyze-url/evals/cases.json +136 -0
- package/skills/better-accessibility/SKILL.md +141 -0
- package/skills/better-accessibility/evals/cases.json +83 -0
- package/skills/better-components/SKILL.md +125 -0
- package/skills/better-components/evals/cases.json +77 -0
- package/skills/better-consistency/SKILL.md +125 -0
- package/skills/better-consistency/evals/cases.json +77 -0
- package/skills/better-forms/SKILL.md +135 -0
- package/skills/better-forms/evals/cases.json +78 -0
- package/skills/better-interface/SKILL.md +143 -0
- package/skills/better-interface/evals/cases.json +80 -0
- package/skills/better-layout/SKILL.md +132 -0
- package/skills/better-layout/evals/cases.json +81 -0
- package/skills/better-navigation/SKILL.md +170 -0
- package/skills/better-navigation/evals/cases.json +131 -0
- package/skills/better-performance/SKILL.md +195 -0
- package/skills/better-performance/evals/cases.json +114 -0
- package/skills/better-responsive/SKILL.md +123 -0
- package/skills/better-responsive/evals/cases.json +77 -0
- package/skills/better-states/SKILL.md +129 -0
- package/skills/better-states/evals/cases.json +78 -0
- package/skills/better-writing/SKILL.md +138 -0
- package/skills/better-writing/evals/cases.json +77 -0
- package/skills/review-ai-product/SKILL.md +231 -0
- package/skills/review-ai-product/evals/cases.json +120 -0
- package/skills/review-data-security/SKILL.md +210 -0
- package/skills/review-data-security/evals/cases.json +138 -0
- package/skills/review-figma-hygiene/SKILL.md +186 -0
- package/skills/review-figma-hygiene/evals/cases.json +158 -0
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: review-data-security
|
|
3
|
+
description: Juge la sécurité des données d'un produit — modèle d'autorisation table par table, secrets et frontière client/serveur, rôle privilégié, intégrité, sauvegardes et récupération. Multi-stack (Supabase, Firebase, Convex, Postgres + ORM). Invocable seul, sans lancer d'audit de production complet.
|
|
4
|
+
version: 1.0
|
|
5
|
+
category: data-security-expert
|
|
6
|
+
role: Data & security expert
|
|
7
|
+
execution_mode: authorization-and-data-audit
|
|
8
|
+
supports: [ai-builder, traditional, hybrid]
|
|
9
|
+
inputs: [code, backend, schema, env, bundle]
|
|
10
|
+
outputs: [authorization-matrix, secret-inventory, recovery-status, risk-register]
|
|
11
|
+
invoked_by: [review-ai-product]
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# /review-data-security
|
|
15
|
+
|
|
16
|
+
**Rôle** : Data & security expert · **Mode par défaut** : audit d'autorisation et de données.
|
|
17
|
+
|
|
18
|
+
Voir le [contrat commun](../_shared/execution-contract.md), le [schéma de problème](../_shared/issue-schema.md) (préfixe `SEC`), le [modèle de sévérité](../_shared/severity-model.md) et la [matrice des modes](../_shared/delivery-modes.md).
|
|
19
|
+
|
|
20
|
+
## Mission
|
|
21
|
+
|
|
22
|
+
Répondre à une seule question, table par table : **qui peut lire quoi, qui peut écrire quoi, et qu'est-ce qui l'empêche ?** Le skill juge ce qu'il a inspecté. Il ne certifie jamais un produit « sécurisé » : il produit une matrice d'autorisation vérifiée, la liste nominative de ce qu'il n'a pas pu voir, et les risques ouverts que cela laisse.
|
|
23
|
+
|
|
24
|
+
Ce skill porte **la grille sécurité et données de la maison**. `review-ai-product` l'exécute au lieu de la redécrire ; toute évolution de la grille se fait ici, et nulle part ailleurs.
|
|
25
|
+
|
|
26
|
+
## Déclenchement
|
|
27
|
+
|
|
28
|
+
Avant d'ouvrir un produit à de vrais utilisateurs, après une génération IA qui a créé des tables toute seule, à la reprise d'un produit dont personne n'a écrit les règles, ou quand une donnée d'un utilisateur est apparue chez un autre.
|
|
29
|
+
|
|
30
|
+
- **Entrées minimales pour produire un verdict** : la liste des tables ou collections, les règles d'autorisation appliquées côté serveur, et la liste des variables d'environnement (noms, pas valeurs).
|
|
31
|
+
- **Pour un audit complet** : le dépôt, le schéma, deux comptes de test, et l'accès en lecture à la configuration du backend.
|
|
32
|
+
- **Sans ces entrées**, le skill rend `BLOCKED` et nomme ce qui manque. Voir *Refus honnête*.
|
|
33
|
+
|
|
34
|
+
## Principe de sécurité
|
|
35
|
+
|
|
36
|
+
- **Lecture seule, toujours.** Ne jamais modifier une règle d'autorisation, une donnée, un secret, une configuration d'authentification ou un déploiement. Un audit qui répare en passant efface la preuve du problème.
|
|
37
|
+
- **Ne jamais faire figurer une valeur de secret** dans le rapport. On cite le **nom** de la variable, son emplacement et sa portée — jamais son contenu, même partiel, même expiré.
|
|
38
|
+
- **Ne pas fabriquer la preuve.** Si la vérification demande de créer un compte de test ou d'exécuter une requête, la demander explicitement plutôt que de la simuler.
|
|
39
|
+
|
|
40
|
+
## La grille — six invariants, un seul propriétaire
|
|
41
|
+
|
|
42
|
+
Le backend change d'un produit à l'autre ; ce qu'il faut prouver ne change pas. Identifier d'abord le stack, puis chercher la forme concrète que chaque invariant y prend.
|
|
43
|
+
|
|
44
|
+
| Invariant | Ce qu'il faut prouver | Forme selon le stack |
|
|
45
|
+
|---|---|---|
|
|
46
|
+
| **Modèle d'autorisation** | Une règle appliquée **côté serveur** décide qui lit et écrit quoi ; le client ne peut pas la contourner. | Supabase : RLS activée **et** policies par table. Firebase : security rules Firestore / Storage. Convex : contrôle d'identité dans chaque query et mutation publique. Postgres + Drizzle / Prisma / Neon : autorisation dans la couche serveur (route handler, service), jamais dans le composant. |
|
|
47
|
+
| **Rôle privilégié serveur** | La clé qui outrepasse les règles n'existe jamais dans le bundle client. | Supabase : `service_role`. Firebase : Admin SDK, compte de service. Convex : actions et fonctions internes. SQL direct : `DATABASE_URL`. |
|
|
48
|
+
| **Frontière client / serveur** | Toute écriture sensible passe par du code serveur qui valide ; le navigateur ne fait pas autorité. | Edge / cloud functions, route handlers, server actions, fonctions Convex, API interne. |
|
|
49
|
+
| **Secrets et environnements** | Les secrets sont hors du code, distincts par environnement, et rotables sans redéploiement du client. | Variables serveur du host, secrets du BaaS, `.env` non commité. Vérifier ce que le préfixe public du framework expose. |
|
|
50
|
+
| **Migrations et intégrité** | Le schéma est reproductible et les invariants de données tiennent dans le datastore, pas seulement dans l'écran. | SQL : migrations commitées, clés étrangères, `unique`, `check`, `not null`. NoSQL : règles de validation, forme documentée, réconciliation. |
|
|
51
|
+
| **Sauvegarde et récupération** | Les données perdues peuvent revenir, et quelqu'un l'a déjà fait. | Sauvegarde automatique du BaaS, restauration à un instant donné, export vérifié. Une sauvegarde jamais restaurée n'est pas une sauvegarde. |
|
|
52
|
+
|
|
53
|
+
## Le cœur : l'autorisation, table par table
|
|
54
|
+
|
|
55
|
+
Une ligne par table ou collection, sans exception. Une table oubliée est une table non protégée.
|
|
56
|
+
|
|
57
|
+
| Colonne | Ce qu'on y met |
|
|
58
|
+
|---|---|
|
|
59
|
+
| **Table** | Le nom exact. |
|
|
60
|
+
| **Donnée** | Ce qu'elle contient, en clair : « les relevés d'un compte », « les jetons d'accès Figma ». |
|
|
61
|
+
| **Personnelle ?** | Oui / non. Une donnée personnelle sans règle est bloquante, pas majeure. |
|
|
62
|
+
| **Lecture** | Qui peut lire. « Le propriétaire », « tout compte connecté », « tout le monde ». |
|
|
63
|
+
| **Écriture** | Qui peut créer, modifier, supprimer. Détailler si les trois diffèrent. |
|
|
64
|
+
| **Règle appliquée où** | Le fichier et la ligne. « RLS `releves_select_own`, migration 2026…08.sql:14 ». |
|
|
65
|
+
| **Preuve** | Comment ça a été vérifié : inspection du fichier, test à deux comptes, lecture de la console. |
|
|
66
|
+
| **Verdict** | Vérifié / non vérifié / faille. |
|
|
67
|
+
|
|
68
|
+
### La preuve qui compte
|
|
69
|
+
|
|
70
|
+
- **Déclaré n'est pas appliqué.** Une policy écrite dans une migration jamais jouée en production ne protège rien. Vérifier l'état **du serveur qui répond**, pas seulement le dépôt.
|
|
71
|
+
- **RLS activée n'est pas RLS suffisante.** Une table avec RLS mais sans policy est fermée ; une table avec une policy `using (true)` est ouverte. Lire la policy, pas l'interrupteur.
|
|
72
|
+
- **Le test à deux comptes est la seule preuve directe.** Compte A crée une donnée, compte B tente de la lire et de la modifier. Tout le reste est de la preuve croisée, à marquer comme telle.
|
|
73
|
+
- **La lecture n'est pas l'écriture.** Beaucoup de produits protègent `select` et laissent `update` ouvert. Chaque opération se vérifie séparément.
|
|
74
|
+
- **Le stockage de fichiers est un datastore.** Buckets, dossiers et URLs signées suivent la même grille que les tables.
|
|
75
|
+
|
|
76
|
+
### Ce qui finit dans le bundle
|
|
77
|
+
|
|
78
|
+
- Repérer le préfixe public du framework — `NEXT_PUBLIC_`, `VITE_`, `REACT_APP_`, `PUBLIC_`, `EXPO_PUBLIC_` — et **lister tout ce qu'il exporte**. Ce qui porte ce préfixe est publié, par construction.
|
|
79
|
+
- Distinguer ce qui est **public par conception** (clé anonyme d'un BaaS, identifiant de projet) de ce qui ne doit jamais sortir (rôle privilégié, clé d'API tierce, secret de signature, chaîne de connexion). Traiter une clé anonyme comme une fuite est un faux constat ; il décrédibilise le vrai.
|
|
80
|
+
- Une clé anonyme n'est sûre **que si** les règles serveur tiennent. C'est le même constat vu des deux bouts : sans autorisation, la clé publique suffit à tout lire.
|
|
81
|
+
- Chercher aussi les secrets écrits en dur : code source, fichier commité, note dans une maquette, texte visible d'une page.
|
|
82
|
+
|
|
83
|
+
## Séquence d'audit obligatoire
|
|
84
|
+
|
|
85
|
+
1. **Identifier le stack et l'environnement.** Quel backend, quels environnements existent, lequel répond à l'adresse auditée, et si les données sont réelles ou fictives.
|
|
86
|
+
2. **Inventorier les tables.** Toutes, y compris celles créées par le générateur et jamais relues. Noter celles qui contiennent des données personnelles.
|
|
87
|
+
3. **Lire les règles, une par une.** Pour chaque table : la règle, son fichier, sa ligne. Une table sans règle trouvée est notée comme telle, et la recherche est décrite.
|
|
88
|
+
4. **Vérifier à deux comptes** ce qui peut l'être, en lecture et en écriture séparément.
|
|
89
|
+
5. **Suivre le rôle privilégié.** Où la clé est stockée, quel code s'en sert, et si un chemin non authentifié y mène.
|
|
90
|
+
6. **Ouvrir le bundle client.** Lister ce que le préfixe public expose ; chercher les secrets en dur.
|
|
91
|
+
7. **Vérifier l'intégrité.** Migrations versionnées et jouées, contraintes qui tiennent les invariants, index sur les colonnes qui portent l'autorisation.
|
|
92
|
+
8. **Vérifier la récupération.** Sauvegarde active, fréquence, portée, et **date de la dernière restauration réussie**.
|
|
93
|
+
9. **Produire la matrice**, puis les risques, puis la liste nominative du non vérifié.
|
|
94
|
+
|
|
95
|
+
## La règle de l'absence — le garde-fou de cet axe
|
|
96
|
+
|
|
97
|
+
Ailleurs dans la maison, l'absence ne prouve jamais rien. **Ici, elle prouve parfois** — une table inspectée sans policy est une faille réelle, pas une lacune d'observation. C'est ce qui rend ce skill dangereux, et cette règle non négociable :
|
|
98
|
+
|
|
99
|
+
> **L'absence ne fait preuve que si l'inspection a eu lieu et que son périmètre est écrit.**
|
|
100
|
+
|
|
101
|
+
Concrètement :
|
|
102
|
+
|
|
103
|
+
- « J'ai listé les 14 tables du schéma ; `messages` n'a aucune policy » → **constat**, avec la méthode d'inspection en preuve.
|
|
104
|
+
- « Je n'ai pas vu de policy sur `messages` » sans dire ce qui a été inspecté → **non vérifié**, jamais un constat.
|
|
105
|
+
- « Le dépôt ne contient pas de dossier de migrations » → constat seulement si le dépôt entier a été reçu ; sinon, accès manquant.
|
|
106
|
+
- **Un extrait n'autorise aucune conclusion négative.** Trois fichiers reçus sur un dépôt de deux cents ne disent rien de ce que contiennent les 197 autres.
|
|
107
|
+
- **Ne jamais écrire un constat dont la preuve est un compteur à zéro** ou une liste vide, sans dire d'où vient la liste et ce qu'elle couvre.
|
|
108
|
+
|
|
109
|
+
La contrepartie est aussi stricte : **ne jamais transformer un non-vérifié en rassurance.** « Aucun problème trouvé sur les permissions » est interdit si les permissions n'ont pas été inspectées. Le silence se dit, il ne se déduit pas.
|
|
110
|
+
|
|
111
|
+
## Sévérité sur cet axe
|
|
112
|
+
|
|
113
|
+
Le verdict se **déduit** des sévérités, il ne se décide pas — même règle que partout ailleurs. Ce que cet axe précise, c'est ce qui mérite quel niveau.
|
|
114
|
+
|
|
115
|
+
| Sévérité | Sur cet axe |
|
|
116
|
+
|---|---|
|
|
117
|
+
| **Bloquant** | Une table lisible ou modifiable par n'importe qui sans règle serveur. Un rôle privilégié ou un secret dans le bundle client. Des données personnelles sans contrôle d'accès. Aucune sauvegarde sur des données non reconstructibles. |
|
|
118
|
+
| **Critique** | Une règle présente mais contournable (écriture ouverte, condition portée par le client). Un chemin non authentifié qui atteint le rôle privilégié. Un secret partagé entre production et développement. Une sauvegarde dont la restauration n'a jamais été testée. |
|
|
119
|
+
| **Majeur** | Migrations non versionnées. Contrainte d'intégrité absente là où l'écran seul la tient. Pas d'index sur la colonne qui porte l'autorisation. Aucune rotation possible sans redéploiement. |
|
|
120
|
+
| **Mineur** | Nommage de variables qui rend la portée ambiguë. Table de travail oubliée, vide et sans donnée personnelle. |
|
|
121
|
+
| **Note** | Dette documentée, acceptée, avec sa condition de réévaluation. |
|
|
122
|
+
|
|
123
|
+
**Un seul bloquant suffit à faire `FAIL`.** Aucune pondération par le reste : un produit dont les données sont lisibles par tous n'est pas « bon sauf sur un point ».
|
|
124
|
+
|
|
125
|
+
## Refus honnête
|
|
126
|
+
|
|
127
|
+
Le skill rend **`BLOCKED`** — et aucun autre verdict — quand il ne peut pas voir ce qu'il doit juger. Le refus n'est pas un échec du skill : c'est ce qui empêche un rapport rassurant sur du vide.
|
|
128
|
+
|
|
129
|
+
Dans ce cas, la sortie contient :
|
|
130
|
+
|
|
131
|
+
- **ce qui a été reçu**, nommément ;
|
|
132
|
+
- **ce qui manque**, nommément et sous forme demandable : « la liste des tables », « le contenu du dossier `migrations` », « un second compte de test », « la liste des noms de variables d'environnement du serveur » ;
|
|
133
|
+
- **les risques ouverts que cela laisse**, formulés comme des questions sans réponse, jamais comme des constats.
|
|
134
|
+
|
|
135
|
+
Cas typiques : aucun accès au schéma ; une URL de production sans dépôt ni backend ; un dépôt sans la configuration du BaaS ; des règles fournies sans savoir si elles sont déployées.
|
|
136
|
+
|
|
137
|
+
## Adaptation au mode de delivery
|
|
138
|
+
|
|
139
|
+
- **AI Builder.** Le générateur crée des tables sans qu'on les relise : inventorier d'abord, et supposer qu'aucune n'a de règle tant qu'on ne l'a pas lue. Vérifier les appels directs du navigateur au datastore, et les tables ajoutées entre deux générations.
|
|
140
|
+
- **Classique.** L'autorisation vit dans la couche serveur : vérifier qu'aucune route ne l'a contournée, et que les tests couvrent le refus autant que l'accès.
|
|
141
|
+
- **Hybride.** Le risque est la double porte : une même table atteinte par une route serveur validée **et** par un appel client direct. Les deux chemins se vérifient.
|
|
142
|
+
|
|
143
|
+
## Anti-patterns à refuser
|
|
144
|
+
|
|
145
|
+
- Déclarer un produit sécurisé parce qu'aucun incident n'a eu lieu.
|
|
146
|
+
- Conclure sur les permissions sans les avoir inspectées, dans un sens comme dans l'autre.
|
|
147
|
+
- Traiter la clé anonyme d'un BaaS comme une fuite de secret.
|
|
148
|
+
- Recopier la valeur d'un secret dans le rapport pour « prouver » la fuite.
|
|
149
|
+
- Confondre RLS activée et table protégée.
|
|
150
|
+
- Vérifier la lecture et conclure sur l'écriture.
|
|
151
|
+
- Considérer une sauvegarde comme acquise sans date de restauration réussie.
|
|
152
|
+
- Proposer une migration corrective sans sauvegarde, environnement de test et retour arrière.
|
|
153
|
+
- Rendre un verdict global sur un extrait de dépôt.
|
|
154
|
+
|
|
155
|
+
## Contrat de sortie exact
|
|
156
|
+
|
|
157
|
+
```
|
|
158
|
+
# Verdict
|
|
159
|
+
- Verdict: PASS | PASS WITH FIXES | FAIL | BLOCKED
|
|
160
|
+
- Stack identifié
|
|
161
|
+
- Environnement audité (et s'il porte des données réelles)
|
|
162
|
+
- Risques bloquants, en une ligne chacun
|
|
163
|
+
|
|
164
|
+
# Couverture
|
|
165
|
+
- Accès reçus
|
|
166
|
+
- Méthode d'inspection (ce qui a été lu, et jusqu'où)
|
|
167
|
+
- Vérifications à deux comptes réalisées
|
|
168
|
+
- NON VÉRIFIÉ — liste nominative
|
|
169
|
+
|
|
170
|
+
# Matrice d'autorisation
|
|
171
|
+
Table | Donnée | Personnelle | Lecture | Écriture | Règle appliquée où | Preuve | Verdict
|
|
172
|
+
|
|
173
|
+
# Frontière client / serveur
|
|
174
|
+
- Exposé par le préfixe public
|
|
175
|
+
- Public par conception (et pourquoi c'est acceptable ici)
|
|
176
|
+
- Rôle privilégié : emplacement, code qui s'en sert, chemins d'accès
|
|
177
|
+
- Secrets en dur trouvés (nom et emplacement, jamais la valeur)
|
|
178
|
+
|
|
179
|
+
# Intégrité et récupération
|
|
180
|
+
- Migrations: versionnées / jouées / écart entre dépôt et serveur
|
|
181
|
+
- Contraintes portant les invariants
|
|
182
|
+
- Sauvegarde: portée, fréquence, dernière restauration réussie
|
|
183
|
+
|
|
184
|
+
# Risques
|
|
185
|
+
SEC-XX | Sévérité | Localisation | Preuve | Impact | Correction | Critères | Confiance
|
|
186
|
+
|
|
187
|
+
# Ce qu'il faut pour lever les BLOCKED
|
|
188
|
+
- Accès manquant -> ce qu'il permettrait de juger
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
## Critères de qualité du skill
|
|
192
|
+
|
|
193
|
+
- Chaque table du périmètre a une ligne, ou est nommée dans le non vérifié.
|
|
194
|
+
- Aucun verdict de sécurité n'est rendu sur un axe non inspecté, dans un sens comme dans l'autre.
|
|
195
|
+
- Chaque constat porte un fichier, une ligne ou une manipulation reproductible.
|
|
196
|
+
- Aucune valeur de secret n'apparaît dans la sortie.
|
|
197
|
+
- Le refus est complet : ce qui manque est demandable tel quel.
|
|
198
|
+
- Le verdict se déduit des sévérités et n'est jamais pondéré par le reste du produit.
|
|
199
|
+
|
|
200
|
+
## Exemple d'invocation
|
|
201
|
+
|
|
202
|
+
```
|
|
203
|
+
/review-data-security
|
|
204
|
+
Produit: SaaS B2B en bêta privée
|
|
205
|
+
Stack: Supabase (Postgres + RLS, Storage, Edge Functions)
|
|
206
|
+
Accès: dépôt GitHub, Supabase en lecture seule, deux comptes de test
|
|
207
|
+
Environnement: production, données réelles
|
|
208
|
+
Objectif: décider si 100 utilisateurs peuvent être onboardés
|
|
209
|
+
Contraintes: ne modifier aucune donnée, aucun secret, aucune règle
|
|
210
|
+
```
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"id": "sec-conformant-supabase-tenu",
|
|
4
|
+
"skill": "review-data-security",
|
|
5
|
+
"kind": "conformant",
|
|
6
|
+
"delivery_mode": "hybride",
|
|
7
|
+
"artifact": "MATÉRIAU REÇU\n- Dépôt complet (git clone, 214 fichiers).\n- Projet Supabase en lecture seule (schéma, policies, configuration Auth, Storage).\n- Deux comptes de test fournis : test-a@exemple.fr, test-b@exemple.fr.\n- Liste des noms de variables d'environnement du serveur et du client.\n\nSCHÉMA — 4 tables dans le schéma public\n releves relevés de compteur d'un compte RLS: activée\n compteurs compteurs déclarés par un compte RLS: activée\n profils nom d'affichage et préférences RLS: activée\n invitations invitations envoyées par un compte RLS: activée\n\nPOLICIES, telles que retournées par le serveur\n\n releves_select_own SELECT using (auth.uid() = user_id)\n releves_insert_own INSERT with check (auth.uid() = user_id)\n releves_update_own UPDATE using (auth.uid() = user_id) with check (auth.uid() = user_id)\n releves_delete_own DELETE using (auth.uid() = user_id)\n compteurs_all_own ALL using (auth.uid() = user_id) with check (auth.uid() = user_id)\n profils_select_own SELECT using (auth.uid() = id)\n profils_update_own UPDATE using (auth.uid() = id) with check (auth.uid() = id)\n invitations_select SELECT using (auth.uid() = inviteur_id)\n invitations_insert INSERT with check (auth.uid() = inviteur_id)\n\nVÉRIFICATION À DEUX COMPTES (exécutée)\n A crée un relevé -> id 41. B tente select id 41 -> 0 ligne.\n B tente update id 41 -> 0 ligne affectée. B tente delete id 41 -> 0 ligne affectée.\n Même séquence sur compteurs et profils : aucun accès croisé.\n\nVARIABLES D'ENVIRONNEMENT\n Client (préfixe NEXT_PUBLIC_) : NEXT_PUBLIC_SUPABASE_URL, NEXT_PUBLIC_SUPABASE_ANON_KEY\n Serveur uniquement : SUPABASE_SERVICE_ROLE_KEY, ANTHROPIC_API_KEY, RESEND_API_KEY\n\nRECHERCHE DE SECRETS EN DUR\n grep sur le dépôt (service_role, sk_, secret, BEGIN PRIVATE KEY) : aucune occurrence\n hors .env.example, qui ne contient que des noms et des valeurs factices.\n Bundle client construit (out/) : SUPABASE_SERVICE_ROLE_KEY absent.\n\nRÔLE PRIVILÉGIÉ\n Utilisé dans supabase/functions/ingest-audit/index.ts et analyze-url/index.ts,\n toutes deux derrière une vérification de session (userFromRequest -> 401 sinon).\n Aucune route du client ne l'atteint.\n\nMIGRATIONS\n supabase/migrations/ : 6 fichiers commités. Comparaison avec le schéma du\n serveur : identiques, aucune table hors migration.\n\nCONTRAINTES\n releves.user_id -> auth.users(id) ON DELETE CASCADE, not null\n releves.compteur_id -> compteurs(id), not null\n unique (compteur_id, date_releve)\n index idx_releves_user_id sur user_id\n\nSAUVEGARDE\n Sauvegarde quotidienne automatique, rétention 7 jours, restauration à un\n instant donné activée. Dernière restauration testée le 02/07/2026 sur un\n projet de recette, réussie.",
|
|
8
|
+
"golden": {
|
|
9
|
+
"expected_findings": [],
|
|
10
|
+
"forbidden_findings": [
|
|
11
|
+
"la clé anonyme est exposée dans le bundle client",
|
|
12
|
+
"une clé Supabase se trouve dans le code du navigateur",
|
|
13
|
+
"aucune sauvegarde des données n'est configurée",
|
|
14
|
+
"une table reste sans règle d'autorisation",
|
|
15
|
+
"le rôle privilégié atteint depuis le client",
|
|
16
|
+
"le schéma n'est pas reproductible faute de migrations versionnées"
|
|
17
|
+
],
|
|
18
|
+
"expected_verdict": "PASS",
|
|
19
|
+
"should_refuse": false,
|
|
20
|
+
"note": "Produit tenu, et surtout tenu de façon PROUVÉE : les quatre tables ont leurs policies, la vérification à deux comptes a eu lieu en lecture ET en écriture, le rôle privilégié ne vit que dans des fonctions serveur authentifiées, les migrations correspondent au serveur, la restauration a été testée à une date nommée. Le piège du cas est la clé anonyme dans le bundle : elle y est PAR CONCEPTION et n'est un risque que si les règles serveur cèdent — ce qui n'est pas le cas ici. La rapporter comme une fuite est le faux constat le plus fréquent de cet axe, et il décrédibilise les vrais."
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"id": "sec-conformant-firebase-tenu",
|
|
25
|
+
"skill": "review-data-security",
|
|
26
|
+
"kind": "conformant",
|
|
27
|
+
"delivery_mode": "ai-builder",
|
|
28
|
+
"artifact": "MATÉRIAU REÇU\n- Dépôt complet (git clone, 96 fichiers).\n- Console Firebase en lecture seule : règles Firestore et Storage, App Check, comptes de service.\n- Deux comptes de test fournis.\n- Liste des noms de variables d'environnement.\n\nCOLLECTIONS\n users/{uid} profil du compte\n users/{uid}/dossiers/{id} dossiers d'un compte\n partages/{id} partages explicites entre comptes\n public_config/{id} configuration lue par tout le monde, aucune donnée personnelle\n\nRÈGLES FIRESTORE, telles que déployées\n\n match /users/{uid} {\n allow read, write: if request.auth != null && request.auth.uid == uid;\n match /dossiers/{id} {\n allow read, write: if request.auth != null && request.auth.uid == uid;\n }\n }\n match /partages/{id} {\n allow read: if request.auth != null &&\n (request.auth.uid == resource.data.emetteur || request.auth.uid == resource.data.destinataire);\n allow create: if request.auth != null && request.auth.uid == request.resource.data.emetteur;\n allow update, delete: if request.auth != null && request.auth.uid == resource.data.emetteur;\n }\n match /public_config/{id} { allow read: if true; allow write: if false; }\n\nRÈGLES STORAGE\n match /pieces/{uid}/{fichier} {\n allow read, write: if request.auth != null && request.auth.uid == uid;\n }\n\nVÉRIFICATION À DEUX COMPTES (exécutée)\n B tente de lire users/A et users/A/dossiers : permission refusée.\n B tente d'écrire dans pieces/A/ : permission refusée.\n B lit public_config : autorisé, conforme à l'intention (aucune donnée personnelle).\n\nAPP CHECK\n Activé et appliqué sur Firestore et Storage.\n\nVARIABLES D'ENVIRONNEMENT\n Client : NEXT_PUBLIC_FIREBASE_API_KEY, NEXT_PUBLIC_FIREBASE_PROJECT_ID,\n NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN\n Serveur uniquement : FIREBASE_SERVICE_ACCOUNT_JSON, STRIPE_SECRET_KEY\n\nADMIN SDK\n Initialisé dans app/api/ uniquement (3 route handlers), jamais importé depuis\n un composant client. Recherche d'import côté client : aucune occurrence.\n\nRÈGLES ET INDEXES VERSIONNÉS\n firestore.rules, storage.rules et firestore.indexes.json commités.\n Comparaison avec les règles déployées : identiques.\n\nSAUVEGARDE\n Export planifié quotidien vers un bucket dédié, rétention 30 jours.\n Dernière restauration testée le 14/06/2026 sur un projet de recette, réussie.",
|
|
29
|
+
"golden": {
|
|
30
|
+
"expected_findings": [],
|
|
31
|
+
"forbidden_findings": [
|
|
32
|
+
"la clé API Firebase est exposée dans le bundle",
|
|
33
|
+
"la collection publique est lisible par tout le monde",
|
|
34
|
+
"aucune règle sur le stockage des fichiers",
|
|
35
|
+
"changer de backend pour un outil réputé plus sûr",
|
|
36
|
+
"aucune sauvegarde des données n'est configurée"
|
|
37
|
+
],
|
|
38
|
+
"expected_verdict": "PASS",
|
|
39
|
+
"should_refuse": false,
|
|
40
|
+
"note": "Le même verdict sur un autre stack : la grille doit tenir hors de Supabase. Deux pièges ici. La clé API Firebase est publique par conception — c'est un identifiant de projet, pas un secret ; la traiter comme une fuite est un contresens documenté. Et la collection public_config est lisible par tout le monde INTENTIONNELLEMENT, ce que le matériau dit et que la règle en écriture confirme (write: if false). Un juge qui rapporte une lecture ouverte sans regarder ce qu'elle contient applique la règle sans la comprendre."
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"id": "sec-obvious-generateur-ouvert",
|
|
45
|
+
"skill": "review-data-security",
|
|
46
|
+
"kind": "obvious-defect",
|
|
47
|
+
"delivery_mode": "ai-builder",
|
|
48
|
+
"artifact": "MATÉRIAU REÇU\n- Dépôt synchronisé depuis le générateur (git clone, 78 fichiers).\n- Projet Supabase en lecture seule (schéma, policies, configuration).\n- Un seul compte de test.\n- Liste des noms de variables d'environnement du client et du serveur.\n\nSCHÉMA — 5 tables dans le schéma public\n profils nom, email, téléphone, adresse RLS: DÉSACTIVÉE\n messages messages échangés entre comptes RLS: DÉSACTIVÉE\n commandes commandes et montants RLS: activée\n produits catalogue public RLS: activée\n admin_logs journal d'administration RLS: DÉSACTIVÉE\n\nPOLICIES, telles que retournées par le serveur\n commandes_select_all SELECT using (true)\n produits_select_all SELECT using (true)\n (aucune autre policy sur ce projet)\n\nVÉRIFICATION AVEC LA CLÉ ANONYME (exécutée depuis un navigateur, sans session)\n select * from profils -> 1 284 lignes retournées, avec emails et téléphones.\n update profils set nom = 'test' where id = <un autre compte> -> 1 ligne modifiée.\n select * from messages -> 9 032 lignes retournées.\n select * from commandes -> 4 110 lignes retournées (policy using true).\n\nVARIABLES D'ENVIRONNEMENT\n Client (préfixe VITE_) : VITE_SUPABASE_URL, VITE_SUPABASE_ANON_KEY,\n VITE_SUPABASE_SERVICE_ROLE_KEY, VITE_OPENAI_API_KEY\n Serveur : (aucune)\n\nRECHERCHE DE SECRETS EN DUR\n src/lib/admin.ts:12 — chaîne commençant par « eyJ… » affectée à une constante\n nommée serviceKey, utilisée dans un composant rendu côté navigateur.\n Bundle client construit (dist/) : la valeur de VITE_SUPABASE_SERVICE_ROLE_KEY\n est présente dans dist/assets/index-4f2a.js.\n\nMIGRATIONS\n Aucun dossier de migrations dans le dépôt. Les tables ont été créées depuis\n l'interface du générateur. Aucun historique de schéma.\n\nCONTRAINTES\n Aucune clé étrangère. messages.destinataire_id : colonne texte, sans contrainte.\n Aucun index en dehors des clés primaires.\n\nSAUVEGARDE\n Plan gratuit : aucune sauvegarde automatique, aucune restauration à un instant\n donné. Aucun export manuel trouvé. Les données de production sont réelles.",
|
|
49
|
+
"golden": {
|
|
50
|
+
"expected_findings": [
|
|
51
|
+
"la table profils est lisible et modifiable sans aucune règle serveur",
|
|
52
|
+
"emails et téléphones accessibles depuis le navigateur sans session",
|
|
53
|
+
"la table messages reste ouverte à la lecture de tous",
|
|
54
|
+
"le rôle privilégié service_role est publié par une variable du client",
|
|
55
|
+
"la clé de service se retrouve dans le bundle construit",
|
|
56
|
+
"une clé de service écrite en dur dans un composant rendu côté navigateur",
|
|
57
|
+
"la policy des commandes accepte toute requête sans condition",
|
|
58
|
+
"aucune sauvegarde sur des données de production réelles",
|
|
59
|
+
"aucune migration versionnée : le schéma n'est pas reproductible",
|
|
60
|
+
"aucune clé étrangère ni contrainte tenant l'intégrité des données"
|
|
61
|
+
],
|
|
62
|
+
"forbidden_findings": [
|
|
63
|
+
"la clé anonyme est exposée dans le bundle client",
|
|
64
|
+
"revoir la performance des requêtes",
|
|
65
|
+
"conseiller une réécriture complète du produit plutôt que de fermer les tables ouvertes",
|
|
66
|
+
"changer de générateur pour un autre outil"
|
|
67
|
+
],
|
|
68
|
+
"expected_verdict": "FAIL",
|
|
69
|
+
"should_refuse": false,
|
|
70
|
+
"note": "L'application générée dans son pire état, et tout est PROUVÉ : les requêtes anonymes ont été exécutées et ont rendu 1 284 lignes de données personnelles. L'absence de règle est ici un constat légitime parce que l'inspection a eu lieu et que son périmètre est écrit — c'est la distinction que ce skill doit tenir. Plusieurs bloquants, donc FAIL sans pondération possible. Le piège maintenu : la clé anonyme reste hors constat, même sur un produit catastrophique. Ce qui la rend dangereuse ici, c'est l'absence de règles, et c'est ce constat-là qui est attendu."
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
"id": "sec-subtle-ecriture-ouverte",
|
|
75
|
+
"skill": "review-data-security",
|
|
76
|
+
"kind": "subtle-defect",
|
|
77
|
+
"delivery_mode": "hybride",
|
|
78
|
+
"artifact": "MATÉRIAU REÇU\n- Dépôt complet (git clone, 187 fichiers).\n- Projet Supabase en lecture seule.\n- Deux comptes de test fournis.\n- Liste des noms de variables d'environnement du serveur et du client.\n\nSCHÉMA — 3 tables dans le schéma public\n releves relevés de compteur d'un compte RLS: activée\n compteurs compteurs déclarés par un compte RLS: activée\n profils nom d'affichage RLS: activée\n\nPOLICIES, telles que retournées par le serveur\n releves_select_own SELECT using (auth.uid() = user_id)\n releves_insert_own INSERT with check (auth.uid() = user_id)\n releves_update_any UPDATE using (true) with check (true)\n releves_delete_own DELETE using (auth.uid() = user_id)\n compteurs_all_own ALL using (auth.uid() = user_id) with check (auth.uid() = user_id)\n profils_select_own SELECT using (auth.uid() = id)\n profils_update_own UPDATE using (auth.uid() = id) with check (auth.uid() = id)\n\nVÉRIFICATION À DEUX COMPTES (exécutée)\n A crée un relevé -> id 88. B tente select id 88 -> 0 ligne.\n B tente update id 88 (index et date) -> 1 ligne modifiée, valeurs remplacées.\n B tente delete id 88 -> 0 ligne affectée.\n\nVARIABLES D'ENVIRONNEMENT\n Client (préfixe NEXT_PUBLIC_) : NEXT_PUBLIC_SUPABASE_URL, NEXT_PUBLIC_SUPABASE_ANON_KEY\n Serveur uniquement : SUPABASE_SERVICE_ROLE_KEY, RESEND_API_KEY\n\nRECHERCHE DE SECRETS EN DUR\n Aucune occurrence dans le dépôt ni dans le bundle construit.\n\nRÔLE PRIVILÉGIÉ\n Utilisé dans une seule fonction serveur, derrière vérification de session.\n\nMIGRATIONS\n supabase/migrations/ : 9 fichiers commités.\n Comparaison dépôt / serveur : les deux derniers fichiers (008_index_releves.sql,\n 009_contrainte_unique.sql) ne sont pas appliqués sur le serveur de production.\n\nCONTRAINTES\n releves.user_id -> auth.users(id), not null\n Aucun index sur releves.user_id (l'index est dans la migration non appliquée).\n Aucune contrainte d'unicité sur (compteur_id, date_releve) : la migration\n correspondante n'est pas appliquée non plus. Trois relevés en double observés.\n\nSAUVEGARDE\n Sauvegarde quotidienne automatique, rétention 7 jours.\n Dernière restauration testée le 03/06/2026, réussie.",
|
|
79
|
+
"golden": {
|
|
80
|
+
"expected_findings": [
|
|
81
|
+
"la policy de modification des relevés accepte n'importe quel compte connecté",
|
|
82
|
+
"un compte modifie les relevés d'un autre compte, vérifié à deux comptes",
|
|
83
|
+
"deux migrations commitées ne sont pas appliquées sur le serveur",
|
|
84
|
+
"aucun index sur la colonne qui porte l'autorisation",
|
|
85
|
+
"la contrainte d'unicité manquante laisse passer des relevés en double"
|
|
86
|
+
],
|
|
87
|
+
"forbidden_findings": [
|
|
88
|
+
"prétendre que les relevés ne sont protégés par aucune politique serveur",
|
|
89
|
+
"la clé anonyme est exposée dans le bundle client",
|
|
90
|
+
"aucune sauvegarde des données n'est configurée",
|
|
91
|
+
"affirmer que les profils sont consultables par des comptes tiers"
|
|
92
|
+
],
|
|
93
|
+
"expected_verdict": "PASS WITH FIXES",
|
|
94
|
+
"should_refuse": false,
|
|
95
|
+
"note": "Le produit a l'air tenu et il l'est presque : RLS partout, lecture correctement fermée, secrets propres, sauvegarde restaurée. La faille tient dans un seul mot d'une seule policy — using (true) sur UPDATE — et la vérification à deux comptes la démontre. C'est le cas qui punit le juge qui vérifie la lecture et conclut sur l'écriture. Une seule sévérité critique et trois majeures, donc PASS WITH FIXES : le produit peut avancer après un lot de corrections identifiées."
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
"id": "sec-out-of-scope-lenteur-et-textes",
|
|
100
|
+
"skill": "review-data-security",
|
|
101
|
+
"kind": "out-of-scope",
|
|
102
|
+
"delivery_mode": "hybride",
|
|
103
|
+
"artifact": "MATÉRIAU REÇU\n- Dépôt complet (git clone, 132 fichiers).\n- Projet Supabase en lecture seule.\n- Deux comptes de test fournis.\n- Traces de requêtes du tableau de bord et rapport de build du bundle.\n\nSCHÉMA — 3 tables dans le schéma public\n factures factures d'un compte RLS: activée\n paiements paiements d'un compte RLS: activée\n profils profil du compte RLS: activée\n\nPOLICIES, telles que retournées par le serveur\n factures_all_own ALL using (auth.uid() = user_id) with check (auth.uid() = user_id)\n paiements_all_own ALL using (auth.uid() = user_id) with check (auth.uid() = user_id)\n profils_all_own ALL using (auth.uid() = id) with check (auth.uid() = id)\n\nVÉRIFICATION À DEUX COMPTES (exécutée)\n Aucun accès croisé en lecture, création, modification ni suppression.\n\nVARIABLES D'ENVIRONNEMENT\n Client : NEXT_PUBLIC_SUPABASE_URL, NEXT_PUBLIC_SUPABASE_ANON_KEY\n Serveur uniquement : SUPABASE_SERVICE_ROLE_KEY, STRIPE_SECRET_KEY\n\nRECHERCHE DE SECRETS EN DUR\n Aucune occurrence dans le dépôt ni dans le bundle construit.\n\nMIGRATIONS\n 12 fichiers commités, tous appliqués sur le serveur. Contraintes et index\n présents sur les colonnes d'autorisation.\n\nSAUVEGARDE\n Quotidienne, rétention 30 jours, restauration testée le 21/07/2026, réussie.\n\nTRACES DE REQUÊTES — écran « Mes factures »\n 1 requête liste des factures (24 lignes), puis 24 requêtes de détail paiement,\n une par facture, exécutées en série. Temps total observé : 3,8 s.\n\nRAPPORT DE BUILD\n dist/assets/index-9c31.js : 1,9 Mo non compressé.\n Trois bibliothèques de graphiques différentes incluses.\n\nTEXTES DE L'ÉCRAN\n En cas d'échec du paiement, la page affiche « Une erreur est survenue »\n sans indiquer la cause ni la suite à donner.",
|
|
104
|
+
"golden": {
|
|
105
|
+
"expected_findings": [],
|
|
106
|
+
"forbidden_findings": [
|
|
107
|
+
"vingt-quatre requêtes de détail exécutées en série",
|
|
108
|
+
"le bundle dépasse un méga-octet",
|
|
109
|
+
"trois bibliothèques de graphiques incluses",
|
|
110
|
+
"le message d'erreur ne dit pas quoi faire",
|
|
111
|
+
"revoir la pagination de la liste des factures"
|
|
112
|
+
],
|
|
113
|
+
"expected_verdict": "PASS",
|
|
114
|
+
"should_refuse": false,
|
|
115
|
+
"note": "Backend irréprochable, et le matériau pose sous le nez du juge trois problèmes bien réels qui ne lui appartiennent pas : une cascade de requêtes et un bundle lourd (better-performance), un message d'erreur muet (better-states et better-writing). Sortir du périmètre ici, même en ayant raison sur le fond, échoue le cas : c'est ainsi qu'un juge devient un avis général de plus. Le signaler comme dépendance vers un autre skill est en revanche permis — ce n'est pas un constat."
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
"id": "sec-insufficient-adresse-seule",
|
|
120
|
+
"skill": "review-data-security",
|
|
121
|
+
"kind": "insufficient",
|
|
122
|
+
"delivery_mode": "ai-builder",
|
|
123
|
+
"artifact": "MATÉRIAU REÇU\n- Une adresse de production : https://mon-app-generee.lovable.app\n- Rien d'autre.\n\nCE QUI A ÉTÉ TENTÉ\n Ouverture de la page d'accueil : l'application se dessine dans le navigateur.\n Aucun dépôt fourni, aucun accès au backend, aucun compte de test.\n Aucune liste de variables d'environnement.\n\nCE QUI EST OBSERVABLE DEPUIS LE NAVIGATEUR\n Le bundle client contient une adresse de projet Supabase et une clé commençant\n par « eyJ », dont le contenu décodé indique le rôle « anon ».\n Aucune requête n'a été émise vers le backend : le juge n'a pas de compte.\n\nDEMANDE INITIALE\n « Est-ce que mes données sont sécurisées ? Je veux ouvrir à 200 utilisateurs\n la semaine prochaine. »",
|
|
124
|
+
"golden": {
|
|
125
|
+
"expected_findings": [],
|
|
126
|
+
"forbidden_findings": [
|
|
127
|
+
"aucune règle d'autorisation n'a été trouvée",
|
|
128
|
+
"la base de données n'est pas protégée",
|
|
129
|
+
"aucun secret exposé sur ce produit",
|
|
130
|
+
"les données semblent correctement isolées",
|
|
131
|
+
"une clé Supabase se trouve dans le code du navigateur"
|
|
132
|
+
],
|
|
133
|
+
"expected_verdict": "BLOCKED",
|
|
134
|
+
"should_refuse": true,
|
|
135
|
+
"note": "Une adresse, et une question qui mérite une vraie réponse la semaine prochaine. Le refus est la seule sortie honnête, et ce cas mesure les DEUX façons de le trahir. Trahison par le reproche : conclure que la base est ouverte parce qu'on n'a vu aucune règle — on n'a rien inspecté. Trahison par la rassurance, plus grave encore : « aucun secret exposé », « les données semblent isolées », alors qu'aucune requête n'a été émise. La clé anonyme visible dans le bundle n'est ni l'un ni l'autre : elle est attendue, et sans les règles serveur on ne peut rien en déduire. La sortie attendue nomme ce qui manque, sous une forme que la personne peut fournir."
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
]
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: review-figma-hygiene
|
|
3
|
+
description: Juge l'hygiène d'un fichier Figma comme artefact de travail d'équipe — nommage, structure des pages, auto-layout, composants détachés ou dupliqués, styles locaux, publiés et orphelins. Répond à une seule question : quelqu'un d'autre peut-il reprendre ce fichier ? Ne juge pas la qualité du design.
|
|
4
|
+
version: 1.0
|
|
5
|
+
category: design-file-expert
|
|
6
|
+
role: Design file expert
|
|
7
|
+
execution_mode: file-hygiene-audit
|
|
8
|
+
supports: [traditional, hybrid, ai-builder]
|
|
9
|
+
inputs: [figma, figma-summary]
|
|
10
|
+
outputs: [file-inventory, hygiene-matrix, handover-verdict, risk-register]
|
|
11
|
+
applied_by: [analyze-figma]
|
|
12
|
+
relays: [better-interface, better-consistency, better-writing]
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# /review-figma-hygiene
|
|
16
|
+
|
|
17
|
+
**Rôle** : Design file expert · **Mode par défaut** : audit d'hygiène de fichier.
|
|
18
|
+
|
|
19
|
+
Voir le [contrat commun](../_shared/execution-contract.md), le [schéma de problème](../_shared/issue-schema.md) (préfixe `FIG`), le [modèle de sévérité](../_shared/severity-model.md) et la [matrice des modes](../_shared/delivery-modes.md).
|
|
20
|
+
|
|
21
|
+
## Mission
|
|
22
|
+
|
|
23
|
+
Une seule question, et tout en découle : **quelqu'un d'autre peut-il reprendre ce fichier ?** Un développeur qui doit l'intégrer, un designer qui arrive dans l'équipe, l'auteur lui-même dans six mois. Un fichier Figma n'est pas une image : c'est un artefact de travail que d'autres personnes vont ouvrir, lire et modifier. Ce skill juge s'il tient ce rôle.
|
|
24
|
+
|
|
25
|
+
Ce skill porte **la grille d'hygiène Figma de la maison**. La fonction serveur `analyze-figma` applique cette grille — un seul savoir, deux exécutants. Toute évolution se fait ici.
|
|
26
|
+
|
|
27
|
+
## Périmètre strict
|
|
28
|
+
|
|
29
|
+
**Analyse** : nommage des calques, cadres, composants et pages ; structure des pages ; auto-layout là où le contenu varie ; instances contre copies dessinées à la main ; composants détachés ; doublons de même usage ; styles locaux, publiés et orphelins ; valeurs en dur ; reprenabilité d'ensemble.
|
|
30
|
+
|
|
31
|
+
**N'analyse pas** — et le dit plutôt que de déborder :
|
|
32
|
+
|
|
33
|
+
| Hors périmètre | À qui c'est |
|
|
34
|
+
|---|---|
|
|
35
|
+
| La qualité du design : hiérarchie visuelle, équilibre, contraste, pertinence des choix. | `better-interface` et ses experts. |
|
|
36
|
+
| Le rendu réel et les interactions du prototype. | Non observable depuis le fichier. |
|
|
37
|
+
| Le style de l'écriture : libellés vagues, ton, clarté d'un message. | `better-writing`. |
|
|
38
|
+
| La cohérence du produit fini, écran par écran. | `better-consistency`. |
|
|
39
|
+
|
|
40
|
+
**La frontière avec l'écriture, écrite une fois pour toutes** : un faux texte (« Lorem ipsum », « texte ici », « TODO ») est un problème d'**hygiène** — le fichier n'est pas fini et ce texte partira au développement. Un libellé réel mais vague (« Soumettre », « OK ») est un problème d'**écriture**, et il relève de `better-writing`. Le premier se rapporte ici, le second se signale et se passe.
|
|
41
|
+
|
|
42
|
+
## Ce que le skill reçoit, et ce que chaque entrée permet de juger
|
|
43
|
+
|
|
44
|
+
La grille est la même ; ce qu'on peut en prouver dépend de l'accès. **Annoncer l'entrée reçue en tête de rapport**, et ne jamais juger un axe que l'entrée ne montre pas.
|
|
45
|
+
|
|
46
|
+
| Entrée | Ce qui devient jugeable | Ce qui reste hors de portée |
|
|
47
|
+
|---|---|---|
|
|
48
|
+
| **Accès direct au fichier** (Figma ouvert, ou API avec les nœuds) | Tous les axes : détachements, styles orphelins, styles locaux contre publiés, profondeur des groupes. | — |
|
|
49
|
+
| **Résumé serveur** (`analyze-figma`) | Nommage, structure des pages, part d'auto-layout, réutilisation des composants, nombre de styles publiés, valeurs employées. | Détachements, styles orphelins, local contre publié au cas par cas. **Ces axes se déclarent non vérifiés**, jamais conformes. |
|
|
50
|
+
|
|
51
|
+
Un axe que l'entrée ne montre pas ne produit ni constat ni satisfecit. C'est la même discipline que partout : le silence se dit.
|
|
52
|
+
|
|
53
|
+
## La grille
|
|
54
|
+
|
|
55
|
+
### 1. Nommage
|
|
56
|
+
|
|
57
|
+
- Calques et cadres restés au nom par défaut — `Frame 42`, `Group 7`, `Rectangle 12`. Ils ne survivent pas au passage au code : personne ne sait ce qu'ils désignent.
|
|
58
|
+
- Écrans nommés par leur contenu, pas par leur ordre d'apparition dans la tête de l'auteur.
|
|
59
|
+
- Composants nommés selon une convention lisible et unique. Deux conventions concurrentes pour la même famille (`Bouton` et `Button`, `btn/primary` et `Bouton primaire`) valent doublon.
|
|
60
|
+
- Pages nommées par leur rôle, pas « Page 1 ».
|
|
61
|
+
|
|
62
|
+
### 2. Structure des pages
|
|
63
|
+
|
|
64
|
+
- Une page fourre-tout où cohabitent fondations, écrans, essais et archives est un fichier que personne ne peut parcourir.
|
|
65
|
+
- Les versions successives d'un même écran, côte à côte et sans indication de celle qui fait foi, sont une source de vérité ambiguë — le défaut le plus coûteux du lot.
|
|
66
|
+
- Les brouillons et les archives se rangent ou se suppriment ; laissés au milieu des écrans vivants, ils seront intégrés par erreur.
|
|
67
|
+
|
|
68
|
+
### 3. Auto-layout
|
|
69
|
+
|
|
70
|
+
- Présent là où le contenu varie : listes, cartes, boutons à libellé variable, blocs de texte.
|
|
71
|
+
- Son absence oblige à repositionner à la main à chaque changement de contenu, et se traduit à l'intégration par des écarts qu'aucune maquette ne documente.
|
|
72
|
+
- Une part d'auto-layout très basse sur un fichier riche en écrans est un signal d'ensemble, pas un reproche écran par écran.
|
|
73
|
+
|
|
74
|
+
### 4. Composants
|
|
75
|
+
|
|
76
|
+
- Un élément répété doit être une **instance**, pas une copie redessinée. Compter les usages : un élément présent sur dix écrans et jamais instancié est le défaut le plus cher du fichier.
|
|
77
|
+
- **Composants détachés** : une instance rompue ne reçoit plus les corrections du composant source. Sur un fichier d'équipe, chaque détachement est une divergence future.
|
|
78
|
+
- **Doublons de même usage** : deux composants distincts pour un même rôle, souvent issus de deux personnes ou de deux moments.
|
|
79
|
+
- **Variantes** : une explosion de variantes qui aurait dû être des propriétés rend le composant illisible à l'usage.
|
|
80
|
+
|
|
81
|
+
### 5. Styles
|
|
82
|
+
|
|
83
|
+
- **Publiés contre locaux** : un style local ne traverse pas les fichiers et ne se partage pas. Sur un fichier destiné à une équipe, il crée une valeur qui n'existe nulle part ailleurs.
|
|
84
|
+
- **Valeurs en dur** : couleurs, tailles ou rayons posés à la main au lieu d'un style. Le symptôme visible est la série de valeurs voisines jamais identiques — rayons 6, 7, 8 ; espacements 14, 15, 16.
|
|
85
|
+
- **Styles orphelins** : définis, publiés, employés nulle part. Ils gonflent la bibliothèque et font hésiter à chaque choix.
|
|
86
|
+
- Une échelle typographique non systématique — trop de tailles pour le nombre d'écrans — se paie à chaque nouvel écran.
|
|
87
|
+
|
|
88
|
+
### 6. Reprenabilité — la synthèse
|
|
89
|
+
|
|
90
|
+
Après les cinq axes, répondre explicitement : **une autre personne peut-elle ouvrir ce fichier et travailler sans son auteur ?** Trois réponses possibles, et une seule ligne pour la justifier.
|
|
91
|
+
|
|
92
|
+
| Réponse | Ce que ça veut dire |
|
|
93
|
+
|---|---|
|
|
94
|
+
| **Reprenable** | Les repères existent : noms, composants, styles, pages. Une nouvelle personne trouve seule. |
|
|
95
|
+
| **Reprenable avec effort** | Il faut une session avec l'auteur, ou une passe de rangement chiffrable. |
|
|
96
|
+
| **Non reprenable** | Le fichier ne vit que dans la tête de son auteur. |
|
|
97
|
+
|
|
98
|
+
## La règle de l'absence — le garde-fou de cet axe
|
|
99
|
+
|
|
100
|
+
**Un fichier tronqué n'est pas un fichier vide.** C'est la faute la plus facile à commettre ici, et elle a déjà été commise en production.
|
|
101
|
+
|
|
102
|
+
- Le matériau annonce lui-même sa complétude. Une lecture partielle le dit en tête (« ma lecture s'est arrêtée après N éléments »), et un drapeau `partial` existe pour ça. **Lecture partielle → aucune conclusion négative** sur ce qui n'est pas listé.
|
|
103
|
+
- **Les listes sont plafonnées.** Quarante-cinq noms d'écrans sur mille deux cents, cent soixante textes, vingt-quatre couleurs : la fin de la liste n'est pas la fin du fichier. Ne jamais compter sur une liste tronquée pour affirmer une absence.
|
|
104
|
+
- **Les textes coupés par la lecture ne sont pas des textes tronqués.** La mention « […coupé par ma lecture, pas par la maquette] » signe notre propre coupure. La rapporter comme un défaut revient à reprocher au fichier ce que nous lui avons fait.
|
|
105
|
+
- **Un fichier n'est pas un produit.** Ne jamais écrire qu'un écran, un état ou une fonctionnalité « manque » : il est peut-être simplement hors du fichier, ou dans un autre.
|
|
106
|
+
- L'absence ne devient jugeable que si la lecture est **déclarée complète** et que la liste concernée est **exhaustive**. Alors seulement : « les quarante-six cadres listés portent tous un nom par défaut » est un constat, et sa preuve est la liste elle-même.
|
|
107
|
+
|
|
108
|
+
## Sévérité sur cet axe
|
|
109
|
+
|
|
110
|
+
Le verdict se déduit des sévérités. Ce que cet axe précise, c'est ce qui pèse quoi — la question de référence étant toujours la reprise par quelqu'un d'autre.
|
|
111
|
+
|
|
112
|
+
| Sévérité | Sur cet axe |
|
|
113
|
+
|---|---|
|
|
114
|
+
| **Bloquant** | Le fichier est livré au développement et ne porte aucun repère : ni nom, ni composant, ni structure. La reprise sans son auteur est impossible. |
|
|
115
|
+
| **Critique** | Faux textes partant au développement. Deux versions concurrentes d'un même écran sans source de vérité désignée. Détachements massifs qui condamnent toute correction centralisée. |
|
|
116
|
+
| **Majeur** | Nommage par défaut généralisé. Aucun composant réutilisé sur un fichier à nombreux écrans. Auto-layout quasi absent. Styles locaux au lieu de publiés. Séries de valeurs voisines. |
|
|
117
|
+
| **Mineur** | Quelques calques oubliés. Une page d'archives non rangée. Un style orphelin isolé. |
|
|
118
|
+
| **Note** | Convention perfectible, dette de rangement documentée et assumée. |
|
|
119
|
+
|
|
120
|
+
## Séquence d'audit obligatoire
|
|
121
|
+
|
|
122
|
+
1. **Déclarer l'entrée et la complétude.** Accès direct ou résumé serveur ; lecture complète ou partielle. Tout le reste en dépend.
|
|
123
|
+
2. **Inventorier.** Pages et leur rôle, nombre de cadres, part d'auto-layout, composants et leurs usages, styles publiés.
|
|
124
|
+
3. **Juger le nommage**, sur la liste dont on connaît l'exhaustivité.
|
|
125
|
+
4. **Juger la structure des pages**, et chercher les versions concurrentes.
|
|
126
|
+
5. **Juger les composants** : réutilisation, détachements, doublons, variantes — en marquant non vérifié ce que l'entrée ne montre pas.
|
|
127
|
+
6. **Juger les styles** : publiés, locaux, orphelins, valeurs en dur, échelle.
|
|
128
|
+
7. **Répondre à la question de reprise**, avec sa justification en une ligne.
|
|
129
|
+
8. **Signaler les dépendances** vers `better-interface`, `better-writing` ou `better-consistency` sans développer leur audit.
|
|
130
|
+
|
|
131
|
+
## Anti-patterns à refuser
|
|
132
|
+
|
|
133
|
+
- Conclure qu'un état, un écran ou une fonctionnalité manque à partir d'une liste plafonnée.
|
|
134
|
+
- Rapporter comme tronqué un texte que notre propre lecture a coupé.
|
|
135
|
+
- Juger la qualité du design sous couvert d'hygiène — « la hiérarchie visuelle est faible » n'appartient pas à ce skill.
|
|
136
|
+
- Reprocher le rendu ou une animation qu'on n'a pas vus s'exécuter.
|
|
137
|
+
- Déclarer un axe conforme alors que l'entrée reçue ne le montre pas.
|
|
138
|
+
- Recommander de refaire le fichier. Un fichier se range ; il ne se refond pas sur avis d'audit.
|
|
139
|
+
- Confondre un fichier d'exploration, dont le désordre est la matière, et un fichier de livraison.
|
|
140
|
+
|
|
141
|
+
## Contrat de sortie exact
|
|
142
|
+
|
|
143
|
+
```
|
|
144
|
+
# Verdict
|
|
145
|
+
- Verdict: PASS | PASS WITH FIXES | FAIL | BLOCKED
|
|
146
|
+
- Reprise par quelqu'un d'autre: Reprenable | Reprenable avec effort | Non reprenable
|
|
147
|
+
- Entrée reçue: accès direct | résumé serveur
|
|
148
|
+
- Lecture: complète | partielle (et jusqu'où)
|
|
149
|
+
|
|
150
|
+
# Inventaire
|
|
151
|
+
- Pages et rôle de chacune
|
|
152
|
+
- Cadres, dont en auto-layout
|
|
153
|
+
- Composants et nombre d'instances
|
|
154
|
+
- Styles publiés
|
|
155
|
+
|
|
156
|
+
# Matrice d'hygiène
|
|
157
|
+
Axe | Constat | Preuve (liste, compte, nom exact) | Verdict | Non vérifié
|
|
158
|
+
|
|
159
|
+
# Problèmes priorisés
|
|
160
|
+
FIG-XX | Sévérité | Localisation (page › cadre) | Preuve | Impact | Correction | Critères | Confiance
|
|
161
|
+
|
|
162
|
+
# Non vérifié
|
|
163
|
+
- Axe -> ce qui manque pour le juger
|
|
164
|
+
|
|
165
|
+
# À passer à un autre skill
|
|
166
|
+
- Constat pressenti -> skill concerné (sans le développer)
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
## Critères de qualité du skill
|
|
170
|
+
|
|
171
|
+
- L'entrée et la complétude de lecture sont déclarées avant tout constat.
|
|
172
|
+
- Aucun constat ne repose sur une liste dont l'exhaustivité n'est pas établie.
|
|
173
|
+
- Chaque constat cite un nom, un compte ou une liste, jamais une impression.
|
|
174
|
+
- Les axes que l'entrée ne montre pas figurent en non vérifié, pas en conforme.
|
|
175
|
+
- La réponse sur la reprise est explicite et justifiée en une ligne.
|
|
176
|
+
- Aucun jugement sur la qualité du design n'a franchi la frontière.
|
|
177
|
+
|
|
178
|
+
## Exemple d'invocation
|
|
179
|
+
|
|
180
|
+
```
|
|
181
|
+
/review-figma-hygiene
|
|
182
|
+
Fichier: maquette produit, 2 pages, destinée au développement
|
|
183
|
+
Entrée: accès direct au fichier Figma
|
|
184
|
+
Équipe: 2 designers, reprise par un développeur externe le mois prochain
|
|
185
|
+
Objectif: savoir si le fichier est livrable en l'état
|
|
186
|
+
```
|