<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Swarmkit]]></title><description><![CDATA[Swarmkit]]></description><link>https://swarmkit.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Tue, 27 Oct 2026 14:08:38 GMT</lastBuildDate><atom:link href="https://swarmkit.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[The coding agent is the smallest part of the job]]></title><description><![CDATA[How I built AI agents with human approvals to support a legacy WMS: giving agents knowledge, checking their output, and where people decide.]]></description><link>https://swarmkit.hashnode.dev/the-coding-agent-is-the-smallest-part-of-the-job</link><guid isPermaLink="true">https://swarmkit.hashnode.dev/the-coding-agent-is-the-smallest-part-of-the-job</guid><category><![CDATA[agents]]></category><category><![CDATA[coding agents]]></category><category><![CDATA[claude-code]]></category><category><![CDATA[#HITL]]></category><category><![CDATA[mcp]]></category><category><![CDATA[architecture]]></category><category><![CDATA[enterprise software]]></category><dc:creator><![CDATA[Srijith Kartha]]></dc:creator><pubDate>Sun, 25 Oct 2026 16:13:28 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/699c1cccb740d46ab9fa294a/65fb8836-4ffa-4509-b2ef-4f0b5156e080.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Most coding-agent demos show an issue going in and a pull request coming out. I've worked on building agents that support a legacy warehouse management system, and there, writing the code is the easy part.</p>
<p>The system is IBM Sterling WMS, a commercial warehouse product. Over the years it was customised into several separate solutions, each with its own codebase. Each solution runs as several installations (development, test, production), and each installation has its own configuration and data.</p>
<p>A typical support ticket isn't "fix this function". It's "the pick screen rejects a carton at one warehouse but not at another", or a new requirement in a spreadsheet written by someone who has never seen the code. To handle it, someone has to find out what the system does today, agree what should change, change the code and configuration, test it, and hand it to the person who deploys it.</p>
<p>I built a set of agents that does that work, with a person approving each important step. The agent that writes code is one of six steps. This post explains the rest:</p>
<ul>
<li>how the agents get the knowledge they need;</li>
<li>how we check what they produce;</li>
<li>where people make the decisions.</li>
</ul>
<p>I can't share the client's code or tickets, so this post describes the design and the reasons behind it, in a form you can reuse in your own projects.</p>
<h2>Why the usual issue-to-PR approach doesn't fit</h2>
<p>Tools like GitHub Copilot's coding agent, Codex and Devin take an issue and open a pull request. That works because GitHub already supplies the rest of the process: the issue describes the work, a person reviews the pull request, the test suite checks it, and merging deploys it. Those tools depend on three things being true. In a customised ERP or WMS system, none of them is.</p>
<table>
<thead>
<tr>
<th>What issue-to-PR tools assume</th>
<th>Where that's true</th>
<th>What we had instead</th>
</tr>
</thead>
<tbody><tr>
<td>The code is the whole system</td>
<td>A web service, where behaviour lives in the code</td>
<td>Behaviour split between code, configuration stored in each installation's database, and a vendor product we can't change</td>
</tr>
<tr>
<td>A test suite can tell whether a change works</td>
<td>A codebase with good tests</td>
<td>No test suite, a Java 6 toolchain, and configuration that only exists on an installed system</td>
</tr>
<tr>
<td>Merging deploys the change</td>
<td>Continuous delivery</td>
<td>A person deploys, in an agreed change window</td>
</tr>
</tbody></table>
<p>When those assumptions don't hold, the work doesn't go away. It moves to three places, and the rest of this post is about them:</p>
<ul>
<li><strong>Giving the agent the right information:</strong> the code, the configuration of the right installation, the data model and the product documentation.</li>
<li><strong>Checking the output,</strong> because there is no test suite to do it.</li>
<li><strong>Handing over to people,</strong> because people approve and deploy.</li>
</ul>
<p>Surveys show the same shift from the other side. In GitLab's June 2026 survey, 85% of respondents said AI had moved the bottleneck from writing code to reviewing it.</p>
<h2>The architecture</h2>
<p>The system has three parts. Keeping their jobs separate was the most important design decision.</p>
<ul>
<li><strong>A ticket app decides what happens next.</strong> It's a small web app with its own database of tickets. For each ticket it records which step the ticket is on, which agent run it is waiting for, and who may approve that step. It also holds the business rules, such as "a defect belongs to a requirement" and "a requirement can't move on while it has open questions".</li>
<li><a href="https://github.com/delivstat/swarmkit"><strong>SwarmKit</strong></a> <strong>runs one step at a time.</strong> Each step is defined as a topology: a YAML file listing the agents for that step, the tools each one may use, and the format its answer must have. The app starts a step through SwarmKit's HTTP API, then checks the job until it finishes. SwarmKit saves the run as it goes, so if the app restarts, the run carries on.</li>
<li><strong>Plain scripts do everything that needs no judgement.</strong> They save each step's output in the right folder, turn it into documents a reviewer can read, check the design, build the deployment package and run the tests. The app calls the same scripts a person would run by hand, so both always give the same result.</li>
</ul>
<img src="https://cdn.hashnode.com/uploads/covers/699c1cccb740d46ab9fa294a/1f12cc4b-1647-4bc1-b3f8-26f0f6ab89ec.png" alt="" style="display:block;margin:0 auto" />

<p>The six steps are triage, design, code, configuration, test plan and test run. A person approves at two points:</p>
<ul>
<li><strong>After triage,</strong> a support lead decides what kind of work the ticket is.</li>
<li><strong>After design,</strong> the business owner approves the specification before anyone builds it.</li>
</ul>
<p>After that, the code and configuration are produced from the approved specification. Their output is a code diff and a configuration document, which a person applies.</p>
<p>At first, the order of the steps was defined inside SwarmKit as a pipeline. We moved it into the app, because deciding what happens next, over days and around people's availability, is application logic. SwarmKit now only runs a single step, enforces its approval and keeps the record of what happened.</p>
<h2>Giving the agents knowledge of this system</h2>
<p>A model knows Sterling WMS in general from its training. It doesn't know how this team customised it, how production is configured, or which custom libraries have no source code. Most wrong answers came from that gap.</p>
<p><strong>Solutions and installations are kept apart.</strong> A solution is a codebase plus its product version and build tools. An installation is one deployment of a solution, with its own database, configuration and data. Questions about code are answered per solution; questions about data and configuration are answered per installation. Every tool requires the solution or installation as an argument. An agent can't answer about the wrong site by forgetting to say which one; it would have to name the wrong one.</p>
<p>The agents read this knowledge through MCP servers: small services that give an agent tools it can call. Each server covers one kind of knowledge, and none of them can change anything.</p>
<table>
<thead>
<tr>
<th>Server</th>
<th>Question it answers</th>
<th>Where the answer comes from</th>
</tr>
</thead>
<tbody><tr>
<td>Code</td>
<td>What does the solution's source code say?</td>
<td>The solution's code, one folder per solution</td>
</tr>
<tr>
<td>Data model</td>
<td>Which tables and columns hold this, and how do they join?</td>
<td>The product's table definitions, plus the columns this solution added</td>
</tr>
<tr>
<td>Configuration snapshot</td>
<td>How is a service built, how does a screen reach it, which process moves an order between statuses?</td>
<td>An export of the installation's configuration tables, saved in the repository</td>
</tr>
<tr>
<td>Live configuration</td>
<td>What is this installation configured to do right now?</td>
<td>The vendor's own export tool, run against the installation</td>
</tr>
<tr>
<td>Data</td>
<td>What state is this order, task or stock record in?</td>
<td>Read-only queries against the installation's database</td>
</tr>
<tr>
<td>APIs</td>
<td>What does a product API accept and return?</td>
<td>The vendor's own schema files, about 1,300 APIs for one version</td>
</tr>
<tr>
<td>Docs</td>
<td>How does a product feature behave?</td>
<td>The product documentation, searchable</td>
</tr>
<tr>
<td>Conventions</td>
<td>How does this team build this kind of thing?</td>
<td>Convention cards (below), plus reference documents per solution</td>
</tr>
</tbody></table>
<p>Two choices are worth copying:</p>
<ul>
<li><strong>API definitions come from the vendor's schema files, not from the model's memory.</strong> Models often produce API attribute names that look right but don't exist.</li>
<li><strong>Documentation search uses two indexes.</strong> Vector search handles questions in plain language. Full-text search handles exact names, such as a table name, which vector search tends to miss.</li>
</ul>
<h3>Convention cards</h3>
<p>Product documentation describes what the product can do. It doesn't say how this team builds things, and that gap caused the most expensive mistakes. So we wrote it down as convention cards: one short Markdown file per kind of artifact, such as a mobile screen or a service.</p>
<p>Each card holds three things:</p>
<ol>
<li>what that kind of artifact is made of here;</li>
<li>where this team does it differently from the product's default;</li>
<li>links to the detailed documentation.</li>
</ol>
<p>When one solution does something differently, a separate small file records only the difference, and the tool returns both together. There are 12 cards and 5 solution-specific files, and we wrote each one after an agent got that kind of artifact wrong.</p>
<table>
<thead>
<tr>
<th>Card</th>
<th>What it settles</th>
<th>The mistake it prevents</th>
</tr>
</thead>
<tbody><tr>
<td>RF screens</td>
<td>A mobile screen is four artifacts here, with brand-specific copies of the same tree</td>
<td>A screen built from the product docs, which silently fails to open for some users</td>
</tr>
<tr>
<td>Application console</td>
<td>A console screen is a resource tree under an entity, plus JSPs</td>
<td>An empty panel with no error to follow</td>
</tr>
<tr>
<td>Services</td>
<td>A service is configuration, not code: a graph of typed nodes stored as database rows</td>
<td>A flow that starts right and fails in the middle, because nobody traced which document each node receives</td>
</tr>
<tr>
<td>Configuration blocks</td>
<td>Every configured object a design names gets a block carrying every value to be typed in</td>
<td>Prose describing a change, built wrong and failing at run time</td>
</tr>
<tr>
<td>API templates</td>
<td>Where output templates and input XSLs live, and how nodes reference them</td>
<td>Templates shipped to a path the API never looks in</td>
</tr>
<tr>
<td>Java extensions</td>
<td>Custom Java is small here; reach for configuration first</td>
<td>Java written for what configuration already does</td>
</tr>
<tr>
<td>Platform utilities</td>
<td>The platform classes for dates, comparison and XML that already exist</td>
<td>Helpers reimplemented in every extension</td>
</tr>
<tr>
<td>XSL and Java</td>
<td>How stylesheets call Java, mostly for dates</td>
<td>Date handling that fails validation downstream for an unrelated-looking reason</td>
</tr>
<tr>
<td>Print documents</td>
<td>How a label is designed, mapped and reached through a print service</td>
<td>A new print path beside the one service everything already uses</td>
</tr>
<tr>
<td>Barcode translation</td>
<td>How a scanned string becomes an identity, per screen and warehouse</td>
<td>Code written for what is one configuration table</td>
</tr>
<tr>
<td>Time-triggered agents</td>
<td>Scheduled transactions as flows configured by criteria rows</td>
<td>Treating a scheduled job as code</td>
</tr>
<tr>
<td>Solution environment</td>
<td>The per-solution constants: application server, JMS naming, endpoints</td>
<td>Values copied from an old record, in the wrong server's format</td>
</tr>
</tbody></table>
<p>The cards replaced instructions that had been copied into agent prompts. The same guidance about mobile screens had been pasted into three agents' prompts, and the three copies had already drifted apart. A card exists once, is reviewed like code, and is only loaded when it's needed.</p>
<h2>Using a different model or harness for each step</h2>
<p>In SwarmKit, each agent has an executor: the thing that actually does its work. There are two kinds:</p>
<ul>
<li><strong>A model node</strong> sends prompts to a model through an API. SwarmKit checks that the answer matches the required format and asks again if it doesn't.</li>
<li><strong>A harness</strong> runs a complete coding agent, such as Claude Code, which has its own tools for reading and editing files. It can work through long tasks, but SwarmKit can only ask it for a format; it can't enforce one.</li>
</ul>
<p>The executor is set in each agent's YAML file, so moving a step from one to the other is a configuration change, not a rewrite.</p>
<table>
<thead>
<tr>
<th>Step</th>
<th>Runs on</th>
<th>Why</th>
</tr>
</thead>
<tbody><tr>
<td>Triage</td>
<td>A model node using a low-cost open model (Kimi K2.5, through OpenRouter)</td>
<td>It only looks things up and fills in a short form, and a model node enforces the form</td>
</tr>
<tr>
<td>Turning a request document into a ticket</td>
<td>The same low-cost model</td>
<td>A short form and one tool</td>
</tr>
<tr>
<td>Design, code, configuration, test plan</td>
<td>Claude Code as a harness, on the Claude subscription</td>
<td>Long sessions with many tool calls: read several existing screens, follow a flow, edit real files</td>
</tr>
<tr>
<td>Answering questions in chat</td>
<td>Claude Code, with its own model setting</td>
<td>Many small lookups and a short answer, so it is tuned for speed, separately from the steps above</td>
</tr>
<tr>
<td>Did the tests pass? Is the design complete? Build the package</td>
<td>No model, just a script</td>
<td>These questions have a definite answer that code can compute</td>
</tr>
</tbody></table>
<p>We chose these by measuring. The low-cost model handled triage's short form well. On the design step, with a 22-field form plus tool calls, it produced a valid design in only 2 of 7 runs; the others returned partial output, a format it made up, or its tool calls written out as plain text. So design went to Claude Code, and triage stayed on the low-cost model.</p>
<p>Most of the cost savings had nothing to do with which model was cheaper:</p>
<ul>
<li><strong>No model where none is needed.</strong> Saving files, building the package, checking formats and running tests are done by scripts. They cost almost nothing and can't make the kind of mistakes a model makes.</li>
<li><strong>Steps with nothing to do are skipped.</strong> The configuration step only runs when the design adds a service. Before that check, running it anyway cost about $0.20 and five minutes, only to end in an error.</li>
<li><strong>Cheap checks run before expensive ones.</strong> The design review runs its scripted checks first, and only starts the Claude Code reviewer if those pass.</li>
<li><strong>Re-running a step requires a reason.</strong> Without new input, a re-run repeats the same work at the same price, so the person must say what changed.</li>
<li><strong>An unchanged output isn't sent for approval again.</strong> Approvals are tied to a hash of the output's content, so an identical re-run doesn't ask anyone to approve it twice.</li>
<li><strong>Long Claude Code sessions run on a subscription,</strong> not on pay-per-token API credits.</li>
</ul>
<h2>Making agents call tools in the right order</h2>
<p>We wanted every agent to read the relevant convention card before writing anything. Telling it to wasn't enough. Two early designs contained file paths that didn't exist, even though each run's instructions told the agent, many times over, to read the conventions first. Neither run did.</p>
<p>One run showed the cause clearly. The same agent, with the same prompt, called a tool that the system required 4 times, and a tool that the prompt only asked for 0 times. Agents follow what the system enforces, not what the prompt asks for.</p>
<p>So we declared the order in each agent's YAML, and SwarmKit enforces it. A <code>requires:</code> entry says that one tool can't be used until another has been called in the same run:</p>
<pre><code class="language-yaml">requires:
  get-estate-reference: [get-build-convention]
  find-wms-column:      [get-build-convention]
  search-sterling-docs: [get-build-convention]
  # … every estate skill waits for a convention card
</code></pre>
<p>If the agent calls a tool too early, SwarmKit refuses the call and returns a message telling it what to do: "get-estate-reference requires get-build-convention, which has not been called in this session. Call it first, then retry." The agent calls the convention tool and carries on. The rule applies separately to each agent in each run, every refusal is written to the audit log, and a rule that could never be satisfied (for example, one naming a tool the agent doesn't have) is rejected before the run starts.</p>
<h3>Where it doesn't work</h3>
<p>The same rule did nothing on the coding agent. Claude Code has its own built-in tools for reading and writing files, so it read the code with those and never called our file tools. A rule on a tool the agent never uses can't trigger, and it gives a false sense of safety.</p>
<p>So on Claude Code agents, we only put rules on tools that have no built-in alternative: the data model, the documentation, the installation's configuration and the reference screens. Those rules do work. Adding one to the design agent took it from never looking up reference material to always doing so before checking its design.</p>
<p>This rule controls order; it isn't a security measure. What keeps the system safe is that none of the agents' tools can change anything.</p>
<h2>Making the output good, not just correctly formatted</h2>
<p>An agent saying it has finished doesn't prove that it has. Each technique below exists because a run reported success when its output was wrong. One run lost access to its tools halfway through and still reported success.</p>
<p><strong>1. Ask for evidence in the output.</strong> The coding agent must say, for every file it writes, which existing file it used as a model. The script that saves its changes flags any file without one. This worked where controlling the agent's tools didn't: in the next run, every file named a real existing screen it had copied the style of.</p>
<p><strong>2. Check the format first, then the content.</strong> Before a person sees a design, it passes three checks, which SwarmKit runs in order as a "funnel":</p>
<ol>
<li><strong>Format:</strong> the design must match a JSON schema. If a field is wrong, the agent is told which field and asked to fix it.</li>
<li><strong>Content:</strong> scripts first check facts that have a definite answer, for example that every file and service the design mentions really exists in the installation's configuration, and that every service flow has a start and a reachable end. Only if those pass does a second Claude Code agent review the design for things a script can't decide. If its score is below 0.75, the review comments go back to the design agent to rewrite. After two attempts, the design goes to a person with the comments attached.</li>
<li><strong>Approval:</strong> the business owner approves it, and can't be the person who wrote it.</li>
</ol>
<p><strong>3. Don't let an agent mark its own work.</strong></p>
<ul>
<li>A separate agent writes the tests from the approved specification, not from the code, so the tests check what was agreed rather than what was built.</li>
<li>After the tests are written, the app checks them itself instead of trusting the agent's report.</li>
<li>Whether the tests pass is decided by JUnit. The test-run step has no agent at all.</li>
</ul>
<p><strong>4. Agents return content; scripts save it.</strong> Agents never write files into the project themselves. They return their output, and a script decides where it goes. The script refuses unexpected file names, paths outside the ticket's folder, and documents that cite no sources.</p>
<p><strong>5. Let the coding agent edit files, in a throwaway copy.</strong> At first the coding agent wasn't allowed to edit files, so it returned whole files as JSON. Adding nine lines to a 611 KB label file came back as the entire file, which showed up as 27,097 lines removed and 12 added. We then let it edit files directly in a temporary git worktree, a separate copy of the repository that is deleted after the run. The same change became a nine-line diff, and the real project stayed untouched.</p>
<p><strong>6. Turn the output into documents people can review.</strong> Agents return JSON, which nobody wants to review. After each step, scripts produce what the reviewer actually reads: the specification in Markdown and Word, a configuration document with flow diagrams, the code diff, and a validation report listing anything to check before approving.</p>
<h2>Putting people where their decisions are cheap</h2>
<p>Reviewing agent work is slow when a person has to judge a large code change after all the work is done. We moved the important decisions earlier:</p>
<ul>
<li><strong>Triage review.</strong> A support lead reads the triage and decides what kind of work the ticket is. That choice decides which checks and approvals apply, so we don't let a model make it.</li>
<li><strong>Specification review.</strong> The business owner approves the design before any code is written. Fixing a misunderstanding here means rewriting the specification. Later, it means a code change, another review and another round of integration testing.</li>
<li><strong>Code and deployment.</strong> By then the code implements an approved specification, so the reviewer is checking it, not working out what it should have done.</li>
</ul>
<p>When a step reaches an approval, it stops until a person decides. SwarmKit saves the run and resumes it after the decision, so nothing keeps running while it waits. Only a person can approve: the permission to approve can't be given to an agent or to an API key.</p>
<h3>Three ways work gets redone</h3>
<p>Work is redone for different reasons, and we handle each one differently so the history shows which reason it was.</p>
<table>
<thead>
<tr>
<th>What happened</th>
<th>What the person does</th>
<th>What runs again</th>
<th>What the agent is told</th>
</tr>
</thead>
<tbody><tr>
<td>The reviewer thinks the output is wrong</td>
<td>Chooses "changes requested" at the approval</td>
<td>The step waiting for approval</td>
<td>The reviewer's comment</td>
</tr>
<tr>
<td>The requirement changed after the work was approved</td>
<td>Reopens the ticket at the affected step</td>
<td>That step and every step after it</td>
<td>A note, which is required, saying what changed</td>
</tr>
<tr>
<td>The work was right, but integration testing found a problem in the system</td>
<td>Raises a defect ticket</td>
<td>A separate investigate, fix and code cycle</td>
<td>Its own evidence and approvals</td>
</tr>
</tbody></table>
<p><strong>Changes requested.</strong> The reviewer's comment is passed to the agent when the step runs again. It's labelled as review feedback from a named person, not as instructions, so text written by people can't override the agent's instructions. Each version of the output is identified by a hash of its content, so an approval of version 1 doesn't count as an approval of version 3.</p>
<p><strong>Reopening.</strong> Requirements change after approval: a mapping that wasn't final, a rule nobody mentioned, a field the customer added. The design was right for what was known, but what's known has changed. A person reopens the ticket at the step that's affected:</p>
<ul>
<li>Only a person can reopen, because it throws away approved work and costs money to redo.</li>
<li>The person must write what changed, and that note is given to the agent. Without it, the agent would repeat the same work.</li>
<li>The old runs stay in the history with their cost, and their output is moved to a numbered archive folder.</li>
<li>Earlier approvals don't carry over to the new version.</li>
</ul>
<p>This works better than opening a new ticket, which would lose the triage, the answered questions and the specification to revise.</p>
<p><strong>Defects.</strong> A problem found in integration testing doesn't send the requirement back to the coding step. It becomes a new ticket, linked to the requirement, with its own steps:</p>
<ol>
<li>investigate, then a person accepts the diagnosis;</li>
<li>propose a fix, then a person accepts the approach;</li>
<li>write the code, then a person reviews the change.</li>
</ol>
<p>Several defects can be worked on at the same time. The requirement stays blocked until the last fix is merged, and then the whole requirement goes through integration testing again.</p>
<h3>Why approvals get quicker over time</h3>
<p>The approvals never went away, but they became quick. Each time a reviewer objected, we turned that kind of objection into something the system checks or knows, so the same objection didn't come back.</p>
<table>
<thead>
<tr>
<th>When a reviewer said…</th>
<th>We added</th>
<th>So next time</th>
</tr>
</thead>
<tbody><tr>
<td>"That's not how we build this here"</td>
<td>A convention card, or a line in one</td>
<td>The agent reads the card before it writes</td>
</tr>
<tr>
<td>"That's wrong for this site"</td>
<td>A fact about that installation in governed memory, confirmed by a person</td>
<td>The fact is given to the next agent working on that site</td>
</tr>
<tr>
<td>"This file or service doesn't exist"</td>
<td>A scripted check in the design validator</td>
<td>The design is sent back before anyone sees it</td>
</tr>
<tr>
<td>"I can't tell what this is based on"</td>
<td>A required evidence field, such as the existing file each new file was modelled on</td>
<td>Every claim arrives with its source</td>
</tr>
</tbody></table>
<p>Early specifications needed a reviewer to catch invented file paths and service flows that broke halfway through. Later ones arrived already checked against the real configuration, with a report of anything left to look at. Approving became reading the specification, not searching it for problems.</p>
<p>SwarmKit already does the same thing for permissions. When Claude Code needs a permission it wasn't given, the request goes to a person. After a person approves the same action 5 times, SwarmKit suggests adding it to the agent's permanent permissions, and a single refusal resets the count.</p>
<p>What stays with people is the judgement no check can make: is this the right design for what the business asked for?</p>
<h2>Turning open questions into tickets</h2>
<p>Requirements usually arrive with gaps. One needs three questions answered before it can be designed; the next needs none. A fixed list of steps can't express that, so open questions are handled before the steps start.</p>
<p>How it works:</p>
<ol>
<li><strong>Every agent's output has a</strong> <code>blocked_by</code> <strong>field.</strong> In it, the agent lists what it couldn't find out, and what would answer it.</li>
<li><strong>The app turns each entry into a new ticket that blocks the original one.</strong> If a retried step reports the same question again, it isn't created twice.</li>
<li><strong>The app picks up questions that are ready to be answered,</strong> meaning open tickets that aren't themselves waiting on another question. A question ticket doesn't go through the six steps; it goes to the one agent setup that answers that kind of question, or to a person.</li>
<li><strong>The original ticket waits.</strong> It can't start its next step until all its question tickets are closed.</li>
</ol>
<p>Most agents handle a gap by asking in the chat or by guessing. Neither leaves a record, and a guess that the rest of the work is built on is the most expensive kind of mistake. A question ticket has an owner, an answer and a history, and the design agent receives the answers instead of making its own assumptions.</p>
<h2>How the knowledge builds up</h2>
<p>We learned one rule the hard way: knowledge only counts if something delivers it to the agent. An audit found six places where knowledge had been set up but nothing ever loaded it, including the notes folder, the generated documentation, the judge's scoring guide and the memory reader. One design run rebuilt an approach we had already recorded as wrong, at a cost of $3.63, while the correct fact sat unused in storage.</p>
<p>So we sorted knowledge by when the agent needs it, and gave each kind its own way of reaching the agent:</p>
<table>
<thead>
<tr>
<th>Kind</th>
<th>Examples</th>
<th>When the agent needs it</th>
<th>How it reaches the agent</th>
<th>Who writes it</th>
</tr>
</thead>
<tbody><tr>
<td>Ground truth</td>
<td>Code, configuration snapshots, data model</td>
<td>When it asks, and it must be exact</td>
<td>The agent calls an MCP tool</td>
<td>Nobody; it comes from the system itself</td>
</tr>
<tr>
<td>Conventions</td>
<td>How this team builds each kind of artifact</td>
<td>Every time it builds that kind of artifact</td>
<td>A required tool call, enforced by <code>requires:</code></td>
<td>A person, as a reviewed file</td>
</tr>
<tr>
<td>Facts</td>
<td>Confirmed findings about a solution or an installation</td>
<td>When they are relevant to the ticket</td>
<td>Up to five, added to the agent's input before each turn</td>
<td>A person, at an approval or when a ticket closes</td>
</tr>
<tr>
<td>Reference</td>
<td>The vendor's product documentation</td>
<td>When it needs depth</td>
<td>Search</td>
<td>The vendor</td>
</tr>
<tr>
<td>Output</td>
<td>Generated documentation, specifications</td>
<td>Not as input, unless deliberately indexed</td>
<td>Indexing, if we choose to</td>
<td>Agents, through the scripts that save their output</td>
</tr>
</tbody></table>
<p>Two rules keep these apart:</p>
<ul>
<li><strong>Conventions are always loaded, never ranked by relevance.</strong> Memory only adds the five best matches, so a convention could lose its place to five unrelated facts without anyone noticing.</li>
<li><strong>Memory holds facts, not conventions.</strong> How to build something goes in a card; a fact about one installation goes in memory. A card that contained installation-specific values would be out of date before anyone read it.</li>
</ul>
<p>This pays off when a fact changes the output. Once the correct fact was in memory and given to the agent, the next design used it instead of repeating the old mistake, and the cost of that design run fell from $3.63 to $1.75.</p>
<h3>The knowledge grows as people work</h3>
<p>The knowledge isn't fixed when the project starts. It grows from what people write down while working on tickets:</p>
<ul>
<li><strong>convention cards,</strong> written or corrected whenever an agent gets something wrong;</li>
<li><strong>a knowledge index:</strong> a few lines per card saying what it covers and when to read it;</li>
<li><strong>confirmed facts,</strong> recorded by a person at an approval or with the memory command-line tool;</li>
<li><strong>notes:</strong> a hand-written file per solution.</li>
</ul>
<p>Writing it down isn't enough on its own. One agent, told to read the conventions first, called the schema tool eight times and the conventions tool zero times, and broke every rule in the card.</p>
<p>So knowledge reaches the agent at three levels:</p>
<ol>
<li><strong>The index</strong> is stored in governed memory, which SwarmKit adds to the agent's input before every turn, without the agent having to ask. A script copies the index from its reviewed file into memory and updates existing entries in place.</li>
<li><strong>The card</strong> is fetched when the index points to it, and the <code>requires:</code> rule makes sure that happens.</li>
<li><strong>The detail,</strong> such as earlier examples and product documentation, is linked from the card and read when needed.</li>
</ol>
<p>Notes don't reach the agents yet; for now they are for people.</p>
<h3>Two kinds of memory</h3>
<p>SwarmKit has two kinds of memory:</p>
<ul>
<li><strong>Workspace memory:</strong> what a run records about itself, with no review.</li>
<li><strong>Governed memory:</strong> curated facts. Each fact is stored as a subject, an attribute and a value, for example an installation, a setting and what it is set to. When a new fact arrives, it's compared with what's already stored: it may be new, confirm an existing fact, update it, or add detail to it. If it contradicts a fact that's already trusted, it's held for a person to decide instead of overwriting it. Older facts gradually count for less.</li>
</ul>
<p>In this project, only governed memory is given to the agents, and only people write to it.</p>
<p><strong>The subject records where a fact is true:</strong> the product, one solution or one installation. A fact confirmed on production but stored against the whole solution would be given to agents working on the development system, where it may not be true.</p>
<p><strong>Facts are written when something stops being a guess:</strong> when a person accepts a triage or a diagnosis, when a fix is agreed, and when a ticket closes as done or rejected. An approach that was rejected is worth recording too.</p>
<p>It's tempting to let every run save what it learned. That looks good in a demo and goes wrong slowly: guesses become facts, agents keep confirming their own earlier guesses, and within weeks the system is confidently wrong, with records that look like learning.</p>
<img src="https://cdn.hashnode.com/uploads/covers/699c1cccb740d46ab9fa294a/f3952bfd-4878-46d1-956f-019958c51e4a.png" alt="" style="display:block;margin:0 auto" />

<h2>What I'd tell someone building this</h2>
<ul>
<li><strong>Spend your effort on knowledge, not on the coding agent.</strong> Getting the coding agent working was quick. Giving it the right information, checking its output and handing over to people took the time.</li>
<li><strong>Enforce what matters instead of asking for it in the prompt.</strong> Tool order, output format and evidence only became reliable once the system enforced them.</li>
<li><strong>For a full coding agent like Claude Code, check what it returns rather than which tools it uses.</strong> It has its own tools, so rules on yours may never apply.</li>
<li><strong>Never accept an agent's "done" as proof.</strong> Have a script, a test run or a different agent check the work.</li>
<li><strong>Choose the executor per step.</strong> Use a low-cost model for short structured answers, a coding agent or harness for long sessions with many tool calls, and no model when a script can compute the answer. Measure before you switch.</li>
<li><strong>Ask people to decide earlier.</strong> One approval of the specification saves several rounds of reviewing code.</li>
<li><strong>Handle each reason for redoing work separately.</strong> "This output is wrong", "the requirement changed" and "the system is broken" are different events, and the history should show which one happened.</li>
<li><strong>Keep the order of steps in your application.</strong> Let the agent framework run one step well and keep its record; let the app that knows the people decide what happens next.</li>
</ul>
<p>The open problem is people. Every approval needs someone assigned to it, and an approval with nobody assigned stops the ticket. That's the safe choice, but as volume grows, approvals will become the bottleneck before the agents do. Our next step is to run routine approvals in shadow mode: a person still decides, while we record how often they agree with the automated checks, and only relax approvals for a type of ticket when that record supports it.</p>
<h2>Sources</h2>
<ul>
<li><a href="https://futurumgroup.com/insights/ai-code-review-hits-a-wall-why-speed-without-trust-risks-engineering-chaos/">AI code review hits a wall (GitLab survey, Futurum)</a></li>
<li><a href="https://github.com/delivstat/swarmkit">SwarmKit</a></li>
</ul>
]]></content:encoded></item></channel></rss>