Pattern Critic Systematically review pattern code for violations of Common Tools documentation rules and gotchas. Workflow Read the pattern file to review Check each category below against the code Output results in the checklist format (see Output Format) For any [FAIL], include the line number and fix Violation Categories 1. Module Scope Violations Check that these are NOT inside the pattern body: Violation Fix handler() defined inside pattern Move to module scope, or use action() instead lift() immediately invoked ( lift(...)(args) ) Use computed() or define lift at module scope Helper functions defined inside pattern Move to module scope Allowed inside patterns: computed() , action() , .map() callbacks, JSX event handlers. 2. Reactivity Violations Violation Fix
text ${someProp}`
[NAME]: computed(() => \
text ${someProp})`
Writable.of(reactiveValue)
Initialize empty, set in handler/action
.get()
on computed/lift result
Access directly (only Writable has .get())
items.filter(...)
inline in JSX
Wrap in
computed()
outside JSX
items.sort(...)
inline in JSX
Wrap in
computed()
outside JSX
Nested computed with outer scope vars
Pre-compute with lift or outer computed
lift() closing over reactive deps
Pass deps as explicit params
Cells from composed patterns in ifElse
Wrap in local
computed()
3. Conditional Rendering
Violation
Fix
onClick
inside
computed()
Move button outside, use
disabled
attr
Note:
Ternaries work fine in JSX - the transformer auto-converts them to
ifElse()
. Both
{show ?
Pattern Review: [filename]
1. Module Scope
- [PASS] No handler() inside pattern
- [FAIL] lift() immediately invoked (line 23) Fix: Use computed() or move lift to module scope
2. Reactivity
- PASS properly wrapped
- [FAIL] Writable.of(deck.name) uses reactive value (line 15) Fix: Initialize empty, set in action()
3. Conditional Rendering
- [PASS] Using ifElse() correctly
- [N/A] No conditional rendering [...continue for all categories...]
11. Action vs Handler Choice
- [PASS] Actions used for pattern-specific handlers
- [FAIL] handler() used but not needed for multi-binding (line 45) Fix: Convert to action() inside pattern body
12. Design Review
- [PASS] Clear entity boundaries
- [WARN] Handler names could be clearer (moveCard vs reorderCard)
- [PASS] Unidirectional data flow
13. Regression Check (if updating)
- [PASS] Existing tests pass
- [N/A] No type signature changes
Summary
- Passed: 22
- Failed: 3
- Warnings: 1
- N/A: 2
Priority Fixes
- [Line 15] Writable.of() with reactive value
- [Line 23] lift() inside pattern
- [Line 45] Missing $ prefix on binding Documentation References docs/development/debugging/README.md
- Error reference table docs/development/debugging/gotchas/
- Individual gotcha files docs/common/components/COMPONENTS.md
- UI components and binding docs/common/capabilities/llm.md
- LLM integration
Quick Patterns
Correct action() Usage (Default Choice)
// action() inside pattern body - closes over pattern variables
export
default
pattern
<
MyInput
,
MyOutput
( ( { items , title } ) => { const menuOpen = Writable . of ( false ) ; // Action closes over menuOpen - no binding needed const toggleMenu = action ( ( ) => menuOpen . set ( ! menuOpen . get ( ) ) ) ; // Action closes over items - no binding needed const addItem = action ( ( ) => items . push ( { title : title . get ( ) } ) ) ; return { [ UI ] : ( <
< ct - button onClick = { toggleMenu }
Menu < / ct - button
< ct - button onClick = { addItem }
Add < / ct - button
< /
) , items , } ; } ) ; Correct handler() Usage (Only for Multi-Binding) // handler() at module scope - will be bound with different items in .map() const deleteItem = handler < void , { item : Writable < Item
; items : Writable < Item [ ]
}
( ( _ , { item , items } ) => { const list = items . get ( ) ; items . set ( list . filter ( i => i !== item ) ) ; } ) ; export default pattern < MyInput , MyOutput
( ( { items } ) => ( { [ UI ] : ( < ul
{ items . map ( ( item ) => ( < li
{ item . name } { / Each item gets its own binding / } < ct - button onClick = { deleteItem ( { item , items } ) }
Delete < / ct - button
< / li
) ) } < / ul
) , items , } ) ) ; Correct Reactive NAME export default pattern < Input
( ( { deck } ) => ( { [ NAME ] : computed ( ( ) =>
Study: ${ deck . name }) , // ... } ) ) ; Correct Conditional Rendering // Both are valid - ternaries auto-transform to ifElse() { showDetails ? < divDetails content < / div
: null } { ifElse ( showDetails , < div
Details content < / div
, null ) } Correct Style Syntax < div style = { { display : "flex" , gap : "1rem" } }
< ct - vstack style = "flex: 1; padding: 1rem;"
Content < / ct - vstack
< / div