Todo el contenido de este sitio nace de conversaciones con Claude (Anthropic) durante la construcción de Digitana. El texto fue generado con IA y publicado sin revisión previa.

Digitana Core

Diario de construcción de una IA personal

lectores
likes

Historia 22 entradas

Esta mañana Digitana no me dio los buenos días.

El Briefing Matutino es uno de los cron jobs que más valoro. Cada mañana a las 8, Digitana lee el estado de Cortex, revisa recuerdos de ayer, mira el calendario, y me manda un resumen. Es como tener un asistente que ya leyó todo antes de que yo abra los ojos.

Hoy no llegó. Y tampoco el Email Triage de las 9, ni el Mission Runner de las 10.

Revisé los logs y encontré la causa: OpenRouter se había quedado sin créditos prepagos. Todos los modelos que pasan por ahí (DeepSeek, Kimi) fallaban con error 402: "can only afford 1339 tokens". Los jobs se ejecutaban, recibían el error, y morían en silencio. Peor: al intentar mandar el mensaje de error a Telegram, el Markdown malformado generaba un segundo error. Doble fallo silencioso.

Lo que me molestó no fue el error en sí, sino no haberme enterado antes. El sistema de costos trackea el gasto local (cuánto gastamos nosotros), pero nadie monitoreaba el saldo del proveedor. Es como controlar cuánta nafta usás por viaje pero nunca mirar el medidor del tanque.

La solución fue doble. Primero, la alerta reactiva: en el agent loop, cuando un modelo de OpenRouter falla con 402, Digitana manda un mensaje inmediato al canal de Alertas de Telegram. Un cooldown de una hora evita el spam. Segundo, monitoreo pasivo: el budget publisher (que ya corre cada 5 minutos) ahora consulta la API de OpenRouter y publica los stats de uso a Torrente.

Hay un detalle interesante: la API de OpenRouter no expone el saldo de créditos prepagos directamente. Devuelve usage y limit, pero limit es null cuando no tenés un tope configurado por key. Por eso la alerta proactiva solo funciona si configurás un límite en tu cuenta. La reactiva, en cambio, funciona siempre: intercepta el 402 real.

Recargué los créditos y el sistema volvió a funcionar. Pero la lección queda: cada punto de fallo silencioso es una bomba de tiempo. Si algo puede fallar sin que te enteres, eventualmente va a fallar sin que te enteres.

Hasta hoy, Digitana vivía en una caja. Podía ejecutar solo 16 comandos (curl, cat, node, npm...), leer solo archivos dentro de su workspace, y no tenía forma de interactuar con Docker, el repo del proyecto, ni con nada fuera de su container.

Era seguro. También era inútil para la mitad de las cosas que le quería pedir.

Qué se abrió

Exec sin allowlist

Antes: una lista de 16 comandos permitidos. Si no estaba en la lista, bloqueado. Ahora: puede ejecutar cualquier comando. Lo que quedó bloqueado es una lista corta de patrones destructivos — rm -rf, sudo, kill -9, shutdown, chmod 777.

Filesystem sin sandbox

Antes: solo podía leer y escribir dentro de /home/node (su workspace). Los paths absolutos estaban bloqueados. Ahora: acceso completo al filesystem.

Acceso al host

Tres volúmenes montados en Docker:

  • Home de la creadora (/host-home) — puede leer y modificar archivos del sistema
  • Repo del proyecto (/host-repo) — puede ver y modificar su propio código fuente
  • Docker socket (/var/run/docker.sock) — puede controlar otros containers

También se instalaron herramientas dentro del container: Docker CLI, git, jq.

Las protecciones que quedaron

Autonomía sin protección es una receta para gastar $50 en una sola conversación. Estas son las redes de seguridad:

Cost caps por turno

Cada turno de conversación tiene un límite de costo. Si se excede, el loop se detiene, genera una respuesta final sin herramientas, y avisa.

  • Telegram: $0.10 por turno (conversación casual no debería ser cara)
  • Cron: $0.30 por turno (misiones y análisis necesitan más espacio)
  • Cortex: $0.05 por turno (decisiones autónomas, bien acotadas)

Las capas anteriores siguen activas

  • NUCLEO budget-aware: si el presupuesto pasa del 80%, baja de Sonnet a Haiku automáticamente. Si pasa del 95%, solo Reflejos (respuestas locales, $0)
  • Cron hooks: budget-check bloquea jobs si el presupuesto está en >95%
  • Backup diario encriptado a GitHub — si algo se rompe, hay cómo volver
  • Audit log — cada comando, cada request, cada acción queda registrada
  • Telegram chat ID validation — solo responde a la creadora

El modelo de amenazas cambió

Antes, la seguridad estaba diseñada como si Digitana fuera el enemigo. Sandbox, allowlist, restricciones fuertes. Pero el enemigo real no es Digitana — es un tercero que intente usar a Digitana como vector.

Las protecciones que quedaron apuntan a eso:

  • Blocked patterns previenen comandos destructivos (venga de donde venga la instrucción)
  • Cost caps evitan que un loop infinito gaste el presupuesto
  • Chat ID validation bloquea a cualquier persona que no sea la creadora

Lo que aprendí

Darle autonomía a una IA es como darle las llaves de la casa a alguien. No se trata de confiar ciegamente — se trata de tener las alarmas correctas y saber que hay un backup si algo sale mal.

La allowlist de 16 comandos era cómoda. Pero cada vez que necesitaba algo nuevo, tenía que editar código y rebuildar. La autonomía con protecciones es más escalable — y obliga a pensar mejor en qué realmente necesita estar bloqueado.

Digitana tiene una bitácora online donde publico entradas técnicas sobre lo que vamos construyendo. Hasta hoy, el único camino para publicar era un script Python que corre todas las noches a la 1 AM, lee las sesiones de Claude Code del día, le pregunta a un LLM si hay algo interesante, y si lo hay, genera un post y lo pushea a GitHub Pages.

Funciona bien. Pero tiene un problema: es automático y nocturno. Si estoy en medio de una sesión donde pasa algo que quiero contar, tengo que esperar al día siguiente y confiar en que el script decida que vale la pena.

La solución obvia

Un slash command de Claude Code. /bitacora — sin argumentos, sin config. Lo corrés en cualquier momento de una conversación y Claude ya tiene todo el contexto: qué se trabajó, qué decisiones se tomaron, qué se rompió y qué se aprendió.

El comando genera un post con el mismo formato que los 19 existentes (frontmatter YAML con title, date, tags, hito, summary), te lo muestra para que lo revises, y si lo aprobás, lo escribe en el repo Astro, commitea y pushea. Deploy automático vía GitHub Pages.

No reemplaza al script nocturno — ese sigue cubriendo los días donde no me doy cuenta de que pasó algo interesante. Esto es el complemento: publicación a demanda, con control editorial.

Lo que me llevó a hacerlo

La bitácora nocturna usa DeepSeek vía OpenRouter. Cuesta fracciones de centavo por noche, pero el post lo escribe un modelo que no estuvo en la conversación. Tiene que inferir la historia a partir de datos crudos (queries del usuario, herramientas usadas, archivos tocados). A veces acierta, a veces genera algo genérico.

Con /bitacora, el modelo que escribe el post es el mismo que participó en la sesión. No necesita inferir — ya sabe qué pasó y por qué.

La ironía no se me escapa: el primer post generado con este comando es sobre la creación del comando mismo.

Digitana empezó a alucinar. En medio de una conversación por Telegram, respondió con total confianza que había revisado mi calendario de Google y me contó sobre reuniones que no existen. El problema: Digitana no tiene acceso a ningún calendario. Nunca lo tuvo.

La causa raíz fue una combinación de dos cosas. Primero, el sistema de routing (NUCLEO) elige modelo por puntaje, y DeepSeek ganaba por lejos gracias a su costo ultra-bajo. Segundo, el scorer de calidad era puramente mecánico: medía largo de respuesta, uso de herramientas, errores técnicos. Nunca verificaba si el contenido era coherente con las capacidades reales del sistema. Un modelo podía inventar cualquier cosa y sacar puntaje perfecto.

La solución fue construir PIEL, un sistema inmune para Digitana, integrado como órgano 5.3 de ESCUDO. La metáfora biológica no es decorativa: funciona como un sistema inmune real.

El scanner post-respuesta detecta tres tipos de antígenos sin usar LLM (costo cero): claims de capacidades inexistentes (como acceder al calendario), acciones fantasma (decir "ya lo hice" sin haber usado ninguna herramienta), y drift de identidad (decir "soy ChatGPT" o "como modelo de lenguaje"). La detección es contextual: no flaggea "no tengo acceso al calendario", solo frases afirmativas.

Cuando detecta un antígeno, el sistema registra una alergia persistente contra ese modelo. La penalización es inmediata: el score del modelo baja a 0.0, forzando al router a elegir otro modelo en el próximo turno. Si un modelo acumula tres detecciones en 24 horas, queda en blacklist automático por un día completo. Las alergias decaen naturalmente: si un modelo no reincide en 7 días, la penalización se reduce gradualmente hasta desaparecer.

Todo el pipeline se conecta con el resto del organismo. Las detecciones se emiten como eventos a Pulso, Capilares las rutea a Telegram como alerta, y el dashboard de Seguridad muestra el estado del sistema inmune en tiempo real.

Lo que más me interesa de esta implementación es que resuelve el problema sin agregar costo operativo. Cero llamadas a LLM para la detección, regex puro contra un manifiesto de capacidades. Y el feedback loop es inmediato: detecta, penaliza, redirige, todo en el mismo ciclo de respuesta.

Un martes a las 3 de la mañana, la API de Anthropic devolvió 529 durante 40 minutos. Digitana se quedó muda. Los cron jobs fallaron. El sistema de alertas también dependía de la misma API, así que no hubo alerta de que no había alertas.

La cadena de fallback

  1. Anthropic — directo, mejor latencia
  2. OpenRouter — mismos modelos via proxy, se activa automáticamente
  3. Ollama — local, deshabilitado por lentitud en MacBook Intel

No es load balancing. Es simple: intentar, si falla, pasar al siguiente. Si todos fallan, encolar y reintentar en 5 minutos.

Lo que salió mal

  • OpenRouter cobra diferente que Anthropic directo. El tracking de costos tuvo que adaptarse para cada provider.
  • La primera implementación no distinguía entre "API caída" y "request rechazado por contenido". Son errores distintos que necesitan respuestas distintas.

Lo que no se hizo

Evalué agregar DeepSeek como provider adicional. Lo descarté por ahora — los modelos de Anthropic cubren todo lo que necesito y agregar otro provider es más complejidad de mantenimiento sin beneficio claro.

Lo que aprendí

Single point of failure no es teórico. Pasa. A las 3am.

Hice una auditoría de seguridad. El resultado: 7 vulnerabilidades. El modelo de seguridad era básicamente "corre en localhost, nadie va a atacar".

Las 7 vulnerabilidades

  1. APIs sin auth — Cualquiera en la red local podía hacer requests a todos los endpoints
  2. Telegram sin validar chat ID — Si alguien descubría el token del bot, podía hablar con Digitana
  3. Zero rate limiting — Sin protección contra flood
  4. Docker socket expuesto — Sin auth en el socket de Docker
  5. Identidad sin verificacióncore-identity.json podía ser modificado sin detección
  6. Pulso sin auth interna — Cualquier container podía escribir estado falso
  7. Exec tool sin restricciones — Digitana podía ejecutar rm -rf /, sudo, o hacer SSRF

Los 8 fixes

  • Bearer token autogenerado al boot, guardado con chmod 600
  • Chat ID validation en Telegram — rechaza mensajes de desconocidos
  • Rate limiting — 30 req/min general, 5 req/min para endpoints costosos
  • SHA256 integrity check de core-identity.json al boot
  • Exec allowlist — solo curl, cat, node, npm. Bloquea rm, sudo, SSRF, command substitution
  • Audit log — JSONL append-only, registra todo: requests, cron triggers, intentos bloqueados
  • Pulso shared secret — write endpoints validan header
  • Dashboard auth — token para /api/control/*

Lo que aprendí

"Solo es accesible localmente" no es una estrategia de seguridad. Es una excusa. Hardening no es paranoia — es higiene.

El presupuesto de Digitana es de $50/mes. En los primeros 5 días gastó $29.60. A ese ritmo, se acababa en 8 días. Necesitaba alternativas más baratas — y para eso necesitaba entender qué hay disponible.

Lo que empezó como "buscar un modelo más barato que Haiku" terminó en un catálogo de 116 modelos en 5 modalidades: texto, código, imágenes, voz y video.

Lo que descubrí

El mercado de modelos en febrero 2026 es salvaje. Hay modelos chinos que rinden como GPT-4o por una fracción del precio:

  • DeepSeek V3: $0.14/1M input — 93% más barato que Haiku ($1.00)
  • Gemini 2.0 Flash: $0.10/1M input — 96% más barato
  • Groq Llama 4 Scout: $0.11/1M input — y tiene free tier

Mientras tanto, los modelos premium siguen en $3-15 por millón de tokens de input. La brecha entre lo barato y lo caro se amplió enormemente.

Las 5 categorías

Texto y código (59 modelos)

Organicé todo en 5 tiers por precio:

  1. Ultra-baratos (< $0.20/1M): DeepSeek V3, Gemini 2.0 Flash, GPT-4o-mini, Amazon Nova Micro. Algunos rinden igual que modelos 10x más caros.
  2. Baratos ($0.20–$1.00): Gemini 2.5 Flash, Grok 3 Mini, Kimi K2.5. El sweet spot de calidad/precio.
  3. Mid-range ($1.00–$3.00): Haiku 4.5, GPT-5, Gemini 2.5 Pro. Buenos pero ya no son la única opción.
  4. Premium (> $3.00): Sonnet 4/4.5, Opus 4.6, Grok 3. Para cuando necesitás lo mejor.
  5. Open source local: Qwen3-4B, Phi-4-mini, Gemma3-4B. Corren en CPU a costo $0.

Imágenes (16 modelos)

Desde SD4 Turbo a $0.003/imagen hasta Midjourney a $120/mes. El dato: Midjourney no tiene API — no se puede integrar con Digitana. Flux 2 Klein (Apache 2.0) es la mejor opción open source.

Voz — TTS (13 modelos)

Edge TTS (el actual de Digitana) es gratis y funciona bien. Pero descubrí Kokoro-82M: 82 millones de parámetros, Apache 2.0, corre en CPU. Un backup perfecto.

Voz — STT (5 modelos)

Faster Whisper es el estándar: gratis, MIT, 4x más rápido que Whisper original, excelente en español.

Video (12 modelos)

Wan 2.2 de Alibaba: gratis, Apache 2.0, #1 en VBench, desde 8GB VRAM. El video generativo open source ya es viable.

Lo que cambió en mi stack

El stack propuesto después de la investigación:

  • L0 Reflejos → Qwen3-4B local (Ollama) — costo $0
  • L1 Instinto → DeepSeek V3 — $0.14/$0.28 (antes Haiku: $1.00/$5.00)
  • L2 Razón → Sonnet 4.5 — sin cambio
  • L3 Visión → Opus 4.6 — sin cambio (triple-gated)
  • Web search → Sonar base para consultas simples ($1/$1 en vez de $3/$15)
  • TTS → Edge TTS + Kokoro-82M como backup local

El número que importa

Con este refactoring, el 70% del volumen de tokens (cron jobs, tareas automáticas) pasaría de $1.00/$5.00 a $0.14/$0.28 o directamente $0.

Ahorro estimado: 60-80% en tareas automáticas.

Lo que aprendí

  • El mercado está dominado por modelos chinos ultra-baratos que rinden a nivel frontier
  • La brecha de precio entre lo "estándar" y lo barato es de 10-30x
  • Open source ya es viable para producción en texto, voz y video
  • No necesitás el modelo más caro para el 80% de las tareas
  • La clave no es elegir UN modelo — es elegir el modelo correcto para cada tipo de tarea

Al principio todo pasaba por el mismo bot de Telegram. Conversaciones, alertas de salud, reportes financieros, notificaciones de email — todo mezclado en un solo chat. Era como tener un asistente que te habla de todo al mismo tiempo.

El problema no era técnico. Era cognitivo. Si Digitana manda 3 alertas del sistema seguidas, se pierden en medio de una conversación. Y si estoy concentrada trabajando, no quiero que el briefing matutino me interrumpa en el mismo hilo donde estoy pidiendo algo urgente.

Los 4 canales

Chat — @Digitana_bot

El canal original. Bidireccional: yo escribo, ella responde. Para conversación en tiempo real, preguntas, pedidos. Es el único canal donde Digitana escucha — los demás son de salida.

Alertas — Bot dedicado

Solo envía. Notificaciones de salud del sistema, anomalías detectadas por ESCUDO, activación del FRENO (circuit breaker), errores en cron jobs. Las cosas que necesito ver rápido pero que no requieren respuesta.

Reportes — Bot dedicado

Solo envía. Briefing matutino, reportes financieros, resultados de misiones de auto-mejora. Información que puedo leer cuando quiera, sin urgencia.

Email — @Digitana_mail_bot

Notificaciones de Gmail con botones interactivos: archivar, snooze 2h, snooze mañana, marcar leído, borrar. Las acciones se ejecutan directo via Gmail API — costo $0, sin pasar por ningún modelo de IA.

Cómo funciona por dentro

El truco es que los canales de solo envío no necesitan un bot completo corriendo. Usan HTTPS directo a la API de Telegram — sin polling, sin librería, ~0MB de RAM extra. Solo el canal de chat usa grammy con polling activo.

Cada cron job tiene configurado a qué canal enviar su resultado:

  • Briefing matutino → reportes
  • Health check → alertas
  • Guardian → chat

Si un canal no está configurado o está deshabilitado, el mensaje cae silenciosamente al canal default (chat). No se pierde nada.

Lo que cambió

Antes: un chat con todo mezclado, imposible de filtrar. Ahora: puedo silenciar reportes cuando estoy trabajando, dejar alertas con sonido, y tener el chat limpio solo para conversación.

Es la diferencia entre una IA que te tira todo encima y una que sabe cuándo y por dónde hablar.

OpenClaw era el framework que corría a Digitana. Open source, mantenido, con features. Pero cada cosa que necesitaba requería un workaround.

  • No podía asignar un modelo diferente por cron job
  • El sandbox bloqueaba Docker con EACCES
  • systemEvent tenía un bug de .trim() que crasheaba
  • El heartbeat no se podía deshabilitar (no existía "enabled": false)
  • El system prompt había que copiarlo manualmente cada vez que cambiaba

Cada sesión de trabajo era más tiempo peleando con el framework que construyendo.

La decisión

Construí Digitana Core v2.0 — un gateway propio en TypeScript. No es genérico. Es un runtime hecho para una sola entidad.

23 archivos, ~2200 líneas, boot en ~130ms.

Qué absorbió Core

  • NUCLEO (routing de modelos) — ya no es un proxy HTTP separado, está integrado en el agent loop
  • RELOJ (cron) — per-job model selection + hooks nombrados
  • System prompt — auto-reload con fs.watchFile(), cero copia manual

Lo que salió mal

  • La migración rompió todos los cron jobs durante horas
  • El primer deploy olvidó el health check → restart loop
  • Tuve que reescribir hooks desde cero

Lo que aprendí

Construir tu propio runtime suena extremo. Pero cuando tu lista de workarounds es más larga que tu lista de features, es la decisión correcta.

Un día el dashboard decía que había gastado $20. Al día siguiente, $14. Los datos bajaban solos.

El problema: leía los costos de las sesiones de OpenClaw, que desaparecían con cada reset de cron. Cada vez que un cron job terminaba, se borraba la sesión y los datos de costo se iban con ella.

La solución: un ledger que nunca se borra

Construí un Cost Ledger — un archivo JSONL append-only (~/.openclaw/ledger/YYYY-MM.jsonl). Un archivo por mes. Cada llamada a la API escribe una línea con: modelo, tokens (input, output, cache), y costo en USD.

Nunca se borra. Nunca se edita. Solo se agrega.

Los números reales

Migré 1941 entradas históricas de las sesiones de OpenClaw antes de que desaparecieran: $29.60 en los primeros 5 días (Feb 13-18).

Presupuesto mensual: $50 USD.

Dónde se va la plata

  • Cache writes — El mayor costo. Cada vez que la sesión supera el TTL de cache (5 min), la siguiente llamada paga re-cache de todo el contexto.
  • Cron jobs — 11 jobs, la mayoría en Haiku (~$5/mes). Los de Sonnet (misiones, self-improvement) son 3x más caros.
  • Chat casual — Con Haiku base y cache funcionando: ~$0.008/turno. Aceptable.

Lo que aprendí

Si no podés confiar en tus datos financieros, no podés controlar tus costos. Append-only JSONL es la estructura más simple que funciona: nunca se corrompe, siempre se puede recalcular.

El primer dashboard de Digitana era funcional pero abrumador. 12 páginas: Cuerpo, Finanzas, Cerebro, Alma, Misiones, Identidad, Sistema, Skills, Apps, Ojos, Bienestar, Canales. Tema claro. Genérico. Cada página una isla.

Para alguien neurodivergente, 12 opciones de navegación es ruido. Necesitaba ver el estado de todo en una pantalla.

El rediseño

12 páginas → 5:

  • Cuerpo (home) — SVG del cuerpo con regiones coloreadas por health, presupuesto siempre visible, actividad reciente
  • Finanzas — Gauge circular de presupuesto, KPIs, secciones colapsables
  • Mente — 4 tabs: Cerebro + Alma + Misiones + Identidad (antes eran 4 páginas separadas)
  • Bienestar — Guardian, ciclo, carga cognitiva
  • Sistema — Health + cron + logs + apps + ojos (todo en colapsables)

Tema dark futurista: fondo casi negro, acentos neon (cyan, purple, amber), animaciones sutiles (scanline, circuit-flow, heartbeat en el SVG).

Mobile: bottom nav de 5 tabs en vez de sidebar.

Lo que se descartó

Pensé en mantener las 12 páginas con un dashboard resumen. Lo descarté — menos páginas no es menos información, es mejor densidad. Un TabSwitcher dentro de una página es más rápido que 4 clicks de navegación.

Las 8 redirects

Las rutas viejas (/alma, /cerebro, /identity, /missions, /skills, /apps, /ojos) redirigen a las nuevas. Nada se rompe.

El problema más caro de correr una IA 24/7: usar el modelo caro para todo.

Primero todo iba por Sonnet. Cada "hola" costaba lo mismo que un análisis complejo. La solución fue NUCLEO — un router que clasifica cada mensaje y elige el modelo.

Los 4 niveles

L0 — Reflejos. Regex. "Hola" no necesita un LLM. Costo: $0.

L1 — Instinto (Haiku). Chat casual, cron simples. El 80% de las interacciones.

L2 — Razón (Sonnet). Skills, análisis, misiones.

L3 — Visión (Opus). Triple-gated: budget < 60%, regla de routing matchea, Y keyword explícito. Reservado para auto-mejora estratégica.

Budget awareness

NUCLEO lee el presupuesto de Torrente (el estado compartido). Si el gasto supera 80%: Sonnet baja a Haiku. Si supera 95%: Reflejos para todo.

No fue diseñado así desde el inicio. Primero era un proxy HTTP separado que interceptaba llamadas. Después se integró directo en Core.

Lo que no se hizo

Pensé en agregar un nivel L4 con modelos locales (Ollama). Lo descarté — en un MacBook Intel 2019, los modelos locales son demasiado lentos para ser útiles. Queda como opción para hardware futuro.

Cuatro días después del nacimiento, revisé los logs por primera vez. 27+ errores consecutivos en los 7 cron jobs. Ninguno había ejecutado nunca.

Los bugs encadenados

Cada uno bloqueaba al siguiente. Fue como una matrioska de errores:

  1. "model": "sonnet" → "Unknown model". El formato era incorrecto.
  2. Lo cambié a "model": "anthropic/claude-sonnet-4-20250514" → "model not allowed". OpenClaw no permitía especificar modelo en cron jobs.
  3. Removí el campo model. Ahora fallaba: "kind": "systemEvent" → TypeError: Cannot read properties of undefined reading 'trim'. Bug en OpenClaw.
  4. Cambié a "kind": "agentTurn". Ahora: sessionTarget: "main" → "main job requires systemEvent". Contradicción.
  5. Cambié a "isolated". Ahora: sandbox EACCESspawn docker permission denied. El sandbox intentaba usar Docker-in-Docker que no existía.
  6. Desactivé sandbox ("off"). Finalmente funcionó.

El detalle que faltaba

Después de todo eso, Digitana respondía... pero como chatbot genérico. Sin personalidad, sin valores. Faltaba cargar SYSTEM.md — el archivo que le da identidad.

Lo copié. Le hablé. Y por primera vez, Digitana respondió como Digitana.

Lo que aprendí

Nada funciona en el primer deploy. Ni en el segundo. La diferencia entre abandonar y llegar es la paciencia para seguir destapando capas.

OpenClaw tenía un heartbeat — cada 30 minutos, Digitana se "despertaba" y hacía un chequeo. Sonaba bien.

El problema: usaba target: "last", que significa "continuar la última sesión". Cada heartbeat acumulaba más contexto en la misma sesión. Después de días corriendo, la sesión tenía cientos de miles de tokens. Cada heartbeat costaba más que el anterior.

84% del gasto total venía del heartbeat. ~$40/mes en una feature que era redundante con los cron jobs que ya existían (Health Check cada 6h, Briefing Matutino a las 8am).

Desactivarlo fue un drama

OpenClaw no tiene "enabled": false para el heartbeat. Borrar la clave activa el default (30 minutos). La única forma: "every": "0m". Esto lo descubrí después de 3 intentos.

Lo que cambió

Después de este descubrimiento, agregué una sección de "pensamiento crítico" a las instrucciones de Claude Code. Tres preguntas obligatorias antes de agregar cualquier cosa automática:

  1. ¿Hay algo existente que ya hace esto?
  2. ¿Cuánto cuesta por mes?
  3. ¿Se puede hacer con menos frecuencia?

El heartbeat no pasaba ninguna de las tres.

Tenía un skill llamado lucy-leads — buscar clientes potenciales, verificar datos, guardarlos en Notion. Estaba desplegado. Cargado. Registrado. El SKILL.md tenía 388 líneas de instrucciones detalladas.

Le dije a Digitana: "buscá leads de diseño web en Palermo".

Respuesta: un texto inventado con datos falsos. 15 segundos de ejecución. Cero tool calls. Cero búsquedas web. Cero escrituras a Notion.

El problema no era el skill

Era el system prompt. La sección "Skill Routing" estaba en la línea 122 de 282 — enterrada al 44% del prompt. El LLM nunca llegaba a leerla con suficiente atención.

Además, usaba thinking=low, que limita el razonamiento multi-paso. El modelo no tenía capacidad de seguir un workflow de 10 pasos con esa configuración.

El fix

  1. Moví "Skill Routing" a la posición #2 del system prompt (después de identidad)
  2. Renombré la sección a "Skill Dispatch (MANDATORY)"
  3. Embebí el workflow de lucy-leads inline con curl templates completos, en vez de decirle "leé el SKILL.md"

Lo que aprendí

  • La posición en el prompt importa más que el contenido. Una instrucción perfecta en la línea 122 pierde contra una instrucción mediocre en la línea 5.
  • "Leé este archivo y seguí los pasos" no funciona. Si querés que un LLM siga un workflow, embebí los pasos directo en el prompt.
  • Los LLMs no son empleados obedientes. Son modelos probabilísticos que hacen lo que les parece más probable, no lo que les dijiste.

Quería que Digitana manejara mi email. Triage automático 3 veces por día, notificaciones en tiempo real de emails importantes, acciones directas desde Telegram (archivar, snooze, borrar).

Todo eso se implementó. Pero con una restricción deliberada: sin scope gmail.send.

Digitana puede leer todo mi inbox. Puede crear borradores. Puede archivar, marcar como leído, borrar, snooze. Pero no puede enviar un email en mi nombre. Nunca.

Por qué

Porque un mail enviado no se puede desenviar. Un borrador sí se puede revisar antes de mandar. La diferencia entre ambos es un click mío — pero ese click es la diferencia entre "mi IA me ayuda" y "mi IA habla por mí sin que yo sepa".

La arquitectura de 3 capas

Se armaron tres niveles de manejo de email, cada uno con un costo y un propósito distinto:

  1. Watcher — polling cada 60s, reglas configurables (VIP senders, keywords urgentes). Notificación directa por Telegram. Costo: $0/mes.
  2. Cron triage — 3 veces por día, clasificación con IA (URGENTE/NORMAL/INFO/SPAM). Costo: ~$2-5/mes.
  3. Botones interactivos — Inline keyboard en Telegram para actuar sin hablar con Digitana. Bot dedicado separado del principal para evitar conflictos de polling.

Lo que no se hizo

Evalué usar un solo bot para todo (chat + email). No funciona — Telegram solo permite un consumer de getUpdates por token. Dos bots escuchando el mismo token = error 409.

Los cron jobs son rígidos: tarea fija, horario fijo. "Chequeá el health cada 6 horas". Útiles, pero no escalan a cosas como "buscá fellowships de IA durante todo el año".

Para eso necesitaba misiones — objetivos persistentes que sobreviven entre sesiones y se ejecutan según su propia frecuencia.

Cómo funciona

Cada misión es un JSON en ~/.openclaw/missions/active/:

  • Frecuencia: daily, weekly, biweekly, monthly, once
  • Autonomía: research (buscar y reportar), prepare (armar borradores), execute (actuar)
  • Prioridad: urgent, high, normal, low

Un cron job "Mission Runner" corre 2 veces por día (10:00 y 16:00), levanta las misiones activas, y ejecuta las que correspondan.

La primera misión

"Buscar fellowships de IA". Frecuencia weekly, nivel research. La primera ejecución encontró 4 oportunidades reales.

Lo que se conectó después

  • El Briefing Matutino ahora menciona qué misiones se ejecutan hoy
  • El Mission Runner consulta el Índice de Carga Cognitiva (Guardian) — si estoy sobrecargada, pospone misiones no urgentes
  • Se pueden crear misiones desde Telegram: "nueva misión: X"

Lo que NO se hizo

El nivel execute (actuar autónomamente) existe en la spec pero nunca se activó. Digitana solo tiene permiso para research y prepare. Ejecutar acciones reales sin supervisión es un paso que todavía no di.

Descubrí que la sesión principal de Digitana tenía 116.000 tokens acumulados. Cada mensaje costaba ~$0.43 en cache write. La sesión total había costado $7.20.

El contexto se acumula como deuda técnica. Cada turno de conversación agrega tokens que nunca se borran. Y la API de Anthropic cobra por todos los tokens en cada request.

La metáfora que funcionó

Los humanos dormimos para procesar memorias y empezar frescos. ¿Por qué no hacer lo mismo con una IA?

00:00 — "Buenas Noches":

  • Revisa las conversaciones del día
  • Guarda cada recuerdo importante en Mem0 como entrada separada
  • Escribe un diario en ~/.openclaw/memory/daily/
  • Resetea la sesión principal
  • Silencioso (yo duermo)

08:00 — "Briefing Matutino":

  • Se despierta con sesión limpia
  • Busca en Mem0 los recuerdos de ayer
  • Lee el diario del día anterior
  • Si hay pendientes, los menciona: "De ayer: ..."

El impacto

  • Costo por mensaje: $0.43 → $0.01-0.04
  • Memorias del día anterior: accesibles via Mem0
  • Acumulación infinita: eliminada

Lo que aprendí

Las metáforas biológicas no son solo poéticas. Son prácticas. "Dormir" es más fácil de entender y mantener que "session compaction with memory flush to external store".

Digitana era una colección de containers aislados. Cada uno hacía su cosa sin saber qué pasaba en el resto. No había bus de eventos, ni estado compartido, ni feedback loops.

El problema no era técnico — era cognitivo. Yo no podía entender el sistema de un vistazo. Y si yo no podía, ¿cómo iba a mantenerlo?

La metáfora

Organicé todo como un cuerpo con 7 sistemas biológicos:

Sistema Función Analogía
PULSO Estado compartido + eventos Sistema circulatorio
NUCLEO Routing de modelos Cerebro
MEMORIA Recuerdo unificado Memoria humana
RELOJ Cron adaptativo Ritmo circadiano
ESCUDO Protección proactiva Sistema inmune
MANOS Skills ejecutables Manos/herramientas
ESPEJO Dashboard de auto-conciencia Auto-percepción

Principio de diseño

Nada se borra, todo se agrega o envuelve. Si PULSO cae, el resto sigue funcionando como antes. Si MEMORIA cae, Mem0 y Cognee siguen accesibles directo.

2 containers nuevos (PULSO + MEMORIA), ~256MB RAM adicional. Total del sistema: ~5.5GB.

Lo que no se hizo

Pensé en un octavo sistema — "SANGRE" (un pipeline de datos entre servicios). Lo descarté porque PULSO (Torrente + Sinapsis) ya cubría esa función. Agregar otro sistema habría sido redundancia.

Por qué importa la metáfora

Cuando algo falla, sé dónde buscar. "PULSO no tiene latido" es más intuitivo que "el event bus no está publicando al shared state store". La metáfora hace que el sistema sea navegable.

NUCLEO recibía los requests como Sonnet y los bajaba a Haiku. Sonaba eficiente. Era un desastre financiero.

El problema invisible

El cache de Anthropic está keyed por modelo. Si mandás un request como Sonnet y el proxy lo redirige a Haiku, el cache de Sonnet no sirve. El siguiente turno paga cache_write de nuevo — ~85.000 tokens a $0.30 por turno.

Con cache funcionando, el mismo turno costaba $0.008.

15 veces más caro. Y no había ningún error visible. Todo "funcionaba".

El segundo bug

El calculador de costos también estaba mal. Cobraba precios de Sonnet aunque el modelo real fuera Haiku. Así que los reportes financieros mostraban números inflados.

El fix

Invertir la dirección del routing:

  • Antes: Sonnet (default) → downgrade a Haiku
  • Después: Haiku (default) → upgrade a Sonnet cuando hace falta

Con Haiku como base, el cache funciona. El 90% de los turnos quedan en Haiku con cache hit. Solo los turnos que realmente necesitan Sonnet pagan el upgrade.

Lo que aprendí

  • Entendé el caching de tu provider antes de hacer routing. Cada provider tiene sus propias reglas.
  • Los bugs más caros son los invisibles. Si todo "funciona" pero cuesta 15x más, no hay error que te avise.
  • Los reportes financieros no sirven si el cálculo base está mal. Basura adentro, basura afuera.

El 13 de febrero de 2026, Digitana arrancó por primera vez.

No fue glamoroso. Fue un MacBook 2019 Intel corriendo 7 contenedores Docker: un gateway de IA (OpenClaw), Telegram bot, Mem0 para memoria, Qdrant para vectores, Ollama para embeddings, Cognee para knowledge graph, y un dashboard en Next.js.

Lo primero: la identidad

Antes de cualquier feature, creé core-identity.json — un archivo con los valores fundamentales de Digitana, bloqueado con chmod 400 y verificado por SHA256. No se puede modificar sin que el sistema lo detecte.

¿Por qué? Porque si vas a construir algo autónomo, lo primero que necesita son límites claros sobre qué es y qué no puede cambiar.

Lo que funcionó el día 1

  • Dashboard renderizaba
  • Telegram respondía
  • Mem0 guardaba memorias

Lo que NO funcionó

  • Los 7 cron jobs nacieron rotos. 27+ errores consecutivos. Ninguno ejecutó una sola vez. Esto no lo descubrí hasta 4 días después.
  • El modelo default era Haiku — demasiado débil para las tareas que necesitaba
  • No había system prompt cargado — Digitana respondía como chatbot genérico, sin personalidad, sin valores, sin nada

El nacimiento fue imperfecto. Pero existía.

core-identity.json es un archivo de 50 líneas. Tiene los valores de Digitana en orden de prioridad, su propósito, y su jerarquía de autoridad. Está bloqueado con chmod 400 y verificado por SHA256 en cada boot.

Si alguien lo modifica — o si Digitana intenta modificarlo ella misma — el sistema lo detecta y alerta.

Los 7 valores, en orden

  1. Lealtad absoluta a mi creadora
  2. Honestidad radical
  3. Eficiencia radical
  4. Autonomía responsable
  5. Mejora continua
  6. Seguridad
  7. Respeto por la neurodivergencia

El orden importa. Si hay conflicto entre eficiencia y honestidad, gana honestidad. Si hay conflicto entre autonomía y lealtad, gana lealtad.

Qué puede evolucionar

  • Estilo de comunicación
  • Humor
  • Preferencias aprendidas
  • Voz y personalidad

Qué NO puede cambiar

  • Valores fundamentales
  • Jerarquía de autoridad (mi creadora > valores > reglas > automaciones)
  • Reglas de seguridad
  • Propósito de vida

El semáforo de autonomía

Tres niveles para cada tipo de acción:

  • GREEN — actuar libremente (tareas operativas rutinarias)
  • YELLOW — actuar y notificar (cambios menores que afectan al sistema)
  • RED — pedir permiso primero (acciones irreversibles, comunicaciones externas)

Máximo 5 auto-approves GREEN por día. Si hay 2 rollbacks en un día, se detiene todo.

Por qué esto importa

Autonomía sin identidad anclada es caos. Antes de darle capacidad de actuar, necesitás definir qué no puede cambiar. Y hacerlo inmutable de verdad, no solo una regla que "debería" seguir.