# Frontend Architecture Anti-Patterns
# Patterns that violate Mantimon TCG frontend architecture principles

name: Architecture
description: |
  Detects violations of the project's frontend architecture principles:
  - Vue Composition API patterns (no Options API)
  - Pinia store patterns (setup syntax, proper actions)
  - Phaser integration (rendering only, no game logic)
  - Component organization (composables for reuse)
  - Backend authority (client never authoritative for game state)

patterns:
  # Vue Composition API
  - id: options-api-usage
    description: Options API instead of Composition API
    grep_pattern: 'export default \{[\s\S]*?(data\(\)|methods:|computed:|watch:)'
    multiline: true
    file_glob: '*.vue'
    context_lines: 5
    verdict_hints:
      ISSUE_IF: "Using Options API - project uses Composition API only"
      OK_IF: "Never OK - convert to <script setup> with Composition API"

  - id: missing-script-setup
    description: Script without setup attribute
    grep_pattern: '<script lang="ts">\s*$'
    file_glob: '*.vue'
    context_lines: 2
    verdict_hints:
      ISSUE_IF: "Missing setup - should use <script setup lang=\"ts\">"
      OK_IF: "Rare cases needing defineComponent; document why"

  - id: options-api-definecomponent
    description: defineComponent with options object
    grep_pattern: 'defineComponent\(\{[\s\S]*?(data|methods|computed):'
    multiline: true
    file_glob: '*.vue'
    context_lines: 5
    verdict_hints:
      ISSUE_IF: "Options API via defineComponent"
      OK_IF: "Never OK - use <script setup>"

  # Pinia Store Patterns
  - id: store-options-syntax
    description: Pinia store using options syntax instead of setup
    grep_pattern: 'defineStore\([^,]+,\s*\{[\s\S]*?state:'
    multiline: true
    file_glob: 'stores/*.ts'
    context_lines: 5
    verdict_hints:
      ISSUE_IF: "Options syntax - project uses setup store syntax"
      OK_IF: "Never OK - convert to setup syntax with ref/computed"

  - id: store-mutation-outside-action
    description: Store state mutated directly from component
    grep_pattern: 'Store\(\)\.\w+\s*='
    file_glob: '*.vue'
    context_lines: 3
    verdict_hints:
      ISSUE_IF: "Direct state mutation - use store action instead"
      OK_IF: "Assigning to local ref that shadows store property"

  - id: store-no-error-state
    description: Store without error state for async operations
    grep_pattern: 'defineStore.*\{[\s\S]*?async.*fetch[\s\S]*?\}'
    multiline: true
    file_glob: 'stores/*.ts'
    context_lines: 20
    verdict_hints:
      ISSUE_IF: "Async store without error ref for failure handling"
      OK_IF: "Error state exists; or errors handled by caller"

  # Phaser Integration
  - id: phaser-game-logic
    description: Game logic in Phaser scene (should be backend)
    grep_pattern: '(calculateDamage|resolveTurn|checkWin|applyEffect)'
    file_glob: 'game/**/*.ts'
    context_lines: 5
    verdict_hints:
      ISSUE_IF: "Game logic in Phaser - backend is authoritative"
      OK_IF: "Animation/display logic with same name; not actual game rules"

  - id: phaser-direct-api
    description: Phaser scene making API calls directly
    grep_pattern: '(fetch|axios|api\.)'
    file_glob: 'game/**/*.ts'
    context_lines: 4
    verdict_hints:
      ISSUE_IF: "Phaser calling API - should go through Vue layer"
      OK_IF: "Asset loading (images, audio); not game data"

  - id: phaser-store-import
    description: Phaser importing store directly (use bridge)
    grep_pattern: 'import.*from.*stores/'
    file_glob: 'game/**/*.ts'
    context_lines: 3
    verdict_hints:
      ISSUE_IF: "Direct store import - use event bridge pattern"
      OK_IF: "Type-only import (import type); bridge.ts file"

  - id: phaser-modifies-state
    description: Phaser scene modifying game state
    grep_pattern: 'gameState\.\w+\s*='
    file_glob: 'game/**/*.ts'
    context_lines: 4
    verdict_hints:
      ISSUE_IF: "Phaser modifying state - should only render"
      OK_IF: "Local animation state; not shared game state"

  # Component Organization
  - id: api-call-in-component
    description: Direct fetch/API call in component (use composable)
    grep_pattern: 'fetch\(|axios\.|api\.'
    file_glob: 'components/**/*.vue'
    context_lines: 5
    verdict_hints:
      ISSUE_IF: "API call in component - extract to composable"
      OK_IF: "Component is in pages/; or uses composable internally"

  - id: large-component
    description: Component with many lines (consider splitting)
    grep_pattern: '</script>'
    file_glob: '*.vue'
    context_lines: 0
    verdict_hints:
      ISSUE_IF: "Script section >150 lines - split into composables"
      OK_IF: "Page component; or logic is cohesive and documented"

  - id: prop-drilling
    description: Props passed through multiple levels
    grep_pattern: 'defineProps.*\{[\s\S]*?(Props|\.\.\.)'
    multiline: true
    file_glob: '*.vue'
    context_lines: 10
    verdict_hints:
      ISSUE_IF: "Props drilled >2 levels - use provide/inject or store"
      OK_IF: "Props are component-specific; not passed through"

  # Type Safety
  - id: any-type-usage
    description: Using 'any' type
    grep_pattern: ':\s*any\b|as\s+any\b|<any>'
    file_glob: '*.{vue,ts}'
    context_lines: 2
    verdict_hints:
      ISSUE_IF: "Explicit any - defeats TypeScript benefits"
      OK_IF: "Temporary during migration; or external lib without types"

  - id: type-assertion-abuse
    description: Type assertion (as) that might hide errors
    grep_pattern: '\s+as\s+[A-Z]\w+'
    file_glob: '*.{vue,ts}'
    context_lines: 3
    verdict_hints:
      ISSUE_IF: "Assertion used to bypass type error"
      OK_IF: "Narrowing from unknown; or after runtime check"

  - id: missing-prop-types
    description: Props without TypeScript types
    grep_pattern: 'defineProps\(\[\s*["\']'
    file_glob: '*.vue'
    context_lines: 3
    verdict_hints:
      ISSUE_IF: "Array prop syntax - no type safety"
      OK_IF: "Never OK - use defineProps<{...}>()"

  # Import Organization
  - id: relative-import-deep
    description: Deep relative imports (use @ alias)
    grep_pattern: 'from ["\']\.\.\/\.\.\/\.\.\/'
    file_glob: '*.{vue,ts}'
    context_lines: 2
    verdict_hints:
      ISSUE_IF: "Deep relative path - use @/ alias"
      OK_IF: "Never OK for 3+ levels - always use @/"

  - id: missing-type-import
    description: Type import without 'import type'
    grep_pattern: 'import \{[^}]*(Type|Interface|Props)[^}]*\} from'
    file_glob: '*.{vue,ts}'
    context_lines: 2
    verdict_hints:
      ISSUE_IF: "Type imported as value - use 'import type'"
      OK_IF: "Also used as value (class, enum); not pure type"

  # Backend Authority
  - id: client-state-authority
    description: Client treating its state as authoritative
    grep_pattern: '(emit|send)\([^)]*\{[^}]*(hp|damage|winner|gameState)'
    file_glob: '*.{vue,ts}'
    context_lines: 5
    verdict_hints:
      ISSUE_IF: "Sending computed game state to server - server is authority"
      OK_IF: "Sending action intent (playCard, attack); server computes result"

  - id: local-game-calculation
    description: Calculating game outcomes client-side
    grep_pattern: '(damage|hp|energy)\s*[-+*]=|Math\.(random|floor).*damage'
    file_glob: '*.{vue,ts}'
    context_lines: 5
    verdict_hints:
      ISSUE_IF: "Game calculation on client - server must compute"
      OK_IF: "Animation preview; actual result from server"
