Alguien estaba delante de su tablero kanban en Notez, abrió el chat de IA y escribió cuatro palabras: qué tareas tengo hoy.
Recibió una lista. Era la lista equivocada.
No se había caído nada. No apareció ningún error. El asistente buscó, encontró algunas notas y las presentó con total seguridad — y ninguna era una de las tarjetas que estaban en el tablero, unos centímetros a la izquierda. Es el peor tipo de bug en un producto de IA, porque parece exactamente una respuesta.
Tres cosas que le faltaban al asistente
Ante una respuesta equivocada, la tentación es culpar al prompt: añadir una línea que le pida al modelo que se esfuerce más, publicarlo y cruzar los dedos. Empecé por ahí y no llegué a ninguna parte, porque el problema no estaba en la redacción. Faltaban tres piezas, y ningún ajuste del prompt crea una pieza que no existe.
No podía ver la pantalla. La petición que llegaba al modelo contenía la pregunta y nada más. Ni qué vista estaba abierta, ni qué tablero, ni el filtro de etiquetas que acotaba la lista, ni la nota del panel lateral. Una persona sentada a tu lado puede echar un vistazo a tu monitor. El modelo respondía con los ojos cerrados y sin forma de decirlo.
No sabía qué es una tarea. Notez no tiene, a propósito, una entidad de tareas. No hay un tipo Tarea, ni un campo de fecha límite, ni una columna de casillas que la app trate de forma especial. Lo que hay es un tablero, y sus tarjetas son lo que la gente quiere decir con "mis tareas". Nadie se lo había dicho al modelo. Cuando le preguntaban por tareas, hacía lo único que podía: buscar la palabra tarea.
No tenía ninguna herramienta que leyera un tablero. Esta es la que hacía imposible arreglar las otras dos. Incluso un modelo que supiera exactamente qué querías decir y qué tablero estabas mirando no tenía forma de obtenerlo. Había una herramienta para listar notas y otra para filtrarlas por etiqueta, y un tablero no es ninguna de las dos cosas: es un conjunto de columnas, cada una con un conjunto ordenado de tarjetas. Una pregunta sobre un tablero no se responde con una función que devuelve una lista.
Decirle al modelo dónde estás
El arreglo empieza en el navegador. Ahora cada mensaje lleva una pequeña descripción de la pantalla en la que se escribió: el modo de vista, qué tablero está abierto, si estás en la papelera, el texto de búsqueda y los filtros de etiquetas aplicados, la nota que tienes abierta y las tarjetas seleccionadas.
Esa descripción entra en el prompt como frases sencillas que el modelo puede citar. Sin embeddings, sin estado oculto, sin suponer qué querías decir. Si el asistente sabe que estás en un tablero, es porque se lo dijo el tablero.
Dos detalles me hicieron pensar más de lo previsto.
El primero: una nota puede estar abierta encima de una lista. Notez abre las notas en un panel lateral, así que la vista que hay detrás sigue montada y sigue describiendo su propia pantalla. Los dos componentes quieren describir el contexto, y solo uno es el que estás mirando. Gana el panel, porque está encima.
El segundo: la gente tiene que poder ver todo esto. Debajo de la conversación hay ahora una fila de chips — Vista de tablero, Esta nota, Seleccionadas: 3 — que dice exactamente qué ve el asistente. En un asistente con contexto invisible solo confías después de probarlo a base de ensayo y error. Con los chips basta con leer.
Definir una tarea, por escrito
Ahora el modelo lleva una definición que antes no tenía:
No existe una entidad separada de tareas o pendientes. Cuando el usuario pregunta por tareas, pendientes, en qué trabajar o qué está en curso, se refiere a las tarjetas del tablero que está mirando, o al tablero del espacio de trabajo cuando no hay ninguno en pantalla. Las columnas cuyo nombre significa terminado (Done, Completed, Archived y similares) no están pendientes.
Esa última frase hace más trabajo que todo lo demás junto. Sin ella, "qué tareas tengo hoy" devuelve todo el tablero, incluida la columna de lo que terminaste la semana pasada: una respuesta técnicamente completa y en la práctica inútil.
La misma definición llega a los clientes MCP. Si tienes Notez conectado a Claude Code o Cursor, tu asistente lee allí exactamente el mismo texto.
Una herramienta que lee un tablero
get_board devuelve un tablero con sus tarjetas agrupadas por columna y el total de cada columna. Sirve para el tablero del espacio de trabajo o para el tablero propio de cualquier nota. Pregunta "qué está en curso" y es una sola llamada, no una búsqueda seguida de cinco lecturas de notas.
También fusioné dos herramientas en una. filter_notes ya no existe: pasó a formar parte de list_notes, que ahora acepta columnas del tablero, valores de propiedades personalizadas y rangos de fechas de creación o de actualización, además de los filtros que ya tenía. Todos los filtros se combinan con AND, así que una pregunta como "notas con la etiqueta trabajo, actualizadas esta semana, que no estén en Done" es una sola llamada y no un bucle de pedir y cribar. Y cada nota de un resultado trae ahora sus etiquetas, su columna, los valores de sus propiedades y sus fechas: el asistente ya no abre una nota solo para leer un campo.
Pocas herramientas completas funcionan mejor que muchas escuetas. Cada herramienta de más es otra ocasión de que el modelo elija la equivocada.
El bug que escribí mientras arreglaba el bug
Con filtros de fecha, alguien tiene que decidir qué significa "hoy", y la respuesta honesta es que depende de dónde estés. Así que el navegador envía su zona horaria y el servidor calcula el rango en ella.
La primera versión de ese código estaba mal de una forma muy difícil de notar. La biblioteca que usé para construir una fecha con zona horaria conserva su desfase al serializarla, y las columnas de fecha de las notas son timestamp without time zone, así que Postgres habría descartado ese desfase sin avisar y habría desplazado cada rango tantas horas como separan tu zona de UTC. Cualquier usuario fuera de UTC habría recibido respuestas un poco equivocadas a "qué creé hoy", sin que nada en ningún sitio informara del fallo.
El test falló antes de pasar, y es la única razón por la que me lo creo. Después lo comprobé otra vez contra una base de datos real: una nota creada a las 22:00 UTC, la una de la madrugada en hora local, sigue contando como de hoy, tal como debe.
Los clientes MCP reciben UTC, porque allí no hay un navegador al que preguntar. Esa limitación está escrita en la descripción de la herramienta en vez de ocultarse, así que el asistente que la lee sabe qué recibe.
Dos interfaces, las mismas herramientas
Notez tiene dos formas de llegar a tus notas con un asistente: el chat dentro de la app y el servidor MCP al que se conecta tu propio editor. Comparten 29 herramientas, y el servidor MCP añade tres propias: dos para subir archivos, que necesitan una terminal, y una que lista tus espacios de trabajo en las conexiones con inicio de sesión. Las compartidas no son parecidas: tienen los mismos nombres, los mismos argumentos y los mismos campos obligatorios, y lo garantiza un test que rompe el build si las dos listas dejan de coincidir.
Ese test existe porque ya dejaron de coincidir una vez. Seis herramientas pasaron meses solo en el chat y nadie se dio cuenta, porque nada las comparaba. Una paridad que depende de que alguien se acuerde acaba perdiéndose.
Lo que responde ahora
Las mismas cuatro palabras, en el mismo tablero:
To Do: Arreglar el bug del login, Escribir las notas de la versión In Progress: Revisar el PR 42
No incluí
Doneporque ahí está lo terminado: Planificar la hoja de ruta del Q4.
Una llamada a una herramienta. La lista correcta. Y una frase que explica qué dejó fuera a propósito, que para un asistente que pasó meses respondiendo con total seguridad a la pregunta equivocada quizá sea lo más importante.
Sigue leyendo
Cómo funciona el chat de IA en Notez — las 29 herramientas con las que el chat de IA trabaja sobre tu espacio de trabajo, lo que no puede hacer y sus límites en cada plan.
Instrucciones para el chat de IA: unas para el equipo, otras para ti — instrucciones permanentes para todo un espacio de trabajo y para ti, y por qué las del propietario van al final del prompt.
Cómo funciona la búsqueda semántica en tus notas — cómo los embeddings encuentran la nota que dice "memoizar" cuando buscas "caché", y dónde la búsqueda por palabras clave sigue ganando.