Sincronización de memoria sin «gana la última escritura»
· 5 min de lectura
Dos equipos, un mismo conjunto de notas, y uno de ellos editado mientras el otro estaba en reposo. Todo sistema de sincronización responde a esto, y la mayoría lo hace con una comparación de marcas de tiempo que descarta un lado en silencio. Eso vale para un borrador que puedes volver a escribir. No vale para el archivo que un agente lee antes de cada turno, porque lo que descarta es la línea que escribiste para que algo no volviera a pasar.
Por eso la sincronización de memoria de OHarness no tiene ningún camino de «gana la última escritura». Hay una conciliación a tres bandas y, cuando esta no puede decidir, hay un conflicto que te espera.
Local primero, y solo local si quieres
Las notas viven en <project>/.oharness/memory/*.md. Funcionan sin iniciar sesión, funcionan sin conexión y son Markdown plano que puedes editar en cualquier editor. Nada de la nube sostiene el peso: la sincronización es una función que se apoya sobre archivos que ya están completos sin ella.
Al iniciar sesión se activa la sincronización automática, que se detecta en unos quince segundos mientras el harness está en marcha. La página Memoria ofrece desactivarla y sincronizar en ese momento. La desactivación persiste tras reiniciar el proceso y volver a iniciar sesión, y bloquea las solicitudes automáticas y manuales hasta que la vuelvas a activar: un interruptor que se revierte en silencio en el siguiente arranque no es un interruptor.
Un límite honesto, indicado en la documentación en lugar de descubierto más tarde: desactivarla cancela las solicitudes en curso, pero los datos que la nube ya ha aceptado no se pueden retirar. Desactivarla detiene la siguiente subida; no borra lo que ya está allí.
A tres bandas, no a dos
La conciliación necesita tres estados, no dos: lo que hay aquí, lo que hay allí y lo último en lo que ambos lados estuvieron de acuerdo. Esto último es la referencia, guardada en ~/.oharness/memory-sync/, y es lo que marca la diferencia entre «estos difieren» y «este ha cambiado».
A partir de ahí los casos no tienen nada de especial, y esa es la idea:
- sin cambios en ambos lados: se omite, sin lectura ni escritura
- cambiado en un lado: se copia al otro
- cambiado en ambos lados: un conflicto, que se muestra y se deja en tus manos
Las eliminaciones son lápidas (tombstones), no ausencias, porque un archivo ausente no se distingue de un archivo que todavía no ha llegado, y tratar lo segundo como lo primero es como un sistema de sincronización borra tu trabajo en una red lenta.
En la nube, R2 guarda revisiones inmutables en Markdown más un manifiesto que se reemplaza de forma atómica:
users/<server-verified-user-id>/projects/<project-id>/memory/
manifest.json
objects/<note-id>/<revision-id>.md
Las escrituras del manifiesto usan compare-and-swap con ETag, así que una actualización concurrente obsoleta se rechaza en lugar de aplicarse. Las revisiones son inmutables, así que el lado perdedor de un conflicto sigue siendo un objeto que existe. Y cuando el manifiesto no ha cambiado, solo se descarga el manifiesto: el caso habitual, un equipo que lleva toda la tarde en marcha sin nada que hacer, cuesta una lectura pequeña.
La identidad de un proyecto es el commit raíz de Git, que es lo que permite que el mismo proyecto en dos clones con dos rutas distintas sea un único proyecto. Los proyectos sin Git recurren a un id basado en la ruta absoluta y, por tanto, no se emparejan entre dispositivos de forma automática. Es una limitación real, no una aspereza que haya que disimular: la alternativa es una heurística que de vez en cuando fusiona dos directorios sin relación, y ese fallo no tiene versión buena.
Cada directorio de memoria local queda vinculado a la primera cuenta y al primer origen en la nube que lo sincronizaron. Cambiar cualquiera de los dos pausa la sincronización y te pide que uses otro directorio. Deliberadamente no hay reasignación con un clic, porque la versión con un clic de esa operación es la que sube las notas de una persona a la cuenta de otra.
Lo que no es
Dos cosas que conviene decir sin rodeos.
No tiene cifrado de extremo a extremo. R2 es privado y todo va por TLS, pero la nube puede leer el contenido de las notas. Si esa contrapartida no te sirve para lo que escribes, desactiva la sincronización y mantén los archivos en local, que es una configuración admitida, no una degradada.
Solo se sincronizan las notas de memoria. Ni los registros de sesión, ni los archivos, ni las credenciales, ni el registro de auditoría de permisos: este último se queda en el equipo que tomó las decisiones y se elimina junto con su sesión.
También hay límites, y son de los de primera versión: 500 entradas de manifiesto por proyecto, lápidas incluidas, 128 KiB por cuerpo de nota y 2 MiB por instantánea serializada. Las revisiones antiguas se conservan; la recolección de basura de revisiones y lápidas aún no está implementada. Las notas eliminadas y las versiones perdedoras de los conflictos se guardan como copia de seguridad en ~/.oharness/memory-backups/ y pueden restaurarse desde el editor, aunque todavía no hay un explorador de copias de seguridad.
Por qué un plugin
La memoria es un plugin de arquitectura integrado, no un servidor MCP. Registra su herramienta, su índice por turno en el prompt del sistema, su página y su sincronizador en segundo plano a través del mismo PluginHost por el que pasan los plugins de modelos, herramientas y auditoría, y { "id": "memory", "enabled": false } en plugins.json elimina los cuatro.
Esa es la prueba que un sistema de plugins supera o no. Si las funciones que vienen de serie llegan al runtime por una puerta privada, la puerta pública es decoración, y todo el que programa contra ella lo descubre poco a poco. La extensión de páginas se mantiene pequeña por la misma razón: descriptores registrados y renderizadores propiedad del host, no JavaScript de interfaz arbitrario ni un SDK de iframes. Una superficie que puede hacer cualquier cosa es una superficie que nadie puede cambiar después.
OHarness es un harness de agentes de código abierto con licencia MIT. La referencia de memoria y sincronización documenta por completo las reglas de conflicto, la estructura de almacenamiento y los límites de configuración, y sincronización autoalojada explica cómo ejecutarlo todo en tu propia infraestructura.