01
El agente propone. Un humano aprueba.
Duct puede preparar un cambio en tus sistemas en vivo: pausar una campaña, añadir palabras clave negativas, marcar un evento clave de GA4. Te muestra una vista previa y una verificación de seguridad. Luego se detiene.
No hay una herramienta de aprobación o aplicación orientada al agente. Ni una deshabilitada, ni una detrás de una bandera. No existe en el registro de herramientas que ve el modelo, por lo que el modelo no puede llamarla, no se le puede convencer de llamarla, y no se le puede inyectar un prompt para que la llame. La puerta destructiva es absoluta: ninguna configuración permite que una operación destructiva se autoaplique. Revertir es parte del contrato de cada ejecutor, no una ocurrencia tardía.
Mayor autonomía es una postura, no una lista de permitidos más amplia. Cambia la frecuencia con la que el agente te interrumpe. Nunca cambia lo que se puede aplicar sin un clic.
backend/service/execution/policy.py
02
Duct alquila un arnés de agente. No se casa con uno.
Los frameworks de agentes son la capa de mayor rotación y menos diferenciada en esta pila. Son 0.x, se lanzan semanalmente, y cada uno de ellos ha forzado una migración disruptiva a alguien. Construir tu producto sobre uno es alquilar una base.
Así que el código de dominio aquí no importa ningún framework de agente. Prompts, herramientas, esquemas, objetivos, puntuación y el contrato de artefacto son Python puro. Las importaciones del framework solo residen en ejecutores y enlazadores, y una prueba aplica el límite con una lista de permitidos explícita. Añadir un archivo a esa lista de permitidos es un acto deliberado, no la forma de arreglar una prueba fallida.
Deliberadamente no hay interfaz AgentHarness, y nunca debería haberla. Toma la intersección y pierdes subagentes, sistema de archivos, habilidades, granularidad HITL y compactación, que es la razón principal para alquilar un arnés. Toma la unión y estás escribiendo un framework.
backend/agents/core/ports/__init__.py
03
Escribe el adaptador en la segunda implementación, no en la primera.
Un puerto con una implementación es una suposición. Un puerto con dos es un hecho. Las abstracciones construidas antes de que exista el segundo caso codifican lo que el primer caso necesitó, y luego todo lo demás lucha contra esa forma.
Cada puerto en la capa de agente tiene dos adaptadores, excepto uno, y ese lo indica en su propio docstring y nombra la implementación que lo merecería. Nombrar la brecha es lo que evita que se llene silenciosamente con una suposición.
STYLE.md
04
La autorización es membresía, y una clave API no es un límite.
La clave API de la app se envía al navegador. Prueba que “esta solicitud provino de la app de Duct”. Nunca ha probado que “este llamador posee esa fila”, y tratarlo como si lo hiciera es cómo se filtran los datos multi-inquilino.
Así que cada ruta que afecta a una fila con alcance de proyecto resuelve un usuario con sesión iniciada y comprueba la membresía. Un no miembro recibe 404, no 403, porque 403 les dice que la fila existe. Una prueba recorre cada ruta en el árbol y falla la compilación si una no está protegida sin una razón escrita.
backend/tests/test_route_auth_boundaries.py
05
Tu máquina, tus claves, tus datos.
Duct se ejecuta como una app de escritorio que incluye el backend como un sidecar local: SQLite en disco, solo loopback, claves de proveedor en el llavero del SO. Sin cuenta, sin servidor, nada sale de la máquina. O autoaloja el backend. O usa la versión alojada en getduct.ai, que ejecuta el mismo código que puedes leer.
Trae tu propio modelo también. Anthropic, OpenAI, Gemini, o cualquier cosa en OpenRouter, asignado por trabajo en tres niveles para que el trabajo que requiere mucho razonamiento y la summarización barata no paguen el mismo precio. Una herramienta que lee todo tu negocio no debería decidir también quién es tu proveedor.
desktop/README.md
06
Los comentarios llevan el porqué. El diff ya muestra el qué.
Un comentario que repite la línea de abajo es peor que ningún comentario, porque enseña a los lectores a saltarse los comentarios. Los que vale la pena escribir llevan lo que el código no puede: la restricción, el contrato, el fallo que motivó la línea, la alternativa que fue rechazada y por qué.
Esto importa más, no menos, en una base de código escrita parcialmente por agentes. Un conjunto de cambios individualmente defendibles que juntos disuelven una arquitectura es la forma específica en que fallan las bases de código aceleradas por agentes: cada cambio argumenta por sí mismo y nada argumenta por el conjunto. El razonamiento dejado en el archivo es lo que permite a la siguiente persona, o al siguiente modelo, notar la diferencia.
STYLE.md
07
Las contribuciones escritas por agentes son bienvenidas. Se revisan con más rigor, no menos.
Muchos proyectos están prohibiendo silenciosamente las solicitudes de extracción asistidas por IA. Duct pide lo contrario: la plantilla de PR pregunta dónde ayudó un agente, para que un revisor sepa dónde mirar con más atención. Eso no es una zona de penalización. Es la misma razón por la que dices de qué parte de un cambio estabas menos seguro.
Duct es un producto de agente construido con agentes. Pretender lo contrario en el proceso de contribución sería deshonesto, y desecharía la señal más útil que un revisor puede obtener.
CONTRIBUTING.md