pattern-dev

Use Skill("ct") for ct CLI documentation when running commands.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "pattern-dev" with this command: npx skills add commontoolsinc/labs/commontoolsinc-labs-pattern-dev

Use Skill("ct") for ct CLI documentation when running commands.

You and the user are a team finding the efficient path to their vision.

Always Plan First

Use EnterPlanMode before building. Scale the plan to the task:

Simple pattern (todo list, counter):

  • One file, types + handlers + UI together

  • Minimal clarification needed

  • Plan in 2-3 sentences

Medium pattern (form with validation, data viewer):

  • Maybe split schemas if types get complex

  • Clarify data shape and key actions

  • Plan in a short list

Complex pattern (multi-entity system, integrations):

  • Consider sub-patterns if genuinely distinct concepts

  • Clarify entities, relationships, actions upfront

  • Plan with structure, but don't over-specify

Always start simple. One file first. Split when it helps, not before.

Pattern Structure

Start simple:

packages/patterns/[name]/ └── main.tsx # Everything in one file to start

Split when it helps (not before):

packages/patterns/[name]/ ├── schemas.tsx # Types, if complex ├── main.tsx # Main pattern └── [other].tsx # Extract when reuse is clear

Don't create separate files for every entity. A Project with Task[] can live in one file until complexity demands otherwise.

Development Approach: Sketch → Run → Iterate

Don't write finished code. Write the minimum to see something work:

  • Sketch — Types, one handler, minimal UI. Just enough to render.

  • Run it — deno task ct check main.tsx and see what happens.

  • Verify — Does it render? Does the handler fire? Check console.

  • Iterate — Add the next piece, run again.

Each iteration should be deployable. If you can't run it, you've written too much.

Verification

Run the code, not just tests. The primary verification is: does it work when you run it?

  • deno task ct check main.tsx — See it render, click things, check console

  • Tests for state logic that's hard to verify by clicking

  • Don't write tests for obvious behavior or code that's still evolving

Pattern tests when needed: deno task ct test [file].test.tsx

Delegate to Agents

pattern-maker — Write Code

For implementing pattern code:

Task({ prompt: "Implement [feature]. Keep it simple, one file.", subagent_type: "pattern-maker" })

pattern-user — Deploy & Debug

For deploying and testing with ct CLI:

Task({ prompt: "Deploy and test [pattern].", subagent_type: "pattern-user" })

pattern-critic — Review (when needed)

For checking violations before release or when stuck:

Task({ prompt: "Review [file] for violations.", subagent_type: "pattern-critic" })

Workflow

Not phases, just common sense:

  • Build — Use pattern-maker to sketch and iterate locally (ct check )

  • Review — Use pattern-critic before deploying to catch common mistakes

  • Deploy — Use pattern-user to deploy to toolshed

  • Fix what's broken — Iterate with maker, re-review, redeploy

  • Commit — At milestones, offer to commit

Always run pattern-critic before first deploy. It's fast (uses haiku) and catches mistakes that cause runtime errors. Skip only for tiny fixes where you're confident.

action() vs handler()

Default to action() — define inside pattern body, close over variables:

const Note = pattern<NoteInput, NoteOutput>(({ title, content }) => { const menuOpen = Writable.of(false);

// Action closes over menuOpen - no binding needed const toggleMenu = action(() => menuOpen.set(!menuOpen.get()));

// Action closes over content - no binding needed const clearContent = action(() => content.set(""));

return { /* ... */ }; });

Use handler() only for per-item binding (e.g., in .map() or when the same handler needs different data bound in different places):

// Module scope - will be bound with different items const deleteItem = handler<void, { item: Writable<Item>; items: Writable<Item[]> }>( (_, { item, items }) => { const list = items.get(); items.set(list.filter(i => i !== item)); } );

const List = pattern<ListInput, ListOutput>(({ items }) => ({ [UI]: ( <ul> {items.map((item) => ( <li> {item.name} {/* Each item gets its own binding */} <button onClick={deleteItem({ item, items })}>Delete</button> </li> ))} </ul> ), items, }));

Decision question: Does this handler need different data bound to different instantiations?

  • YES → Use handler() at module scope, bind with item-specific data

  • NO → Use action() inside pattern body, close over what you need

Common Gotchas

Don't use computed() for conditional JSX rendering. Use JSX ternaries instead — the compiler transforms them into reactive ifElse() calls. Nested ternaries work correctly (ternary inside the branch of another ternary is also transformed). Inside a computed() body, ternaries are plain JS where a Writable<boolean> is always truthy. See docs/common/concepts/computed/computed.md .

// ❌ WRONG - computed() for conditional sections {computed(() => { if (!showAdmin.get()) return null; return <div>{showForm ? <form>...</form> : null}</div>; // ^^^^^^^^ plain JS — Writable is always truthy! })}

// ✅ RIGHT - bare JSX ternaries, nest as needed {showAdmin ? <div>{showForm ? <form>...</form> : null}</div> : null}

fetchData URLs must be CORS-friendly. Patterns run in the browser, so any URL passed to fetchData must respond with Access-Control-Allow-Origin: * (or a matching origin). RSS feeds, private APIs, and many .xml endpoints do NOT support CORS. Prefer public JSON APIs that explicitly allow cross-origin requests. If a brief specifies a non-CORS source, replace it with a CORS-compatible alternative that provides equivalent data.

Don't .set() upstream cells inside computed() . Writing to a cell that feeds into the same reactive graph causes cycles (the runtime caps at 101 iterations and throws). Only write to leaf cells — cells whose updates don't feed back into the current computation. If you need to derive a value AND produce a side-effect, split them: use computed() for the derivation and action() for the write.

// ❌ WRONG - .set() on a cell that feeds this computed → reactive cycle const sorted = computed(() => { const items = allItems.get(); statusMessage.set(${items.length} items); // statusMessage feeds UI that reads allItems return items.sort(compareFn); });

// ✅ RIGHT - computed for derivation, action for side-effects const sorted = computed(() => [...allItems.get()].sort(compareFn)); const updateStatus = action(() => statusMessage.set(${allItems.get().length} items));

When patterns compose, output/input field names must match exactly. If pattern A's output feeds into pattern B's input, the field names in A's Output type must match B's Input type character-for-character. There is no automatic mapping — chartData and chartEntries are different fields. Coordinate naming across related patterns before building.

Documentation

Start with docs/common/patterns/ —especially docs/common/patterns/meta/ which contains generalizable idioms that grow over time.

Prefer docs over existing patterns in packages/patterns/ —docs contain validated snippets while existing patterns may be outdated. Use packages/patterns/ as reference but don't copy blindly.

Phase skills consult as needed:

  • Types: docs/common/concepts/types-and-schemas/

  • Actions/handlers: docs/common/concepts/action.md , docs/common/concepts/handler.md

  • Testing: docs/common/workflows/pattern-testing.md

  • Components: docs/common/components/COMPONENTS.md

  • Debugging: docs/development/debugging/

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Research

knowledge-base

No summary provided by upstream source.

Repository SourceNeeds Review
General

lit-component

No summary provided by upstream source.

Repository SourceNeeds Review
General

pattern-critic

No summary provided by upstream source.

Repository SourceNeeds Review