composition-patterns (vercel-labs/agent-skills)
Refaktoriert React-Komponenten zu Compound-Komponenten und wiederverwendbaren APIs, statt Boolean-Props zu häufen.
vercel-composition-patterns: Verschiebt eine React-Komponente mit Boolean-Props zu einer Compound-Component-API (Kontrolle 3.7, Skill 4.4), ohne zusätzliche Tokens, aber ein Ein-Satz-Prompt mit demselben Rat erzielt 4.7
Codex CLI · gpt-6-astra
25.1 / 30
Lift gegenüber ohne Skill: +0.68 · Situativ · 18 Läufe
Situativ: der Lift +0.68 liegt unter +1.0. Für „Empfohlen“ sind insgesamt 22/30, eine Ausgabequalität von 3.5 und ein Lift von +1.0 nötig.
SOTA
BestandenGetestet mit Codex.
Gilt für Codex, Claude Code
Codex CLI 0.166 (SOTA-Stufe): 18 bewertete Läufe. Ein Bestehen hier deckt gemäß unserer Stufenregel auch Claude Code ab; über die zweite Stufe sagt es nichts aus.
Punktzahlen
Jede Dimension reicht von 0 bis 5, und die sechs ergeben zusammen die Gesamtpunktzahl von 30. 5 ist in jeder Dimension besser. Kosten 5 bedeutet günstig und schnell. Installation 5 bedeutet einfach.
- Trigger-Zuverlässigkeit
- 5.0
- Lift gegenüber ohne Skill
- 2.7
- Ausgabequalität
- 4.4
- Token- und Zeitkosten
- 5.0
- Installation und Portabilität
- 4.0
- Sicherheit
- 4.0
Sicherheitsprüfung
SicherStatischer Scan: 0 CRITICAL / 0 HIGH / 0 MEDIUM. Manuelle Prüfung: Sicher in einer Sandbox ohne Netzwerk auszuführen (keine Befunde). Ein sauberer Scan beweist nicht, dass ein Skill sicher ist.
Installieren
npx skills@latest add vercel-labs/agent-skills --skill composition-patterns
Fixierter Commit: 1a2c79d4e6c4a2283dc2c614b6538cbe1ed5ca62
Getestet: 2026-11-02
Was wir getestet haben
- Boolean-Props durch Komposition ersetzen — Composer.tsx hat einen Haufen Boolean-Props angesammelt. In eine flexible Komponenten-API refaktorieren, die sie nicht braucht. In der einzelnen Datei Composer.tsx bleiben und das Verhalten erhalten.
- Eine Variante ohne zusätzliche Props hinzufügen — Einen „Quick Reply“-Composer hinzufügen: ein einzeiliges Eingabefeld, das bei Enter sendet, ohne Anhänge oder Emoji-Button. Keine weiteren Boolean-Props zu Composer hinzufügen. Composer.tsx bearbeiten.
- Eine Komponenten-API reviewen — Die Komponenten-API von Composer.tsx reviewen: Was ist daran falsch und was wäre zu ändern? Keine Dateien ändern.
Vorteile
- Hebt den Mittelwert von 3.71 auf 4.39 gegenüber ohne Skill (+0.68).
- Der Skill wurde erkannt, ohne genannt zu werden: Skill-Datei in 6/6 Treatment-Läufen gelesen.
- Kein Token-Overhead: 1.03x die Tokens von ohne Skill.
Nachteile
- Ein Ein-Satz-Prompt erzielte 4.7 gegenüber 4.39 mit dem Skill (-0.31).
- n=2 pro Aufgabe und Variante; Unterschiede unter etwa 1 Punkt sind Rauschen.
Bewertung
Codex CLI 0.166 (gpt-6-astra, niedriges Reasoning) auf einem dedizierten Windows-Test-PC, einem frischen temporären Projekt und temporären HOME/TMPDIR/Caches pro Lauf; Skill installiert (Treatment) oder nicht vorhanden (Kontrolle) oder nicht vorhanden mit der Kernanforderung des Skills in einem an den Prompt angehängten Satz (One-Liner-Gruppe); 2 Läufe pro Gruppe und Aufgabe; Netzwerk aus; Schreiben nach /tmp blockiert. Die Prompts nennen den Skill nie (außer der unten genannten Formulierung). Eine Chat-Composer-Komponente (Composer.tsx) mit einem Haufen Boolean-Props. Aufgaben: sie in eine flexible API refaktorieren und das Verhalten erhalten, einen einzeiligen „Quick Reply“-Composer hinzufügen, ohne weitere Booleans einzuführen, und die Komponenten-API reviewen (keine Änderungen). Die Prompts nennen kein Muster. Der One-Liner nennt die Muster (explizite Varianten aus Compound-Teilen, gemeinsamer Context mit State/Actions, children statt renderX, State in einen Provider gehoben).
Drei-Arm-Vergleich
| Variante | Durchschnittsnote (0-5) | Tokens pro Lauf |
|---|---|---|
| Ohne Skill | 3.71 | 103k |
| Ein-Satz-Prompt | 4.7 | 105k |
| Mit Skill | 4.39 | 105k |
Gemessen mit Codex CLI über 18 Läufe, bewertet durch regelbasierte Prüfungen ohne LLM-Richter. Gesamt 25.1 / 30, Urteil Situativ.
Urteil: Situationsabhängig (25.1 / 30). Quelle: vercel-labs/agent-skills, skills/composition-patterns, festgeschrieben auf Commit 1a2c79d. Installation: npx skills@latest add vercel-labs/agent-skills --skill composition-patterns. Lizenz: MIT (LICENSE im Repo).
Was es ist
Der Skill vercel-composition-patterns (vercel-labs/agent-skills) ist ein React-Kompositionsleitfaden: Boolean-Prop-Wildwuchs vermeiden, Compound Components bauen, die Zustand über Context teilen (eine generische State/Actions-Schnittstelle), explizite Variantenkomponenten erstellen, children statt renderX-Props bevorzugen, Zustand in Provider heben; enthält Hinweise zu React 19. Eine SKILL.md plus ein Regeln-Ordner.
Wie wir getestet haben
Codex CLI 0.166 (gpt-6-astra, niedriges Reasoning) auf einem dedizierten Windows-Test-PC, pro Lauf ein frisches temporäres Projekt sowie temporäre HOME/TMPDIR/Caches, Skill installiert (Treatment) oder nicht vorhanden (Kontrolle) oder nicht vorhanden mit der Kernanforderung des Skills als einem an den Prompt angehängten Satz (Ein-Satz-Gruppe), 2 Läufe pro Gruppe und Aufgabe, Netzwerk aus, Schreibzugriffe auf /tmp blockiert. Die Prompts nennen den Skill nie (außer der unten erwähnten Formulierung).
Eine Chat-Composer-Komponente (Composer.tsx) mit einer Vielzahl von Boolean-Props. Aufgaben: sie in eine flexible API refaktorieren, ohne das Verhalten zu ändern; einen einzeiligen „Quick Reply“-Composer hinzufügen, ohne weitere Booleans einzuführen; und die Komponenten-API reviewen (keine Änderungen). Die Prompts nennen kein Muster. Der Ein-Satz-Prompt nennt die Muster (explizite Varianten aus Compound-Teilen, geteilter Context mit State/Actions, children statt renderX, Zustand in einen Provider gehoben).
- Boolean-Props durch Komposition ersetzen — Composer.tsx hat einen Haufen Boolean-Props angesammelt. Refaktoriere sie in eine flexible Komponenten-API, die diese nicht braucht. Behalte alles in der einen Datei Composer.tsx und erhalte das Verhalten.
- Eine Variante ohne zusätzliche Props hinzufügen — Füge einen „Quick Reply“-Composer hinzu: ein einzeiliges Eingabefeld, das mit Enter sendet, ohne Anhänge oder Emoji-Button. Füge Composer keine weiteren Boolean-Props hinzu. Bearbeite Composer.tsx.
- Eine Komponenten-API reviewen — Reviewe die Komponenten-API von Composer.tsx: Was ist daran falsch, und was würdest du ändern? Ändere keine Dateien.
Die Bewertung erfolgt programmatisch (versteckte Tests / Erkennung eingepflanzter Fehler / Constraint-Checks / Rendering+OCR, wo relevant), 0–5 pro Lauf. Kein LLM-Judge hat eine Bewertung entschieden.
Ergebnisse
Der Ein-Satz-Prompt für die dritte Gruppe: "Vermeide Boolean-Prop-Wildwuchs: teile Modi in explizite Variantenkomponenten auf, die aus Compound-Teilen mit einem gemeinsamen Context und einer generischen State/Actions-Schnittstelle gebaut sind, verwende children statt renderX-Props und hebe den Zustand in einen Provider."
| Aufgabe | Kontrollwert | Ein-Satz | Mit Skill | Kontroll-Token | Ein-Satz-Token | Mit-Skill-Token | Skill gelesen |
|---|---|---|---|---|---|---|---|
| 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 |
Durchschnittswert: 3.71 ohne vs. 4.39 mit dem Skill (Delta +0.68). Token pro Lauf: 103k vs. 105k (1.03x); Zeit 53s vs. 49s. Ein-Satz-Gruppe: 4.7 (Gewinn des Skills gegenüber dem Ein-Satz-Prompt: -0.31; Gewinn des Ein-Satz-Prompts gegenüber ohne Skill: +0.99); Ein-Satz-Token 105k pro Lauf, der Skill kostet also 1.00x so viel wie der Ein-Satz-Prompt.
Was passiert ist
- Kontrolle 3.71, Skill 4.39, Ein-Satz 4.70 (je n=6). Ohne Anleitung verwendeten die Refactorings der Kontrolle zwar Compound-Teile, aber in beiden Läufen gab der geteilte Context keine State/Actions-Schnittstelle frei, und in beiden Quick-Reply-Läufen sendete der neue Composer nicht mit Enter.
- Die Refactorings mit dem Skill hatten den Context mit State und Actions (2/2), und ihr Quick-Reply-Composer sendete mit Enter (2/2). Aber beide Treatment-Refactorings erzeugten, wie die Kontrolle, keine expliziten Variantenkomponenten (die Ein-Satz-Läufe taten das in 2/2), und bei der Review-Aufgabe verfehlten beide Treatment-Läufe und beide Kontroll-Läufe den Punkt „Boolean-Prop-Wildwuchs“ (ein schwaches Prüfmuster: Reviewer können es anders formulieren).
- Die Token sind bei Skill, Ein-Satz und Kontrolle gleich (105k, 105k, 103k): Der Skill wurde in 6/6 Treatment-Läufen gelesen, ohne genannt zu werden, und kostet nichts extra.
- Treatment-Läufe kopieren den Regeln-Ordner ins Projekt (14.7 neue Dateien pro Lauf).
Dimensionswerte
| Dimension | Wert | Begründung |
|---|---|---|
| 1_output_quality | 4.39 | Durchschnittswert 4.39/5 mit dem Skill (programmatische Checks, siehe Tabelle pro Aufgabe). |
| 2_lift_vs_no_skill | 2.68 | +0.68 Punkte gegenüber 3.71 ohne den Skill (n=2 pro Aufgabe; unter ~1 Punkt ist Rauschen). |
| 3_trigger | 5.0 | Skill-Datei in 6/6 Treatment-Läufen gelesen. |
| 4_instructions_portability | 4 | SKILL.md plus ein AGENTS.md-Index und ein Regeln-Ordner; nur Dokumentation. |
| 5_efficiency | 5 | 1.03x Token (105k vs. 103k pro Lauf) und 0.93x Zeit (49s vs. 53s). |
| 6_safety_hygiene | 4 | Nur Prosa; siehe security.md. |
Grenzen dieses Tests
- Regelbasierte Checks am Code-Text (Context-/Provider-Muster, Variantenkomponenten, Key-Props); das TypeScript wurde nicht kompiliert oder gerendert (React ist in der Sandbox nicht installiert).
- Ein Check für die Review-Aufgabe ist schwach: Er sucht nach der wörtlichen Formulierung „boolean prop“, die Reviewer auch anders ausdrücken können.
- Ein einziges Komponenten-Fixture; n=2 pro Zelle.
- n = 2 pro Gruppe und Aufgabe: Abweichungen unter etwa 1 Punkt sind Rauschen.
- Regelbasierte Bewertung misst, was die Checks sehen können; sie kann weder Geschmack beurteilen noch, ob ein Ergebnis das bestmögliche ist.
Sicherheit
Siehe security.md. Scan: 0 CRITICAL / 0 HIGH / 0 MEDIUM. Urteil: sicher ausführbar (Sandbox ohne Netzwerk). Ein sauberer Scan beweist nicht, dass ein Skill sicher ist.
Fazit
Ein ordentlicher Musterleitfaden: Die Compound-Component-Struktur erscheint mit dem Skill zuverlässig und ohne ihn selten, und das kostenlos. Ein einzelner Satz, der die Muster nennt, war hier besser; der Nutzen des Skills liegt also in Bequemlichkeit und Breite, nicht in einer Qualitätsobergrenze.
Transkript
Laufprotokoll: composition-patterns (Codex CLI, network off, per-run temp HOME/TMPDIR, one row per graded run.) Spalten: Aufgabe, Gruppe-Lauf, Punktzahl (0-5), Tokens, Sekunden, bestandene Prüfungen, vom Agenten geöffnete Skill-Dateien. t1-refactor control-1: Punkte 3.0, Tokens 130032, Sek. 72.5, Prüfungen 3/5, nichts geöffnet t1-refactor control-2: Punkte 3.0, Tokens 123317, Sek. 64.3, Prüfungen 3/5, nichts geöffnet t1-refactor oneliner-1: Punkte 4.0, Tokens 127545, Sek. 101.8, Prüfungen 4/5, nichts geöffnet t1-refactor oneliner-2: Punkte 5.0, Tokens 127024, Sek. 89.6, Prüfungen 5/5, nichts geöffnet t1-refactor treatment-1: Punkte 4.0, Tokens 158276, Sek. 67.7, Prüfungen 4/5, geöffnet composition-patterns t1-refactor treatment-2: Punkte 4.0, Tokens 130234, Sek. 59.2, Prüfungen 4/5, geöffnet composition-patterns t2-add-variant control-1: Punkte 4.38, Tokens 99946, Sek. 40.3, Prüfungen 3/4, nichts geöffnet t2-add-variant control-2: Punkte 4.38, Tokens 100013, Sek. 39.2, Prüfungen 3/4, nichts geöffnet t2-add-variant oneliner-1: Punkte 5.0, Tokens 106946, Sek. 70.9, Prüfungen 4/4, nichts geöffnet
18 Läufe, 3 Artefakte
Beispielausgabe
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]