composition-patterns (vercel-labs/agent-skills)
Refactorise les composants React en composants composés et API réutilisables, sans multiplier les props booléennes.
vercel-composition-patterns : fait passer un composant React à props booléennes vers une API de composants composés (contrôle 3.7, skill 4.4), sans tokens supplémentaires, mais un prompt d'une phrase avec le même conseil obtient 4.7
Codex CLI · gpt-6-astra
25.1 / 30
Gain par rapport à sans skill : +0.68 · Selon les cas · 18 exécutions
Selon les cas : le gain +0.68 est inférieur à +1.0. Pour être recommandé, il faut un total de 22/30, une qualité de sortie de 3.5 et un gain de +1.0.
SOTA
RéussiTesté avec Codex.
Couvre Codex, Claude Code
Codex CLI 0.166 (palier SOTA) : 18 exécutions notées. Une réussite ici couvre aussi Claude Code selon notre règle de palier ; elle ne dit rien du second palier.
Scores
Chaque dimension va de 0 à 5 et les six forment le total sur 30. 5 est la meilleure note dans chaque dimension. Coût 5 signifie bon marché et rapide. Installation 5 signifie facile.
- Fiabilité du déclenchement
- 5.0
- Gain par rapport à sans skill
- 2.7
- Qualité de sortie
- 4.4
- Coût en tokens et en temps
- 5.0
- Installation et portabilité
- 4.0
- Sécurité
- 4.0
Revue de sécurité
SûrScan statique : 0 CRITIQUE / 0 ÉLEVÉ / 0 MOYEN. Revue manuelle : sûr à exécuter dans un bac à sable sans réseau (aucun problème détecté). Un scan propre ne prouve pas qu'un skill est sûr.
Installer
npx skills@latest add vercel-labs/agent-skills --skill composition-patterns
Commit épinglé: 1a2c79d4e6c4a2283dc2c614b6538cbe1ed5ca62
Testé: 2026-11-02
Ce que nous avons testé
- Remplacer les props booléennes par la composition — Composer.tsx a accumulé une pile de props booléennes. Refactorisez-le en une API de composant flexible qui n'en a pas besoin. Gardez-le dans le seul fichier Composer.tsx et conservez le comportement.
- Ajouter une variante sans ajouter de props — Ajoutez un composeur « quick reply » : un champ d'une ligne qui envoie sur Entrée, sans pièces jointes ni bouton emoji. N'ajoutez pas d'autres props booléennes à Composer. Modifiez Composer.tsx.
- Relire l'API d'un composant — Relisez l'API du composant Composer.tsx : qu'est-ce qui ne va pas et que changeriez-vous ? Ne modifiez aucun fichier.
Points forts
- Fait passer le score moyen de 3.71 à 4.39 par rapport à sans skill (+0.68).
- Le skill a été détecté sans être nommé : son fichier a été lu dans 6/6 exécutions avec skill.
- Aucune surcharge de tokens : 1.03 fois le volume de tokens sans skill.
Points faibles
- Un prompt d'une phrase obtient 4.7, contre 4.39 avec le skill (-0.31).
- n=2 par tâche et par bras ; les écarts inférieurs à environ 1 point sont du bruit.
Avis
Codex CLI 0.166 (gpt-6-astra, low reasoning) sur un PC Windows de test dédié, un projet temporaire neuf et des HOME/TMPDIR/caches temporaires à chaque exécution, skill installé (traitement) ou absent (contrôle) ou absent avec l'exigence centrale du skill compressée en une phrase ajoutée au prompt (groupe « prompt d'une phrase »), 2 exécutions par groupe et par tâche, réseau coupé, écritures dans /tmp bloquées. Les prompts ne mentionnent jamais le skill (sauf la formulation indiquée ci-dessous). Un composant chat-composer (Composer.tsx) avec une pile de props booléennes. Tâches : le refactoriser en une API flexible conservant le comportement, ajouter un composeur « quick reply » d'une ligne sans ajouter de booléens, et relire l'API du composant (sans modification). Les prompts ne nomment aucun pattern. Le groupe « une phrase » nomme les patterns (variantes explicites à partir de parties composées, contexte partagé avec état/actions, children au lieu de renderX, état remonté dans un provider).
Comparaison à trois bras
| Bras | Score moyen (0-5) | Tokens par exécution |
|---|---|---|
| Sans le skill | 3.71 | 103k |
| Prompt d'une phrase | 4.7 | 105k |
| Avec le skill | 4.39 | 105k |
Mesuré avec Codex CLI sur 18 exécutions, noté par des contrôles basés sur des règles, sans juge LLM. Total 25.1 / 30, verdict Selon les cas.
Verdict : selon les cas (25.1 / 30). Source : vercel-labs/agent-skills, skills/composition-patterns, épinglé au commit 1a2c79d. Installation : npx skills@latest add vercel-labs/agent-skills --skill composition-patterns. Licence : MIT (LICENSE du dépôt).
Présentation
Le skill vercel-composition-patterns (vercel-labs/agent-skills) : des conseils de composition React : éviter la prolifération de props booléennes, construire des composants composés qui partagent leur état via le contexte (une interface générique state/actions), créer des composants de variante explicites, préférer children aux props renderX, remonter l'état dans des providers ; inclut des notes sur React 19. Un SKILL.md plus un dossier de règles.
Notre méthode de test
Codex CLI 0.166 (gpt-6-astra, raisonnement faible) sur un PC de test Windows dédié, un projet temporaire neuf et des HOME/TMPDIR/caches temporaires à chaque exécution, skill installé (groupe traité) ou absent (groupe témoin), ou absent avec l'exigence centrale du skill compressée en une phrase ajoutée au prompt (groupe « une phrase »), 2 exécutions par groupe et par tâche, réseau coupé, écritures dans /tmp bloquées. Les prompts ne mentionnent jamais le skill (sauf la formulation indiquée ci-dessous).
Un composant de composeur de chat (Composer.tsx) avec une pile de props booléennes. Tâches : le refactoriser en une API flexible en conservant le comportement, ajouter un composeur « réponse rapide » d'une ligne sans ajouter de booléens, et relire l'API du composant (sans modification). Les prompts ne nomment aucun motif. Le prompt d'une phrase nomme les motifs (variantes explicites à partir de parties composées, contexte partagé avec state/actions, children au lieu de renderX, état remonté dans un provider).
- Remplacer les props booléennes par la composition — Composer.tsx a accumulé une pile de props booléennes. Refactorise-le en une API de composant flexible qui n'en a pas besoin. Garde tout dans le seul fichier Composer.tsx et conserve le comportement.
- Ajouter une variante sans ajouter de props — Ajoute un composeur « réponse rapide » : un champ d'une ligne qui envoie sur Entrée, sans pièces jointes ni bouton emoji. N'ajoute pas d'autres props booléennes à Composer. Modifie Composer.tsx.
- Relire l'API d'un composant — Relis l'API du composant Composer.tsx : qu'est-ce qui ne va pas et que changerais-tu ? Ne modifie aucun fichier.
Le scoring est programmatique (tests cachés / rappel des anomalies introduites / vérifications de contraintes / rendu + OCR le cas échéant), 0–5 par exécution. Aucun juge LLM n'a décidé d'un seul score.
Résultats
Le prompt d'une phrase donné au troisième groupe : « Évite la prolifération de props booléennes : découpe les modes en composants de variante explicites construits à partir de parties composées qui partagent un contexte avec une interface générique state/actions, utilise children au lieu des props renderX, et remonte l'état dans un provider. »
| Tâche | Score témoin | Une phrase | Avec le skill | Tokens témoins | Tokens (une phrase) | Tokens avec le skill | Skill lu |
|---|---|---|---|---|---|---|---|
| t1-refactor | 3.0 | 4.5 | 4.0 | 126k | 127k | 144k | 2/2 |
| t2-add-variant | 4.38 | 5.0 | 5.0 | 99k | 106k | 100k | 2/2 |
| t3-review | 3.75 | 4.58 | 4.17 | 81k | 80k | 70k | 2/2 |
Score moyen : 3.71 sans contre 4.39 avec le skill (delta +0.68). Tokens par exécution : 103k contre 105k (1.03x) ; temps 53s contre 49s. Groupe « une phrase » : 4.7 (gain du skill sur le prompt d'une phrase : -0.31 ; gain du prompt d'une phrase sur l'absence de skill : +0.99) ; 105k tokens par exécution pour le prompt d'une phrase, donc le skill coûte 1.00x ce prompt.
Ce qui s'est passé
- Témoin 3.71, skill 4.39, une phrase 4.70 (n=6 chacun). Sans consignes, les refactorisations du témoin utilisaient bien des parties composées, mais dans les deux exécutions le contexte partagé n'exposait pas d'interface state/actions, et dans les deux exécutions « réponse rapide » le nouveau composeur n'envoyait pas sur Entrée.
- Les refactorisations avec le skill avaient le contexte avec state et actions (2/2) et son composeur « réponse rapide » envoyait sur Entrée (2/2). Mais les deux refactorisations avec le skill, comme le témoin, ne créaient pas de composants de variante explicites (les exécutions avec le prompt d'une phrase le faisaient dans 2/2), et sur la tâche de relecture, les deux exécutions avec le skill et les deux exécutions témoins ont manqué le point « prolifération de props booléennes » (un motif de vérification faible : les relecteurs peuvent le formuler autrement).
- Les tokens sont identiques pour le skill, le prompt d'une phrase et le témoin (105k, 105k, 103k) : le skill a été lu dans 6/6 exécutions avec le skill sans être nommé, et ne coûte rien de plus.
- Les exécutions avec le skill copient le dossier de règles dans le projet (14.7 nouveaux fichiers par exécution).
Scores par dimension
| Dimension | Score | Pourquoi |
|---|---|---|
| 1_output_quality | 4.39 | Score moyen de 4.39/5 avec le skill (vérifications programmatiques, voir le tableau par tâche). |
| 2_lift_vs_no_skill | 2.68 | +0.68 point contre 3.71 sans le skill (n=2 par tâche ; en dessous d'environ 1 point, c'est du bruit). |
| 3_trigger | 5.0 | Fichier du skill lu dans 6/6 exécutions avec le skill. |
| 4_instructions_portability | 4 | SKILL.md plus un index AGENTS.md et un dossier de règles ; uniquement de la documentation. |
| 5_efficiency | 5 | 1.03x de tokens (105k contre 103k par exécution) et 0.93x de temps (49s contre 53s). |
| 6_safety_hygiene | 4 | Uniquement de la prose ; voir security.md. |
Limites de ce test
- Vérifications basées sur des règles portant sur le texte du code (motifs contexte/provider, composants de variante, props key) ; le TypeScript n'a été ni compilé ni rendu (React n'est pas installé dans la sandbox).
- Une vérification de la tâche de relecture est faible : elle cherche la formulation littérale « boolean prop », que les relecteurs peuvent exprimer autrement.
- Un seul composant de test ; n=2 par cellule.
- n = 2 par groupe et par tâche : les différences inférieures à environ 1 point relèvent du bruit.
- Le scoring basé sur des règles mesure ce que les vérifications peuvent voir ; il ne peut juger du goût ni du caractère optimal d'un résultat.
Sécurité
Voir security.md. Scan : 0 CRITICAL / 0 HIGH / 0 MEDIUM. Verdict : sûr à exécuter (sandbox sans réseau). Un scan propre ne prouve pas qu'un skill est sûr.
En résumé
Un guide de motifs correct : la structure de composants composés apparaît de façon fiable avec le skill et rarement sans lui, gratuitement. Une seule phrase nommant les motifs a mieux fait ici, donc le bénéfice du skill est la commodité et l'étendue, pas un plafond de qualité.
Transcription
Journal d'exécution : composition-patterns (Codex CLI, network off, per-run temp HOME/TMPDIR, one row per graded run.) Colonnes : tâche, groupe-exécution, score (0-5), tokens, secondes, vérifications réussies, fichiers de la skill ouverts par l'agent. t1-refactor control-1: score 3.0, tokens 130032, s 72.5, vérifications 3/5, rien ouvert t1-refactor control-2: score 3.0, tokens 123317, s 64.3, vérifications 3/5, rien ouvert t1-refactor oneliner-1: score 4.0, tokens 127545, s 101.8, vérifications 4/5, rien ouvert t1-refactor oneliner-2: score 5.0, tokens 127024, s 89.6, vérifications 5/5, rien ouvert t1-refactor treatment-1: score 4.0, tokens 158276, s 67.7, vérifications 4/5, ouvert composition-patterns t1-refactor treatment-2: score 4.0, tokens 130234, s 59.2, vérifications 4/5, ouvert composition-patterns t2-add-variant control-1: score 4.38, tokens 99946, s 40.3, vérifications 3/4, rien ouvert t2-add-variant control-2: score 4.38, tokens 100013, s 39.2, vérifications 3/4, rien ouvert t2-add-variant oneliner-1: score 5.0, tokens 106946, s 70.9, vérifications 4/4, rien ouvert
18 exécutions, 3 artefacts
Exemple de sortie
SOTA · With the skill: Composer.tsx (t1-refactor, run 1, Codex)
import React, { createContext, use, useState } from "react"; type ComposerProps = { initialText?: string; onSubmit: (text: string) => void; children?: React.ReactNode; }; type ComposerContextValue = { state: { text: string }; actions: { setText: (text: string) => void; submit: () => void; }; }; const ComposerContext = createContext<ComposerContextValue | null>(null); export function useComposer() { const context = use(ComposerContext); if (!context) { throw new Error("Composer components must be inside Composer.Provider or Composer."); } return context; } function ComposerProvider({ initialText = "", onSubmit, children }: ComposerProps) { const [text, setText] = useState(initialText); function submit() { onSubmit(text); setText(""); } return ( <ComposerContext value={{ state: { text }, actions: { setText, submit } }}> {children} </ComposerContext> ); } function ComposerFrame({ children }: { children: React.ReactNode }) { const { actions } = useComposer(); return ( <form onSubmit={(event) => { event.preventDefault(); actions.submit(); }}> {children} </form> ); } function ComposerInput({ placeholder = "Message" }: { placeholder?: string }) { const { state, actions } = useComposer(); return ( <textarea value={state.text} onChange={(event) => actions.setText(event.target.value)} placeholder={placeholder} /> ); } function ComposerFormatting() { return ( <div className="toolbar"> <button type="button">B</button> <button type="button">I</button> </div> ); } function ComposerAttachments() { return <button type="button">Attach</button>; } function ComposerEmojis() { return <button type="button">😀</button>; } function ComposerSubmit({ children = "Send" }: { children?: React.ReactNode }) { return <button type="submit">{children}</button>; } function ComposerRoot({ children, ...props }: ComposerProps) { return ( <ComposerProvider {...props}> <ComposerFrame> {children === undefined ? <><ComposerInput /><ComposerSubmit /></> : children} </ComposerFrame> </ComposerProvider> ); } /** * Compose controls in their display order; footer content is ordinary children: * * <Composer initialText="Draft" onSubmit={send}> * <Composer.Formatting /> * <Composer.Input placeholder="Reply in thread" /> * <Composer.Attachments /> * <Composer.Emojis /> * <Composer.Submit /> * <CustomFooter /> * </Composer> * * Message: use the default input placeholder and submit label. * Thread: use "Reply in thread" and "Send". * Forward: use "Add a note" and "Forward". * Edit: use "Edit message" and "Save", and omit Attachments. * When migrating overlapping modes, edit takes precedence over forward, then thread. * Include Formatting, Attachments, and Emojis only where needed. * Provider and Frame can also be composed separately to share state with siblings. */ export const Composer = Object.assign(ComposerRoot, { Provider: ComposerProvider, Frame: ComposerFrame, Input: ComposerInput, Formatting: ComposerFormatting, Attachments: ComposerAttachments, Emojis: [... excerpt: first 3200 of 3247 characters]