<?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="en">
    <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>This is a web feed, also known as an Atom feed. Subscribe by copying the URL from the address bar into your newsreader. Visit About Feeds to learn more and get started. It&#x27;s free.</tabi:about_feeds>
        <tabi:visit_the_site>Visit website</tabi:visit_the_site>
        <tabi:recent_posts>Recent posts</tabi:recent_posts>
        <tabi:last_updated_on>Updated on $DATE</tabi:last_updated_on>
        <tabi:default_theme></tabi:default_theme>
        <tabi:post_listing_date>date</tabi:post_listing_date>
        <tabi:current_section>Agent Architecture</tabi:current_section>
    </tabi:metadata><link rel="extra-stylesheet" href="https://futurewithml.netlify.app/skins/cyber.css?h=eb029a27afbc61465b52" /><title>Future With ML - Agent Architecture</title>
        <subtitle>Machine Learning Design Patterns, MLOps, and AI Engineering insights by Christian Picon Calderon</subtitle>
    <link href="https://futurewithml.netlify.app/tags/agent-architecture/atom.xml" rel="self" type="application/atom+xml"/>
    <link href="https://futurewithml.netlify.app/tags/agent-architecture/" 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/tags/agent-architecture/atom.xml</id><entry xml:lang="en">
        <title>The Inheritance Paradox: Tool Scoping in Multi-Agent Systems</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/posts/claude-code-plugin-journey-part2/" type="text/html"/>
        <id>https://futurewithml.netlify.app/posts/claude-code-plugin-journey-part2/</id>
        
            <content type="html">&lt;h1 id=&quot;the-inheritance-paradox-tool-scoping-in-multi-agent-systems&quot;&gt;The Inheritance Paradox: Tool Scoping in Multi-Agent Systems&lt;&#x2F;h1&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;This is Part 2 of a three-part series. See &lt;a href=&quot;&#x2F;posts&#x2F;claude-code-plugin-journey-part0&#x2F;&quot;&gt;Part 0&lt;&#x2F;a&gt; for the plugin overview and installation, or &lt;a href=&quot;&#x2F;posts&#x2F;claude-code-plugin-journey-part1&#x2F;&quot;&gt;Part 1&lt;&#x2F;a&gt; for the development journey.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Abstract&lt;&#x2F;strong&gt;: This article presents empirical observations from developing a Claude Code plugin, revealing fundamental patterns in multi-agent AI system design. Through documented development sessions, we identify three key findings: (1) implicit tool inheritance models consistently fail in practice, requiring explicit scope contracts; (2) platform-enforced topology constraints drive the emergence of specific architectural patterns; and (3) agent naming conventions become functional requirements in AI systems. These findings have implications for the design of agent frameworks beyond the specific platform studied.&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-introduction&quot;&gt;1. Introduction&lt;&#x2F;h2&gt;
&lt;p&gt;Agent-based development platforms represent an emerging paradigm in software engineering, where AI instances collaborate on complex tasks through structured orchestration. These platforms offer extension mechanisms—plugins, commands, agents—that allow developers to customize behavior for specific domains.&lt;&#x2F;p&gt;
&lt;p&gt;This article examines Claude Code as a case study in agent framework design. Claude Code enables developers to build custom plugins with commands (user-invoked shortcuts), agents (specialized Claude instances), skills (context providers), hooks (event handlers), and MCP integrations (external service connections).&lt;&#x2F;p&gt;
&lt;p&gt;During the development of &lt;code&gt;agent-team-creator&lt;&#x2F;code&gt;, a plugin for generating project-specific AI agents, we documented systematic observations about platform behavior. These observations reveal patterns relevant to multi-agent system design more broadly.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;research-questions&quot;&gt;Research Questions&lt;&#x2F;h3&gt;
&lt;ol&gt;
&lt;li&gt;How do tool inheritance models behave in practice versus documentation?&lt;&#x2F;li&gt;
&lt;li&gt;What architectural patterns emerge from platform topology constraints?&lt;&#x2F;li&gt;
&lt;li&gt;How do naming conventions influence agent behavior in AI systems?&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;2-platform-architecture-analysis&quot;&gt;2. Platform Architecture Analysis&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;2-1-component-type-taxonomy&quot;&gt;2.1 Component Type Taxonomy&lt;&#x2F;h3&gt;
&lt;p&gt;Claude Code plugins support five distinct extension types, each with different triggering mechanisms and scope:&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Component&lt;&#x2F;th&gt;&lt;th&gt;Trigger&lt;&#x2F;th&gt;&lt;th&gt;Invocation&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 decides)&lt;&#x2F;td&gt;&lt;td&gt;Implicit&lt;&#x2F;td&gt;&lt;td&gt;Cross-product&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Commands&lt;&#x2F;td&gt;&lt;td&gt;User (&lt;code&gt;&#x2F;syntax&lt;&#x2F;code&gt;)&lt;&#x2F;td&gt;&lt;td&gt;Explicit&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 (Task tool)&lt;&#x2F;td&gt;&lt;td&gt;Explicit&#x2F;Implicit&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;Events&lt;&#x2F;td&gt;&lt;td&gt;Automatic&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;Tool calls&lt;&#x2F;td&gt;&lt;td&gt;Explicit&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;Key Observation&lt;&#x2F;strong&gt;: Skills are unique in being cross-product (functional in web UI, API, and CLI), while all other components are Claude Code-specific. This suggests different inheritance models may apply to different component types.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;2-2-tool-inventory&quot;&gt;2.2 Tool Inventory&lt;&#x2F;h3&gt;
&lt;p&gt;The platform provides 15 built-in tools available to agents:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Read, Write, Edit, MultiEdit (file operations)&lt;&#x2F;li&gt;
&lt;li&gt;Glob, Grep, LS (search operations)&lt;&#x2F;li&gt;
&lt;li&gt;Bash (system commands)&lt;&#x2F;li&gt;
&lt;li&gt;WebFetch, WebSearch (web access)&lt;&#x2F;li&gt;
&lt;li&gt;NotebookRead, NotebookEdit (Jupyter support)&lt;&#x2F;li&gt;
&lt;li&gt;TodoRead, TodoWrite (task tracking)&lt;&#x2F;li&gt;
&lt;li&gt;exit_plan_mode (workflow control)&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;Additionally, MCP (Model Context Protocol) servers provide external service integrations—database access, API connections, third-party services.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;2-3-lifecycle-events&quot;&gt;2.3 Lifecycle Events&lt;&#x2F;h3&gt;
&lt;p&gt;Nine hook events define the agent lifecycle:&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Event&lt;&#x2F;th&gt;&lt;th&gt;Trigger&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;Before any tool execution&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;PostToolUse&lt;&#x2F;td&gt;&lt;td&gt;After tool execution&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;UserPromptSubmit&lt;&#x2F;td&gt;&lt;td&gt;User message received&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Stop&lt;&#x2F;td&gt;&lt;td&gt;Agent completion&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;SubagentStop&lt;&#x2F;td&gt;&lt;td&gt;Subagent completion&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;SessionStart&lt;&#x2F;td&gt;&lt;td&gt;Session initialization&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;SessionEnd&lt;&#x2F;td&gt;&lt;td&gt;Session termination&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;PreCompact&lt;&#x2F;td&gt;&lt;td&gt;Before context compaction&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Notification&lt;&#x2F;td&gt;&lt;td&gt;System notification&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;Hook exit codes follow Unix conventions: 0 for success, 2 for blocking errors (Claude processes stderr), other codes for non-blocking errors.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;3-key-findings&quot;&gt;3. Key Findings&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;3-1-the-inheritance-paradox&quot;&gt;3.1 The Inheritance Paradox&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;strong&gt;Documentation Claim&lt;&#x2F;strong&gt;:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;“MCP Tools: Subagents can access MCP tools from configured MCP servers. When the tools field is omitted, subagents inherit all MCP tools available to the main thread.”&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Observed Behavior&lt;&#x2F;strong&gt;: Plugin-defined subagents cannot access MCP tools under any tested configuration.&lt;&#x2F;p&gt;
&lt;p&gt;This discrepancy is documented across four GitHub issues (#13605, #15810, #14496, #7296), each describing a different manifestation of scope inheritance failure:&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Issue&lt;&#x2F;th&gt;&lt;th&gt;Failure Mode&lt;&#x2F;th&gt;&lt;th&gt;Scope Context&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;Plugin agents can’t access 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;Subagents don’t inherit MCP&lt;&#x2F;td&gt;&lt;td&gt;Parent-child hierarchy&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;#14496&lt;&#x2F;td&gt;&lt;td&gt;Complex prompts break MCP access&lt;&#x2F;td&gt;&lt;td&gt;Prompt complexity&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;#7296&lt;&#x2F;td&gt;&lt;td&gt;Task-launched agents lack MCP&lt;&#x2F;td&gt;&lt;td&gt;User-scoped inheritance&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;&lt;strong&gt;Analysis&lt;&#x2F;strong&gt;: The pattern suggests a fundamental architectural issue rather than isolated bugs. Tool inheritance operates differently across scope boundaries:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Built-in agents&lt;&#x2F;strong&gt; inherit MCP tools correctly&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Plugin-defined agents&lt;&#x2F;strong&gt; do not inherit MCP tools&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Task-launched agents&lt;&#x2F;strong&gt; receive a fresh scope without user-level configurations&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;&lt;strong&gt;Implication for Framework Design&lt;&#x2F;strong&gt;: Implicit tool inheritance models require explicit contracts at scope boundaries. Assuming tools propagate through agent hierarchies creates fragile dependencies. Explicit tool declarations at each scope level, while verbose, produce predictable behavior.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;3-2-topology-constraints-and-emergent-patterns&quot;&gt;3.2 Topology Constraints and Emergent Patterns&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;strong&gt;Discovery&lt;&#x2F;strong&gt;: The platform enforces a single-level nesting limit for agent spawning.&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;Agent&#x27;, &#x27;Subagent&#x27;];
    const blockedName = &#x27;Subsubagent&#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;When an agent attempts to spawn a nested subagent, the platform returns:&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;Design Rationale (Inferred)&lt;&#x2F;strong&gt;:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Prevents infinite recursion in autonomous systems&lt;&#x2F;li&gt;
&lt;li&gt;Maintains bounded resource consumption&lt;&#x2F;li&gt;
&lt;li&gt;Ensures predictable execution topology&lt;&#x2F;li&gt;
&lt;li&gt;Simplifies debugging and monitoring&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;&lt;strong&gt;Emergent Architecture: 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;Main Agent&#x27;);
    const spokeNames = [decodeHTML(&#x27;Agent 1&#x27;), decodeHTML(&#x27;Agent 2&#x27;), decodeHTML(&#x27;Agent 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;This constraint eliminates tree-structured agent hierarchies in favor of flat, hub-coordinated topologies. The “hub” agent coordinates parallel execution of “spoke” agents. No spoke can spawn additional agents.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Observed Trade-off&lt;&#x2F;strong&gt;:&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Lost Capability&lt;&#x2F;th&gt;&lt;th&gt;Gained Property&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Recursive decomposition&lt;&#x2F;td&gt;&lt;td&gt;Bounded execution depth&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Dynamic hierarchy&lt;&#x2F;td&gt;&lt;td&gt;Predictable topology&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Arbitrary nesting&lt;&#x2F;td&gt;&lt;td&gt;Simplified resource management&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;&lt;strong&gt;Implication for Framework Design&lt;&#x2F;strong&gt;: Topology constraints are design decisions, not merely limitations. The hub-and-spoke pattern that emerges from single-level nesting produces systems that are easier to reason about, debug, and monitor than arbitrary recursive structures.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;3-3-naming-as-behavior&quot;&gt;3.3 Naming as Behavior&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;strong&gt;Discovery&lt;&#x2F;strong&gt;: Claude Code infers agent behavior from agent names.&lt;&#x2F;p&gt;
&lt;p&gt;An agent named &lt;code&gt;code-reviewer&lt;&#x2F;code&gt; triggers built-in review behaviors that may override custom system prompt instructions. The platform applies heuristics based on naming conventions.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Observed Pattern&lt;&#x2F;strong&gt;:&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Agent Name&lt;&#x2F;th&gt;&lt;th&gt;Inferred Behavior&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;Generic code review patterns&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;Test generation patterns&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;Less inference, more custom control&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;&lt;strong&gt;Analysis&lt;&#x2F;strong&gt;: This represents a “convention over configuration” paradigm applied to AI agent systems. The platform assumes semantically meaningful names carry behavioral intent.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Implications&lt;&#x2F;strong&gt;:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Naming conventions become functional requirements&lt;&#x2F;li&gt;
&lt;li&gt;Distinctive, non-generic names preserve custom behavior&lt;&#x2F;li&gt;
&lt;li&gt;Agent system design must consider name-based inference&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;4-emergent-patterns&quot;&gt;4. Emergent Patterns&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;4-1-i-o-separation-hybrid-architecture&quot;&gt;4.1 I&#x2F;O Separation (Hybrid Architecture)&lt;&#x2F;h3&gt;
&lt;p&gt;In response to MCP access limitations, a clear architectural pattern emerged: separating I&#x2F;O operations from intelligence operations.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Layer Model&lt;&#x2F;strong&gt;:&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Layer&lt;&#x2F;th&gt;&lt;th&gt;Responsibilities&lt;&#x2F;th&gt;&lt;th&gt;Components&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;MCP access, file ops, user interaction, caching&lt;&#x2F;td&gt;&lt;td&gt;Commands&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Intelligence&lt;&#x2F;td&gt;&lt;td&gt;Reasoning, analysis, formatting, decisions&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;6-Phase Pipeline Example&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;Availability&quot;, &quot;io&quot;: &quot;external&quot;},
    {&quot;phase&quot;: 1, &quot;owner&quot;: &quot;Command&quot;, &quot;label&quot;: &quot;Data Resolve&quot;, &quot;io&quot;: &quot;external&quot;},
    {&quot;phase&quot;: 2, &quot;owner&quot;: &quot;Command&quot;, &quot;label&quot;: &quot;Load Content&quot;, &quot;io&quot;: &quot;local&quot;},
    {&quot;phase&quot;: 3, &quot;owner&quot;: &quot;Command&quot;, &quot;label&quot;: &quot;Validation&quot;, &quot;io&quot;: &quot;external&quot;},
    {&quot;phase&quot;: 4, &quot;owner&quot;: &quot;Agent&quot;, &quot;label&quot;: &quot;Reasoning&quot;, &quot;io&quot;: &quot;none&quot;},
    {&quot;phase&quot;: 5, &quot;owner&quot;: &quot;Agent&quot;, &quot;label&quot;: &quot;Formatting&quot;, &quot;io&quot;: &quot;none&quot;},
    {&quot;phase&quot;: 6, &quot;owner&quot;: &quot;Command&quot;, &quot;label&quot;: &quot;Delivery&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;Generalizability&lt;&#x2F;strong&gt;: This separation applies to any framework where tool access constraints exist. The intelligence layer becomes portable and testable in isolation. The I&#x2F;O layer becomes the platform-specific adapter.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;4-2-phase-based-validation&quot;&gt;4.2 Phase-Based Validation&lt;&#x2F;h3&gt;
&lt;p&gt;Each phase transition represents a validation checkpoint:&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Checkpoint&lt;&#x2F;th&gt;&lt;th&gt;Validation&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Pre-phase&lt;&#x2F;td&gt;&lt;td&gt;Input contract verification&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Mid-phase&lt;&#x2F;td&gt;&lt;td&gt;Progress checkpoint&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Post-phase&lt;&#x2F;td&gt;&lt;td&gt;Output contract enforcement&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Inter-phase&lt;&#x2F;td&gt;&lt;td&gt;Data transformation validation&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;&lt;strong&gt;Error Recovery Strategies&lt;&#x2F;strong&gt;:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Rollback to last valid state&lt;&#x2F;li&gt;
&lt;li&gt;Retry with modified parameters&lt;&#x2F;li&gt;
&lt;li&gt;Escalate to fallback mode&lt;&#x2F;li&gt;
&lt;li&gt;Preserve partial results&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h3 id=&quot;4-3-graceful-degradation&quot;&gt;4.3 Graceful Degradation&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;strong&gt;FALLBACK_MODE Pattern&lt;&#x2F;strong&gt;:&lt;&#x2F;p&gt;
&lt;p&gt;When external services become unavailable, the system degrades gracefully:&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;Normal Mode&#x27;;
    const mode1Steps = [&#x27;Phase 1: MCP Resolve&#x27;, &#x27;Phase 3: Dup Check&#x27;, &#x27;Phase 6: Create Issue&#x27;];
    const mode2Title = &#x27;Fallback Mode&#x27;;
    const mode2Steps = [&#x27;Phase 1: Skip&#x27;, &#x27;Phase 3: Skip&#x27;, &#x27;Phase 6: Local File&#x27;];
    const sharedTitle = &#x27;Intelligence Layer (Always Active)&#x27;;
    const sharedSteps = [&#x27;Phase 4: Reasoning&#x27;, &#x27;Phase 5: Formatting&#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;The intelligence layers (Phases 4-5) function identically in both modes. Core reasoning is preserved even when I&#x2F;O capabilities are reduced.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;5-framework-design-implications&quot;&gt;5. Framework Design Implications&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;5-1-tool-access-philosophy&quot;&gt;5.1 Tool Access Philosophy&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;strong&gt;Recommendation&lt;&#x2F;strong&gt;: Prefer explicit tool declarations over implicit inheritance.&lt;&#x2F;p&gt;
&lt;p&gt;The inheritance paradox demonstrates that implicit models create unexpected scope boundaries. Explicit declarations, while verbose, produce predictable behavior:&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; Explicit (recommended)
&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; Implicit (problematic)
&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; Behavior varies by context
&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;5-2-documentation-fidelity&quot;&gt;5.2 Documentation Fidelity&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;strong&gt;Requirement&lt;&#x2F;strong&gt;: Implementation must match documentation.&lt;&#x2F;p&gt;
&lt;p&gt;The gap between documented and actual MCP behavior consumed significant debugging effort. Automated testing of documented behaviors would catch such discrepancies before they reach users.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;5-3-testing-in-real-contexts&quot;&gt;5.3 Testing in Real Contexts&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;strong&gt;Requirement&lt;&#x2F;strong&gt;: Test with actual platform constraints.&lt;&#x2F;p&gt;
&lt;p&gt;MCP access works in isolation testing but fails in plugin contexts. Integration tests must execute in production-like environments with actual scope boundaries.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;5-4-developer-experience-considerations&quot;&gt;5.4 Developer Experience Considerations&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;strong&gt;Observation&lt;&#x2F;strong&gt;: Complex deployment requirements discourage experimentation.&lt;&#x2F;p&gt;
&lt;p&gt;The three-location sync requirement, lack of hot reload, and strict schema validation add friction to the development cycle. Each constraint adds approximately 2-5 minutes per change cycle versus seconds for hot-reload systems.&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;Trade-off Analysis&lt;&#x2F;strong&gt;:&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Constraint&lt;&#x2F;th&gt;&lt;th&gt;Benefit&lt;&#x2F;th&gt;&lt;th&gt;Cost&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Three-location sync&lt;&#x2F;td&gt;&lt;td&gt;Version consistency&lt;&#x2F;td&gt;&lt;td&gt;Manual sync overhead&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;No hot reload&lt;&#x2F;td&gt;&lt;td&gt;State predictability&lt;&#x2F;td&gt;&lt;td&gt;Restart delay&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;Strict schema&lt;&#x2F;td&gt;&lt;td&gt;Early error detection&lt;&#x2F;td&gt;&lt;td&gt;Debugging difficulty&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. Conclusion&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;summary-of-contributions&quot;&gt;Summary of Contributions&lt;&#x2F;h3&gt;
&lt;p&gt;This empirical study of Claude Code plugin development reveals patterns applicable to multi-agent AI system design:&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;The Inheritance Paradox&lt;&#x2F;strong&gt;: Tool access inheritance models documented in specifications may not function across all scope boundaries. Explicit contracts prevent unexpected behavior.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Topology-Driven Architecture&lt;&#x2F;strong&gt;: Platform-enforced constraints (like single-level nesting) drive the emergence of specific patterns (hub-and-spoke) that may be superior to unconstrained designs.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Naming as Behavior&lt;&#x2F;strong&gt;: In AI agent systems, naming conventions carry functional implications beyond labeling.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;I&#x2F;O Separation&lt;&#x2F;strong&gt;: Separating I&#x2F;O from intelligence creates portable, testable, gracefully-degrading systems.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;h3 id=&quot;limitations&quot;&gt;Limitations&lt;&#x2F;h3&gt;
&lt;p&gt;This study is based on observations from a single platform (Claude Code) during a specific development period. Findings may not generalize to all agent frameworks. Additionally, platform behavior may change as bugs are resolved.&lt;&#x2F;p&gt;
&lt;h3 id=&quot;future-research-directions&quot;&gt;Future Research Directions&lt;&#x2F;h3&gt;
&lt;ol&gt;
&lt;li&gt;Comparative analysis of tool inheritance models across agent platforms&lt;&#x2F;li&gt;
&lt;li&gt;Formal specification of scope contracts for multi-agent systems&lt;&#x2F;li&gt;
&lt;li&gt;Empirical studies of naming-behavior coupling in other AI systems&lt;&#x2F;li&gt;
&lt;li&gt;Framework design patterns that explicitly leverage topology constraints&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;appendix-evidence-summary&quot;&gt;Appendix: Evidence Summary&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;github-issues-referenced&quot;&gt;GitHub Issues Referenced&lt;&#x2F;h3&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;Issue&lt;&#x2F;th&gt;&lt;th&gt;Description&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;Custom plugin agents can’t access MCP&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;#15810&lt;&#x2F;td&gt;&lt;td&gt;Subagents don’t inherit MCP from plugin-defined agents&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;#14496&lt;&#x2F;td&gt;&lt;td&gt;Inconsistent MCP access with complex prompts&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;#7296&lt;&#x2F;td&gt;&lt;td&gt;Scope inheritance failure for Task-launched agents&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;h3 id=&quot;research-metrics&quot;&gt;Research Metrics&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Observation Period&lt;&#x2F;strong&gt;: January 3-4, 2026&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Token Investment&lt;&#x2F;strong&gt;: ~265,000 tokens across sessions&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Distinct Observations&lt;&#x2F;strong&gt;: 13 documented findings&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Finding Types&lt;&#x2F;strong&gt;: 4 Decisions, 8 Discoveries, 1 Change&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;methodology&quot;&gt;Methodology&lt;&#x2F;h3&gt;
&lt;p&gt;Observations were collected through development sessions using the claude-mem persistent memory system. Each finding was cross-referenced against documentation and, where applicable, GitHub issue reports. Workarounds were developed through iterative experimentation.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;h2 id=&quot;get-the-plugin&quot;&gt;Get the Plugin&lt;&#x2F;h2&gt;
&lt;p&gt;The plugin discussed in this research is 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;Use the &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; tab to request features, report bugs, or discuss improvements.&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;&lt;strong&gt;Series Navigation:&lt;&#x2F;strong&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Part 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; — What the plugin does and how to use it&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Part 1&lt;&#x2F;strong&gt;: &lt;a href=&quot;&#x2F;posts&#x2F;claude-code-plugin-journey-part1&#x2F;&quot;&gt;The Hybrid Architecture Pattern&lt;&#x2F;a&gt; — Building the plugin, lessons learned&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Part 2&lt;&#x2F;strong&gt; (You are here): Research insights, patterns&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;hr &#x2F;&gt;
&lt;p&gt;&lt;em&gt;This article is based on empirical observations from developing the agent-team-creator Claude Code plugin. For practical implementation guidance, see Part 1.&lt;&#x2F;em&gt;&lt;&#x2F;p&gt;
</content>
        <summary type="html">The Inheritance Paradox: Tool Scoping in Multi-Agent Systems

This is Part 2 of a three-part series. See Part 0 for the plugin overview and installation, or Part 1 for the development journey.

Abstract: This article presents empirical observations from developing a Claude Code plugin, revealing fundamental patterns in multi-agent AI system design. Through documented development sessions, we identify three key findings: (1) implicit tool inheritance models consistently fail in practice, requiring explicit scope contracts; (2) platform-enforced topology constraints drive the emergence of specific architectural patterns; and (3) agent naming conventions become functional requirements in AI systems. These findings have implications for the design of agent frameworks beyond the specific platform studied.
…</summary>
        </entry>
</feed>
