Status: Aprovado Data: 2026-02-07 Autor: Aria (Architect Agent) Modo: YOLO - Decisões autônomas com rationale claro
Virtual Office é um SaaS greenfield que visualiza agentes AIOS trabalhando em tempo real. Esta arquitetura prioriza MVP speed com fundação sólida para escala futura.
Decisões Chave:
- Frontend: React 18 + Vite (ecossistema maduro, time real expertise)
- Rendering: Canvas 2D isométrico (MVP velocity, 3D em roadmap)
- WebSocket: Socket.io (auto-reconnect, broadcasting, dev experience)
- State: Zustand (simplicidade, performance, real-time friendly)
- License: HMAC-SHA256 (pragmático, seguro suficiente para MVP)
- Database: SQLite (local-first, zero config, migração fácil)
- Testing: Vitest + Playwright (Vite ecosystem, velocidade)
Timeline: 8-12 semanas para MVP completo (4 epics, 17 stories)
Decisão: React 18 com TypeScript + Vite
Rationale:
- Ecosystem Maturo: Maior biblioteca de componentes (Shadcn/ui, Radix), state managers, ferramentas
- Real-Time Support: Concurrent features do React 18 otimizam atualizações frequentes (WebSocket streams)
- Time Expertise: Equipe já tem experiência com React (menor curva de aprendizado)
- Hiring Pool: Facilita contratação futura se necessário
- Vite: Build ultra-rápido (< 1s HMR), TypeScript out-of-box
Alternativas Consideradas:
- Vue 3: Excelente, mas ecossistema menor para enterprise features
- Svelte: Mais rápido, mas ecossistema imaturo para SaaS complexo
Implicações:
- Bundle size: ~150KB gzipped (React + ReactDOM + Router)
- Dev velocity: Alta (componentes prontos, debugging maduro)
- Performance: 30+ FPS garantido com otimizações corretas
ADR: docs/architecture/adrs/adr-001-frontend-framework.md
Decisão: Canvas 2D com projeção isométrica para MVP, migração 3D em v2.0
Rationale:
- MVP Velocity: Canvas 2D é 3-5x mais rápido de implementar que Three.js
- Performance Garantida: 60 FPS em hardware modesto (sem GPU requirements)
- Baixa Complexidade: Menos bugs, debugging mais simples
- Visual Suficiente: Isométrico entrega "office aesthetic" sem overhead 3D
- Migração Path: Arquitetura permite swap para Three.js sem refactor total
Implementação:
// Camada de abstração para rendering
interface Renderer {
drawAgent(agent: Agent, position: Position): void;
drawMonitor(monitor: Monitor, content: Activity[]): void;
animate(deltaTime: number): void;
}
class Canvas2DRenderer implements Renderer {
// Isometric projection: x' = (x - y), y' = (x + y) / 2
projectIsometric(x: number, y: number): [number, number] {
return [x - y, (x + y) / 2];
}
}
// Future: class WebGLRenderer implements RendererAssets:
- Sprites: Desks, monitors, chairs em PNG (com alpha channel)
- Agents: Avatar isométrico por role (dev=blue, qa=green, etc.)
- Animations: Sprite sheets para online/offline transitions
Alternativa 3D (v2.0):
- Three.js com modelos low-poly
- Mantém interface
Rendererpara compatibilidade
ADR: docs/architecture/adrs/adr-002-rendering-approach.md
Decisão: Socket.io para comunicação real-time
Rationale:
- Auto-Reconnection: Lida com network drops automaticamente (crítico para desktop apps)
- Broadcasting: Facilita multi-client sync para Enterprise features futuras
- Rooms: Isola activity por projeto (Performance optimization)
- Fallback: HTTP long-polling se WebSocket falhar (rare mas útil)
- Dev Experience: Debug tools, logging estruturado, middlewares
Arquitetura:
// Backend: Socket.io Server
import { Server } from 'socket.io';
const io = new Server(httpServer, {
cors: { origin: process.env.FRONTEND_URL },
pingTimeout: 60000,
pingInterval: 25000
});
io.on('connection', (socket) => {
socket.on('subscribe_project', ({ projectId }) => {
socket.join(`project:${projectId}`);
// Enviar estado inicial
socket.emit('initial_state', getProjectState(projectId));
});
});
// AIOS Connector → Socket.io bridge
aiosConnector.on('agent_activity', (activity) => {
io.to(`project:${activity.projectId}`).emit('agent_update', activity);
});Protocol:
// Client → Server
{
type: 'subscribe_project',
projectId: string
}
// Server → Client
{
type: 'agent_update',
agent: string,
status: 'online' | 'offline',
activity?: ActivityItem
}Performance:
- Latência: < 100ms (local network)
- Throughput: 100+ msgs/sec por projeto
- Memory: ~10KB per socket connection
Alternativa Native WS: Requer implementar reconnection manual, rooms, broadcasting (3-4 dias extra)
ADR: docs/architecture/adrs/adr-003-websocket-stack.md
Decisão: Zustand para global state, React Query para server state
Rationale:
- Simplicidade: Zero boilerplate vs Redux (5x menos código)
- Performance: Subscription granular (re-renders mínimos)
- Real-Time Friendly: Update state fora de React (WebSocket handlers)
- DevTools: Redux DevTools compatibility
- TypeScript: Type inference excelente
Arquitetura:
// Store: Agent Status
interface AgentState {
agents: Record<string, Agent>;
updateAgent: (id: string, update: Partial<Agent>) => void;
setAgents: (agents: Agent[]) => void;
}
export const useAgentStore = create<AgentState>((set) => ({
agents: {},
updateAgent: (id, update) =>
set((state) => ({
agents: {
...state.agents,
[id]: { ...state.agents[id], ...update }
}
})),
setAgents: (agents) =>
set({ agents: Object.fromEntries(agents.map(a => [a.id, a])) })
}));
// React Query: Activity Logs (server cache)
export function useActivityLog(projectId: string) {
return useQuery({
queryKey: ['activity', projectId],
queryFn: () => fetchActivityLog(projectId),
staleTime: Infinity, // WebSocket atualiza, não polling
refetchOnWindowFocus: false
});
}
// WebSocket Integration
socket.on('agent_update', (data) => {
useAgentStore.getState().updateAgent(data.agentId, data);
});State Separation:
- Zustand: UI state (active project, filters, sidebar open)
- React Query: Server cache (activity logs, project list)
- WebSocket: Real-time push updates
Alternativas:
- Redux: Over-engineered para MVP (toolkit ainda verbose)
- Jotai: Excelente mas menos maduro (debugging inferior)
- Context: Não escala com 10+ agentes updating (performance)
ADR: docs/architecture/adrs/adr-004-state-management.md
Decisão: HMAC-SHA256 com secret key server-side
Rationale:
- Pragmatismo: Suficientemente seguro para MVP, simples de implementar
- Velocidade: 1-2 dias vs 5-7 dias para RSA key infrastructure
- Reversibilidade: Fácil migrar para RSA em v2.0 se necessário
- Tamper-Proof: License key assinado, adulteração detectável
- No PKI: Não requer public/private key management
Estrutura de License Key:
VIRTUAL-OFFICE-ENT-{userId}-{expiryDate}-{hmac}
Exemplo:
VIRTUAL-OFFICE-ENT-user123-20271231-a3f2d8e9b1c4...
Algoritmo:
import crypto from 'crypto';
const SECRET_KEY = process.env.LICENSE_SECRET_KEY; // 32+ bytes random
function generateLicenseKey(userId: string, expiryDate: string): string {
const payload = `${userId}-${expiryDate}`;
const hmac = crypto
.createHmac('sha256', SECRET_KEY)
.update(payload)
.digest('hex');
return `VIRTUAL-OFFICE-ENT-${payload}-${hmac}`;
}
function validateLicenseKey(key: string): boolean {
const parts = key.split('-');
if (parts[0] !== 'VIRTUAL' || parts[1] !== 'OFFICE') return false;
const userId = parts[3];
const expiryDate = parts[4];
const providedHmac = parts[5];
const payload = `${userId}-${expiryDate}`;
const expectedHmac = crypto
.createHmac('sha256', SECRET_KEY)
.update(payload)
.digest('hex');
if (providedHmac !== expectedHmac) return false;
if (new Date(expiryDate) < new Date()) return false;
return true;
}Storage:
// Local storage (encrypted)
const encryptedKey = AES.encrypt(licenseKey, deviceId).toString();
localStorage.setItem('license', encryptedKey);Security:
- Secret Key: Environment variable, nunca commitado
- Rate Limiting: Max 5 validation attempts/hora
- Audit Log: Todas tentativas logadas
Upgrade Path (v2.0):
- Migrar para RSA-2048 se bypass se tornar problema
- Adicionar online activation/deactivation
Alternativas:
- RSA: Mais seguro mas 5x mais complexo (overkill para MVP)
- JWT: Requer token server (adiciona infra complexity)
ADR: docs/architecture/adrs/adr-005-license-crypto.md
Decisão: SQLite para local storage, migração para PostgreSQL em team server
Rationale:
- Zero Config: Arquivo local, sem servidor externo
- Portabilidade: Funciona em macOS, Windows, Linux
- Performance: 100k+ inserts/sec (suficiente para activity logs)
- Simplicidade: SQL familiar, sem ORM overhead
- Migração Path: Fácil export para PostgreSQL quando team features chegarem
Schema:
-- Projetos
CREATE TABLE projects (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
path TEXT NOT NULL,
aios_version TEXT,
last_active_at DATETIME,
archived BOOLEAN DEFAULT 0,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
-- Activity Logs
CREATE TABLE activities (
id INTEGER PRIMARY KEY AUTOINCREMENT,
project_id TEXT NOT NULL,
agent_id TEXT NOT NULL,
type TEXT NOT NULL, -- 'code' | 'command' | 'file_op' | 'tool_call'
content TEXT,
file_path TEXT,
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (project_id) REFERENCES projects(id)
);
CREATE INDEX idx_activities_project_timestamp ON activities(project_id, timestamp DESC);
CREATE INDEX idx_activities_agent ON activities(agent_id);
-- License Data
CREATE TABLE license (
key TEXT PRIMARY KEY,
tier TEXT NOT NULL, -- 'open_source' | 'enterprise'
validated_at DATETIME,
expires_at DATE
);ORM: Drizzle ORM (TypeScript-first, zero runtime overhead)
import { drizzle } from 'drizzle-orm/better-sqlite3';
import Database from 'better-sqlite3';
const sqlite = new Database('virtual-office.db');
const db = drizzle(sqlite);
// Type-safe queries
const recentActivities = await db
.select()
.from(activities)
.where(eq(activities.projectId, projectId))
.orderBy(desc(activities.timestamp))
.limit(50);Performance:
- Activity log: 1 milhão de registros sem degradação
- Cleanup: Auto-delete activities > 30 dias (CRON)
Backup:
// Auto-backup diário
setInterval(() => {
fs.copyFileSync('virtual-office.db', `backups/vo-${Date.now()}.db`);
}, 24 * 60 * 60 * 1000);Alternativas:
- JSON Files: Performance degrada com 10k+ activities
- IndexedDB: Browser-only (não funciona em Electron)
- PostgreSQL: Over-engineered para single-user MVP
ADR: docs/architecture/adrs/adr-006-database-choice.md
Decisão: Vitest (unit/integration) + Playwright (E2E)
Rationale:
- Vitest: Native Vite integration (10x mais rápido que Jest)
- Playwright: Multi-browser (Chromium, Firefox, WebKit), stable API
- TypeScript: Ambos têm suporte first-class
- Paralelização: Vitest workers, Playwright sharding
- Dev Experience: Hot reload (Vitest), UI mode (ambos)
Arquitetura de Testes:
tests/
├── unit/ # Vitest: Utils, stores, logic
│ ├── stores/
│ ├── utils/
│ └── components/
├── integration/ # Vitest: API, WebSocket, DB
│ ├── api/
│ ├── websocket/
│ └── database/
└── e2e/ # Playwright: User flows
├── onboarding.spec.ts
├── agent-monitoring.spec.ts
└── project-switching.spec.ts
Unit Tests (Vitest):
import { describe, it, expect } from 'vitest';
import { useAgentStore } from '@/stores/agent';
describe('Agent Store', () => {
it('updates agent status', () => {
const { updateAgent, agents } = useAgentStore.getState();
updateAgent('dev-1', { status: 'online' });
expect(agents['dev-1'].status).toBe('online');
});
});E2E Tests (Playwright):
import { test, expect } from '@playwright/test';
test('monitor agent activity', async ({ page }) => {
await page.goto('http://localhost:3000');
// Conectar projeto
await page.click('[data-testid="add-project"]');
await page.fill('[data-testid="project-path"]', '/path/to/aios');
await page.click('[data-testid="connect"]');
// Verificar agente online
await expect(page.locator('[data-agent="dev"]')).toHaveClass(/online/);
// Clicar agente, ver activity log
await page.click('[data-agent="dev"]');
await expect(page.locator('[data-testid="activity-panel"]')).toBeVisible();
});CI Pipeline (GitHub Actions):
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
- run: npm ci
- run: npm run test:unit # Vitest
- run: npm run test:e2e # Playwright
- uses: codecov/codecov-action@v3Coverage Targets:
- Backend: 80% (business logic crítico)
- Frontend: 60% (UI testing é custoso)
- E2E: User flows críticos (onboarding, monitoring, switching)
Alternativas:
- Jest: Mais lento que Vitest (sem Vite integration)
- Cypress: Bom mas Playwright tem melhor API e multi-browser
ADR: docs/architecture/adrs/adr-007-testing-strategy.md
┌─────────────────────────────────────────────────────────┐
│ Virtual Office UI │
│ (React 18 + Vite) │
│ │
│ ┌─────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Canvas │ │ Agent │ │ Activity │ │
│ │ Renderer │ │ Status │ │ Detail │ │
│ │ (2D Iso) │ │ Grid │ │ Panel │ │
│ └─────────────┘ └──────────────┘ └──────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ State Management (Zustand) │ │
│ └─────────────────────────────────────────────────┘ │
└────────────────────┬────────────────────────────────────┘
│ WebSocket (Socket.io Client)
│
┌────────────────────┴────────────────────────────────────┐
│ Backend API Server │
│ (Node.js + Express) │
│ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ Socket.io Server │ │
│ │ (Rooms per project, Broadcasting) │ │
│ └──────────────┬──────────────────────────────────┘ │
│ │ │
│ ┌──────────────┴──────────────┐ ┌─────────────────┐ │
│ │ AIOS Connector │ │ SQLite DB │ │
│ │ (IPC/WebSocket to AIOS) │ │ (Projects, │ │
│ │ │ │ Activities) │ │
│ └──────────────┬──────────────┘ └─────────────────┘ │
└─────────────────┴─────────────────────────────────────┘
│
┌─────────┴─────────┐
│ AIOS Framework │
│ (Local Instance) │
└───────────────────┘
User Frontend Backend AIOS SQLite
│ │ │ │ │
│ Open App │ │ │ │
├──────────>│ │ │ │
│ │ Connect WS │ │ │
│ ├───────────>│ │ │
│ │ │ Subscribe │ │
│ │ ├───────────>│ │
│ │ │ │ │
│ │ │ Agent Start│ │
│ │ │<───────────┤ │
│ │ │ Store Log │ │
│ │ ├────────────┼────────────>│
│ │ Update │ │ │
│ │<───────────┤ │ │
│ Visual │ │ │ │
│ Update │ │ │ │
│<──────────┤ │ │ │
│ │ │ │ │
│ Click │ │ │ │
│ Agent │ │ │ │
├──────────>│ │ │ │
│ │ Fetch Log │ │ │
│ ├───────────>│ Query DB │ │
│ │ ├────────────┼────────────>│
│ │ │<───────────┼─────────────┤
│ │ Activities │ │ │
│ │<───────────┤ │ │
│ Display │ │ │ │
│ Detail │ │ │ │
│<──────────┤ │ │ │
Connection Methods:
- WebSocket (Preferred): AIOS expõe porta WS para external monitoring
- IPC (Fallback): Process communication se AIOS no mesmo host
- File Watching (Last Resort): Monitor
.aios/logs/directory
Event Schema:
interface AIOSEvent {
type: 'agent_activated' | 'agent_deactivated' | 'task_started' | 'task_completed' | 'activity';
projectId: string;
agentId: string;
timestamp: string;
payload?: {
activityType?: 'code' | 'command' | 'file_op' | 'tool_call';
content?: string;
filePath?: string;
};
}Backend Connector:
import WebSocket from 'ws';
class AIOSConnector {
private ws: WebSocket;
connect(aiosUrl: string) {
this.ws = new WebSocket(aiosUrl);
this.ws.on('message', (data) => {
const event: AIOSEvent = JSON.parse(data.toString());
this.handleEvent(event);
});
}
handleEvent(event: AIOSEvent) {
// Store in DB
db.insert(activities).values({
projectId: event.projectId,
agentId: event.agentId,
type: event.payload?.activityType || 'unknown',
content: event.payload?.content,
filePath: event.payload?.filePath,
timestamp: new Date(event.timestamp)
});
// Broadcast to frontend
io.to(`project:${event.projectId}`).emit('agent_update', event);
}
}┌──────────────┐
│ User Enters │
│ License Key │
└──────┬───────┘
│
▼
┌──────────────────┐
│ Parse Key Parts │
│ (user, expiry, │
│ hmac) │
└──────┬───────────┘
│
▼
┌──────────────────┐
│ Compute Expected │
│ HMAC with Secret │
└──────┬───────────┘
│
▼
┌──────────────────┐ Valid ┌──────────────┐
│ Compare HMACs ├────────────>│ Unlock │
│ & Check Expiry │ │ Enterprise │
└──────┬───────────┘ └──────────────┘
│
│ Invalid
▼
┌──────────────────┐
│ Show Error + │
│ Purchase Link │
└──────────────────┘
Sensitive Data:
- License key: AES-256-GCM encrypted em localStorage
- Project paths: Plain (não sensível)
- Activity logs: Plain (local database, sem transmissão)
Network Security:
- WebSocket: TLS (wss://) em production
- CORS: Whitelist frontend origin apenas
- Rate Limiting: Express rate-limit middleware
| Threat | Mitigation |
|---|---|
| License key bypass | HMAC validation, rate limiting, audit log |
| Activity log tampering | SQLite integrity check, file permissions |
| WebSocket hijacking | Origin validation, session tokens |
| XSS | React auto-escaping, CSP headers |
| CSRF | SameSite cookies, CORS |
Canvas Rendering:
class OptimizedRenderer {
private offscreenCanvas: OffscreenCanvas;
private dirtyRects: Set<Rect> = new Set();
// Only redraw changed areas
render() {
if (this.dirtyRects.size === 0) return;
for (const rect of this.dirtyRects) {
this.renderRect(rect);
}
this.dirtyRects.clear();
}
// Use requestAnimationFrame
animate = () => {
this.render();
requestAnimationFrame(this.animate);
}
}Virtual Scrolling (Activity Panel):
import { useVirtualizer } from '@tanstack/react-virtual';
function ActivityPanel({ activities }: Props) {
const virtualizer = useVirtualizer({
count: activities.length,
getScrollElement: () => parentRef.current,
estimateSize: () => 60, // 60px per row
overscan: 5
});
// Only render visible rows
return virtualizer.getVirtualItems().map(item => (
<ActivityRow key={item.key} activity={activities[item.index]} />
));
}WebSocket Throttling:
import throttle from 'lodash/throttle';
// Max 10 updates/sec per agent
const throttledEmit = throttle((projectId, data) => {
io.to(`project:${projectId}`).emit('agent_update', data);
}, 100);Database Indexing:
idx_activities_project_timestamp: Fast recent activities queryidx_activities_agent: Fast per-agent filtering
Memory Management:
// Cleanup old activities (30+ days) daily
setInterval(() => {
db.delete(activities)
.where(lt(activities.timestamp, new Date(Date.now() - 30 * 24 * 60 * 60 * 1000)))
.execute();
}, 24 * 60 * 60 * 1000);Open Source:
- Frontend: Static build deployed to GitHub Pages
- Backend: User runs locally (
npm run backend) - Database: SQLite em
~/.virtual-office/
Enterprise (Cloud):
- Frontend: Vercel (CDN, auto-scaling)
- Backend: Railway (Node.js, auto-deploy)
- Database: SQLite (migrate to PostgreSQL para team server)
# .github/workflows/deploy.yml
name: Deploy
on:
push:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: npm ci
- run: npm run test
- run: npm run lint
build:
needs: test
runs-on: ubuntu-latest
steps:
- run: npm run build
- uses: actions/upload-artifact@v3
with:
name: dist
path: dist/
deploy-frontend:
needs: build
runs-on: ubuntu-latest
steps:
- uses: vercel/vercel-action@v2
with:
vercel-token: ${{ secrets.VERCEL_TOKEN }}
deploy-backend:
needs: build
runs-on: ubuntu-latest
steps:
- uses: railway-deploy@v1
with:
api-key: ${{ secrets.RAILWAY_API_KEY }}Error Tracking: Sentry
import * as Sentry from "@sentry/react";
Sentry.init({
dsn: process.env.VITE_SENTRY_DSN,
environment: process.env.NODE_ENV,
tracesSampleRate: 0.1 // 10% transaction sampling
});Analytics: PostHog (privacy-friendly)
import posthog from 'posthog-js';
posthog.init(process.env.VITE_POSTHOG_KEY, {
api_host: 'https://app.posthog.com',
autocapture: false, // Manual events only
disable_session_recording: true
});
// Track key events
posthog.capture('project_connected', { projectId });
posthog.capture('agent_activity_viewed', { agentId });Current (MVP): Canvas 2D Isométrico Future: Three.js 3D
Migration Strategy:
- Manter interface
Rendererabstrata - Implementar
WebGLRendererem paralelo - Feature flag:
ENABLE_3D_RENDERING - A/B test com 10% users
- Gradual rollout baseado em GPU detection
Current: SQLite (local) Future: PostgreSQL (cloud)
Migration:
-- Export SQLite
sqlite3 virtual-office.db .dump > dump.sql
-- Import PostgreSQL
psql -U postgres -d virtual_office < dump.sqlCode Changes: Drizzle ORM abstrai DB (zero code change)
Architecture:
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ User A UI │ │ User B UI │ │ User C UI │
└──────┬──────┘ └──────┬──────┘ └──────┬──────┘
│ │ │
│ WebSocket │ WebSocket │
└───────────┬───────┴───────────────────┘
│
┌────────▼────────┐
│ Team Server │
│ (PostgreSQL) │
└─────────────────┘
| ADR | Decisão | Rationale Chave |
|---|---|---|
| 001 | React 18 | Ecosystem maduro, real-time support, time expertise |
| 002 | Canvas 2D | MVP velocity (3-5x faster), performance garantida |
| 003 | Socket.io | Auto-reconnect, broadcasting, dev experience |
| 004 | Zustand | Simplicidade, performance, real-time friendly |
| 005 | HMAC | Pragmático para MVP, upgrade path para RSA |
| 006 | SQLite | Zero config, portabilidade, migração fácil |
| 007 | Vitest + Playwright | Vite integration, multi-browser, TypeScript |
- Story 1.1: Project setup, monorepo, CI/CD
- Story 1.2: Backend WebSocket + AIOS connector
- Story 1.3: Frontend shell + agent grid
- Story 1.4: Agent online/offline status
Deliverable: Prototype funcional - conecta AIOS, mostra status
- Story 2.1: Monitor activity feed
- Story 2.2: Detail panel
- Story 2.3: Filtering
- Story 2.4: Activity export
Deliverable: Real-time monitoring completo
- Story 3.1: Project discovery
- Story 3.2: Project switcher
- Story 3.3: Project management
- Story 3.4: Activity isolation
Deliverable: Multi-project workspace funcional
- Story 4.1: License system
- Story 4.2: Upgrade prompts
- Story 4.3: Team collaboration MVP
- Story 4.4: Documentation + Launch
Deliverable: MVP completo pronto para lançamento
Esta arquitetura prioriza MVP speed sem comprometer fundação sólida:
✅ Decisões Pragmáticas: Canvas 2D, HMAC, SQLite (implementação rápida) ✅ Tech Stack Maduro: React, Socket.io, Zustand (low risk, high velocity) ✅ Future-Proof: Abstrações permitem upgrades (3D, RSA, PostgreSQL) ✅ Performance Garantida: < 3s load, 30+ FPS, < 100ms latency ✅ Timeline Realista: 8-12 semanas para 17 stories
Próximos Passos:
- @ux-design-expert: Criar design system e mockups
- @dev: Iniciar Story 1.1 (Project Setup)
- @devops: Configurar CI/CD pipeline
Aprovação: Arquitetura pronta para implementação Data: 2026-02-07 Arquiteto: Aria (YOLO Mode)