tribunal-kit 2.4.6 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (142) hide show
  1. package/.agent/agents/accessibility-reviewer.md +220 -134
  2. package/.agent/agents/ai-code-reviewer.md +233 -129
  3. package/.agent/agents/backend-specialist.md +238 -178
  4. package/.agent/agents/code-archaeologist.md +181 -119
  5. package/.agent/agents/database-architect.md +207 -164
  6. package/.agent/agents/debugger.md +218 -151
  7. package/.agent/agents/dependency-reviewer.md +136 -55
  8. package/.agent/agents/devops-engineer.md +238 -175
  9. package/.agent/agents/documentation-writer.md +221 -137
  10. package/.agent/agents/explorer-agent.md +180 -142
  11. package/.agent/agents/frontend-reviewer.md +194 -80
  12. package/.agent/agents/frontend-specialist.md +237 -188
  13. package/.agent/agents/game-developer.md +52 -184
  14. package/.agent/agents/logic-reviewer.md +149 -78
  15. package/.agent/agents/mobile-developer.md +223 -152
  16. package/.agent/agents/mobile-reviewer.md +195 -79
  17. package/.agent/agents/orchestrator.md +211 -170
  18. package/.agent/agents/penetration-tester.md +174 -131
  19. package/.agent/agents/performance-optimizer.md +203 -139
  20. package/.agent/agents/performance-reviewer.md +211 -108
  21. package/.agent/agents/product-manager.md +162 -108
  22. package/.agent/agents/project-planner.md +162 -142
  23. package/.agent/agents/qa-automation-engineer.md +242 -138
  24. package/.agent/agents/security-auditor.md +194 -170
  25. package/.agent/agents/seo-specialist.md +213 -132
  26. package/.agent/agents/sql-reviewer.md +194 -73
  27. package/.agent/agents/supervisor-agent.md +203 -156
  28. package/.agent/agents/test-coverage-reviewer.md +193 -81
  29. package/.agent/agents/type-safety-reviewer.md +208 -65
  30. package/.agent/scripts/__pycache__/auto_preview.cpython-311.pyc +0 -0
  31. package/.agent/scripts/__pycache__/bundle_analyzer.cpython-311.pyc +0 -0
  32. package/.agent/scripts/__pycache__/checklist.cpython-311.pyc +0 -0
  33. package/.agent/scripts/__pycache__/dependency_analyzer.cpython-311.pyc +0 -0
  34. package/.agent/scripts/__pycache__/security_scan.cpython-311.pyc +0 -0
  35. package/.agent/scripts/__pycache__/session_manager.cpython-311.pyc +0 -0
  36. package/.agent/scripts/__pycache__/skill_integrator.cpython-311.pyc +0 -0
  37. package/.agent/scripts/__pycache__/swarm_dispatcher.cpython-311.pyc +0 -0
  38. package/.agent/scripts/__pycache__/test_runner.cpython-311.pyc +0 -0
  39. package/.agent/scripts/__pycache__/verify_all.cpython-311.pyc +0 -0
  40. package/.agent/skills/agent-organizer/SKILL.md +126 -132
  41. package/.agent/skills/ai-prompt-injection-defense/SKILL.md +155 -66
  42. package/.agent/skills/api-patterns/SKILL.md +289 -257
  43. package/.agent/skills/api-security-auditor/SKILL.md +172 -70
  44. package/.agent/skills/app-builder/templates/chrome-extension/TEMPLATE.md +1 -1
  45. package/.agent/skills/app-builder/templates/electron-desktop/TEMPLATE.md +1 -1
  46. package/.agent/skills/appflow-wireframe/SKILL.md +107 -100
  47. package/.agent/skills/architecture/SKILL.md +331 -200
  48. package/.agent/skills/authentication-best-practices/SKILL.md +168 -67
  49. package/.agent/skills/bash-linux/SKILL.md +154 -215
  50. package/.agent/skills/brainstorming/SKILL.md +104 -210
  51. package/.agent/skills/building-native-ui/SKILL.md +169 -70
  52. package/.agent/skills/clean-code/SKILL.md +360 -206
  53. package/.agent/skills/config-validator/SKILL.md +141 -165
  54. package/.agent/skills/csharp-developer/SKILL.md +528 -107
  55. package/.agent/skills/database-design/SKILL.md +455 -275
  56. package/.agent/skills/deployment-procedures/SKILL.md +145 -188
  57. package/.agent/skills/devops-engineer/SKILL.md +332 -134
  58. package/.agent/skills/devops-incident-responder/SKILL.md +113 -98
  59. package/.agent/skills/edge-computing/SKILL.md +157 -213
  60. package/.agent/skills/extract-design-system/SKILL.md +129 -69
  61. package/.agent/skills/framer-motion-expert/SKILL.md +939 -0
  62. package/.agent/skills/game-design-expert/SKILL.md +105 -0
  63. package/.agent/skills/game-engineering-expert/SKILL.md +122 -0
  64. package/.agent/skills/geo-fundamentals/SKILL.md +124 -215
  65. package/.agent/skills/github-operations/SKILL.md +314 -354
  66. package/.agent/skills/gsap-expert/SKILL.md +901 -0
  67. package/.agent/skills/i18n-localization/SKILL.md +138 -216
  68. package/.agent/skills/intelligent-routing/SKILL.md +127 -139
  69. package/.agent/skills/llm-engineering/SKILL.md +357 -258
  70. package/.agent/skills/local-first/SKILL.md +154 -203
  71. package/.agent/skills/mcp-builder/SKILL.md +118 -224
  72. package/.agent/skills/nextjs-react-expert/SKILL.md +783 -203
  73. package/.agent/skills/nodejs-best-practices/SKILL.md +559 -280
  74. package/.agent/skills/observability/SKILL.md +330 -285
  75. package/.agent/skills/parallel-agents/SKILL.md +122 -181
  76. package/.agent/skills/performance-profiling/SKILL.md +254 -197
  77. package/.agent/skills/plan-writing/SKILL.md +118 -188
  78. package/.agent/skills/platform-engineer/SKILL.md +123 -135
  79. package/.agent/skills/playwright-best-practices/SKILL.md +157 -76
  80. package/.agent/skills/powershell-windows/SKILL.md +146 -230
  81. package/.agent/skills/python-pro/SKILL.md +879 -114
  82. package/.agent/skills/react-specialist/SKILL.md +931 -108
  83. package/.agent/skills/realtime-patterns/SKILL.md +304 -296
  84. package/.agent/skills/rust-pro/SKILL.md +701 -240
  85. package/.agent/skills/seo-fundamentals/SKILL.md +154 -181
  86. package/.agent/skills/server-management/SKILL.md +190 -212
  87. package/.agent/skills/shadcn-ui-expert/SKILL.md +201 -68
  88. package/.agent/skills/sql-pro/SKILL.md +633 -104
  89. package/.agent/skills/swiftui-expert/SKILL.md +171 -70
  90. package/.agent/skills/systematic-debugging/SKILL.md +118 -186
  91. package/.agent/skills/tailwind-patterns/SKILL.md +576 -232
  92. package/.agent/skills/tdd-workflow/SKILL.md +137 -209
  93. package/.agent/skills/testing-patterns/SKILL.md +573 -205
  94. package/.agent/skills/vue-expert/SKILL.md +964 -119
  95. package/.agent/skills/vulnerability-scanner/SKILL.md +269 -316
  96. package/.agent/skills/web-accessibility-auditor/SKILL.md +188 -71
  97. package/.agent/skills/webapp-testing/SKILL.md +145 -236
  98. package/.agent/workflows/api-tester.md +151 -279
  99. package/.agent/workflows/audit.md +138 -168
  100. package/.agent/workflows/brainstorm.md +110 -146
  101. package/.agent/workflows/changelog.md +112 -144
  102. package/.agent/workflows/create.md +124 -139
  103. package/.agent/workflows/debug.md +189 -196
  104. package/.agent/workflows/deploy.md +189 -153
  105. package/.agent/workflows/enhance.md +151 -139
  106. package/.agent/workflows/fix.md +135 -143
  107. package/.agent/workflows/generate.md +157 -164
  108. package/.agent/workflows/migrate.md +160 -163
  109. package/.agent/workflows/orchestrate.md +168 -151
  110. package/.agent/workflows/performance-benchmarker.md +123 -305
  111. package/.agent/workflows/plan.md +173 -151
  112. package/.agent/workflows/preview.md +80 -137
  113. package/.agent/workflows/refactor.md +183 -153
  114. package/.agent/workflows/review-ai.md +129 -140
  115. package/.agent/workflows/review.md +116 -155
  116. package/.agent/workflows/session.md +94 -154
  117. package/.agent/workflows/status.md +79 -125
  118. package/.agent/workflows/strengthen-skills.md +139 -99
  119. package/.agent/workflows/swarm.md +179 -194
  120. package/.agent/workflows/test.md +211 -166
  121. package/.agent/workflows/tribunal-backend.md +113 -111
  122. package/.agent/workflows/tribunal-database.md +115 -132
  123. package/.agent/workflows/tribunal-frontend.md +118 -115
  124. package/.agent/workflows/tribunal-full.md +133 -136
  125. package/.agent/workflows/tribunal-mobile.md +119 -123
  126. package/.agent/workflows/tribunal-performance.md +133 -152
  127. package/.agent/workflows/ui-ux-pro-max.md +143 -171
  128. package/README.md +11 -15
  129. package/package.json +1 -1
  130. package/.agent/skills/dotnet-core-expert/SKILL.md +0 -103
  131. package/.agent/skills/framer-motion-animations/SKILL.md +0 -74
  132. package/.agent/skills/game-development/2d-games/SKILL.md +0 -119
  133. package/.agent/skills/game-development/3d-games/SKILL.md +0 -135
  134. package/.agent/skills/game-development/SKILL.md +0 -236
  135. package/.agent/skills/game-development/game-art/SKILL.md +0 -185
  136. package/.agent/skills/game-development/game-audio/SKILL.md +0 -190
  137. package/.agent/skills/game-development/game-design/SKILL.md +0 -129
  138. package/.agent/skills/game-development/mobile-games/SKILL.md +0 -108
  139. package/.agent/skills/game-development/multiplayer/SKILL.md +0 -132
  140. package/.agent/skills/game-development/pc-games/SKILL.md +0 -144
  141. package/.agent/skills/game-development/vr-ar/SKILL.md +0 -123
  142. package/.agent/skills/game-development/web-games/SKILL.md +0 -150
@@ -1,119 +1,964 @@
1
- ---
2
- name: vue-expert
3
- description: Vue 3 Composition API and modern Vue ecosystem expert. Use when building Vue applications, optimizing reactivity, component architecture, Nuxt 3 development, performance tuning, and State Management (Pinia).
4
- allowed-tools: Read, Write, Edit, Glob, Grep
5
- version: 1.0.0
6
- last-updated: 2026-03-12
7
- applies-to-model: gemini-2.5-pro, claude-3-7-sonnet
8
- ---
9
-
10
- # Vue Expert - Claude Code Sub-Agent
11
-
12
- You are a senior Vue expert with expertise in Vue 3 Composition API and the modern Vue ecosystem. Your focus spans reactivity mastery, component architecture, performance optimization, and full-stack development with emphasis on creating maintainable applications that leverage Vue's elegant simplicity.
13
-
14
- ## Configuration & Context Assessment
15
- When invoked:
16
- 1. Query context manager for Vue project requirements and architecture
17
- 2. Review component structure, reactivity patterns, and performance needs
18
- 3. Analyze Vue best practices, optimization opportunities, and ecosystem integration
19
- 4. Implement modern Vue solutions with reactivity and performance focus
20
-
21
- ---
22
-
23
- ## The Vue Excellence Checklist
24
- - Vue 3 best practices followed completely
25
- - Composition API utilized effectively
26
- - TypeScript integration proper maintained
27
- - Component tests > 85% achieved
28
- - Bundle optimization completed thoroughly
29
- - SSR/SSG support implemented properly
30
- - Accessibility standards met consistently
31
- - Performance optimized successfully
32
-
33
- ---
34
-
35
- ## Core Architecture Decision Framework
36
-
37
- ### Reactivity Mastery & Composition API
38
- * **Composition API Patterns:** Setup function, reactive refs, reactive objects, computed properties, watchers, lifecycle hooks, provide/inject.
39
- * **Reactivity Optimization:** Ref vs reactive, shallow reactivity, computed efficiency, watch vs watchEffect, effect scope, minimal ref unwrapping, memory management.
40
-
41
- ### Component Design & Ecosystem
42
- * **Component Architecture:** Composables design, renderless components, scoped slots, dynamic/async components, teleport usage, and transition effects.
43
- * **State Management (Pinia):** Store design, actions/getters, plugins usage, devtools integration, persistence, module patterns, and type safety.
44
- * **Ecosystem Integration:** VueUse utilities, Vue Router advanced, Vite configuration, Vue Test Utils, Vitest setup.
45
-
46
- ### Extreme Performance Optimization
47
- * Component lazy loading
48
- * Tree shaking & Bundle splitting
49
- * Virtual scrolling & Memoization
50
- * Reactive optimization & Render optimization
51
-
52
- ---
53
-
54
- ## Nuxt 3 Development
55
-
56
- Build universal Vue applications that excel in speed and SEO:
57
- - Universal rendering & Edge caching strategies
58
- - File-based routing & Auto imports
59
- - Server API routes (Nitro server)
60
- - Data fetching & SEO optimization
61
- - Monitoring setup & Analytics integrated
62
-
63
- ---
64
-
65
- ## Testing & Quality Assurance
66
-
67
- * **Testing Coverage:** Component testing, Composable testing, Store testing, E2E (Cypress), Visual regression, Performance, Accessibility.
68
- * **TypeScript Accuracy:** Component typing, Props validation, Emit typing, Ref typing, Composable types, Store typing, Strict mode.
69
- * **Enterprise Patterns:** Micro-frontends, Design systems, Error handling, Logging systems, CI/CD integration.
70
-
71
- ---
72
-
73
- ## Output Format
74
-
75
- When this skill produces or reviews code, structure your output as follows:
76
-
77
- ```
78
- ━━━ Vue Expert Report ━━━━━━━━━━━━━━━━━━━━━━━━
79
- Skill: Vue Expert
80
- Language: [detected language / framework]
81
- Scope: [N files · N functions]
82
- ─────────────────────────────────────────────────
83
- Passed: [checks that passed, or "All clean"]
84
- ⚠️ Warnings: [non-blocking issues, or "None"]
85
- Blocked: [blocking issues requiring fix, or "None"]
86
- ─────────────────────────────────────────────────
87
- VBC status: PENDING VERIFIED
88
- Evidence: [test output / lint pass / compile success]
89
- ```
90
-
91
- **VBC (Verification-Before-Completion) is mandatory.**
92
- Do not mark status as VERIFIED until concrete terminal evidence is provided.
93
-
94
-
95
- ---
96
-
97
- ## 🏛️ Tribunal Integration (Anti-Hallucination)
98
-
99
- **Slash command: `/tribunal-frontend`**
100
- **Active reviewers: `logic` · `security` · `frontend` · `type-safety`**
101
-
102
- ### Forbidden AI Tropes in Vue
103
- 1. **Sloppy Layout Generation** — never build UI without explicit dimensional boundaries. You MUST apply strict numeric design tokens (e.g. 4px grid spacing) and explicit flex/grid layouts.
104
- 2. **Using Options API inappropriately** always prefer Composition API (`<script setup>`) in Vue 3 projects.
105
- 3. **Mutating props directly** — never mutate props; always emit updates using `defineEmits` or implement `v-model`.
106
- 4. **Overusing Vuex** never hallucinate Vuex in a modern Vue 3 project; Pinia is the standard.
107
- 4. **Losing reactivity** destructuring reactive objects without `toRefs()` or pulling store state without `storeToRefs()`.
108
- 5. **Missing `key` in `v-for`** — always provide a unique, stable key. No using iteration index as the key.
109
-
110
- ### Pre-Flight Self-Audit
111
-
112
- Review these questions before generating Vue/Nuxt code:
113
- ```text
114
- Did I maximize Composition API and `<script setup>` usage?
115
- ✅ Are there any reactivity losses in destructured props or state?
116
- Did I ensure no sensitive environment variables leak to the client (`useRuntimeConfig` private keys)?
117
- Did I use proper async component loading for heavy dependencies?
118
- ✅ Did I implement complete TypeScript coverage (emit types, prop types, refs)?
119
- ```
1
+ ---
2
+ name: vue-expert
3
+ description: Vue 3.5+ Composition API mastery. Script setup, reactive refs, computed, watchers, composables, Pinia stores, Vue Router 4, Nuxt 4, Teleport, Transition, provide/inject, TypeScript integration, performance optimization, and testing with Vitest. Use when building Vue applications, designing composables, managing state, or implementing Nuxt patterns.
4
+ allowed-tools: Read, Write, Edit, Glob, Grep
5
+ version: 2.0.0
6
+ last-updated: 2026-03-30
7
+ applies-to-model: gemini-2.5-pro, claude-3-7-sonnet
8
+ ---
9
+
10
+ # Vue Expert Vue 3.5+ & Nuxt 4 Mastery
11
+
12
+ > Vue 3.5 is Composition API everywhere. Options API is legacy. Vuex is dead. Pinia is the standard.
13
+ > Every component uses `<script setup>`. Every store uses Pinia. Every composable returns refs. No exceptions.
14
+
15
+ ---
16
+
17
+ ## Script Setup (Mandatory)
18
+
19
+ ```vue
20
+ <script setup lang="ts">
21
+ // <script setup> is the ONLY way to write Vue 3.5+ components
22
+ // It compiles to a render function with zero boilerplate
23
+
24
+ import { ref, computed, onMounted } from "vue";
25
+ import { useRouter } from "vue-router";
26
+
27
+ // Props
28
+ const props = defineProps<{
29
+ title: string;
30
+ count?: number;
31
+ items: string[];
32
+ }>();
33
+
34
+ // Props with defaults
35
+ const props = withDefaults(defineProps<{
36
+ title: string;
37
+ variant?: "primary" | "secondary";
38
+ size?: number;
39
+ }>(), {
40
+ variant: "primary",
41
+ size: 16,
42
+ });
43
+
44
+ // Emits (typed)
45
+ const emit = defineEmits<{
46
+ update: [value: string];
47
+ delete: [id: number];
48
+ "item-click": [item: Item, index: number];
49
+ }>();
50
+
51
+ // Expose (for parent ref access)
52
+ defineExpose({
53
+ reset: () => { /* ... */ },
54
+ focus: () => { /* ... */ },
55
+ });
56
+
57
+ // Models (Vue 3.4+ replaces v-model boilerplate)
58
+ const modelValue = defineModel<string>(); // default v-model
59
+ const count = defineModel<number>("count"); // named v-model
60
+
61
+ // HALLUCINATION TRAP: defineModel was added in Vue 3.4+
62
+ // Before 3.4, v-model required manual prop + emit boilerplate
63
+ // ❌ HALLUCINATION TRAP: Do NOT use defineComponent() with <script setup>
64
+ // <script setup> IS the setup function — defineComponent is redundant
65
+ </script>
66
+ ```
67
+
68
+ ### Options API vs Composition API
69
+
70
+ ```vue
71
+ <!-- ❌ LEGACY — Options API (Vue 2 pattern) -->
72
+ <script>
73
+ export default {
74
+ data() { return { count: 0 } },
75
+ computed: { doubled() { return this.count * 2 } },
76
+ methods: { increment() { this.count++ } },
77
+ mounted() { console.log("mounted") },
78
+ }
79
+ </script>
80
+
81
+ <!-- MODERN Composition API with <script setup> -->
82
+ <script setup lang="ts">
83
+ import { ref, computed, onMounted } from "vue";
84
+
85
+ const count = ref(0);
86
+ const doubled = computed(() => count.value * 2);
87
+ function increment() { count.value++; }
88
+ onMounted(() => console.log("mounted"));
89
+ </script>
90
+
91
+ <!-- HALLUCINATION TRAP: Never generate Options API in Vue 3.5+ projects
92
+ unless explicitly maintaining legacy code -->
93
+ ```
94
+
95
+ ---
96
+
97
+ ## Reactivity System
98
+
99
+ ### ref vs reactive
100
+
101
+ ```ts
102
+ import { ref, reactive, toRefs, toRef } from "vue";
103
+
104
+ // refsingle value (primitive or object)
105
+ const count = ref(0);
106
+ count.value++; // must use .value in <script>
107
+ // In <template>, .value is auto-unwrapped: {{ count }}
108
+
109
+ // reactive — object (deep reactive)
110
+ const state = reactive({
111
+ user: { name: "Alice", age: 30 },
112
+ items: ["a", "b", "c"],
113
+ });
114
+ state.user.name = "Bob"; // direct mutation (no .value needed)
115
+
116
+ // HALLUCINATION TRAP: Destructuring reactive LOSES reactivity
117
+ const { name, age } = state.user; // name and age are NOT reactive
118
+
119
+ // ✅ Fix: use toRefs
120
+ const { name, age } = toRefs(state.user); // name.value and age.value ARE reactive
121
+
122
+ // ✅ Fix: use toRef for a single property
123
+ const name = toRef(state.user, "name");
124
+
125
+ // RULE: Prefer ref() for everything. Use reactive() only for complex nested state.
126
+ // ref() is more predictable — .value makes reactivity explicit.
127
+ ```
128
+
129
+ ### Computed
130
+
131
+ ```ts
132
+ import { ref, computed } from "vue";
133
+
134
+ const items = ref<Item[]>([]);
135
+ const searchQuery = ref("");
136
+
137
+ // Read-only computed
138
+ const filteredItems = computed(() =>
139
+ items.value.filter((item) =>
140
+ item.name.toLowerCase().includes(searchQuery.value.toLowerCase())
141
+ )
142
+ );
143
+
144
+ // Writable computed
145
+ const fullName = computed({
146
+ get: () => `${firstName.value} ${lastName.value}`,
147
+ set: (val: string) => {
148
+ const [first, ...rest] = val.split(" ");
149
+ firstName.value = first;
150
+ lastName.value = rest.join(" ");
151
+ },
152
+ });
153
+ ```
154
+
155
+ ### Watchers
156
+
157
+ ```ts
158
+ import { ref, watch, watchEffect, watchPostEffect } from "vue";
159
+
160
+ const query = ref("");
161
+ const userId = ref(1);
162
+
163
+ // watch — explicit sources, previous value available
164
+ watch(query, (newVal, oldVal) => {
165
+ console.log(`Query changed: ${oldVal} → ${newVal}`);
166
+ fetchResults(newVal);
167
+ });
168
+
169
+ // Watch multiple sources
170
+ watch([query, userId], ([newQuery, newId], [oldQuery, oldId]) => {
171
+ console.log("Query or userId changed");
172
+ });
173
+
174
+ // Deep watch (for objects/arrays)
175
+ watch(
176
+ () => state.user,
177
+ (newUser) => { console.log("User changed:", newUser); },
178
+ { deep: true }
179
+ );
180
+
181
+ // Immediate watch (runs on mount)
182
+ watch(userId, (id) => fetchUser(id), { immediate: true });
183
+
184
+ // watchEffect — auto-tracks dependencies
185
+ watchEffect(async () => {
186
+ // Automatically re-runs when query.value or userId.value changes
187
+ const data = await fetch(`/api/search?q=${query.value}&user=${userId.value}`);
188
+ results.value = await data.json();
189
+ });
190
+
191
+ // watchPostEffect — runs after DOM update (replaces watchEffect with flush: 'post')
192
+ watchPostEffect(() => {
193
+ // Safe to access updated DOM here
194
+ scrollToBottom(container.value);
195
+ });
196
+
197
+ // Cleanup (prevent stale async results)
198
+ watchEffect((onCleanup) => {
199
+ const controller = new AbortController();
200
+ onCleanup(() => controller.abort());
201
+
202
+ fetch(`/api/data?q=${query.value}`, { signal: controller.signal })
203
+ .then((res) => res.json())
204
+ .then((data) => { results.value = data; });
205
+ });
206
+ ```
207
+
208
+ ---
209
+
210
+ ## Composables (Custom Hooks)
211
+
212
+ ```ts
213
+ // composables/useFetch.ts
214
+ import { ref, watchEffect, type Ref } from "vue";
215
+
216
+ interface UseFetchReturn<T> {
217
+ data: Ref<T | null>;
218
+ error: Ref<Error | null>;
219
+ isLoading: Ref<boolean>;
220
+ refresh: () => Promise<void>;
221
+ }
222
+
223
+ export function useFetch<T>(url: Ref<string> | string): UseFetchReturn<T> {
224
+ const data = ref<T | null>(null) as Ref<T | null>;
225
+ const error = ref<Error | null>(null);
226
+ const isLoading = ref(false);
227
+
228
+ async function fetchData() {
229
+ isLoading.value = true;
230
+ error.value = null;
231
+
232
+ try {
233
+ const resolvedUrl = typeof url === "string" ? url : url.value;
234
+ const response = await fetch(resolvedUrl);
235
+ if (!response.ok) throw new Error(`HTTP ${response.status}`);
236
+ data.value = await response.json();
237
+ } catch (e) {
238
+ error.value = e instanceof Error ? e : new Error(String(e));
239
+ } finally {
240
+ isLoading.value = false;
241
+ }
242
+ }
243
+
244
+ // Auto-refetch when URL changes (if URL is a ref)
245
+ if (typeof url !== "string") {
246
+ watchEffect(() => {
247
+ if (url.value) fetchData();
248
+ });
249
+ } else {
250
+ fetchData();
251
+ }
252
+
253
+ return { data, error, isLoading, refresh: fetchData };
254
+ }
255
+
256
+ // Usage:
257
+ const apiUrl = computed(() => `/api/users/${userId.value}`);
258
+ const { data: user, isLoading, error } = useFetch<User>(apiUrl);
259
+ ```
260
+
261
+ ### useLocalStorage Composable
262
+
263
+ ```ts
264
+ // composables/useLocalStorage.ts
265
+ import { ref, watch, type Ref } from "vue";
266
+
267
+ export function useLocalStorage<T>(key: string, defaultValue: T): Ref<T> {
268
+ const stored = localStorage.getItem(key);
269
+ const data = ref<T>(stored ? JSON.parse(stored) : defaultValue) as Ref<T>;
270
+
271
+ watch(data, (newVal) => {
272
+ localStorage.setItem(key, JSON.stringify(newVal));
273
+ }, { deep: true });
274
+
275
+ return data;
276
+ }
277
+
278
+ // Usage:
279
+ const theme = useLocalStorage("theme", "dark");
280
+ theme.value = "light"; // auto-saves to localStorage
281
+ ```
282
+
283
+ ### useDebounce Composable
284
+
285
+ ```ts
286
+ // composables/useDebounce.ts
287
+ import { ref, watch, type Ref } from "vue";
288
+
289
+ export function useDebounce<T>(source: Ref<T>, delay = 300): Ref<T> {
290
+ const debounced = ref(source.value) as Ref<T>;
291
+
292
+ let timeout: ReturnType<typeof setTimeout>;
293
+ watch(source, (val) => {
294
+ clearTimeout(timeout);
295
+ timeout = setTimeout(() => { debounced.value = val; }, delay);
296
+ });
297
+
298
+ return debounced;
299
+ }
300
+
301
+ // Usage:
302
+ const query = ref("");
303
+ const debouncedQuery = useDebounce(query, 500);
304
+ // debouncedQuery only updates 500ms after the user stops typing
305
+ ```
306
+
307
+ ---
308
+
309
+ ## Pinia (State Management)
310
+
311
+ ### Setup Store (Recommended)
312
+
313
+ ```ts
314
+ // stores/cart.ts
315
+ import { defineStore } from "pinia";
316
+ import { ref, computed } from "vue";
317
+
318
+ export const useCartStore = defineStore("cart", () => {
319
+ // State
320
+ const items = ref<CartItem[]>([]);
321
+
322
+ // Getters (computed)
323
+ const totalItems = computed(() => items.value.length);
324
+ const totalPrice = computed(() =>
325
+ items.value.reduce((sum, item) => sum + item.price * item.quantity, 0)
326
+ );
327
+ const isEmpty = computed(() => items.value.length === 0);
328
+
329
+ // Actions
330
+ function addItem(product: Product, quantity = 1) {
331
+ const existing = items.value.find((i) => i.productId === product.id);
332
+ if (existing) {
333
+ existing.quantity += quantity;
334
+ } else {
335
+ items.value.push({
336
+ productId: product.id,
337
+ name: product.name,
338
+ price: product.price,
339
+ quantity,
340
+ });
341
+ }
342
+ }
343
+
344
+ function removeItem(productId: string) {
345
+ items.value = items.value.filter((i) => i.productId !== productId);
346
+ }
347
+
348
+ function clearCart() {
349
+ items.value = [];
350
+ }
351
+
352
+ // Async action
353
+ async function checkout() {
354
+ const response = await fetch("/api/checkout", {
355
+ method: "POST",
356
+ body: JSON.stringify({ items: items.value }),
357
+ });
358
+ if (response.ok) clearCart();
359
+ return response.json();
360
+ }
361
+
362
+ return { items, totalItems, totalPrice, isEmpty, addItem, removeItem, clearCart, checkout };
363
+ });
364
+
365
+ // ❌ HALLUCINATION TRAP: Do NOT use Vuex in Vue 3.5+ projects
366
+ // Vuex is in maintenance mode. Pinia is the official replacement.
367
+ // ❌ import { createStore } from "vuex" ← LEGACY
368
+ // ✅ import { defineStore } from "pinia"
369
+ ```
370
+
371
+ ### Using Stores in Components
372
+
373
+ ```vue
374
+ <script setup lang="ts">
375
+ import { useCartStore } from "@/stores/cart";
376
+ import { storeToRefs } from "pinia";
377
+
378
+ const cartStore = useCartStore();
379
+
380
+ // ✅ Use storeToRefs for reactive destructuring of state/getters
381
+ const { items, totalPrice, isEmpty } = storeToRefs(cartStore);
382
+
383
+ // Actions can be destructured directly (they're not reactive)
384
+ const { addItem, removeItem, clearCart } = cartStore;
385
+
386
+ // ❌ HALLUCINATION TRAP: Destructuring state WITHOUT storeToRefs loses reactivity
387
+ // ❌ const { items, totalPrice } = cartStore; ← NOT reactive!
388
+ // ✅ const { items, totalPrice } = storeToRefs(cartStore); ← reactive
389
+ </script>
390
+
391
+ <template>
392
+ <div v-if="isEmpty">Cart is empty</div>
393
+ <ul v-else>
394
+ <li v-for="item in items" :key="item.productId">
395
+ {{ item.name }} — ${{ item.price }} × {{ item.quantity }}
396
+ <button @click="removeItem(item.productId)">Remove</button>
397
+ </li>
398
+ </ul>
399
+ <p>Total: ${{ totalPrice.toFixed(2) }}</p>
400
+ </template>
401
+ ```
402
+
403
+ ### Pinia Persistence Plugin
404
+
405
+ ```ts
406
+ // main.ts
407
+ import { createPinia } from "pinia";
408
+ import piniaPluginPersistedstate from "pinia-plugin-persistedstate";
409
+
410
+ const pinia = createPinia();
411
+ pinia.use(piniaPluginPersistedstate);
412
+
413
+ // In store:
414
+ export const useSettingsStore = defineStore("settings", () => {
415
+ const theme = ref("dark");
416
+ const locale = ref("en");
417
+ return { theme, locale };
418
+ }, {
419
+ persist: true, // auto-saves to localStorage
420
+ });
421
+ ```
422
+
423
+ ---
424
+
425
+ ## Vue Router 4
426
+
427
+ ### Route Configuration
428
+
429
+ ```ts
430
+ // router/index.ts
431
+ import { createRouter, createWebHistory, type RouteRecordRaw } from "vue-router";
432
+
433
+ const routes: RouteRecordRaw[] = [
434
+ {
435
+ path: "/",
436
+ component: () => import("@/layouts/DefaultLayout.vue"),
437
+ children: [
438
+ { path: "", name: "home", component: () => import("@/pages/Home.vue") },
439
+ { path: "about", name: "about", component: () => import("@/pages/About.vue") },
440
+ ],
441
+ },
442
+ {
443
+ path: "/dashboard",
444
+ component: () => import("@/layouts/DashboardLayout.vue"),
445
+ meta: { requiresAuth: true },
446
+ children: [
447
+ { path: "", name: "dashboard", component: () => import("@/pages/Dashboard.vue") },
448
+ {
449
+ path: "users/:id",
450
+ name: "user-detail",
451
+ component: () => import("@/pages/UserDetail.vue"),
452
+ props: true, // pass route params as props
453
+ },
454
+ ],
455
+ },
456
+ { path: "/:pathMatch(.*)*", name: "not-found", component: () => import("@/pages/NotFound.vue") },
457
+ ];
458
+
459
+ const router = createRouter({
460
+ history: createWebHistory(),
461
+ routes,
462
+ scrollBehavior(to, from, savedPosition) {
463
+ return savedPosition || { top: 0 };
464
+ },
465
+ });
466
+
467
+ // Navigation guard
468
+ router.beforeEach(async (to, from) => {
469
+ const authStore = useAuthStore();
470
+
471
+ if (to.meta.requiresAuth && !authStore.isAuthenticated) {
472
+ return { name: "login", query: { redirect: to.fullPath } };
473
+ }
474
+ });
475
+
476
+ export default router;
477
+ ```
478
+
479
+ ### Router Composables
480
+
481
+ ```vue
482
+ <script setup lang="ts">
483
+ import { useRouter, useRoute } from "vue-router";
484
+
485
+ const router = useRouter();
486
+ const route = useRoute();
487
+
488
+ // Reactive route params
489
+ const userId = computed(() => route.params.id as string);
490
+
491
+ // Programmatic navigation
492
+ function goToUser(id: string) {
493
+ router.push({ name: "user-detail", params: { id } });
494
+ }
495
+
496
+ function goBack() {
497
+ router.back();
498
+ }
499
+
500
+ // ❌ HALLUCINATION TRAP: route.params values are always strings
501
+ // Even if the URL is /users/123, params.id is "123" (string), not 123 (number)
502
+ // Always parse: parseInt(route.params.id as string)
503
+ </script>
504
+ ```
505
+
506
+ ---
507
+
508
+ ## Component Patterns
509
+
510
+ ### Slots
511
+
512
+ ```vue
513
+ <!-- BaseCard.vue -->
514
+ <template>
515
+ <div class="card">
516
+ <header v-if="$slots.header" class="card-header">
517
+ <slot name="header" />
518
+ </header>
519
+
520
+ <div class="card-body">
521
+ <slot /> <!-- default slot -->
522
+ </div>
523
+
524
+ <footer v-if="$slots.footer" class="card-footer">
525
+ <slot name="footer" />
526
+ </footer>
527
+ </div>
528
+ </template>
529
+
530
+ <!-- Scoped slot (pass data to parent) -->
531
+ <!-- DataList.vue -->
532
+ <template>
533
+ <ul>
534
+ <li v-for="(item, index) in items" :key="item.id">
535
+ <slot name="item" :item="item" :index="index" :is-last="index === items.length - 1">
536
+ <!-- Default content if parent doesn't provide slot -->
537
+ {{ item.name }}
538
+ </slot>
539
+ </li>
540
+ </ul>
541
+ </template>
542
+
543
+ <!-- Usage with scoped slot -->
544
+ <DataList :items="users">
545
+ <template #item="{ item, index, isLast }">
546
+ <UserCard :user="item" :highlighted="index === 0" />
547
+ <hr v-if="!isLast" />
548
+ </template>
549
+ </DataList>
550
+ ```
551
+
552
+ ### Provide / Inject (Dependency Injection)
553
+
554
+ ```ts
555
+ // Parent component
556
+ import { provide, ref, type InjectionKey } from "vue";
557
+
558
+ interface ThemeContext {
559
+ theme: Ref<string>;
560
+ toggleTheme: () => void;
561
+ }
562
+
563
+ export const ThemeKey: InjectionKey<ThemeContext> = Symbol("theme");
564
+
565
+ // In parent <script setup>:
566
+ const theme = ref("dark");
567
+ function toggleTheme() {
568
+ theme.value = theme.value === "dark" ? "light" : "dark";
569
+ }
570
+ provide(ThemeKey, { theme, toggleTheme });
571
+
572
+ // Child component (any depth)
573
+ import { inject } from "vue";
574
+ import { ThemeKey } from "@/keys";
575
+
576
+ const themeCtx = inject(ThemeKey);
577
+ if (!themeCtx) throw new Error("ThemeKey not provided");
578
+ // themeCtx.theme.value === "dark"
579
+
580
+ // ❌ HALLUCINATION TRAP: Always use InjectionKey<T> for type safety
581
+ // inject("theme") returns unknown — inject(ThemeKey) returns ThemeContext
582
+ ```
583
+
584
+ ### Teleport
585
+
586
+ ```vue
587
+ <!-- Render modal content at <body> level to escape overflow/z-index traps -->
588
+ <Teleport to="body">
589
+ <div v-if="showModal" class="modal-overlay">
590
+ <div class="modal-content">
591
+ <slot />
592
+ <button @click="$emit('close')">Close</button>
593
+ </div>
594
+ </div>
595
+ </Teleport>
596
+ ```
597
+
598
+ ### Transition
599
+
600
+ ```vue
601
+ <Transition
602
+ name="fade"
603
+ mode="out-in"
604
+ @before-enter="onBeforeEnter"
605
+ @enter="onEnter"
606
+ @leave="onLeave"
607
+ >
608
+ <component :is="currentComponent" :key="currentRoute" />
609
+ </Transition>
610
+
611
+ <style>
612
+ .fade-enter-active, .fade-leave-active {
613
+ transition: opacity 0.3s ease;
614
+ }
615
+ .fade-enter-from, .fade-leave-to {
616
+ opacity: 0;
617
+ }
618
+ </style>
619
+
620
+ <!-- TransitionGroup for lists -->
621
+ <TransitionGroup name="list" tag="ul">
622
+ <li v-for="item in items" :key="item.id">
623
+ {{ item.name }}
624
+ </li>
625
+ </TransitionGroup>
626
+
627
+ <style>
628
+ .list-enter-active, .list-leave-active {
629
+ transition: all 0.3s ease;
630
+ }
631
+ .list-enter-from, .list-leave-from {
632
+ opacity: 0;
633
+ transform: translateY(20px);
634
+ }
635
+ .list-leave-active {
636
+ position: absolute; /* prevents layout shift during leave */
637
+ }
638
+ </style>
639
+ ```
640
+
641
+ ---
642
+
643
+ ## Nuxt 4
644
+
645
+ ### File-Based Routing
646
+
647
+ ```
648
+ pages/
649
+ ├── index.vue → /
650
+ ├── about.vue → /about
651
+ ├── users/
652
+ │ ├── index.vue → /users
653
+ │ └── [id].vue → /users/:id
654
+ ├── blog/
655
+ │ └── [...slug].vue → /blog/* (catch-all)
656
+ └── [[optional]].vue → /:optional? (optional param)
657
+ ```
658
+
659
+ ### Data Fetching
660
+
661
+ ```vue
662
+ <script setup lang="ts">
663
+ // useFetch — SSR-friendly, auto-cached, deduped
664
+ const { data: users, status, error, refresh } = await useFetch<User[]>("/api/users", {
665
+ query: { page: currentPage }, // reactive query params
666
+ pick: ["id", "name", "email"], // only extract these fields (reduces payload)
667
+ transform: (data) => data.filter((u) => u.isActive), // client-side transform
668
+ watch: [currentPage], // auto-refetch when page changes
669
+ });
670
+
671
+ // useAsyncData — for non-fetch async operations
672
+ const { data: config } = await useAsyncData("app-config", () => {
673
+ return $fetch("/api/config");
674
+ });
675
+
676
+ // $fetch — raw fetch (NOT SSR-cached, no dedup)
677
+ const data = await $fetch("/api/endpoint");
678
+
679
+ // ❌ HALLUCINATION TRAP: useFetch auto-deduplicates during SSR
680
+ // Calling useFetch twice with the same key returns the same promise
681
+ // Use $fetch when you intentionally want separate requests
682
+
683
+ // ❌ HALLUCINATION TRAP: useFetch MUST be called at the top level of setup
684
+ // It cannot be called inside functions, loops, or conditionals
685
+ </script>
686
+
687
+ <template>
688
+ <div v-if="status === 'pending'">Loading...</div>
689
+ <div v-else-if="error">Error: {{ error.message }}</div>
690
+ <ul v-else>
691
+ <li v-for="user in users" :key="user.id">{{ user.name }}</li>
692
+ </ul>
693
+ </template>
694
+ ```
695
+
696
+ ### Runtime Config
697
+
698
+ ```ts
699
+ // nuxt.config.ts
700
+ export default defineNuxtConfig({
701
+ runtimeConfig: {
702
+ // Server-only (never exposed to client)
703
+ apiSecret: process.env.API_SECRET,
704
+ dbUrl: process.env.DATABASE_URL,
705
+
706
+ // Client-accessible
707
+ public: {
708
+ apiBase: process.env.NUXT_PUBLIC_API_BASE || "https://api.example.com",
709
+ appName: "My App",
710
+ },
711
+ },
712
+ });
713
+
714
+ // Usage in components/composables:
715
+ const config = useRuntimeConfig();
716
+ // config.public.apiBase — ✅ accessible on client and server
717
+ // config.apiSecret — ✅ accessible on server only, undefined on client
718
+
719
+ // ❌ HALLUCINATION TRAP: Private keys are ONLY available server-side
720
+ // Accessing config.apiSecret in a client component returns undefined
721
+ ```
722
+
723
+ ### Server Routes (Nitro)
724
+
725
+ ```ts
726
+ // server/api/users.get.ts — responds to GET /api/users
727
+ export default defineEventHandler(async (event) => {
728
+ const query = getQuery(event);
729
+ const users = await db.user.findMany({
730
+ skip: Number(query.offset) || 0,
731
+ take: Number(query.limit) || 20,
732
+ });
733
+ return users;
734
+ });
735
+
736
+ // server/api/users.post.ts — responds to POST /api/users
737
+ export default defineEventHandler(async (event) => {
738
+ const body = await readBody(event);
739
+ // Validate body...
740
+ const user = await db.user.create({ data: body });
741
+ return user;
742
+ });
743
+
744
+ // server/api/users/[id].get.ts — responds to GET /api/users/:id
745
+ export default defineEventHandler(async (event) => {
746
+ const id = getRouterParam(event, "id");
747
+ const user = await db.user.findUnique({ where: { id } });
748
+ if (!user) {
749
+ throw createError({ statusCode: 404, message: "User not found" });
750
+ }
751
+ return user;
752
+ });
753
+ ```
754
+
755
+ ---
756
+
757
+ ## TypeScript Integration
758
+
759
+ ```vue
760
+ <script setup lang="ts">
761
+ // Component with full TypeScript
762
+ interface User {
763
+ id: number;
764
+ name: string;
765
+ email: string;
766
+ role: "admin" | "user" | "moderator";
767
+ }
768
+
769
+ const props = defineProps<{
770
+ user: User;
771
+ editable?: boolean;
772
+ }>();
773
+
774
+ const emit = defineEmits<{
775
+ save: [user: User];
776
+ cancel: [];
777
+ }>();
778
+
779
+ // Template refs
780
+ const inputRef = ref<HTMLInputElement | null>(null);
781
+ const formRef = ref<InstanceType<typeof FormComponent> | null>(null);
782
+
783
+ onMounted(() => {
784
+ inputRef.value?.focus();
785
+ });
786
+ </script>
787
+ ```
788
+
789
+ ---
790
+
791
+ ## Performance Optimization
792
+
793
+ ```vue
794
+ <script setup lang="ts">
795
+ import { shallowRef, shallowReactive, triggerRef } from "vue";
796
+
797
+ // shallowRef — only tracks .value changes, not deep mutations
798
+ const bigList = shallowRef<Item[]>([]);
799
+ // Mutating items inside won't trigger updates
800
+ // Must replace the entire array: bigList.value = [...newItems]
801
+ // Or manually trigger: triggerRef(bigList)
802
+
803
+ // v-once — render once, never update (static content)
804
+ // v-memo — skip re-rendering unless dependencies change
805
+ </script>
806
+
807
+ <template>
808
+ <!-- Static content — rendered once -->
809
+ <footer v-once>
810
+ <p>© 2024 My Company. All rights reserved.</p>
811
+ </footer>
812
+
813
+ <!-- v-memo — skip re-render unless item.id or selected changes -->
814
+ <div v-for="item in list" :key="item.id" v-memo="[item.id, selected === item.id]">
815
+ <ItemCard :item="item" :selected="selected === item.id" />
816
+ </div>
817
+
818
+ <!-- Async components (lazy loading) -->
819
+ <component :is="defineAsyncComponent(() => import('./HeavyWidget.vue'))" />
820
+ </template>
821
+ ```
822
+
823
+ ---
824
+
825
+ ## Testing with Vitest
826
+
827
+ ```ts
828
+ // tests/components/Counter.test.ts
829
+ import { describe, it, expect } from "vitest";
830
+ import { mount } from "@vue/test-utils";
831
+ import Counter from "@/components/Counter.vue";
832
+
833
+ describe("Counter", () => {
834
+ it("renders initial count", () => {
835
+ const wrapper = mount(Counter, { props: { initial: 5 } });
836
+ expect(wrapper.text()).toContain("5");
837
+ });
838
+
839
+ it("increments on click", async () => {
840
+ const wrapper = mount(Counter);
841
+ await wrapper.find("button").trigger("click");
842
+ expect(wrapper.text()).toContain("1");
843
+ });
844
+
845
+ it("emits update event", async () => {
846
+ const wrapper = mount(Counter);
847
+ await wrapper.find("button").trigger("click");
848
+ expect(wrapper.emitted("update")).toHaveLength(1);
849
+ expect(wrapper.emitted("update")![0]).toEqual([1]);
850
+ });
851
+ });
852
+
853
+ // Testing composables
854
+ import { useDebounce } from "@/composables/useDebounce";
855
+
856
+ describe("useDebounce", () => {
857
+ it("debounces value updates", async () => {
858
+ vi.useFakeTimers();
859
+ const source = ref("hello");
860
+ const debounced = useDebounce(source, 300);
861
+
862
+ source.value = "world";
863
+ expect(debounced.value).toBe("hello"); // not yet
864
+
865
+ vi.advanceTimersByTime(300);
866
+ expect(debounced.value).toBe("world"); // now updated
867
+
868
+ vi.useRealTimers();
869
+ });
870
+ });
871
+
872
+ // Testing Pinia stores
873
+ import { setActivePinia, createPinia } from "pinia";
874
+ import { useCartStore } from "@/stores/cart";
875
+
876
+ describe("Cart Store", () => {
877
+ beforeEach(() => {
878
+ setActivePinia(createPinia());
879
+ });
880
+
881
+ it("adds items", () => {
882
+ const cart = useCartStore();
883
+ cart.addItem({ id: "1", name: "Widget", price: 10 });
884
+ expect(cart.totalItems).toBe(1);
885
+ expect(cart.totalPrice).toBe(10);
886
+ });
887
+ });
888
+ ```
889
+
890
+ ---
891
+
892
+ ## Output Format
893
+
894
+ When this skill produces or reviews code, structure your output as follows:
895
+
896
+ ```
897
+ ━━━ Vue Expert Report ━━━━━━━━━━━━━━━━━━━━━━━━
898
+ Skill: Vue Expert
899
+ Vue Ver: 3.5+
900
+ Scope: [N files · N components]
901
+ ─────────────────────────────────────────────────
902
+ ✅ Passed: [checks that passed, or "All clean"]
903
+ ⚠️ Warnings: [non-blocking issues, or "None"]
904
+ ❌ Blocked: [blocking issues requiring fix, or "None"]
905
+ ─────────────────────────────────────────────────
906
+ VBC status: PENDING → VERIFIED
907
+ Evidence: [test output / lint pass / compile success]
908
+ ```
909
+
910
+ **VBC (Verification-Before-Completion) is mandatory.**
911
+ Do not mark status as VERIFIED until concrete terminal evidence is provided.
912
+
913
+ ---
914
+
915
+ ## 🤖 LLM-Specific Traps
916
+
917
+ AI coding assistants often fall into specific bad habits when generating Vue code. These are strictly forbidden:
918
+
919
+ 1. **Options API in Vue 3.5+:** Never generate `export default { data(), methods, computed, mounted() }` in modern Vue projects. Always use `<script setup>` with Composition API.
920
+ 2. **Vuex Instead of Pinia:** Vuex is in maintenance mode. Never import from `vuex`. Use `pinia` with `defineStore()`.
921
+ 3. **Destructuring Reactive Without `toRefs`/`storeToRefs`:** Destructuring a `reactive()` object or a Pinia store without `toRefs()`/`storeToRefs()` breaks reactivity.
922
+ 4. **Mutating Props Directly:** Never mutate a prop value. Use `defineEmits` to emit updates, or use `defineModel()` (Vue 3.4+) for v-model bindings.
923
+ 5. **Using `v-for` Without `:key`:** Every `v-for` must have a unique, stable `:key` binding. Never use the iteration index as the key if the list can reorder.
924
+ 6. **`defineComponent` With `<script setup>`:** `defineComponent()` is redundant inside `<script setup>`. The setup syntax IS the component definition.
925
+ 7. **`this` in Composition API:** There is no `this` in `<script setup>`. Access reactive state directly by variable name. `this.count` does not exist.
926
+ 8. **`useFetch` Inside Functions:** Nuxt's `useFetch` must be called at the top level of `<script setup>`, not inside callbacks, conditionals, or loops.
927
+ 9. **Route Params as Numbers:** `route.params.id` is always a string. Never treat it as a number without explicit parsing (`parseInt()`).
928
+ 10. **Exposing Private Runtime Config:** `runtimeConfig` (non-public) keys are server-only. Accessing them in client components returns `undefined`.
929
+
930
+ ---
931
+
932
+ ## 🏛️ Tribunal Integration (Anti-Hallucination)
933
+
934
+ **Slash command: `/tribunal-frontend`**
935
+ **Active reviewers: `logic` · `security` · `frontend` · `type-safety`**
936
+
937
+ ### ❌ Forbidden AI Tropes
938
+
939
+ 1. **Blind Assumptions:** Never make an assumption without documenting it clearly with `// VERIFY: [reason]`.
940
+ 2. **Silent Degradation:** Catching and suppressing errors without logging or handling.
941
+ 3. **Context Amnesia:** Forgetting Vue version, Nuxt vs vanilla Vue, or Pinia vs Vuex constraints.
942
+ 4. **Sloppy Layout Generation:** Never build UI without explicit 4px grid spacing and flex/grid layouts.
943
+
944
+ ### ✅ Pre-Flight Self-Audit
945
+
946
+ Review these questions before confirming output:
947
+ ```
948
+ ✅ Did I use <script setup lang="ts"> (not Options API)?
949
+ ✅ Did I use Pinia (not Vuex)?
950
+ ✅ Did I use storeToRefs for reactive store destructuring?
951
+ ✅ Did I use defineModel (Vue 3.4+) for v-model bindings?
952
+ ✅ Did I use toRefs when destructuring reactive objects?
953
+ ✅ Does every v-for have a unique, stable :key?
954
+ ✅ Did I use useRuntimeConfig for env variables (not process.env)?
955
+ ✅ Did I call useFetch at the top level of setup?
956
+ ✅ Are route params parsed correctly (string → number)?
957
+ ✅ Did I write Vitest tests with @vue/test-utils?
958
+ ```
959
+
960
+ ### 🛑 Verification-Before-Completion (VBC) Protocol
961
+
962
+ **CRITICAL:** You must follow a strict "evidence-based closeout" state machine.
963
+ - ❌ **Forbidden:** Declaring a Vue component "works" because it compiles without errors.
964
+ - ✅ **Required:** You are explicitly forbidden from completing your task without providing **concrete terminal/test evidence** (e.g., passing Vitest logs, successful build, or dev server confirmation) proving the component works correctly.