---
description: "Comprehensive code quality protocol - MANDATORY for ALL agents"
applyTo: "**"
priority: "CRITICAL"
readTime: "2 minutes"
---

# CODE QUALITY PROTOCOL - MANDATORY

**READ THIS BEFORE ANY CODE CHANGE.**

This protocol prevents ALL common mistakes in MyRoadTrip codebase.
Violation = rejected code, blocked commit, failed CI/CD.

---

## 🎯 Pre-Flight Checklist (30 seconds)

**BEFORE editing ANY file:**

```bash
# 1. Run validation (catches 95% of issues)
./scripts/validate-quality.sh

# 2. Check specific domain
grep -r "YourFunction\|YourComponent" apps/ packages/

# 3. Read architecture doc
cat docs/core/architecture.md | grep -A 10 "YourDomain"
```

**STOP if validation fails. Fix issues BEFORE coding.**

---

## 📚 Authority Table

| Category | Authority File | Line Range | Update Frequency |
|----------|---------------|------------|------------------|
| Type System | `.github/instructions/TYPE_SAFETY_PROTOCOL.md` | Full | Never edit |
| Architecture | `docs/core/architecture.md` | All | On major changes |
| API Design | `docs/technical/reference.md` → API Endpoints | 100-300 | On endpoint add |
| CRDT Sync | `docs/core/architecture.md` → CRDT Flow | 150-200 | Never edit |
| Security | This file → Security Rules | Below | Never edit |
| Performance | This file → Performance Rules | Below | Never edit |
| Firebase | This file → Firebase Rules | Below | Never edit |
| Offline | This file → Offline Rules | Below | Never edit |

---

## 🏗️ ARCHITECTURE RULES (NEVER BREAK)

### Rule A1: NO WebSocket (HTTP polling only)
**Why:** Mobile disconnections at 130km/h break WebSocket
**Correct:**
```typescript
// ✅ HTTP polling with adaptive backoff
const pollUpdates = async () => {
  try {
    const response = await fetch('/api/plans/updates', {
      signal: AbortSignal.timeout(5000)
    })
    backoff = 10000 // Reset to 10s
  } catch {
    backoff = Math.min(backoff * 2, 60000) // Max 60s
  }
}
setInterval(pollUpdates, backoff)
```
**Wrong:**
```typescript
// ❌ WebSocket disconnects on tunnels/poor signal
const ws = new WebSocket('ws://api/sync')
ws.onmessage = (msg) => { /* NEVER REACHED */ }
```

### Rule A2: Domain Module Isolation
**Structure:**
```
apps/web/src/modules/{domain}/
  ├── components/    # UI only, no business logic
  ├── composables/   # Reusable logic
  ├── pages/         # Route entry points
  ├── services/      # API calls + business logic
  └── stores/        # Pinia state (1 per domain)
```
**Correct:**
```typescript
// ✅ Service handles business logic
// apps/web/src/modules/planner/services/plannerService.ts
export const createPlan = async (dto: CreatePlanDTO) => {
  const response = await api.post('/api/plans', dto)
  return response.data
}
```
**Wrong:**
```typescript
// ❌ Component directly calls API (no error handling, no reusability)
// apps/web/src/modules/planner/components/PlanCard.vue
const createPlan = async () => {
  const response = await fetch('/api/plans', { /* ... */ })
}
```

### Rule A3: Backend Layered Architecture
**Structure:**
```
apps/api/src/modules/{domain}/
  ├── {domain}.module.ts      # Fastify plugin
  ├── {domain}.routes.ts      # Route definitions
  ├── {domain}.controller.ts  # Request/response handling
  ├── {domain}.service.ts     # Business logic
  ├── {domain}.repository.ts  # Database access
  └── {domain}.schemas.ts     # Zod validation
```
**Correct:**
```typescript
// ✅ Controller → Service → Repository
// controller.ts
async createPlan(req, reply) {
  const dto = CreatePlanSchema.parse(req.body) // Validation
  const plan = await this.service.createPlan(dto, req.user.id)
  reply.send(plan)
}

// service.ts
async createPlan(dto, userId) {
  // Business logic (offline queue, notifications, etc.)
  return this.repository.createPlan(dto, userId)
}

// repository.ts
async createPlan(dto, userId) {
  // Pure Firestore access
  return this.firestore.collection('plans').add({...})
}
```
**Wrong:**
```typescript
// ❌ Controller directly accesses database (no business logic layer)
async createPlan(req, reply) {
  const plan = await firestore.collection('plans').add(req.body)
  reply.send(plan)
}
```

---

## 🔐 SECURITY RULES

### Rule S1: NEVER trust user input
**Correct:**
```typescript
// ✅ Zod validation + sanitization
import { z } from 'zod'

const CreatePlanSchema = z.object({
  name: z.string().min(1).max(100).trim(),
  description: z.string().max(500).optional(),
  isPublic: z.boolean().default(false)
})

const dto = CreatePlanSchema.parse(req.body) // Throws if invalid
```
**Wrong:**
```typescript
// ❌ Direct usage (SQL injection, XSS, type confusion)
const plan = await db.query(`INSERT INTO plans VALUES ('${req.body.name}')`)
```

### Rule S2: Authentication on ALL protected routes
**Correct:**
```typescript
// ✅ Middleware enforces auth
app.get('/api/plans', { 
  preHandler: [requireAuth] 
}, async (req, reply) => {
  const plans = await service.findByUserId(req.user.id) // req.user exists
})
```
**Wrong:**
```typescript
// ❌ No auth check (any user can access all plans)
app.get('/api/plans', async (req, reply) => {
  const plans = await service.findAll() // Exposes all users' data
})
```

### Rule S3: Firestore Rules = Server-side enforcement
**Correct:**
```javascript
// firebase/firestore.rules
match /plans/{planId} {
  allow read: if request.auth != null && (
    resource.data.userId == request.auth.uid ||
    resource.data.collaborators.hasAny([request.auth.uid])
  );
  allow write: if request.auth != null && 
    resource.data.userId == request.auth.uid;
}
```
**Wrong:**
```javascript
// ❌ Client-side check only (easily bypassed)
allow read, write: if request.auth != null; // Any authenticated user can access/modify ANY plan
```

### Rule S4: Rate limiting on ALL endpoints
**Correct:**
```typescript
// ✅ Rate limiter middleware
import rateLimit from '@fastify/rate-limit'

app.register(rateLimit, {
  max: 100,
  timeWindow: '15 minutes'
})

app.post('/api/ai/suggestions', {
  config: { rateLimit: { max: 10, timeWindow: '1 minute' } }
}, handler)
```
**Wrong:**
```typescript
// ❌ No rate limit (DDoS, API quota abuse)
app.post('/api/ai/suggestions', handler)
```

---

## 🔥 FIREBASE RULES

### Rule F1: ALWAYS use server-side SDK (Admin SDK)
**Correct:**
```typescript
// ✅ apps/api/src/core/firebase.ts
import admin from 'firebase-admin'

const firestore = admin.firestore()
// Has full access, bypasses security rules (intended for server)
```
**Wrong:**
```typescript
// ❌ Client SDK on backend (respects security rules, slower)
import { initializeApp } from 'firebase/app'
const app = initializeApp(config)
```

### Rule F2: Batch operations for multiple writes
**Correct:**
```typescript
// ✅ Batch write (atomic, 1 network call)
const batch = firestore.batch()
stages.forEach(stage => {
  batch.set(firestore.collection('stages').doc(), stage)
})
await batch.commit()
```
**Wrong:**
```typescript
// ❌ Individual writes (N network calls, not atomic)
for (const stage of stages) {
  await firestore.collection('stages').add(stage)
}
```

### Rule F3: Index ALL query fields
**Correct:**
```javascript
// firebase/firestore.indexes.json
{
  "indexes": [
    {
      "collectionGroup": "plans",
      "queryScope": "COLLECTION",
      "fields": [
        { "fieldPath": "userId", "order": "ASCENDING" },
        { "fieldPath": "createdAt", "order": "DESCENDING" }
      ]
    }
  ]
}
```
**Wrong:**
```typescript
// ❌ Query without index (fails in production)
firestore.collection('plans')
  .where('userId', '==', uid)
  .orderBy('createdAt', 'desc') // MISSING INDEX ERROR
```

### Rule F4: Pagination with cursor (not offset)
**Correct:**
```typescript
// ✅ Cursor-based pagination (O(1) on Firestore)
const query = firestore.collection('plans')
  .where('userId', '==', uid)
  .orderBy('createdAt', 'desc')
  .startAfter(lastDoc)
  .limit(20)
```
**Wrong:**
```typescript
// ❌ Offset-based (fetches all N documents, then skips N)
const query = firestore.collection('plans')
  .where('userId', '==', uid)
  .orderBy('createdAt', 'desc')
  .offset(page * 20) // Fetches 100 docs to skip 80
  .limit(20)
```

---

## 🚀 PERFORMANCE RULES

### Rule P1: Memoize expensive computations
**Correct:**
```typescript
// ✅ Vue computed (cached, reactive)
const totalDistance = computed(() => {
  return route.value.stages.reduce((sum, stage) => 
    sum + stage.distance, 0
  )
})
```
**Wrong:**
```typescript
// ❌ Method called on every render (recalculates unnecessarily)
const totalDistance = () => {
  return route.stages.reduce((sum, stage) => sum + stage.distance, 0)
}
```

### Rule P2: Debounce user input
**Correct:**
```typescript
// ✅ Debounced search (1 API call after user stops typing)
import { useDebounceFn } from '@vueuse/core'

const debouncedSearch = useDebounceFn(async (query: string) => {
  const results = await api.get(`/api/search?q=${query}`)
  searchResults.value = results.data
}, 300)
```
**Wrong:**
```typescript
// ❌ Search on every keystroke (100 API calls for "Paris")
const onInput = async (event) => {
  const results = await api.get(`/api/search?q=${event.target.value}`)
  searchResults.value = results.data
}
```

### Rule P3: Lazy load routes and components
**Correct:**
```typescript
// ✅ router.ts - Code splitting
{
  path: '/planner',
  component: () => import('./modules/planner/pages/PlannerPage.vue')
}

// ✅ Component - Lazy component
const HeavyChart = defineAsyncComponent(() => 
  import('./components/HeavyChart.vue')
)
```
**Wrong:**
```typescript
// ❌ Eager import (all code in initial bundle)
import PlannerPage from './modules/planner/pages/PlannerPage.vue'

{
  path: '/planner',
  component: PlannerPage
}
```

### Rule P4: Virtual scrolling for large lists
**Correct:**
```typescript
// ✅ Virtual scroller (renders 20 items, not 1000)
<q-virtual-scroll
  :items="plans"
  :item-size="80"
  v-slot="{ item }"
>
  <PlanCard :plan="item" />
</q-virtual-scroll>
```
**Wrong:**
```typescript
// ❌ Render all 1000 items (DOM explosion, lag)
<div v-for="plan in plans" :key="plan.id">
  <PlanCard :plan="plan" />
</div>
```

---

## 🔄 CRDT SYNC RULES (Yjs)

### Rule C1: NEVER mutate Y.Doc directly in components
**Correct:**
```typescript
// ✅ Service wraps Y.Doc mutations
// services/syncService.ts
export const updatePlanName = (ydoc: Y.Doc, name: string) => {
  const ymap = ydoc.getMap('plan')
  ymap.set('name', name)
  // Syncs to Redis + other clients automatically
}

// Component
import { updatePlanName } from '@/services/syncService'
updatePlanName(ydoc, newName)
```
**Wrong:**
```typescript
// ❌ Component mutates Y.Doc (tight coupling, no abstraction)
const ymap = ydoc.getMap('plan')
ymap.set('name', newName)
```

### Rule C2: ONE Y.Doc per plan (not per stage)
**Correct:**
```typescript
// ✅ Y.Doc structure
const ydoc = new Y.Doc()
const ymap = ydoc.getMap('plan')
ymap.set('id', planId)
ymap.set('name', 'Trip to Paris')
ymap.set('stages', Y.Array([...]))
```
**Wrong:**
```typescript
// ❌ Multiple Y.Docs (sync conflicts, memory overhead)
const planDoc = new Y.Doc()
const stage1Doc = new Y.Doc()
const stage2Doc = new Y.Doc()
```

### Rule C3: Snapshot Y.Doc to Firestore every 10 updates
**Correct:**
```typescript
// ✅ Periodic snapshot
let updateCount = 0
ydoc.on('update', () => {
  updateCount++
  if (updateCount >= 10) {
    const snapshot = Y.encodeStateAsUpdate(ydoc)
    firestore.collection('plans').doc(planId).update({
      ydocSnapshot: snapshot,
      snapshotVersion: serverTimestamp()
    })
    updateCount = 0
  }
})
```
**Wrong:**
```typescript
// ❌ Snapshot on EVERY update (Firestore write quota exceeded)
ydoc.on('update', () => {
  const snapshot = Y.encodeStateAsUpdate(ydoc)
  firestore.collection('plans').doc(planId).update({ ydocSnapshot: snapshot })
})
```

### Rule C4: Redis for real-time, Firestore for persistence
**Correct:**
```typescript
// ✅ Redis = ephemeral CRDT state
redis.set(`ydoc:${planId}`, Y.encodeStateAsUpdate(ydoc), 'EX', 3600)

// ✅ Firestore = periodic snapshots
firestore.collection('plans').doc(planId).update({
  ydocSnapshot: snapshot,
  snapshotVersion: serverTimestamp()
})
```
**Wrong:**
```typescript
// ❌ Firestore for real-time (slow, expensive)
ydoc.on('update', () => {
  firestore.collection('plans').doc(planId).update({ ydocState: Y.encodeStateAsUpdate(ydoc) })
})
```

---

## 📴 OFFLINE RULES (IndexedDB + Queue)

### Rule O1: IndexedDB = single source of truth offline
**Correct:**
```typescript
// ✅ Read from IndexedDB first
const plan = await db.plans.get(planId)
if (plan) return plan

// If not in IndexedDB, fetch from API
const response = await api.get(`/api/plans/${planId}`)
await db.plans.put(response.data) // Cache for offline
return response.data
```
**Wrong:**
```typescript
// ❌ Always fetch from API (fails offline)
const response = await api.get(`/api/plans/${planId}`)
return response.data
```

### Rule O2: Queue failed operations, retry on reconnect
**Correct:**
```typescript
// ✅ Offline queue
try {
  await api.post('/api/plans', dto)
} catch (error) {
  if (error.message === 'Network Error') {
    await db.offlineQueue.add({
      method: 'POST',
      url: '/api/plans',
      body: dto,
      timestamp: Date.now()
    })
  }
}

// On reconnect
window.addEventListener('online', async () => {
  const queue = await db.offlineQueue.toArray()
  for (const op of queue) {
    await api.request(op)
    await db.offlineQueue.delete(op.id)
  }
})
```
**Wrong:**
```typescript
// ❌ Silent failure (user loses data)
try {
  await api.post('/api/plans', dto)
} catch {
  // Nothing happens, data lost
}
```

### Rule O3: Optimistic UI updates
**Correct:**
```typescript
// ✅ Update UI immediately, rollback on error
const optimisticPlan = { ...dto, id: tempId }
plans.value.push(optimisticPlan)

try {
  const created = await api.post('/api/plans', dto)
  plans.value = plans.value.map(p => p.id === tempId ? created : p)
} catch {
  plans.value = plans.value.filter(p => p.id !== tempId)
  showError('Failed to create plan')
}
```
**Wrong:**
```typescript
// ❌ Wait for API (slow perceived performance)
const created = await api.post('/api/plans', dto)
plans.value.push(created)
```

---

## 🌐 API DESIGN RULES

### Rule D1: RESTful resource naming
**Correct:**
```
GET    /api/plans              # List plans
POST   /api/plans              # Create plan
GET    /api/plans/:id          # Get plan
PATCH  /api/plans/:id          # Update plan
DELETE /api/plans/:id          # Delete plan
POST   /api/plans/:id/stages   # Add stage to plan
```
**Wrong:**
```
GET  /api/getPlans            # Not RESTful
POST /api/createPlan          # Not RESTful
POST /api/updatePlan/:id      # Should be PATCH
GET  /api/deletePlan/:id      # Should be DELETE
```

### Rule D2: Consistent response format
**Correct:**
```typescript
// ✅ Success response
reply.send({
  data: plans,
  meta: { total: 42, page: 1, perPage: 20 }
})

// ✅ Error response
reply.status(400).send({
  error: {
    code: 'INVALID_INPUT',
    message: 'Plan name is required',
    details: { field: 'name', reason: 'missing' }
  }
})
```
**Wrong:**
```typescript
// ❌ Inconsistent formats
reply.send(plans) // No wrapper
reply.send({ success: true, result: plans }) // Different wrapper
reply.status(400).send('Error: invalid input') // String error
```

### Rule D3: HTTP status codes correctly
**Correct:**
```typescript
200 OK          → Successful GET, PATCH, DELETE
201 Created     → Successful POST
204 No Content  → Successful DELETE (no body)
400 Bad Request → Invalid input
401 Unauthorized → Missing/invalid token
403 Forbidden   → Valid token, insufficient permissions
404 Not Found   → Resource doesn't exist
409 Conflict    → Duplicate resource
500 Server Error → Unhandled exception
```
**Wrong:**
```typescript
// ❌ Always 200 (client can't detect errors)
reply.send({ success: false, error: 'Plan not found' })

// ❌ Wrong codes
reply.status(500).send({ error: 'Plan not found' }) // Should be 404
reply.status(401).send({ error: 'Cannot delete others plan' }) // Should be 403
```

### Rule D4: Pagination on all list endpoints
**Correct:**
```typescript
// ✅ Query params + meta
GET /api/plans?page=1&perPage=20

{
  data: [...],
  meta: { total: 142, page: 1, perPage: 20, totalPages: 8 }
}
```
**Wrong:**
```typescript
// ❌ No pagination (returns 10,000 plans)
GET /api/plans

[...10000 plans...]
```

---

## ⚠️ ERROR HANDLING RULES

### Rule E1: NEVER swallow errors silently
**Correct:**
```typescript
// ✅ Log + user feedback
try {
  await api.post('/api/plans', dto)
} catch (error) {
  console.error('Failed to create plan:', error)
  Notify.create({
    type: 'negative',
    message: 'Failed to create plan. Please try again.'
  })
  captureException(error) // Sentry/logging
}
```
**Wrong:**
```typescript
// ❌ Silent failure (user confused, no logs)
try {
  await api.post('/api/plans', dto)
} catch {
  // Nothing
}
```

### Rule E2: Specific error types
**Correct:**
```typescript
// ✅ Custom error classes
class ValidationError extends Error {
  constructor(public field: string, public reason: string) {
    super(`Validation failed: ${field}`)
    this.name = 'ValidationError'
  }
}

class NotFoundError extends Error {
  constructor(public resource: string, public id: string) {
    super(`${resource} not found: ${id}`)
    this.name = 'NotFoundError'
  }
}

// Handle specifically
try {
  await service.getPlan(id)
} catch (error) {
  if (error instanceof NotFoundError) {
    reply.status(404).send({ error: error.message })
  } else if (error instanceof ValidationError) {
    reply.status(400).send({ error: error.message })
  }
}
```
**Wrong:**
```typescript
// ❌ Generic Error (can't distinguish types)
throw new Error('Something went wrong')
```

### Rule E3: Input validation BEFORE business logic
**Correct:**
```typescript
// ✅ Validate first
const dto = CreatePlanSchema.parse(req.body) // Throws ZodError
const plan = await service.createPlan(dto, req.user.id)
reply.send(plan)
```
**Wrong:**
```typescript
// ❌ Business logic executes with invalid data
const plan = await service.createPlan(req.body, req.user.id)
// service does validation too late (database already accessed)
```

---

## 🧪 TESTING RULES

### Rule T1: Test business logic (services), not UI
**Correct:**
```typescript
// ✅ Service test (pure logic, no DOM)
describe('PlannerService', () => {
  it('should create plan with default settings', async () => {
    const dto = { name: 'Trip' }
    const plan = await service.createPlan(dto, 'user123')
    
    expect(plan.name).toBe('Trip')
    expect(plan.userId).toBe('user123')
    expect(plan.settings.avoidHighways).toBe(false) // Default
  })
})
```
**Wrong:**
```typescript
// ❌ Component test (brittle, slow)
it('should create plan when button clicked', async () => {
  const wrapper = mount(PlanCard)
  await wrapper.find('button').trigger('click')
  await wrapper.vm.$nextTick()
  expect(wrapper.find('.plan-name').text()).toBe('Trip')
})
```

### Rule T2: Mock external dependencies
**Correct:**
```typescript
// ✅ Mock API
vi.mock('@/services/api', () => ({
  api: {
    post: vi.fn().mockResolvedValue({ data: mockPlan })
  }
}))
```
**Wrong:**
```typescript
// ❌ Real API calls (slow, flaky, requires network)
const plan = await api.post('/api/plans', dto)
```

### Rule T3: Test error paths
**Correct:**
```typescript
// ✅ Test both success and failure
it('should handle API error gracefully', async () => {
  api.post.mockRejectedValue(new Error('Network Error'))
  
  await service.createPlan(dto, userId)
  
  expect(console.error).toHaveBeenCalledWith('Failed to create plan:', expect.any(Error))
  expect(Notify.create).toHaveBeenCalledWith({ type: 'negative' })
})
```
**Wrong:**
```typescript
// ❌ Only happy path tested
it('should create plan', async () => {
  const plan = await service.createPlan(dto, userId)
  expect(plan.name).toBe('Trip')
})
```

---

## 🚨 ANTI-PATTERNS (NEVER DO THIS)

| Anti-Pattern | Why Bad | Correct Alternative |
|--------------|---------|---------------------|
| Direct DOM manipulation | Breaks Vue reactivity | Use `ref` and `v-model` |
| Global state in modules | Not reactive, hard to test | Use Pinia stores |
| Inline styles | Not maintainable | Use Quasar classes or scoped styles |
| Mutating props | Breaks one-way data flow | Emit events, use `v-model` |
| Long functions (>50 lines) | Hard to test/maintain | Extract smaller functions |
| Nested callbacks | Callback hell | Use `async/await` |
| Magic numbers | Hard to understand | Use named constants |
| Copy-paste code | Duplication bugs | Extract reusable functions |

---

## 📋 Pre-Commit Checklist

**Before every commit, verify:**

- [ ] `./scripts/validate-types.sh` passes (Type Safety)
- [ ] `./scripts/validate-quality.sh` passes (This protocol)
- [ ] `pnpm type-check` passes (TypeScript)
- [ ] `pnpm lint` passes (ESLint)
- [ ] `pnpm test` passes (Unit tests)
- [ ] No `console.log` left (use `console.error` for errors)
- [ ] No `TODO` comments without JIRA ticket
- [ ] No commented-out code (delete or explain why)
- [ ] Updated `docs/technical/reference.md` if API/architecture changed

**Git will automatically run validation. If it fails:**
1. Read error message carefully
2. Fix the issue
3. Re-run validation manually
4. Commit again

**To bypass (EMERGENCY ONLY):**
```bash
git commit --no-verify
```

---

## 🆘 Emergency Fix Commands

```bash
# Find all violations of specific rule
grep -r "new WebSocket" apps/          # Find WebSocket usage
grep -r "\.offset(" apps/              # Find Firestore offset pagination
grep -r "console.log" apps/            # Find debug logs
grep -r "@ts-ignore" apps/             # Find ignored TS errors
grep -r "any" packages/types/          # Find any types

# Auto-fix common issues
pnpm lint --fix                         # Fix ESLint issues
pnpm prettier --write "**/*.{ts,vue}"   # Format code

# Check specific domain
pnpm --filter @my-roadtrip/web exec tsc --noEmit
pnpm --filter @my-roadtrip/api exec tsc --noEmit
```

---

## 📊 Quality Gates (CI/CD)

**All must pass in CI/CD:**

1. ✅ Type Safety (`validate-types.sh`)
2. ✅ Code Quality (`validate-quality.sh`)
3. ✅ TypeScript compilation (`pnpm type-check`)
4. ✅ Linting (`pnpm lint`)
5. ✅ Unit tests (`pnpm test`)
6. ✅ Build succeeds (`pnpm build`)
7. ✅ No security vulnerabilities (`pnpm audit`)

**If ANY gate fails:**
- PR cannot be merged
- Deployment blocked
- Fix immediately

---

## 🎓 Learning Resources

| Topic | Resource |
|-------|----------|
| Type System | `.github/instructions/TYPE_SAFETY_PROTOCOL.md` |
| Architecture | `docs/core/architecture.md` |
| API Reference | `docs/technical/reference.md` |
| CRDT Sync | `docs/core/architecture.md` → CRDT Flow |
| Firebase Best Practices | [Official Docs](https://firebase.google.com/docs/firestore/best-practices) |
| Fastify | [Official Docs](https://www.fastify.io/docs/latest/) |
| Vue 3 | [Official Docs](https://vuejs.org/guide/introduction.html) |
| Pinia | [Official Docs](https://pinia.vuejs.org/) |
| Yjs | [Official Docs](https://docs.yjs.dev/) |

---

## 🔄 Protocol Updates

**This protocol is LIVING DOCUMENTATION.**

Update when:
- New anti-pattern discovered
- Architecture decision changes
- New technology added
- Production incident reveals gap

**Update process:**
1. Identify missing rule/anti-pattern
2. Add to relevant section with examples
3. Update validation scripts
4. Update ESLint rules if applicable
5. Announce in team channel

**Last updated:** 2025-10-29
**Version:** 1.0.0
**Maintainer:** Architecture Team
