CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl-labs/vue-best-practices

Vue 3 patterns — Composition API, composables, reactivity, component design,

97

1.33x
Quality

93%

Does it follow best practices?

Impact

99%

1.33x

Average score across 8 eval scenarios

SecuritybySnyk

Passed

No known issues

Overview
Quality
Evals
Security
Files

criteria.jsonevals/scenario-7/

{
  "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

tile.json