Vue 3 patterns — Composition API, composables, reactivity, component design,
97
93%
Does it follow best practices?
Impact
99%
1.33xAverage score across 8 eval scenarios
Passed
No known issues
{
"context": "Tests whether the agent correctly distinguishes between computed (for derived values with no side effects), watch (for side effects triggered by specific state changes), and watchEffect (for side effects that depend on multiple reactive sources), avoiding common pattern misuse.",
"type": "weighted_checklist",
"checklist": [
{
"name": "computed for derived values",
"description": "Values derived from reactive state (e.g. filtered list, total count, display label) are declared with `computed()` and contain no side effects — no API calls, no DOM mutations, no router calls inside computed",
"max_score": 12
},
{
"name": "watch for specific state side effects",
"description": "Side effects triggered by a specific state change (e.g. storing filter preferences to localStorage when filters change, redirecting when auth state changes) use `watch(source, callback)` with an explicit source",
"max_score": 12
},
{
"name": "watchEffect for multi-source side effects",
"description": "A side effect that depends on multiple reactive sources simultaneously (e.g. updating the document title or a URL query string based on multiple filter values) uses `watchEffect()` rather than multiple `watch()` calls or `computed()`",
"max_score": 12
},
{
"name": "No side effects in computed",
"description": "No `computed()` call contains side effects such as API calls, router.push(), localStorage writes, or console.log inside its getter function",
"max_score": 10
},
{
"name": "watch getter for nested property",
"description": "If watching a nested property of a ref or reactive object, the watch uses a getter function: `watch(() => obj.value.property, handler)` rather than watching the parent ref directly",
"max_score": 8
},
{
"name": "script setup lang ts",
"description": "All .vue files use `<script setup lang=\"ts\">`",
"max_score": 6
},
{
"name": "defineProps generic typing",
"description": "Any component props are declared using `defineProps<T>()` with a TypeScript interface",
"max_score": 6
},
{
"name": "ref for primitive state",
"description": "Primitive reactive values (booleans, strings, numbers, nulls) are declared with `ref()`, not `reactive()`",
"max_score": 6
},
{
"name": "No reassignment of reactive objects",
"description": "If any reactive() object is reset or bulk-updated, it uses `Object.assign(target, newValues)` rather than reassigning the variable to a new reactive() object",
"max_score": 8
},
{
"name": "aria-label on buttons",
"description": "All interactive button elements include a descriptive `:aria-label` binding",
"max_score": 6
},
{
"name": "role=alert on error regions",
"description": "Error message containers include `role=\"alert\"`",
"max_score": 6
},
{
"name": "v-if/v-else-if/v-else chain",
"description": "Any template section displaying async data uses a `v-if` / `v-else-if` / `v-else` chain for loading, error, and success states",
"max_score": 8
}
]
}evals
scenario-1
scenario-2
scenario-3
scenario-4
scenario-5
scenario-6
scenario-7
scenario-8
skills
vue-best-practices
verifiers