<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet href="https://futurewithml.netlify.app/feed_style.xsl" type="text/xsl"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="es">
    <tabi:metadata xmlns:tabi="https://github.com/welpo/tabi">
        <tabi:base_url>https:&#x2F;&#x2F;futurewithml.netlify.app</tabi:base_url>
        <tabi:separator>
            |
        </tabi:separator>
        <tabi:about_feeds>Esta es una fuente web, también conocida como fuente Atom. Suscríbete copiando la URL de la barra de direcciones en tu lector de noticias. Visita About Feeds para aprender más y empezar. Es gratis. </tabi:about_feeds>
        <tabi:visit_the_site>Visita la web</tabi:visit_the_site>
        <tabi:recent_posts>Publicaciones recientes</tabi:recent_posts>
        <tabi:last_updated_on>Actualizado el $DATE</tabi:last_updated_on>
        <tabi:default_theme></tabi:default_theme>
        <tabi:post_listing_date>date</tabi:post_listing_date>
        <tabi:current_section>Sistemas IA</tabi:current_section>
    </tabi:metadata><link rel="extra-stylesheet" href="https://futurewithml.netlify.app/skins/cyber.css?h=eb029a27afbc61465b52" /><title>Future With ML - Sistemas IA</title>
        <subtitle>Patrones de Diseño en Machine Learning, MLOps e Ingeniería de IA por Christian Picon Calderon</subtitle>
    <link href="https://futurewithml.netlify.app/es/tags/sistemas-ia/atom.xml" rel="self" type="application/atom+xml"/>
    <link href="https://futurewithml.netlify.app/es/tags/sistemas-ia/" rel="alternate" type="text/html"/>
    <generator uri="https://www.getzola.org/">Zola</generator><updated>2026-01-06T12:00:00+00:00</updated><id>https://futurewithml.netlify.app/es/tags/sistemas-ia/atom.xml</id><entry xml:lang="es">
        <title>La Paradoja de la Herencia: Scoping de Herramientas en Sistemas Multi-Agente</title>
        <published>2026-01-06T12:00:00+00:00</published>
        <updated>2026-01-06T12:00:00+00:00</updated>
        <author>
            <name>Christian Picon Calderon</name>
        </author>
        <link rel="alternate" href="https://futurewithml.netlify.app/es/posts/claude-code-plugin-journey-part2/" type="text/html"/>
        <id>https://futurewithml.netlify.app/es/posts/claude-code-plugin-journey-part2/</id>
        
            <content type="html">&lt;h1 id=&quot;la-paradoja-de-la-herencia-scoping-de-herramientas-en-sistemas-multi-agente&quot;&gt;La Paradoja de la Herencia: Scoping de Herramientas en Sistemas Multi-Agente&lt;&#x2F;h1&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Esta es la Parte 2 de una serie de tres partes. Ve &lt;a href=&quot;&#x2F;posts&#x2F;claude-code-plugin-journey-part0&#x2F;&quot;&gt;Parte 0&lt;&#x2F;a&gt; para la visión general del plugin e instalación, o &lt;a href=&quot;&#x2F;posts&#x2F;claude-code-plugin-journey-part1&#x2F;&quot;&gt;Parte 1&lt;&#x2F;a&gt; para el viaje de desarrollo.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Resumen&lt;&#x2F;strong&gt;: Este artículo presenta observaciones empíricas del desarrollo de un plugin de Claude Code, revelando patrones fundamentales en el diseño de sistemas IA multi-agente. A través de sesiones de desarrollo documentadas, identificamos tres hallazgos clave: (1) los modelos de herencia implícita de herramientas fallan consistentemente en la práctica, requiriendo contratos de scope explícitos; (2) las restricciones de topología impuestas por la plataforma impulsan la emergencia de patrones arquitectónicos específicos; y (3) las convenciones de nombres se convierten en requisitos funcionales en sistemas IA. Estos hallazgos tienen implicaciones para el diseño de frameworks de agentes más allá de la plataforma específica estudiada.&lt;&#x2F;p&gt;
&lt;span id=&quot;continue-reading&quot;&gt;&lt;&#x2F;span&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;1-introduccion&quot;&gt;1. Introducción&lt;&#x2F;h2&gt;
&lt;p&gt;Las plataformas de desarrollo basadas en agentes representan un paradigma emergente en ingeniería de software, donde instancias de IA colaboran en tareas complejas a través de orquestación estructurada. Estas plataformas ofrecen mecanismos de extensión—plugins, commands, agentes—que permiten a los desarrolladores personalizar el comportamiento para dominios específicos.&lt;&#x2F;p&gt;
&lt;p&gt;Este artículo examina Claude Code como caso de estudio en diseño de frameworks de agentes. Claude Code permite a los desarrolladores construir plugins personalizados con commands (atajos invocados por usuario), agentes (instancias especializadas de Claude), skills (proveedores de contexto), hooks (manejadores de eventos), e integraciones MCP (conexiones a servicios externos).&lt;&#x2F;p&gt;
&lt;p&gt;Durante el desarrollo de &lt;code&gt;agent-team-creator&lt;&#x2F;code&gt;, un plugin para generar agentes IA específicos del proyecto, documentamos observaciones sistemáticas sobre el comportamiento de la plataforma. Estas observaciones revelan patrones relevantes para el diseño de sistemas multi-agente de manera más amplia.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;preguntas-de-investigacion&quot;&gt;Preguntas de Investigación&lt;&#x2F;h3&gt;
&lt;ol&gt;
&lt;li&gt;¿Cómo se comportan los modelos de herencia de herramientas en práctica versus documentación?&lt;&#x2F;li&gt;
&lt;li&gt;¿Qué patrones arquitectónicos emergen de las restricciones de topología de la plataforma?&lt;&#x2F;li&gt;
&lt;li&gt;¿Cómo influyen las convenciones de nombres en el comportamiento de agentes en sistemas IA?&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;2-analisis-de-arquitectura-de-plataforma&quot;&gt;2. Análisis de Arquitectura de Plataforma&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;2-1-taxonomia-de-tipos-de-componentes&quot;&gt;2.1 Taxonomía de Tipos de Componentes&lt;&#x2F;h3&gt;
&lt;p&gt;Los plugins de Claude Code soportan cinco tipos de extensión distintos, cada uno con diferentes mecanismos de activación y scope:&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Componente&lt;&#x2F;th&gt;&lt;th&gt;Activador&lt;&#x2F;th&gt;&lt;th&gt;Invocación&lt;&#x2F;th&gt;&lt;th&gt;Scope&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Skills&lt;&#x2F;td&gt;&lt;td&gt;Auto (Claude decide)&lt;&#x2F;td&gt;&lt;td&gt;Implícita&lt;&#x2F;td&gt;&lt;td&gt;Cross-producto&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Commands&lt;&#x2F;td&gt;&lt;td&gt;Usuario (&lt;code&gt;&#x2F;sintaxis&lt;&#x2F;code&gt;)&lt;&#x2F;td&gt;&lt;td&gt;Explícita&lt;&#x2F;td&gt;&lt;td&gt;Claude Code&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Agents&lt;&#x2F;td&gt;&lt;td&gt;Spawn (herramienta Task)&lt;&#x2F;td&gt;&lt;td&gt;Explícita&#x2F;Implícita&lt;&#x2F;td&gt;&lt;td&gt;Claude Code&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Hooks&lt;&#x2F;td&gt;&lt;td&gt;Eventos&lt;&#x2F;td&gt;&lt;td&gt;Automática&lt;&#x2F;td&gt;&lt;td&gt;Claude Code&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;MCPs&lt;&#x2F;td&gt;&lt;td&gt;Llamadas a herramientas&lt;&#x2F;td&gt;&lt;td&gt;Explícita&lt;&#x2F;td&gt;&lt;td&gt;Claude Code&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;&lt;strong&gt;Observación Clave&lt;&#x2F;strong&gt;: Los Skills son únicos en ser cross-producto (funcionales en web UI, API, y CLI), mientras todos los otros componentes son específicos de Claude Code. Esto sugiere que diferentes modelos de herencia pueden aplicarse a diferentes tipos de componentes.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;2-2-inventario-de-herramientas&quot;&gt;2.2 Inventario de Herramientas&lt;&#x2F;h3&gt;
&lt;p&gt;La plataforma provee 15 herramientas built-in disponibles para agentes:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Read, Write, Edit, MultiEdit (operaciones de archivo)&lt;&#x2F;li&gt;
&lt;li&gt;Glob, Grep, LS (operaciones de búsqueda)&lt;&#x2F;li&gt;
&lt;li&gt;Bash (comandos de sistema)&lt;&#x2F;li&gt;
&lt;li&gt;WebFetch, WebSearch (acceso web)&lt;&#x2F;li&gt;
&lt;li&gt;NotebookRead, NotebookEdit (soporte Jupyter)&lt;&#x2F;li&gt;
&lt;li&gt;TodoRead, TodoWrite (seguimiento de tareas)&lt;&#x2F;li&gt;
&lt;li&gt;exit_plan_mode (control de flujo de trabajo)&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Adicionalmente, los servidores MCP (Model Context Protocol) proveen integraciones de servicios externos—acceso a bases de datos, conexiones API, servicios de terceros.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;2-3-eventos-de-ciclo-de-vida&quot;&gt;2.3 Eventos de Ciclo de Vida&lt;&#x2F;h3&gt;
&lt;p&gt;Nueve eventos de hook definen el ciclo de vida del agente:&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Evento&lt;&#x2F;th&gt;&lt;th&gt;Activador&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;PreToolUse&lt;&#x2F;td&gt;&lt;td&gt;Antes de cualquier ejecución de herramienta&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;PostToolUse&lt;&#x2F;td&gt;&lt;td&gt;Después de ejecución de herramienta&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;UserPromptSubmit&lt;&#x2F;td&gt;&lt;td&gt;Mensaje de usuario recibido&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Stop&lt;&#x2F;td&gt;&lt;td&gt;Completación de agente&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;SubagentStop&lt;&#x2F;td&gt;&lt;td&gt;Completación de subagente&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;SessionStart&lt;&#x2F;td&gt;&lt;td&gt;Inicialización de sesión&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;SessionEnd&lt;&#x2F;td&gt;&lt;td&gt;Terminación de sesión&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;PreCompact&lt;&#x2F;td&gt;&lt;td&gt;Antes de compactación de contexto&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Notification&lt;&#x2F;td&gt;&lt;td&gt;Notificación del sistema&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;Los códigos de salida de hooks siguen convenciones Unix: 0 para éxito, 2 para errores bloqueantes (Claude procesa stderr), otros códigos para errores no bloqueantes.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;3-hallazgos-clave&quot;&gt;3. Hallazgos Clave&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;3-1-la-paradoja-de-la-herencia&quot;&gt;3.1 La Paradoja de la Herencia&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;strong&gt;Afirmación de Documentación&lt;&#x2F;strong&gt;:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;“Herramientas MCP: Los subagentes pueden acceder a herramientas MCP de servidores MCP configurados. Cuando el campo tools se omite, los subagentes heredan todas las herramientas MCP disponibles para el hilo principal.”&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Comportamiento Observado&lt;&#x2F;strong&gt;: Los subagentes definidos en plugins no pueden acceder a herramientas MCP bajo ninguna configuración probada.&lt;&#x2F;p&gt;
&lt;p&gt;Esta discrepancia está documentada en cuatro issues de GitHub (#13605, #15810, #14496, #7296), cada uno describiendo una manifestación diferente de fallo de herencia de scope:&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Issue&lt;&#x2F;th&gt;&lt;th&gt;Modo de Fallo&lt;&#x2F;th&gt;&lt;th&gt;Contexto de Scope&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;#13605&lt;&#x2F;td&gt;&lt;td&gt;Agentes de plugin no pueden acceder a MCP&lt;&#x2F;td&gt;&lt;td&gt;Plugin vs built-in&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;#15810&lt;&#x2F;td&gt;&lt;td&gt;Subagentes no heredan MCP&lt;&#x2F;td&gt;&lt;td&gt;Jerarquía padre-hijo&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;#14496&lt;&#x2F;td&gt;&lt;td&gt;Prompts complejos rompen acceso MCP&lt;&#x2F;td&gt;&lt;td&gt;Complejidad de prompt&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;#7296&lt;&#x2F;td&gt;&lt;td&gt;Agentes lanzados con Task carecen de MCP&lt;&#x2F;td&gt;&lt;td&gt;Herencia de scope de usuario&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;&lt;strong&gt;Análisis&lt;&#x2F;strong&gt;: El patrón sugiere un problema arquitectónico fundamental en lugar de bugs aislados. La herencia de herramientas opera diferentemente a través de límites de scope:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Agentes built-in&lt;&#x2F;strong&gt; heredan herramientas MCP correctamente&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Agentes definidos en plugins&lt;&#x2F;strong&gt; no heredan herramientas MCP&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Agentes lanzados con Task&lt;&#x2F;strong&gt; reciben un scope fresco sin configuraciones de nivel de usuario&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;&lt;strong&gt;Implicación para Diseño de Frameworks&lt;&#x2F;strong&gt;: Los modelos de herencia implícita de herramientas requieren contratos explícitos en límites de scope. Asumir que las herramientas se propagan a través de jerarquías de agentes crea dependencias frágiles. Las declaraciones explícitas de herramientas en cada nivel de scope, aunque verbose, producen comportamiento predecible.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;3-2-restricciones-de-topologia-y-patrones-emergentes&quot;&gt;3.2 Restricciones de Topología y Patrones Emergentes&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;strong&gt;Descubrimiento&lt;&#x2F;strong&gt;: La plataforma impone un límite de anidamiento de un solo nivel para spawn de agentes.&lt;&#x2F;p&gt;





&lt;div class=&quot;blocked-flow-container&quot; id=&quot;blocked-&quot;&gt;
    &lt;canvas class=&quot;blocked-flow-canvas&quot; aria-label=&quot;Flow diagram with blocked step&quot;&gt;&lt;&#x2F;canvas&gt;
&lt;&#x2F;div&gt;

&lt;script&gt;
(function() {
    const containerId = &#x27;blocked-&#x27;;
    const nodeNames = [&#x27;Agente&#x27;, &#x27;Subagente&#x27;];
    const blockedName = &#x27;Subsubagente&#x27;;

    function initBlockedFlow() {
        const container = document.getElementById(containerId);
        if (!container) return;

        const canvas = container.querySelector(&#x27;.blocked-flow-canvas&#x27;);
        const ctx = canvas.getContext(&#x27;2d&#x27;);

        let particles = [];
        let time = 0;
        let pulsePhase = 0;

        const colors = {
            nodes: [&#x27;#ff0080&#x27;, &#x27;#00d4ff&#x27;, &#x27;#00ff88&#x27;, &#x27;#8b5cf6&#x27;],
            blocked: &#x27;#ff4444&#x27;,
            blockedBg: &#x27;#442222&#x27;,
            particle: &#x27;#00ff88&#x27;,
            connection: &#x27;rgba(0, 212, 255, 0.4)&#x27;,
            blockedConnection: &#x27;rgba(255, 68, 68, 0.5)&#x27;,
            text: &#x27;#e0e0e4&#x27;
        };

        function resize() {
            const rect = container.getBoundingClientRect();
            canvas.width = rect.width;
            canvas.height = 180;
        }
        resize();
        window.addEventListener(&#x27;resize&#x27;, resize);

        function getNodePositions() {
            const allNodes = [...nodeNames, blockedName];
            const margin = 60;
            const spacing = (canvas.width - margin * 2) &#x2F; (allNodes.length - 1);
            const y = canvas.height &#x2F; 2;

            return allNodes.map((name, i) =&gt; ({
                label: name,
                x: margin + i * spacing,
                y: y,
                radius: 26,
                isBlocked: i === allNodes.length - 1,
                color: i &lt; nodeNames.length ? colors.nodes[i % colors.nodes.length] : colors.blockedBg
            }));
        }

        function draw() {
            ctx.clearRect(0, 0, canvas.width, canvas.height);
            const nodes = getNodePositions();

            pulsePhase += 0.05;

            &#x2F;&#x2F; Draw connections
            for (let i = 0; i &lt; nodes.length - 1; i++) {
                const from = nodes[i];
                const to = nodes[i + 1];
                const isBlockedConnection = to.isBlocked;

                ctx.beginPath();
                ctx.moveTo(from.x + from.radius, from.y);
                ctx.lineTo(to.x - to.radius, to.y);

                if (isBlockedConnection) {
                    ctx.strokeStyle = colors.blockedConnection;
                    ctx.setLineDash([8, 4]);
                } else {
                    ctx.strokeStyle = colors.connection;
                    ctx.setLineDash([]);
                }
                ctx.lineWidth = 3;
                ctx.stroke();
                ctx.setLineDash([]);

                &#x2F;&#x2F; Arrow
                if (!isBlockedConnection) {
                    const midX = (from.x + to.x) &#x2F; 2;
                    ctx.fillStyle = colors.connection;
                    ctx.beginPath();
                    ctx.moveTo(midX - 5, from.y - 6);
                    ctx.lineTo(midX + 8, from.y);
                    ctx.lineTo(midX - 5, from.y + 6);
                    ctx.closePath();
                    ctx.fill();
                }
            }

            &#x2F;&#x2F; Draw &quot;BLOCKED&quot; label on the blocked connection
            const lastNormal = nodes[nodes.length - 2];
            const blocked = nodes[nodes.length - 1];
            const midX = (lastNormal.x + blocked.x) &#x2F; 2;

            ctx.save();
            ctx.fillStyle = colors.blocked;
            ctx.font = &#x27;bold 11px JetBrains Mono&#x27;;
            ctx.textAlign = &#x27;center&#x27;;

            &#x2F;&#x2F; Pulsing X
            const pulse = Math.sin(pulsePhase) * 0.3 + 1;
            ctx.font = `bold ${14 * pulse}px JetBrains Mono`;
            ctx.fillText(&#x27;❌&#x27;, midX, nodes[0].y - 20);

            ctx.font = &#x27;bold 10px JetBrains Mono&#x27;;
            ctx.fillText(&#x27;BLOCKED&#x27;, midX, nodes[0].y + 32);
            ctx.restore();

            &#x2F;&#x2F; Draw nodes
            nodes.forEach((node, i) =&gt; {
                if (node.isBlocked) {
                    &#x2F;&#x2F; Blocked node - pulsing red border
                    ctx.shadowColor = colors.blocked;
                    ctx.shadowBlur = 10 + Math.sin(pulsePhase) * 5;

                    ctx.fillStyle = node.color;
                    ctx.beginPath();
                    ctx.arc(node.x, node.y, node.radius, 0, Math.PI * 2);
                    ctx.fill();

                    ctx.strokeStyle = colors.blocked;
                    ctx.lineWidth = 3;
                    ctx.setLineDash([4, 4]);
                    ctx.stroke();
                    ctx.setLineDash([]);

                    ctx.shadowBlur = 0;

                    &#x2F;&#x2F; Strikethrough effect
                    ctx.strokeStyle = colors.blocked;
                    ctx.lineWidth = 2;
                    ctx.beginPath();
                    ctx.moveTo(node.x - node.radius + 5, node.y - node.radius + 5);
                    ctx.lineTo(node.x + node.radius - 5, node.y + node.radius - 5);
                    ctx.stroke();
                } else {
                    &#x2F;&#x2F; Normal node
                    ctx.shadowColor = node.color;
                    ctx.shadowBlur = 15;

                    ctx.fillStyle = node.color;
                    ctx.beginPath();
                    ctx.arc(node.x, node.y, node.radius, 0, Math.PI * 2);
                    ctx.fill();

                    ctx.shadowBlur = 0;

                    ctx.strokeStyle = &#x27;#00ff88&#x27;;
                    ctx.lineWidth = 2;
                    ctx.stroke();
                }

                &#x2F;&#x2F; Label
                ctx.fillStyle = node.isBlocked ? &#x27;#666&#x27; : &#x27;#0a0a0f&#x27;;
                ctx.font = &#x27;bold 10px JetBrains Mono&#x27;;
                ctx.textAlign = &#x27;center&#x27;;
                ctx.textBaseline = &#x27;middle&#x27;;

                &#x2F;&#x2F; Wrap text if needed
                const words = node.label.split(&#x2F;(?=[A-Z])&#x2F;);
                if (words.length &gt; 1 &amp;&amp; node.label.length &gt; 8) {
                    ctx.fillText(words[0], node.x, node.y - 6);
                    ctx.fillText(words.slice(1).join(&#x27;&#x27;), node.x, node.y + 6);
                } else {
                    ctx.fillText(node.label, node.x, node.y);
                }
            });

            &#x2F;&#x2F; Particles (only on valid connections)
            particles.forEach(p =&gt; {
                p.progress += p.speed;
                if (p.progress &gt;= 1) {
                    p.segmentIndex++;
                    p.progress = 0;
                    if (p.segmentIndex &gt;= nodes.length - 2) {
                        p.segmentIndex = 0;
                    }
                }

                const from = nodes[p.segmentIndex];
                const to = nodes[p.segmentIndex + 1];

                if (from &amp;&amp; to &amp;&amp; !to.isBlocked) {
                    const ease = p.progress * p.progress * (3 - 2 * p.progress);
                    const x = from.x + (to.x - from.x) * ease;
                    const y = from.y + (to.y - from.y) * ease;

                    ctx.fillStyle = colors.particle;
                    ctx.shadowColor = colors.particle;
                    ctx.shadowBlur = 10;
                    ctx.beginPath();
                    ctx.arc(x, y, 4, 0, Math.PI * 2);
                    ctx.fill();
                    ctx.shadowBlur = 0;
                }
            });

            time++;
            requestAnimationFrame(draw);
        }

        &#x2F;&#x2F; Initialize particles
        for (let i = 0; i &lt; 2; i++) {
            particles.push({
                segmentIndex: i,
                progress: 0,
                speed: 0.015
            });
        }

        if (!window.matchMedia(&#x27;(prefers-reduced-motion: reduce)&#x27;).matches) {
            draw();
        } else {
            &#x2F;&#x2F; Static version
            const nodes = getNodePositions();
            nodes.forEach(node =&gt; {
                ctx.fillStyle = node.color;
                ctx.beginPath();
                ctx.arc(node.x, node.y, node.radius, 0, Math.PI * 2);
                ctx.fill();

                ctx.fillStyle = node.isBlocked ? &#x27;#666&#x27; : &#x27;#0a0a0f&#x27;;
                ctx.font = &#x27;bold 10px JetBrains Mono&#x27;;
                ctx.textAlign = &#x27;center&#x27;;
                ctx.fillText(node.label, node.x, node.y);
            });
        }
    }

    if (document.readyState === &#x27;loading&#x27;) {
        document.addEventListener(&#x27;DOMContentLoaded&#x27;, initBlockedFlow);
    } else {
        initBlockedFlow();
    }
})();
&lt;&#x2F;script&gt;

&lt;style&gt;
.blocked-flow-container {
    background: var(--bg-0);
    border: 1px solid var(--divider-color);
    border-radius: 12px;
    padding: 1rem;
    margin: 1.5rem 0;
    position: relative;
}

.blocked-flow-canvas {
    width: 100%;
    height: 180px;
    display: block;
}

@media (max-width: 768px) {
    .blocked-flow-container {
        overflow-x: auto;
    }

    .blocked-flow-canvas {
        min-width: 500px;
    }
}
&lt;&#x2F;style&gt;
&lt;p&gt;Cuando un agente intenta spawnear un subagente anidado, la plataforma retorna:&lt;&#x2F;p&gt;
&lt;pre class=&quot;z-code&quot;&gt;&lt;code&gt;&lt;span class=&quot;z-text z-plain&quot;&gt;Error: Subagents cannot spawn subagents
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;strong&gt;Justificación de Diseño (Inferida)&lt;&#x2F;strong&gt;:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Previene recursión infinita en sistemas autónomos&lt;&#x2F;li&gt;
&lt;li&gt;Mantiene consumo de recursos acotado&lt;&#x2F;li&gt;
&lt;li&gt;Asegura topología de ejecución predecible&lt;&#x2F;li&gt;
&lt;li&gt;Simplifica debugging y monitoreo&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;Arquitectura Emergente: Hub-and-Spoke&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;







&lt;div class=&quot;hub-spoke-container&quot; id=&quot;hub-spok&quot;&gt;
    &lt;canvas class=&quot;hub-spoke-canvas&quot; aria-label=&quot;Hub-and-spoke agent topology visualization&quot;&gt;&lt;&#x2F;canvas&gt;
    &lt;div class=&quot;hub-spoke-info&quot;&gt;
        &lt;span class=&quot;info-label&quot;&gt;Topology&lt;&#x2F;span&gt;
        &lt;span class=&quot;info-value&quot;&gt;Hub-and-Spoke&lt;&#x2F;span&gt;
    &lt;&#x2F;div&gt;
&lt;&#x2F;div&gt;

&lt;script&gt;
(function() {
    const containerId = &#x27;hub-spok&#x27;;

    &#x2F;&#x2F; Decode HTML entities (Tera encodes &#x2F; as &amp;#x2F;)
    function decodeHTML(str) {
        const textarea = document.createElement(&#x27;textarea&#x27;);
        textarea.innerHTML = str;
        return textarea.value;
    }

    const hubName = decodeHTML(&#x27;Agente Principal&#x27;);
    const spokeNames = [decodeHTML(&#x27;Agente 1&#x27;), decodeHTML(&#x27;Agente 2&#x27;), decodeHTML(&#x27;Agente 3&#x27;)];
    const showUser = true;
    const showCommand = true;

    function initHubSpoke() {
        const container = document.getElementById(containerId);
        if (!container) return;

        const canvas = container.querySelector(&#x27;.hub-spoke-canvas&#x27;);
        const ctx = canvas.getContext(&#x27;2d&#x27;);

        let particles = [];
        let time = 0;

        &#x2F;&#x2F; Colors
        const colors = {
            user: &#x27;#ff0080&#x27;,
            command: &#x27;#ff8800&#x27;,
            hub: &#x27;#00d4ff&#x27;,
            spoke: &#x27;#8b5cf6&#x27;,
            particle: &#x27;#00ff88&#x27;,
            connection: &#x27;rgba(0, 212, 255, 0.3)&#x27;
        };

        function resize() {
            const rect = container.getBoundingClientRect();
            canvas.width = rect.width;
            canvas.height = 300;
        }
        resize();
        window.addEventListener(&#x27;resize&#x27;, resize);

        &#x2F;&#x2F; Get node positions
        function getNodes() {
            const centerX = canvas.width &#x2F; 2;
            const centerY = canvas.height &#x2F; 2 + 20;
            const spokeRadius = Math.min(canvas.width * 0.35, 120);

            const nodes = [];

            &#x2F;&#x2F; User node
            if (showUser) {
                nodes.push({
                    type: &#x27;user&#x27;,
                    label: &#x27;User&#x27;,
                    x: centerX,
                    y: 40,
                    radius: 20,
                    color: colors.user
                });
            }

            &#x2F;&#x2F; Command node
            if (showCommand) {
                nodes.push({
                    type: &#x27;command&#x27;,
                    label: &#x27;Command&#x27;,
                    x: centerX,
                    y: showUser ? 100 : 50,
                    radius: 22,
                    color: colors.command
                });
            }

            &#x2F;&#x2F; Hub node
            const hubY = showUser &amp;&amp; showCommand ? centerY : centerY - 30;
            nodes.push({
                type: &#x27;hub&#x27;,
                label: hubName,
                x: centerX,
                y: hubY,
                radius: 32,
                color: colors.hub
            });

            &#x2F;&#x2F; Spoke nodes
            const spokeCount = spokeNames.length;
            const angleStep = Math.PI &#x2F; (spokeCount + 1);
            const startAngle = Math.PI;

            spokeNames.forEach((name, i) =&gt; {
                const angle = startAngle - angleStep * (i + 1);
                nodes.push({
                    type: &#x27;spoke&#x27;,
                    label: name,
                    x: centerX + Math.cos(angle) * spokeRadius,
                    y: hubY + Math.sin(angle) * spokeRadius * 0.7,
                    radius: 24,
                    color: colors.spoke
                });
            });

            return nodes;
        }

        function createParticle(from, to) {
            return {
                fromX: from.x,
                fromY: from.y,
                toX: to.x,
                toY: to.y,
                progress: Math.random(),
                speed: 0.005 + Math.random() * 0.003,
                x: from.x,
                y: from.y
            };
        }

        function draw() {
            ctx.clearRect(0, 0, canvas.width, canvas.height);
            const nodes = getNodes();

            const hub = nodes.find(n =&gt; n.type === &#x27;hub&#x27;);
            const spokes = nodes.filter(n =&gt; n.type === &#x27;spoke&#x27;);
            const command = nodes.find(n =&gt; n.type === &#x27;command&#x27;);
            const user = nodes.find(n =&gt; n.type === &#x27;user&#x27;);

            &#x2F;&#x2F; Draw connections
            ctx.lineWidth = 2;
            ctx.strokeStyle = colors.connection;

            &#x2F;&#x2F; User -&gt; Command
            if (user &amp;&amp; command) {
                ctx.beginPath();
                ctx.moveTo(user.x, user.y + user.radius);
                ctx.lineTo(command.x, command.y - command.radius);
                ctx.stroke();
            }

            &#x2F;&#x2F; Command -&gt; Hub
            if (command &amp;&amp; hub) {
                ctx.beginPath();
                ctx.moveTo(command.x, command.y + command.radius);
                ctx.lineTo(hub.x, hub.y - hub.radius);
                ctx.stroke();
            } else if (user &amp;&amp; hub) {
                ctx.beginPath();
                ctx.moveTo(user.x, user.y + user.radius);
                ctx.lineTo(hub.x, hub.y - hub.radius);
                ctx.stroke();
            }

            &#x2F;&#x2F; Hub -&gt; Spokes
            spokes.forEach(spoke =&gt; {
                ctx.beginPath();
                ctx.moveTo(hub.x, hub.y);
                ctx.lineTo(spoke.x, spoke.y);
                ctx.stroke();
            });

            &#x2F;&#x2F; Draw nodes
            nodes.forEach(node =&gt; {
                &#x2F;&#x2F; Glow
                const gradient = ctx.createRadialGradient(
                    node.x, node.y, 0,
                    node.x, node.y, node.radius * 2
                );
                gradient.addColorStop(0, node.color.replace(&#x27;)&#x27;, &#x27;, 0.3)&#x27;).replace(&#x27;#&#x27;, &#x27;rgba(&#x27;).replace(&#x2F;^rgba\(([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})&#x2F;, (m, r, g, b) =&gt; `rgba(${parseInt(r, 16)}, ${parseInt(g, 16)}, ${parseInt(b, 16)}`));
                gradient.addColorStop(1, &#x27;transparent&#x27;);

                &#x2F;&#x2F; Simpler glow approach
                ctx.shadowColor = node.color;
                ctx.shadowBlur = 15;

                &#x2F;&#x2F; Node circle
                ctx.fillStyle = node.color;
                ctx.beginPath();
                ctx.arc(node.x, node.y, node.radius, 0, Math.PI * 2);
                ctx.fill();

                ctx.shadowBlur = 0;

                &#x2F;&#x2F; Border
                ctx.strokeStyle = node.color;
                ctx.lineWidth = 2;
                ctx.stroke();

                &#x2F;&#x2F; Label
                ctx.fillStyle = &#x27;#e0e0e4&#x27;;
                ctx.font = node.type === &#x27;hub&#x27; ? &#x27;bold 11px JetBrains Mono&#x27; : &#x27;10px JetBrains Mono&#x27;;
                ctx.textAlign = &#x27;center&#x27;;
                ctx.textBaseline = &#x27;middle&#x27;;

                &#x2F;&#x2F; Wrap long labels
                const maxWidth = node.radius * 1.8;
                const words = node.label.split(&#x27; &#x27;);
                if (words.length &gt; 1 &amp;&amp; ctx.measureText(node.label).width &gt; maxWidth) {
                    ctx.fillText(words[0], node.x, node.y - 6);
                    ctx.fillText(words.slice(1).join(&#x27; &#x27;), node.x, node.y + 6);
                } else {
                    ctx.fillText(node.label, node.x, node.y);
                }

                &#x2F;&#x2F; Type indicator below node
                if (node.type === &#x27;hub&#x27;) {
                    ctx.fillStyle = &#x27;#8888a0&#x27;;
                    ctx.font = &#x27;9px JetBrains Mono&#x27;;
                    ctx.fillText(&#x27;(Hub)&#x27;, node.x, node.y + node.radius + 14);
                }
            });

            &#x2F;&#x2F; Update and draw particles
            particles.forEach(p =&gt; {
                p.progress += p.speed;
                if (p.progress &gt;= 1) {
                    p.progress = 0;
                    &#x2F;&#x2F; Reverse direction
                    [p.fromX, p.toX] = [p.toX, p.fromX];
                    [p.fromY, p.toY] = [p.toY, p.fromY];
                }

                const ease = p.progress * p.progress * (3 - 2 * p.progress);
                p.x = p.fromX + (p.toX - p.fromX) * ease;
                p.y = p.fromY + (p.toY - p.fromY) * ease;

                ctx.fillStyle = colors.particle;
                ctx.beginPath();
                ctx.arc(p.x, p.y, 3, 0, Math.PI * 2);
                ctx.fill();

                &#x2F;&#x2F; Glow
                ctx.shadowColor = colors.particle;
                ctx.shadowBlur = 10;
                ctx.beginPath();
                ctx.arc(p.x, p.y, 3, 0, Math.PI * 2);
                ctx.fill();
                ctx.shadowBlur = 0;
            });

            time++;
            requestAnimationFrame(draw);
        }

        &#x2F;&#x2F; Initialize particles
        const nodes = getNodes();
        const hub = nodes.find(n =&gt; n.type === &#x27;hub&#x27;);
        const spokes = nodes.filter(n =&gt; n.type === &#x27;spoke&#x27;);

        spokes.forEach(spoke =&gt; {
            particles.push(createParticle(hub, spoke));
        });

        &#x2F;&#x2F; Respect reduced motion
        if (!window.matchMedia(&#x27;(prefers-reduced-motion: reduce)&#x27;).matches) {
            draw();
        } else {
            &#x2F;&#x2F; Static version
            const nodes = getNodes();
            const hub = nodes.find(n =&gt; n.type === &#x27;hub&#x27;);
            const spokes = nodes.filter(n =&gt; n.type === &#x27;spoke&#x27;);
            const command = nodes.find(n =&gt; n.type === &#x27;command&#x27;);
            const user = nodes.find(n =&gt; n.type === &#x27;user&#x27;);

            ctx.lineWidth = 2;
            ctx.strokeStyle = colors.connection;

            if (user &amp;&amp; command) {
                ctx.beginPath();
                ctx.moveTo(user.x, user.y + user.radius);
                ctx.lineTo(command.x, command.y - command.radius);
                ctx.stroke();
            }

            if (command &amp;&amp; hub) {
                ctx.beginPath();
                ctx.moveTo(command.x, command.y + command.radius);
                ctx.lineTo(hub.x, hub.y - hub.radius);
                ctx.stroke();
            }

            spokes.forEach(spoke =&gt; {
                ctx.beginPath();
                ctx.moveTo(hub.x, hub.y);
                ctx.lineTo(spoke.x, spoke.y);
                ctx.stroke();
            });

            nodes.forEach(node =&gt; {
                ctx.fillStyle = node.color;
                ctx.beginPath();
                ctx.arc(node.x, node.y, node.radius, 0, Math.PI * 2);
                ctx.fill();

                ctx.fillStyle = &#x27;#e0e0e4&#x27;;
                ctx.font = node.type === &#x27;hub&#x27; ? &#x27;bold 11px JetBrains Mono&#x27; : &#x27;10px JetBrains Mono&#x27;;
                ctx.textAlign = &#x27;center&#x27;;
                ctx.textBaseline = &#x27;middle&#x27;;
                ctx.fillText(node.label, node.x, node.y);
            });
        }
    }

    if (document.readyState === &#x27;loading&#x27;) {
        document.addEventListener(&#x27;DOMContentLoaded&#x27;, initHubSpoke);
    } else {
        initHubSpoke();
    }
})();
&lt;&#x2F;script&gt;

&lt;style&gt;
.hub-spoke-container {
    background: var(--bg-0);
    border: 1px solid var(--divider-color);
    border-radius: 12px;
    padding: 1rem;
    margin: 1.5rem 0;
    position: relative;
}

.hub-spoke-canvas {
    width: 100%;
    height: 300px;
    display: block;
}

.hub-spoke-info {
    position: absolute;
    bottom: 1rem;
    right: 1rem;
    background: rgba(0, 0, 0, 0.6);
    padding: 0.5rem 0.75rem;
    border-radius: 6px;
    font-family: &#x27;JetBrains Mono&#x27;, monospace;
    font-size: 0.75rem;
}

.hub-spoke-info .info-label {
    color: var(--meta-color);
    display: block;
    margin-bottom: 0.25rem;
}

.hub-spoke-info .info-value {
    color: #00d4ff;
}

@media (max-width: 768px) {
    .hub-spoke-canvas {
        height: 250px;
    }
}
&lt;&#x2F;style&gt;
&lt;p&gt;Esta restricción elimina jerarquías de agentes en estructura de árbol en favor de topologías planas coordinadas por hub. El agente “hub” coordina ejecución paralela de agentes “spoke”. Ningún spoke puede spawnear agentes adicionales.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Trade-off Observado&lt;&#x2F;strong&gt;:&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Capacidad Perdida&lt;&#x2F;th&gt;&lt;th&gt;Propiedad Ganada&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Descomposición recursiva&lt;&#x2F;td&gt;&lt;td&gt;Profundidad de ejecución acotada&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Jerarquía dinámica&lt;&#x2F;td&gt;&lt;td&gt;Topología predecible&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Anidamiento arbitrario&lt;&#x2F;td&gt;&lt;td&gt;Gestión de recursos simplificada&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;&lt;strong&gt;Implicación para Diseño de Frameworks&lt;&#x2F;strong&gt;: Las restricciones de topología son decisiones de diseño, no meras limitaciones. El patrón hub-and-spoke que emerge del anidamiento de un solo nivel produce sistemas más fáciles de razonar, depurar, y monitorear que estructuras recursivas arbitrarias.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;3-3-naming-como-comportamiento&quot;&gt;3.3 Naming como Comportamiento&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;strong&gt;Descubrimiento&lt;&#x2F;strong&gt;: Claude Code infiere comportamiento de agente a partir de nombres de agentes.&lt;&#x2F;p&gt;
&lt;p&gt;Un agente llamado &lt;code&gt;code-reviewer&lt;&#x2F;code&gt; activa comportamientos de revisión built-in que pueden sobrescribir instrucciones personalizadas del system prompt. La plataforma aplica heurísticas basadas en convenciones de nombres.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Patrón Observado&lt;&#x2F;strong&gt;:&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Nombre de Agente&lt;&#x2F;th&gt;&lt;th&gt;Comportamiento Inferido&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;code-reviewer&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;Patrones genéricos de revisión de código&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;test-writer&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;Patrones de generación de tests&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;implementation-planner&lt;&#x2F;code&gt;&lt;&#x2F;td&gt;&lt;td&gt;Menos inferencia, más control personalizado&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;&lt;strong&gt;Análisis&lt;&#x2F;strong&gt;: Esto representa un paradigma de “convención sobre configuración” aplicado a sistemas de agentes IA. La plataforma asume que nombres semánticamente significativos llevan intención de comportamiento.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Implicaciones&lt;&#x2F;strong&gt;:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Las convenciones de nombres se convierten en requisitos funcionales&lt;&#x2F;li&gt;
&lt;li&gt;Nombres distintivos, no genéricos preservan comportamiento personalizado&lt;&#x2F;li&gt;
&lt;li&gt;El diseño de sistemas de agentes debe considerar inferencia basada en nombres&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;4-patrones-emergentes&quot;&gt;4. Patrones Emergentes&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;4-1-separacion-i-o-arquitectura-hibrida&quot;&gt;4.1 Separación I&#x2F;O (Arquitectura Híbrida)&lt;&#x2F;h3&gt;
&lt;p&gt;En respuesta a las limitaciones de acceso MCP, emergió un patrón arquitectónico claro: separar operaciones de I&#x2F;O de operaciones de inteligencia.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Modelo de Capas&lt;&#x2F;strong&gt;:&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Capa&lt;&#x2F;th&gt;&lt;th&gt;Responsabilidades&lt;&#x2F;th&gt;&lt;th&gt;Componentes&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;I&#x2F;O&lt;&#x2F;td&gt;&lt;td&gt;Acceso MCP, operaciones de archivo, interacción con usuario, caché&lt;&#x2F;td&gt;&lt;td&gt;Commands&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Inteligencia&lt;&#x2F;td&gt;&lt;td&gt;Razonamiento, análisis, formateo, decisiones&lt;&#x2F;td&gt;&lt;td&gt;Agents&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;&lt;strong&gt;Ejemplo de Pipeline de 6 Fases&lt;&#x2F;strong&gt;:&lt;&#x2F;p&gt;



&lt;div class=&quot;pipeline-container&quot; id=&quot;pipeline&quot;&gt;
    &lt;canvas class=&quot;pipeline-canvas&quot; aria-label=&quot;Pipeline flow visualization&quot;&gt;&lt;&#x2F;canvas&gt;
    &lt;div class=&quot;pipeline-legend&quot;&gt;
        &lt;span class=&quot;legend-item&quot;&gt;&lt;span class=&quot;legend-dot command&quot;&gt;&lt;&#x2F;span&gt; Command (I&#x2F;O)&lt;&#x2F;span&gt;
        &lt;span class=&quot;legend-item&quot;&gt;&lt;span class=&quot;legend-dot agent&quot;&gt;&lt;&#x2F;span&gt; Agent (Intelligence)&lt;&#x2F;span&gt;
    &lt;&#x2F;div&gt;
&lt;&#x2F;div&gt;

&lt;script&gt;
(function() {
    const containerId = &#x27;pipeline&#x27;;
    const phasesData = [
    {&quot;phase&quot;: 0, &quot;owner&quot;: &quot;Command&quot;, &quot;label&quot;: &quot;Disponibilidad&quot;, &quot;io&quot;: &quot;external&quot;},
    {&quot;phase&quot;: 1, &quot;owner&quot;: &quot;Command&quot;, &quot;label&quot;: &quot;Resolver Datos&quot;, &quot;io&quot;: &quot;external&quot;},
    {&quot;phase&quot;: 2, &quot;owner&quot;: &quot;Command&quot;, &quot;label&quot;: &quot;Cargar Contenido&quot;, &quot;io&quot;: &quot;local&quot;},
    {&quot;phase&quot;: 3, &quot;owner&quot;: &quot;Command&quot;, &quot;label&quot;: &quot;Validación&quot;, &quot;io&quot;: &quot;external&quot;},
    {&quot;phase&quot;: 4, &quot;owner&quot;: &quot;Agent&quot;, &quot;label&quot;: &quot;Razonamiento&quot;, &quot;io&quot;: &quot;none&quot;},
    {&quot;phase&quot;: 5, &quot;owner&quot;: &quot;Agent&quot;, &quot;label&quot;: &quot;Formateo&quot;, &quot;io&quot;: &quot;none&quot;},
    {&quot;phase&quot;: 6, &quot;owner&quot;: &quot;Command&quot;, &quot;label&quot;: &quot;Entrega&quot;, &quot;io&quot;: &quot;external&quot;}
];

    function initPipeline() {
        const container = document.getElementById(containerId);
        if (!container) return;

        const canvas = container.querySelector(&#x27;.pipeline-canvas&#x27;);
        const ctx = canvas.getContext(&#x27;2d&#x27;);

        &#x2F;&#x2F; Responsive sizing
        function resize() {
            const rect = container.getBoundingClientRect();
            canvas.width = rect.width;
            canvas.height = 180;
        }
        resize();
        window.addEventListener(&#x27;resize&#x27;, resize);

        &#x2F;&#x2F; Colors
        const colors = {
            command: &#x27;#00d4ff&#x27;,
            agent: &#x27;#00ff88&#x27;,
            external: &#x27;#8b5cf6&#x27;,
            local: &#x27;#ff8800&#x27;,
            none: &#x27;#444466&#x27;,
            particle: &#x27;#00ff88&#x27;,
            bg: &#x27;rgba(10, 10, 15, 0.8)&#x27;
        };

        &#x2F;&#x2F; Animation state
        let particles = [];
        let time = 0;

        &#x2F;&#x2F; Get phase positions
        function getPhasePositions() {
            const phases = phasesData;
            const margin = 60;
            const spacing = (canvas.width - margin * 2) &#x2F; (phases.length - 1);
            return phases.map((p, i) =&gt; ({
                ...p,
                x: margin + i * spacing,
                y: canvas.height &#x2F; 2,
                radius: 24
            }));
        }

        &#x2F;&#x2F; Create particle
        function createParticle(positions) {
            return {
                phaseIndex: 0,
                progress: 0,
                speed: 0.01 + Math.random() * 0.01,
                x: positions[0].x,
                y: positions[0].y
            };
        }

        &#x2F;&#x2F; Draw
        function draw() {
            ctx.clearRect(0, 0, canvas.width, canvas.height);
            const positions = getPhasePositions();

            &#x2F;&#x2F; Draw connections
            ctx.lineWidth = 2;
            ctx.strokeStyle = &#x27;rgba(0, 212, 255, 0.3)&#x27;;
            ctx.beginPath();
            positions.forEach((pos, i) =&gt; {
                if (i === 0) ctx.moveTo(pos.x, pos.y);
                else ctx.lineTo(pos.x, pos.y);
            });
            ctx.stroke();

            &#x2F;&#x2F; Draw arrows
            for (let i = 0; i &lt; positions.length - 1; i++) {
                const from = positions[i];
                const to = positions[i + 1];
                const midX = (from.x + to.x) &#x2F; 2;
                const midY = (from.y + to.y) &#x2F; 2;

                ctx.fillStyle = &#x27;rgba(0, 212, 255, 0.5)&#x27;;
                ctx.beginPath();
                ctx.moveTo(midX - 6, midY - 4);
                ctx.lineTo(midX + 6, midY);
                ctx.lineTo(midX - 6, midY + 4);
                ctx.closePath();
                ctx.fill();
            }

            &#x2F;&#x2F; Draw phase nodes
            positions.forEach((pos, i) =&gt; {
                const isCommand = pos.owner === &#x27;Command&#x27;;
                const color = isCommand ? colors.command : colors.agent;

                &#x2F;&#x2F; Glow
                const gradient = ctx.createRadialGradient(pos.x, pos.y, 0, pos.x, pos.y, pos.radius * 2);
                gradient.addColorStop(0, color.replace(&#x27;)&#x27;, &#x27;, 0.3)&#x27;).replace(&#x27;rgb&#x27;, &#x27;rgba&#x27;));
                gradient.addColorStop(1, &#x27;transparent&#x27;);
                ctx.fillStyle = gradient;
                ctx.beginPath();
                ctx.arc(pos.x, pos.y, pos.radius * 2, 0, Math.PI * 2);
                ctx.fill();

                &#x2F;&#x2F; Node
                ctx.fillStyle = color;
                ctx.beginPath();
                ctx.arc(pos.x, pos.y, pos.radius, 0, Math.PI * 2);
                ctx.fill();

                &#x2F;&#x2F; Phase number
                ctx.fillStyle = &#x27;#0a0a0f&#x27;;
                ctx.font = &#x27;bold 14px JetBrains Mono, monospace&#x27;;
                ctx.textAlign = &#x27;center&#x27;;
                ctx.textBaseline = &#x27;middle&#x27;;
                ctx.fillText(pos.phase, pos.x, pos.y);

                &#x2F;&#x2F; Label
                ctx.fillStyle = &#x27;#e0e0e4&#x27;;
                ctx.font = &#x27;11px JetBrains Mono, monospace&#x27;;
                ctx.fillText(pos.label, pos.x, pos.y + pos.radius + 16);

                &#x2F;&#x2F; I&#x2F;O indicator
                if (pos.io !== &#x27;none&#x27;) {
                    ctx.fillStyle = pos.io === &#x27;external&#x27; ? colors.external : colors.local;
                    ctx.font = &#x27;9px JetBrains Mono, monospace&#x27;;
                    ctx.fillText(pos.io.toUpperCase(), pos.x, pos.y - pos.radius - 10);
                }
            });

            &#x2F;&#x2F; Update and draw particles
            particles.forEach(p =&gt; {
                if (p.phaseIndex &gt;= positions.length - 1) {
                    p.phaseIndex = 0;
                    p.progress = 0;
                }

                const from = positions[p.phaseIndex];
                const to = positions[p.phaseIndex + 1];

                p.progress += p.speed;
                if (p.progress &gt;= 1) {
                    p.phaseIndex++;
                    p.progress = 0;
                }

                if (to) {
                    p.x = from.x + (to.x - from.x) * p.progress;
                    p.y = from.y + (to.y - from.y) * p.progress;
                }

                &#x2F;&#x2F; Draw particle
                ctx.fillStyle = colors.particle;
                ctx.beginPath();
                ctx.arc(p.x, p.y, 4, 0, Math.PI * 2);
                ctx.fill();

                &#x2F;&#x2F; Particle glow
                const pGrad = ctx.createRadialGradient(p.x, p.y, 0, p.x, p.y, 12);
                pGrad.addColorStop(0, &#x27;rgba(0, 255, 136, 0.4)&#x27;);
                pGrad.addColorStop(1, &#x27;transparent&#x27;);
                ctx.fillStyle = pGrad;
                ctx.beginPath();
                ctx.arc(p.x, p.y, 12, 0, Math.PI * 2);
                ctx.fill();
            });

            time++;
            requestAnimationFrame(draw);
        }

        &#x2F;&#x2F; Initialize particles
        const positions = getPhasePositions();
        for (let i = 0; i &lt; 3; i++) {
            const p = createParticle(positions);
            p.phaseIndex = i * 2;
            particles.push(p);
        }

        &#x2F;&#x2F; Respect reduced motion
        if (!window.matchMedia(&#x27;(prefers-reduced-motion: reduce)&#x27;).matches) {
            draw();
        } else {
            &#x2F;&#x2F; Static version
            const positions = getPhasePositions();
            ctx.clearRect(0, 0, canvas.width, canvas.height);

            ctx.lineWidth = 2;
            ctx.strokeStyle = &#x27;rgba(0, 212, 255, 0.5)&#x27;;
            ctx.beginPath();
            positions.forEach((pos, i) =&gt; {
                if (i === 0) ctx.moveTo(pos.x, pos.y);
                else ctx.lineTo(pos.x, pos.y);
            });
            ctx.stroke();

            positions.forEach((pos) =&gt; {
                const color = pos.owner === &#x27;Command&#x27; ? colors.command : colors.agent;
                ctx.fillStyle = color;
                ctx.beginPath();
                ctx.arc(pos.x, pos.y, pos.radius, 0, Math.PI * 2);
                ctx.fill();

                ctx.fillStyle = &#x27;#0a0a0f&#x27;;
                ctx.font = &#x27;bold 14px JetBrains Mono, monospace&#x27;;
                ctx.textAlign = &#x27;center&#x27;;
                ctx.textBaseline = &#x27;middle&#x27;;
                ctx.fillText(pos.phase, pos.x, pos.y);

                ctx.fillStyle = &#x27;#e0e0e4&#x27;;
                ctx.font = &#x27;11px JetBrains Mono, monospace&#x27;;
                ctx.fillText(pos.label, pos.x, pos.y + pos.radius + 16);
            });
        }
    }

    if (document.readyState === &#x27;loading&#x27;) {
        document.addEventListener(&#x27;DOMContentLoaded&#x27;, initPipeline);
    } else {
        initPipeline();
    }
})();
&lt;&#x2F;script&gt;

&lt;style&gt;
.pipeline-container {
    background: var(--bg-0);
    border: 1px solid var(--divider-color);
    border-radius: 12px;
    padding: 1.5rem 1rem 1rem;
    margin: 1.5rem 0;
    position: relative;
}

.pipeline-canvas {
    width: 100%;
    height: 180px;
    display: block;
}

.pipeline-legend {
    display: flex;
    justify-content: center;
    gap: 2rem;
    margin-top: 0.5rem;
    font-family: &#x27;JetBrains Mono&#x27;, monospace;
    font-size: 0.8rem;
    color: var(--meta-color);
}

.legend-item {
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

.legend-dot {
    width: 12px;
    height: 12px;
    border-radius: 50%;
}

.legend-dot.command {
    background: #00d4ff;
}

.legend-dot.agent {
    background: #00ff88;
}

@media (max-width: 768px) {
    .pipeline-container {
        overflow-x: auto;
    }

    .pipeline-canvas {
        min-width: 600px;
    }
}
&lt;&#x2F;style&gt;
&lt;p&gt;&lt;strong&gt;Generalizabilidad&lt;&#x2F;strong&gt;: Esta separación aplica a cualquier framework donde existan restricciones de acceso a herramientas. La capa de inteligencia se vuelve portable y testeable en aislamiento. La capa de I&#x2F;O se convierte en el adaptador específico de plataforma.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;4-2-validacion-basada-en-fases&quot;&gt;4.2 Validación Basada en Fases&lt;&#x2F;h3&gt;
&lt;p&gt;Cada transición de fase representa un checkpoint de validación:&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Checkpoint&lt;&#x2F;th&gt;&lt;th&gt;Validación&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Pre-fase&lt;&#x2F;td&gt;&lt;td&gt;Verificación de contrato de entrada&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Mid-fase&lt;&#x2F;td&gt;&lt;td&gt;Checkpoint de progreso&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Post-fase&lt;&#x2F;td&gt;&lt;td&gt;Cumplimiento de contrato de salida&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Inter-fase&lt;&#x2F;td&gt;&lt;td&gt;Validación de transformación de datos&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;&lt;strong&gt;Estrategias de Recuperación de Errores&lt;&#x2F;strong&gt;:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Rollback al último estado válido&lt;&#x2F;li&gt;
&lt;li&gt;Reintentar con parámetros modificados&lt;&#x2F;li&gt;
&lt;li&gt;Escalar a modo fallback&lt;&#x2F;li&gt;
&lt;li&gt;Preservar resultados parciales&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h3 id=&quot;4-3-degradacion-graciosa&quot;&gt;4.3 Degradación Graciosa&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;strong&gt;Patrón FALLBACK_MODE&lt;&#x2F;strong&gt;:&lt;&#x2F;p&gt;
&lt;p&gt;Cuando los servicios externos se vuelven no disponibles, el sistema degrada graciosamente:&lt;&#x2F;p&gt;









&lt;div class=&quot;comparison-container&quot; id=&quot;comparis&quot;&gt;
    &lt;canvas class=&quot;comparison-canvas&quot; aria-label=&quot;Mode comparison visualization&quot;&gt;&lt;&#x2F;canvas&gt;
&lt;&#x2F;div&gt;

&lt;script&gt;
(function() {
    const containerId = &#x27;comparis&#x27;;
    const mode1Title = &#x27;Modo Normal&#x27;;
    const mode1Steps = [&#x27;Fase 1: Resolver MCP&#x27;, &#x27;Fase 3: Verificar Dups&#x27;, &#x27;Fase 6: Crear Issue&#x27;];
    const mode2Title = &#x27;Modo Fallback&#x27;;
    const mode2Steps = [&#x27;Fase 1: Saltar&#x27;, &#x27;Fase 3: Saltar&#x27;, &#x27;Fase 6: Archivo Local&#x27;];
    const sharedTitle = &#x27;Capa de Inteligencia (Siempre Activa)&#x27;;
    const sharedSteps = [&#x27;Fase 4: Razonamiento&#x27;, &#x27;Fase 5: Formateo&#x27;];

    function initComparison() {
        const container = document.getElementById(containerId);
        if (!container) return;

        const canvas = container.querySelector(&#x27;.comparison-canvas&#x27;);
        const ctx = canvas.getContext(&#x27;2d&#x27;);

        let particles = [];
        let time = 0;

        const colors = {
            mode1: &#x27;#00d4ff&#x27;,
            mode2: &#x27;#ff8800&#x27;,
            shared: &#x27;#00ff88&#x27;,
            skip: &#x27;#444466&#x27;,
            particle: &#x27;#00ff88&#x27;,
            connection: &#x27;rgba(255, 255, 255, 0.2)&#x27;,
            text: &#x27;#e0e0e4&#x27;,
            subtext: &#x27;#8888a0&#x27;
        };

        function resize() {
            const rect = container.getBoundingClientRect();
            canvas.width = rect.width;
            canvas.height = 320;
        }
        resize();
        window.addEventListener(&#x27;resize&#x27;, resize);

        function draw() {
            ctx.clearRect(0, 0, canvas.width, canvas.height);

            const leftX = canvas.width * 0.25;
            const rightX = canvas.width * 0.75;
            const centerX = canvas.width &#x2F; 2;

            const headerY = 35;
            const stepsStartY = 80;
            const stepSpacing = 45;
            const sharedY = 240;

            &#x2F;&#x2F; Draw mode headers
            function drawHeader(x, title, color) {
                ctx.fillStyle = color;
                ctx.font = &#x27;bold 14px JetBrains Mono&#x27;;
                ctx.textAlign = &#x27;center&#x27;;
                ctx.shadowColor = color;
                ctx.shadowBlur = 10;
                ctx.fillText(title, x, headerY);
                ctx.shadowBlur = 0;

                &#x2F;&#x2F; Underline
                ctx.strokeStyle = color;
                ctx.lineWidth = 2;
                ctx.beginPath();
                ctx.moveTo(x - 60, headerY + 8);
                ctx.lineTo(x + 60, headerY + 8);
                ctx.stroke();
            }

            drawHeader(leftX, mode1Title, colors.mode1);
            drawHeader(rightX, mode2Title, colors.mode2);

            &#x2F;&#x2F; Draw steps for each mode
            function drawStep(x, y, text, color, isSkip) {
                const width = 130;
                const height = 32;

                ctx.shadowColor = color;
                ctx.shadowBlur = isSkip ? 0 : 12;

                ctx.fillStyle = isSkip ? colors.skip : color;
                ctx.beginPath();
                ctx.roundRect(x - width&#x2F;2, y - height&#x2F;2, width, height, 6);
                ctx.fill();

                ctx.shadowBlur = 0;

                if (!isSkip) {
                    ctx.strokeStyle = &#x27;#00ff88&#x27;;
                    ctx.lineWidth = 1;
                    ctx.stroke();
                } else {
                    ctx.setLineDash([4, 4]);
                    ctx.strokeStyle = &#x27;#666&#x27;;
                    ctx.lineWidth = 1;
                    ctx.stroke();
                    ctx.setLineDash([]);
                }

                ctx.fillStyle = isSkip ? &#x27;#666&#x27; : &#x27;#0a0a0f&#x27;;
                ctx.font = isSkip ? &#x27;10px JetBrains Mono&#x27; : &#x27;bold 10px JetBrains Mono&#x27;;
                ctx.textAlign = &#x27;center&#x27;;
                ctx.textBaseline = &#x27;middle&#x27;;
                ctx.fillText(text, x, y);

                return { x, y, width, height };
            }

            &#x2F;&#x2F; Mode 1 steps
            const mode1Nodes = [];
            mode1Steps.forEach((step, i) =&gt; {
                const y = stepsStartY + i * stepSpacing;
                mode1Nodes.push(drawStep(leftX, y, step, colors.mode1, false));
            });

            &#x2F;&#x2F; Mode 2 steps
            const mode2Nodes = [];
            mode2Steps.forEach((step, i) =&gt; {
                const y = stepsStartY + i * stepSpacing;
                const isSkip = step.toLowerCase().includes(&#x27;skip&#x27;);
                mode2Nodes.push(drawStep(rightX, y, step, colors.mode2, isSkip));
            });

            &#x2F;&#x2F; Connections between steps
            ctx.strokeStyle = colors.connection;
            ctx.lineWidth = 1;

            for (let i = 0; i &lt; mode1Nodes.length - 1; i++) {
                const from = mode1Nodes[i];
                const to = mode1Nodes[i + 1];
                ctx.beginPath();
                ctx.moveTo(from.x, from.y + from.height&#x2F;2);
                ctx.lineTo(to.x, to.y - to.height&#x2F;2);
                ctx.stroke();
            }

            for (let i = 0; i &lt; mode2Nodes.length - 1; i++) {
                const from = mode2Nodes[i];
                const to = mode2Nodes[i + 1];
                ctx.beginPath();
                ctx.moveTo(from.x, from.y + from.height&#x2F;2);
                ctx.lineTo(to.x, to.y - to.height&#x2F;2);
                ctx.stroke();
            }

            &#x2F;&#x2F; Shared section
            ctx.fillStyle = colors.shared;
            ctx.font = &#x27;bold 13px JetBrains Mono&#x27;;
            ctx.textAlign = &#x27;center&#x27;;
            ctx.shadowColor = colors.shared;
            ctx.shadowBlur = 10;
            ctx.fillText(sharedTitle, centerX, sharedY - 25);
            ctx.shadowBlur = 0;

            &#x2F;&#x2F; Shared steps box
            const sharedWidth = 200;
            const sharedHeight = 50;
            ctx.fillStyle = &#x27;rgba(0, 255, 136, 0.15)&#x27;;
            ctx.strokeStyle = colors.shared;
            ctx.lineWidth = 2;
            ctx.beginPath();
            ctx.roundRect(centerX - sharedWidth&#x2F;2, sharedY - 10, sharedWidth, sharedHeight, 8);
            ctx.fill();
            ctx.stroke();

            &#x2F;&#x2F; Shared step text
            ctx.fillStyle = colors.text;
            ctx.font = &#x27;11px JetBrains Mono&#x27;;
            sharedSteps.forEach((step, i) =&gt; {
                ctx.fillText(step, centerX, sharedY + 8 + i * 16);
            });

            &#x2F;&#x2F; Arrows from both modes to shared
            ctx.strokeStyle = colors.connection;
            ctx.lineWidth = 2;

            &#x2F;&#x2F; Left arrow
            const lastMode1 = mode1Nodes[mode1Nodes.length - 1];
            ctx.beginPath();
            ctx.moveTo(lastMode1.x, lastMode1.y + lastMode1.height&#x2F;2);
            ctx.quadraticCurveTo(lastMode1.x, sharedY - 30, centerX - sharedWidth&#x2F;2, sharedY + 15);
            ctx.stroke();

            &#x2F;&#x2F; Right arrow
            const lastMode2 = mode2Nodes[mode2Nodes.length - 1];
            ctx.beginPath();
            ctx.moveTo(lastMode2.x, lastMode2.y + lastMode2.height&#x2F;2);
            ctx.quadraticCurveTo(lastMode2.x, sharedY - 30, centerX + sharedWidth&#x2F;2, sharedY + 15);
            ctx.stroke();

            &#x2F;&#x2F; &quot;Always Active&quot; badge
            ctx.fillStyle = colors.shared;
            ctx.font = &#x27;bold 9px JetBrains Mono&#x27;;
            ctx.fillText(&#x27;✓ ALWAYS ACTIVE&#x27;, centerX, sharedY + sharedHeight + 8);

            &#x2F;&#x2F; Animate particles
            particles.forEach(p =&gt; {
                p.progress += p.speed;
                if (p.progress &gt;= 1) {
                    p.progress = 0;
                    p.side = p.side === &#x27;left&#x27; ? &#x27;right&#x27; : &#x27;left&#x27;;
                }

                const nodes = p.side === &#x27;left&#x27; ? mode1Nodes : mode2Nodes;
                const segmentIndex = Math.floor(p.progress * nodes.length);
                const segmentProgress = (p.progress * nodes.length) % 1;

                if (segmentIndex &lt; nodes.length - 1) {
                    const from = nodes[segmentIndex];
                    const to = nodes[segmentIndex + 1];
                    const x = from.x + (to.x - from.x) * segmentProgress;
                    const y = from.y + (to.y - from.y) * segmentProgress;

                    ctx.fillStyle = colors.particle;
                    ctx.shadowColor = colors.particle;
                    ctx.shadowBlur = 10;
                    ctx.beginPath();
                    ctx.arc(x, y, 4, 0, Math.PI * 2);
                    ctx.fill();
                    ctx.shadowBlur = 0;
                }
            });

            time++;
            requestAnimationFrame(draw);
        }

        &#x2F;&#x2F; Initialize particles
        particles.push({ side: &#x27;left&#x27;, progress: 0, speed: 0.008 });
        particles.push({ side: &#x27;right&#x27;, progress: 0.5, speed: 0.008 });

        if (!window.matchMedia(&#x27;(prefers-reduced-motion: reduce)&#x27;).matches) {
            draw();
        } else {
            draw();
            &#x2F;&#x2F; Just draw once for static
        }
    }

    if (document.readyState === &#x27;loading&#x27;) {
        document.addEventListener(&#x27;DOMContentLoaded&#x27;, initComparison);
    } else {
        initComparison();
    }
})();
&lt;&#x2F;script&gt;

&lt;style&gt;
.comparison-container {
    background: var(--bg-0);
    border: 1px solid var(--divider-color);
    border-radius: 12px;
    padding: 1rem;
    margin: 1.5rem 0;
    position: relative;
}

.comparison-canvas {
    width: 100%;
    height: 320px;
    display: block;
}

@media (max-width: 768px) {
    .comparison-container {
        overflow-x: auto;
    }

    .comparison-canvas {
        min-width: 500px;
    }
}
&lt;&#x2F;style&gt;
&lt;p&gt;Las capas de inteligencia (Fases 4-5) funcionan idénticamente en ambos modos. El razonamiento core se preserva incluso cuando las capacidades de I&#x2F;O están reducidas.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;5-implicaciones-para-diseno-de-frameworks&quot;&gt;5. Implicaciones para Diseño de Frameworks&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;5-1-filosofia-de-acceso-a-herramientas&quot;&gt;5.1 Filosofía de Acceso a Herramientas&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;strong&gt;Recomendación&lt;&#x2F;strong&gt;: Preferir declaraciones explícitas de herramientas sobre herencia implícita.&lt;&#x2F;p&gt;
&lt;p&gt;La paradoja de la herencia demuestra que los modelos implícitos crean límites de scope inesperados. Las declaraciones explícitas, aunque verbose, producen comportamiento predecible:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;yaml&quot; class=&quot;language-yaml z-code&quot;&gt;&lt;code class=&quot;language-yaml&quot; data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;z-source z-yaml&quot;&gt;&lt;span class=&quot;z-comment z-line z-number-sign z-yaml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-line z-number-sign z-yaml&quot;&gt;#&lt;&#x2F;span&gt; Explícito (recomendado)
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-yaml&quot;&gt;&lt;span class=&quot;z-string z-unquoted z-plain z-out z-yaml&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag z-yaml&quot;&gt;tools&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-key-value z-mapping z-yaml&quot;&gt;:&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-yaml&quot;&gt;  &lt;span class=&quot;z-punctuation z-definition z-block z-sequence z-item z-yaml&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;z-string z-unquoted z-plain z-out z-yaml&quot;&gt;Read&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-yaml&quot;&gt;  &lt;span class=&quot;z-punctuation z-definition z-block z-sequence z-item z-yaml&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;z-string z-unquoted z-plain z-out z-yaml&quot;&gt;Write&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-yaml&quot;&gt;  &lt;span class=&quot;z-punctuation z-definition z-block z-sequence z-item z-yaml&quot;&gt;-&lt;&#x2F;span&gt; &lt;span class=&quot;z-string z-unquoted z-plain z-out z-yaml&quot;&gt;Grep&lt;&#x2F;span&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-yaml&quot;&gt;
&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-yaml&quot;&gt;&lt;span class=&quot;z-comment z-line z-number-sign z-yaml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-line z-number-sign z-yaml&quot;&gt;#&lt;&#x2F;span&gt; Implícito (problemático)
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-source z-yaml&quot;&gt;&lt;span class=&quot;z-string z-unquoted z-plain z-out z-yaml&quot;&gt;&lt;span class=&quot;z-entity z-name z-tag z-yaml&quot;&gt;tools&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-separator z-key-value z-mapping z-yaml&quot;&gt;:&lt;&#x2F;span&gt; &lt;span class=&quot;z-string z-unquoted z-plain z-out z-yaml&quot;&gt;inherit&lt;&#x2F;span&gt;  &lt;span class=&quot;z-comment z-line z-number-sign z-yaml&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment z-line z-number-sign z-yaml&quot;&gt;#&lt;&#x2F;span&gt; Comportamiento varía por contexto
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;5-2-fidelidad-de-documentacion&quot;&gt;5.2 Fidelidad de Documentación&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;strong&gt;Requisito&lt;&#x2F;strong&gt;: La implementación debe coincidir con la documentación.&lt;&#x2F;p&gt;
&lt;p&gt;La brecha entre comportamiento documentado y real de MCP consumió esfuerzo significativo de debugging. Pruebas automatizadas de comportamientos documentados captarían tales discrepancias antes de que lleguen a usuarios.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;5-3-testing-en-contextos-reales&quot;&gt;5.3 Testing en Contextos Reales&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;strong&gt;Requisito&lt;&#x2F;strong&gt;: Probar con restricciones reales de plataforma.&lt;&#x2F;p&gt;
&lt;p&gt;El acceso MCP funciona en testing aislado pero falla en contextos de plugin. Los tests de integración deben ejecutarse en ambientes similares a producción con límites de scope reales.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;5-4-consideraciones-de-experiencia-de-desarrollador&quot;&gt;5.4 Consideraciones de Experiencia de Desarrollador&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;strong&gt;Observación&lt;&#x2F;strong&gt;: Los requisitos complejos de deployment desalientan la experimentación.&lt;&#x2F;p&gt;
&lt;p&gt;El requisito de sincronización en tres ubicaciones, falta de hot reload, y validación estricta de schema añaden fricción al ciclo de desarrollo. Cada restricción añade aproximadamente 2-5 minutos por ciclo de cambio versus segundos para sistemas con hot-reload.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Análisis de Trade-off&lt;&#x2F;strong&gt;:&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Restricción&lt;&#x2F;th&gt;&lt;th&gt;Beneficio&lt;&#x2F;th&gt;&lt;th&gt;Costo&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Sync en tres ubicaciones&lt;&#x2F;td&gt;&lt;td&gt;Consistencia de versión&lt;&#x2F;td&gt;&lt;td&gt;Overhead de sync manual&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Sin hot reload&lt;&#x2F;td&gt;&lt;td&gt;Predictabilidad de estado&lt;&#x2F;td&gt;&lt;td&gt;Delay de reinicio&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Schema estricto&lt;&#x2F;td&gt;&lt;td&gt;Detección temprana de errores&lt;&#x2F;td&gt;&lt;td&gt;Dificultad de debugging&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;6-conclusion&quot;&gt;6. Conclusión&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;resumen-de-contribuciones&quot;&gt;Resumen de Contribuciones&lt;&#x2F;h3&gt;
&lt;p&gt;Este estudio empírico del desarrollo de plugins de Claude Code revela patrones aplicables al diseño de sistemas IA multi-agente:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;La Paradoja de la Herencia&lt;&#x2F;strong&gt;: Los modelos de herencia de acceso a herramientas documentados en especificaciones pueden no funcionar a través de todos los límites de scope. Los contratos explícitos previenen comportamiento inesperado.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Arquitectura Impulsada por Topología&lt;&#x2F;strong&gt;: Las restricciones impuestas por plataforma (como anidamiento de un solo nivel) impulsan la emergencia de patrones específicos (hub-and-spoke) que pueden ser superiores a diseños sin restricciones.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Naming como Comportamiento&lt;&#x2F;strong&gt;: En sistemas de agentes IA, las convenciones de nombres llevan implicaciones funcionales más allá del etiquetado.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Separación I&#x2F;O&lt;&#x2F;strong&gt;: Separar I&#x2F;O de inteligencia crea sistemas portables, testeables, con degradación graciosa.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h3 id=&quot;limitaciones&quot;&gt;Limitaciones&lt;&#x2F;h3&gt;
&lt;p&gt;Este estudio está basado en observaciones de una sola plataforma (Claude Code) durante un período específico de desarrollo. Los hallazgos pueden no generalizarse a todos los frameworks de agentes. Adicionalmente, el comportamiento de la plataforma puede cambiar a medida que se resuelven bugs.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;direcciones-futuras-de-investigacion&quot;&gt;Direcciones Futuras de Investigación&lt;&#x2F;h3&gt;
&lt;ol&gt;
&lt;li&gt;Análisis comparativo de modelos de herencia de herramientas a través de plataformas de agentes&lt;&#x2F;li&gt;
&lt;li&gt;Especificación formal de contratos de scope para sistemas multi-agente&lt;&#x2F;li&gt;
&lt;li&gt;Estudios empíricos de acoplamiento naming-comportamiento en otros sistemas IA&lt;&#x2F;li&gt;
&lt;li&gt;Patrones de diseño de frameworks que explícitamente aprovechan restricciones de topología&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;apendice-resumen-de-evidencia&quot;&gt;Apéndice: Resumen de Evidencia&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;issues-de-github-referenciados&quot;&gt;Issues de GitHub Referenciados&lt;&#x2F;h3&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Issue&lt;&#x2F;th&gt;&lt;th&gt;Descripción&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;#13605&lt;&#x2F;td&gt;&lt;td&gt;Agentes de plugin personalizados no pueden acceder a MCP&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;#15810&lt;&#x2F;td&gt;&lt;td&gt;Subagentes no heredan MCP de agentes definidos en plugins&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;#14496&lt;&#x2F;td&gt;&lt;td&gt;Acceso inconsistente a MCP con prompts complejos&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;#7296&lt;&#x2F;td&gt;&lt;td&gt;Fallo de herencia de scope para agentes lanzados con Task&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;h3 id=&quot;metricas-de-investigacion&quot;&gt;Métricas de Investigación&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Período de Observación&lt;&#x2F;strong&gt;: Enero 3-4, 2026&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Inversión de Tokens&lt;&#x2F;strong&gt;: ~265,000 tokens a través de sesiones&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Observaciones Distintas&lt;&#x2F;strong&gt;: 13 hallazgos documentados&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Tipos de Hallazgo&lt;&#x2F;strong&gt;: 4 Decisiones, 8 Descubrimientos, 1 Cambio&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;metodologia&quot;&gt;Metodología&lt;&#x2F;h3&gt;
&lt;p&gt;Las observaciones fueron recolectadas a través de sesiones de desarrollo usando el sistema de memoria persistente claude-mem. Cada hallazgo fue cruzado contra documentación y, donde aplicable, reportes de issues de GitHub. Los workarounds fueron desarrollados a través de experimentación iterativa.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;obten-el-plugin&quot;&gt;Obtén el Plugin&lt;&#x2F;h2&gt;
&lt;p&gt;El plugin discutido en esta investigación es open source: &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Cpicon&#x2F;claude-code-plugins&quot;&gt;Cpicon&#x2F;claude-code-plugins&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Usa la pestaña &lt;a href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Cpicon&#x2F;claude-code-plugins&#x2F;issues&quot;&gt;GitHub Issues&lt;&#x2F;a&gt; para solicitar features, reportar bugs, o discutir mejoras.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;&lt;strong&gt;Navegación de la Serie:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Parte 0&lt;&#x2F;strong&gt;: &lt;a href=&quot;&#x2F;posts&#x2F;claude-code-plugin-journey-part0&#x2F;&quot;&gt;Agent Team Creator&lt;&#x2F;a&gt; — Qué hace el plugin y cómo usarlo&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Parte 1&lt;&#x2F;strong&gt;: &lt;a href=&quot;&#x2F;posts&#x2F;claude-code-plugin-journey-part1&#x2F;&quot;&gt;El Patrón de Arquitectura Híbrida&lt;&#x2F;a&gt; — Construyendo el plugin, lecciones aprendidas&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Parte 2&lt;&#x2F;strong&gt; (Estás aquí): Insights de investigación, patrones&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;&lt;em&gt;Este artículo está basado en observaciones empíricas del desarrollo del plugin agent-team-creator de Claude Code. Para guía práctica de implementación, ve Parte 1.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
</content>
        <summary type="html">La Paradoja de la Herencia: Scoping de Herramientas en Sistemas Multi-Agente

Esta es la Parte 2 de una serie de tres partes. Ve Parte 0 para la visión general del plugin e instalación, o Parte 1 para el viaje de desarrollo.

Resumen: Este artículo presenta observaciones empíricas del desarrollo de un plugin de Claude Code, revelando patrones fundamentales en el diseño de sistemas IA multi-agente. A través de sesiones de desarrollo documentadas, identificamos tres hallazgos clave: (1) los modelos de herencia implícita de herramientas fallan consistentemente en la práctica, requiriendo contratos de scope explícitos; (2) las restricciones de topología impuestas por la plataforma impulsan la emergencia de patrones arquitectónicos específicos; y (3) las convenciones de nombres se convierten en requisitos funcionales en sistemas IA. Estos hallazgos tienen implicaciones para el diseño de frameworks de agentes más allá de la plataforma específica estudiada.
…</summary>
        </entry>
</feed>
