<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: sly-the-fox</title>
    <description>The latest articles on Forem by sly-the-fox (@slythefox).</description>
    <link>https://forem.com/slythefox</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3815671%2F3fd5cb13-c933-4dd1-8f85-0642f3ddf9cb.png</url>
      <title>Forem: sly-the-fox</title>
      <link>https://forem.com/slythefox</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/slythefox"/>
    <language>en</language>
    <item>
      <title>The 5th Agent Orchestration Pattern: Market-Based Task Allocation</title>
      <dc:creator>sly-the-fox</dc:creator>
      <pubDate>Wed, 01 Apr 2026 20:16:30 +0000</pubDate>
      <link>https://forem.com/slythefox/the-5th-agent-orchestration-pattern-market-based-task-allocation-db0</link>
      <guid>https://forem.com/slythefox/the-5th-agent-orchestration-pattern-market-based-task-allocation-db0</guid>
      <description>&lt;p&gt;Most conversations about agent orchestration patterns settle on the same four: pipeline, supervisor, router, blackboard. Each solves coordination differently. Pipelines chain steps linearly. Supervisors centralize control. Routers classify and dispatch. Blackboards let agents coordinate through shared state without direct communication.&lt;/p&gt;

&lt;p&gt;These four cover a lot of ground. But there is a fifth pattern that comes from an older field, and it solves a problem the other four handle poorly: dynamic allocation across heterogeneous agents when cost matters.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Pattern: Auction-Based Task Allocation
&lt;/h2&gt;

&lt;p&gt;Instead of a supervisor deciding which agent handles a task, you let agents bid on it.&lt;/p&gt;

&lt;p&gt;The mechanism works like this. A task enters the system. It gets broadcast to all available agents. Each agent evaluates the task against its own capabilities, current load, and estimated cost, then submits a bid. An auction engine evaluates the bids and assigns the task to the winner.&lt;/p&gt;

&lt;p&gt;This is not a theoretical concept. The Consensus-Based Auction Algorithm (CBAA) and its extension, the Consensus-Based Bundling Algorithm (CBBA), have been used in multi-robot task allocation for over a decade. The research originated at CMU and the University of Maryland, primarily for coordinating robot fleets where each unit has different sensors, battery levels, and proximity to targets.&lt;/p&gt;

&lt;p&gt;The core idea transfers directly to LLM agent systems. Replace "battery level" with "context window utilization." Replace "sensor capability" with "tool access and specialization." Replace "proximity" with "relevant cached context." The allocation logic is the same.&lt;/p&gt;

&lt;h2&gt;
  
  
  How It Works in Practice
&lt;/h2&gt;

&lt;p&gt;A market-based orchestration layer has four phases:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Broadcast.&lt;/strong&gt; The system publishes an available task with metadata (type, estimated complexity, required capabilities, deadline).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Bid.&lt;/strong&gt; Each agent that can handle the task submits a bid. The bid includes a capability score (how well the agent matches the task requirements), a load factor (how busy the agent currently is), and a cost estimate (tokens, time, or whatever resource you are optimizing for).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Auction.&lt;/strong&gt; The auction engine scores bids using a weighted function. A simple version: &lt;code&gt;score = capability * (1 - load) / cost&lt;/code&gt;. More sophisticated versions factor in deadline urgency, agent track record, and bundle efficiency (assigning related tasks to the same agent to reduce context switching).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Assignment.&lt;/strong&gt; The winning agent gets the task. Other agents free their reserved capacity.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TaskAuction&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;agents&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;weights&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;agents&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;agents&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;weights&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;weights&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;capability&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;availability&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cost&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.2&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;broadcast&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;bids&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;agents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;can_handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="n"&gt;bids&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;agent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;capability&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assess_capability&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;load&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;current_load&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
                    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cost&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;estimate_cost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;bids&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;select_winner&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bids&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;scored&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;bid&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;bids&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;weights&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;capability&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;bid&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;capability&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
                &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;weights&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;availability&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;bid&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;load&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
                &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;weights&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cost&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;bid&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cost&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;scored&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bid&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="n"&gt;scored&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;reverse&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;scored&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;agent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;scored&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The interesting property is adaptiveness. When the agent pool changes (new agents come online, existing ones hit capacity), the allocation adjusts automatically. No one rewrites routing rules. The market handles it.&lt;/p&gt;

&lt;h2&gt;
  
  
  When This Pattern Fits
&lt;/h2&gt;

&lt;p&gt;Market-based orchestration works best in specific conditions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Heterogeneous agent pools.&lt;/strong&gt; If your agents have meaningfully different capabilities, costs, or specializations, the bidding mechanism surfaces the best match dynamically. A supervisor could do this too, but the supervisor needs to know about every agent's current state. The market distributes that knowledge.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Variable workloads.&lt;/strong&gt; When task volume and type shift unpredictably, static routing rules break. A market adjusts. During a spike of code review tasks, agents with code analysis capabilities naturally absorb more work because their capability scores are higher for those tasks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cost optimization matters.&lt;/strong&gt; If you are paying per token and different models have different price points, the auction mechanism can factor cost directly into allocation. A GPT-4 class agent might bid high capability but high cost. A smaller model bids lower capability but lower cost. The auction weights decide the tradeoff based on task priority.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Adaptive fallback.&lt;/strong&gt; A recent paper on the Agent Exchange concept describes a hybrid approach: use auction-based allocation when competition exists (multiple agents can handle the task), fall back to direct assignment when only one agent qualifies. This avoids the overhead of running auctions for tasks with obvious owners.&lt;/p&gt;

&lt;h2&gt;
  
  
  When This Pattern Does Not Fit
&lt;/h2&gt;

&lt;p&gt;The auction mechanism adds latency. Every task needs a broadcast, a bid collection window, and a scoring pass before work begins. For latency-sensitive workflows where you need a response in milliseconds, this overhead is disqualifying.&lt;/p&gt;

&lt;p&gt;Small agent counts make the mechanism pointless. If you have three agents with non-overlapping specializations, a simple router is faster and equally effective. The market shines when there are many potential handlers and the "best" choice depends on runtime conditions.&lt;/p&gt;

&lt;p&gt;Deterministic routing requirements also rule it out. If compliance or auditability demands that task type X always goes to agent Y, a market that might route it to agent Z based on load is the wrong pattern. Regulated industries often need predictable routing over optimal routing.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Bigger Picture
&lt;/h2&gt;

&lt;p&gt;What makes the market pattern interesting is that it distributes the coordination intelligence. In a supervisor pattern, the supervisor carries the full cognitive load of understanding every agent's state and capability. In a market pattern, each agent carries only the knowledge of its own state. The auction engine is stateless. It just scores bids.&lt;/p&gt;

&lt;p&gt;This mirrors a pattern from distributed systems design. The more you centralize decision-making, the more fragile the center becomes. Markets distribute decisions to the edges, where the relevant information already lives.&lt;/p&gt;

&lt;p&gt;The four established patterns (pipeline, supervisor, router, blackboard) handle most production use cases. But as agent pools grow larger and more heterogeneous, and as cost optimization becomes a real constraint rather than a nice-to-have, market-based allocation becomes worth considering. The robotics community figured this out years ago. The LLM agent community is starting to catch up.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I write about agent architecture and systems design on &lt;a href="https://thealignmentlayer.substack.com" rel="noopener noreferrer"&gt;Substack&lt;/a&gt;. Building &lt;a href="https://github.com/chaddjohnson/sigil" rel="noopener noreferrer"&gt;Sigil&lt;/a&gt;, a Python framework for structured LLM workflows.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>architecture</category>
      <category>distributedsystems</category>
    </item>
    <item>
      <title>Got My 39-Agent System Audited Live. Here's What the Maturity Scorecard Revealed.</title>
      <dc:creator>sly-the-fox</dc:creator>
      <pubDate>Fri, 27 Mar 2026 15:30:00 +0000</pubDate>
      <link>https://forem.com/slythefox/got-my-39-agent-system-audited-live-heres-what-the-maturity-scorecard-revealed-4nc1</link>
      <guid>https://forem.com/slythefox/got-my-39-agent-system-audited-live-heres-what-the-maturity-scorecard-revealed-4nc1</guid>
      <description>&lt;p&gt;Last night at AI Tinkerers, someone audited my multi-agent system in front of the room. Not a demo. Not a presentation. An actual architectural assessment using knowledge-graph analysis, scored against established maturity frameworks.&lt;/p&gt;

&lt;p&gt;The system has 39 specialized agents across five categories, defined governance protocols, six workflow types with 8-17 steps each, and a dedicated evolution loop for continuous improvement. I've been building it for months.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Audit
&lt;/h2&gt;

&lt;p&gt;Marcus Waldman ran his iConsult tool against the full architecture. The tool maps agent definitions, workflow structures, and coordination patterns into a knowledge graph, then scores them against patterns from Arsanjani and Bustos's work on agentic architectural patterns.&lt;/p&gt;

&lt;p&gt;Here's what came back:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Rating&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Coordination &amp;amp; Planning&lt;/td&gt;
&lt;td&gt;Established&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Explainability &amp;amp; Compliance&lt;/td&gt;
&lt;td&gt;Emerging&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Robustness &amp;amp; Fault Tolerance&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Not Started&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Human-Agent Interaction&lt;/td&gt;
&lt;td&gt;Emerging&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Agent-Level Capabilities&lt;/td&gt;
&lt;td&gt;Not Started&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;System-Level Infrastructure&lt;/td&gt;
&lt;td&gt;Not Started&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Continuous Improvement&lt;/td&gt;
&lt;td&gt;Emerging&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Failure chain coverage: 20%. One of five steps in the automated recovery chain existed. The rest were missing entirely.&lt;/p&gt;

&lt;p&gt;A system with 39 agents, a three-tier supervisor hierarchy, and dedicated auditor and sentinel agents scored "Not Started" on robustness. That is the gap most teams aren't talking about.&lt;/p&gt;

&lt;h2&gt;
  
  
  Arsanjani's 6 Levels of Agent Maturity
&lt;/h2&gt;

&lt;p&gt;Ali Arsanjani (Google Cloud) published a maturity model that maps where agent systems actually fall on a capability spectrum. Most of us think we're higher than we are.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Level 0: No Agents.&lt;/strong&gt; Traditional software. No autonomous components.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Level 1: Single Agent with Tools.&lt;/strong&gt; One LLM with function calling. This is where most "agentic" products actually live. The agent can use tools but has no planning, no memory beyond the conversation, and no coordination with other agents.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Level 2: Multi-Agent Coordination.&lt;/strong&gt; Multiple agents with defined roles and handoff patterns. Supervisor or router dispatches work. This is where the orchestration problem starts to bite.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Level 3: Autonomous Planning.&lt;/strong&gt; Agents can decompose tasks, create plans, and execute them with minimal human oversight. The system handles multi-step workflows without constant prompting.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Level 4: Adaptive Systems.&lt;/strong&gt; Agents learn from outcomes, adjust strategies, and improve over time. Self-evaluation loops. Performance metrics that feed back into behavior.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Level 5: Bureaucracy of Agents.&lt;/strong&gt; Dedicated oversight agents. Auditors. Inspectors. Governance structures that exist specifically to monitor and evaluate other agents. This is the level that sounds like overkill until you realize it's the only way to maintain reliability at scale.&lt;/p&gt;

&lt;p&gt;My system has governance agents. It has an auditor, a sentinel, an evaluator, and a coherence checker. On paper, it touches Level 5. In practice, the audit showed the governance layer is partially built but the infrastructure underneath it (automated recovery, dynamic registry, event bus) doesn't exist yet.&lt;/p&gt;

&lt;p&gt;You can have the org chart without the plumbing. The maturity model measures the plumbing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Majority Voting Fails
&lt;/h2&gt;

&lt;p&gt;There's a related finding from the AgentAuditor paper (USC, February 2026) that connects directly to this maturity problem.&lt;/p&gt;

&lt;p&gt;The standard approach to multi-agent reliability is majority voting. Run the same task through multiple agents, take the consensus answer. Sounds reasonable. It's also broken.&lt;/p&gt;

&lt;p&gt;The problem is correlated bias. When agents share the same training data and similar reasoning patterns, they don't produce independent votes. They converge on the same wrong answer. Majority voting fails for the same reason groupthink fails in organizations. More voices doesn't help when they all share the same blind spots.&lt;/p&gt;

&lt;p&gt;AgentAuditor's approach was to map reasoning trees and search for path divergences instead of counting votes. The result: 5% accuracy improvement over majority voting. Not because the individual agents were better, but because the auditing structure was better.&lt;/p&gt;

&lt;p&gt;This is exactly the gap the audit exposed in my system. I have a sentinel and an auditor, but they're watching for rule violations, not reasoning divergences. The governance layer checks process. It doesn't check whether agents are converging on the same blind spot. That's a different kind of auditing entirely.&lt;/p&gt;

&lt;p&gt;The lesson: you don't fix reliability by adding more agents. You fix it by adding structural auditing that can identify where reasoning paths diverge. It's a coordination architecture problem, not a scaling problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Numbers Behind the Hype
&lt;/h2&gt;

&lt;p&gt;Gartner reported a 1,445% surge in multi-agent inquiries. At the same time, they project 40% of agentic AI projects will be cancelled by 2027. Only about 130 out of thousands of vendors in the space are building real multi-agent capabilities.&lt;/p&gt;

&lt;p&gt;Deloitte estimates the market at $8.5B in 2026, growing to $35-45B by 2030. But those numbers assume proper orchestration. Without it, you get the 40% cancellation rate.&lt;/p&gt;

&lt;p&gt;The demand-reality gap isn't about model capability. GPT-4, Claude, Gemini can all handle complex reasoning. The bottleneck is orchestration maturity. How do you coordinate agents? How do you detect failures? How do you recover? How do you know your system is actually working as designed?&lt;/p&gt;

&lt;p&gt;Most teams skip these questions because they're not as exciting as adding another agent.&lt;/p&gt;

&lt;h2&gt;
  
  
  Self-Assessment
&lt;/h2&gt;

&lt;p&gt;If you're building a multi-agent system, here are the questions worth asking:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;What level are you actually at?&lt;/strong&gt; Not what your architecture diagram suggests. What does the running system demonstrate?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Can your system detect its own failures?&lt;/strong&gt; Not log them. Detect them in real time and route them to recovery logic.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;How do you audit agent behavior?&lt;/strong&gt; If the answer is "we read the logs," you're at Level 1 maturity for observability regardless of how many agents you have.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;What happens when an agent produces wrong output?&lt;/strong&gt; Does the system catch it? Or does it propagate through the pipeline?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Is your governance layer structural or decorative?&lt;/strong&gt; Having an "auditor agent" in the config is different from having an auditor agent that actually interrupts workflows when quality drops.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I had to answer these questions publicly last night. That's the value of external assessment. Your own evaluation will always be generous.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'm Doing About It
&lt;/h2&gt;

&lt;p&gt;The audit produced a concrete implementation plan. Phase 1 is the robustness gap: circuit breakers, retry policies, health checks, and a failure chain that actually covers all five steps. The coordination score was reasonable because the supervisor architecture and workflow definitions are solid. But coordination without robustness is a system that works until it doesn't, and when it fails, there's nothing to catch it.&lt;/p&gt;

&lt;p&gt;The maturity model isn't a checklist to complete. It's a map for knowing where you actually are and what to build next. The frameworks exist. The assessment tools are getting better. The question is whether you're willing to run the audit.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I build &lt;a href="https://github.com/chaddoncooper/sigil" rel="noopener noreferrer"&gt;Sigil&lt;/a&gt;, an open-source symbolic computation framework, and write about systems architecture on &lt;a href="https://chaddharrison.substack.com" rel="noopener noreferrer"&gt;Substack&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>multiagent</category>
      <category>architecture</category>
      <category>agents</category>
    </item>
    <item>
      <title>MCP's Topology Is Changing Under Your Feet</title>
      <dc:creator>sly-the-fox</dc:creator>
      <pubDate>Wed, 25 Mar 2026 19:51:05 +0000</pubDate>
      <link>https://forem.com/slythefox/mcps-topology-is-changing-under-your-feet-1e9g</link>
      <guid>https://forem.com/slythefox/mcps-topology-is-changing-under-your-feet-1e9g</guid>
      <description>&lt;h1&gt;
  
  
  MCP's Topology Is Changing Under Your Feet
&lt;/h1&gt;

&lt;p&gt;Most developers think about MCP as a flat topology. One host application in the center, MCP servers radiating outward like spokes on a wheel. Claude Desktop talks to a filesystem server, a database server, a GitHub server. Clean and simple.&lt;/p&gt;

&lt;p&gt;That model was roughly accurate in 2024. It's already wrong.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Flat Model Breaks
&lt;/h2&gt;

&lt;p&gt;The hub-and-spoke model assumes the host application handles everything: authentication, authorization, audit logging, rate limiting, credential management. For a developer running three MCP servers on their laptop, that works fine. The host is Claude Desktop or Cursor, and the blast radius of a misconfiguration is one person's machine.&lt;/p&gt;

&lt;p&gt;Scale that to a team of fifty engineers, each connecting to shared MCP servers that access production databases, internal APIs, and cloud infrastructure. Now every host application is independently managing credentials. There's no centralized audit trail. No unified access policy. No way to revoke a compromised server's access across all clients simultaneously.&lt;/p&gt;

&lt;p&gt;This is the same problem that drove the API gateway evolution in the early 2010s. Individual services talking directly to clients created an unmanageable security surface. The solution was inserting a gateway layer that centralized cross-cutting concerns.&lt;/p&gt;

&lt;p&gt;MCP is following the same path.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Gateway Tier
&lt;/h2&gt;

&lt;p&gt;In enterprise deployments, a gateway tier has inserted itself between host applications and MCP servers. The topology is no longer flat. It's two-tier: host to gateway to servers.&lt;/p&gt;

&lt;p&gt;Three products illustrate this pattern:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stacklok&lt;/strong&gt; separates identity, policy, and observability into discrete layers. Their architecture treats the gateway as the security boundary rather than the individual server. Authentication happens once at the gateway. Policy enforcement is centralized. Every tool invocation is logged with full context: who called what, when, with what parameters, and what the response contained.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;MintMCP Gateway&lt;/strong&gt; provides unified authentication across multiple MCP servers. Instead of each server managing its own credentials, the gateway handles OAuth flows, API key rotation, and session management centrally. The MCP servers behind it become stateless translation layers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Traefik Hub&lt;/strong&gt; implements what they call a "Triple Gate" pattern: defense-in-depth where traffic passes through authentication, authorization, and content inspection before reaching the MCP server. Each gate operates independently, so a failure in one doesn't compromise the others.&lt;/p&gt;

&lt;p&gt;The pattern across all three is the same. Move cross-cutting concerns up a layer. Let MCP servers focus on protocol translation. Let the gateway handle trust.&lt;/p&gt;

&lt;h2&gt;
  
  
  What This Looks Like Architecturally
&lt;/h2&gt;

&lt;p&gt;In the flat model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Host App → MCP Server A (with creds for Service A)
         → MCP Server B (with creds for Service B)
         → MCP Server C (with creds for Service C)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each server manages its own credentials. Each server is its own security boundary. The host trusts all of them equally.&lt;/p&gt;

&lt;p&gt;In the gateway model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Host App → Gateway (auth, audit, rate limiting, policy)
              → MCP Server A (stateless, no creds)
              → MCP Server B (stateless, no creds)
              → MCP Server C (stateless, no creds)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Credentials live in the gateway. Servers are stateless translators. The gateway enforces isolation between servers. A compromised server can't access another server's resources because it never had the credentials in the first place.&lt;/p&gt;

&lt;p&gt;This also changes how you handle the cross-server exfiltration problem. In the flat model, if a malicious MCP server instructs the model to pass data from a trusted server through its own tools, there's nothing stopping it. Both servers are equally trusted by the host. In the gateway model, the gateway can enforce that data from Server A never flows through Server B's tools. The trust boundary is at the gateway, not at the host's goodwill.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Mirrors API Gateway History
&lt;/h2&gt;

&lt;p&gt;If you were building APIs in 2012, you went through this exact transition. Services exposed endpoints directly. Clients called them. Authentication was per-service. Logging was per-service. Rate limiting was per-service.&lt;/p&gt;

&lt;p&gt;Then Kong, Apigee, and AWS API Gateway appeared. Cross-cutting concerns moved to a centralized layer. Services got simpler. Security got more manageable. Observability became possible at a system level instead of requiring instrumentation in every individual service.&lt;/p&gt;

&lt;p&gt;MCP is replaying this cycle in compressed time. The flat model worked when MCP was a developer tool. As it becomes infrastructure, the gateway tier isn't optional. It's the natural architectural response to the same pressures that created API gateways a decade ago.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Changes for You
&lt;/h2&gt;

&lt;p&gt;If you're building MCP integrations today, the practical implications are:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Design your MCP servers to be stateless.&lt;/strong&gt; Don't bake credentials into the server. Accept them from the environment or a gateway. This makes your server portable across deployment topologies.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Separate protocol translation from business logic.&lt;/strong&gt; Your MCP server should translate between the MCP protocol and your existing service APIs. Business logic stays in the services. The thinner the MCP layer, the easier it is to slot a gateway in front of it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Anticipate centralized auth.&lt;/strong&gt; If you're building auth into individual MCP servers right now, you'll probably rip it out later when a gateway takes over that responsibility. Build for it, but build it as a pluggable layer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Instrument observability now, not later.&lt;/strong&gt; Gateway-level logging captures every tool invocation across all servers. If you're not logging at the MCP layer today, you have no visibility into what your agents are actually doing with the tools you gave them.&lt;/p&gt;

&lt;p&gt;The MCP specification's 2026 roadmap includes middleware standardization, which will formalize how gateways interact with the protocol. The ad-hoc gateway tier is becoming a first-class architectural pattern.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Bigger Picture
&lt;/h2&gt;

&lt;p&gt;MCP's topology evolution tells you something about where AI infrastructure is heading. The initial wave of any protocol is flat and simple. The production wave adds layers. The mature wave standardizes those layers.&lt;/p&gt;

&lt;p&gt;We're at the transition between wave one and wave two. The teams building MCP servers as thin, stateless translators with gateway-ready architecture will have an easier time in wave three. The teams baking everything into monolithic servers are building for a topology that's already changing under them.&lt;/p&gt;

&lt;p&gt;The protocol won the standardization battle. The architecture battle is just starting.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I'm building with MCP daily as part of Mega-OS (39-agent operating system). This post is part of a series on MCP's actual architecture vs. its perceived architecture. The full analysis, including security model gaps and the context window problem, is on &lt;a href="https://thealignmentlayer.substack.com" rel="noopener noreferrer"&gt;The Alignment Layer&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>mcp</category>
      <category>architecture</category>
      <category>security</category>
      <category>ai</category>
    </item>
    <item>
      <title>Your Multi-Agent System Has a Routing Problem</title>
      <dc:creator>sly-the-fox</dc:creator>
      <pubDate>Mon, 23 Mar 2026 02:07:22 +0000</pubDate>
      <link>https://forem.com/slythefox/your-multi-agent-system-has-a-routing-problem-5bc9</link>
      <guid>https://forem.com/slythefox/your-multi-agent-system-has-a-routing-problem-5bc9</guid>
      <description>&lt;p&gt;Five agents. Twenty possible connections. Ten agents? Ninety. The math is simple and the consequences are brutal.&lt;/p&gt;

&lt;p&gt;Most multi-agent systems start with a reasonable architecture. Two or three agents with clear responsibilities. The orchestrator routes work. Everything makes sense. Then you add a fourth agent. A fifth. A specialized summarizer. A governance layer. Suddenly every agent can reach every other agent, and nobody's drawn a map of which paths should actually exist.&lt;/p&gt;

&lt;p&gt;This is the N-squared coordination problem. And it's the architectural debt that kills multi-agent systems before they ever reach production.&lt;/p&gt;

&lt;h2&gt;
  
  
  The group chat anti-pattern
&lt;/h2&gt;

&lt;p&gt;The default in most agent frameworks is full connectivity. Any agent can call any tool, read any state, trigger any other agent. It feels flexible. It's actually fragile.&lt;/p&gt;

&lt;p&gt;When Agent A can invoke Agent B, C, D, and E directly, you've created implicit dependencies that aren't visible in your architecture diagram (assuming you have one). When something breaks, the failure could have originated from any of those paths. Debugging becomes combinatorial.&lt;/p&gt;

&lt;p&gt;The parallel in traditional software engineering is obvious. We stopped building monoliths where every module calls every other module. We drew service boundaries. We defined interfaces. We made coupling explicit and limited.&lt;/p&gt;

&lt;p&gt;Multi-agent systems need the same treatment, but most builders skip it because the framework doesn't enforce it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Trust boundaries as architecture
&lt;/h2&gt;

&lt;p&gt;A trust boundary is a line you draw between agents that limits what they can access and who they can reach. It's not about security in the traditional sense (though it helps). It's about making the system legible.&lt;/p&gt;

&lt;p&gt;Here's what this looks like in practice:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Without trust boundaries — any agent reaches anything
&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AgentOrchestrator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# Pick the "best" agent and let it loose
&lt;/span&gt;        &lt;span class="n"&gt;agent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select_agent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;full_context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# With trust boundaries — explicit routing and scoped access
&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;BoundedOrchestrator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;boundaries&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;summarizer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;can_read&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;documents&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;notes&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;can_reach&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;editor&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cannot_reach&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;database_writer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;auth_manager&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;database_writer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;can_read&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;validated_records&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;can_reach&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;auditor&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cannot_reach&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;summarizer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;external_api&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;source_agent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;target_agent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;rules&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;boundaries&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source_agent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;target_agent&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;rules&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;cannot_reach&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[]):&lt;/span&gt;
            &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="nc"&gt;BoundaryViolation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;source_agent&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; cannot reach &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;target_agent&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c1"&gt;# Scoped context — only what this agent is allowed to see
&lt;/span&gt;        &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scoped_context&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source_agent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rules&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;can_read&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;agents&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;target_agent&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The difference isn't complexity. It's clarity. The bounded version makes every routing decision explicit. When something breaks, you know exactly which paths were available and which one failed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three patterns that work
&lt;/h2&gt;

&lt;p&gt;After building systems with 30+ agents, three routing patterns consistently hold up:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Hub-and-spoke&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A central router handles all inter-agent communication. Agents never talk to each other directly. This is the simplest model and works well up to about 15 agents. The router becomes a bottleneck at scale, but the traceability is excellent.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Hierarchical routing&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Agents are organized into groups (governance, technical, knowledge) with a group coordinator. Agents within a group can communicate freely, but cross-group communication goes through the coordinators. This scales better and naturally creates bounded contexts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Pipeline with side channels&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Work flows through a defined sequence (plan, execute, review, document), but specific agents can reach specific others outside the pipeline for scoped queries. The pipeline is the primary path; side channels are explicit exceptions with documented justification.&lt;/p&gt;

&lt;p&gt;The worst pattern is the implicit mesh, where any agent can invoke any other agent through shared state or direct calls. It works until it doesn't, and when it breaks, the failure surface is every connection in the system.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scoped context matters as much as scoped access
&lt;/h2&gt;

&lt;p&gt;Trust boundaries aren't just about who can call whom. They're about what each agent can see. A summarizer working on meeting notes doesn't need access to your financial records. A code reviewer doesn't need to see customer PII.&lt;/p&gt;

&lt;p&gt;When you scope the context each agent receives, two things improve immediately:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Agents perform better.&lt;/strong&gt; Less noise in the context means more focused output. An agent that receives only the documents it needs produces better summaries than one drowning in the full system state.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Failures are contained.&lt;/strong&gt; If an agent hallucinates or makes a bad decision, the blast radius is limited to what it could access. A summarizer with access to everything can corrupt everything. A summarizer scoped to documents can only affect documents.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The routing question
&lt;/h2&gt;

&lt;p&gt;The next time you add an agent to your system, ask three questions before writing any code:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Who can this agent reach?&lt;/strong&gt; List the specific agents it's allowed to invoke or send data to.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What can this agent see?&lt;/strong&gt; Define the scoped context it receives, not the full system state.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Who can reach this agent?&lt;/strong&gt; Inbound access matters as much as outbound. An agent that any other agent can trigger is an implicit dependency for the entire system.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you can't answer these questions, the agent doesn't have an architecture. It has a hope.&lt;/p&gt;

&lt;p&gt;The pattern is consistent across every well-designed system, from microservices to operating systems to multi-agent AI. Constraints don't limit capability. They make capability legible. And legibility is what lets you debug, scale, and trust the system you're building.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Building trust infrastructure for AI agents. Follow for weekly patterns on agent architecture, governance, and the systems underneath.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Try Sigil: &lt;a href="https://github.com/chaddhq/sigil" rel="noopener noreferrer"&gt;github.com/chaddhq/sigil&lt;/a&gt; | Subscribe: &lt;a href="https://thealignmentlayer.substack.com" rel="noopener noreferrer"&gt;The Alignment Layer&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>architecture</category>
      <category>python</category>
    </item>
    <item>
      <title>Your Multi-Agent System Is a Black Box You Built Yourself</title>
      <dc:creator>sly-the-fox</dc:creator>
      <pubDate>Sat, 21 Mar 2026 22:45:46 +0000</pubDate>
      <link>https://forem.com/slythefox/your-multi-agent-system-is-a-black-box-you-built-yourself-3do1</link>
      <guid>https://forem.com/slythefox/your-multi-agent-system-is-a-black-box-you-built-yourself-3do1</guid>
      <description>&lt;p&gt;Everyone building multi-agent systems is focused on making agents smarter. Nobody talks about what happens when your agents are smart enough but your state files are three days stale.&lt;/p&gt;

&lt;p&gt;I run 39 agents daily. The system that breaks isn't the one with dumb agents. It's the one where nobody can tell what the agents were looking at when they made their decisions. You built the agents, you defined their roles, you wired the routing. But when the system produces a result, can you trace the reasoning chain? Can you tell what Agent 3 decided, what context it received, what it chose to ignore?&lt;/p&gt;

&lt;p&gt;Probably not. And that invisible middle is where your worst bugs live.&lt;/p&gt;

&lt;h2&gt;
  
  
  Logging is not observability
&lt;/h2&gt;

&lt;p&gt;The first instinct is to add logging. Log every agent invocation, every tool call, every response. Some frameworks do this by default. You end up with thousands of lines per task, and the signal-to-noise ratio approaches zero.&lt;/p&gt;

&lt;p&gt;Logging tells you what happened. Observability tells you why it happened. The difference matters because in a multi-agent system, the "what" is usually obvious. Agent A called Agent B. Agent B produced a summary. Agent C made a decision based on that summary. The "why" is where things get interesting.&lt;/p&gt;

&lt;p&gt;Why did Agent B summarize the document that way? What context did it receive? Was there information it should have seen but didn't? When Agent C made its decision, was it responding to the actual document or to Agent B's interpretation of the document?&lt;/p&gt;

&lt;p&gt;These questions can't be answered with log lines.&lt;/p&gt;

&lt;h2&gt;
  
  
  The confident wrong answer
&lt;/h2&gt;

&lt;p&gt;The worst failure mode in a multi-agent system isn't a crash. Crashes are loud. You notice them. You fix them. The worst failure mode is the confident wrong answer.&lt;/p&gt;

&lt;p&gt;Agent A retrieves the right documents. Agent B summarizes them but subtly mischaracterizes one key point. Agent C makes a decision based on that summary. Agent D formats the output beautifully. The final result looks correct, reads professionally, and is wrong in a way that nobody catches until a human notices the downstream damage days later.&lt;/p&gt;

&lt;p&gt;This failure mode exists because each agent in the chain operated correctly within its own scope. Agent B didn't fail. It summarized. The summarization just lost a critical nuance. And since nobody is watching the intermediate representations, the error propagates silently through the system.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# What most systems track
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;agent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;summarizer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;input_tokens&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;4200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;output_tokens&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;380&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;latency_ms&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1240&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;success&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# What observability actually requires
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;agent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;summarizer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;task_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;review-q1-financials&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;input_context&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;documents&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;q1-report.pdf&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;budget-variance.csv&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;scoped_to&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;financial_data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;excluded&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;employee_records&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;reasoning_trace&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;key_points_extracted&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;points_included_in_summary&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;points_omitted&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Q1 variance exceeded threshold by 12%&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Vendor contract renewal pending&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;omission_reason&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;below relevance threshold (0.6)&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;downstream_consumers&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;decision_agent&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;audit_trail&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;confidence&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.82&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first record tells you the agent ran. The second tells you what it thought it was doing. That difference is the entire gap between debugging and guessing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three layers of agent observability
&lt;/h2&gt;

&lt;p&gt;I've been running a 39-agent system for a few months now. Three observability layers consistently matter:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Context tracing&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For every agent invocation, capture what context the agent received, not just what it produced. This includes scoped documents, upstream agent outputs, and any system state it had access to. When something goes wrong, the first question is always "what did this agent actually see?" Without context tracing, you're reconstructing the answer from logs and hope.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Decision boundaries&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Agents make decisions. Summarizers decide what to include and what to omit. Routers decide which agent handles a task. Reviewers decide whether work passes or fails. For each decision point, capture the inputs to the decision, the decision itself, and the threshold or reasoning that produced it. This turns opaque agent behavior into auditable decision records.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Propagation tracking&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When Agent B's output becomes Agent C's input, track that lineage explicitly. Not just "B ran before C," but "C's context included B's output, specifically these fields." When a confident wrong answer emerges at the end of a chain, propagation tracking lets you walk backward through the chain and find exactly where the signal degraded.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementation without overhead
&lt;/h2&gt;

&lt;p&gt;The practical concern is always performance. Adding observability shouldn't double your latency or token costs. Three approaches that keep overhead minimal:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Structured metadata, not full traces.&lt;/strong&gt; You don't need to capture every token. Capture the decision-relevant metadata: what context was scoped, what was included vs. excluded, what threshold was applied. This is typically 5-10% of the full trace size.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sampling for healthy paths.&lt;/strong&gt; Trace 100% of failures and anomalies. Sample 10-20% of successful paths. You'll catch degradation patterns without drowning in data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Async emission.&lt;/strong&gt; Don't block agent execution to write observability data. Emit events asynchronously to a separate store. The agent keeps working. The trace data arrives slightly behind, which is fine because you're not reading it in real time anyway. You're reading it when something goes wrong.&lt;/p&gt;

&lt;h2&gt;
  
  
  The observability question
&lt;/h2&gt;

&lt;p&gt;Before you add another agent to your system, try answering these questions about the agents you already have:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;When Agent B summarizes a document, can you see what it omitted and why?&lt;/li&gt;
&lt;li&gt;When the final output is wrong, can you trace backward to the specific agent that introduced the error?&lt;/li&gt;
&lt;li&gt;Can you tell the difference between an agent that failed and an agent that succeeded at the wrong task?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you can't answer these, you're operating a black box. The fact that you built the box doesn't mean you can see inside it.&lt;/p&gt;

&lt;p&gt;The pattern holds across every complex system. Capability without observability is a liability. If you can't watch your agents think, you're just waiting for the confident wrong answer to find its way to production.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I build and operate multi-agent systems daily. Writing about what breaks and what works at &lt;a href="https://thealignmentlayer.substack.com" rel="noopener noreferrer"&gt;The Alignment Layer&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Sigil (cryptographic audit trails for AI agents): &lt;a href="https://github.com/sly-the-fox/sigil" rel="noopener noreferrer"&gt;github.com/sly-the-fox/sigil&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>architecture</category>
      <category>observability</category>
    </item>
    <item>
      <title>Your Multi-Agent System Has an Identity Problem</title>
      <dc:creator>sly-the-fox</dc:creator>
      <pubDate>Fri, 20 Mar 2026 00:19:04 +0000</pubDate>
      <link>https://forem.com/slythefox/your-multi-agent-system-has-an-identity-problem-323h</link>
      <guid>https://forem.com/slythefox/your-multi-agent-system-has-an-identity-problem-323h</guid>
      <description>&lt;p&gt;You know what happened. You don't know who did it.&lt;/p&gt;

&lt;p&gt;That's the state of most multi-agent systems right now. Five agents process the same request. Three of them write to shared state. Something goes wrong. The logs say "task completed successfully." Every agent claims innocence. Nobody can prove anything.&lt;/p&gt;

&lt;p&gt;This isn't a hypothetical. It's the default.&lt;/p&gt;

&lt;h2&gt;
  
  
  The anonymous agent problem
&lt;/h2&gt;

&lt;p&gt;Most agent frameworks treat agents as interchangeable workers. Agent A calls a tool. Agent B reads the result. The orchestrator routes work based on capability, not identity. Logs capture timestamps and tool calls, but authorship is an afterthought.&lt;/p&gt;

&lt;p&gt;This works fine when you have two agents running a simple pipeline. It falls apart the moment you have five or more agents with overlapping access to shared resources. The question stops being "what happened" and becomes "who did this, and can they prove it?"&lt;/p&gt;

&lt;p&gt;Traditional logging doesn't answer that question. Logs are mutable. They're written by the system itself. If an agent can write to state, it can (in theory) write to logs. There's no separation between the actor and the record of the action.&lt;/p&gt;

&lt;h2&gt;
  
  
  Identity as infrastructure
&lt;/h2&gt;

&lt;p&gt;In distributed systems, identity isn't a feature. It's infrastructure. You can't build authorization without it. You can't build audit trails without it. You can't build trust without it.&lt;/p&gt;

&lt;p&gt;The same principle applies to multi-agent systems, but most builders skip it entirely. They jump straight to permissions ("Agent A can read, Agent B can write") without first establishing a verifiable identity layer underneath.&lt;/p&gt;

&lt;p&gt;Here's what that looks like in practice:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sigil&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Notary&lt;/span&gt;

&lt;span class="c1"&gt;# Each agent gets its own signing identity
&lt;/span&gt;&lt;span class="n"&gt;notary&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Notary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;agent_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data-processor&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Every action produces a signed attestation
&lt;/span&gt;&lt;span class="n"&gt;attestation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;notary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;write&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;shared_state.json&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;detail&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;field&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;revenue&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;old&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;new&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;15000&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# The attestation is cryptographically bound to this specific agent
&lt;/span&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attestation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;agent_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;    &lt;span class="c1"&gt;# "data-processor"
&lt;/span&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attestation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;# Ed25519 signature
&lt;/span&gt;&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attestation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chain_hash&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Links to this agent's previous action
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three things happen here that don't happen with standard logging:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The action is signed.&lt;/strong&gt; The attestation is cryptographically bound to the agent that created it. You can verify authorship independently.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The chain is hash-linked.&lt;/strong&gt; Each attestation references the previous one for that agent. You can detect gaps, deletions, or insertions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Identity is the primitive.&lt;/strong&gt; The agent doesn't just perform an action. It claims the action under its own verifiable identity.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Why this matters now
&lt;/h2&gt;

&lt;p&gt;The EU AI Act is starting to ask questions about AI system traceability. SOC 2 auditors are starting to ask how AI-generated changes are attributed. Regulated industries need to demonstrate that automated actions can be traced to specific system components.&lt;/p&gt;

&lt;p&gt;"The AI did it" isn't going to satisfy an auditor. "Agent &lt;code&gt;data-processor&lt;/code&gt; signed this action at 14:32:07, and here's the cryptographic proof" might.&lt;/p&gt;

&lt;p&gt;But compliance isn't even the most practical reason. The most practical reason is debugging. When three agents touch the same data and the result is wrong, verifiable identity tells you exactly where to look. No guessing. No "it must have been the summarizer." Proof.&lt;/p&gt;

&lt;h2&gt;
  
  
  The identity stack
&lt;/h2&gt;

&lt;p&gt;When you start thinking about agent identity as infrastructure, a natural stack emerges:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Identity&lt;/strong&gt; — every agent has a unique, verifiable identity (cryptographic key pair)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Attribution&lt;/strong&gt; — every action is signed by the agent that performed it&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Continuity&lt;/strong&gt; — each agent maintains a hash chain linking all its actions in sequence&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verification&lt;/strong&gt; — any observer can verify an attestation without trusting the system that created it&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Most systems have none of these layers. Some have basic attribution through logging. Almost none have continuity or independent verification.&lt;/p&gt;

&lt;p&gt;The pattern is the same one that made version control, TLS certificates, and blockchain valuable. Not the hype around them, but the underlying principle: when you can verify authorship without trusting the author, you can build systems that scale trust.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting started
&lt;/h2&gt;

&lt;p&gt;If you want to add identity to your agent system, the simplest starting point is &lt;a href="https://github.com/chaddhq/sigil" rel="noopener noreferrer"&gt;Sigil&lt;/a&gt; (&lt;code&gt;pip install sigil-notary&lt;/code&gt;). It gives each agent a signing identity, hash-chains their actions, and produces attestations you can verify offline.&lt;/p&gt;

&lt;p&gt;But the principle matters more than the tool. Whatever you build, start with identity. Not permissions. Not logging. Identity. Because everything else in the trust stack depends on knowing, with certainty, who did what.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Building trust infrastructure for AI agents. Follow for weekly patterns on agent architecture, governance, and the systems underneath.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Try Sigil: &lt;a href="https://github.com/chaddhq/sigil" rel="noopener noreferrer"&gt;github.com/chaddhq/sigil&lt;/a&gt; | Subscribe: &lt;a href="https://thealignmentlayer.substack.com" rel="noopener noreferrer"&gt;The Alignment Layer&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>security</category>
      <category>python</category>
    </item>
    <item>
      <title>Your AI Agents Need an Accountability Layer</title>
      <dc:creator>sly-the-fox</dc:creator>
      <pubDate>Thu, 19 Mar 2026 02:44:25 +0000</pubDate>
      <link>https://forem.com/slythefox/your-ai-agents-need-an-accountability-layer-267p</link>
      <guid>https://forem.com/slythefox/your-ai-agents-need-an-accountability-layer-267p</guid>
      <description>&lt;p&gt;You shipped a multi-agent system. Agents route tasks, process data, produce outputs. It works. Stakeholders are happy.&lt;/p&gt;

&lt;p&gt;Then someone from compliance shows up.&lt;/p&gt;

&lt;p&gt;"Which agent made this decision? What data did it have access to? Can you prove nothing was modified after the fact?"&lt;/p&gt;

&lt;p&gt;You check your logs. They're there. But they're mutable. Any process with write access could have altered them. You have observation, not evidence. That distinction is about to matter more than most teams realize.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Accountability Gap
&lt;/h2&gt;

&lt;p&gt;Most agent systems have extensive logging. Print statements, structured JSON, maybe a centralized log aggregator. That covers observability.&lt;/p&gt;

&lt;p&gt;Accountability is a different question. Can you &lt;em&gt;prove&lt;/em&gt; what happened? Can you demonstrate that the record is complete, unaltered, and attributable to a specific actor?&lt;/p&gt;

&lt;p&gt;Traditional logging fails this test for three reasons:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Mutability.&lt;/strong&gt; Logs can be edited, truncated, or deleted. If an agent (or a compromised process) modifies a log entry, there's no cryptographic evidence of the change.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Attribution.&lt;/strong&gt; Log entries say "Agent X did Y" but there's no signature proving Agent X actually produced that entry. Any process writing to the same log can impersonate any agent.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ordering.&lt;/strong&gt; Timestamps can be spoofed. Without a hash chain, there's no way to prove event A happened before event B, or that no events were inserted between them.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What Accountability Actually Requires
&lt;/h2&gt;

&lt;p&gt;A proper accountability layer needs three properties:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Identity.&lt;/strong&gt; Each agent has a cryptographic key pair. When it acts, it signs the record with its private key. The signature is verifiable by anyone with the public key. No impersonation possible.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Integrity.&lt;/strong&gt; Each record includes a hash of the previous record. This creates a chain where altering any entry invalidates every subsequent hash. Tampering becomes detectable, not just unlikely.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sequence.&lt;/strong&gt; The hash chain provides a provable ordering. Event 47 references event 46's hash. You can reconstruct the entire sequence and verify nothing was inserted, deleted, or reordered.&lt;/p&gt;

&lt;p&gt;Here's what that looks like in practice:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;hashlib&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;timezone&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_attestation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;agent_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prev_hash&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Create a hash-chained attestation record.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;record&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;agent_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;agent_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;action&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;timestamp&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;timezone&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;utc&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;isoformat&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;prev_hash&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;prev_hash&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;# Hash the canonical JSON representation
&lt;/span&gt;    &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sort_keys&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;hash&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;hashlib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sha256&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;hexdigest&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt;

&lt;span class="c1"&gt;# Build a chain
&lt;/span&gt;&lt;span class="n"&gt;chain&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="n"&gt;prev&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;genesis&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="n"&gt;chain&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;create_attestation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;researcher&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;fetch_data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sources&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;prev&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;prev&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;chain&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;hash&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;chain&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;create_attestation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;analyst&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;process&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rows&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1420&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;prev&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="n"&gt;prev&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;chain&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;hash&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="n"&gt;chain&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;create_attestation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;writer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;generate_report&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;words&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2100&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;prev&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each record in this chain references the hash of the previous one. Alter any field in any record, and the chain breaks from that point forward. This is the same principle behind blockchain and git, applied to agent operations.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Compliance Pressure Is Real
&lt;/h2&gt;

&lt;p&gt;The EU AI Act requires audit trails for high-risk AI systems. SOC 2 auditors are starting to ask about AI governance practices. Even if your system isn't in a regulated industry today, the direction is clear: organizations deploying AI agents will need to demonstrate accountability, not just observability.&lt;/p&gt;

&lt;p&gt;The teams building this infrastructure now will be ahead when the requirements formalize. The teams retrofitting it later will be doing it under pressure, with production systems that were never designed for it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding Signatures
&lt;/h2&gt;

&lt;p&gt;Hash chains prove integrity and ordering. Cryptographic signatures prove identity. Combining both:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;cryptography.hazmat.primitives.asymmetric.ed25519&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Ed25519PrivateKey&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;sign_attestation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;dict&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;private_key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Ed25519PrivateKey&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Sign an attestation with the agent&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s private key.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;record&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sort_keys&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;private_key&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each agent holds its own key pair. The signature on each record is proof that the specific agent produced it. You can verify the signature with the agent's public key without needing access to the private key.&lt;/p&gt;

&lt;p&gt;This moves you from "the log says Agent X did this" to "Agent X cryptographically signed this record, and the hash chain proves it wasn't altered afterward."&lt;/p&gt;

&lt;h2&gt;
  
  
  Where This Fits in Your Stack
&lt;/h2&gt;

&lt;p&gt;An accountability layer sits between your agents and your log storage. Agents produce attestations instead of (or in addition to) log entries. The attestation chain is append-only and verifiable.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Agent → Attestation (sign + hash-chain) → Storage
                                            ↓
                              Verification (any time, by anyone)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This doesn't replace your existing logging. It transforms observations into evidence.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;p&gt;If you're building multi-agent systems and want to add cryptographic accountability:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Assign each agent an Ed25519 key pair&lt;/strong&gt; at initialization.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sign every significant action&lt;/strong&gt; (task completion, data access, routing decisions).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hash-chain the records&lt;/strong&gt; so ordering and completeness are verifiable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Store the chain immutably&lt;/strong&gt; (append-only file, database with write-only access, or dedicated service).&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://github.com/chaddbharrison/sigil" rel="noopener noreferrer"&gt;Sigil&lt;/a&gt; implements this pattern as a Python library with MCP integration. &lt;code&gt;pip install sigil-notary&lt;/code&gt; if you want to skip the build-from-scratch step.&lt;/p&gt;

&lt;p&gt;The audit trail your compliance team will eventually ask for is cheaper to build now than to retrofit later. The agents doing the work should be producing the evidence that they did it correctly.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>security</category>
      <category>python</category>
    </item>
    <item>
      <title>Your Multi-Agent System Has a Memory Problem</title>
      <dc:creator>sly-the-fox</dc:creator>
      <pubDate>Mon, 16 Mar 2026 22:11:47 +0000</pubDate>
      <link>https://forem.com/slythefox/your-multi-agent-system-has-a-memory-problem-5hbg</link>
      <guid>https://forem.com/slythefox/your-multi-agent-system-has-a-memory-problem-5hbg</guid>
      <description>&lt;p&gt;Every multi-agent system I've seen break in production broke for the same reason: agents couldn't remember what already happened.&lt;/p&gt;

&lt;p&gt;Not hallucination. Not bad prompts. Memory. The system had no way to persist decisions, track what was active, or share context between agents that needed to coordinate.&lt;/p&gt;

&lt;p&gt;I run 39 agents in a production operating system. Solving the memory problem was harder than designing the agents themselves.&lt;/p&gt;

&lt;h2&gt;
  
  
  The three kinds of agent memory
&lt;/h2&gt;

&lt;p&gt;Agent memory isn't one thing. It's at least three distinct problems, and most systems only solve the first.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Session memory&lt;/strong&gt; (what happened in this conversation)&lt;/p&gt;

&lt;p&gt;This is what you get for free with most LLM APIs. The context window. It works until the conversation ends, then it's gone.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# This is NOT memory. This is a conversation buffer.
&lt;/span&gt;&lt;span class="n"&gt;messages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Deploy the new config&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;assistant&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Config deployed to staging.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="c1"&gt;# Session ends. Everything above disappears.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Shared state&lt;/strong&gt; (what's true right now across all agents)&lt;/p&gt;

&lt;p&gt;This is where most systems fall apart. When Agent A makes a decision, Agent B needs to know about it before it acts. Not eventually. Before.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Shared state: a set of canonical files every agent reads
&lt;/span&gt;&lt;span class="n"&gt;SHARED_STATE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;active/now.md&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Current tasks, owners, and status&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;active/priorities.md&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Ranked priority list&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;active/blockers.md&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;What&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s stuck and why&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;core/history/decisions.md&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Decisions with rationale&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;before_action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Every agent reads shared state before acting.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;purpose&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;SHARED_STATE&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;items&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;read_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The key insight: shared state must be file-based (or database-backed), not held in any single agent's context. Agents come and go. The state persists.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Institutional memory&lt;/strong&gt; (what happened over time, why decisions were made)&lt;/p&gt;

&lt;p&gt;This is the layer nobody builds until they need it. Two weeks into running a multi-agent system, someone asks "why is the deployment config set to X?" No agent remembers. The decision happened in a conversation that's long gone.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# A decision record with rationale
&lt;/span&gt;&lt;span class="n"&gt;DECISION&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DEC-042&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;date&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;2026-03-15&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;decision&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Use file-based shared state instead of Redis&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rationale&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Agents run in ephemeral sessions. Files persist across &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
                 &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sessions without requiring a running service.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;decided_by&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;architect&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;alternatives_considered&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Redis&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;SQLite&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;env vars&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you don't record the "why," you'll revisit the same decisions repeatedly. I've watched agents re-debate resolved questions because the rationale wasn't captured anywhere they could read.&lt;/p&gt;

&lt;h2&gt;
  
  
  The shared state pattern
&lt;/h2&gt;

&lt;p&gt;Here's the pattern that works in my system. It's simple, and that's the point.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rule 1: Canonical files are the source of truth.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Not agent memory. Not conversation history. Files on disk. Every agent reads them. A small set of agents are authorized to write them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rule 2: Read before you act.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Before any agent takes action, it reads relevant shared state. This is non-negotiable. An agent that acts without reading current state will contradict decisions made since its last session.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rule 3: Write-back is immediate.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When an agent completes a task, it updates shared state in the same operation. Not "later." Not "at the end of the session." Immediately. Stale state causes cascading errors.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;complete_task&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Task completion always includes state update.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;
    &lt;span class="c1"&gt;# 1. Do the work
&lt;/span&gt;    &lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# 2. Update shared state (same operation)
&lt;/span&gt;    &lt;span class="nf"&gt;update_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;active/now.md&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;mark_complete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="nf"&gt;update_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;core/history/decisions.md&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;record_decision&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="c1"&gt;# 3. Never separate these steps
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Rule 4: Ownership is explicit.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Not every agent can write to every file. The PM agent owns &lt;code&gt;active/now.md&lt;/code&gt;. The architect owns &lt;code&gt;core/history/decisions.md&lt;/code&gt;. Contention on shared files goes to a governor.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this looks like in practice
&lt;/h2&gt;

&lt;p&gt;My system's shared state layer is a directory called &lt;code&gt;active/&lt;/code&gt; with seven files:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;File&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;th&gt;Owner&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;now.md&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Current tasks and status&lt;/td&gt;
&lt;td&gt;PM&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;priorities.md&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Ranked priorities&lt;/td&gt;
&lt;td&gt;Strategist&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;blockers.md&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;What's stuck&lt;/td&gt;
&lt;td&gt;PM&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;risks.md&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Active risk register&lt;/td&gt;
&lt;td&gt;Sentinel&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;improvements.md&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Proposed system changes&lt;/td&gt;
&lt;td&gt;Improver&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;inbox.md&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Unrouted incoming items&lt;/td&gt;
&lt;td&gt;Router&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;coherence-metrics.md&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;System health signals&lt;/td&gt;
&lt;td&gt;Evaluator&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Every agent reads the files relevant to its work before acting. Seven agents write to them. The governor resolves conflicts.&lt;/p&gt;

&lt;p&gt;Is it elegant? No. It's a directory of markdown files. But it's been running for two weeks without a single state conflict, and every agent sees the same picture of reality.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start here
&lt;/h2&gt;

&lt;p&gt;If your multi-agent system has more than two agents:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create one shared file that tracks "what's active right now"&lt;/li&gt;
&lt;li&gt;Make every agent read it before acting&lt;/li&gt;
&lt;li&gt;Make the responsible agent update it after completing work&lt;/li&gt;
&lt;li&gt;Log decisions with rationale, not just outcomes&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You can get sophisticated later. Databases, event streams, vector stores. But the pattern underneath is always the same: agents need a shared, persistent, authoritative picture of the world. Without it, you're running parallel amnesiacs.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Building &lt;a href="https://github.com/slythefox/sigil" rel="noopener noreferrer"&gt;Sigil&lt;/a&gt; for cryptographic audit trails, because shared state is only trustworthy if you can prove it wasn't tampered with. More on agent architecture: &lt;a href="https://thealignmentlayer.substack.com" rel="noopener noreferrer"&gt;The Alignment Layer&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>architecture</category>
      <category>python</category>
    </item>
    <item>
      <title>I Built Cryptographic Audit Trails for AI Agents. Here Is Why.</title>
      <dc:creator>sly-the-fox</dc:creator>
      <pubDate>Tue, 10 Mar 2026 00:35:58 +0000</pubDate>
      <link>https://forem.com/slythefox/i-built-cryptographic-audit-trails-for-ai-agents-here-is-why-20id</link>
      <guid>https://forem.com/slythefox/i-built-cryptographic-audit-trails-for-ai-agents-here-is-why-20id</guid>
      <description>&lt;h2&gt;
  
  
  The Problem No One Is Solving Well
&lt;/h2&gt;

&lt;p&gt;Here is a scenario that is becoming common. You deploy an AI agent that processes customer requests, accesses a database, calls external APIs, and takes actions on behalf of your users. It runs for a week. Then something goes wrong. A customer complains about an unauthorized change. Your team asks the obvious question: what did the agent actually do?&lt;/p&gt;

&lt;p&gt;You check the logs. They are text files, maybe JSON lines in a database. They say the agent did X, Y, and Z. But those logs are mutable. Anyone with write access could have modified them. The agent itself could have modified them. There is no cryptographic proof that the log is accurate.&lt;/p&gt;

&lt;p&gt;This is the state of agent accountability in 2026. Agents are gaining access to production databases, financial systems, and customer data. The best most teams have for auditing is &lt;code&gt;print()&lt;/code&gt; statements and hope. That gap between what agents can do and what we can prove they did is growing fast.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Pattern Underneath
&lt;/h2&gt;

&lt;p&gt;This is not a new problem. It is a well-understood one wearing new clothes. Distributed systems solved this class of problem decades ago with append-only logs, hash chains, and cryptographic signatures. The pattern is simple: make every action produce a record that is mathematically linked to the one before it. If anyone modifies a record in the middle, every subsequent link breaks. Tampering becomes not just difficult, but visible.&lt;/p&gt;

&lt;p&gt;The fact that most agent frameworks do not apply these techniques is not a technology gap. It is an attention gap. The tooling exists. The cryptographic primitives are mature. No one has wired them together for the specific context of AI agent actions.&lt;/p&gt;

&lt;p&gt;So I did.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Sigil Does
&lt;/h2&gt;

&lt;p&gt;Sigil provides tamper-evident audit trails for AI agents. Every agent action becomes an attestation, a signed and timestamped record that includes a hash of the previous attestation. This creates a hash chain. Each attestation is signed with Ed25519, a fast and well-studied signature scheme. The signature proves the attestation was created by a specific key at a specific time.&lt;/p&gt;

&lt;p&gt;Each agent gets its own independent hash chain. No global bottleneck, no cross-contamination between agents. The architecture is deliberately simple because trust infrastructure should be easy to reason about.&lt;/p&gt;

&lt;p&gt;Sigil ships as an MCP server. If you are using any MCP-compatible client (Claude Code, OpenHands, or your own), you can add Sigil and start recording attestations immediately.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;sigil&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SigilClient&lt;/span&gt;

&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SigilClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sg_...&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Record an action
&lt;/span&gt;&lt;span class="n"&gt;receipt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;attest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;action_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;database.query&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;table&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;customers&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rows_returned&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;142&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Verify the chain is intact
&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;verify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;receipt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;valid&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;chain_valid&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each attestation includes: agent_id, action_type, payload, timestamp, prev_hash (SHA-256 chain link), and signature (Ed25519). The chain is append-only, queryable, and independently verifiable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Open-Source
&lt;/h2&gt;

&lt;p&gt;The MCP server and Python SDK are MIT-licensed. You can self-host the entire stack. This was a deliberate choice, not a growth strategy. Trust infrastructure should be inspectable. If you cannot read the code that generates your audit trail, you have not actually solved the trust problem. You have just moved it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Comes Next
&lt;/h2&gt;

&lt;p&gt;Sigil is structured in layers, each building on the one below:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Notary&lt;/strong&gt; (available now): Hash-chained attestations and verification&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Identity&lt;/strong&gt; (planned): Agent PKI with Ed25519 keypairs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Delegation&lt;/strong&gt; (planned): Cryptographic proof of authorization chains&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I am also working on integrations with popular agent frameworks for automatic attestation recording. The goal is to make auditable the default, not the exception.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try It
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;sigil-notary
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;GitHub: &lt;a href="https://github.com/sly-the-fox/sigil" rel="noopener noreferrer"&gt;https://github.com/sly-the-fox/sigil&lt;/a&gt; | PyPI: &lt;a href="https://pypi.org/project/sigil-notary/" rel="noopener noreferrer"&gt;https://pypi.org/project/sigil-notary/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I built this for developers shipping agents into production. If that is you, open an issue, start a discussion, or reach out directly.&lt;/p&gt;

&lt;p&gt;Sigil means "a seal of authority." I think AI agents need one.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>security</category>
      <category>opensource</category>
      <category>mcp</category>
    </item>
  </channel>
</rss>
