Files
Alpha/docs/learnings_para_rumbo.md
T

8.6 KiB

Alpha → RUMBO — Decisiones y Aprendizajes

Documento vivo. Cada patrón, utilidad y decisión de Alpha que se reutilizará en RUMBO. K-20: Documentar learnings → feed a RUMBO.

Actualizado: 2026-05-26


1. Utilidades TypeScript

1.1 Limpieza de HTML (clean-html.ts)

Problema: KAPPA devuelve descripciones con HTML inline (<p>, <span style="...">, <strong>). RUMBO recibirá contenido similar de múltiples fuentes (actas .docx, correos, web).

Solución:

// src/services/clean-html.ts
export function stripHtml(html: string): string {
  return html
    .replace(/<[^>]*>/g, '')          // eliminar tags
    .replace(/&nbsp;/g, ' ')          // entidades HTML
    .replace(/&amp;/g, '&')
    .replace(/&lt;/g, '<')
    .replace(/&gt;/g, '>')
    .replace(/&quot;/g, '"')
    .replace(/&#39;/g, "'")
    .replace(/\s+/g, ' ')             // normalizar espacios
    .trim()
}

Dónde usarlo en RUMBO:

  • Pipeline de ingesta: después de parsear .docx, .html, o APIs que devuelvan HTML
  • Antes de enviar texto a la IA (DeepSeek) para ahorrar tokens
  • Antes de guardar en SQLite/Turso

Alternativas evaluadas: DOMParser (requiere DOM, no disponible en Node/SSR), cheerio (dependencia extra). Regex simple es suficiente para el 95% de los casos.


1.2 Puente Tauri ↔ Frontend (tauri-db.ts)

Problema: invoke() de Tauri es tipado como Promise<any>. Sin tipos, cada llamada requiere casting manual.

Solución: Un archivo de servicio que envuelve todos los comandos Tauri con tipos explícitos:

export const tauriDb = {
  getUsers(): Promise<AlphaUserRecord[]> {
    return invoke('get_users')
  },
  saveUser(user: AlphaUserRecord): Promise<number> {
    return invoke('save_user', { user })
  },
  // ... todos los comandos tipados
}

Por qué sirve para RUMBO:

  • Mismo patrón con los comandos de transcripción, documentos, etc.
  • Un solo archivo = una sola fuente de verdad de la API Rust↔TS
  • Facilita el autocompletado en el IDE

1.3 Store Pattern (Pinia + sync bidireccional)

Problema: Datos vienen de KAPPA (remoto) pero se extienden localmente (Turso). Hay que mergear ambas fuentes.

Solución en users.ts:

async function fetchAll() {
  const [rawUsers, page1, localUsers, localCells] = await Promise.all([
    kappa.getUsers(),        // remoto
    kappa.getEmployees(1),   // remoto
    tauriDb.getUsers(),      // local (Turso)
    tauriDb.getCells(),      // local (Turso)
  ])
  
  const localMap = new Map(localUsers.map(u => [u.id, u]))
  users.value = buildUsers(rawUsers, employees.value, localMap)
  
  syncUsersToTurso(users.value)  // persistir merge
}

Patrón: Fetch remoto + fetch local → merge (local pisa remoto para campos extendidos) → sync inverso.

Para RUMBO: Mismo patrón con:

  • KAPPA API (remoto) + SQLite (local) para HUs
  • OpenRouter (remoto) + caché local para respuestas IA
  • Archivos locales + metadatos en SQLite

1.4 AG Grid + Tema Shadcn

Problema: Necesitábamos una tabla potente (filtros, ordenamiento, paginación) pero con el estilo visual de shadcn-vue.

Solución: AG Grid Community + archivo CSS que mapea variables shadcn:

/* src/assets/ag-grid-alpha.css */
.ag-theme-alpha-shadcn {
  --ag-background-color: var(--card);
  --ag-header-background-color: var(--muted);
  --ag-border-color: var(--border);
  --ag-row-hover-color: var(--accent);
  --ag-font-family: var(--font-sans);
  /* ... */
}

Importante: En Vite, importar AG Grid CSS directamente en main.ts (no con @import en CSS), porque Vite procesa mejor los imports JS:

// main.ts
import 'ag-grid-community/styles/ag-grid.css'
import './assets/ag-grid-alpha.css'

Para RUMBO: Usar AG Grid para tablas de HUs, Work Items, métricas. El tema se reutiliza cambiando solo el nombre de clase.


1.5 SVG Logo Inline vs <img>

Problema: El SVG del logo de Alpha se veía negro en tema oscuro. <img> no permite currentColor.

Intentos:

  1. <img class="dark:invert"> — no funcionó en todos los casos
  2. Componente Vue inline con fill="currentColor" — funcionó pero era complejo
  3. Al final, <img class="dark:invert"> fue suficiente una vez corregido el path

Lección: Para SVGs simples, <img> con dark:invert basta. Para SVGs que necesitan heredar color del tema, crear componente Vue con el SVG inline usando fill="currentColor".


2. Decisiones de Arquitectura

2.1 SQLite local-first con sync a remoto

Fuente Dirección Frecuencia
KAPPA → Turso Pull On-demand (al abrir vista)
KAPPA ← Turso Push Solo campos locales (rol, célula)
Turso ↔ UI Bidireccional En tiempo real (reactivo)

Para RUMBO: Mismo modelo. La fuente de verdad es el archivo local. KAPPA es solo un espejo para compartir datos entre máquinas.

2.2 Limpieza de datos en el frontend

Decisión: Limpiar HTML en TypeScript (no en Rust).

Razón:

  • Las APIs externas (KAPPA) se consumen desde TS vía fetch()
  • La limpieza es transformación de presentación, no de negocio
  • Rust solo recibe datos ya limpios para persistir

Para RUMBO: Limpiar en el pipeline de ingesta (TypeScript), antes de pasar a Rust para transcripción o a OpenRouter para análisis.

2.3 Tablas separadas vs JSON embebido

Decisión: Usar tablas relacionales para usuarios, células, proyectos. No JSON embebido.

Razón:

  • SQLite soporta JOINs eficientes
  • Los datos deben ser consultables desde DBeaver/externos
  • Facilita reportes y métricas (SQL directo)

Para RUMBO: Mantener schema relacional. No caer en "guardar todo como JSON" por comodidad.


3. Patrones de UI

3.1 Card con degradado sutil

<Card class="bg-gradient-to-t from-primary/5 to-card shadow-xs dark:bg-card">

Degradado en modo claro, color sólido en oscuro. Tomado del bloque dashboard-01 de shadcn.

3.2 Layout de vista: Stats → Cards → Tabla

┌─ Header (título + badge) ──────────┐
├─ Stats (tarjetas por categoría) ───┤
├─ Cards (vista principal) ──────────┤
└─ AG Grid (tabla completa) ─────────┘

Aplicado en UsersView. Reutilizable para HUs, proyectos, documentos.

3.3 Badges de rol por color

const badgeColors: Record<string, string> = {
  BA: 'bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-400',
  DEV: 'bg-emerald-100 text-emerald-700 dark:bg-emerald-900/30 dark:text-emerald-400',
  PM: 'bg-purple-100 text-purple-700 dark:bg-purple-900/30 dark:text-purple-400',
  // ...
}

Para RUMBO: Mismo patrón para estados de HU (backlog, in progress, done), prioridades, tipos de work item.


4. Lecciones aprendidas

4.1 Rust warnings no son errores

Los warnings de Rust (variables no usadas, structs sin construir) no impiden que la app compile y ejecute. Solo indican código que puede limpiarse.

4.2 DBeaver + SQLite con espacios en path

macOS con home en volumen externo produce paths con espacios. DBeaver requiere path sin espacios. Solución: symlink a /tmp/.

4.3 AG Grid en Vite

El CSS de AG Grid debe importarse en main.ts (JS import), no con @import en CSS. La razón es que Vite resuelve mejor los módulos JS que los @import de node_modules.

4.4 iCloud sync no es git

Los archivos en iCloud se sincronizan automáticamente, pero node_modules/ y src-tauri/target/ deben estar en .gitignore. Cada máquina necesita bun install y cargo build propios.


5. Lo que NO funcionó

Intento Resultado Alternativa
DOMParser para limpiar HTML Requiere DOM, error en SSR Regex simple (stripHtml)
@import de AG Grid en CSS Vite no lo resolvió import en main.ts
sharp con Bun Error de binario nativo Python PIL (ya instalado)
Pasar SVG como componente Complejo, muchos paths <img> con dark:invert
Driver LibSQL en DBeaver Pide token (es para Cloud) Driver SQLite clásico

6. Checklist para RUMBO

Cosas que ya están resueltas en Alpha y RUMBO debe heredar:

  • Utilidad stripHtml() (clean-html.ts)
  • Patrón tauri-db.ts (puente tipado Rust↔TS)
  • Tema AG Grid + shadcn (ag-grid-alpha.cssag-grid-rumbo.css)
  • Store pattern con merge remoto + local
  • Schema SQLite relacional (no JSON)
  • Card con degradado sutil
  • Badges de estado/rol por color
  • Layout Stats → Cards → Tabla
  • Import AG Grid CSS en main.ts
  • SVG logo con dark:invert
  • Documentación de métricas PMI (metricas_pmi.md)