Keyboard shortcuts

Press or to navigate between chapters

Press ? to show this help

Press Esc to hide this help

FND-006: Cero Compromisos en la Práctica: Salud del Código, Disciplina de Errores y el Estándar de Preparación para Producción

A partir de la versión 0.7.0 · Tipo: Calidad · Rev. 1

Referencia canónica · Ratificada por el equipo · Rev. 1 Discusión original del RFC: #5653


Una nota para el equipo antes de que lean esto.

Este es el sexto documento del marco de madurez de ZeroClaw. Los cinco anteriores abordaron la arquitectura, la documentación, la gobernanza, la infraestructura de ingeniería y la colaboración: el andamiaje estructural y humano que rodea el trabajo. Cada uno respondió una pregunta distinta sobre cómo construimos este proyecto juntos. Si los has leído todos, quizá hayas notado una pregunta que ninguno respondió: sí, pero ¿cómo lo escribimos realmente bien? El RFC de arquitectura te dijo con qué forma construir. El RFC de documentación te dijo cómo registrarlo. El RFC de gobernanza te dijo cómo coordinarte. El RFC de CI/CD te dijo cómo establecer controles. El RFC de cultura te dijo cómo trabajar con las personas que te rodean. Ninguno te dijo cómo se ve la calidad a nivel de la frase, dentro de una función, en el momento en que estás tomando una decisión.

Eso es para lo que está este documento.

Los temas específicos aquí, manejo de errores, documentación de API, diseño de pruebas, deuda técnica, son temas de Rust en la superficie. Las habilidades que desarrollan no lo son. La tecnología cambia. Cambia más rápido con cada iteración que la vez anterior. Las herramientas que estás usando hoy, este lenguaje, este framework, este asistente de IA, serán superadas. Algunas de ellas dentro del tiempo de vida de este proyecto. El criterio que este documento intenta ayudarte a construir no será superado. Se acumulará silenciosamente en el trasfondo de cada decisión que tomes, en cada lenguaje en el que alguna vez escribas, en cada sistema que alguna vez construyas, y en trabajos que quizás no tengan nada que ver con el software. Esa es la inversión que estamos haciendo en ti. No en tu capacidad de escribir Rust. En tu capacidad de pensar sobre la calidad, el fallo y el oficio, y de llevar ese pensamiento contigo a cada herramienta que alguna vez tomes en tus manos, incluidas las herramientas de IA que estás usando hoy y las que aún no existen.

Tómate tu tiempo con ello.


El Conjunto de Marcos de Madurez

Este RFC es el sexto de un conjunto de documentos que, en conjunto, forman el marco de madurez de ZeroClaw. Están diseñados para ser leídos como un todo, aunque cada uno puede entenderse por sí mismo.

RFCÁmbitoProblema
Arquitectura Intencional: Transición a MicrokernelLo que estamos construyendo y cómo está estructurado#5574
Estándares de Documentación y Arquitectura del ConocimientoCómo documentamos lo que construimos#5576
Organización del equipo y gobernanza del proyectoCómo coordinamos y tomamos decisiones#5577
Infraestructura de ingeniería: Pipeline de CI/CDCómo construimos, probamos y enviamos de manera confiable#5579
Cultura de Contribución: Colaboración Humana, Asociación con IA y Crecimiento del EquipoCómo trabajamos juntos y crecemos#5615
Compromiso cero en la práctica: salud del código, disciplina de errores y el estándar de preparación para producciónCómo escribimos código que perduraesta RFC

Los primeros cinco RFC responden preguntas estructurales y humanas. Este responde la pregunta que subyace en todas ellas: dada la estructura, dado el equipo, dadas las herramientas, ¿qué significa escribir bien el código?


Tabla de contenidos

  1. Una filosofía de desarrollo: La inversión en juicio
  2. Evaluación honesta: lo que el código nos está diciendo
    • 2.1 La evidencia
    • 2.2 Lo que los números no muestran
    • 2.3 Qué es bueno
  3. Criterios y Estándares: La Distinción Central
  4. Las Siete Disciplinas
    • 4.1 El manejo de errores como una preocupación de diseño
    • 4.2 Superficie de la API pública como una Promesa
    • 4.3 Pruebas como retroalimentación del diseño
    • 4.4 Triaje de la deuda técnica
    • 4.5 Seguridad en la capa de aplicación
    • 4.6 Observabilidad como capacidad de depuración
    • 4.7 Trabajo por encima del suelo
  5. Lo que esto significa para el desarrollo asistido por IA
  6. La portabilidad de la artesanía
  7. Lo que esto significa para los colaboradores

Historial de revisiones

RevisarFechaResumen
12026-04-12Borrador inicial

1. Una filosofía de desarrollo: La inversión en el juicio

La RFC de arquitectura introdujo una jerarquía de decisiones que describe cómo debe fluir cada elección en este proyecto:

Vision
  └── Architecture
        └── Design
              └── Implementation
                    └── Testing
                          └── Documentation
                                └── Release

Esa jerarquía responde a la pregunta de qué construir en cada capa. Este RFC se encuentra dentro de las capas de Implementación y Pruebas y plantea una pregunta diferente: ¿qué tan bien?

La respuesta a “qué tan bien” no es una lista de verificación. Las listas de verificación pueden cumplirse sin ser comprendidas, y en el software, la comprensión es lo que crea resultados duraderos. Un colaborador que ha memorizado las reglas las seguirá hasta que la situación sea ligeramente diferente. Un colaborador que ha interiorizado el criterio detrás de las reglas lo aplicará correctamente a situaciones que las reglas no anticiparon, incluidas las situaciones que más importan, que son siempre las que nadie planificó.

Esta distinción importa especialmente en el contexto de este proyecto. ZeroClaw opera en un entorno de herramientas potentes: generación de código con IA, gates de CI que detectan una amplia gama de errores comunes, linters de IDE, escáneres de seguridad automatizados. Estas herramientas son genuinamente valiosas. Definen un piso, un mínimo por debajo del cual el código no debería fusionarse. Pero lo que no pueden hacer es pensar. No pueden decidir si un error es operacional o un error del programador. No pueden evaluar si una prueba está verificando el comportamiento correcto. No pueden determinar si una API pública está documentada con la claridad suficiente para que un futuro colaborador implemente correctamente a partir de ella. Solo pueden comprobar lo que fueron programadas para comprobar.

La brecha entre “lo que las herramientas pueden verificar” y “la calidad que sirve a los usuarios, los colaboradores y el proyecto a lo largo del tiempo” se llena con el criterio. Ese criterio es lo que este documento intenta ayudarle a desarrollar, no para reemplazar las herramientas, sino para dirigirlas.


2. Evaluación honesta: Lo que el código nos está diciendo

Esta sección no es una crítica. Es un diagnóstico. El mismo enfoque que se aplicó en el RFC de arquitectura se aplica aquí: no puedes mejorar lo que no puedes nombrar, y los detalles son útiles precisamente porque son específicos.

2.1 La evidencia

La descomposición del workspace del RFC §5574 tuvo éxito. Los crates existen, los límites de los traits son reales y el compilador hace cumplir la dirección de las dependencias. Eso es un trabajo genuinamente bueno. Y dentro de esos nuevos crates, los mismos patrones que caracterizaban al monolito original se han trasladado, porque el código base avanzó antes de que el equipo tuviera un modelo compartido de cómo se ve la “calidad a nivel de implementación”.

Estos son hechos medidos, no estimaciones:

MétricaValorLo que indica
zeroclaw-config/src/schema.rs16.800 líneasAhora el archivo más grande del código base; el loop_.rs original fue señalado con 9,500 líneas en el RFC de arquitectura; este lo supera
zeroclaw-channels/src/orchestrator/mod.rs11,813 líneasSegundo archivo más grande; un único módulo que concentra la responsabilidad
zeroclaw-runtime/src/onboard/wizard.rs7,988 líneasUn único flujo de trabajo en un solo archivo
zeroclaw-runtime/src/agent/loop_.rs6,101 líneasReducido de ~9,500 en el monolito: progreso real y medible; aún es grande
zeroclaw-channels/src/orchestrator/telegram.rs5.122 líneasUna implementación de canal; un archivo
Llamadas a .unwrap() / .expect() en crates5,630Cada uno es una decisión postergada sobre el manejo de errores, ver §4.1
Llamadas a .unwrap() / .expect() en src/ heredado240La migración llevó el patrón hacia adelante a gran escala
Funciones públicas en zeroclaw-api371Toda la superficie fundamental de la API; todos los demás crates dependen de este
Líneas de comentarios de documentación en zeroclaw-api~27Proporción aproximada de 14:1 de API pública sin documentar, véase §4.2
#[allow(unused_imports)] / #[allow(dead_code)] en los módulos src/ heredados~30+ instanciasEl compilador ha identificado código que ya no se está utilizando; se le ha pedido que no lo mencione.
TODO / FIXME / todo!() / unimplemented!() en toda la base de código20Notablemente bajo, sugiere que la mayor parte de la deuda es silenciosa en lugar de estar marcada

La última fila merece su propia nota. Veinte marcadores explícitos de trabajo incompleto en una base de código de este tamaño no son un indicio de que el trabajo esté casi terminado. Es un indicio de que la mayor parte del trabajo incompleto no está siendo etiquetado como tal. La deuda sin marcar es más difícil de encontrar, de priorizar y de asignar que la deuda que ha sido nombrada. El silencio no es lo mismo que la completitud.

2.2 Lo que los números no muestran

Estos números miden lo que es contable. Las preguntas de mayor impacto cualitativo no pueden ser contadas:

  • Si las 5,630 llamadas .unwrap() están en rutas críticas o en utilidades de prueba
  • Si las pruebas existentes están probando el comportamiento o los detalles de la implementación
  • Si las funciones públicas en zeroclaw-api pueden implementarse correctamente por alguien que solo lea la firma y el tipo
  • Si un mensaje de registro emitido durante un fallo de producción contendría suficiente contexto para diagnosticar el fallo
  • Si un colaborador que trabaja en el módulo de seguridad comprende qué datos han cruzado un límite de confianza y cuáles no.

Estas son preguntas de juicio. No tienen un control de integración continua (CI). Tienen los estándares que este documento propone nombrar, y la cultura de revisión y mentoría que estamos construyendo juntos.

2.3 Qué es bueno

El diagnóstico no debe oscurecer lo que está genuinamente bien construido.

La capa de traits en zeroclaw-api es la arquitectura correcta. Provider, Channel, Tool, Memory, Observer, RuntimeAdapter y Peripheral son abstracciones limpias y bien razonadas. Son los puntos de separación correctos. El problema no es el diseño. Es que el diseño aún no está completamente expresado en la documentación, la cobertura de pruebas y la disciplina de manejo de errores. Este RFC trata de cerrar esa brecha.

El modelo de seguridad está bien pensado. Los códigos de emparejamiento, los niveles de autonomía, las capas de sandboxing y la aplicación de políticas demuestran una verdadera intención de diseño. Esa intención debe ser comprendida por cada colaborador que escriba código cerca de un límite de confianza, y este RFC existe en parte para dar a los colaboradores el vocabulario necesario para reconocer dónde están esos límites.

La infraestructura de observabilidad está madura. OpenTelemetry, Prometheus y las métricas DORA están implementadas en función de un trait Observer limpio. La infraestructura está disponible. La brecha formativa radica en cómo los colaboradores la utilizan para que realmente sea útil cuando algo falla.

La suite de pruebas no está ausente. La inversión existente en pruebas es real. El trabajo que describe este RFC trata sobre la calidad y la distribución de esa inversión: qué se prueba, cómo, y si las pruebas demuestran lo que aparentan demostrar.

ADR-004 es una excelente pieza de registro arquitectónico. Demuestra que el equipo puede producir documentación de diseño de alta calidad cuando la expectativa es clara. Este RFC propone una expectativa equivalente para el propio código.


3. Puertas y Estándares: La Distinción Central

Esta es la idea organizativa de todo el documento. Comprenderla claramente es más importante que cualquier técnica específica en la §4.

Una puerta es binaria. Aprobado o rechazado. Es automatizada, impuesta por herramientas y define el mínimo por debajo del cual no se fusiona código. El RFC de CI/CD construyó las puertas. Son reales y funcionan.

PuertaQué verifica
cargo fmt --checkEl código está formateado de manera consistente en todo el espacio de trabajo.
cargo clippy --workspace --all-targets -D warningsNo hay antipatrones conocidos de Clippy; en todo el espacio de trabajo
cargo deny checkNo hay avisos de seguridad pendientes de confirmación; cumplimiento de la licencia y del código fuente
cargo nextest run --workspaceLas pruebas que existen, pasan

Una norma es aspiracional. Describe cómo se ve la calidad por encima del mínimo. Se aplica mediante el juicio, la revisión por pares y los hábitos que el equipo construye en conjunto.

EstándarLo que describe
Disciplina de manejo de erroresLos fallos se categorizan; los errores operativos aparecen con contexto en la capa adecuada.
Documentación de la APICada elemento público cuenta con documentación suficiente para ser utilizado correctamente sin necesidad de leer la implementación.
Calidad de las pruebasLas pruebas afirman el comportamiento, no la implementación; la dificultad de las pruebas se considera como retroalimentación del diseño.
Triaje de deudasLa deuda no atendida se etiqueta, localiza y pondera según el riesgo; la deuda de alto riesgo tiene un propietario.
Postura de seguridadLos límites de confianza son explícitos a nivel de implementación, no solo a nivel de política.
Disciplina de observabilidadLos mensajes de registro responden a la pregunta de diagnóstico; los spans delimitan unidades significativas de trabajo.

Las puertas y los estándares no están en competencia. Son capas complementarias. Las puertas sin estándares generan código que pasa todas las verificaciones y aún así falla a los usuarios. Los estándares sin puertas son inaplicables. Necesitas ambos. El proyecto actualmente tiene buenas puertas y estándares poco desarrollados.

Un código puede superar todas las pruebas y seguir siendo incomprensible para el siguiente colaborador, silencioso donde debería mostrar errores, imposible de probar de forma aislada e inseguro en el límite donde la entrada del usuario se encuentra con la lógica de negocio. La marca verde responde a la pregunta “¿este código pasó las reglas que escribimos?”. No responde a la pregunta “¿es este código bueno?”. Estas no son la misma pregunta.

Esto no es una crítica a los gates. Los gates son valiosos precisamente porque definen una línea base compartida y aplicable dentro de la cual trabaja cada colaborador. El objetivo de este documento es construir el vocabulario y el criterio compartidos que definen cómo se ve lo bueno por encima de esa línea base, y explicar claramente por qué ese criterio no puede delegarse a una herramienta.


4. Las siete disciplinas

4.1 El manejo de errores como una preocupación de diseño

Cada llamada a .unwrap() es una decisión. La mayoría de las 5,630 en el código base no se tomaron de forma consciente. Se tomaron por defecto, porque .unwrap() es el camino de menor resistencia cuando necesitas extraer un valor de un Result o un Option y quieres seguir adelante. El problema con las decisiones tomadas por defecto es que no son decisiones. Son aplazamientos. Y lo que aplazan es una pregunta real: ¿qué debería suceder aquí cuando esto falla?

La respuesta depende del tipo de fallo que estés manejando. Hay tres tipos, y cada uno tiene una respuesta correcta diferente.

Errores del programador son violaciones de invariantes que deberían ser imposibles en código correcto. Una función que requiere un Vec no vacío, llamada con uno vacío. Un match de enum que alcanza un brazo que el sistema de tipos debería haber hecho inalcanzable. Estos representan bugs, no fallos operacionales, sino lógica incorrecta. panic! es la respuesta correcta, porque el objetivo es encontrarlos en tiempo de desarrollo, no frente a un usuario en tiempo de ejecución. assert! y debug_assert! son las herramientas adecuadas. .expect() con un mensaje que explique por qué este estado es imposible también es apropiado aquí. Hace que el razonamiento sea explícito y buscable, de modo que la siguiente persona que lea el código entienda por qué el panic fue intencional.

Los errores operativos son modos de fallo esperados. Tiempos de espera de red agotados. Archivos que no existen. Claves de API que han expirado. Respuestas del proveedor que llevan un estado de error. Usuarios que proporcionan entradas con formato incorrecto. Estos no son bugs. Son las condiciones normales de operación de un sistema que interactúa con el mundo. La respuesta correcta es Result<T, E>. El operador ? propaga el fallo a un llamador que está en mejor posición para decidir qué hacer al respecto. Un .unwrap() sobre un error operativo es un panic diferido: se disparará, tarde o temprano, en condiciones reales, frente a un usuario real, sin contexto útil y sin oportunidad de recuperación.

Los errores de configuración son configuraciones malformadas o faltantes descubiertas al iniciar. La respuesta correcta es fallar rápido, pero de forma específica. No un panic con un stack trace, no un mensaje vago de “invalid config”. Un mensaje que señale el campo específico, explique qué se esperaba y le indique al operador qué debe proporcionar. Un usuario que no puede iniciar ZeroClaw debido a una configuración incorrecta debería salir del proceso con una comprensión clara de exactamente qué corregir.

Tipo de fallo¿Qué significa?Respuesta correcta
Error de programaciónSe ha violado una invariant; esto debería ser imposible en un código correcto.panic!, assert!, .expect("razón por la que esto es seguro")
Error operativoModo de fallo esperado; el mundo no está cooperandoResult<T, E>, ?, tipo de error estructurado con contexto
Error de configuraciónConfiguración de inicio no válida o faltanteFallar rápido con un mensaje específico y accionable

Antes de cada .unwrap() o .expect(), pregúntate: ¿qué tipo de fallo es este? Si la respuesta es “error del programador: este estado no puede ocurrir en código correcto”, entonces .expect() con un comentario que explique el porqué es la elección correcta, y comunica tu razonamiento a todo futuro lector. Si la respuesta es cualquier otra cosa, usa ? o maneja el fallo de forma explícita.

Vale la pena entender el operador ? por lo que dice, no solo por lo que hace. Dice: reconozco que esta operación puede fallar. Estoy propagando explícitamente ese fallo a quien me llama, que está en mejor posición para decidir qué hacer al respecto. Ese reconocimiento es arquitectónicamente significativo: hace visible el contrato de manejo de errores en el punto de llamada y traslada las decisiones a la capa que tiene más contexto.

El objetivo no es eliminar todas las llamadas a .unwrap(). Algunas son correctas. El objetivo es que cada una represente una decisión consciente, con el razonamiento visible para cualquiera que lea el código. La diferencia entre .unwrap() y .expect("este vector está garantizado como no vacío por el llamador — véase §4.2 de las invariantes del motor de SOP") no es solo de estilo. Es la diferencia entre un juicio diferido y un juicio documentado.

4.2 Superficie de la API pública como una Promesa

pub es un contrato.

Cuando marcas una función, estructura, rasgo o módulo como público, estás haciendo una promesa a cada llamador. Esto incluye al contribuidor que implementa contra ella el próximo mes sin recordar tu intención original. Incluye al asistente de IA que lee tu crate para generar una implementación. Incluye a la persona que depura un incidente en producción y necesita entender lo que esto debía hacer. Incluye a ti mismo, volviendo a este código después de dos meses trabajando en otra cosa.

Un elemento público sin documentación es una promesa sin términos. Quien lo invoca no tiene forma de saber qué suposiciones hiciste al escribirlo, qué condiciones de error puede devolver y bajo qué circunstancias, qué efectos secundarios tiene, si es seguro llamarlo de forma concurrente, o cuál es la diferencia sutil entre dos funciones con nombres similares. Se ven obligados a inferir, a partir del nombre, la firma de tipos y el cuerpo de la implementación, algo que podrías haberles dicho en tres oraciones.

La situación de zeroclaw-api es lo suficientemente específica como para nombrarla directamente. Este es el único crate del que depende toda la arquitectura. Cada provider, canal, herramienta, backend de memoria, observador, adaptador de runtime e implementación de periférico en el workspace está construido sobre estos traits y tipos. Una interfaz sin documentar en esta base propaga confusión a cada crate que la implementa, a cada test que la ejercita y a cada código generado por IA que trabaja con ella. La proporción de 14:1 de superficie de API pública sin documentar no es una preferencia de estilo de documentación. Es una brecha en el contrato que el RFC de arquitectura dijo que era la capa más importante del sistema.

La dimensión de IA aquí es práctica y directa: cuando le pides a un asistente de IA que implemente un trait o llame a una función que no tiene documentación, la IA infiere la intención a partir del nombre y la firma de tipos. A veces esa inferencia es correcta. Con mayor frecuencia, produce código que compila, pasa el verificador de tipos y se comporta incorrectamente bajo condiciones específicas que la IA no sabía que debía anticipar, porque nadie las dejó por escrito. La documentación no es solo para humanos. Es la especificación que proporcionas a cada herramienta que alguna vez trabajará con tu código, y a cada persona que alguna vez dependerá de él.

Como mínimo, cada elemento público en zeroclaw-api debe incluir:

  • Una frase que describa lo que hace. No lo que es: lo que hace.
  • Una sección # Errores (si devuelve Result): bajo qué condiciones falla y qué variantes de error necesita manejar el llamador.
  • Una sección # Panics (si puede entrar en pánico): bajo qué condiciones y por qué?
  • Precondiciones (si alguna no es obvia): ¿qué debe ser cierto antes de llamar a esto?

Un comentario de documentación de tres oraciones en un método público de un trait vale más para el siguiente implementador que cien líneas de implementación sin explicación. La implementación les dice qué hace el código. La documentación les dice qué se supone que debe hacer, que es lo que importa cuando ambas divergen.

4.3 Pruebas como retroalimentación del diseño

El objetivo de una prueba no es producir una marca de verificación verde. El objetivo es crear un registro preciso y ejecutable de lo que un fragmento de código se supone que debe hacer: un registro que falle estrepitosamente si ese comportamiento alguna vez cambia.

Esta distinción es importante porque hay dos tipos fundamentalmente diferentes de pruebas, y solo una de ellas logra ese objetivo.

Una prueba que accede al estado interno de un struct, establece valores directamente, llama a un método y hace aserciones sobre los valores de retorno está probando la implementación. Si la implementación cambia, si el mismo comportamiento se logra mediante un mecanismo diferente, la prueba falla, aunque nada de lo que le importa al usuario haya cambiado. Esto crea fricción contra la refactorización sin crear seguridad. También tiende a pasar cuando el comportamiento es incorrecto de formas que la prueba no anticipó.

Una prueba que construye valores a través de interfaces públicas, ejecuta el comportamiento mediante métodos públicos y verifica los resultados observables está probando el comportamiento. Si la implementación cambia pero el comportamiento se mantiene, la prueba se supera. Si el comportamiento cambia de una manera que afecta a los usuarios, la prueba falla. Esto es lo que permite realizar refactorizaciones con confianza: las pruebas verifican que se obtenga el resultado correcto, no que se obtenga de una manera específica.

El principio más importante es el diagnóstico:

Una prueba que es difícil de escribir suele indicarte algo sobre el diseño.

Si escribir una prueba unitaria para una función requiere levantar una conexión a base de datos, simular seis dependencias, construir un objeto de configuración completo e iniciar explícitamente un runtime asíncrono, esa función probablemente está haciendo demasiado, dependiendo de demasiadas cosas o ubicada en la capa equivocada de la arquitectura. La dificultad no es una molestia que haya que sortear. Es retroalimentación. La prueba está siendo honesta sobre algo que el código todavía no es honesto consigo mismo.

Esto se conecta directamente con la estructura de crates que estableció el RFC de arquitectura. Uno de los propósitos de la descomposición en crates era crear componentes que puedan probarse de forma aislada. zeroclaw-tool-call-parser debería poder probarse con una entrada &str y sin runtime. zeroclaw-config debería poder probarse construyendo los structs de configuración directamente. Las implementaciones de traits en zeroclaw-api deberían poder probarse contra implementaciones falsas del trait, no contra el stack de producción completo. Cuando te encuentres incapaz de probar un componente sin todo su entorno, pregúntate si ha entrado en la implementación una dependencia que la arquitectura no pretendía. La prueba te está dando la respuesta; la cuestión es si la estás escuchando.

Un enfoque práctico para mejorar la calidad de las pruebas con el tiempo:

  • Cuando corriges un error, escribe una prueba que lo habría detectado. Este hábito, practicado de manera consistente, acerca el conjunto de pruebas a los modos de fallo que realmente importan.
  • Cuando agregues comportamiento, escribe una prueba que demuestre que el comportamiento existe y puede verificarse de forma aislada.
  • Cuando una prueba es difícil de escribir, dedica tiempo a preguntar por qué antes de recurrir a un simulacro. La respuesta a esa pregunta suele ser más valiosa que la prueba que estabas a punto de escribir.

4.4 Triaje de la deuda técnica

La palabra “deuda” es útil porque conlleva la implicación correcta: acumula intereses. La deuda que se deja sin examinar en un área de alto tráfico del código base se acumula de forma compuesta: el código nuevo se adapta a su presencia, las nuevas suposiciones se construyen sobre las antiguas, y el costo de abordarla crece con cada capa que se añade encima.

El error más común que cometen los equipos con la deuda técnica es tratarla como binaria: o bien todo es deuda y no se puede hacer nada al respecto, o bien nada es deuda y no se debe dedicar tiempo a ello. Ambas posturas son incorrectas. La pregunta útil es: ¿qué deuda, en qué ubicación, conlleva el mayor riesgo en este momento?

Dos ejes determinan la prioridad.

Proximidad a un límite de confianza. El código que maneja la entrada del usuario, aplica políticas de seguridad, ejecuta herramientas, gestiona la autenticación o procesa datos de fuentes externas está operando cerca de un límite de confianza. Las fallas en este contexto pueden ser explotadas, corromper silenciosamente el estado o generar un comportamiento incorrecto con consecuencias de seguridad. La deuda técnica cerca de los límites de confianza conlleva un riesgo desproporcionado en relación con su tamaño.

Radio de impacto. La deuda en zeroclaw-api, la base de la que depende todo lo demás, tiene un radio de impacto mayor que la deuda en la implementación de un solo canal. Una suposición errónea en un tipo fundamental se propaga a todos los lugares donde se usa ese tipo. La deuda en un crate hoja afecta solo a los consumidores de ese crate.

Alto radio de explosiónRadio de explosión bajo
Cerca de un límite de confianzaDirección en el ciclo actualDirección en el próximo ciclo planificado
Lejos de un límite de confianzaDirección en una refactorización planificadaAbordar de manera oportunista, a medida que el trabajo adyacente pasa

Este marco implica que un .unwrap() en la ruta de aplicación de la política de seguridad no es el mismo problema que un .unwrap() en un formateador de visualización de la CLI. Ambos aparecen en el recuento de 5.630. El recuento nos indica el alcance. La triaje nos indica la prioridad.

Cuando estés trabajando en un archivo y notes deuda técnica, un .unwrap() que representa un error operacional no manejado, una función que ha crecido hasta manejar cuatro responsabilidades separadas, un #[allow(dead_code)] silenciando algo que nadie llama, no necesitas arreglarlo todo. Necesitas preguntarte: ¿está esto en una ubicación de alto riesgo? Si lo está, abórdalo en este PR o crea un issue de seguimiento con la ubicación específica, el riesgo y un responsable propuesto. Si no lo está, puedes marcarlo con un comentario // TODO(debt): <description> que lo haga visible sin hacerlo urgente. Lo que no debes hacer es dejarlo completamente sin marcar, porque el silencio es la forma en que 5,630 decisiones aplazadas se acumulan sin que nadie note la tendencia.

El patrón Strangler Fig también se aplica a este nivel. El RFC de arquitectura lo aplicó a nivel de crate: construir la nueva estructura alrededor de la antigua y migrar hacia adentro con el tiempo. El mismo patrón funciona dentro de un archivo grande. No se reescribe schema.rs en un solo PR. Se identifican las funciones que están más cerca de los límites de confianza, las que se modifican con más frecuencia o las más difíciles de probar, y se extraen primero, mejorando la estructura de forma incremental, dejando que el resto siga a un ritmo que el equipo pueda sostener.

4.5 Seguridad en la capa de aplicación

La RFC de CI/CD estableció la postura de seguridad para la cadena de suministro: cargo deny encuentra vulnerabilidades conocidas en las dependencias, aplica el cumplimiento de licencias y asegura que las dependencias provengan de fuentes aprobadas. Este es el sistema inmunológico para lo que ingresa al proyecto. Esta sección trata sobre la postura de seguridad del código que se ejecuta.

cargo deny no puede encontrar una vulnerabilidad que cree la lógica de tu aplicación. No puede indicarte si se está validando la entrada del usuario antes de que llegue a tu lógica de negocio. No puede determinar si una ejecución de herramienta está respetando el nivel de autonomía que debe imponer. Tampoco puede decirte si una ruta de error está silenciosamente ignorando un fallo en una verificación de seguridad. Todo esto requiere un colaborador que entienda dónde están los límites de confianza y cómo debe ser el código responsable en ambos lados de ellos.

Tres principios que deben guiar cualquier código escrito cerca de un límite de confianza:

Los límites de confianza son explícitos, no asumidos. Un límite de confianza es cualquier punto donde los datos llegan desde fuera de tu control directo: entrada del usuario desde cualquier canal, respuestas de API de proveedores, contenidos de archivos del sistema de archivos, salidas de plugins, resultados de herramientas, lecturas de hardware. En cada límite de confianza, valida antes de procesar. No asumas la forma, el tamaño, el tipo ni el contenido de datos que no produjiste tú mismo. El modelo de seguridad de ZeroClaw define estos límites a nivel de política. La implementación debe reflejarlos a nivel de código, no porque la política vaya a fallar, sino porque la defensa en profundidad significa que cada capa del sistema cumple con su parte, en lugar de confiar en que todas las demás capas cumplieron con la suya.

Huella mínima. Una función que necesita leer un archivo no debería poder escribir uno. Una implementación de trait que maneja los mensajes de un canal no debería tener acceso al estado de otro canal. Una herramienta que se ejecuta en el nivel de autonomía 1 no debería estar en posición de ejercer capacidades que requieren el nivel 3. El modelo de seguridad ya define estas restricciones. La disciplina consiste en escribir implementaciones que no adquieran más capacidad de la que requieren para la tarea en cuestión, y en notar cuándo una implementación está intentando alcanzar algo fuera de su alcance previsto.

Falle de forma estrepitosa cerca de los límites de seguridad. Un error en una verificación de seguridad, una evaluación de política fallida, un fallo en la verificación de firma, un intento de llamada a herramienta no autorizado, una discordancia en el código de emparejamiento, nunca deben ser silenciados sin más. Deben registrarse, propagarse y manejarse explícitamente. Un error en un asistente de visualización puede recuperarse con elegancia mediante un mensaje de registro. Un error en una ruta de autorización no. Sepa qué tipo de función está escribiendo, y deje que esa determinación dicte con cuánta agresividad expone los fallos que esta produce.

Estos no son principios de seguridad avanzados. Son higiene fundamental que aplica a cualquier código que toque algo que un usuario pueda influenciar. El RFC de arquitectura describió el modelo de seguridad como “cuidadoso”. El trabajo que este RFC solicita es hacer que ese cuidado sea legible a nivel de implementación: en las funciones que validan entradas, en las rutas de error que manejan fallos de políticas, en los límites entre lo que se le pidió al sistema que hiciera y lo que realmente hace.

4.6 Observabilidad como capacidad de depuración

La infraestructura de observabilidad está madura: trazas de OpenTelemetry, métricas de Prometheus, seguimiento de DORA y un trait Observer limpio están todos en su lugar. Este es un trabajo de calidad de producción. La brecha de aprendizaje está entre tener la infraestructura y usarla de una manera que realmente ayude cuando algo sale mal, idealmente antes de que sepas qué salió mal.

Considera dos mensajes de registro. Ambos se compilan. Ambos pasan la integración continua (CI). Ambos son sintácticamente correctos.

#![allow(unused)]
fn main() {
error!("la solicitud falló");
}
#![allow(unused)]
fn main() {
error!(
    provider = %provider_name,
    model    = %model_id,
    user     = %sender_id,
    tool     = %tool_name,
    attempt  = attempt,
    elapsed  = ?elapsed,
    err      = %e,
    "la solicitud del proveedor falló — se agotaron los reintentos"
);
}

El primero es un registro. Confirma que algo salió mal. El segundo es un diagnóstico. Responde a las preguntas que importan: qué estábamos intentando hacer, en qué contexto, con qué parámetros y qué fue exactamente lo que salió mal. La diferencia entre ambos no es la sofisticación técnica. Es si la persona que escribió el mensaje estaba pensando en la persona que algún día necesitará leerlo.

La pregunta que debes hacerte antes de escribir cualquier mensaje de registro en warn o superior:

¿Qué necesita saber la persona que debe diagnosticar este fallo en el peor momento?

Esa persona podría ser tú, dentro de seis meses, sin recordar haber escrito este código. Podría ser otro colaborador que nunca ha visto este módulo. Podría ser un usuario que presenta un informe de error con un fragmento de registro que copió desde su terminal. Escribe para ellos. Los campos que casi siempre importan son: ¿qué estábamos intentando hacer?, ¿qué contexto estaba vigente en ese momento? y ¿qué salió mal específicamente?

El mismo principio rige el diseño de los spans de trazado. Un span debe representar una unidad significativa de trabajo, contener el contexto necesario para comprender dicho trabajo y tener un nombre que tenga sentido al leerlo en un gráfico de llamas o en un visor de trazas.

#![allow(unused)]
fn main() {
// Un registro
let _span = span!(Level::INFO, "proceso");

// Un diagnóstico
let _span = span!(
    Level::INFO,
    `agent.tool_call`,
    tool = %tool_name,
    turn = turn_number,
    sender = %sender_id,
);
}

El registro estructurado y el diseño significativo de spans no son preferencias de estilo. Son lo que hace que la infraestructura de observabilidad que tienes sea realmente útil, no solo durante el desarrollo, sino en manos de usuarios que ejecutan ZeroClaw en hardware que nunca verás, en configuraciones que no anticipaste, encontrando errores que no planificaste. La infraestructura crea la capacidad. La disciplina con la que los colaboradores la utilizan determina si esa capacidad se traduce en sistemas diagnosticables.

4.7 Trabajo por encima del suelo

Las seis disciplinas anteriores abordan cada una un dominio específico. Esta sección las sintetiza en una imagen única de cómo se ve “por encima del mínimo” en la práctica: lo que un revisor, un futuro colaborador o un usuario realmente experimenta cuando se encuentra con código que cumple los estándares descritos en este RFC.

DimensiónEn el límite inferior, los gates pasanPor encima del mínimo, estándar cumplido
Manejo de erroresEl código se compila; no hay advertencias de ClippyLos fallos se categorizan; los errores operativos aparecen con contexto; los panics son intencionales y están documentados.
DocumentaciónLas pruebas de documentación pasan si existenCada elemento público puede entenderse y utilizarse correctamente sin necesidad de leer la implementación.
PruebasLas pruebas existentes pasanLas pruebas afirman el comportamiento; la dificultad de las pruebas se trata como retroalimentación de diseño; se cubren los modos de fallo que importan
DeudaSin errores ni advertencias del compilador (con #[allow] silenciando el resto)La deuda está etiquetada, ubicada y ponderada por riesgo; la deuda de alto riesgo tiene un propietario y un cronograma.
Seguridadcargo deny se pasaLos límites de confianza son explícitos; las fallas de seguridad se manifiestan de manera clara; las implementaciones respetan su alcance previsto.
ObservabilidadEl código se ejecuta y emite algoLos mensajes de registro responden a la pregunta de diagnóstico; los spans delimitan unidades significativas de trabajo con contexto útil.
Organización del códigoEl archivo se compila; existe la estructura del móduloLas funciones hacen una sola cosa; los archivos agrupan preocupaciones relacionadas; los archivos grandes son candidatos para su extracción, no la norma.

Ninguno de estos es alcanzable únicamente mediante automatización. Todos ellos son alcanzables por contribuyentes que comprenden por qué son importantes y han desarrollado el criterio necesario para aplicarlos de manera consistente. Ese es el objetivo de este documento.


5. Qué significa esto para el desarrollo asistido por IA

El RFC de cultura abordó cómo trabajar con herramientas de IA como parte de un equipo colaborativo. Esta sección aborda algo más específico: qué sucede cuando el código generado por IA se enfrenta a los estándares descritos anteriormente, y qué se necesita para reconocer y cerrar la brecha cuando no los cumple.

Las herramientas de IA son realmente buenas para superar las barreras. Generan código que se compila, satisface el verificador de tipos, pasa Clippy y, a menudo, produce pruebas junto con la implementación. Este es un valor real, y no es el objetivo de esta sección minimizarlo. El problema no es que las herramientas de IA sean poco fiables. El problema es que son fiables en lo incorrecto: producen código que pasa las comprobaciones, en lugar de código que cumple con los estándares.

La razón es estructural. La IA genera código en función de lo que puede inferir. Si una función no tiene documentación, la IA infiere la intención a partir del nombre y la firma, y a veces esa inferencia es correcta, y a veces produce un comportamiento sutilmente erróneo que solo se manifiesta en condiciones que nadie probó. Si un tipo de error no tiene documentación sobre cuándo se devuelve, la IA lo maneja basándose en el nombre de la variante. Si una suite de pruebas prueba la implementación en lugar del comportamiento, la IA genera implementaciones que coinciden con esas pruebas, lo cual puede o no coincidir con el comportamiento previsto que las pruebas debían capturar. El techo de calidad de la salida de la IA está determinado por la calidad del contexto que proporcionas. Un mejor contexto, documentación más clara, tipos de error más específicos, pruebas centradas en el comportamiento, produce una mejor salida. Un contexto poco desarrollado produce una salida que pasa los controles y delega el juicio a quien la revise después.

Esto crea una responsabilidad específica y no opcional para los colaboradores que trabajan con herramientas de IA.

La revisión no es opcional porque la haya escrito una IA. El RFC de cultura lo expresó claramente, y vale la pena repetirlo con detalles concretos: al revisar código generado por IA, las preguntas de control —¿compila?, ¿pasan las pruebas?— son el principio de la revisión, no el final. Las preguntas estándar son: ¿este código maneja los errores operativos correctamente, o les hace .unwrap()? ¿Está documentada la nueva API pública? ¿La prueba verifica el comportamiento o la implementación? ¿Está esto cerca de un límite de confianza y, de ser así, valida sus entradas? Estas preguntas son tu responsabilidad independientemente de quién escribió el código o qué herramientas se usaron para producirlo.

La IA amplifica tu criterio, no tu ausencia de él. Un colaborador que aún no tiene un modelo mental de cómo se ve un buen manejo de errores aceptará el manejo de errores generado por IA tal cual: .unwrap() incluido. Un colaborador que ha interiorizado §4.1 puede mirar el mismo resultado y dirigir la herramienta: “esta es una ruta de error operacional; usa ? y propaga el fallo al llamador con contexto.” La herramienta producirá una versión corregida. El mismo patrón se aplica a cada disciplina en §4. La herramienta es poderosa en manos de alguien que sabe qué pedir. Sin esa dirección, produce código que satisface al compilador y delega las decisiones reales a la siguiente persona en la cadena.

Esta relación se potencia en ambas direcciones. Un equipo que comprende los estándares obtiene progresivamente más valor de las herramientas de IA a medida que estas mejoran, porque puede dirigir herramientas más capaces con mayor precisión. La brecha entre “lo que produjo la herramienta” y “lo que exige el estándar” se convierte en algo que pueden cerrar mediante dirección en lugar de reescritura manual. Un equipo que no desarrolla ese criterio obtiene un camino más rápido hacia el mismo nivel mínimo de calidad, sin la capacidad de superarlo. La inversión descrita a lo largo de este documento es también, directamente, una inversión en la efectividad a largo plazo de cada herramienta de IA que el equipo llegue a usar, porque el valor de esas herramientas escala con la claridad del criterio que las dirige.


6. La portabilidad de Craft

La tecnología cambia. Cambia más rápido con cada iteración que la vez anterior, y ese ritmo se está acelerando. Las herramientas específicas de este documento: Rust, cargo, clippy, el SDK de OpenTelemetry, los asistentes de IA que el equipo usa hoy, serán reemplazadas. Algunas de ellas dentro del tiempo de vida de este proyecto. Las plataformas cambiarán. Los lenguajes evolucionarán. El ecosistema de herramientas se verá diferente en cinco años de como se ve hoy, y diferente otra vez en diez.

Los modelos mentales en este documento no cambiarán.

La pregunta “¿qué debería ocurrir aquí cuando esto falla, y quién necesita saberlo?” no caduca cuando cambia el lenguaje. La harás en el próximo lenguaje que aprendas. La harás al diseñar un sistema distribuido donde el “lenguaje” es un protocolo de red. La harás al construir cualquier cosa de la que dependan otras personas y que no puedas supervisar personalmente. El mecanismo específico de Rust para responderla: Result<T, E>, el operador ?, tipos de error estructurados con contexto, es una respuesta a una pregunta que existe en todas partes.

La pregunta “¿cuál es la interfaz pública que estoy prometiendo, y mi documentación refleja esa promesa?”: te la harás al diseñar una API, al escribir una especificación técnica, al definir el alcance de las responsabilidades de un equipo, al comunicar requisitos a otro equipo, a una herramienta de IA, a un cliente, a un contratista. El modelo de promesa y términos de las interfaces públicas se extiende mucho más allá de Rust y mucho más allá del software.

La pregunta “¿qué demuestra realmente mi prueba?” se extiende más allá del software hacia cualquier dominio donde necesites verificar que un sistema se comporta según lo previsto. El instinto de hacerla, de distinguir entre la evidencia de que tu implementación existe y la evidencia de que ocurre lo correcto, es la habilidad. La sintaxis para expresarla en Rust es incidental.

La pregunta “¿qué necesitaría saber la persona que tiene que diagnosticar este fallo?” es una pregunta de ingeniería que se aplica a cualquier cosa que construyas de la que dependan otras personas. También es, en un nivel más profundo, una pregunta sobre empatía, sobre recordar que la persona al otro lado de tu trabajo es una persona real con un problema real, en un momento que no puedes predecir, con un contexto que no estarás ahí para proporcionar.

No estás aprendiendo Rust. Estás, a través del vehículo de Rust, aprendiendo a construir cosas en las que se puede confiar. Eso es transferible. Se acumulará mientras lo practiques, en cada lenguaje, cada sistema, cada equipo y cada dominio en el que trabajes.

Esta es la inversión que el proyecto está realizando en ti. No en tus habilidades técnicas específicas, sino en tu capacidad para aportar juicio, oficio y cuidado a lo que construyas a continuación. Y, a su vez, es la inversión que tú haces en cada persona que algún día dependerá de algo que hayas construido.


7. Qué significa esto para los colaboradores

Si eres nuevo en Rust o en el desarrollo de software:

Las siete disciplinas en §4 no son requisitos que deba dominar antes de poder contribuir. Son un mapa del territorio: cosas que encontrará mientras trabaja, nombradas con suficiente claridad para que sepa qué está viendo cuando las encuentre.

Comienza con §4.1. El modelo mental de manejo de errores es lo más importante que puedes internalizar desde el principio, y no es específico de Rust. Cuando leas código existente y te encuentres con .unwrap(), pregúntate en cuál de las tres categorías cae. Cuando escribas nuevo código, haz la misma pregunta sobre tus propias decisiones. Este hábito, practicado de manera consistente, mejora cada archivo que toca y desarrolla un juicio que te acompañará en todas partes.

No esperes a sentirte listo para aplicar estos estándares. Aplícalos de manera imperfecta, haz preguntas cuando no estés seguro de a qué categoría pertenece algo y considera los comentarios que recibas en la revisión como la enseñanza que se pretende que sean. Nadie llegó sabiendo estas cosas. Se aprendieron, lentamente, a través de exactamente el tipo de trabajo que estás haciendo aquí.

Si estás utilizando herramientas de IA para ayudarte a contribuir:

Los estándares de este documento son aquellos contra los que una revisión cuidadosa evaluará el código generado por IA. También son, en la práctica, el contexto que hace que la salida de la IA sea más correcta antes de llegar a revisión. Antes de pedirle a una IA que implemente algo, verifique si las interfaces contra las que implementará están documentadas. Si no lo están, documéntelas primero, o incluya la documentación como parte de lo que le pide a la IA que produzca. La salida será más correcta, habrá cerrado una brecha real en los cimientos, y el siguiente colaborador que llegue se beneficiará de ambas cosas.

Cuando recibas comentarios sobre código generado por IA, trátalos como comentarios sobre el código en sí, no como una crítica a tu decisión de usar IA. Los estándares se aplican por igual, independientemente de la autoría. La pregunta siempre es: ¿este código cumple con el estándar? Si no lo cumple, ¿qué debe cambiar y por qué?

Si estás revisando solicitudes de extracción (pull requests):

Las preguntas de control —¿compila?, ¿pasan las pruebas?, ¿lo acepta Clippy?— son el piso, no el techo. Una revisión que solo responde esas preguntas es una revisión incompleta. Use el marco de trabajo de la §3 y las disciplinas de la §4 para estructurar sus observaciones. Nombre el estándar que está aplicando, explique por qué es importante y separe claramente las objeciones bloqueantes de las sugerencias no bloqueantes.

El objetivo de una revisión no es encontrar fallos. Es transferir comprensión. Cada comentario específico que incluye una explicación, “esta es una ruta de error operacional; aquí está el porqué .unwrap() crea un riesgo de producción en este punto y qué usar en su lugar”, es una inversión en el contribuidor al que estás revisando. Esa inversión se acumula. El contribuidor que comprende el principio lo aplicará correctamente en las siguientes diez situaciones en las que importe, sin necesidad de que se lo digan de nuevo.

Si eres un mantenedor o un colaborador más experimentado:

Tú estás en la mejor posición para hacer realidad estos estándares, no imponiéndolos desde arriba, sino modelándolos en tu propio código y nombrándolos explícitamente en las revisiones. La enseñanza más efectiva en un proyecto de código abierto ocurre en los hilos de PR y en los comentarios del código, no en los documentos. Este documento proporciona el vocabulario. Usarlo de manera consistente en las revisiones cotidianas es lo que lo convierte de palabras en una página en práctica compartida.

Cuando veas un .unwrap() en una ruta de error operacional, nómbralo como tal. Cuando veas una función pública sin documentación, hazte la pregunta: ¿qué necesita saber aquí un futuro implementador? Cuando veas una prueba que se rompería con una refactorización válida, explica por qué eso importa. Estas no son correcciones: son la mentoría continua que el RFC de cultura identificó como una de las cosas más importantes que un contribuidor con más experiencia puede ofrecer.