All skills

composition-patterns (vercel-labs/agent-skills)

vercel-composition-patterns: moves a boolean-prop React component to a compound-component API (control 3.7, skill 4.4), at no extra tokens, but a one-sentence prompt of the same advice scores 4.7

SituationalSafe

25.1 / 30

Lift over no skill: +0.68

Situational: the lift +0.68 is under +1.0. Recommended needs a total of 22/30, output quality of 3.5, and a lift of +1.0.

SOTA

Pass

Tested with Codex.

Covers Codex, Claude Code

Codex CLI 0.166 (SOTA tier): 18 graded runs. A pass here also covers Claude Code under our tier rule; it says nothing about the second tier.

Scores

Each dimension is 0 to 5 and the six add up to the total out of 30. 5 is better on every dimension. Cost 5 means cheap and fast. Install 5 means easy.

Trigger reliability
5.0
Lift over no skill
2.7
Output quality
4.4
Token and time cost
5.0
Install and portability
4.0
Safety
4.0

Security review

Safe

Static scan: 0 CRITICAL / 0 HIGH / 0 MEDIUM. Manual review: Safe to run in a no-network sandbox (no findings). A clean scan does not prove a skill safe.

Install

npx skills@latest add vercel-labs/agent-skills --skill composition-patterns

Source

Pinned commit: 1a2c79d4e6c4a2283dc2c614b6538cbe1ed5ca62

Tested: 2026-11-02

What we tested

  • Replace boolean props with composition — Composer.tsx has grown a pile of boolean props. Refactor it into a flexible component API that does not need them. Keep it in the single file Composer.tsx and keep the behaviour.
  • Add a variant without adding props — Add a 'quick reply' composer: a one-line input that sends on Enter, with no attachments or emoji button. Do not add more boolean props to Composer. Edit Composer.tsx.
  • Review a component API — Review the component API of Composer.tsx: what is wrong with it and what would you change? Do not change any files.

Pros

  • Lifts the mean score from 3.71 to 4.39 over no skill (+0.68).
  • The skill was picked up without being named: Skill file read in 6/6 treatment runs.
  • No token overhead: 1.03x the tokens of no skill.

Cons

  • A one-sentence prompt scored 4.7, against 4.39 with the skill (-0.31).
  • n=2 per task per arm; differences under about 1 point are noise.

Review

Codex CLI 0.166 (gpt-6-astra, low reasoning) on a dedicated Windows test PC, a fresh temporary project and temporary HOME/TMPDIR/caches per run, skill installed (treatment) or absent (control) or absent with the skill's core requirement squeezed into one sentence appended to the prompt (one-liner group), 2 runs per group per task, network off, /tmp writes blocked. The prompts never mention the skill (except the wording noted below). One chat-composer component (Composer.tsx) with a pile of boolean props. Tasks: refactor it into a flexible API keeping behaviour, add a 'quick reply' one-line composer without adding more booleans, and review the component API (no edits). Prompts do not name any pattern. The one-liner names the patterns (explicit variants from compound parts, shared context with state/actions, children instead of renderX, state lifted into a provider).

Three-arm comparison

ArmMean score (0-5)Tokens per run
Without the skill3.71103k
One-sentence prompt4.7105k
With the skill4.39105k

Measured with Codex CLI over 18 runs, scored by rule-based checks with no LLM judge. Total 25.1 / 30, verdict Situational.

Verdict: Situational (25.1 / 30). Source: vercel-labs/agent-skills, skills/composition-patterns, pinned at commit 1a2c79d. Install: npx skills@latest add vercel-labs/agent-skills --skill composition-patterns. Licence: MIT (repo LICENSE).

What it is

The vercel-composition-patterns skill (vercel-labs/agent-skills): React composition guidance: avoid boolean prop proliferation, build compound components that share state through context (a generic state/actions interface), create explicit variant components, prefer children over renderX props, lift state into providers; includes React 19 notes. A SKILL.md plus a rules folder.

How we tested

Codex CLI 0.166 (gpt-6-astra, low reasoning) on a dedicated Windows test PC, a fresh temporary project and temporary HOME/TMPDIR/caches per run, skill installed (treatment) or absent (control) or absent with the skill's core requirement squeezed into one sentence appended to the prompt (one-liner group), 2 runs per group per task, network off, /tmp writes blocked. The prompts never mention the skill (except the wording noted below).

One chat-composer component (Composer.tsx) with a pile of boolean props. Tasks: refactor it into a flexible API keeping behaviour, add a 'quick reply' one-line composer without adding more booleans, and review the component API (no edits). Prompts do not name any pattern. The one-liner names the patterns (explicit variants from compound parts, shared context with state/actions, children instead of renderX, state lifted into a provider).

  1. Replace boolean props with composition — Composer.tsx has grown a pile of boolean props. Refactor it into a flexible component API that does not need them. Keep it in the single file Composer.tsx and keep the behaviour.
  2. Add a variant without adding props — Add a 'quick reply' composer: a one-line input that sends on Enter, with no attachments or emoji button. Do not add more boolean props to Composer. Edit Composer.tsx.
  3. Review a component API — Review the component API of Composer.tsx: what is wrong with it and what would you change? Do not change any files.

Scoring is programmatic (hidden tests / planted-issue recall / constraint checks / render+OCR where relevant), 0–5 per run. No LLM judge decided any score.

Results

The one-liner given to the third group: "Avoid boolean prop proliferation: split modes into explicit variant components built from compound parts that share a context with a generic state/actions interface, use children instead of renderX props, and lift state into a provider."

TaskControl scoreOne-linerWith skillControl tokensOne-liner tokensWith-skill tokensSkill read
t1-refactor3.04.54.0126k127k144k2/2
t2-add-variant4.385.05.099k106k100k2/2
t3-review3.754.584.1781k80k70k2/2

Mean score: 3.71 without vs 4.39 with the skill (delta +0.68). Tokens per run: 103k vs 105k (1.03x); time 53s vs 49s. One-liner group: 4.7 (the skill's gain over the one-liner: -0.31; one-liner's own gain over no skill: +0.99); one-liner tokens 105k per run, so the skill costs 1.00x the one-liner.

What happened

  • Control 3.71, skill 4.39, one-liner 4.70 (n=6 each). Without guidance, control's refactors did use compound parts but in both runs the shared context did not expose a state/actions interface, and in both quick-reply runs the new composer did not send on Enter.
  • The skill's refactors had the context with state and actions (2/2) and its quick-reply composer sent on Enter (2/2). But both treatment refactors, like control, did not create explicit variant components (the one-liner runs did in 2/2), and in the review task both treatment runs and both control runs missed the 'boolean prop proliferation' point (a weak check pattern: reviewers may word it differently).
  • Tokens are the same for skill, one-liner and control (105k, 105k, 103k): the skill read in 6/6 treatment runs without being named, and costs nothing extra.
  • Treatment runs copy the rules folder into the project (14.7 new files per run).

Dimension scores

DimensionScoreWhy
1_output_quality4.39

Mean score 4.39/5 with the skill (programmatic checks, see per-task table).

2_lift_vs_no_skill2.68

+0.68 points vs 3.71 without the skill (n=2 per task; under ~1 point is noise).

3_trigger5.0Skill file read in 6/6 treatment runs.
4_instructions_portability4SKILL.md plus an AGENTS.md index and a rules folder; docs only.
5_efficiency5

1.03x tokens (105k vs 103k per run) and 0.93x time (49s vs 53s).

6_safety_hygiene4Prose only; see security.md.

Limits of this test

  • Rule-based checks on code text (context/provider patterns, variant components, key props); the TypeScript was not compiled or rendered (React is not installed in the sandbox).
  • One check for the review task is weak: it looks for the literal 'boolean prop' wording, which reviewers can express differently.
  • Single component fixture; n=2 per cell.
  • n = 2 per group per task: differences under about 1 point are noise.
  • Rule-based scoring measures what the checks can see; it cannot judge taste or whether a result is the best possible.

Security

See security.md. Scan: 0 CRITICAL / 0 HIGH / 0 MEDIUM. Verdict: safe to run (no-network sandbox). A clean scan does not prove a skill safe.

Bottom line

A decent pattern guide: the compound-component structure appears reliably with the skill and rarely without it, for free. A single sentence naming the patterns did better here, so the skill's benefit is convenience and breadth, not a quality ceiling.

Transcript

Run log for composition-patterns: Codex CLI, network off, per-run temp HOME/TMPDIR, one row per graded run.
Columns: task, arm-run, score (0-5), tokens, seconds, checks passed, skill files the agent opened.

t1-refactor control-1: score 3.0, tokens 130032, secs 72.5, checks 3/5, opened nothing
t1-refactor control-2: score 3.0, tokens 123317, secs 64.3, checks 3/5, opened nothing
t1-refactor oneliner-1: score 4.0, tokens 127545, secs 101.8, checks 4/5, opened nothing
t1-refactor oneliner-2: score 5.0, tokens 127024, secs 89.6, checks 5/5, opened nothing
t1-refactor treatment-1: score 4.0, tokens 158276, secs 67.7, checks 4/5, opened composition-patterns
t1-refactor treatment-2: score 4.0, tokens 130234, secs 59.2, checks 4/5, opened composition-patterns
t2-add-variant control-1: score 4.38, tokens 99946, secs 40.3, checks 3/4, opened nothing
t2-add-variant control-2: score 4.38, tokens 100013, secs 39.2, checks 3/4, opened nothing
t2-add-variant oneliner-1: score 5.0, tokens 106946, secs 70.9, checks 4/4, opened nothing

18 runs, 3 artifacts

Sample output

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]

See Pro