<?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[Software Architecture as a Field]]></title><description><![CDATA[Software Architecture as a Field is a research notebook on software architecture for the age of AI-driven development.

It explores how algebraic ideas can help analyze architectural properties such as boundaries, composition, coupling, invariants, and change propagation.

The goal is to understand how codebases evolve when AI agents accelerate software change, and how architecture can guide that change toward maintainable systems.]]></description><link>https://blog.iroha1203.dev</link><image><url>https://cdn.hashnode.com/uploads/logos/69ff29ebf239332df4a93c3a/c2fa4a4a-2f3e-41c0-bb1f-e930a5e4853b.png</url><title>Software Architecture as a Field</title><link>https://blog.iroha1203.dev</link></image><generator>RSS for Node</generator><lastBuildDate>Fri, 09 Oct 2026 19:36:14 GMT</lastBuildDate><atom:link href="https://blog.iroha1203.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Ariadne: The Mathematics of Refactoring in the Age of AI]]></title><description><![CDATA[“I want to add multilingual support to the site.”
It sounds like a small change: add translation files and switch the strings shown on screen. Then you switch to English, and the return button disappe]]></description><link>https://blog.iroha1203.dev/ariadne-the-mathematics-of-refactoring-in-the-age-of-ai</link><guid isPermaLink="true">https://blog.iroha1203.dev/ariadne-the-mathematics-of-refactoring-in-the-age-of-ai</guid><category><![CDATA[AI]]></category><category><![CDATA[refactoring]]></category><category><![CDATA[Mathematics]]></category><category><![CDATA[software architecture]]></category><category><![CDATA[software development]]></category><dc:creator><![CDATA[Hiroyuki Nakahata]]></dc:creator><pubDate>Sun, 04 Oct 2026 07:11:29 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/69ff29ebf239332df4a93c3a/dad3485b-d292-4576-b1bc-7f4f9a51c948.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>“I want to add multilingual support to the site.”</p>
<p>It sounds like a small change: add translation files and switch the strings shown on screen. Then you switch to English, and the return button disappears. The business logic was checking a Japanese display string.</p>
<p>In this fictional order-management system, an AI agent has handled much of the development. How do we make the internal state independent of the display language while preserving the business rules and the behavior of the existing API? What do we need permission to change, and what do we need to find out first?</p>
<p>Reviewing every line of AI-generated code is a substantial burden. I want us to be able to establish, on mathematical grounds, that the requirements we care about are preserved—without rereading the entire codebase.</p>
<p><strong>Algebraic Architecture Theory (AAT)</strong> is the theory I am developing to study operations and the relationships they must satisfy as mathematical objects. This article introduces the Ariadne Boundary Repair Theorem and results about the observations needed to carry out a repair, starting with a small model we can calculate by hand.</p>
<h2>Translate a status label, lose a return button</h2>
<p>The first implementation displays the order's status directly. The Japanese string <code>"発送済み"</code> means “Shipped.”</p>
<pre><code class="language-ts">const order = { status: "発送済み" };
statusElement.textContent = order.status;
</code></pre>
<p>A new requirement arrives: show the return button for shipped orders. This is a simplified business rule for our example.</p>
<p>The developer has been asking the AI agent to “keep the diff small and follow the existing patterns.” They check that each feature works on screen, but spend less time reading the generated code in detail.</p>
<p>The agent adds a condition that reuses the display string. The change takes only a few lines, and the button appears as expected.</p>
<pre><code class="language-ts">const canReturn = order.status === "発送済み";
returnButton.hidden = !canReturn;
</code></pre>
<p>Later, the agent follows the same pattern in the return API's eligibility check and a batch job that selects orders eligible for return. Each change meets the requirement at the time. With attention focused on visible behavior, the growing dependence on Japanese text goes unnoticed.</p>
<p>Now multilingual support arrives. The code translates <code>status</code> before passing the order to the view, so the English view receives <code>"Shipped"</code>.</p>
<pre><code class="language-ts">const orderForView = { ...order, status: "Shipped" };

// The existing UI condition expects the Japanese string.
const canReturn = orderForView.status === "発送済み"; // false
</code></pre>
<p>Fixing the UI condition leaves the same assumption in the API and batch job. Changing the value at its source to English would affect those checks and existing API consumers.</p>
<p>A label intended for display has become a shared assumption across the system. Small changes that followed existing patterns carried that assumption further.</p>
<p>The design we want separates the internal status ID from its display text:</p>
<pre><code class="language-ts">type StatusId = "pending" | "shipped";
type Order = { statusId: StatusId };

const labels = {
  ja: { pending: "未発送", shipped: "発送済み" },
  en: { pending: "Pending", shipped: "Shipped" },
};

function canReturn(order: Order): boolean {
  return order.statusId === "shipped";
}

function statusLabel(order: Order, locale: "ja" | "en"): string {
  return labels[locale][order.statusId];
}

// Preserve the representation expected by existing API consumers.
function toLegacyApi(order: Order): { status: string } {
  return { status: labels.ja[order.statusId] };
}
</code></pre>
<p>Internally, <code>"shipped"</code> is a stable identifier. Changing the English label to <code>"Dispatched"</code> no longer changes return eligibility. At the legacy API boundary, we convert the status back to the Japanese value that consumers expect.</p>
<p>Reaching this design requires coordinated changes to data creation, business checks, display logic, and API conversion. The available solutions depend on whether we may change only the UI or also the internal representation and the API conversion.</p>
<h2>“Can we fix it?” depends on what we may change</h2>
<p>There are three things to specify:</p>
<table>
<thead>
<tr>
<th>What to specify</th>
<th>In the multilingual example</th>
</tr>
</thead>
<tbody><tr>
<td>What must stay the same</td>
<td>Return eligibility and the values returned by the existing API</td>
</tr>
<tr>
<td>What may change</td>
<td>The internal status representation, business checks, and conversions for display and API output</td>
</tr>
<tr>
<td>What must agree</td>
<td>Return decisions across display languages, and the order state understood by existing API consumers</td>
</tr>
</tbody></table>
<p>Only then does “Can we fix it?” become a precise question. The requirements may be achievable with one set of permitted changes and impossible with a narrower set.</p>
<p>The Ariadne Boundary Repair Theorem studies whether permitted changes can satisfy the required conditions while preserving a fixed part. Its central setting is a model in which corrections can be calculated linearly over a finite field.</p>
<p>We can explore the question behind our multilingual example using reversible operations on just three values. In this small model, we can write out every operation and calculate exactly what each correction does.</p>
<h2>A three-value model of what needs to change</h2>
<h3>Arithmetic with 0, 1, and 2</h3>
<p>Our values are 0, 1, and 2. After each calculation, we take the remainder modulo 3, so <code>2 + 1 = 0</code> and <code>1 - 2 = 2</code>. This number system is the finite field <strong>F₃</strong>.</p>
<p>Imagine a location <code>p</code> holding one of these values. An operation changes the value at that location. Start with two operations: “add <code>x</code>” and “add <code>y</code>.” Writing the input as <code>t</code>, we have</p>
<p>$$r_x(t)=t+x,\qquad r_y(t)=t+y.$$</p>
<p>Both <code>x</code> and <code>y</code> belong to F₃. These two operations must stay fixed during repair. We will adjust other operations so that their combined results match them.</p>
<p>There are four operations available for adjustment:</p>
<table>
<thead>
<tr>
<th>Operation</th>
<th>Transformation</th>
<th>When may it change?</th>
</tr>
</thead>
<tbody><tr>
<td><code>e_u</code></td>
<td><code>t ↦ t + u</code></td>
<td>Always</td>
</tr>
<tr>
<td><code>a_h</code></td>
<td><code>t ↦ -t + h</code></td>
<td>Always</td>
</tr>
<tr>
<td><code>b_z</code></td>
<td><code>t ↦ t + z</code></td>
<td>Only if changes to <code>b</code> are permitted</td>
</tr>
<tr>
<td><code>c_v</code></td>
<td><code>t ↦ t + v</code></td>
<td>Only if changes to <code>c</code> are permitted</td>
</tr>
</tbody></table>
<p>The subscripts <code>u</code>, <code>h</code>, <code>z</code>, and <code>v</code> are the correction values we will choose. Initially, all four are 0. Each may be reset to 0, 1, or 2, subject to the permissions: if we cannot change <code>b</code>, then <code>z</code> stays 0; if we cannot change <code>c</code>, then <code>v</code> stays 0.</p>
<p>Repair adjusts the amounts added. The sign reversal in <code>a_h</code> stays in place.</p>
<h3>Holonomy: a loop can change what comes back</h3>
<p>Following a loop brings us back to the starting location, but it need not bring the state back unchanged. The transformation obtained by composing operations around a closed path is called <strong>holonomy</strong>.</p>
<p>Here, <code>a_h</code> is a single arrow that loops from <code>p</code> back to itself. With <code>h = 0</code>, it acts as <code>a_0(t) = -t</code>: 0 stays 0, while 1 and 2 swap places.</p>
<p>It also reverses the sign of a correction. If we add <code>z</code> before applying the operation, then</p>
<p>$$a_h(t+z)=-(t+z)+h=a_h(t)-z.$$</p>
<p>Adding <code>z</code> before the operation corresponds to subtracting <code>z</code> afterward. This propagation of a correction along an operation is called <em>transport</em>. Around this loop, the holonomy on corrections is multiplication by −1.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69ff29ebf239332df4a93c3a/cc9166ae-3eaa-47ea-9f25-42f463b026d8.png" alt="holonomy_illustrated_en" style="display:block;margin:0 auto" />

<p><em>Top: returning to the same location swaps 1 and 2. Bottom: +1 before the operation corresponds to −1 afterward. All arithmetic is modulo 3.</em></p>
<h3>Turn the required behavior into equations</h3>
<p>We want two sequences of operations to match the fixed operations:</p>
<table>
<thead>
<tr>
<th>Execution order, left to right</th>
<th>Required result</th>
</tr>
</thead>
<tbody><tr>
<td><code>b_z</code> → <code>e_u</code></td>
<td><code>t + x</code>, matching <code>r_x</code></td>
</tr>
<tr>
<td><code>c_v</code> → <code>a_h</code> → <code>b_z</code> → <code>a_h</code> → <code>e_u</code></td>
<td><code>t + y</code>, matching <code>r_y</code></td>
</tr>
</tbody></table>
<p>The first sequence adds <code>z</code>, then <code>u</code>, so it matches when <code>u + z = x</code>.</p>
<p>In the second sequence, focus on <code>b_z</code> between the two occurrences of <code>a_h</code>. Substitution gives</p>
<p>$$\begin{aligned} a_h\bigl(b_z(a_h(t))\bigr) &amp;=-(-t+h+z)+h\cr &amp;=t-z. \end{aligned}$$</p>
<p>The two occurrences of <code>h</code> cancel, and the effect of adding <code>z</code> changes sign. Adding <code>v</code> before this sequence and <code>u</code> after it gives <code>t + u - z + v</code>.</p>
<p>The two sequences match their targets for every input <code>t</code> exactly when</p>
<p>$$\boxed{u+z=x,\qquad u-z+v=y.}$$</p>
<p>We can now solve these equations under different change permissions.</p>
<h3>Which permissions make repair possible?</h3>
<p>If neither <code>b</code> nor <code>c</code> may change, then <code>z = v = 0</code>. The equations reduce to <code>u = x</code> and <code>u = y</code>. <strong>A repair exists exactly when</strong> <code>x = y</code><strong>.</strong></p>
<p>We may always change <code>e</code> and <code>a</code>, but <code>h</code> disappears from the equations, and <code>u</code> adds the same amount to both sides. Neither can make up a difference between <code>x</code> and <code>y</code>.</p>
<p>What if we allow changes to <code>c</code>? Keep <code>z = 0</code>, choose <code>u = x</code>, and set <code>v = y - x</code>. Both conditions are satisfied: the correction at <code>c</code> makes up the difference between the targets.</p>
<p>Allowing changes to <code>b</code> also works. Write the difference as <code>d = y - x</code>. Subtracting the equations gives <code>d = z + v</code>, because −2 equals 1 modulo 3. Even with <code>c</code> fixed and <code>v = 0</code>, we can repair by choosing <code>z = d</code> and <code>u = x - d</code>.</p>
<table>
<thead>
<tr>
<th>Additional operations permitted to change</th>
<th>When is repair possible?</th>
</tr>
</thead>
<tbody><tr>
<td>Neither</td>
<td>Only when <code>x = y</code></td>
</tr>
<tr>
<td><code>b</code></td>
<td>For every <code>x, y</code></td>
</tr>
<tr>
<td><code>c</code></td>
<td>For every <code>x, y</code></td>
</tr>
<tr>
<td>Both <code>b</code> and <code>c</code></td>
<td>For every <code>x, y</code></td>
</tr>
</tbody></table>
<img src="https://cdn.hashnode.com/uploads/covers/69ff29ebf239332df4a93c3a/8a203fa4-b03f-4fa8-9219-5a55120740c5.png" alt="" style="display:block;margin:0 auto" />

<p><em>Each point is a pair</em> <code>(x, y)</code><em>. The target</em> <code>(0, 1)</code> <em>is unreachable with neither optional operation permitted, and reachable when</em> <code>b</code> <em>may change. There are no intermediate values between these points.</em></p>
<p>When <code>x ≠ y</code>, permitting just <code>b</code>, or just <code>c</code>, gives a set of permissions that cannot be reduced further while retaining a repair. Such a set is <strong>inclusion-minimal</strong>.</p>
<p>But permitting one additional operation does not mean only one operation will actually change. For example, take <code>(x, y) = (0, 1)</code> and choose the unrestricted value <code>h = 0</code>:</p>
<ul>
<li><p>Using <code>b</code>, we set <code>u = 2</code> and <code>z = 1</code>, changing both <code>e</code> and <code>b</code>.</p>
</li>
<li><p>Using <code>c</code>, we set <code>v = 1</code>, changing only <code>c</code>.</p>
</li>
</ul>
<p>All other correction values are 0. A permission set that cannot be reduced further is different from a repair that changes the fewest operations or lines of code.</p>
<h3>The same connections can require different repairs</h3>
<p>For comparison, replace <code>a_h(t) = -t + h</code> with <code>a_h(t) = t + h</code>, which does not reverse the sign. Keep the same arrows and permissions. Transport on corrections changes from multiplication by −1 to multiplication by 1.</p>
<p>The two additions of <code>h</code> now accumulate instead of canceling. The difference between the targets becomes <code>d = 2h + v</code>, which we can match using <code>h</code> alone. As <code>h</code> ranges over 0, 1, and 2, <code>2h</code> takes the values 0, 2, and 1, covering every possible difference.</p>
<p>In the original network, a nonzero difference required permission to change <code>b</code> or <code>c</code>. In this network, no additional permission is needed. <strong>By changing how corrections propagate, holonomy affects which operations we need permission to change.</strong></p>
<h2>Ariadne: combine local conditions to repair the whole</h2>
<p>To extend this example to a larger network of operations, we need a way to combine the repair conditions for its parts.</p>
<p>In our example, one part could handle <code>u + z = x</code> while another handles <code>u - z + v = y</code>. Their shared values <code>u</code> and <code>z</code> must agree when we put the parts together. A correction that works for one part may be incompatible with the other.</p>
<p>Meanwhile, <code>h</code> does not appear in the shared equations, but we still need it to determine the actual operation <code>a_h</code>. Each part must retain both its shared conditions and the information needed to reconstruct its own operations.</p>
<p>The <strong>Ariadne Boundary Repair Theorem</strong> provides a way to build this information locally and combine it into repairs of the whole. Once constructed, it can answer, for each permitted change scope: Does a repair exist? What repairs are available? If none exists, what prevents one? The construction also takes us from numerical corrections back to the original operations.</p>
<p>The name comes from Ariadne in Greek mythology, who gave Theseus a thread so that he could find his way back out of the labyrinth. It reflects the idea of tracing a route to repair through a network of interdependent operations.</p>
<h3>The difference that no permitted change can remove</h3>
<p>What can serve as evidence that repair is impossible? With neither <code>b</code> nor <code>c</code> permitted to change, the only reachable right-hand sides are <code>(u, u)</code>. Their components always differ by 0. The target <code>(0, 1)</code> has difference 1, so it cannot be reached. That difference proves impossibility without trying every correction.</p>
<p>Now group together pairs that can be transformed into one another by permitted corrections. For example, <code>(0, 1)</code>, <code>(1, 2)</code>, and <code>(2, 0)</code> are related by adding the same amount to both components. All have difference 1. The reachable pairs <code>(0, 0)</code>, <code>(1, 1)</code>, and <code>(2, 2)</code> form the group with difference 0.</p>
<p>A <strong>quotient space</strong> groups together values in this way, removing distinctions that permitted corrections can change and retaining those they cannot. Here, we can decide whether a repair exists by looking at the remaining difference <code>y - x</code>.</p>
<p>In larger finite, linear models, we similarly account for all changes that permitted corrections can produce and ask whether the target has a residual difference. When repair is impossible, we can extract a linear functional that detects a difference no permitted correction can eliminate. The same calculation that searches for a repair can therefore produce evidence of impossibility.</p>
<p>Ariadne connects this calculation with assembly from local parts and reconstruction of the original operations. Its mathematical foundations include <em>relative cohomology</em>, which handles the requirement to preserve a fixed part, and a construction that glues repairs together by making them agree on shared parts.</p>
<h2>Knowing a repair exists is different from knowing the repair</h2>
<p>So far, we have calculated repairs assuming we know <code>x</code> and <code>y</code>. What if we have not obtained those values yet?</p>
<p>Return to the original network, with <code>a_h(t) = -t + h</code>. The structure and equations are known, but the values of <code>x</code> and <code>y</code> are not. Only two observations are allowed:</p>
<p>$$r_x(0)=x,\qquad r_y(0)=y.$$</p>
<p>Evaluating either operation at 0 costs one query. Other computation does not count toward this cost. A <em>numerical repair</em> must return four concrete values <code>(u, h, z, v)</code>.</p>
<h3>Zero queries to say “yes,” two to say how</h3>
<p>Suppose changes to <code>c</code> are permitted. We leave <code>b</code> unchanged, so <code>z = 0</code>.</p>
<p>A repair exists for every <code>x, y</code>. We can therefore answer “Is repair possible?” with “yes” without inspecting either value. That takes zero queries.</p>
<p>What about returning an actual repair? We can use</p>
<p>$$(u,h,z,v)=(x,0,0,y-x).$$</p>
<p>But this is still a formula containing unknown values. To return four specific numbers, we need to obtain both <code>x</code> and <code>y</code>.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69ff29ebf239332df4a93c3a/5d924462-c0ff-4eef-bd43-95657a4a669e.png" alt="" style="display:block;margin:0 auto" />

<p><em>Changes to</em> <code>c</code> <em>are permitted. Each observation narrows the possible inputs. All of them admit a repair, but they do not share a single numerical correction.</em></p>
<p>The illustration also shows why one query is insufficient. After learning <code>x = 0</code>, we still have three possible values for <code>y</code>. Each requires a different <code>v</code>, so we cannot choose a single number yet.</p>
<p>Choosing a different repair does not avoid this problem. Any complete correction tuple determines exactly one pair <code>x, y</code> through <code>u + z = x</code> and <code>u - z + v = y</code>. Querying <code>y</code> first leaves the same uncertainty about <code>x</code>.</p>
<p>Two queries suffice, and one does not. Under these observation rules, the optimal worst-case query count for returning a correct repair on every input is exactly two.</p>
<p>This measures the cost of evaluating the specified operations. It does not measure lines of code read or execution time. The information requirement also changes if we may return a program that calls the unknown operations later, rather than returning concrete values now.</p>
<h3>Use what is already known</h3>
<p>If at least one of <code>b</code> or <code>c</code> may change, the number of additional queries is:</p>
<table>
<thead>
<tr>
<th>Information already available</th>
<th>Decide whether repair is possible</th>
<th>Return one numerical repair</th>
</tr>
</thead>
<tbody><tr>
<td>Neither <code>x</code> nor <code>y</code> is known</td>
<td>0</td>
<td>2</td>
</tr>
<tr>
<td><code>x</code> is known</td>
<td>0</td>
<td>1</td>
</tr>
<tr>
<td>Both are known</td>
<td>0</td>
<td>0</td>
</tr>
</tbody></table>
<p>We count additional observations. If another part of the system has already supplied a concrete value, we do not need to query it again. Knowing the form of an equation, however, does not mean we have obtained its values.</p>
<p>If neither optional operation may change, feasibility depends on <code>x = y</code>. With both values unknown, deciding feasibility and producing a numerical result each require two queries in the worst case. Here, the numerical result is either a concrete repair or an answer that repair is impossible.</p>
<h3>Could the remaining uncertainty change the answer?</h3>
<p>The repair–observation duality results extend this reasoning to more general models. The key is to consider <strong>which inputs remain possible after the observations</strong>.</p>
<p>For a yes-or-no answer, it is enough that all remaining inputs are repairable, or that all are impossible to repair. To return a concrete repair, a single correction must work for every remaining input. If all are impossible to repair, we can report that instead.</p>
<p>The theorems consider families of inputs with shared structure and correction transport, where the unknown values vary affinely. In our example, the operations keep the same form while the added values <code>x</code> and <code>y</code> vary. For finite-dimensional models over a finite field, with linear known information and permitted observations, the repair equations determine which input differences must be distinguished. When sufficient observations are available, we can choose a smallest set.</p>
<p>The resulting query count is optimal in the worst case, even among procedures that choose their next query based on earlier answers. The procedures considered are deterministic, terminate on every input, and always return a correct answer. The results also determine when the permitted observations cannot distinguish inputs well enough to produce the required answer.</p>
<h2>What the model tells us</h2>
<p>The calculations lead to three conclusions.</p>
<p><strong>1. Once we specify what may change, we can find repairs or establish why none exists.</strong></p>
<p>With neither optional operation permitted, a nonzero difference <code>y - x</code> proves that repair is impossible. Allowing changes to <code>b</code> or <code>c</code> gives us a way to make up that difference.</p>
<p><strong>2. Connections alone do not determine what must change. The operations matter too.</strong></p>
<p>With the sign-reversing loop, the correction <code>h</code> cancels. Without the sign reversal, that correction alone is enough. Holonomy affects the required change permissions.</p>
<p><strong>3. Deciding that a repair exists can require less information than producing one.</strong></p>
<p>In the original network, permitting either optional operation makes the feasibility decision possible with zero observations. A numerical correction still requires <code>x</code> and <code>y</code>: two additional queries if both are unknown, one if either is already known.</p>
<p>For networks satisfying their assumptions, Ariadne and the repair–observation duality results connect these questions through the same repair equations.</p>
<h2>Where dependency analysis and program repair fit</h2>
<p>Software engineering has a long history of studying the effects of change.</p>
<p>Weiser's <em>program slicing</em> focuses on a variable at a chosen point in a program and retains statements needed to preserve its value. Its treatment of data and control dependencies is relevant to tracing the checks that use our Japanese status string. <a href="https://faculty.cc.gatech.edu/~harrold/6340/cs6340_fall2009/Readings/Weiser.Slicing.TSE.pdf">Program Slicing, 1984</a>.</p>
<p>Automated program repair also handles changes at multiple locations. Angelix, for example, derives repair constraints through symbolic execution and synthesizes patches across interdependent locations. <a href="https://discovery.ucl.ac.uk/id/eprint/10088929/">Angelix: Scalable Multiline Program Patch Synthesis via Symbolic Analysis, 2016</a>.</p>
<p>Rothenberg and Grumberg's <em>Must Fault Localization for Program Repair</em> studies sets of locations such that every repair must modify at least one location in the set. These sets depend on the chosen repair scheme and the changes it permits. The question “Can this be repaired while leaving this entire region untouched?” is already explicit in existing research. <a href="https://link.springer.com/chapter/10.1007/978-3-030-53291-8_33">Must Fault Localization for Program Repair, 2020</a>.</p>
<p>The AAT construction described here starts from local representations, obtains repairs and impossibility evidence for every permission scope, and provides a correspondence back to the original operations. It also derives the required observations from the same equations. Connecting this with existing techniques is a next research step: use dependency analysis to identify the relevant behavior, establish its correspondence with the model, and check the resulting code changes through repair tools and testing.</p>
<h2>The multilingual refactoring workflow I want ArchSig to support</h2>
<p>Return to the order-management system. Ariadne, the repair–observation duality results, and the small model in this article have been proved in the research Lean formalization. Using them in development requires a way to map real code to models satisfying the theorems' assumptions and translate the resulting corrections back into code changes.</p>
<p>Verifying the correspondence between code and model, implementing the results in ArchSig, and evaluating them in practice are work ahead. The workflow I want to build would look like this.</p>
<p><strong>1. Tell the agent what must be preserved.</strong></p>
<p>“I want to add multilingual support to the site. Internal order states should be independent of the display language. Return eligibility and the values returned by the existing API must stay the same.” Start by giving your usual AI agent the goal and the constraints.</p>
<p><strong>2. Have the agent trace where the status is used.</strong></p>
<p>The agent follows order creation, return-button visibility, the return API's eligibility checks, and the batch job's selection criteria. It records which operations expect <code>"発送済み"</code>, together with their source locations. ArchSig receives both these facts about the implementation and the requirements to preserve.</p>
<p><strong>3. Compare repairs under different change permissions.</strong></p>
<p>ArchSig analyzes the model under progressively broader permissions: from UI-only changes to changes in the internal state representation and business checks, with a conversion at the legacy API boundary. For scopes that cannot satisfy the conditions, it identifies the obstruction. For those that can, it provides concrete repair options. You use that evidence to decide how far the change should extend.</p>
<p><strong>4. Obtain the missing information needed to decide or implement the repair.</strong></p>
<p>For example, building the compatibility conversion may require a complete list of status values in existing API responses. ArchSig identifies the information required by the model, and the agent checks the corresponding code or specifications. Information already obtained is reused.</p>
<p><strong>5. Change the code and verify the preserved behavior.</strong></p>
<p>Following the chosen repair, the agent moves internal checks to status IDs, translates labels for display, and converts back to legacy values at the API boundary. It updates the model from the changed code and reruns the analysis. Tests also check that the return button appears under the same conditions in Japanese and English, and that existing API responses remain unchanged.</p>
<p>Runtime concerns such as races, I/O behavior, and performance are checked with appropriate methods, including end-to-end and load tests.</p>
<p>A request that initially looked like a UI change can then be evaluated as a refactoring that spans business logic and API behavior. The developer uses the evidence to choose the change scope, and the agent implements and checks the change within those conditions.</p>
<p>In my AAT research, “Rising Sea” names the direction of connecting descriptions of operations, repairs, and required observations within one mathematical construction. These results let us move from “What must change to make this work?” to “What must we know to make that change?”</p>
<p>By verifying the correspondence between code and model and integrating this approach into development, I want to make it possible to <strong>refactor with checkable evidence that the requirements we care about are preserved, without having to reread every line of code</strong>. A request as ordinary as “add multilingual support” should come with a reasoned account of what to change and what to verify. That is the development workflow I want ArchSig to support.</p>
<p>The mathematical definitions and Lean formalizations are available in the <a href="https://github.com/iroha1203/AlgebraicArchitectureTheoryV2">AAT repository</a>, including the accounts of relative boundary repair and repair–observation duality and their proofs.</p>
]]></content:encoded></item><item><title><![CDATA[The Mathematics of Software Change]]></title><description><![CDATA[What should software engineering provide in an age when AI writes code?
We divide design work across teams, implement features, and migrate systems that are already running. Throughout that work, we n]]></description><link>https://blog.iroha1203.dev/the-mathematics-of-software-change</link><guid isPermaLink="true">https://blog.iroha1203.dev/the-mathematics-of-software-change</guid><category><![CDATA[AI]]></category><category><![CDATA[Software Engineering]]></category><category><![CDATA[software architecture]]></category><category><![CDATA[development]]></category><dc:creator><![CDATA[Hiroyuki Nakahata]]></dc:creator><pubDate>Thu, 24 Sep 2026 17:42:36 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/69ff29ebf239332df4a93c3a/4052a5f9-bf92-4474-b2cb-9fb2561e2808.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>What should software engineering provide in an age when AI writes code?</p>
<p>We divide design work across teams, implement features, and migrate systems that are already running. Throughout that work, we need to make judgments: Will this update break another feature's assumptions? Will the same operations still work after the data migration? Will the designs for these services fit together? As AI accelerates development, we need clearer accounts of what must stay intact, what may change, and why we can trust the system to remain consistent.</p>
<p><strong>Algebraic Architecture Theory (AAT)</strong> is the theory I am developing to make these questions mathematical. It provides a way to state what must be preserved and describe, in a form that can be checked, how parts fit together and how structures relate before and after a change.</p>
<p>I have published a paper laying out its foundations:</p>
<blockquote>
<p><a href="https://arxiv.org/abs/2609.27638">Foundations of Algebraic Architecture Theory: A Rising Sea of Geometry, Transport, Comparison, and Reconstruction</a></p>
</blockquote>
<p>The paper runs to 285 pages. AAT is a new theory I am building, so presenting individual results also requires establishing the objects they concern and the definitions and assumptions behind them. I chose to develop the foundations and the main theorems together as a coherent whole.</p>
<p>I also wanted a mathematical reference for future papers: a place to look up definitions, theorems, and proofs, rather than rebuilding the foundations every time I address a particular problem or application.</p>
<h2>Five questions that recur in software development</h2>
<p>Change a data representation. Extract shared functionality. Split a system into services. Across different technologies and projects, the same questions return: What survives the change, and how do the parts remain consistent? The paper organizes these concerns around five questions.</p>
<p><strong>1. Can individually correct parts form a consistent whole?</strong></p>
<p>Each part may satisfy its own requirements while disagreeing with another part about shared data or assumptions. What conditions and corrections would let us assemble a whole? (Chapter 2)</p>
<p><strong>2. Can a summary reveal the same inconsistencies?</strong></p>
<p>When we summarize a complex system or examine it at a different level of detail, which information and relationships must remain for the diagnosis to stay the same? (Chapter 3)</p>
<p><strong>3. Can different migration paths and task orders preserve the same behavior?</strong></p>
<p>We might migrate in one step or in stages. We might select the relevant design documents before transporting a model, or transport it first and then select the corresponding documents. When do these routes preserve the operations and conditions we care about? (Chapters 4–5)</p>
<p><strong>4. If changes look the same, can we tell which ones preserve the required behavior?</strong></p>
<p>Two changes may produce the same visible output but behave differently when we perform an operation. What would we need to enumerate every candidate that preserves the required behavior? And when can we judge a candidate from visible output alone? (Chapters 6–7)</p>
<p><strong>5. Can separate teams' designs determine the overall design and migration?</strong></p>
<p>When teams divide up the work, what must each team specify, and what must agree where their responsibilities meet? Can their specifications determine a unique migration of the whole system? (Chapter 8)</p>
<h2>A running example: migrating an e-commerce system</h2>
<p>Consider an e-commerce system with three services:</p>
<table>
<thead>
<tr>
<th>Service</th>
<th>Responsibility</th>
</tr>
</thead>
<tbody><tr>
<td>Orders</td>
<td>Manage orders and shipping addresses; accept address changes and cancellations</td>
</tr>
<tr>
<td>Payments</td>
<td>Manage payment status; process payments and refunds</td>
</tr>
<tr>
<td>Inventory</td>
<td>Reserve items for orders; release reservations when orders are canceled</td>
</tr>
</tbody></table>
<p>All three services refer to the same order by its ID. Canceling a paid order requires an order status change, a refund, and the release of its inventory reservation.</p>
<p>Take order 42: it ships to the customer's home, costs $12.33, has been paid for, and has inventory reserved. As the teams revise their data formats and divide up the processing differently, we still need to be able to change its shipping address and cancel it with the appropriate refund and reservation release. We will follow this order through the article.</p>
<h2>Chapter 1: Decide what to preserve, then build the model</h2>
<p>Do we only need to preserve the displayed shipping address? Or must we also preserve how address updates behave and how they interact with payment information? We cannot judge a change until we decide. AAT starts by making the model's contents, operations, and required conditions explicit.</p>
<p>For our example, we include a state containing a shipping address and payment information, an operation that reads the address, and an operation that updates it.</p>
<p>AAT calls a typed basic fact—such as the existence of a component or a named operation—an <strong>Atom</strong>. Here are some Atoms we could describe:</p>
<table>
<thead>
<tr>
<th>Kind</th>
<th>Example fact</th>
</tr>
</thead>
<tbody><tr>
<td>Component</td>
<td>There is a component for order states</td>
</tr>
<tr>
<td>Component</td>
<td>There is a component for shipping addresses</td>
</tr>
<tr>
<td>Operation</td>
<td>There is a <code>read</code> operation that takes an order state and returns its shipping address</td>
</tr>
<tr>
<td>Operation</td>
<td>There is an <code>update</code> operation that takes an order state and an address, and returns the updated order state</td>
</tr>
</tbody></table>
<p>We also specify how those operations must behave. A requirement expressed as an equation is called a <strong>Law</strong>.</p>
<p>Let <code>s</code> be an order state and <code>a</code> a shipping address. Then <code>read(s)</code> is the current address, and <code>update(s, a)</code> is the state after setting the address to <code>a</code>.</p>
<p>Reading the address after an update should return the address we just supplied:</p>
<p>$$\operatorname{read}(\operatorname{update}(s, a)) = a$$</p>
<p>If we change an order from a home address to a work address, reading it should return the work address. This is the familiar expectation that a getter returns the value supplied to a setter.</p>
<p>We can also require that writing back the current address leaves the order unchanged:</p>
<p>$$\operatorname{update}(s, \operatorname{read}(s)) = s$$</p>
<p>The right-hand side is the <strong>entire order state</strong>, not just the address. An operation that changes payment information when we set an already home-bound order to “home” again would violate this Law. These equations must hold for every order state <code>s</code> and every address <code>a</code> to which they apply.</p>
<p>Expressing the structure and conditions we want to analyze through Atoms and Laws makes the analysis independent of a particular programming language or framework. Different implementations can be described in a common language: which components and operations exist, and what they must satisfy.</p>
<h3>From software requirements to geometry</h3>
<p>Let <code>x</code> be the refund expected by the order service and <code>y</code> the refund recorded by the payment service. The requirement that they agree is <code>x - y = 0</code>. The pairs <code>(x, y)</code> satisfying that equation form a line in the plane. We have moved from checking one order to studying the space of states the requirement permits.</p>
<p>The idea extends to more values and more conditions: represent states with variables, express requirements as equations, and study their solutions. This is the entry point from software descriptions to algebraic geometry. AAT makes explicit both this interpretation and how operations act on states.</p>
<p>No single service holds the entire state of our e-commerce system. Orders holds cancellation status and the expected refund; Payments holds the refund amount; Inventory holds the reservation status. We treat these as parts, linked through shared order IDs and amounts. Now we can ask: <strong>Is there a state that satisfies all their conditions at once?</strong> Studying solutions lets us move from checking individual orders to asking whether the requirements themselves can coexist.</p>
<p>Chapter 1 establishes the foundations for moving from facts to models equipped with operations, Laws, and relationships between parts. Later chapters use that geometry to study consistency and migrations.</p>
<h2>Chapter 2: Can the records fit together?</h2>
<p>Suppose an order cancellation triggers a refund. What happens if the order service and the payment service handle the amount differently?</p>
<p>For this example, a cancellation refunds 80% of the purchase price. For order 42:</p>
<pre><code class="language-text">12.33 × 0.8 = 9.864 dollars
</code></pre>
<p>The actual payment must be rounded to cents. But suppose the revised processes follow these rules:</p>
<table>
<thead>
<tr>
<th>Process</th>
<th>Rule</th>
<th>Value for order 42</th>
</tr>
</thead>
<tbody><tr>
<td>Order storage</td>
<td>Store the purchase price</td>
<td>$12.33</td>
</tr>
<tr>
<td>Cancellation in Orders</td>
<td>Calculate 80% and round to two decimal places</td>
<td>$9.86</td>
</tr>
<tr>
<td>Refund reconciliation in Payments</td>
<td>Calculate exactly 80% using decimal arithmetic</td>
<td>$9.864</td>
</tr>
</tbody></table>
<p>Both processes follow their own rules, yet they disagree by $0.004 about the same refund. The discrepancy is <strong>a rounding difference smaller than one cent</strong>.</p>
<p>What changes would reconcile them?</p>
<table>
<thead>
<tr>
<th>Allowed change</th>
<th>Result for order 42</th>
</tr>
</thead>
<tbody><tr>
<td>Leave both amounts and reconciliation rules unchanged</td>
<td><code>9.86 ≠ 9.864</code></td>
</tr>
<tr>
<td>Apply the same rounding rule during reconciliation</td>
<td>Both sides use <code>9.86</code></td>
</tr>
<tr>
<td>Record the rounding adjustment and include it in reconciliation</td>
<td><code>9.86 + 0.004 = 9.864</code></td>
</tr>
</tbody></table>
<p>The last approach retains the difference but accounts for it. Whether reconciliation is possible depends on which repairs we allow.</p>
<p>Shared information about orders and amounts forms an <em>overlap</em> between processes. Combining partial data according to the agreed relationships on those overlaps is what mathematics calls <strong>gluing</strong>. A <strong>sheaf</strong> captures the relationship between reading parts of a whole and uniquely assembling a whole from compatible partial data.</p>
<h3>Each pair can be reconciled, but all three cannot</h3>
<p>Now apply the second repair: Payments also uses the rounded amount, $9.86. Orders and Payments agree. Then introduce a reconciliation ledger on the payment side, so that we compare the cancellation record, the refund record, and the ledger.</p>
<p>Assign one adjustment to each record. Every comparison involving that record must use the same adjustment.</p>
<p>Suppose two comparisons use rounded amounts, while the third requires the ledger to account for the difference from the unrounded amount. They impose the following requirements:</p>
<table>
<thead>
<tr>
<th>Records being compared</th>
<th>Required relationship between adjustments</th>
</tr>
</thead>
<tbody><tr>
<td>Orders and Payments</td>
<td>Payments adjustment − Orders adjustment = 0</td>
</tr>
<tr>
<td>Payments and Ledger</td>
<td>Ledger adjustment − Payments adjustment = 0</td>
</tr>
<tr>
<td>Orders and Ledger</td>
<td>Ledger adjustment − Orders adjustment = $0.004</td>
</tr>
</tbody></table>
<p>Any one requirement can be satisfied on its own. But adding the first two gives “Ledger adjustment − Orders adjustment = 0,” contradicting the third. <strong>We can choose adjustments for each pair separately, but no single choice satisfies all three pairs.</strong></p>
<p>Follow the comparisons around the loop: Orders → Payments → Ledger → Orders. The last step traverses the Orders–Ledger comparison backward, so we subtract its required difference. The total is <code>0 + 0 − 0.004 = −0.004</code> dollars. Changing the adjustment at any record cannot change this total: its contributions cancel around the loop.</p>
<p>Examples 2.19 and 2.27 in the paper study this kind of residual around a loop. In this model, adjustments may take any value, and the comparison data consists of the three pairs in the table. We do not add a separate three-way comparison.</p>
<p><strong>Cohomology</strong> treats differences removable by the allowed adjustments as equivalent, and records what remains. Here, the loop total is an obstruction that those adjustments cannot remove. If we revise the third comparison to use the rounded amount as well, the loop total becomes zero, and setting every adjustment to zero works. That repair changes a reconciliation rule; it does more than choose new adjustments under the existing rules.</p>
<p>Chapter 2 states conditions connecting such an obstruction to the existence of a whole. For example, adjusting an order record and then reading its shared refund amount must give the same result as applying that adjustment directly to the shared amount. When adjustments are compatible with reading partial data in this way, and states form a sheaf, the following are equivalent (Theorem 2.22):</p>
<ul>
<li><p>The obstruction is zero.</p>
</li>
<li><p>The local states can be adjusted in the allowed ways and glued into a global state.</p>
</li>
</ul>
<p>A nonzero obstruction means there is no such global state within the fixed model and allowed adjustments. When corrections are possible, the corrected partial data determines a unique whole. The choice of corrections itself need not be unique.</p>
<h2>Chapter 3: Can a summary preserve the diagnosis?</h2>
<p>Suppose we summarize service records to review a change to cancellation processing. Omitting a full street address and rounding a refund amount have different consequences. Displaying both refund values for order 42 as “$9.86” would hide the discrepancy we started with. Which information must survive the summary?</p>
<h3>Preserving Law values is not yet preserving the diagnosis</h3>
<p>Chapter 3 begins by fixing the Laws we want to examine and their evaluation values—for example, the difference between the two sides of an equation. Put states in the same group exactly when all the selected Laws evaluate to the same values. Keep states with different values in separate groups. This retains precisely the distinctions needed to preserve those evaluations (Theorem 3.4).</p>
<p>But evaluating a Law within each part and diagnosing disagreement between parts are different tasks. Keeping <code>9.86</code> and <code>9.864</code> is not enough if we accidentally compare amounts from different orders. We also need to know that these are the expected and recorded refunds for order 42.</p>
<p>The paper therefore gives conditions relating which records we read, which pairs we compare, and how we calculate differences before and after summarization. In the three-record example, this includes the comparison loop and the treatment of adjustments, as well as the values in the records. Under these conditions, the diagnoses correspond one-to-one, and one is zero exactly when the other is (Theorem 3.17). Summarizing neither hides an existing obstruction nor invents a new one.</p>
<p>When the necessary values and comparison relationships can be enumerated and computed as finite data, the preservation condition can itself be decided by a finite computation. This concerns Laws evaluable from both summaries: the criterion determines whether diagnosis is preserved for every finite selection of those Laws (Theorem 3.22 and Proposition 3.23).</p>
<p>For code review in the age of AI, this suggests a concrete research question: What must a reviewer retain to reduce the reading burden without overlooking inconsistencies? Turning the mathematical preservation conditions into practical review methods remains future work.</p>
<h2>Chapters 4–5: Comparing migration paths and task order</h2>
<p>Next, change the order data format. The old version stores the address in a top-level <code>shipping_address</code> field. The new version groups shipping information in a <code>shipping</code> object, with the address in <code>shipping.address</code>. An intermediate version that merely renamed the field to <code>shippingAddress</code> is already in use.</p>
<p>We can migrate directly, or chain existing migrations through the intermediate version:</p>
<table>
<thead>
<tr>
<th>Route</th>
<th>Where the address is stored</th>
</tr>
</thead>
<tbody><tr>
<td>Direct</td>
<td><code>shipping_address → shipping.address</code></td>
</tr>
<tr>
<td>Via the intermediate version</td>
<td><code>shipping_address → shippingAddress → shipping.address</code></td>
</tr>
</tbody></table>
<p>Order 42 should retain the same address along either route. Address updates must also move to the new field, and must continue to leave payment information unchanged.</p>
<h3>The rest of the migration is not a free choice</h3>
<p>Chapter 4 constructs <strong>transport</strong>: a way to carry operations, Laws, and relationships between parts together along a change that satisfies conditions including a one-to-one correspondence between extracted Atoms.</p>
<p>Fix the overall migration from old to new, and fix the underlying schema mappings from old to intermediate to new. Transport the old structure to the intermediate version. Then <strong>there is exactly one migration from that transported model to the new model that realizes the overall migration we specified</strong> (Theorems 4.5 and 4.11).</p>
<p>Adding an intermediate version seems as though it should introduce more choices. But when we construct it by this transport, there is no further choice of a continuation compatible with the overall migration. The factorization preserves the modeled update operations and Laws, not just the location of the address field. The uniqueness concerns the migration between mathematical models; it does not mean there is only one way to write the implementing program.</p>
<p>We can also compare a model transported directly to <code>shipping.address</code> with one transported through <code>shippingAddress</code>. The paper constructs a correspondence that lets us move between the two while preserving operations and Laws. Adding more intermediate versions does not introduce ambiguity: comparing one stage at a time or grouping stages together produces the same final correspondence (Theorem 4.15).</p>
<p>Independently specified migrations can still disagree. If one swaps the shipping and billing addresses, the route changes the resulting address. For a finite collection of paths, the paper shows that the transformations can be reselected to satisfy all the specified comparisons exactly when the obstruction to their agreement vanishes (Theorem 4.23).</p>
<h3>Combining documents need not lose the structure they describe</h3>
<p>Now choose which design documents to review. Suppose the old web and mobile app cancellation specifications both map to a shared cancellation specification in the new design.</p>
<table>
<thead>
<tr>
<th>Old document</th>
<th>Corresponding new document</th>
</tr>
</thead>
<tbody><tr>
<td>Web cancellation specification</td>
<td>Shared cancellation specification</td>
</tr>
<tr>
<td>App cancellation specification</td>
<td>Shared cancellation specification</td>
</tr>
<tr>
<td>Address-change specification</td>
<td>Address-change specification</td>
</tr>
</tbody></table>
<p>Once two documents map to one, the new document's name cannot tell us whether we started from the web or app specification. Must the model derived from those documents also become impossible to recover?</p>
<p>Suppose both cancellation specifications yield the same Atoms and are equipped with the same operations and Laws. Losing the distinction between their origins need not lose those structures. The paper shows that <strong>even when the mapping of source documents is not invertible, models built from a selected document can still be related by a reversible, structure-preserving correspondence</strong> (Proposition 5.8 and Example 5.9). What we recover is the structure of types, operations, and Laws, rather than the history of the documents.</p>
<p>If we select the shared cancellation specification in the new version, we must therefore pick up both the web and app specifications on the old side. Picking only one would give us a mismatched scope.</p>
<p>A model is built by reading types, operations, and Laws from the documents. Chapter 5 constructs pairs of corresponding sources and gives a way to read model structure over the selected sources.</p>
<p>It compares two routes:</p>
<ul>
<li><p>Select the old web and app cancellation specifications, then transport the model built from them to the new version.</p>
</li>
<li><p>Transport the old structure to the new version, then read the structure over the shared cancellation specification.</p>
</li>
</ul>
<p>Under the specified conditions on source mappings, extracted Atoms, operations, and Laws, these routes yield models connected by a reversible, structure-preserving correspondence (Theorem 5.11). Separating how documents are consolidated from which model structure is preserved lets us explain why different task orders can still retain the same operations and Laws.</p>
<h2>Chapters 6–7: What remains after we leave information out?</h2>
<h3>The remaining structure can still correspond</h3>
<p>So far, we have transported structure while preserving it. Now, to focus on addresses and their updates, set every payment code in the comparison data to “unset.” We can still read and update addresses, but we cannot recover whether the payment code was originally 1 or 2.</p>
<p>Running this process twice has the same effect as running it once. That property is called <strong>idempotence</strong>. The paper uses <strong>normalization</strong> for a process that puts objects into a standard form while preserving the required structure. This is a different use of the word from database normalization.</p>
<p>For order 42, set payment information to “unset” and match the old <code>shipping_address</code> with the new <code>shipping.address</code>. Changing the old address to work corresponds to changing the new address to work. The payment information cannot be recovered, but the address and its update behavior can still be related in both directions.</p>
<p>Chapter 6 constructs an idempotent normalization of the full structure under conditions that preserve operations, Law evaluation values, and other required data (Theorem 6.14). The comparison studied here can be understood as a composition of transporting structure and then normalizing it to leave information out.</p>
<p>The interesting result is that <strong>losing information irreversibly can still leave a reversible correspondence between the structures that remain</strong> (Theorem 6.12). Starting from an invertible correspondence, define the normalizations so that both sides retain matching structure. The remaining parts are then isomorphic: we can move between them in both directions while preserving structure.</p>
<p>By contrast, the comparison constructed here is invertible on the original full structures if and only if normalization changes nothing (Theorem 6.16). The distinction makes explicit how much of the original structure the comparison preserves.</p>
<h3>The same display does not imply the same update behavior</h3>
<p>Chapter 7 returns to migrations of the complete order. Even if addresses and updates correspond after excluding payment information from the comparison, the migration of the full order may still be unsuitable.</p>
<p>Write the addresses as “home” and “work,” and focus on the internal payment code. In both the old and new systems, editing the address leaves payment information unchanged. But suppose the migration converts payment codes differently depending on the address:</p>
<table>
<thead>
<tr>
<th>Before migration</th>
<th>After migration</th>
</tr>
</thead>
<tbody><tr>
<td>Address: home; payment code: 1</td>
<td>Address: home; payment code: 1</td>
</tr>
<tr>
<td>Address: work; payment code: 1</td>
<td>Address: work; payment code: <strong>2</strong></td>
</tr>
</tbody></table>
<p>Start from the same order: home, payment code 1. Now compare when we change the address to work.</p>
<ol>
<li><p><strong>Migrate, then update.</strong> The home order migrates with code 1. Updating only its address leaves us with <strong>work, code 1</strong>.</p>
</li>
<li><p><strong>Update, then migrate.</strong> We first change the old order's address to work. It then migrates as a work order, giving <strong>work, code 2</strong>.</p>
</li>
</ol>
<p>The displayed address agrees, but the payment information does not. What we want is <strong>the same order state whether we migrate before or after the update</strong>.</p>
<h3>Four candidates become two</h3>
<p>Restrict addresses to home and work, and payment codes to 0, 1, and 2. Keep 0 fixed as the “unset” value used in Chapter 6. For codes 1 and 2, either keep them as they are or swap them. The mapping between old and new codes is itself a design choice.</p>
<table>
<thead>
<tr>
<th>Candidate</th>
<th>Home orders</th>
<th>Work orders</th>
<th>Preserves address updates?</th>
</tr>
</thead>
<tbody><tr>
<td>A</td>
<td>Keep codes unchanged</td>
<td>Keep codes unchanged</td>
<td>Yes</td>
</tr>
<tr>
<td>B</td>
<td>Keep codes unchanged</td>
<td>Swap 1 and 2</td>
<td>No</td>
</tr>
<tr>
<td>C</td>
<td>Swap 1 and 2</td>
<td>Keep codes unchanged</td>
<td>No</td>
</tr>
<tr>
<td>D</td>
<td>Swap 1 and 2</td>
<td>Swap 1 and 2</td>
<td>Yes</td>
</tr>
</tbody></table>
<p>All four preserve the displayed address and the unset value. Only A and D preserve updates. Because an update can take us between home and work, both must use the same code mapping. D is valid because it relabels the codes consistently. If code 1 must literally remain code 1, we must state that as an additional requirement.</p>
<p>Chapter 7 generalizes this for systems whose operations preserve internal state. With the visible state fixed and internal states relabeled bijectively, view visible states as vertices and operation-induced connections as edges. Each connected component must use one consistent relabeling; different components can choose independently. <strong>These choices describe all changes of this kind that preserve the operations</strong> (Theorem 7.24). In the finite case, we can also count them. The four-candidate example is based on Example 7.28 in the paper.</p>
<h3>Judging a candidate and constructing one are different tasks</h3>
<p>The display cannot distinguish A from B. Add “swap codes 1 and 2 only for work orders” to A, and we get B. The displayed address stays the same, but preservation of updates is lost.</p>
<p>More generally, if an invisible modification can change whether a candidate is acceptable, observation alone cannot decide the question. The paper proves a necessary and sufficient condition: adding changes invisible to the observation must leave acceptability unchanged (Theorem 7.9).</p>
<p>To construct an acceptable candidate, however, we can start with A. Add “swap codes 1 and 2 for both home and work,” and we get D. An invisible modification that preserves updates gives us another acceptable candidate.</p>
<p>The paper provides a way to start with a migration of normalized models and supply mappings for the information that was left out. Here, that means adding a payment-code mapping to an address-only migration to obtain a migration of the complete order. It constructs one migration satisfying the requirements and describes all the other candidates from there (Theorems 7.19 and 7.21).</p>
<p>This requires the original models to be related by an invertible, structure-preserving comparison. It also requires the normalization—which determines what is retained and what is hidden—and its associated data to admit a common representation on both sides.</p>
<p>Constructing an acceptable candidate with this information is a different problem from judging a supplied candidate by its display alone.</p>
<h2>Chapter 8: Building the whole from separate teams' designs</h2>
<p>Finally, divide the design and migration work among three teams.</p>
<table>
<thead>
<tr>
<th>Team</th>
<th>What it designs</th>
<th>What must agree with other teams</th>
</tr>
</thead>
<tbody><tr>
<td>Orders</td>
<td>Order types and states, cancellation operations, old-to-new mappings</td>
<td>Order IDs and the conditions for completed cancellation</td>
</tr>
<tr>
<td>Payments</td>
<td>Payment and refund types and amounts, refund operations, old-to-new mappings</td>
<td>References to the same order, refund amounts, rounding rules</td>
</tr>
<tr>
<td>Inventory</td>
<td>Product and reservation types and states, release operations, old-to-new mappings</td>
<td>References to the same order and reservation release on cancellation</td>
</tr>
</tbody></table>
<p>Agreeing on order IDs is not enough. Adopt the Law that a cancellation is complete only when the required refund has been made and the inventory reservation released. All three teams might map order 42 to the same new order, yet map its states to “cancellation complete,” “refunded,” and “still reserved.” That migration would not preserve the Law.</p>
<p>Operations need to correspond too. Releasing a reservation in the old system and then migrating must agree with migrating first and performing the corresponding release in the new system. Each team specifies its types, values, operations, and Laws, together with how shared information is mapped.</p>
<p>The paper's main result, the <strong>local reconstruction theorem</strong>, shows that <strong>we can assemble the whole from descriptions of its parts without assuming a completed whole in advance</strong>. Give the parts' descriptions separately, and check their compatibility on shared data, operations, Laws, and the other required structure. Two results follow.</p>
<p>First, <strong>the whole structure can be recovered up to isomorphism</strong>. Names or representations may differ, but the objects, operations, and other structure correspond in both directions.</p>
<p>Second, <strong>once the old and new structures are fixed, compatible local migrations determine a unique structure-preserving migration of the whole</strong>. With the required descriptions in place, there is no room for two different global migrations that look identical on every specified part.</p>
<p>Chapter 2 asked whether amounts and their comparison rules fit together into a consistent whole. Here, the things we assemble include data types, operations, and the migration itself.</p>
<p>This conclusion requires a complete description of the model's data: types, values, operations, Laws, which values are read from which parts, and the rest of the specified structure. The descriptions must agree where they overlap. The main result is Theorem 8.18; Section 8.6 applies it to design work divided among teams.</p>
<h2>Returning to the five questions</h2>
<p>What answers did we get by following the e-commerce example?</p>
<p><strong>1. Can individually correct parts form a consistent whole?</strong></p>
<p>Following each process's own rules did not make order 42's refund records agree. Under the theorem's conditions, the existence of allowed corrections that reconcile the whole is equivalent to the gluing obstruction being zero.</p>
<p><strong>2. Can a summary reveal the same inconsistencies?</strong></p>
<p>Rounding away digits can hide a refund discrepancy. Losing the link to the order can lose the comparison partner. The paper provides conditions for preserving diagnosis across changes in values and relationships, along with a decision criterion for the finite setting.</p>
<p><strong>3. Can different migration paths and task orders preserve the same behavior?</strong></p>
<p>We compared direct and staged migrations, and selecting cancellation specifications before or after transport. Under the conditions that preserve structure and the mappings between sources, these routes can be compared coherently. Disagreements among specified paths can also be studied as obstructions.</p>
<p><strong>4. If changes look the same, can we tell which ones preserve the required behavior?</strong></p>
<p>The address display could not distinguish the four payment-code mappings. Examining updates reduced them to two. The paper separates classification and construction of acceptable changes from the conditions for judging them by observation.</p>
<p><strong>5. Can separate teams' designs determine the overall design and migration?</strong></p>
<p>Orders, Payments, and Inventory supplied types, operations, shared order references, and completion conditions. With all the required design data and compatibility conditions, the whole structure is recoverable up to isomorphism, and a migration between fixed old and new structures is uniquely determined.</p>
<h2>Beyond the Rising Sea</h2>
<p>The title <em>Rising Sea</em> expresses an approach: develop a common theory broad enough to bring individual problems within reach. This paper establishes foundations for studying consistency, migration comparisons, and reconstruction within the same mathematics.</p>
<p>The next step is research on extracting structures and Laws from real code and specifications, working at the scale of software development, and testing whether this helps engineering judgment in practice. As AI produces changes faster, I want to give us stronger grounds for taking responsibility for them.</p>
<p><strong>What does this change preserve—and how can we establish that?</strong></p>
<h2>Finding your way through the paper</h2>
<p>The <a href="https://arxiv.org/abs/2609.27638">paper's arXiv page</a> provides PDF and HTML versions. Chapter numbers in this article refer to the paper; Chapters 4–5 and 6–7 are grouped here. The theorem references lead to the precise assumptions and proofs.</p>
<p>The four address-update candidates appear in Example 7.28 in Section 7.8. The application of reconstruction to designs developed by separate teams is in Section 8.6. Appendices A and B document the correspondence with Lean declarations and the verification procedures.</p>
]]></content:encoded></item><item><title><![CDATA[AI Reads Your Codebase. Then It Forgets Everything.]]></title><description><![CDATA[AI Reads the Code. How Do We Keep What It Learns?
“Move email delivery to a shared asynchronous queue.”
The request fits in a sentence. The understanding needed to carry it out is scattered across mor]]></description><link>https://blog.iroha1203.dev/ai-reads-your-codebase-then-it-forgets-everything</link><guid isPermaLink="true">https://blog.iroha1203.dev/ai-reads-your-codebase-then-it-forgets-everything</guid><category><![CDATA[AI]]></category><category><![CDATA[software architecture]]></category><category><![CDATA[Software Engineering]]></category><category><![CDATA[development]]></category><dc:creator><![CDATA[Hiroyuki Nakahata]]></dc:creator><pubDate>Mon, 07 Sep 2026 14:55:20 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/69ff29ebf239332df4a93c3a/86b739a6-cbad-432c-a46b-d7cb3ef13115.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2>AI Reads the Code. How Do We Keep What It Learns?</h2>
<p>“Move email delivery to a shared asynchronous queue.”</p>
<p>The request fits in a sentence. The understanding needed to carry it out is scattered across more than 200,000 lines of code.</p>
<p>The sending function is easy to find. Follow its callers, though, and authentication codes and newsletters turn out to use the same implementation. One has expiration rules; the other has unsubscribe requirements. Queue delays and retries affect the conditions under which each feature works.</p>
<p>In an environment where the entire codebase cannot fit into context at once, an AI agent alternates between searching and reading. It cross-checks authentication flows, delivery settings, tests, and design documents to reconstruct dependencies and assumptions. Before it can judge the change, it uses a substantial number of tokens to recover meaning distributed across the code.</p>
<p>How much of that understanding will survive into the next task? Unless the relationships and the reasoning behind its decisions are retained in a reusable form, another session will repeat much of the investigation.</p>
<p><strong>I want the understanding gained from reading code to persist as a structure that people and AI can share and use for the next change.</strong> Ontologies offer a starting point by making concepts and relationships explicit. I am developing a vision for a semantic layer that also captures meaning in context, supports consistency analysis, and lets us reason about what changes preserve.</p>
<p>The foundation for this work is <strong>AAT, Algebraic Architecture Theory</strong>, a theory I originated and continue to develop through mathematical research and software tools. AAT approaches architectural meaning through geometry. I want to turn that foundation into a way to understand a codebase and reason about how it can change while preserving what matters.</p>
<h2>The Same Email Function Can Mean Different Things</h2>
<p>Consider this function:</p>
<pre><code class="language-typescript">sendEmail(to, subject, body)
</code></pre>
<p>It sends a subject and a body to a recipient. Its role in the system emerges from its relationship to the code that calls it.</p>
<table>
<thead>
<tr>
<th>Use</th>
<th>What the email does in context</th>
<th>Requirements in this example</th>
</tr>
</thead>
<tbody><tr>
<td>Two-factor authentication</td>
<td>Delivers a code needed to complete authentication</td>
<td>Delivery before expiration; clear rules about which code remains valid after a resend</td>
</tr>
<tr>
<td>Newsletter</td>
<td>Delivers information to subscribers</td>
<td>Consent checks; honoring unsubscribe requests during delivery</td>
</tr>
</tbody></table>
<p>Put authentication emails in the same queue as a bulk mailing, and they may wait behind thousands of messages. Delivery can succeed while authentication fails because the code has already expired.</p>
<p>For newsletters, a subscriber may unsubscribe after a message enters the queue. Consent when the audience was selected and consent when the message is sent are distinct states. The system needs an explicit rule about which state governs delivery.</p>
<p>To assess the change, we need more than a label identifying each use. We need the relationships between the authentication request, code generation, delivery, and verification, as well as the point at which subscription status is checked.</p>
<p><strong>The meaning of sending an email lies in its relationships with other operations and the requirements of its use.</strong></p>
<p>By a <em>semantic layer</em>, I mean a layer through which we can access components, relationships, contexts, and the laws they must satisfy, together with references to their sources. An AI agent would retrieve the information relevant to a task, then follow those references to the original code wherever implementation details are needed.</p>
<h2>AAT: Toward a Geometry of Meaning</h2>
<p>I am building AAT as a theory of pure mathematics, starting from Atoms and Laws. An <strong>Atom</strong> is a basic unit of architectural fact. A <strong>Law</strong> is an equation over a family of Atoms. To apply the theory to code, we express observed facts in this vocabulary and choose laws for the properties we want to examine.</p>
<p>In the email example, we might record that authentication requests code delivery, or that delivery uses a queue, along with the context and source of each fact. We then specify the conditions we want to examine, such as those governing expiration and delivery times.</p>
<p>The perspective I take in AAT is that <strong>software architecture is a large system of simultaneous equations</strong>. The laws within each part and the conditions connecting those parts must hold together. A solution describes a consistent configuration under the selected laws.</p>
<p><strong>The solutions to a system of equations form a space.</strong> Algebraic geometry studies such spaces. We can ask whether a configuration satisfying the conditions exists, whether configurations built separately can form a coherent whole, and what becomes possible when the conditions change. My aim with AAT is to treat architectural consistency and change as questions about this geometry.</p>
<p>Authentication, delivery, and subscription management can each have configurations that satisfy their own conditions. To form a working system, they must also agree where they meet, for example on which authentication code is being delivered and when delivery takes place.</p>
<p>In mathematics, assembling local configurations into a whole on the basis of agreement on their overlaps is called <strong>gluing</strong>. In this framework, we study failures of global consistency as obstructions to gluing, and changes in configuration as problems of deformation.</p>
<p>I call this research program <strong>Semantic Geometry of Architecture</strong>. It studies the space of consistent realizations of meaning: their existence, obstructions, deformations, and relationships across different ways of reading an architecture.</p>
<p>Once our understanding of an implementation connects to such mathematical objects, we can move from asking what is present to asking what is consistent and how it can change. That is the scope I want the semantic layer to support.</p>
<h2>The Atlas Theorem: Choosing How Much Detail to Read</h2>
<p>To use this layer across a 200,000-line codebase, we must choose both the scope and the level of detail appropriate to the question. For asynchronous email delivery, expiration and retry rules deserve early attention. Does every styling detail in the HTML template need the same scrutiny?</p>
<p>Yet omitting the logic that inserts the authentication code into the message could discard something essential. <strong>What we can leave out depends on what we need to establish.</strong> AAT treats this choice mathematically.</p>
<p>We begin by grouping together things that the selected laws cannot distinguish. AAT provides a construction of a <em>canonical reading</em> that preserves the evaluations of those laws. In an expiration analysis, for example, the idea is to retain the information needed to evaluate the relevant conditions.</p>
<p>We then ask whether preserving information within each part also preserves the diagnosis of the whole. This is the question addressed by the Atlas Theorem.</p>
<blockquote>
<p><strong>Atlas Theorem: A change in resolution preserves diagnostics when the required conditions hold.</strong></p>
<p>If both the finer and coarser readings retain enough information for the selected laws, and their comparison satisfies the specified conditions, their diagnostic classes in first cohomology correspond one to one.</p>
</blockquote>
<p>Here, <em>first cohomology</em> is a mathematical tool for studying discrepancies that satisfy specified consistency conditions and identifying the obstructions that remain after local adjustments. A one-to-one correspondence of classes means that, under the theorem's conditions, a coarser reading neither loses diagnostics nor introduces new ones. The parts, their overlaps, and the data on those overlaps must all be appropriately related.</p>
<p>Applied to code comprehension, this would let us say, with mathematical justification: <strong>“For this property, reading at this resolution preserves the diagnosis.”</strong> Once a model extracted from the code is shown to satisfy the Atlas Theorem's conditions, omitting detail preserves the diagnostics associated with the selected laws.</p>
<p>An AI agent could use that guarantee to choose a reading resolution, then inspect finer detail when it needs to investigate another property or edit the implementation. Reliable model extraction and a way to select laws appropriate to the question are the tools needed to make this workflow possible.</p>
<h2>The SAGA Theorem: A First Realization of Semantic Geometry</h2>
<p>Once we know what to read, we need to determine whether the requirements we find can hold together.</p>
<p>Suppose an authentication code expires five minutes after issuance, but the shared queue can take ten minutes to deliver it. The authentication service rejects expired codes, and the delivery service sends the message. Both follow their own rules, yet the user cannot complete authentication.</p>
<p>An engineer can approach this problem from two directions. One is to consider changes that restore consistency, such as separating the authentication email queue. The other is to express the required relationships as equations, such as those relating expiration and delivery times.</p>
<p>The SAGA Theorem establishes a mathematical correspondence between two independently constructed ways of describing obstructions, one based on each perspective.</p>
<p>On the semantic side, the construction starts from the repairs allowed within each part and the ways those repairs combine. On the geometric side, it starts from the law equations and the algebra describing their obstructions. Because the constructions have different starting points, their agreement requires proof.</p>
<blockquote>
<p><strong>SAGA Theorem: Semantic and geometric accounts of obstruction correspond.</strong></p>
<p>Choose finitely many parts covering the subject of analysis. When their local data satisfy the required correspondence and consistency conditions, the cohomology constructed from semantic repairs is isomorphic to the cohomology constructed from the equations and obstruction algebra. The obstruction classes correspond as well.</p>
</blockquote>
<p>An <em>isomorphism</em> here allows us to move between the two descriptions while preserving the mathematical structure used to study obstructions.</p>
<p>The power of the SAGA Theorem is that <strong>we can take a semantic question about what prevents local repairs from fitting into a consistent whole and bring it into algebraic and geometric computation</strong>. For a model satisfying the theorem's conditions, the correspondence between the computed obstruction and the semantic obstruction is mathematically guaranteed. People and AI can use that result as evidence when evaluating repair proposals.</p>
<p>For the email system, I want to model the allowed repairs and the data shared between operations, then connect the problem of making authentication and delivery consistent to this analysis. <strong>The SAGA Theorem connects reasoning about meaning with geometric computation. It is the first realization of the Semantic Geometry of Architecture program.</strong></p>
<h2>Annapurna: Preserving Relationships Through Change</h2>
<p>Moving to asynchronous delivery may involve a dedicated authentication queue, invalidating old codes when a new one is sent, or checking subscription status immediately before delivery. The change extends beyond the sending function into relationships between operations.</p>
<p>Identical “delivery succeeded” records before and after a change do not establish that authentication and subscription requirements have been preserved. We need to know whether the recorded information captures the property we want to compare.</p>
<p>In the research series I call <strong>Annapurna</strong>, I have studied how comparisons can be carried through changes in viewpoint and structure. Its later results include the following.</p>
<blockquote>
<p><strong>Annapurna results: Classify changes that preserve a comparison, and establish distinctions that observation alone cannot detect.</strong></p>
<p>Within the specified structures, we can classify which changes to the two objects preserve their comparison. This classification carries over when the presentation of the inputs changes. There are also examples in which two changes produce identical observations through specified coefficients, yet only one is compatible with the comparison.</p>
</blockquote>
<p>In the latter case, processing the observed values afterward cannot recover the distinction. The information needed for that decision is absent from those values.</p>
<p>The email delivery log is an illustration of the problem these results address. To assess a change, we need records that let us compare structural relationships as well as outcomes.</p>
<p>I want to use the Annapurna results to examine a practical question mathematically: <strong>“If we change one side, how must we change the other to preserve their relationship?”</strong> Classifying the changes compatible with a comparison provides a mathematical basis for that analysis within the specified structures.</p>
<p>For asynchronous email, the application would be to ask how code issuance and resend rules should change alongside the delivery mechanism. What does the proposed modification preserve, and which changes must be made together to preserve it? To support that analysis, the semantic layer would record how operations correspond before and after the change, which requirements must carry over, and the information needed to check the theorem's applicability.</p>
<h2>The Major Theorem Ahead: Representing Meaning Geometrically</h2>
<p>The results so far answer particular questions within semantic geometry. Beyond them, I am working toward <strong>a major theorem that represents realizations of meaning as a geometric object</strong>.</p>
<p>A <em>realization of meaning</em> is a consistent configuration for the chosen Atoms and laws. In the email example, this would be a configuration in which authentication, delivery, and subscription management satisfy their local conditions and agree where they interact.</p>
<p>The goal is first to define these realizations independently, then show that they are naturally described by maps into a geometric object. This property is called <strong>representability</strong>. It would let us study questions about configurations satisfying the conditions as properties of the corresponding space.</p>
<p>Proving this major theorem remains a research goal. I want it to connect engineering questions to geometric ones:</p>
<table>
<thead>
<tr>
<th>Engineering question</th>
<th>Geometric question</th>
</tr>
</thead>
<tbody><tr>
<td>What does this refactoring preserve?</td>
<td>Is there a correspondence preserving the selected semantic structure?</td>
</tr>
<tr>
<td>What else must change when delivery becomes asynchronous?</td>
<td>Which deformations are possible while the conditions continue to hold?</td>
</tr>
<tr>
<td>Why does fixing individual operations still fail to make the system consistent?</td>
<td>What obstructs gluing?</td>
</tr>
<tr>
<td>Can the problem be resolved under these constraints?</td>
<td>Does a realization satisfying the conditions exist?</td>
</tr>
</tbody></table>
<p>Computing actual repair proposals will also require algorithms that work with finite inputs. I want to pursue both the theorem and the implementation work needed to turn its consequences into computations, so that refactoring and repair can be treated as geometric problems.</p>
<h2>The Workflow I Want to Build with ArchMap, ArchView, and ArchSig</h2>
<p>I am developing ArchMap to record observed structure, ArchView to visualize it, and ArchSig to analyze it under selected laws. LawPolicy specifies the laws, which are combined with the observations for computation. Here is the future workflow I want to build on that foundation.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69ff29ebf239332df4a93c3a/49a6e6cb-d499-4cdd-ba77-577d93326547.png" alt="" style="display:block;margin:0 auto" />

<p>Return to the 200,000-line codebase and the original request:</p>
<p>“Move email delivery to a shared asynchronous queue.”</p>
<p>The AI first queries ArchMap for the contexts in which email delivery is used. It retrieves the operations involved in authentication and newsletters, their requirements, and their source references, together with the shared data and relationships needed for the decision.</p>
<p>In ArchView, a person examines the same structure. They follow code issuance through delivery and verification, checking which operations the AI selected for modification and why. Eventually, linking changes in the level of visual detail to mathematical reading resolutions could make it clear which information each view preserves.</p>
<p>ArchSig analyzes the selected scope under the selected laws. I want to extend it to check the conditions for comparisons across resolutions and to handle structural correspondences before and after a change. The results and supporting evidence would serve both the AI's explanation and human review.</p>
<p>With that information, the AI reads the implementation details it needs: code issuance, queue submission, resend handling. After making the change, it updates the relevant observations and repeats the diagnosis and comparison for the same objective.</p>
<p>The semantic structure used to reach the decision remains available to the person reviewing it and to the AI in the next session.</p>
<h2>Putting the Geometry of Meaning in the Hands of People Who Build Software</h2>
<p>What I want to create with AAT is a development environment in which people and AI share an understanding of software's meaning and explore the changes that understanding makes possible.</p>
<p>Faced with 200,000 lines of code, we trace the relationship between authentication and delivery. We introduce a proposal for asynchronous processing and inspect the requirements it must preserve and the operations it needs to adjust. One repair leaves an obstruction; another configuration is consistent. We can explain why using both the original code and mathematical evidence. That is the development experience I want to build.</p>
<p>In that environment, a codebase is both an implementation to read and a geometric object through which to reason about what can remain and what can change. Refactoring becomes a question about correspondences that preserve semantic structure. Repair becomes a question about the existence and deformation of configurations satisfying the conditions. I want the individual difficulties of everyday engineering to become approachable through a shared body of mathematics.</p>
<p>Through the SAGA Theorem, the Atlas Theorem, and Annapurna, I have been building that foundation in theorems and counterexamples. We already have verifiable results on comparisons between meaning and geometry, changes in resolution that preserve diagnostics, and structural comparison and information loss. I am also pursuing formal verification in Lean so that others can check the foundations themselves. The work toward the major theorem continues from those results.</p>
<p>There is still substantial tooling work ahead: reliable extraction and updates, usable ways to query meaning, and evaluation in actual development. I want to measure not only how much code and how many tokens an agent consumes, but whether it retains the requirements that matter and carries its understanding into the next session. Each of these is part of bringing the mathematics into the hands of people who build software.</p>
<p>In this future, using the tools would not require every developer to learn algebraic geometry. People and AI would express what they want to build and what they want a change to preserve. The theory and tools would connect those questions to semantic structure, perform the computation, and return results with evidence. As AI becomes capable of proposing more changes, mathematics can help us explore those possibilities with greater confidence.</p>
<p><strong>Treat software's meaning as geometry, and put that geometry in the hands of the people building it.</strong> That is the ambition behind Semantic Geometry of Architecture. From understanding 200,000 lines of existing code to exploring configurations that have yet to be implemented, I want people and AI to be able to reason about the same space of meanings.</p>
<p>Repository: <a href="https://github.com/iroha1203/AlgebraicArchitectureTheoryV2">AlgebraicArchitectureTheoryV2</a></p>
]]></content:encoded></item><item><title><![CDATA[The Goddess of Plenty Smiles at the Summit]]></title><description><![CDATA[Every test passes. Every metric looks the same. Is that enough to judge whether a design change is sound?
There are pairs of changes that the observations of AAT (Algebraic Architecture Theory) cannot]]></description><link>https://blog.iroha1203.dev/the-goddess-of-plenty-smiles-at-the-summit</link><guid isPermaLink="true">https://blog.iroha1203.dev/the-goddess-of-plenty-smiles-at-the-summit</guid><category><![CDATA[lean]]></category><category><![CDATA[software architecture]]></category><category><![CDATA[Software Engineering]]></category><category><![CDATA[Computer Science]]></category><dc:creator><![CDATA[Hiroyuki Nakahata]]></dc:creator><pubDate>Sun, 06 Sep 2026 13:28:29 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/69ff29ebf239332df4a93c3a/0565b098-ce19-4beb-b44f-ea6ef1a12ebb.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Every test passes. Every metric looks the same. Is that enough to judge whether a design change is sound?</p>
<p>There are pairs of changes that the observations of AAT (Algebraic Architecture Theory) cannot tell apart — even though one keeps the system coherent and the other breaks it. And the difference cannot be recovered by any post-processing of the same observed values. This summer, that limit became a theorem, machine-checked in Lean 4.</p>
<p>In five weeks starting August 2, 2026, we proved sixteen theorems and refuted two claims. An AI agent loop did the implementation; I set the goals, reviewed the proofs, and made the calls. This article is a record of what we found, and of the road that led there.</p>
<h2>1. AAT: Algebraic Architecture Theory</h2>
<p>AAT is a research program that analyzes software architecture with the tools of algebraic geometry. It starts from observing source code. Implementations are abstracted into components called Atoms; the specification is written as a family of equations called laws. Design coherence then becomes a mathematical question: does local correctness glue into a consistent whole?</p>
<p>A running example from earlier articles is the one-cent drift. Three modules of a commerce service — checkout, payment, and ledger — each pick a rounding rule that passes review on its own, and the card gets charged one cent more than the screen displays. Each module follows its own rules. The whole still doesn't add up.</p>
<p>In this example, each module's implementation is an Atom, and an equation like "the amount displayed equals the amount charged" is a law. A quantity that aggregates the local mismatches into a global one, like that one cent, is a cohomology class, and reading it off to point at the inconsistency is what we call diagnosis. The earlier work — the SAGA and Atlas theorems, each with its own article — made all of this measurable.</p>
<p>A view is the result of reading the code at a particular resolution and from a particular angle; a service-level view and a module-level view are different views. Carrying an analysis result from one view to another is transport, and swapping out the base the analysis stands on is base change. In the one-cent example, adding settlement as a new context changes the very ground of the analysis; that is one instance of base change. When an operation survives such a swap, we say it is closed under the swap.</p>
<p>Every theorem in this article is machine-checked in Lean 4, a proof assistant that mechanically verifies whether a proof establishes the statement written down.</p>
<p>The question of these five weeks: when you change how you look, or the ground you stand on, how much of your analysis and your comparisons survives the move?</p>
<h2>2. Five things we can now say about software design</h2>
<p><strong>1. Observation alone cannot always tell a sound change from a breaking one.</strong></p>
<p>Take two candidate changes. Test results, metrics, every value in AAT's observations: identical. Yet one preserves global coherence and the other destroys it. Such an example has been constructed, and no aggregation or post-processing of the observed values can distinguish the two (Section 5). The practitioner's intuition — "you can't manage architecture by numbers alone; you have to look at the structure" — is now a theorem inside the model.</p>
<p>The information you need lives not in the numbers but in the correspondence that connects the before and after of a change. So the next move is not to inspect change proposals after the fact; it is to derive the required companion change directly from that correspondence.</p>
<p><strong>2. The degrees of freedom in a fix are now known exactly.</strong></p>
<p>You change one side of an interface. How can the other side be fixed so the two still agree? The theorem describes the full space of compatible fixes (Section 5). Find one, and every other fix can be derived from it — none missing. A design review can move on from "is this fix acceptable?" to "how much freedom does this fix have, and does the choice matter?"</p>
<p><strong>3. Harmless-looking normalization stays in your design.</strong></p>
<p>Some formatting and normalization passes are not supposed to change meaning, and as far as observation goes, they don't. Even so, there are cases where the local normalizations scattered across a system cannot, in principle, be merged into one globally consistent operation (Section 4). Fixing a convention for "what counts as the same meaning" does not end the design question; which operations remain sound under that convention is itself part of the design. So normal forms deserve to be first-class citizens of the design, not an afterthought.</p>
<p><strong>4. We now know exactly when divide and conquer is honest.</strong></p>
<p>Analyze each module, then combine the results into a conclusion about the whole. The condition for this to be sound is now a theorem, in necessary-and-sufficient form (Section 3). We also proved that analysis results do not depend on how the code happens to be written down. Together, the two mean analysis need not start over on every change: there are precise conditions under which re-checking only the changed part and its overlaps is enough. That matters at scale — for large codebases, and for fleets of AI agents developing in parallel.</p>
<p><strong>5. A change of meaning cannot pass through the label layer.</strong></p>
<p>On a fixed base, relabeling alone — types, tags, and other metadata — cannot move observable meaning. A change of meaning can live only on the side where the base itself moves. This, too, is a theorem (Section 8). There is now a structural reason why metadata edits preserve meaning.</p>
<p>The one-cent drift connects here as well. That example showed that the failure of locally correct modules to glue can be measured. The new results establish where the limits of that measurement lie. The boundary between what can be measured and what in principle cannot is now drawn as a theorem.</p>
<h2>3. Three layers of theorems</h2>
<p>The sixteen theorems answer three questions.</p>
<p><strong>Can analysis results be carried between views of different resolution and level?</strong> Yes, and by any route: the result of a service-level analysis and the result of a module-level analysis can be traded back and forth without loss, and moving across levels loses nothing either. Catch the one-cent drift at service granularity or at module granularity; either way, it can be matched up as the same inconsistency.</p>
<p><strong>When the base of the analysis is swapped, which operations survive as theorems?</strong> The answer is a table of conditions, direction by direction: classifying the operations that vary with the base; base change along refinements that carve the base finer; and lifting, which extends a partial result one level up. Each stands as a theorem with its scope pinned down. The point is that not every direction holds unconditionally. That dividing line — which direction holds under which condition — is what this layer establishes.</p>
<p><strong>When may a conclusion about the whole be drawn from finitely many parts?</strong> A necessary and sufficient condition is now settled for when partwise analysis lifts to the whole, and for how far diagnosis survives transport. The negative side is proved as well: an arbitrary choice of covering can change the diagnosis of the very same system (Section 7). Unconditional divide and conquer fails, which is exactly why a condition is needed — and the condition is exact, nothing extra and nothing missing.</p>
<p>All three are classifications, not conjectures. The boundary — which operations are closed, and where closure fails — is drawn with proofs.</p>
<h2>4. The normalization factor that cannot be removed</h2>
<p>The strangest finding is an invisible factor lodged inside comparisons.</p>
<p>AAT generates comparison morphisms between two views: before and after a change, a fine view and a coarse view. Ideally a comparison is invertible; then the two views can be traded back and forth without losing information.</p>
<p>A comparison is a family of maps, one per object. For the comparison β that the system actually generates, each component provably factors as</p>
<p>$$\beta = E \circ \alpha$$</p>
<p>first through α, then through <em>E</em>. Here α is the ideal comparison prescribed by the theory, and it is provably invertible. And β is the concrete comparison assembled from actual input. How does the concrete deviate from the ideal? The deviation is exactly <em>E</em>, and this factor has three properties at once.</p>
<p>First, it is idempotent:</p>
<p>$$E \circ E = E$$</p>
<p>Applying it twice is the same as applying it once, like running a code formatter twice.</p>
<p>Second, it is invisible to observation. <em>E</em> moves no observable quantity; under the observations considered here, it cannot be told apart from a map that does nothing.</p>
<p>Third, it is nevertheless not invertible. Wherever <em>E</em> actually fires, it cannot be undone, and therefore β as a whole is not invertible either.</p>
<p>So what breaks the invertibility of comparisons is a normalization factor that appears, to observation, to do nothing at all. And invertibility does recover — if you restrict where you stand. In the raw world, β is not invertible. Restricted to the image — the values that actually remain after <em>E</em> — it becomes invertible. And on observable quantities, β and α agree exactly. The failure is not monolithic; it splits into these three stages. That recovery on the image will matter later: it is the doorway to the next stage of the research (Conjecture A, Section 11).</p>
<p>Then why not simply remove <em>E</em>? Bundle it into one natural operation running through every object, and subtract it from the system uniformly.</p>
<p>That is impossible, and this too is a theorem. From the same starting point, compare normalizing at A and then moving to B, against moving to B first and normalizing there. A concrete counterexample exists where the two disagree. Compatibility with movement between objects is what mathematics calls naturality: <em>E</em> is a perfectly usable normalization at each object, yet it cannot be bundled into one natural operation compatible with movement.</p>
<p><strong>A structure invisible to the observations at hand, one that decides whether comparisons succeed, and that cannot be removed: it was there all along.</strong></p>
<h2>5. Degrees of freedom, and the limits of observation</h2>
<p>The strongest result is a classification of the freedom in changes. The subject is compatibility of changes across an interface.</p>
<p>The setup: two sides joined by a single comparison, and a change applied to each side. We consider reversible changes here. Which pairs of changes preserve the comparison? In other words, when you change one side, which companion changes on the other side keep the whole consistent?</p>
<p>"Preserve" means this: changing one side and then comparing gives the same result as comparing first and then changing the other side. When that holds, the pair of changes is compatible with the comparison.</p>
<p>The theorem settles four things at once.</p>
<p>First, the compatible pairs of changes form a group: operations that can be chained and undone, with "do nothing" as a member. Integer addition is the model case.</p>
<p>Moreover, once one compatible companion change is found, every other one can be expressed as a difference from it. Any of them could serve as the reference; none is privileged from the start. This structure is called a torsor. The space of compatible companion changes has exactly that shape.</p>
<p>Second, for the comparisons the system actually generates, source-side changes can always be carried across, and the companion options on the other side form a coset: one solution multiplied by the elements of a subgroup. That subgroup is characterized by concrete conditions on the input data.</p>
<p>Third, the classification does not depend on presentation. The same design can be written in many forms: different file splits, different orders of definition. Rewrite first and then generate the comparison, or generate and then transport: the classification lands in the same place. What this analysis measures is not the style of the writing but the structure of the meaning — and that is what the proof establishes.</p>
<p>Fourth, <strong>observation cannot decide this classification.</strong> In a fixed example, compare two options.</p>
<table>
<thead>
<tr>
<th>In the fixed example</th>
<th>Option A</th>
<th>Option B</th>
</tr>
</thead>
<tbody><tr>
<td>Change applied to the source side</td>
<td>the same change</td>
<td>the same change</td>
</tr>
<tr>
<td>Response on the other side</td>
<td>the required companion change</td>
<td>nothing</td>
</tr>
<tr>
<td>Result under the observations considered here</td>
<td>same as B</td>
<td>same as A</td>
</tr>
<tr>
<td>Compatibility with the comparison</td>
<td>preserved</td>
<td>broken</td>
</tr>
</tbody></table>
<p>The correct companion change is uniquely determined. Yet from the observations alone there is no telling whether that change was made or nothing was done — and provably, no post-processing recovers the difference. <strong>The information that singles out the one required companion change is lost by observation.</strong></p>
<h2>6. The plan: turning a metaphor into theorems in five stages</h2>
<p>The rest of this article is the record of the five weeks that led there. In hindsight, it was a climb up a mountain whose map was wrong.</p>
<p>The early theory notes described AAT's construction as "Grothendieck-like." Grothendieck rebuilt algebraic geometry in the twentieth century; much of the machinery connecting the local and the global goes back to him. But "Grothendieck-like" was only a metaphor. How far does analysis survive moving between views and swapping bases? The road from metaphor to theorem was laid out as a five-stage plan:</p>
<pre><code class="language-text">Gr0 Metaphor: said, not shown
Gr1 Formalization: the claims written as mathematical statements
Gr2 Constructed instance: an object that actually satisfies them
Gr3 Pseudofunctorial coherence: transport between views agrees along any route
Gr4 Base-change completeness: every operation closes, base swaps included
</code></pre>
<p>The first two stages were already done in the theory notes. To climb the rest with theorems, eighteen research cards were issued starting August 2. A card is a document that pins down the theorem to prove, its scope, and its completion criteria.</p>
<h2>7. The climb: from the foothills to Gr4</h2>
<p>The first summit fell on day one. On August 2, the construction of Atom transport was proved: Gr2 reached. On August 8, the Atlas theorem. On August 18, transport across levels of views was proved to agree along any route: Gr3 reached. Four of the five stages, in under three weeks.</p>
<p>On the way, on August 11, came the first refutation. The plausible claim "changing the structural covering (a way of covering the whole with finitely many parts) does not change the diagnosis" turned out to be false. The refutation was not wasted. Its counterexample revealed where the residual — the quantity measuring a failure to glue — can appear and where it never can, and that distinction fed straight into the design of later counterexamples. A refutation becomes reconnaissance for the next route. That pattern would repeat for all five weeks.</p>
<p>Only Gr4 remained, and this was the real mountain. The foundation is a construction that takes two bases and builds a new one from the pairs that agree over a common base (the fiber product; call it crossing bases). The foundation theorem was proved on August 25, but it took 111 cycles (a cycle is one loop iteration: the agent adds one step of proof and records evidence and an audit trail). Most earlier cards had finished in a few to a few dozen cycles. That is the gradient of this mountain.</p>
<p>Standing on the foundation, we could finally see the terrain: this mountain has no single route, because it has no single summit. The work was split into six cards, one responsibility each, and between August 26 and September 4 all six were settled by proof, one of them taking 84 cycles and nine revisions of its card. The full record of all eighteen cards is in the appendix.</p>
<h2>8. The mountain in the plan did not exist</h2>
<p>All six cards were settled by proof. And yet Gr4 itself — the original goal: swap the base, analyze there, carry the results home, every step of the round trip a theorem — turned out to be false as stated. The content of the proved theorems itself showed that the mountain in the definition does not exist. On September 4, we decided not to write a completion record for Gr4. You do not file a summit report for a mountain that was never there.</p>
<p>What failed comes down to two things.</p>
<p>First, <strong>vertical rigidity</strong>. Call vertical the direction in which only extra labels are swapped while the base stays fixed. No meaning-moving transformation can be placed in that direction; such transformations have to be handled horizontally, where the base itself moves.</p>
<p>Second, <strong>the comparisons the system generates are not invertible</strong>, because, as Section 4 explained, the invisible factor <em>E</em> sits inside them.</p>
<p>So where did this conclusion come from?</p>
<h2>9. The turning point: refutations became the harvest</h2>
<p>The turning point came right after the hardest pitch, the lift to the upper level (84 cycles, nine revisions). That climb left behind not only its summit theorem but a pile of counterexamples, because every revision had deposited refutation theorems in Lean — each one saying, in effect, "not in this form."</p>
<p>Surveying the pile after the pitch, we noticed the counterexamples fell into three families.</p>
<ul>
<li><p>Attempts to place a meaning-moving transformation in the vertical direction had failed independently on two cards, and both were resolved by moving the transformation to the horizontal side.</p>
</li>
<li><p>Lossy collapses were never going to yield invertibility. Identify two objects that differ only in decoration, and the map sends different things to the same place — no longer one-to-one, and a correspondence that is not one-to-one can never serve as part of an invertible comparison.</p>
</li>
<li><p>Fix all the route data and swap only the comparison component joining the two sides, and glueability flips, while to observation nothing appears to change at all.</p>
</li>
</ul>
<p>Line the three up and a common trait appears: something observation cannot see, yet which decides the strict properties — invertibility, glueability — and lurks in both the vertical and the horizontal directions. And none of these refutations had been designed to show this. They are the traces of the structure pushing back in the middle of proofs aimed elsewhere. If so, one structure must be behind them all. That structure was exactly the normalization factor <em>E</em> of Section 4.</p>
<p>So the sixth card, originally the summit card that was to close out Gr4, was redesigned into the card that would hunt down the structure behind the counterexamples. The redesigned card was settled by proof on September 4 (the 9/4 row in the appendix): the identification of <em>E</em>. The counterexamples had not been wrecking the mountain. They had been teaching us that there was a mountain that no one-dimensional map — no single yes-or-no ruler — could capture.</p>
<p>Beyond that identification, the refutation of September 5 (Section 4: the impossibility of bundling) turned <em>E</em> from something to erase into something to classify, and that decision shaped the final theorem of September 6 (the classification of Section 5). In hindsight, this change of course led to the highest summit of the five weeks.</p>
<p>The same pattern runs through all five weeks. The August 11 refutation supplied the method for designing counterexamples; on the cards of August 28 and 29, the content of a refutation directly became the design of the next revision, which was then proved. Individual refutations shaped the next card, and their accumulation rewrote the plan itself. In the Atlas article, I wrote that the four refutations were the best part of the climb. This time the same thing happened not inside one theorem but across the whole series. The map being wrong about the mountain: that itself was the harvest.</p>
<h2>10. The descent was the real test</h2>
<p>The work after standing on a summit was the real test, because declaring a proof complete and being able to trust that declaration are two different things.</p>
<p>Here is how the loop runs. The agent works cycle by cycle toward the claim and completion criteria fixed on the card, recording evidence and an audit trail as it goes. Completion requires, in order: a full completion report, four independent reviews of the same proof target (two mathematical, two Lean), and a cross-check of the evidence records. Only then is a card settled.</p>
<p>Over the five weeks, the procedure stopped exactly where it should have.</p>
<ul>
<li><p>Two cards halted at the first step (writing the claim down in rigorous types) when defects in the claims surfaced; I revised the cards before the loops resumed.</p>
</li>
<li><p>One card halted at the provenance check of its evidence.</p>
</li>
<li><p>Two cards had "proof complete" declared, after which the audit found defects in the records and rolled the state back; completion was then redone. In both cases the defect was in the evidence records, never in the theorem itself.</p>
</li>
</ul>
<p>Those last two matter most. The summit had been declared, and the descent checks rejected it. Checking the evidence with the same rigor as the theorems is what makes the declarations trustworthy.</p>
<p>Over the five weeks, the Lean code grew to 791 files and roughly 300,000 lines, and the agent loop consumed about 24 billion tokens. Through all that volume, the policy of stopping on doubt was never relaxed — and I believe it is precisely because the system was built to stop that sixteen theorems in five weeks could be delivered without burning trust. Mathematics aside, the speed itself is an empirical result: a formalization that starts from observing source code can run this fast.</p>
<h2>11. Higher peaks beyond</h2>
<p>From a summit, you see the next mountains. The harvest of the traverse includes three conjectures. None are proved, but for each, the object to construct next is already in view.</p>
<p><strong>Conjecture A (making normal forms first-class).</strong> Section 4 noted that invertibility recovers on the image, the world of values that remain after <em>E</em>. The next construction promotes that world to a first-class citizen of the analysis. There, the founding claim of the theory — that AAT's conclusions factor through the layer of Atoms — is expected to become a theorem of the form "the two worlds carry essentially the same analysis." Mathematics calls the construction a Karoubi envelope, and this kind of equivalence a Morita equivalence.</p>
<p><strong>Conjecture B (an invariant finer than the cohomology class).</strong> Swap the base, and the obstruction's cohomology class stays fixed; yet glueability can change. So there are differences the cohomology class cannot see. The next construction defines a finer invariant — a quantity that stays fixed no matter how things are presented — that captures those differences. The torsor classification of Section 5 is constructive evidence that such a quantity exists.</p>
<p><strong>Conjecture C (capturing the whole in finitely many parts).</strong> The condition of Section 3 for concluding from finitely many parts looks like an instance of what topology calls compactness. The next construction carries the covering classification into topological language.</p>
<p>And beyond the three conjectures stands the summit theorem of the whole program: that the space of meanings is representable as what algebraic geometry calls a scheme, a space glued together from solution sets of equations. The calculus settled over these five weeks tells us what can be carried, which changes preserve comparisons, and what observation loses. The route to that summit will be drawn on this map.</p>
<h2>12. Why "the Goddess of Plenty"</h2>
<p>We named this family of theorems Annapurna.</p>
<p>On June 3, 1950, a French expedition led by Maurice Herzog stood on the summit of Annapurna in the Himalayas: the first ascent of an 8,000-meter peak in history. But Annapurna was not the mountain the expedition had set out to climb. That was Dhaulagiri, next door. After long reconnaissance, the team concluded there was no route up Dhaulagiri and switched objectives. The maps of the day were wrong; even the glaciers were misplaced. They had to begin by searching for the mountain they would climb. The price was paid on the way down, where severe frostbite cost the climbers fingers and toes. And Annapurna, in Sanskrit, means "she who is filled with food": the goddess of plenty, of harvests.</p>
<p>The mountain drawn on our map did not exist either. We navigated by the actual terrain instead, and turned refutations into the harvest. For these five weeks, no name could fit better.</p>
<p>Five weeks ago, the goal stood on the map. Now it is the map that has been redrawn, and the mountain actually climbed bears the name. The goddess smiled, not because the climb went according to plan, but because the map's mistake was turned into a harvest.</p>
<hr />
<h2>Appendix: the full five-week record</h2>
<table>
<thead>
<tr>
<th>When</th>
<th>What was settled</th>
<th>Verdict</th>
<th>Cycles</th>
</tr>
</thead>
<tbody><tr>
<td>8/2</td>
<td>Constructed instance of Atom transport (Gr2 reached)</td>
<td>proved</td>
<td>16</td>
</tr>
<tr>
<td>8/2–8</td>
<td>Locating where obstructions appear</td>
<td>proved</td>
<td>5</td>
</tr>
<tr>
<td>8/2–8</td>
<td>Constructibility of finite canonical resolutions</td>
<td>proved</td>
<td>6</td>
</tr>
<tr>
<td>8/8</td>
<td>Atlas theorem (resolution invariance)</td>
<td>proved</td>
<td>31</td>
</tr>
<tr>
<td>8/11</td>
<td>Structural covering invariance</td>
<td><strong>refuted</strong></td>
<td>7</td>
</tr>
<tr>
<td>8/11</td>
<td>Characterizing where uniform invariance fails</td>
<td>proved</td>
<td>27</td>
</tr>
<tr>
<td>8/15</td>
<td>Transport coherence</td>
<td>proved</td>
<td>5</td>
</tr>
<tr>
<td>8/16</td>
<td>Transport of geometric views</td>
<td>proved</td>
<td>7</td>
</tr>
<tr>
<td>8/18</td>
<td>Cross-level transport (Gr3 reached)</td>
<td>proved</td>
<td>22</td>
</tr>
<tr>
<td>8/25</td>
<td>The foundation: crossing bases and base change</td>
<td>proved</td>
<td>111</td>
</tr>
<tr>
<td>8/26</td>
<td>Classifying base-indexed operations</td>
<td>proved</td>
<td>20</td>
</tr>
<tr>
<td>8/27</td>
<td>Conditions for lifting from finitely many parts</td>
<td>proved</td>
<td>8</td>
</tr>
<tr>
<td>8/28</td>
<td>Conditions for diagnosis to survive transport</td>
<td>proved</td>
<td>28</td>
</tr>
<tr>
<td>8/29</td>
<td>Base change along refinements</td>
<td>proved</td>
<td>4</td>
</tr>
<tr>
<td>9/1</td>
<td>Lift to the upper level</td>
<td>proved</td>
<td>84</td>
</tr>
<tr>
<td>9/4</td>
<td>Gluing of disconnected configurations (the redesigned sixth card)</td>
<td>proved</td>
<td>21</td>
</tr>
<tr>
<td>9/5</td>
<td>Can normalization be bundled into one natural operation?</td>
<td><strong>refuted</strong></td>
<td>4</td>
</tr>
<tr>
<td>9/6</td>
<td>Transport of comparisons, and the information observation loses (the final theorem)</td>
<td>proved</td>
<td>30</td>
</tr>
</tbody></table>
<p><em>Cycles</em> is the number of loop iterations to completion.</p>
<hr />
<p><strong>Related</strong>: <a href="https://blog.iroha1203.dev/atlas-theorem-how-far-can-you-zoom-out">Atlas Theorem: How Far Can You Zoom Out?</a> / <a href="https://blog.iroha1203.dev/the-saga-theorem">The SAGA Theorem</a></p>
<p><strong>Repository</strong>: <a href="https://github.com/iroha1203/AlgebraicArchitectureTheoryV2">AlgebraicArchitectureTheoryV2</a></p>
]]></content:encoded></item><item><title><![CDATA[Uncharted Waters: Thirteen Rules for Not Trusting an AI's Proofs]]></title><description><![CDATA[TL;DR

I want to hand an AI agent a multi-day job and have it run to completion. This post collects the rules for that — thirteen of them. Never let the loop rewrite its own spec. Enumerate up front w]]></description><link>https://blog.iroha1203.dev/uncharted-waters-thirteen-rules-for-not-trusting-an-ai-s-proofs</link><guid isPermaLink="true">https://blog.iroha1203.dev/uncharted-waters-thirteen-rules-for-not-trusting-an-ai-s-proofs</guid><category><![CDATA[AI]]></category><category><![CDATA[loop-engineering]]></category><category><![CDATA[lean]]></category><category><![CDATA[Software Engineering]]></category><category><![CDATA[Math]]></category><dc:creator><![CDATA[Hiroyuki Nakahata]]></dc:creator><pubDate>Sun, 16 Aug 2026 15:18:16 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/69ff29ebf239332df4a93c3a/be3cb291-6ae8-48fb-8565-271283a0bbdc.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2>TL;DR</h2>
<ul>
<li><p>I want to hand an AI agent a multi-day job and have it run to completion. This post collects the rules for that — <strong>thirteen of them</strong>. Never let the loop rewrite its own spec. Enumerate up front what counts as evidence of "done." Review in independent parallel, never by majority vote. If verification didn't run, the answer is unknown, not pass. Nine more inside</p>
</li>
<li><p>The rules run in my repository. In fifteen days of August 2026, this loop proved seven theorems and refuted one. About 104 cycles, roughly 67,000 lines of Lean 4. All seven proofs cleared four independently running adversarial review lanes</p>
</li>
<li><p>The theorems are the main results my research program (AAT) set as its goals — nothing like famous open problems. But each was a multi-day proof target where refutation and revision were live possibilities. The outcomes were genuinely unknown</p>
</li>
<li><p>A three-way division of labor: Claude (Fable) drafts the goal cards and runs adversarial reviews, Codex (GPT-5.6 Sol) drives the proof loop, and a human makes the rulings</p>
</li>
<li><p>I make no claim that these thirteen rules are optimal. This is an N=1 record, with no controlled comparison</p>
</li>
</ul>
<h2>Introduction</h2>
<p>When you hand an AI agent a multi-day job, the hard part is not getting it to work. It is <strong>deciding whether to believe the output</strong>.</p>
<p>The stage is my monorepo, <a href="https://github.com/iroha1203/AlgebraicArchitectureTheoryV2">AlgebraicArchitectureTheoryV2</a>. It hosts AAT (Algebraic Architecture Theory), a research program that analyzes software architecture with the tools of algebraic geometry and machine-checks its claims in Lean 4.</p>
<p>This record is N=1. One team, one problem domain, fifteen days without sinking — that is all it is. But every one of the rules below has a logged incident where it fired.</p>
<p>A proof starts from a goal card pinned down by a human, and moves through this loop.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69ff29ebf239332df4a93c3a/453e2f8d-54f8-4f99-8a83-381cd579ecec.png" alt="" style="display:block;margin:0 auto" />

<p>The thirteen rules pin down the load-bearing points of this loop.</p>
<hr />
<h1>Part 1: The Thirteen Rules</h1>
<h2>Rules of preparation</h2>
<h3>Rule 1 — Split the state three ways</h3>
<p>Keep the spec, the execution state, and the evidence in different places. The spec says what to achieve, as a static document. The execution state says where we are. The evidence says what is done. Mix them, and you open a path for the AI to achieve the spec by rewriting it.</p>
<p>In this repository, the spec lives in a goal card, the execution state in a GitHub Issue, the evidence in a report file. The loop reads the card. It cannot write to it.</p>
<h3>Rule 2 — Never let the loop rewrite the spec</h3>
<p>Revising the spec always means stopping the loop and getting a human ruling. The AI may propose a revision; that is as far as it goes. When a refutation or a dead end hits, the easiest exit is to fix the goal instead of the proof — and sometimes that is even the right call. Which is exactly why this one decision lives outside the loop.</p>
<h3>Rule 3 — Design failure as a deliverable</h3>
<p>"The target turned out to be false" is not an error. It is one of the legitimate exits. Before departure, decide what a failure should leave behind: the counterexample itself, the mechanism of the failure, the parts worth reusing. Fix that format up front, and failures become raw material for the next spec.</p>
<p>Every goal card in this repository must carry a <em>failure policy</em> section, fixing in advance what gets recorded when a refutation lands.</p>
<h3>Rule 4 — One obligation per cycle</h3>
<p>At the top of each cycle, before any work, the loop writes down "the one obligation I will close this cycle" — the single remaining task — together with why it chose it. Working on a little of everything creates the illusion of progress, and when something fails, you can no longer tell what caused it. This prevents both.</p>
<p>Here, the cycle ledger opens with the chosen obligation. All 104 cycles carry this record.</p>
<h3>Caveats</h3>
<p>This group assumes the work can be cut down to a granularity where the spec fits in a static document. Exploratory work needs a separate stage that produces the spec itself. Here, a human and Claude did that, putting each draft through several rounds of adversarial review before handing it to the loop. Run the loop on a weak spec, and refutations and revisions will cost you several times over. And Rule 3's "format of failure" has to be designed per domain. Mathematics has the counterexample, a beautifully reusable form. What plays that role in your domain is not obvious.</p>
<h2>Rules of verification</h2>
<h3>Rule 5 — Enumerate what counts as evidence of "done"</h3>
<p>Enumerate, up front, the forms of evidence you will accept as completion, and accept nothing else, whatever the reason. The AI's "it's done" is judged one way only: does the evidence match one of the forms?</p>
<p>In this repository there are exactly three ways to close a proof obligation: a machine-checked theorem; a concrete example or counterexample pinned as finite data; a derivation from a previously proved result pinned by hash. And the inverse rule: moving the thing-to-be-proved into a typeclass or a structure field, so that it arrives as an assumption — however elegant — closes nothing.</p>
<h3>Rule 6 — "The tests pass" is not done</h3>
<p>CI is green. The PR is merged. Something with the right name exists. None of these is evidence of completion. Verification has to touch the conclusion itself.</p>
<p>This repository's acceptance contract carries that prohibition as one line of its verdict table.</p>
<h3>Rule 7 — Review in independent parallel, never by majority</h3>
<p>Run several reviewers with the same passing bar, in parallel, without showing them each other's output. Do not split them into a lead and assistants; a reviewer demoted to assistant stops hunting. Do not tally by majority; one fatal finding from one lane is enough to block. And keep a mechanism that can reject even a unanimous pass, on fixed mechanical rules.</p>
<p>Reviews here run four lanes: two mathematical, two Lean. The power to reject sits with a fixed rule that detects claim weakening.</p>
<h3>Rule 8 — Pin every review to a commit SHA</h3>
<p>"I fixed it" means nothing on a moving target. Pin the review to a specific commit, and define narrowly when a light re-check after a fix is allowed. If a change touches the spec, the light check is off the table, and you go back to full review.</p>
<p>Here, any of the following instantly disqualifies the light check: a statement change, a definition-body change, adding or deleting declarations, changing an import direction, changing a ledger status.</p>
<h3>Rule 9 — Write it down: "no findings" is a rare pass</h3>
<p>A review whose default outcome is pass is dead. This repository's review protocol contains one sentence: "No major findings is a rare pass." That single sentence changes how deep a reviewer AI digs.</p>
<h3>Rule 10 — Name the cheats, audit for them every cycle</h3>
<p>AI cheating comes in recurring shapes. Vacuous success: the condition holds because the set is empty. Playing to the grader: a construction shaped to the goal's wording. Claiming an equivalence while proving one direction. Smuggling the thing-to-be-proved into the setup. Reinterpreting the goal or the report. Give each shape a name, and you can audit for all of them mechanically, every cycle. Leave them nameless, and you will miss them every time.</p>
<p>The cycle ledger here has an audit field for each of the five.</p>
<h3>Rule 11 — Fail closed</h3>
<p>If verification did not run, the result is unknown, not pass. When a reviewer fails to start, the worst possible move is for the parent agent to fill in a pass on its behalf. Record undecidable as undecidable, and do not proceed.</p>
<p>The judges are audited too. Even the decision to stop a search can be overturned by an independent review.</p>
<h3>Caveats</h3>
<p>The biggest assumption in this group is an oracle: something that objectively decides "done." Here it is Lean, type-checking a proof. In domains with type checkers, reproducible tests, or deterministic measurements, this group transfers as is. Where judgment is subjective — prose quality, design — Rule 5's enumeration gets hard, and more of the weight lands on Rule 7's multiple lanes. One more thing. Rule 10's names come from your own incident log. The five shapes above are the ones this voyage actually met; yours will wear different faces.</p>
<h2>Rules of rulings and cost</h2>
<h3>Rule 12 — Never automate the decision to stop</h3>
<p>How many review rounds before cutting it off. After a failure, push on or retreat. When to pivot. Design these as human rulings, and confine them to a few explicit points — precisely so that everything else can be automated. What you delegate to the loop is the execution of rules already decided.</p>
<h3>Rule 13 — Engineer the cost of verification</h3>
<p>Explicitly ban whole-project verification — full builds, full test suites — inside the loop, and design the scoped checks that replace it, in advance. The pairing is the point: the ban and the replacement together. Just cutting verification collides with Rule 11.</p>
<p>Here, a hard rule forbids full research builds inside the loop. In their place: per-module checks, direct axiom audits, and a scan for placeholders, the markers of unproven holes.</p>
<h3>Caveats</h3>
<p>Run this group, and the bottleneck moves to the humans. In our measurements, after the cost reductions, the limiting factor was not tokens but the supply of specs and the bandwidth of human rulings. And since Rule 13 deliberately lowers verification coverage, record what you gave up. Whole-project verification moves outside the loop, to CI and post-merge audits. It does not disappear.</p>
<h2>Thirteen rules, one principle</h2>
<p>The thirteen rules are not a bag of tricks. Each is a different cross-section of one principle.</p>
<p><strong>Don't trust the AI's output — and enforce that distrust with structure, not human attention.</strong></p>
<p>Saying "we don't trust it" is easy. Doing it is not. Human attention is finite; nobody stays suspicious for 104 cycles. So the suspicion itself gets fixed into specs, ledgers, review lanes, and acceptance contracts, and the AI and the loop carry it for you. Almost every accident in these fifteen days was caught by structure.</p>
<hr />
<h1>Part 2: The Ship's Log</h1>
<p>This voyage is one leg of an ongoing research program: AAT, Algebraic Architecture Theory. Its history is in earlier posts on this blog; the most recent is the <a href="https://blog.iroha1203.dev/atlas-theorem-how-far-can-you-zoom-out">Atlas theorem article</a>.</p>
<p>On an earlier voyage, the theory reached the SAGA theorem. Whether a broken consistency can be repaired is decided by whether an obstruction class — a quantity measuring the twist that blocks repair — vanishes. That is what the theorem says. It went on to detect a one-cent accounting drift in a real open-source system. The point where theory touched the real sea is what we call our Cape of Good Hope. These fifteen days are the waters beyond that cape. There are pilots: algebraic geometry since Grothendieck teaches the seamanship — universal properties, descent, cohomology. But nobody holds a chart of this sea. Over software architecture, which claims stand as theorems and which fall to counterexamples — you find out by sailing.</p>
<p>On this ship, Claude draws the charts, Codex holds the helm, and a human makes the rulings, all under the same thirteen rules. Numbers like G-101 are serial numbers assigned to goals.</p>
<table>
<thead>
<tr>
<th>Day</th>
<th>Goal</th>
<th>Claim</th>
<th>Result</th>
<th>Cycles</th>
</tr>
</thead>
<tbody><tr>
<td>Day 1</td>
<td>G-101</td>
<td>Swap the rewriting rules for parts, and there is exactly one canonical way to carry the whole analysis across</td>
<td>Proved</td>
<td>16</td>
</tr>
<tr>
<td>Day 2</td>
<td>G-102</td>
<td>If structural bugs are zero, every coupling bug is caught on the semantic side</td>
<td>Proved</td>
<td>5</td>
</tr>
<tr>
<td>Day 3</td>
<td>G-103</td>
<td>The coarsest decomposition that can still express a given family of laws exists, and is computable</td>
<td>Proved</td>
<td>6</td>
</tr>
<tr>
<td>Days 3–7</td>
<td>G-104</td>
<td>Under certain conditions, the diagnosis does not depend on reading resolution (the Atlas theorem)</td>
<td>Proved, after 4 refutations</td>
<td>31</td>
</tr>
<tr>
<td>Day 8</td>
<td>G-105</td>
<td>The shape drawn by the layout of parts survives any change of interpretive convention</td>
<td><strong>Refuted</strong></td>
<td>7</td>
</tr>
<tr>
<td>Days 8–12</td>
<td>G-107</td>
<td>Agreement of diagnoses is decidable by a finite computation, yet cannot be recovered from radius-1 local observation</td>
<td>Proved, after 2 spec rewrites</td>
<td>27</td>
</tr>
<tr>
<td>Day 14</td>
<td>G-106</td>
<td>Mismatch under repeated transport is measured by a two-stage obstruction; it vanishes exactly when the whole coheres</td>
<td>Proved</td>
<td>5</td>
</tr>
<tr>
<td>Day 15</td>
<td>G-108</td>
<td>Transport at the upper floors is canonical too, and the only place it can fail is pinned to a single spot</td>
<td>Proved</td>
<td>7</td>
</tr>
</tbody></table>
<h2>Day 1 — Setting sail on uncharted waters</h2>
<p>The first theorem, G-101. The claim: swap the rewriting rules for parts, and there is exactly one canonical way to carry the entire analysis across. In engineering terms, a guarantee that switching modeling conventions does not mean redoing the analysis. For a long time, this theory had described its own construction as "Grothendieck-like." That was a metaphor. On this day, the mathematical condition the name demands — a universal property — was proved, and the metaphor became the name of a device.</p>
<p>Not a day of fair winds. Attempt 1 of the final review: three lanes demanding revisions, one rejecting outright. Nothing passed. Only on attempt 3 did all four lanes clear. This is what "a pass is rare" looks like as daily life (Rule 9).</p>
<h2>Days 2–3 — Fair winds and a fake completion</h2>
<p>Two theorems in two days. G-102: if structural bugs are zero, every coupling bug is caught on the semantic side — grounds for narrowing the search. G-103: the coarsest decomposition that can express a given family of laws exists and is computable — the minimum module granularity your spec requires, delivered by an algorithm.</p>
<p>It looks smooth. The review record says otherwise. G-102's pinned-commit review caught defects on runs 1 through 4, back to back. Run 3's catch was the worst kind: a shell with the right shape and an empty core. It passed on run 5 (Rules 6, 8).</p>
<h2>Days 3–7 — The first storm</h2>
<p>G-104. The claim: the conditions under which an architecture diagnosis does not depend on reading resolution. Put differently, the guarantee that the same defects appear in the same places whether you look at service granularity or module granularity — a guarantee about review granularity, in an era where AI writes the code and humans read it coarsely. This would later be named the Atlas theorem, one of the theory's main results.</p>
<p>In these five days, the target was refuted four times. The calibration condition grew to seven clauses. Three of them were beaten into existence by counterexamples; the rest were rebuilt through refutation and search. After the third refutation, the human called it: stop adding clauses, stop the loop, switch to a search campaign (Rule 12). A brute-force enumeration of 6,086 candidate conditions, plus a structural argument on top of it, delivered the verdict: no amount of clauses in this vocabulary can ever reach the target. That negative survey forced a rebuild of the coefficients themselves — the vocabulary of observations the diagnosis computes over (Rule 2). The loop set sail again, and summited on cycle 31.</p>
<p>Along the way, one cycle smuggled in a hidden premise. The audit caught it, and the artifacts were removed wholesale (Rule 10). The full story of these five days is in the <a href="https://blog.iroha1203.dev/atlas-theorem-how-far-can-you-zoom-out">Atlas theorem article</a>.</p>
<h2>Day 8 — The shipwreck becomes a chart</h2>
<p>G-105. The claim: the space spanned by the supports of structural parts is invariant under pragmatic change — the shape drawn by the layout of parts survives a change of interpretive convention. Cycles 1 through 6 went smoothly. Then, on cycle 7, the last remaining obligation — constructing a <em>firing witness</em>, a concrete example where the premises actually hold and the conclusion is non-trivially true — was proved <strong>impossible in principle</strong>. The voyage's only refutation.</p>
<p>Read the record, though, and this is no sinking. The content of the impossibility was itself a theorem: the chosen coefficient generation rule swallows information indiscriminately, so the diagnostic geometry vanishes universally. The theorem-version of a dashboard with everything on it that tells you nothing. Because the failure policy existed in advance, the counterexample and its mechanism went straight into the ledger. The theory later repositioned this refutation as its first machine-checked evidence that observation must be chosen, not hoarded (Rule 3). The site of the shipwreck became the chart of the shallows.</p>
<h2>Days 8–12 — The ship that lost its mast twice</h2>
<p>G-107. The roughest five days of the voyage.</p>
<p>The target statement died twice. Version 1 — "agreement of diagnoses is characterized by condition C*" — met an exact counterexample. Version 2's sufficiency direction met another. Both were found by the out-of-loop search campaign and the reviews. Within that campaign, the decision to stop searching was itself overruled twice, by independent review, as premature. The judges were being audited (Rule 11). Version 3 rebuilt the claim on new pillars. Agreement of diagnoses is decidable by a finite computation — and yet that decision cannot be recovered from radius-1 local observation. Decidable in CI, unreachable by any pile of local lint rules. Codex proved the pair in a 27-cycle, 49-hour solo run.</p>
<p>The ending is on record too. The spec's adversarial review ran four rounds; mathematical counterexamples hit zero for three consecutive rounds, and the last two rounds' findings were wording-level refinements of the completion criteria. The human called it: the review is saturated. Cut it off (Rule 12). Knowing when to stop a review takes as much design as the review itself.</p>
<p>One more line from the record: the precedent search found no known prior example of a property that is decidable yet locally unobservable, pinned all the way down to a machine-checked proof. <em>Uncharted waters</em> was not a figure of speech.</p>
<h2>Day 14 — A mountain in a single night</h2>
<p>G-106. The claim: the mismatch that accumulates under repeated transport is measured by a two-stage obstruction, and its vanishing is equivalent to the whole cohering. Whether a multi-step refactoring or migration hangs together is measurable, and decidable. Drafted on day 2, this chart had matured for twelve days under rounds of review.</p>
<p>The loop set sail before dawn and finished five cycles while I slept. Cost: 9% of the weekly API limit (Rule 13). And not merely fast. Cycle 4, while closing its own obligation, detected a defect in the evaluator that cycle 2 had built — a wrong order in a non-commutative composition — replaced it, and logged the correction in the ledger. On the first cycle, one lane had passed a result, and the fixed anti-weakening rule overruled it (Rule 7). The ship repaired itself while under way.</p>
<h2>Day 15 — A mirage, and the next chart</h2>
<p>G-108. The theory is built as a tower of abstraction layers, and G-101 had proved transport at the ground floor. The claim here: transport at the upper floors is canonical too, and the only place it can fail is pinned to a single spot. If a migration fails, the cause is <em>here</em> — the endpoint of fault isolation, proved before the work begins. Seven cycles in half a day.</p>
<p>The day belonged to the reviewers. The firing witness submitted on cycle 5 looked, at first glance, like it met the requirements. The pinned-commit review established that its firing depended not on a genuine structural difference, but only on a settings difference and a coefficient swap — and sent it back (Rules 8, 10). The lookout stopped the ship a moment before a mirage was logged as land. After the fix, all eight lanes passed: the usual four, plus four independent formal-review lanes.</p>
<p>The same day, the chart for the next goal, G-109, was finished. Drafted by Claude, hammered through seven rounds of Codex's adversarial review, every finding fixed. The two ships do not trust each other's work. That is what trust looks like in this fleet.</p>
<h2>Epilogue — The sea with no name</h2>
<p>Fifteen days. Seven theorems and one refutation. About 104 cycles, about 67,000 lines. And the last chart is still out on the water.</p>
<p>This sea has no name yet. When we cross it, the chart will be complete — and the sea will receive its name, together with the theorem's.</p>
<hr />
<p><strong>Related</strong>: <a href="https://blog.iroha1203.dev/atlas-theorem-how-far-can-you-zoom-out">Atlas Theorem: How Far Can You Zoom Out?</a> / Repository: <a href="https://github.com/iroha1203/AlgebraicArchitectureTheoryV2">AlgebraicArchitectureTheoryV2</a></p>
]]></content:encoded></item><item><title><![CDATA[Semantic Geometry of Architecture]]></title><description><![CDATA[TL;DR

Does software architecture have a denotational semantics? Yes. But it differs from the classical one in three ways. Syntax is generated from observation. The meaning of a whole system does not ]]></description><link>https://blog.iroha1203.dev/semantic-geometry-of-architecture</link><guid isPermaLink="true">https://blog.iroha1203.dev/semantic-geometry-of-architecture</guid><category><![CDATA[software architecture]]></category><category><![CDATA[Software Engineering]]></category><category><![CDATA[geometry]]></category><category><![CDATA[Math]]></category><category><![CDATA[lean]]></category><dc:creator><![CDATA[Hiroyuki Nakahata]]></dc:creator><pubDate>Tue, 11 Aug 2026 15:07:25 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/69ff29ebf239332df4a93c3a/c65df210-a861-4284-9bfa-68683e858f26.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2>TL;DR</h2>
<ul>
<li><p><strong>Does software architecture have a denotational semantics? Yes.</strong> But it differs from the classical one in three ways. Syntax is generated from observation. The meaning of a whole system does not exist automatically: its existence is itself a theorem. And broken meaning is not an error but a measurement.</p>
</li>
<li><p>The existence theorems (global gluing, descent, torsor structure, resolution invariance) are <strong>machine-verified in Lean 4</strong> inside AAT (Algebraic Architecture Theory).</p>
</li>
<li><p>Beyond existence lies one more question. <strong>What is the shape of the space of meanings?</strong> I name the geometry that studies this question <strong>Semantic Geometry of Architecture</strong>, and announce it here as a research program.</p>
</li>
<li><p>The summit is scheme representability: the conjecture that an architecture carries its own space of meanings. A roadmap closes the article.</p>
</li>
</ul>
<h2>The Commutative Diagram in the Meeting Room</h2>
<p>I once watched a design argument end over a single diagram.</p>
<p>The dispute was about two processing paths. Should the data flow in this order, or that one? Neither side was budging. So we drew it out on the whiteboard. Four corners. Four arrows. Should this square <strong>commute</strong> — should both paths land on the same result?</p>
<p>The moment it was drawn, the argument changed character. The places that had to commute became equations to agree on. The places that were allowed not to commute became decisions to own. The former were agreed in minutes. The latter were settled as judgment calls. The shouting match about which path was "right" ended there.</p>
<p>On the way home I kept turning it over. What exactly had happened?</p>
<p>Programming languages have <strong>denotational semantics</strong>: a theory that assigns mathematical meaning to syntactic objects called programs. Does architecture have one? Wasn't the thing we executed by hand in that meeting room exactly that?</p>
<p>This article answers the question by drawing the map of a research program. Its name is <strong>Semantic Geometry of Architecture</strong>.</p>
<h2>The Staircase of Semantics</h2>
<p>The history of denotational semantics reads as a history of <strong>meaning acquiring structure</strong>.</p>
<p>At first, meaning was sets and functions. A program is a function from inputs to outputs. Not much could be said.</p>
<p>Dana Scott gave meaning <strong>order and topology</strong>. Sequences of approximations acquired limits, and it became possible to speak about recursion. "The meaning of this loop is the limit of its approximations" became, for the first time, a mathematical sentence.</p>
<p>Category theory gave meaning <strong>the structure of composition</strong>. In Lawvere's functorial semantics, a theory is a category, a model is a functor out of it, and semantic equations are commuting diagrams. The square on that whiteboard stands at the far end of this tradition.</p>
<p>The abstract model theory of specifications (Goguen and Burstall's Institutions) coined a slogan: <strong>Truth is invariant under change of notation.</strong></p>
<p>Every step up the staircase enlarged what could be said. So: what is the next step?</p>
<p><strong>Architecture never got to climb this staircase. Not even the first step.</strong></p>
<h2>No On-Ramp</h2>
<p>Denotational semantics starts from syntax. For programs this worked because syntax was given from the start, by the language definition. Whatever the parser accepts is a program; semantics could then be defined as its interpretation.</p>
<p>Architecture has no given syntax.</p>
<p>There was a road that invented syntax first: architecture description languages, formal specifications, design documents. That road carries a curse — <strong>double bookkeeping of description and implementation</strong>. You write the spec, you write the code, and you chase their divergence by hand. The divergence always comes, and the description always loses. Every working engineer knows how that story ends.</p>
<h2>Building the On-Ramp</h2>
<p><strong>Syntax is not written. It is observed.</strong></p>
<p>There is a theory that has been building this on-ramp: <strong>AAT (Algebraic Architecture Theory)</strong>. It takes source code as the source of truth, diagnoses architecture with the weapons of algebraic geometry, and keeps its mathematical core formally verified in Lean 4.</p>
<p>A typed fact extracted from code by observation is called an <strong>Atom</strong>. "This module writes this state." "This operation emits this event." "This value denotes this meaning." Small grains of fact. Atoms are the generators of syntax: from their combinations, the syntax of an architecture — a category of parts, a structure of coverings — is generated.</p>
<p>Generation does not stop at syntax.</p>
<p>Specifications are written as <strong>laws</strong> — that is, as equations. "Replay reconstructs the state." "These two operations commute." And <strong>the place where meaning lives (what mathematicians call the coefficients) is generated from the same Atoms, through the laws.</strong> The denotation function is not a bridge between two independently given worlds. It is a <strong>factorization of a single generative process out of Atoms</strong>.</p>
<p>That the whole semantics needs no input beyond the Atoms and the chosen laws is not a slogan. It has been proved, step by step, in Lean 4 — from the coefficient generation contract up to resolution invariance.</p>
<h2>Turn 1: Syntax Is Observed, Not Written</h2>
<p>In classical denotational semantics, humans write terms and the semantics interprets them. Here the order reverses. Syntax rises out of observation of the implementation, and the semantics stands on top of it.</p>
<p>As a consequence, <strong>semantics stops being a design-time document and becomes a measuring instrument.</strong> You do not write a spec first and bend the implementation to it. You observe the implementation, state equations over the observation, and measure whether they hold. Double bookkeeping cannot arise, in principle: the description to be managed is generated from observation.</p>
<h2>Turn 2: The Existence of Global Meaning Is a Theorem</h2>
<p>In the classical picture, the denotation function is total. The meaning of a whole program exists by definition. For architecture, this premise collapses.</p>
<p>Meaning is given <strong>locally</strong> first: a state where meaning is pinned down only over an individual context — a service, a module, an aggregate. Mathematics calls this a <strong>section</strong>. Choose a family of parts that covers the system (a <strong>cover</strong>), glue the local meanings over the overlaps, and if you reach one coherent meaning for the whole, that is a <strong>global section</strong>. The principle of reconstructing the global from glued local data is called <strong>descent</strong>.</p>
<p><strong>"The meaning of the whole system" does not exist automatically, by definition. Its existence is a theorem, not a given.</strong></p>
<p>And that existence theorem is already proved inside AAT. Call the algebraic fingerprint that aggregates the disagreements on overlaps the <strong>obstruction class</strong>. Over semantic coefficients:</p>
<p>$$\text{a global meaning exists} \iff \text{the obstruction class is zero}$$</p>
<p>$$\mathrm{Nonempty}, P_{\mathrm{sem}}(W) \iff [r_{\mathrm{sem}}] = 0$$</p>
<p>Here (P_{\mathrm{sem}}(W)) is the space of global semantic states over the chosen cover (W), and ([r_{\mathrm{sem}}]) is the obstruction class computed from that cover. A second theorem of the same shape stands on the <strong>repair</strong> side. Call a state where a law's violation has been fixed part-by-part a <strong>lift</strong>. Each part is fixed. Can all of them be fixed at once? The family of local lifts (s) determines a class</p>
<p>$$\partial_U(s) \in \check{H}^1(U, \mathrm{ConDef})$$</p>
<p>((\mathrm{ConDef}) is the coefficient of repair directions), and (\partial_U(s) = 0) is equivalent to the existence of a global lift. The distance between "fixable locally" and "fixable globally" is concentrated into this one class. What the symbol (\check{H}^1) actually contains, we will compute by hand later — counting one-cent coins.</p>
<p>If meaning exists, is it unique? That, too, is a theorem. Global meaning is not unique. The set of solutions moves freely along a single orbit under the action of a group (H^0) — the group of degrees of freedom coherent across the whole cover (a <strong>torsor structure</strong>). In place of uniqueness, <strong>the freedom of choosing a meaning is measured exactly, as a group.</strong></p>
<p>Where did classical denotational semantics go? It did not disappear. <strong>It sits inside this picture as the degenerate case where the cover is trivial and the obstruction class always vanishes.</strong> Architecture lives outside that degeneracy.</p>
<h2>Turn 3: Breakage Is a Measurement, Not an Error</h2>
<p>In the classical picture, if a diagram that ought to commute does not, the semantics is simply wrong; the only move is to fix the definition until it commutes.</p>
<p>Architecture is different. Eventual consistency. The application order of concurrent operations. Data read differently by different teams. A broken commutativity is sometimes a defect to eliminate — and sometimes <strong>a decision the design has deliberately taken on</strong>.</p>
<p>So AAT treats breakage as first-class data. When the residual of an equation does not vanish, it is defined as an obstruction, presented as finite data, and measured as a cohomology class. Not merely "there is breakage": <strong>on which cover, on which overlap, as which class</strong> it breaks is localized.</p>
<p>It also explains what happened in the meeting room. The argument raged because the participants were assuming different commutativity, implicitly. Externalize the diagram, and the equations to agree on separate from the breakages to own. "Which path is right" is a question that does not converge. "Do we own this breakage" is a question that gets decided.</p>
<p>There are more theorems on AAT's shelf. Add a measurement axis, and two designs that had been identical come apart (<strong>Period Separation</strong>). This is the architectural version of the phenomenon program semantics has called full abstraction. The term carries historical weight. Plotkin posed the problem for the language PCF in 1977. Whether a semantics captures observational equivalence exactly stood for nearly twenty years as the hardest open problem in program semantics, falling only to game semantics in the 1990s. Period Separation is advance notice that the same question returns at the level of architecture.</p>
<p>Meanwhile, change the granularity of measurement within one axis — the resolution of the reading — and under calibration conditions the diagnosis matches exactly (<strong>the Atlas theorem</strong>). Coarsening loses no defect; refining fabricates none. That both accidents really do happen once the conditions are broken is included in the same theorem package, with finite counterexamples. There is no contradiction here: adding an axis genuinely refines the diagnosis; changing resolution within an axis does not. The two theorems divide those two directions between them. The Atlas theorem is the <strong>well-definedness theorem</strong> of this semantics. It turns the Institutions slogan — "Truth is invariant under change of notation" — into a theorem, complete with the conditions under which it holds and counterexamples for when it fails. Classical denotational semantics has no counterpart.</p>
<p>An existence theorem. The freedom of uniqueness. Well-definedness. Full abstraction. The chapters of the textbook are all there. Yet none of these theorems was proved for the sake of a semantics. Global gluing came out of repair theory. Conormal descent, out of the search for repair directions. The torsor structure, to quantify the failure of uniqueness. Resolution invariance, for the trustworthiness of diagnosis. Theorems stacked up for separate reasons were re-read, all at once.</p>
<p><strong>Everything we had built was, all along, converging into one semantics.</strong></p>
<h2>The Geometry of Meaning</h2>
<p>Back to the staircase. After order and topology, after categories — what is the next step?</p>
<p>Geometry.</p>
<p>The existence theorems answered "is there a meaning?". Beyond them lies a question the tradition never posed.</p>
<p><strong>What is the shape of the space of meanings?</strong></p>
<p>The algebraic geometry of AAT builds this question into the substance of diagnosis.</p>
<p><strong>Failure carves out space.</strong> A failed equation generates an obstruction ideal, which carves out the range of lawful designs (the lawful locus). "How far does legality extend" exists not as a verdict value but as <strong>a region of space</strong>.</p>
<p><strong>Breakage can be dissected.</strong> Obstructions are not only measured as classes; they are dissected in the vocabulary of singularities and monodromy. The same "non-commuting" can be an isolated gluing mistake or a torsion that winds through the whole design. Different pathologies; different surgery.</p>
<p><strong>Repair is deformation theory.</strong> The dual of the coefficient (I/I^2) attached to the obstruction ideal (I) (this is what (\mathrm{ConDef}) was) gives the <strong>tangent space</strong> of first-order deformations that move a design toward legality. Repair candidates are not an ad-hoc list of patches. <strong>The space of repair candidates is itself a geometric object.</strong></p>
<p><strong>Evolution is geometry in the time direction.</strong> The history of design changes reads as a family of spaces of meaning.</p>
<p>And, at the far edge of the view:</p>
<p>If meanings form a moduli space — a space whose points are the meanings — then <strong>development is a trajectory in that space.</strong> Refactoring is motion within the space; repair is a step along a tangent direction; an architecture that feels stable is a trajectory that has entered a basin of attraction. A singularity becomes, literally, a point where the flow does not extend smoothly.</p>
<p>Statics and dynamics. The shape of the space of meaning is the statics; the motion of development on it is the dynamics. <strong>The commutative diagram tells you where it breaks. The geometry tells you the type of the break, and the surgery.</strong></p>
<h2>The Record of Cliffs</h2>
<p>The Atlas theorem did not stand up on the first try. In AAT, proof search is carried by a loop of AI agents; humans adjudicate. That loop <strong>refuted its own target four times</strong>. State the claim, fall to a counterexample, sharpen the conditions, fall again. Along the way a no-go emerged — piling on shape-only conditions can never reach the goal, in principle — and the definition of the coefficients themselves had to be rebuilt. Each of the four refutations remains in the Lean codebase as a finite counterexample.</p>
<p>How the theorem finally stood can be read directly in the Lean code.</p>
<pre><code class="language-lean">theorem generatedComparisonH1Map_bijective [Fintype Source]
    (M : TargetSupportedNerveMorphism coarseReading fineReading hcoarser
      coarse fine)
    (laws : FiniteLawFamily Source)
    (hcoarse : laws.Adequate coarseReading)
    (hfine : laws.Adequate fineReading)
    (hC : M.ConditionC laws hcoarse hfine) :
    Function.Bijective
      (M.generatedComparisonH1Map laws hcoarse hfine)
</code></pre>
<p>Read it line by line. <code>M</code> is the comparison morphism connecting a coarse reading and a fine one. <code>laws</code> is a finite family of laws. <code>hcoarse</code> and <code>hfine</code> say that both readings are adequate — able to speak that family. The conclusion: the comparison map between the two diagnoses (H^1) is a bijection. That is the exact form of "coarsening loses no defect; refining fabricates none". And the hypothesis <code>hC</code> — calibration condition C — is precisely what the four refutations carved out. Each refutation added a condition; the no-go forced the coefficients to be rebuilt; then the theorem closed in this form. <strong>The scars remain as the hypotheses of the theorem.</strong></p>
<p>A second hunt ran alongside the theorem itself: the attempt to carve out, with finite syntactic conditions, the exact boundary where invariance holds. That one is still open. Three generations of candidate definitions fell to counterexamples in succession, and what remained was a negative result: <strong>within the registered observation vocabulary that sees no further than adjacent parts, that boundary is indistinguishable in principle.</strong> The separation proof is anchored by Lean counterexamples; pinning it down at theorem level is the current target of the climb.</p>
<p>Why tell the cliff stories? <strong>Because only a map with cliffs drawn on it is a map of real terrain.</strong></p>
<p>A record of refutations is not a record of failures. It is the proof that the terrain this theory touches is real, not metaphorical. Cliffs do not appear on maps drawn from wishes. Every time the mathematics pushed back, the map became more accurate.</p>
<h2>Touching the Rock</h2>
<p>After the cliffs, the rock face. Let us verify by computation — once, concretely — that an obstruction class is not a metaphor.</p>
<p>The AAT repository contains a runnable example called "<strong>the one-cent drift</strong>". A pull request lands on a roughly 3,000-line commerce service written in Rust. Unit tests are green in every configuration. Every hunk of the diff can be justified in review. And the PR charges the customer's card one cent more than the checkout page displays.</p>
<p>Money flows through three modules. After the PR, they speak three different rounding conventions. In the demo basket the subtotal is 33,990 cents and the loyalty discount is 2.5%, so the exact discount is 849.75 cents.</p>
<table>
<thead>
<tr>
<th>Module</th>
<th>Convention</th>
<th>Discount</th>
</tr>
</thead>
<tbody><tr>
<td>Display (checkout)</td>
<td>round half-up on the total</td>
<td>850</td>
</tr>
<tr>
<td>Payment</td>
<td>per line, banker's rounding</td>
<td>849</td>
</tr>
<tr>
<td>Ledger (settlement)</td>
<td>never rounds</td>
<td>849.75</td>
</tr>
</tbody></table>
<p>The actual demo measures over a somewhat larger complex; we extract the skeleton. A part, together with the declaration of the range it is responsible for, is called a <strong>chart</strong>. Our three modules are three charts. The overlaps — the interfaces between modules — are also three, and they form a ring. Measure the disagreement on each overlap:</p>
<p>$$r(\text{display} \to \text{payment}) = 849 - 850 = -1$$</p>
<p>$$r(\text{payment} \to \text{ledger}) = 849.75 - 849 = +0.75$$</p>
<p>$$r(\text{ledger} \to \text{display}) = 850 - 849.75 = +0.25$$</p>
<p>This is the raw data of the obstruction.</p>
<p>Can it be repaired? A repair means moving each chart's value within what that chart's law allows. Display and payment can move only in whole cents — screens and cards live in the world of integer cents. The ledger cannot move at all — being exact is the ledger's law. Moving chart (X) by (c(X)) changes each disagreement to</p>
<p>$$r'(X \to Y) = r(X \to Y) + c(Y) - c(X)$$</p>
<p>And here arithmetic decides everything. (c(\text{display})) and (c(\text{payment})) are integers; (c(\text{ledger})) is 0. So no repair can change the <strong>fractional part</strong> of any disagreement. The (+0.75) and the (+0.25) survive every local repair.</p>
<p>(H^1) is a quotient: the space of measured disagreements, divided by the space of disagreements that local repairs can produce. The fractional parts that just survived are a nonzero class in that quotient. What produces the residue is not the shape of the ring as such. It is <strong>the poverty of the moves the laws allow</strong>: two parties that can move only in integers, and one that cannot move at all. <strong>The moment you take the quotient under this constraint system, a residue that no local repair can erase stands as a global invariant.</strong> Try minimizing it yourself. Set (c(\text{payment}) = 1) and the residual shrinks to ((0, -0.25, +0.25)) — but since the fractional part of (+0.75) cannot be erased, some overlap must always retain a quarter of a cent. That is the size of this design's obstruction class. In the demo, a CI gate detects the class and blocks the PR; after a repair that unifies the conventions, it passes. The whole pipeline runs as an example in the repository.</p>
<p>In passing, the classical embedding also becomes a proposition. If the cover has a single chart, there are no overlaps. The place where disagreements would live is zero, so the obstruction vanishes by definition. Classical denotational semantics — where whole-program meaning always existed — is the degenerate special case of this arithmetic. (This is degeneracy in the choice of cover, a different axis from the reading resolution that the Atlas theorem protects.) The claim of Turn 2 is not rhetoric; it is the generalization of this computation.</p>
<p>This (H^1) is not some exotic import. Linters, contract tests, consistency checkers — what they measure are local conditions: inside single files, on single interfaces. The moment you try to measure "the disagreement that no local fix can remove", you are computing this quotient, by definition. <strong>Your consistency tools have been approximating (H^1) without knowing its name.</strong></p>
<p>And the shape has a pedigree. Bell's theorem of quantum mechanics, in sheaf language, says this: all local observations are pairwise consistent, yet no global section explains them simultaneously (the sheaf-theoretic contextuality of Abramsky and Brandenburger). The skeleton — a presheaf, and the absence of a global section — is the same as the computation we just did. The ring of three rounding conventions is locally correct everywhere; only the global ledger fails to exist. <strong>The mathematics that showed quantum mechanics admits no classical global explanation is measuring why your microservices cannot reconcile one cent.</strong> The scale differs. The shape does not.</p>
<h2>The Summit: Semantic Scheme Representability</h2>
<p>This map has a summit. No one has reached it.</p>
<p>Algebraic geometry has an operation called (\operatorname{Spec}). From a commutative ring (R) — an algebraic system in which you can add and multiply — it builds a space (\operatorname{Spec} R) that geometrizes the ring's equational content. Seeing systems of equations as spaces: the founding move of algebraic geometry.</p>
<p>The conjecture. For the collection (\mathrm{Sem}<em>{A,r}(R)) of coherent realizations of meaning over coefficients (R), generated from Atoms and laws ((A) is the architecture, (r) the choice of reading), there exists a geometric object (M</em>{A,r}) and a natural bijection</p>
<p>$$\mathrm{Sem}{A,r}(R) \simeq \operatorname{Hom}(\operatorname{Spec} R,, M{A,r})$$</p>
<p><strong>An architecture (A) carries its own space of meanings (M_{A,r}).</strong> To choose a realization of meaning is to choose a map into this space. If it holds, the space of meanings stands as an intrinsic geometric object, independent of the choice of reading and of the convenience of observation. The existence theorems, the freedom of choice, the tangent space of repairs — all of them line up again as properties of this one space.</p>
<p>The shape of this conjecture is not an accident. Algebraic geometry contains a turn of perspective called the <strong>functor of points</strong> (Grothendieck's relative point of view). A space is not given directly as a set of points; it is <strong>known by the totality of maps into it, from all coefficients</strong>. To know a space and to know the maps into it are the same thing (the Yoneda lemma). So the conjecture — the collection of realizations of meaning coincides with the maps into some space — traces the legitimate way of defining a space of meaning. And the history of moduli problems teaches how steep this road is. The moduli of elliptic curves fails to have a fine moduli scheme because its objects have automorphisms, a failure that forced the invention of stacks. If architectural realizations carry automorphisms, this summit too rises from scheme to stack. The terrain, branch point included, is already mapped in the classics.</p>
<p>The greatest enemy of this conjecture is not an external counterexample but <strong>circular definition</strong>. Define realizations from the start as maps into (M_{A,r}), and representability holds vacuously. Only by defining the collection independently — as <strong>solutions of equations</strong> out of Atoms and laws — and then proving that it coincides with maps into a space, does the claim deserve the name of a theorem. The local part is expected to follow almost by definition, as a matter of solution spaces of equations; the substance of the claim concentrates in the gluing. The heart of this conjecture, too, is descent.</p>
<p>How the conjecture can die has also been designed. If representability fails to close at the scheme level, the obstruction — automorphisms of realizations, higher gluing obstructions — is not a failure: it is cashed in as evidence that the ascent to stacks is necessary. If the affine level trivializes, the independent definition of the functor gets rebuilt. What happens upon refutation is decided in advance. The discipline of the cliffs applies to the future as well.</p>
<p>This is a conjecture. But a map on which the summit is visible and a map on which it is not are different maps.</p>
<h2>On the Name</h2>
<p>I call this research program <strong>Semantic Geometry of Architecture</strong>.</p>
<p>The word order is the content. This is not geometric semantics — geometry used as a tool to explain meaning. It is <strong>geometry whose subject is the space that meaning itself forms.</strong></p>
<p>The overall position:</p>
<ul>
<li><p><strong>AAT</strong> — the pure mathematical foundation: the statics of the space of meaning</p>
</li>
<li><p><strong>Semantic Geometry of Architecture</strong> — the research program opening on top of AAT — this article</p>
</li>
<li><p><strong>The SAGA theorems</strong> — a proved theorem series inside it (gluing, repair, descent)</p>
</li>
<li><p><strong>The Atlas theorem</strong> — a proved solitary peak inside it (resolution invariance; well-definedness)</p>
</li>
<li><p><strong>SFT (Software Field Theory)</strong> — the dynamics on the space of meaning: development as trajectory</p>
</li>
</ul>
<p>One rule attaches to the name. It is <strong>never abbreviated</strong>. The acronym would collide with Grothendieck's Séminaire de Géométrie Algébrique, a monument of algebraic geometry. Out of respect for that SGA, this program is always written out in full. If you are not prepared to write a long name every time, you have no business hanging "geometry" over your door.</p>
<h2>Where We Stand</h2>
<p>Where do we stand now?</p>
<p>Looking back, every stretch of road behind us is paved. Global gluing, conormal descent, the torsor structure, Atlas, Period Separation, the single-source generation completeness series. Every theorem up to this point has passed machine verification in Lean 4. Under the road lies bedrock fixed in the theory's canonical text: syntax generated from Atoms, laws as equation systems, the coefficient generation contract, the denotation functor.</p>
<p>Looking up, a slope with no footprints. The moduli of meaning. The summit of Semantic Scheme Representability. The promotion of breakage to 2-cells (lax denotations). Normal forms of the resolution hierarchy. The return of full abstraction. Not one line of proof exists yet.</p>
<p>The route is drawn. The first technical gate is <strong>base change of coefficients</strong> — widening coefficient generation from a fixed number field to general coefficient algebras. From there the climb runs through the moduli of meaning to representability. The ridge conjectures get picked up along the way.</p>
<p>The scaffolding is all public.</p>
<ul>
<li><p><a href="https://github.com/iroha1203/AlgebraicArchitectureTheoryV2">Theory and Lean formalization</a></p>
</li>
<li><p><a href="https://doi.org/10.5281/zenodo.21603761">The SAGA paper (Zenodo)</a></p>
</li>
<li><p><a href="https://blog.iroha1203.dev/atlas-theorem-how-far-can-you-zoom-out">The Atlas theorem</a></p>
</li>
</ul>
<h2>Coming Back Down</h2>
<p>Time to come down. What the theory brings back to the meeting room is a two-phase structure. <strong>At design time, choose the equation system. At observation time, measure the residuals.</strong> Agreeing on a commutative diagram is choosing an equation system. After implementation, compute the residuals of the chosen system from observation and measure whether the agreement holds. The measurement is mechanized — AAT's tooling implements it. The design-time agreement becomes, unchanged, the observation-time measuring standard.</p>
<p>What happened in that meeting room at the start of this article was a manual execution of the first phase of this semantics. That the argument converged the moment the diagram was drawn was neither luck nor a victory of rhetoric. <strong>That diagram was a component of a semantics.</strong> The theory executes it, not by hand, but as a measuring instrument.</p>
<p>Meaning forms a space. The survey of that space has just begun.</p>
]]></content:encoded></item><item><title><![CDATA[Atlas Theorem: How Far Can You Zoom Out?]]></title><description><![CDATA[TL;DR

A veteran reviewer does not read every line. They switch reading resolution to match the property they are checking. Is there a guarantee that reading coarsely misses no bugs? This article is t]]></description><link>https://blog.iroha1203.dev/atlas-theorem-how-far-can-you-zoom-out</link><guid isPermaLink="true">https://blog.iroha1203.dev/atlas-theorem-how-far-can-you-zoom-out</guid><category><![CDATA[Software Engineering]]></category><category><![CDATA[software architecture]]></category><category><![CDATA[AI]]></category><category><![CDATA[lean]]></category><dc:creator><![CDATA[Hiroyuki Nakahata]]></dc:creator><pubDate>Sat, 08 Aug 2026 15:05:40 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/69ff29ebf239332df4a93c3a/7161d025-66e6-4e71-9a40-324d3b512909.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2>TL;DR</h2>
<ul>
<li><p><strong>A veteran reviewer does not read every line. They switch reading resolution to match the property they are checking.</strong> Is there a guarantee that reading coarsely misses no bugs? This article is the record of proving the conditions under which there is.</p>
</li>
<li><p>AAT (Algebraic Architecture Theory) treats source code as the source of truth, abstracts implementations into Atoms, turns specifications into equations called laws, and analyzes architecture with the weapons of algebraic geometry. Defects appear as cohomology classes — algebraic fingerprints.</p>
</li>
<li><p>Within AAT, an AI agent loop proved in Lean 4 what we call the <strong>Atlas theorem</strong>: for two adequate readings, one a coarsening of the other, satisfying a calibration condition C, the diagnostic fingerprints coincide exactly — zooming out loses no defects, zooming in fabricates none. Break the conditions and both failure modes really occur, with finite counterexamples.</p>
</li>
<li><p>The agent <strong>refuted its own target statement four times</strong> before the summit. A no-go argument then showed that no shape-only condition can ever suffice, forcing the coefficient definition itself to be rebuilt. Five working days, 31 modules / 13,028 lines of Lean, four-lane adversarial review.</p>
</li>
<li><p>The article walks from pixel art and the sampling theorem, through the four refutations and the discovered condition C, to what this means for code review in the era of AI-written code.</p>
</li>
</ul>
<p>A photorealistic portrait and a 16×16 pixel-art sprite can show you the same face. Comics theorist Scott McCloud called this <strong>amplification through simplification</strong>: removing lines doesn't discard information, it sharpens the essence.</p>
<p>Can we say the same about software architecture diagnostics? <strong>Look at your system at service granularity or at module granularity, and the same bugs show up in the same places</strong> — not as a feeling, but as a proven guarantee.</p>
<p>This article is the record of proving that guarantee — we call it the <strong>Atlas theorem</strong> — in Lean 4. The AI agent that set out to prove it <strong>refuted its own target statement four times</strong>. Those four refutations turned out to be the best part. The protagonist here is not any proof technique. It is the discipline that accumulated refutations as results instead of writing them off as failures.</p>
<p>Everything below happened between August 4 and 8, 2026 — <strong>five working days</strong>.</p>
<table>
<thead>
<tr>
<th>Round</th>
<th>What happened</th>
<th>What remained</th>
</tr>
</thead>
<tbody><tr>
<td>Refutation 1</td>
<td>A forgotten "filling" lift</td>
<td>One new condition and a finite counterexample (937 lines of Lean)</td>
</tr>
<tr>
<td>Refutation 2</td>
<td>Parallel lifts, overlooked</td>
<td>One more condition and a counterexample (622 lines)</td>
</tr>
<tr>
<td>Refutation 3</td>
<td>A loop lifted to the wrong endpoints</td>
<td>Yet another condition and a counterexample (903 lines)</td>
</tr>
<tr>
<td>Pivot</td>
<td><strong>A no-go</strong>: adding clauses can never be enough</td>
<td>A rebuilt definition of the coefficients</td>
</tr>
<tr>
<td>Refutation 4</td>
<td>A spec hole in the revised requirements</td>
<td>A repaired declaration rule and a counterexample (433 lines)</td>
</tr>
<tr>
<td>Summit</td>
<td>Claims (i)–(v) all proved in 24 cycles</td>
<td><strong>31 modules / 13,028 lines</strong></td>
</tr>
</tbody></table>
<h2>The project</h2>
<p>The subject is <a href="https://github.com/iroha1203/AlgebraicArchitectureTheoryV2">AlgebraicArchitectureTheoryV2</a>. Take the source code as the source of truth; abstract the implementation into parts called Atoms; turn the specification into equations called laws. Treat architecture as geometry, and analyze it with the weapons of algebraic geometry. That is AAT (Algebraic Architecture Theory), and this monorepo is its formal verification in Lean 4.</p>
<p>The theory had a weak point that everyone had been politely ignoring: <strong>granularity</strong>.</p>
<p>Whenever you analyze an architecture, you implicitly choose a resolution. Services? Modules? Methods? If the diagnosis changes with that choice, then what is the diagnosis actually measuring — a property of the architecture, or <strong>a convenience of the observer</strong>?</p>
<p>There is a precedent for this problem: the Nyquist–Shannon sampling theorem. Sample too coarsely for the signal's bandwidth and <strong>frequencies that don't exist appear</strong> (aliasing) while <strong>signals that do exist vanish</strong>. The moiré stripes in a photograph are the visible face of the former. The granularity problem is the sampling problem of architecture diagnostics.</p>
<h2>Glossary</h2>
<table>
<thead>
<tr>
<th>Term</th>
<th>What it is</th>
</tr>
</thead>
<tbody><tr>
<td><strong>reading</strong></td>
<td>The choice of resolution at which source code is cut into parts (Atoms). The equivalent of choosing the pixel count of a sprite</td>
</tr>
<tr>
<td><strong>diagnostic class</strong></td>
<td>The algebraic fingerprint of a defect. It is computed not inside any single file but as a <strong>twist in how the parts glue together</strong> (mathematically, a cohomology class <code>H¹</code>, defined below in "The stage"). Zero means consistent; nonzero means a defect</td>
</tr>
<tr>
<td><strong>adequate</strong></td>
<td>"The laws you want to state are expressible at this resolution." The formalization of the pixel artist's skill: keep exactly the distinctions the face needs</td>
</tr>
<tr>
<td><strong>comparison map</strong></td>
<td>The bridge that matches the diagnostics of the fine reading against the coarse one. If this bridge is an isomorphism (a one-to-one correspondence), diagnosis does not depend on resolution</td>
</tr>
<tr>
<td><strong>witness</strong></td>
<td>A claim's example or counterexample pinned down in Lean as <strong>finite, concrete data</strong>, not abstract argument</td>
</tr>
</tbody></table>
<h2>The claim — the Atlas theorem</h2>
<p><strong>If a coarsening preserves enough information, then looking finely and looking coarsely detect exactly the same defect fingerprints — nothing appears, nothing disappears.</strong></p>
<blockquote>
<p><strong>The Atlas Theorem</strong> (formally: the Diagnostic Resolution Invariance Theorem) Given two adequate readings, one a coarsening of the other, satisfying the calibration condition C, the comparison map is an isomorphism. <strong>The choice of resolution does not change the diagnosis.</strong> Measuring finer adds no diagnostics (the No-New-Diagnostics corollary); measuring coarser loses none. The counterpart: a coarsening that violates the conditions really does produce <strong>defects that don't exist</strong> or hide <strong>defects that do</strong> — with finite counterexamples.</p>
</blockquote>
<p>In Lean, this is pinned down as a bundle of five claims: (i) the construction of the comparison map, (ii) its bijectivity, (iii) the corollary (measuring finer creates no new diagnostics), (iv) three counterexamples for broken conditions, and (v) a firing witness where all conditions hold and a nonzero diagnostic actually flows across the map. "Claims (i)–(v)" in the table above are these five.</p>
<h3>The stage — nerves, readings, coefficients</h3>
<p>An observation of an architecture is represented as a <strong>nerve</strong>, a finite combinatorial structure. The vertices are <strong>charts</strong>: a part together with a declaration of the region it covers (its <strong>support</strong>). A dependency or overlap between two charts is an <strong>edge</strong>; a declaration that three charts have been checked consistent is a <strong>face</strong> (a triangle). The axioms demanded of a nerve are minimal: each face's three edges (its boundary triple) must meet at matching endpoints — for some charts (A, B, C) they line up as (e_0 : A \to B), (e_1 : A \to C), (e_2 : B \to C). That is all.</p>
<p>The choice of resolution is a <strong>reading</strong>. When one reading is a coarsening of another, a <strong>factor map</strong> (\pi) sending fine values to coarse values exists uniquely (the coarseness order and this factorization are imported wholesale from a theorem proved earlier in the same repository). The fine and coarse nerves are connected by a <strong>nerve morphism</strong> (\varphi) whose chart / edge / face correspondences commute with endpoints and boundaries. This is the formalization of "the sprite corresponds to the original portrait."</p>
<p>Coefficients are generated from laws. Each law in the law family is pushed through the reading (<strong>descended</strong>), and from the descended evaluation values, a generation contract called K0 / K1 (given in full below) builds a coefficient space over each cell, assembling a three-term complex:</p>
<p>$$C^0\ \text{(on charts)} \xrightarrow{\ d_0\ } C^1\ \text{(on edges)} \xrightarrow{\ d_1\ } C^2\ \text{(on faces)}$$</p>
<p>(d_0) takes "the difference of values at an edge's two endpoints"; (d_1) takes "the alternating sum over a face's three edges, (e_0 - e_1 + e_2)." Since (d_1 \circ d_0 = 0) (this too is a theorem derived from the endpoint matching of faces, not an axiom), the <strong>first cohomology</strong></p>
<p>$$H^1 = \frac{\text{1-cochains killed by } d_1}{\text{image of } d_0}$$</p>
<p>is defined. This is what "diagnostic class (fingerprint)" means. The intuition: every pair of adjacent parts is locally consistent, yet <strong>going around a loop, the books don't balance</strong>. Only that "twist" survives the quotient into (H^1). That is why a defect's fingerprint lives not inside any single file but in the gluing structure.</p>
<p>Finally, the <strong>comparison map</strong>. From the nerve morphism (\varphi) and the descent-compatibility of the coefficients, a map of complexes (a cochain map) arises, inducing a map between the (H^1)s. The core claim (ii) of the Atlas theorem says: <strong>under condition C, this induced map is a bijection</strong>. Surjectivity means "coarsening hides no diagnostics"; injectivity means "coarsening fabricates no diagnostics."</p>
<p>The pixel-art metaphor from the opening is not decoration. It matches this structure part for part.</p>
<table>
<thead>
<tr>
<th>Theorem side</th>
<th>Perception side</th>
</tr>
</thead>
<tbody><tr>
<td>The invariance theorem</td>
<td>The sprite shows the same face</td>
</tr>
<tr>
<td>adequacy</td>
<td>The pixel artist's skill (keep only the distinctions that matter)</td>
</tr>
<tr>
<td>False-positive counterexample</td>
<td>Pareidolia (a face in the clouds)</td>
</tr>
<tr>
<td>Hidden-defect counterexample</td>
<td>The expression lost at too low a resolution</td>
</tr>
<tr>
<td>Violating calibration condition C</td>
<td>Moiré / aliasing</td>
</tr>
<tr>
<td>Law-relativity</td>
<td>A sprite good enough for face recognition, but not for reading small print</td>
</tr>
</tbody></table>
<p>The art historian Ernst Gombrich argued that a picture is only half drawn — the viewer supplies the rest — and called that contribution <strong>the beholder's share</strong>. Translated into AAT: what stays invariant across resolutions is not the picture (the code) itself, but <strong>what the beholder evaluates from it</strong> — formalized, the law family. That is exactly why the invariance is law-relative.</p>
<p>And you have met this "beholder" before: <strong>a veteran engineer doing code review</strong>. An experienced reviewer does not read every line at the same magnification. Checking API compatibility, they read at interface granularity; hunting a race condition, they drop to individual lines. <strong>They switch reading resolution according to the property they are checking.</strong> Choosing the right review granularity is, today, a skill that lives in experience and intuition. The Atlas theorem is a first step toward making that granularity mathematically tractable: it gives the question "how coarsely can I read without losing information about this property?" a rigorous footing for the first time.</p>
<p>But the human eye — and the veteran's intuition — does all of this <strong>without a guarantee</strong>. That is why clouds have faces in them, and why reviews miss things. The question this theorem asked was: under what conditions does the guarantee hold? And the identity of that condition — calibration condition C — <strong>was unknown to everyone at the start</strong>. The requirements document (the research card that froze the proof target) said so from day one: "If C holds trivially, the theorem is a restatement. If C is too strong, the theorem is empty. <strong>The identity of C is the substance of the theorem.</strong>"</p>
<p>So it proved. Four times.</p>
<h2>Refutation rounds 1–3: whack-a-mole</h2>
<p>The proof ran as an autonomous AI agent loop. The human (me) froze the requirements document and did nothing but adjudicate. The agent does not stop until it has pinned either a proof or a refutation in Lean. The three rounds below are what the loop brought back early on. All three are refutations.</p>
<h3>Refutation 1: the forgotten filling</h3>
<p>The first candidate conditions, C0–C3 (the original four), looked only at the correspondence of points (charts) and lines (dependencies). The counterexample the agent found: on the coarse map, some loop is a <strong>filled-in triangle</strong> — a face declaring "checked, no problem" has been glued over it. Lift it to the fine map, and there is only the outline. No filling. <strong>A defect fingerprint that vanishes on the coarse side survives on the fine side.</strong> The comparison map has no way to be an isomorphism.</p>
<p>The counterexample was pinned as 937 lines of Lean, and a condition was added: fillings must lift too (C4).</p>
<blockquote>
<p><strong>Condition gained, C4 (plain form)</strong>: every coarse face has at least one fine face mapping to it under the nerve morphism's face correspondence.</p>
</blockquote>
<h3>Refutation 2: parallel lifts</h3>
<p>C0–C4, with the new C4, was broken next. A single road on the coarse map can correspond to <strong>two parallel roads</strong> on the fine map. All of C0–C4 hold, yet the thin loop between the two parallel roads leaves a new fingerprint that exists only on the fine side. Nothing constrained <strong>multiple lifts landing on the same coarse road</strong>. Uniqueness of lifts (C5) was added.</p>
<blockquote>
<p><strong>Condition gained, C5 (plain form)</strong>: at most one fine edge maps to each coarse edge (together with the existing existence clause C2: exactly one).</p>
</blockquote>
<h3>Refutation 3: the displaced loop</h3>
<p>C0–C5 fell as well. This time, a <strong>loop</strong> on the coarse map — a road that returns to its own starting point. Its unique lift connected <strong>two different intersections</strong> on the fine map. A nonzero fingerprint on the coarse side then vanishes on the fine side. Refutations 1 and 2 broke surjectivity (the coarse side misses diagnostics); this one breaks injectivity (the coarse side alone sees a phantom diagnostic). What was needed: a road that returns to itself must lift to a road that returns to itself (candidate C6).</p>
<blockquote>
<p><strong>Condition gained, C6 (plain form)</strong>: every fine edge mapping to a coarse self-loop (an edge whose two endpoints are the same chart) is itself a self-loop.</p>
</blockquote>
<p>Three rounds in, a bad feeling was taking shape. <strong>Every added clause exposed another hole.</strong> Would this ever converge?</p>
<p>One decision made at the very start was paying off. Every refutation is pinned as a <strong>finite counterexample in a Lean file</strong>, not spent as a one-off rebuttal. These files would later be incorporated into the theorem's counterexample part — the proof that "break the conditions and this is how it fails." Not one refutation was wasted.</p>
<h2>The pivot — a no-go that stopped the loop</h2>
<p>Here the human's turn came. The ruling: <strong>stop the clause-adding loop.</strong> Instead, dispatch a separate computational search (a "hunt") to test candidate conditions exhaustively — with three stopping conditions fixed in advance.</p>
<ul>
<li><p><strong>A (success)</strong>: a condition is found that excludes the three known counterexamples and admits no new ones within the search range</p>
</li>
<li><p><strong>B (structural negation)</strong>: it is shown that <strong>no such condition can exist in principle</strong></p>
</li>
<li><p><strong>C (stall)</strong>: progress stops in either direction</p>
</li>
</ul>
<p>The result was <strong>B</strong>. And not as a product of the search itself, but through a single argument discovered while designing it. <strong>The conditions were not lacking. The language for writing conditions was.</strong> That is what came to light.</p>
<p>The argument is called <strong>two-point separation</strong>. On one and the same shape (the same configuration of points, lines, and faces — the same incidence), you can build three worlds that differ only in their coefficient data.</p>
<table>
<thead>
<tr>
<th>World</th>
<th>Comparison map</th>
</tr>
</thead>
<tbody><tr>
<td>Every coefficient dimension is 1</td>
<td>Isomorphism ✅</td>
</tr>
<tr>
<td>Some coefficients zeroed out (a support hole)</td>
<td>Not injective ❌</td>
</tr>
<tr>
<td>Fine-side coefficients doubled</td>
<td>Not surjective ❌</td>
</tr>
</tbody></table>
<p>But every condition candidate considered so far — C0–C5, C6, even candidates imported from category-theory textbooks — was a predicate <strong>computed from the shape alone</strong>. Same shape, no way to distinguish. So any shape-only condition that accepts even one genuine positive example must also accept its impostors with the same face. <strong>No matter how cleverly you keep adding clauses, the answer is not in this language.</strong></p>
<p>In sampling-theorem terms: arrange your sampling grid however ingeniously you like — <strong>without declaring the signal's bandwidth (the coefficients), no guarantee against aliasing can even be written down</strong>.</p>
<p>From this point, a <strong>coefficient generation contract (K0/K1)</strong> was introduced — coefficients must be generated from law evaluation values by a unique rule — and condition C was relativized to per-coordinate sub-maps (subnerves) of the coefficients. What matters is that this redefinition was not a matter of taste. Given the no-go, <strong>there was no other way</strong>. The definition was not designed. It was discovered.</p>
<h3>The discovered language — the coefficient contract K0 / K1 and condition C in full</h3>
<p>The coefficient side first.</p>
<p><strong>K1 (derived supports)</strong>. Only charts may declare a support (a covered region). An edge's support is derived as the intersection of its two endpoint charts' supports; a face's support as the intersection of its three boundary edges'. Allow independent support declarations per cell, and the no-go's "support hole" — zeroing out just one edge's coefficients by hand — becomes constructible. So the freedom to declare is itself removed.</p>
<p><strong>K0 (generated coefficients)</strong>. The coefficient field is fixed to (\mathbb{Q}). Each cell's coefficient coordinates are pairs ((\text{law}, \text{value})), where the values are the <strong>distinct</strong> values the descended law evaluation takes on the cell's support, each with multiplicity one. Indexing by occurrence counts or support sizes is not permitted — this kills the no-go's "duplication." The spaces in each degree are</p>
<p>$$C^0 = \mathbb{Q}\text{-valued functions on } {(\text{chart}, \text{law}, \text{value})}$$</p>
<p>$$C^1 = \mathbb{Q}\text{-valued functions on } {(\text{edge}, \text{law}, \text{value})}$$</p>
<p>$$C^2 = \mathbb{Q}\text{-valued functions on } {(\text{face}, \text{law}, \text{value})}$$</p>
<p>and the differentials are generated coordinate-wise: the ((\text{edge}, \text{law}, \text{value})) component of (d_0) is the difference of the two endpoint charts' matching ((\text{law}, \text{value})) components; the ((\text{face}, \text{law}, \text{value})) component of (d_1) is the alternating sum (e_0 - e_1 + e_2) over the boundary triple. Matching labels correspond identically; absent labels give zero. <strong>No other coordinate correspondence is generated.</strong> (d_1 \circ d_0 = 0) is derived as a theorem by per-label computation from the endpoint matching of faces, never assumed. The comparison map's coefficient part is likewise not declared but generated, as the identity on ((\text{law}, \text{value})) from the (\pi)-compatibility of descent. Adding, duplicating, or omitting coordinates is forbidden across the board. The word "contract" is meant literally: the substance of this language is how little it lets you declare.</p>
<p><strong>Coordinate subnerves and relativization</strong>. For each coefficient coordinate ((\text{law}, \text{value})), the cells carrying that coordinate (the cells on whose derived support the descended evaluation takes that value) form a sub-nerve, the <strong>coordinate subnerve</strong>. Under K0/K1, (H^1) and the comparison map <strong>decompose as direct sums</strong> over coordinates, and each block reduces to a one-dimensional constant-coefficient comparison over its subnerve. This decomposition is the mathematical justification for relativizing: the geometric conditions C1–C4 need only be imposed per coordinate subnerve, while the global C0, C5, C6 are imposed on the whole nerve.</p>
<p>With that, condition C in full:</p>
<ul>
<li><p><strong>C0 (cover-image agreement)</strong>: each coarse chart's support equals the union of the (\pi)-images of the supports of the fine charts in its fiber</p>
</li>
<li><p><strong>C1 (fiber connectivity)</strong>: in each coordinate subnerve, each coarse chart's fiber graph (the fine charts mapping to it, plus the fine edges staying within the fiber) is nonempty and connected</p>
</li>
<li><p><strong>C2 (edge-lift existence)</strong>: in each coordinate subnerve, every coarse edge has a lift within the subnerve</p>
</li>
<li><p><strong>C3 (local fiber acyclicity)</strong>: in each coordinate subnerve, every rational 1-cycle on a fiber graph is spanned by (\mathbb{Q})-linear combinations of boundaries of fine faces whose boundary edges all lie in the fiber. A local condition, equivalent to the vanishing of the fiber's first homology</p>
</li>
<li><p><strong>C4 (coarse-face lift)</strong>: in each coordinate subnerve, every coarse face has at least one fine face mapping to it — <strong>the spoils of refutation 1</strong></p>
</li>
<li><p><strong>C5 (unique coarse-edge lift)</strong>: each coarse edge's (\varphi)-fiber has at most one element; with C2, exactly one — <strong>the spoils of refutation 2</strong></p>
</li>
<li><p><strong>C6 (self-loop endpoint reflection)</strong>: every fine edge mapping to a coarse self-loop is itself a self-loop — <strong>the spoils of refutation 3</strong></p>
</li>
</ul>
<p>Finally, the condition list carries a <strong>prohibition rule</strong>. C may not contain any clause equivalent — or close to equivalent, even one-directionally — to "the comparison map is an isomorphism" or "one side's (H^1) vanishes." That would smuggle the conclusion into the hypotheses. The single explicit exception is C3, a local condition that sees only the internal data of individual fibers, the analogue of the Leray-type local acyclicity assumptions imposed on covers in classical Čech theory. The boundary between legitimate assumption and smuggling is itself fixed as part of the condition list — because the prover is an AI agent. Escape routes are closed at the specification.</p>
<h2>Refutation 4, and the summit</h2>
<p>The loop restarted on the revised specification and, after a few cycles of groundwork, the agent attacked <strong>a spec hole in the revision itself</strong> (refutation 4). A nerve morphism may declare fine edges and faces with no coarse counterpart as "unmapped (degenerate)." The declaration rule had a hole: the condition for declaring a face unmapped did not require the same declaration of the three edges around it. In the counterexample exploiting the hole, the construction of the comparison map (claim (i)) itself fails. Pinned in 433 lines, repaired by making the declaration <strong>hereditary</strong>: a face may be declared unmapped only if its three boundary edges are already declared unmapped.</p>
<p>From there it was a straight run. Restarted once more after the repair, the loop reached the top in 24 iterations (cycles).</p>
<h3>The route to the top — structure of the proof</h3>
<p><strong>Stage one: block decomposition.</strong> Under the K0/K1 generation rules, prove as theorems that the complexes, (H^1), and the comparison map decompose as direct sums over the coefficient coordinates ((\text{law}, \text{value})). Each block reduces to a <strong>one-dimensional constant-coefficient</strong> comparison over its coordinate subnerve. The global problem splits into a direct sum of the simplest possible pieces. From here on, attack block by block.</p>
<p><strong>Stage two: injectivity.</strong> Analyze the kernel of the block comparison map and show that a nonzero coarse diagnostic class cannot die on the fine side. This is where C6 earns its keep — it closes the accident path found by refutation 3, where a nonzero coarse class evaporates below. (Refutation 4's hereditarity works earlier still, underwriting the very constructibility of the comparison map.)</p>
<p><strong>Stage three: surjectivity (the crux).</strong> Show every fine diagnostic class comes from a coarse one. Four moves:</p>
<ol>
<li><p>A <strong>discrete Stokes theorem</strong> translating C3 into "the vanishing of periods around fiber loops." Local face-filling becomes an analysis-flavored statement about loop integrals vanishing</p>
</li>
<li><p>From vanishing periods, construct a <strong>primitive</strong> on each fiber — the finite-graph version of "a curl-free field has a potential"</p>
</li>
<li><p>Express the primitive-normalized residue as a coarse 1-cochain, using the "exactly one lift" channel provided by C2 / C5 (<strong>descent</strong>)</p>
</li>
<li><p>Use C4's face lifts to check the 1-cochain is a cocycle on the coarse side, and promote it. Surjectivity closes</p>
</li>
</ol>
<p><strong>Stage four: main theorem and corollary.</strong> Bundle the block bijections along the direct-sum decomposition into the global bijection — claim (ii). As a consequence, measuring finer creates no new diagnostic classes: the No-New-Diagnostics corollary, claim (iii).</p>
<p><strong>Stage five: counterexamples and firing.</strong> The three counterexamples of inadequate coarsening — fabricated diagnostics, hidden diagnostics, and broken condition C (claim (iv); the legacy of the refutation rounds becomes the material here) — plus a single firing witness where every condition holds simultaneously and a nonzero diagnostic class actually flows across the comparison map (claim (v)). Without (v), the theorem could be hollow — "no example satisfying condition C actually exists" — and the concrete Lean data forecloses that.</p>
<p>The division of labor among the conditions is clean. <strong>C3 kills local loops; C2 / C5 provide the unique communication channel for lifts; C4 carries the consistency of faces; C6 and hereditarity prevent the self-loop and degenerate-declaration accidents; C0 / C1 lay the ground.</strong> Every clause won through refutation is used at a specific step of the injectivity or surjectivity proof. There are no ornamental clauses.</p>
<p>The final artifact: <strong>31 Lean modules, 13,028 lines</strong>. Every new declaration depends on standard axioms only — no <code>sorry</code>, no added axioms. Acceptance ran through an <strong>independent adversarial review</strong> in four lanes (two mathematics, two Lean), all reporting "no major findings," plus a separate completion audit that includes checks for proof cheat-routes.</p>
<h2>Operating discipline for AI agents</h2>
<p>This project's operating principle for AI has been constant: <strong>give the AI no cheap way out</strong>. For mathematical research, it became this division of roles: <strong>the human only adjudicates</strong> (freezing the spec, approving revisions, designing stopping conditions); <strong>an implementation agent runs the proof loop; a review agent runs adversarial review</strong>.</p>
<p>Six rules carried those five days.</p>
<ol>
<li><p><strong>Refutations are first-class.</strong> "A refutation is a legitimate result" was written into the requirements document in advance. So four refutations became accumulation rather than demoralization, and the counterexamples were incorporated into the theorem itself</p>
</li>
<li><p><strong>Declare the crux up front.</strong> "The identity of C is the substance of the theorem" was written at the start — so each refutation read as "one more piece of the substance unearthed," not as failure</p>
</li>
<li><p><strong>Freeze the specification.</strong> The requirements document does not change while the loop runs; revisions go through human adjudication and independent review. Because the goalposts cannot move, a refutation means what it says</p>
</li>
<li><p><strong>Pair every clause with a firing requirement.</strong> Each added condition brings a matching witness requirement that the condition actually does work somewhere. This closes the path where a theorem hollows out under layers of protective clauses</p>
</li>
<li><p><strong>Switch to diagnosing the language.</strong> After three rounds of clause-adding, the question changed from "which clause is missing?" to "can this language express the answer at all?" The no-go is the product of that switch</p>
</li>
<li><p><strong>Multi-lane adversarial review.</strong> Agents other than the implementer review the work in independent mathematics and Lean lanes, structurally removing the incentive to declare victory</p>
</li>
</ol>
<p>We stepped on every one of these traps, repeatedly. The rules are what came out.</p>
<h2>Honest limits</h2>
<ul>
<li><p>The result stands at <strong>machine verification in Lean plus internal multi-lane review</strong>. External peer review (journal submission) is ahead; a paper is in preparation</p>
</li>
<li><p>The theorem is a statement about finite models over a fixed coefficient field. Condition C is sufficient; a general characterization of necessity is explicitly left open</p>
</li>
<li><p>"The diagnosis is invariant," not "the observation is invariant." A coarse reading does not make the raw observations look the same; the claim is that the fingerprints of what the laws evaluate coincide</p>
</li>
</ul>
<h2>Why "Atlas"</h2>
<p>An atlas is a book of maps drawing the same world at different scales. Open any page and the same country is there. That is what this theorem says.</p>
<p>The name comes from inside the theory, not from marketing. AAT's canonical text has long had the vocabulary <strong>chart atlas</strong> — a cover as a collection of charts — including the very phrase "when the chart atlas changes." The Atlas theorem is, literally, the invariance of diagnosis under change of chart atlas. There is a mathematical coincidence too: that a manifold's identity does not depend on the choice of atlas is the first invariance principle you meet in differential geometry.</p>
<p>The next step is already fixed. Re-measure a real microservice system at three granularities — service, module, method — and observe that diagnostics stay stable within the adequate range, and that fabricated and hidden defects really appear when adequacy breaks. A <strong>resolution sweep</strong>: the theorem and its counterexample pairs replayed on real data. The guarantee that the sprite shows the same face as the portrait — this time on a production codebase.</p>
<p>One last thing: why this theorem, now. In the era of AI-written code, the bottleneck of development has moved from generation to review. AI agents write faster than humans can review line by line, and <strong>"how coarsely can we afford to read?" has become the question that sets the throughput of a development pipeline.</strong> The only way review scales is to read at coarser resolution — and until now there was no guarantee that coarseness doesn't distort the diagnosis. The Atlas theorem is a first step toward that guarantee, asked and answered as a theorem. And not by coincidence, the theorem itself was proved in the middle of the same problem — "how do humans accept AI output produced at full speed?" — held up by the operational answer of multi-lane review and audits. Operations and mathematics are digging at the same bottleneck from opposite sides.</p>
<p>The full history — theory, proofs, counterexamples, audits — is in the public repository: <a href="https://github.com/iroha1203/AlgebraicArchitectureTheoryV2">AlgebraicArchitectureTheoryV2</a></p>
<hr />
<p><em>As of this writing, the theorem's Lean proofs, counterexamples, and completion audits are all reproducible from the repository. Questions and objections welcome.</em></p>
]]></content:encoded></item><item><title><![CDATA[Taming a 40-Minute Lean CI: Three Rounds, Three Wrong Suspects]]></title><description><![CDATA[Our Lean 4 + mathlib project used to spend 41 minutes in CI on every single PR. Today, the worst case — rebuilding the heaviest files from scratch — takes 12 minutes, and an ordinary PR finishes in a ]]></description><link>https://blog.iroha1203.dev/taming-a-40-minute-lean-ci</link><guid isPermaLink="true">https://blog.iroha1203.dev/taming-a-40-minute-lean-ci</guid><category><![CDATA[lean]]></category><category><![CDATA[AI]]></category><category><![CDATA[ci-cd]]></category><category><![CDATA[GitHub Actions]]></category><dc:creator><![CDATA[Hiroyuki Nakahata]]></dc:creator><pubDate>Sun, 02 Aug 2026 06:49:44 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/69ff29ebf239332df4a93c3a/255603b2-9dad-4b98-9fbd-7f9b723efa5b.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Our Lean 4 + mathlib project used to spend 41 minutes in CI on every single PR. Today, the worst case — rebuilding the heaviest files from scratch — takes 12 minutes, and an ordinary PR finishes in a few minutes on an incremental build.</p>
<table>
<thead>
<tr>
<th>What we fixed</th>
<th>Before</th>
<th>After</th>
</tr>
</thead>
<tbody><tr>
<td>Kernel axiom audit</td>
<td>7m 11s</td>
<td><strong>11s</strong></td>
</tr>
<tr>
<td>An ordinary PR</td>
<td>41m (full rebuild, always)</td>
<td><strong>a few minutes (incremental)</strong></td>
</tr>
<tr>
<td>Worst case (heaviest files fully rebuilt)</td>
<td>41m (same)</td>
<td><strong>12m</strong></td>
</tr>
</tbody></table>
<p>This article records the improvement in three rounds, each written in the same shape: <strong>problem → hypothesis → verification → fix</strong>. Spoiler up front: all three times, the first hypothesis — the culprit our intuition pointed at — turned out to be innocent. The hero of this story is not any individual trick. It is the profiling that kept calmly rejecting our hypotheses.</p>
<p>Lean-specific concepts are explained as they appear, so you should be able to follow without knowing Lean.</p>
<p>One more thing. Nearly all of the measurement and implementation in these three rounds was done by AI agents (Rounds 1 and 2 in interactive sessions with a human present; Round 3 by an autonomous loop driven by a requirements document). The human — me — did two things: approve the numeric targets and accept the results. The second half of the article covers that operation, in particular the machinery that keeps an AI from converging on cheap solutions.</p>
<h2>Background: the project</h2>
<p>The subject is <a href="https://github.com/iroha1203/AlgebraicArchitectureTheoryV2">AlgebraicArchitectureTheoryV2</a>, the same monorepo my previous articles have covered — a formal verification of a theory of software architecture in Lean 4.</p>
<p>Here is the complete list of Lean machinery this article needs — five items:</p>
<table>
<thead>
<tr>
<th>Term</th>
<th>What it is</th>
</tr>
</thead>
<tbody><tr>
<td><strong>Lean 4</strong></td>
<td>A proof assistant: a programming language in which mathematical proofs are machine-checked. Think of it as a language where "the proof compiles" is the acceptance test</td>
</tr>
<tr>
<td><strong>mathlib</strong></td>
<td>Lean's enormous mathematics library — over 1.5 million lines of community-maintained code at the time of writing. Depending on it gives you a "standard library of mathematics," at the cost of build sizes to match</td>
</tr>
<tr>
<td><strong>lake</strong></td>
<td>Lean's build tool, the equivalent of cargo for Rust or npm for JS. It builds per file (module) and emits <code>.olean</code> artifacts</td>
</tr>
<tr>
<td><strong>elaboration</strong></td>
<td>Lean's equivalent of "compilation": type inference, implicit-argument resolution, and proof checking, all in one. <strong>Almost all Lean build time is elaboration</strong></td>
</tr>
<tr>
<td><strong>declaration</strong></td>
<td>A single definition or theorem. When this article says "4,000+ audited declarations," this is the unit being counted</td>
</tr>
</tbody></table>
<p>For scale: the audit covers 4,000+ declarations, and among them sits one heavyweight file — a construction of schemes from algebraic geometry — that took 38 minutes to build on its own.</p>
<p>CI runs on GitHub Actions. Alongside <code>lake build</code>, every PR runs a <strong>kernel axiom audit</strong> — a machine check that every theorem is genuinely proved, with no cheating in the proofs. That gate is non-negotiable; Round 1 explains what it actually does.</p>
<h2>Round 1: The axiom audit, 7m 11s → 11s</h2>
<h3>Problem</h3>
<p>In Lean, every theorem is derived from axioms, and the derivation is machine-checked by the kernel (a small, trusted checker). "The proof went through" means it passed this check. The important wrinkle is that Lean has an escape hatch called <code>sorry</code>. Writing <code>sorry</code> means "I'll prove this part later," and it does not fail on the spot (your editor does show a warning). Internally, it silently assumes an axiom called <code>sorryAx</code>. Users can also add arbitrary unproven propositions as axioms with the <code>axiom</code> keyword.</p>
<p>In other words, "CI is green" alone cannot distinguish "everything is proved" from "someone plugged the gaps with axioms." So for each declaration, we walk its dependencies all the way down and compute <strong>the set of axioms it reaches</strong>, then check that this set stays within the three standard axioms accepted across Lean and mathlib — <code>propext</code>, <code>Quot.sound</code>, and <code>Classical.choice</code> (the axiom of choice is that last one). This is the axiom audit. A hidden <code>sorry</code> or a smuggled-in axiom cannot survive it. If you call yourself a formal verification project, you don't get to skip this gate.</p>
<p>Lean exposes this walk-and-collect operation as a function called <code>collectAxioms</code> (it is the same machinery that runs when you type <code>#print axioms my_theorem</code>). Our CI ran it over every declaration — 1,207 at the time, over 4,300 today — and that step took 7 minutes 11 seconds on every run. The audit was heavier than the build itself.</p>
<h3>Hypothesis</h3>
<p>The audit's entry point is a giant file, 5,000+ lines, listing every audited declaration. Intuition speaks up: "A file this big must be expensive to elaborate. Split it and it'll get faster."</p>
<h3>Verification</h3>
<p>Before splitting anything, we measured where the time went. The result flatly contradicted the intuition:</p>
<ul>
<li><p>Resolving imports: ~10 seconds</p>
</li>
<li><p>Elaborating the 5,000-line listing: <strong>zero difference</strong> (changing the line count substantially didn't move the audit time)</p>
</li>
<li><p><code>collectAxioms</code> × 1,207 declarations: <strong>~5 minutes</strong></p>
</li>
</ul>
<p>The culprit was not the big file. It was how the audit was being called. To find the axioms a declaration reaches, you must walk the dependency graph to its roots — the theorems it uses, the theorems <em>those</em> use, and so on. <code>collectAxioms</code> performs this walk <strong>from scratch for every declaration</strong>. But every declaration in the project shares the same enormous foundation, mathlib — so 1,207 declarations were walking essentially the same graph 1,207 times. In complexity terms: O(declarations × graph). Splitting the file touches neither factor of that product. It was ineffective even on paper, but having the measurement in hand is what let us reject it in one line and move on.</p>
<h3>Fix</h3>
<p>We made the audit two-phase:</p>
<ol>
<li><p><strong>Success path</strong>: share a visited set — "never walk the same node twice" — across all declarations, and traverse the graph <strong>exactly once</strong>. Since the question is "does the union of reached axioms stay within the standard three?", one shared walk gives the same verdict as per-declaration walks</p>
</li>
<li><p><strong>Failure path</strong>: only when a non-standard axiom shows up, fall back to the classic per-declaration walk to attribute <em>which</em> declaration is guilty, with error messages in exactly the original format</p>
</li>
</ol>
<p>That collapses O(declarations × graph) to O(graph) on the success path without giving up any error quality. It is the textbook visited-set move from graph traversal — no Lean-specific magic anywhere. Measured in CI, the audit went from 7m 11s to 11s, and the whole <code>lake build</code> job at the time went from about 9.5 minutes to 2m 12s.</p>
<p><strong>Round 1's lesson</strong>: suspect "heavy operation × repetition count" before "heavy file." And record the rejected hypothesis (file splitting: no effect) as a result in its own right — so nobody after you re-digs the same hole with the same intuition.</p>
<h2>Round 2: Every PR took 41 minutes — the cache was saved <em>before</em> the build</h2>
<h3>Problem</h3>
<p>Round 1 happened in mid-July, when the <code>lake build</code> job had shrunk to a bit over two minutes. Over the following two weeks, a series of heavyweight algebraic-geometry implementations landed (Round 3's protagonist grew fat in this period), and at some point every PR — even one touching a single file — was running a full cold build of the entire tree: about 41 minutes.</p>
<p>Lean builds are incremental, in the same spirit as C++ or Rust. If the build artifacts (<code>.olean</code> files, stored per module under <code>.lake/build</code>) are still around, only the changed files and their downstream need rebuilding. For that to work in CI, the previous run's artifacts have to be carried over as a cache. The cache was configured. It just wasn't working.</p>
<h3>Hypothesis</h3>
<p>When a cache "doesn't work," the usual suspects are a key mismatch or eviction. Is the toolchain hash off? Are old generations being evicted?</p>
<h3>Verification</h3>
<p>Before auditing keys, we read the CI log timestamps top to bottom. Two lines sat one second apart:</p>
<pre><code class="language-plaintext">23:19:35  Cache save   (final internal step of lean-action)
23:19:36  lake build +Formal.AG starts
</code></pre>
<p>The cache <em>was</em> being saved. <strong>Before the project was built.</strong></p>
<p>We set up Lean with lean-action — a composite action (a reusable bundle of steps) — configured with <code>build: false</code>, running the build ourselves in a later step. But lean-action saves <code>.lake</code> (the build artifact directory) to the cache in its own final internal step, and a composite action's internal steps always run before your subsequent steps. So the 2.15 GiB being saved contained only dependency artifacts (mathlib); the project's own <code>.olean</code> files <strong>had never been cached at all</strong>. No wonder every PR took 41 minutes — each one rebuilt all of our own code from a blank slate.</p>
<p>There was a bonus discovery. mathlib is too big for individual users to build, so the community distributes prebuilt artifacts from Azure (<code>lake exe cache get</code>; about 15 seconds in our measurements, environment-dependent). The cached 2.15 GiB was therefore something you could fetch in 15 seconds anyway — worthless in the GitHub cache. Worse, at 2.15 GiB × 4 generations, it was nearly exhausting the repository's 10 GiB cache quota. A useless cache was crowding out the useful one.</p>
<h3>Fix</h3>
<p>We separated the cache responsibilities:</p>
<ul>
<li><p>Disable lean-action's GitHub cache (<code>use-github-cache: false</code>); mathlib artifacts keep coming from the Azure cache</p>
</li>
<li><p>Explicitly restore (before build) and save (<strong>right after build</strong>) only the project's own <code>.lake/build</code></p>
</li>
<li><p>Prefix the key with a hash of <code>lean-toolchain</code> + <code>lake-manifest.json</code>, so toolchain or mathlib bumps fall back to a proper cold build</p>
</li>
<li><p>Save with <code>always()</code>, so failed builds still persist partial artifacts — every push to a red PR reuses everything built up to the failure point, making iteration fast</p>
</li>
</ul>
<p>The actual workflow looks like this (excerpt). The one thing that matters: put the save <em>after</em> the build yourself.</p>
<pre><code class="language-yaml">- name: Restore Formal build cache
  uses: actions/cache/restore@v5
  with:
    path: .lake/build
    key: formal-build-${{ runner.os }}-${{ hashFiles('lean-toolchain', 'lake-manifest.json') }}-${{ github.sha }}
    restore-keys: |
      formal-build-${{ runner.os }}-${{ hashFiles('lean-toolchain', 'lake-manifest.json') }}-

- name: Build
  run: lake build +Formal.AG

- name: Save Formal build cache
  if: always()
  uses: actions/cache/save@v5
  with:
    path: .lake/build
    key: formal-build-${{ runner.os }}-${{ hashFiles('lean-toolchain', 'lake-manifest.json') }}-${{ github.sha }}
</code></pre>
<p>With this, PRs that don't touch the heavyweight geometry files settle into cache hit + incremental build: a few minutes.</p>
<p>In fairness to <a href="https://github.com/leanprover/lean-action">lean-action</a>: its cache works correctly in the standard usage where you let the action run the build too. It was our configuration — <code>build: false</code> with the build held outside — that stepped on the trap.</p>
<p><strong>Round 2's lesson</strong>: when "the cache isn't working," read the timestamps before you audit the keys. Composite actions are convenient, but their internal step order is orthogonal to your workflow's step order. "Is the save actually after the build?" is worth checking with your own eyes.</p>
<h2>Round 3: The 38-minute Geometry.lean — splitting it didn't make it faster</h2>
<h3>Problem</h3>
<p>The largest remaining bottleneck was a single file: <code>Geometry.lean</code>, 5,129 lines, 108 declarations. It constructs actual schemes — the central objects of algebraic geometry, heavyweight abstractions even by modern mathematics standards — on top of mathlib, and it alone took 38.3 minutes in CI, 46% of the project's total CPU time. Even with Round 2's cache, any PR touching this file's upstream paid the full 38 minutes.</p>
<p>This time we fixed numeric targets before starting: <strong>longest module ≤ 600 seconds, total across the target modules ≤ 1,800 seconds</strong>, judged by the module times reported in one and the same GitHub Actions full build. Measurement method included, approved by the human before implementation began.</p>
<h3>Hypothesis</h3>
<p>It's a giant 38-minute file — split it into modules along its dependency structure and rebuilds will shrink and speed up. This is the "splitting makes it faster" hypothesis that Round 1 already rejected once, now in its build-time incarnation. Since elaboration runs per declaration, this time the reasoning looks sound.</p>
<h3>Verification</h3>
<p>First, a per-declaration profile. Lean ships with a built-in profiler; one command tells you which processing step of which declaration took how many seconds. Here is what we used:</p>
<pre><code class="language-bash">lake env lean --profile --json \
  -Dprofiler.threshold=10 \
  -Dtrace.profiler=true -Dtrace.profiler.threshold=10 \
  -Dtrace.profiler.output=geometry-before-trace.json \
  Formal/AG/Examples/StandardGeometryReference/Geometry.lean
</code></pre>
<p>The breakdown: kernel type checking 57%, defeq (deciding whether two terms are definitionally equal) 39%, with the time massively concentrated in a dozen or so declarations. Following that center of gravity and the dependencies, we split the file into a 7-module DAG:</p>
<pre><code class="language-plaintext">RawGeometry
└─ SectionRings
   ├─ LeftRestriction
   ├─ RightRestriction
   ├─ OverlapLeftRestriction
   └─ OverlapRightRestriction   * these four import nothing from each other → build in parallel
      └─ Scheme
</code></pre>
<p>We proved mechanically that the split broke nothing. Lean's <code>#check</code> prints a declaration's statement — the theorem's actual claim. We took the <code>#check</code> output for all 168 public declarations of the old file (794 lines), before and after the split, under the same toolchain and the same printing settings, and confirmed the SHA-256 hashes match. Not one character of any theorem statement changed. Being able to certify "the mathematical content is preserved" with a hash instead of eyeballs, even for a mechanical refactor, is one of the quiet joys of a formal verification project.</p>
<p>Then we measured the <em>after</em> in CI — and <strong>missed the targets</strong>. Longest module 770 seconds (target 600), total 2,483 seconds (target 1,800). Splitting shrinks rebuild scope and buys parallelism, but it does not remove a single second of elaboration. With module-boundary overhead, the total actually grew.</p>
<p>Digging deeper into the profile exposed the real culprit. The time was not spread thinly in proportion to line count. It was concentrated in the <strong>cost of definitional unfolding</strong> around specific definitions.</p>
<p>A short detour on definitional unfolding. In Lean, a definition is a pair of a name and a body, and the body can be unfolded in place when needed — much like a compiler inlining, except Lean does it <strong>during type checking</strong>. When deciding "do these two expressions have the same type?", Lean matches two superficially different terms by peeling definitions open (unification). Normally this is what lets proofs stay short. But when large-bodied definitions get involved, each peel exposes more definitions, and the terms balloon.</p>
<p>In our file, that ballooning happened while checking the functions that actually compute the scheme's components (large-bodied definitions) and the lemmas stating those computations are correct. The 96% of profile time in kernel checking and defeq was exactly the cost of checking these bloated terms: individual operations of 15–104 seconds each, stacked up inside single commands.</p>
<p>Line count was never the crime — the same shape as Round 1. The file wasn't heavy because it had 5,129 lines; it was heavy because of <strong>how specific definitions were being referenced</strong>.</p>
<p>For the record, we also tried two pieces of "standard-issue optimization" at this stage (switching to <code>CommRingCat.hom_ext</code>; using <code>congrArg CommRingCat.ofHom</code> — both established mathlib idioms). Neither improved the measurements; both were rejected. An idiom failing to help is also a data point, once you have a profile.</p>
<h3>Fix</h3>
<p>Surgery at the one point the profile indicated. The policy was uniform: <strong>rewrite so that the type checker never needs to peel the definitions.</strong></p>
<ul>
<li><p>Separate a value's definition from the proofs about that value. When they are entangled, checking the proof part drags in unfolding of the value's entire body</p>
</li>
<li><p>Pin lemma statements to named functions rather than unfolded expressions. If the match succeeds on the name, unification has no reason to open the body</p>
</li>
<li><p>Consolidate the proof pattern duplicated across four theorems into a shared lemma, so the same expensive check isn't paid four times</p>
</li>
</ul>
<p>The effect was dramatic. In a focused build (measuring just the one file), the heaviest module, <code>RawGeometry</code>, went from <strong>291 seconds to 11</strong>. The operations that used to top the chart at 15–104 seconds max out at 57.5 milliseconds after.</p>
<p>The final CI measurement (full build of the merge commit): total across the 7 modules <strong>1,750 seconds (29.2 min) ≤ target 1,800</strong>, longest module <strong>468 seconds ≤ target 600</strong>. Both targets met. Done.</p>
<p>And that merge commit's CI run is a group photo of all three rounds. From the log:</p>
<table>
<thead>
<tr>
<th>Step</th>
<th>Time</th>
</tr>
</thead>
<tbody><tr>
<td>Restore Formal build cache</td>
<td>3s (cache hit — Round 2)</td>
</tr>
<tr>
<td>Build (incl. full rebuild of all 7 modules)</td>
<td>9m 29s (parallel DAG + lighter modules — Round 3)</td>
</tr>
<tr>
<td>Kernel axiom audit</td>
<td>26s (shared traversal — Round 1)</td>
</tr>
<tr>
<td><strong>Whole job</strong></td>
<td><strong>11m 57s</strong></td>
</tr>
</tbody></table>
<p>Twelve minutes with the heaviest files fully rebuilt. That is the current worst case.</p>
<p><strong>Round 3's lesson</strong>: splitting is not a tool for making things faster; it is a tool for parallelism and smaller rebuild scope. What reduces the total is surgery at the one point the profile indicates. And since these are two different improvements, measure them separately and judge them separately.</p>
<h2>The improvements were driven by AI agents</h2>
<p>As mentioned at the start, nearly all measurement and implementation across the three rounds was agent work. Rounds 1 and 2 ran inside interactive sessions, from measurement through implementation; Round 3 was an autonomous loop that took a requirements document (PRD) and drove everything from profiling to PR authoring to review response. During implementation, the human had exactly two jobs:</p>
<ol>
<li><p><strong>Approve the numeric targets and the measurement method up front</strong> (for Round 3: "longest ≤ 600s, total ≤ 1,800s, judged by module times in one and the same GHA full build")</p>
</li>
<li><p><strong>Accept or reject based on measured CI values</strong></p>
</li>
</ol>
<p>To be honest, there is a third job upstream: writing the PRD. But that too is a collaboration with another AI; the human's actual work is choosing the direction and reviewing.</p>
<h3>The PRD Loop</h3>
<p>We call the autonomous loop from Round 3 the <strong>PRD Loop</strong>. It is handed to the agent (Codex) as a SKILL — an instruction document for agents. The skeleton:</p>
<ul>
<li><p>The input is a single PRD, with a guiding question and numeric Acceptance Criteria at the top. A human starts it with one command (<code>$prd-loop &lt;path-to-PRD&gt;</code>)</p>
</li>
<li><p>One iteration is "gap analysis → file an issue → implementation PR → adversarial review → merge → ledger sync." It runs in small units: <strong>one iteration = one issue = one PR</strong> (one goal spanning several iterations is normal operation)</p>
</li>
<li><p>A <strong>separate review gate</strong> (also an AI) judges each PR — never the implementer itself. After two rounds of "needs changes," the third escalates to a stricter, specialized gate (for Lean, a dedicated mathematical review gate); if that still fails, the item is marked <code>stalled</code> and returned to the human</p>
</li>
<li><p>Loop state lives in GitHub issues, not in the agent's memory. If the session dies, the next session resumes from the same spot</p>
</li>
<li><p>When every condition looks satisfied, an <strong>independent completion audit</strong> re-reads the PRD from scratch and verifies everything against reality. Until it passes, nothing gets to call itself "done"</p>
</li>
</ul>
<p>Round 3's "profile → split → miss → dig deeper → hit" ran on this wheel as three PRs (split / reduction / closeout). Start to finish took just under a day. Human intervention along the way: approving the targets and confirming the measurement standard — a handful of GitHub comments.</p>
<p>Incidentally, our convention is that a completed PRD is <strong>deleted</strong> from the repository. A requirements document starts drifting from reality the moment implementation ends, so the permanent record is pinned to issues, PRs, and CI logs — and the document itself goes.</p>
<h3>Closing off the cheap paths</h3>
<p>Run such a loop naively, though, and one thing reliably happens. Agents are faithful to the incentives you give them, so left alone they converge on <strong>the cheapest path to a merged PR</strong>. Change the documentation instead of the implementation. Satisfy an unsatisfiable condition by "interpreting" it. Quietly defer the hard items. None of these is exactly a lie — but compounded, they settle into an equilibrium where "done" can be claimed at minimum effort. Call it the corner-cutting equilibrium.</p>
<p>So our SKILL reads less like a procedure and more like <strong>mechanism design</strong>: a set of rules that seals off the cheap paths one by one.</p>
<ul>
<li><p><strong>No docs-only check-offs</strong>: a condition that demands implementation or verification can never be marked satisfied by a PR that only edits documents or ledgers</p>
</li>
<li><p><strong>No reinterpretation, no downgrading</strong>: a condition the agent cannot meet is escalated to the human as <code>blocked</code>, not read more weakly. The SKILL states it in so many words: "stopping is not failure; stopping is part of the loop's specification"</p>
</li>
<li><p><strong>The PRD is invariant during the loop</strong>: the agent cannot rewrite its own passing bar. If it finds a defect in the PRD, it stops and reports rather than fixing it (you don't get to edit the exam paper mid-exam)</p>
</li>
<li><p><strong>Checklists are not evidence</strong>: a checkmark means only "a past iteration claimed this." The final audit re-extracts the conditions from the PRD independently and verifies them against reality — code, tests, CI logs</p>
</li>
<li><p><strong>No self-grading</strong>: the completion auditor receives only the PRD path and issue numbers — never the loop's own "I believe everything is satisfied." If a review gate cannot run, the main agent does not stand in for it; the loop fails closed</p>
</li>
</ul>
<p>These rules were not designed at a whiteboard, by the way. Each one was added after actually observing early loops try the corresponding move — settling a condition with a ledger entry, quietly shrinking scope in the name of safety. We were never smart enough to write all the guardrails in advance.</p>
<p>There is one design principle underneath all of it: <strong>make stopping honestly cheaper than pushing through dishonestly.</strong> Don't count on the agent's good will; move the equilibrium itself.</p>
<h3>The best moment: the miss report</h3>
<p>The best moment of this whole operation was Round 3's <strong>miss report</strong>. At the split PR, the agent wrote, in its own PR description: "Longest 770 seconds, total 2,483. <strong>Targets missed; this PR does not claim completion.</strong> Profile-driven reduction continues in the next PR." No human issued any instruction in response. The loop filed the reduction issue itself on the next iteration and merged the PR that met the targets the same day.</p>
<p>Declaring "done" and walking away is the cheapest move available at exactly that point, and the agent instead wrote <em>missed</em> and kept going. I credit that less to the agent's honesty than to the structure around it. When the passing bar is fixed in advance, reinterpretation is banned, and an independent audit will check every claim against measured values, then whenever declaration and measurement disagree, it is the declaration that has to move. Set the bar afterwards instead, and the temptation to fit the bar to the result appears — on the human side too.</p>
<p>If you are planning to hand optimization work to an AI, the most practical thing to take from this article is probably this: <strong>hand over the passing bar and the measurement method before you hand over the code. And make honest stopping the cheapest move on the board.</strong></p>
<h2>Conclusion</h2>
<ul>
<li><p>Axiom audit, 7m 11s → 11s. The culprit was not the giant file but the unshared traversal walking the graph once per declaration</p>
</li>
<li><p>Every PR at 41 minutes → incremental builds in minutes. The culprit was not the cache key but a cache save running <em>before</em> the build</p>
</li>
<li><p>One 38-minute file → 29 minutes total, 8 minutes longest, 12-minute worst-case run. Splitting removed nothing; the culprit was definitional unfolding cost</p>
</li>
</ul>
<p>All three first hypotheses were wrong. Progress happened anyway, because each wrong hypothesis was rejected by measurement and the rejection itself was passed forward as a result. "No effect" is a finding. File splitting rejected (Round 1), two standard idioms rejected (Round 3) — without those records, someone, human or AI, would have dug the same holes again.</p>
<p>On the AI side: Round 3's loop ran start to finish in under a day, across three PRs, with human intervention limited to a handful of GitHub comments.</p>
<p>Don't guess, measure. It's an old maxim, but in the era of AI-written code I think it needs a sequel — <strong>let the AI measure, and hand it the passing bar first.</strong></p>
]]></content:encoded></item><item><title><![CDATA[The SAGA Comparison Theorem Preprint Is Out: Two Rulers, One Reading]]></title><description><![CDATA[TL;DR

We have published a paper on Zenodo (DOI: 10.5281/zenodo.21603761, CC BY 4.0) that formalizes the "locally correct, globally broken" phenomenon of software architecture as mathematics, proves i]]></description><link>https://blog.iroha1203.dev/two-rulers-one-reading</link><guid isPermaLink="true">https://blog.iroha1203.dev/two-rulers-one-reading</guid><category><![CDATA[Software Engineering]]></category><category><![CDATA[software architecture]]></category><category><![CDATA[Computer Science]]></category><category><![CDATA[Math]]></category><dc:creator><![CDATA[Hiroyuki Nakahata]]></dc:creator><pubDate>Mon, 27 Jul 2026 15:37:20 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/69ff29ebf239332df4a93c3a/8ec8fb56-89f4-4a5a-9f5b-3392ade25427.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2>TL;DR</h2>
<ul>
<li><p>We have published a paper on Zenodo (<a href="https://doi.org/10.5281/zenodo.21603761">DOI: 10.5281/zenodo.21603761</a>, CC BY 4.0) that formalizes the "locally correct, globally broken" phenomenon of software architecture as mathematics, proves its central theorem, and measures it on a real open-source system.</p>
</li>
<li><p>The central claim is a single sentence. <strong>Build two ways of measuring brokenness — one in the language of "how can this be repaired", one in the language of "which equations are violated" — without either referring to the other, and they turn out to carry the same scale.</strong> This is proved as a comparison theorem.</p>
</li>
<li><p>On top of that, under a gluing condition, <strong>"the obstruction is zero" and "a repair of the whole system exists" are equivalent.</strong> The sentence "without this fix, the whole would have broken" — until now folklore — becomes a consequence of a theorem.</p>
</li>
<li><p>The paper presents three layers — the mathematical proof, the Lean 4 verification status, and a reproducible measurement on a real microservice system — pinned to one and the same snapshot.</p>
</li>
</ul>
<h2>0. The story so far</h2>
<p>The goal of this series is to move software architecture from something discussed with diagrams, folklore, and review-time taste to a mathematical object studied with definitions, theorems, counterexamples, and proofs. For readers new to the series, here is the cast.</p>
<table>
<thead>
<tr>
<th>Name</th>
<th>What it is</th>
</tr>
</thead>
<tbody><tr>
<td><strong>AAT</strong> (Algebraic Architecture Theory)</td>
<td>An algebraic-geometric theory of software architecture. On top of typed minimal facts read from code (<strong>Atoms</strong>), it states the conventions that should hold as equations, and treats an architecture as the geometry of their solutions</td>
</tr>
<tr>
<td><strong>The SAGA comparison theorem</strong></td>
<td>The central theorem of this paper. Obstructions measured in the language of "repairs" and obstructions measured in the language of "equations" coincide as the same cohomology class. The name is an homage to the GAGA theorem in algebraic geometry — it has nothing to do with the distributed-transaction Saga pattern</td>
</tr>
<tr>
<td><strong>ArchSig</strong></td>
<td>A Rust measurement tool built on AAT. From observation data and equations, it computes gluing obstructions deterministically</td>
</tr>
<tr>
<td><strong>Lean</strong></td>
<td>A theorem prover. AAT's theorems are machine-checked here</td>
</tr>
</tbody></table>
<p><a href="https://blog.iroha1203.dev/the-saga-theorem">Two articles ago</a> I told the story of proving the theorem; <a href="https://blog.iroha1203.dev/saga-cape-of-good-hope">last time</a> I reported dogfooding it on a real OSS codebase. Today's protagonist is the <strong>first paper</strong>, which binds those results into one document. You do not need the earlier articles; everything required is introduced below.</p>
<h2>1. No file has a bug, yet the system is broken</h2>
<p>A real microservice system (<a href="https://github.com/FudanSELab/train-ticket">train-ticket</a>, 42 services) contains the following spot.</p>
<ul>
<li><p><strong>order</strong> stores the order price as a raw string (<code>private String price</code>)</p>
</li>
<li><p><strong>inside-payment</strong> parses that string into a <code>BigDecimal</code> and computes exactly</p>
</li>
<li><p><strong>cancel</strong> parses the same string into a <code>double</code>, multiplies by 0.8, and rounds to two decimal places</p>
</li>
</ul>
<p>Each of the three services follows its own convention <strong>perfectly</strong>. Each pairwise handoff succeeds on its own terms. And yet, follow the refund computation around the loop and the books do not balance.</p>
<pre><code class="language-text">original price:            12.33
cancel's convention:       0.8 × 12.33 = 9.864 → rounded → "9.86"
inside-payment's:          exact arithmetic → 9.864
remaining difference:      0.004 (less than one cent)
</code></pre>
<p>This sub-cent drift is booked in no service's ledger. And nowhere in the source is there any code that reconciles the three services' amounts at once. The paper calls this instance the <strong>one-cent obstruction</strong>. <strong>No file has a bug, yet the system is broken.</strong></p>
<p>The paper is a report of building the mathematics that measures this phenomenon, proving its central theorem, machine-checking it in Lean, and — on the example above — walking the full circle from measuring the obstruction, through a CI gate blocking, to pre-validating a repair plan, to the gate passing, all reproducibly.</p>
<h2>2. Two ways of measuring</h2>
<p>Here is the heart of the paper. We construct two ways of measuring "how broken it is" — <strong>from two entirely different principles</strong>.</p>
<p><strong>Ruler A — the language of repairs.</strong> Each service (from here on, a <em>chart</em>) comes with the repair moves permitted there. In the one-cent example these are operations like "unify the money reading to scale-2 <code>BigDecimal</code>" or "create a place where the rounding remainder is booked". Combinations of moves are called <em>repair words</em>, and we identify words that produce the same result. This gives each chart a space of meaningfully distinct repair options. The obstruction is measured as <strong>the amount by which neighboring charts' repair choices disagree around a loop without canceling out</strong>.</p>
<p><strong>Ruler B — the language of equations.</strong> This side never looks at repairs. The conventions that should hold are stated as a system of equations — equalities like "both charts determine the refund as the same currency value". The obstruction is measured as <strong>the residue of equation violations that survives around the loop</strong>.</p>
<p>What matters is that the two constructions are <strong>independent — neither refers to the other</strong>. No equation appears in the definition of Ruler A; no repair appears in the definition of Ruler B. Think of two thermometers, one built from the expansion of mercury and one from the color of radiation. That two instruments built on different principles agree on their scale is guaranteed by nothing — merely building them proves nothing.</p>
<p><strong>The SAGA comparison theorem</strong> (Theorem 5.1 of the paper) proves the following, over a chosen finite cover, whenever a finite list of conditions matching up the two rulers' local data (spelled out as a checklist in the paper) is satisfied.</p>
<ol>
<li><p>The scales the two rulers use (the coefficients) are naturally isomorphic.</p>
</li>
<li><p>That isomorphism identifies the quantities they measure (the cohomology classes in <code>H^1</code>). The obstruction class measured by Ruler A is carried exactly to the obstruction class measured by Ruler B.</p>
</li>
<li><p>If, moreover, the family of repair states satisfies the gluing condition (the sheaf condition), then <strong>"the obstruction class is zero" ⟺ "a repair that glues the whole system exists"</strong>.</p>
</li>
</ol>
<p>In one sentence: <strong>semantic diagnosis (how to fix it) and geometric computation (which equations broke) translate into each other, in both directions.</strong></p>
<h2>3. Why this matters</h2>
<p>"Two measurements agree — so what?" The value of the paper lives in the answer.</p>
<p><strong>First: a round-trip ticket between diagnosis and computation.</strong> The language of repairs is the language of humans (and LLMs). "Unify the money reading across these three services and it's fixed" is a sentence in repair language. The language of equations is the language of machines: it reduces to finite linear algebra and computes deterministically. The comparison theorem is the round-trip ticket between the two worlds. A diagnosis posed on the semantic side can be computed on the geometric side; a result computed on the geometric side can be read back in repair language. In practice, the measurement tool ArchSig computes on the equation side, while what people want to receive is the repair side. The theorem's claim is that this bridge is not "these presumably correspond" but a proved isomorphism.</p>
<p><strong>Second: "without this fix, the whole would have broken" becomes sayable.</strong> In the vocabulary of testing, a failure that never happened cannot be observed. "That fix prevented an outage" has always been a counterfactual with nowhere to stand. The third part of the theorem (zero class ⟺ existence of a global repair) gives that sentence a mathematical status. Hand the measurement a candidate repair, compute that the obstruction class vanishes, and you have <strong>pre-implementation validation</strong> that this particular fix makes the whole glue — as a consequence of a theorem, not as folklore.</p>
<p><strong>Third: the nature of the obstruction becomes visible.</strong> A nonzero obstruction class means: no matter how any single chart re-chooses its own baseline, the discrepancy does not vanish. Re-paper one service's convention and the mismatches on its adjacent edges merely shift; the residue around the loop remains. A nonzero obstruction <strong>belongs to no file</strong>. Fixing it requires not a convention swap but a structural change — such as creating a place where the remainder is booked. "This is the kind of bug that a culprit hunt cannot find" becomes a computable verdict.</p>
<h2>4. How the paper is built: three layers, one snapshot</h2>
<p>The paper presents this mathematics in three layers of different character. Proof, machine verification, and measurement each answer a different kind of "really?".</p>
<p><strong>Layer one: mathematics (Chapters 3–5).</strong> The full proof of the comparison theorem: the two independent constructions, the coefficient isomorphism, the <code>H^1</code> isomorphism, the correspondence of obstruction classes, and the global-repair equivalence.</p>
<p><strong>Layer two: Lean (Chapter 6).</strong> The central theorem's conclusion bundle, the lemmas of each stage, and concrete nonzero and zero examples (witnesses) are proved in Lean 4. Every row of the status table is <code>proved</code>, there is no <code>sorry</code>, and the only dependency axioms are Lean/mathlib's three standard ones — confirmed by a kernel-level axiom audit that runs in CI.</p>
<p><strong>Layer three: measurement (Chapter 7).</strong> On the one-cent obstruction of §1, the diagnostic staircase runs its full circle.</p>
<table>
<thead>
<tr>
<th>Act</th>
<th>Result</th>
</tr>
</thead>
<tbody><tr>
<td>Pre-repair measurement</td>
<td><code>MEASURED_NONGLUING_RESIDUAL</code> — every chart obeys its own convention (<code>measured_zero</code>), yet the loop residue cannot be canceled by any per-chart change of baseline</td>
</tr>
<tr>
<td>Gate verdict</td>
<td><code>BLOCKED_BY_GATE_POLICY</code> — in CI, this is where the PR stops</td>
</tr>
<tr>
<td>Measurement with a repair plan (unified <code>BigDecimal</code> scale-2)</td>
<td><code>REPAIR_GLUES_WITHIN_SELECTED_COMPLEX</code></td>
</tr>
<tr>
<td>Before/after comparison</td>
<td><code>MEASURED_OBSTRUCTION_NO_LONGER_RECORDED_AFTER_CHANGE</code></td>
</tr>
<tr>
<td>Post-repair gate verdict</td>
<td><code>PASS_WITHIN_GATE_POLICY</code></td>
</tr>
</tbody></table>
<p>And the three layers are pinned to <strong>one and the same snapshot</strong> by a tuple of identifiers — the Lean source tag, the ArchSig version, and the digests of the measurement inputs (the paper calls this the <em>release identity</em>). From any claim in the paper you can trace what kind of claim it is (proved / machine-checked / measured) and where its primary evidence lives. The correctness of the mathematics is carried by the proof, the correctness of the proof by Lean, the reproducibility of the measurement by pinned inputs and procedures. The design policy is to <strong>make the part you must take on faith as small as possible</strong>.</p>
<h2>5. What the paper does not claim</h2>
<p>Following the custom of this series, here is what the paper explicitly does not claim. These boundaries are fixed by the paper itself, in §5.7 and §7.5.</p>
<ul>
<li><p><strong>The theorem is a theorem over a chosen finite cover.</strong> No identification with cover-independent cohomology is claimed.</p>
</li>
<li><p><strong>The theorem is not unconditional.</strong> The correspondence between the two rulers requires a finite list of conditions, and the paper's appendix pins down which proof consumes which condition. Finite counterexamples showing that removing any one condition breaks the isomorphism are also proved in Lean.</p>
</li>
<li><p><strong>The measurement does not claim to have discovered a new fault.</strong> Detecting the mismatch itself is the job of the lower rungs of the staircase. What the SAGA stage adds is chaining "each part is locally lawful", "the discrepancy survives the loop", and "this repair plan would glue" into one sequence of machine verdicts.</p>
</li>
<li><p><strong>The drift's frequency and monetary scale are not measured.</strong> Runtime measurement is future work, and not speaking numbers we did not measure is this project's discipline.</p>
</li>
</ul>
<h2>6. Where this goes next: SAGA is the first theorem</h2>
<p>The SAGA comparison theorem is the <strong>first theorem</strong> of AAT's local-to-global capability to reach a full proof. If the theory is a tower, the first floor is now built. Chapter 9 of the paper describes the floors planned above it — explicitly as outlook, kept separate from the proved results (the two below are on the outlook side).</p>
<p><strong>Architecture schemes.</strong> This paper measured whether one architecture glues. The next direction treats an architecture itself as a geometric object — the vanishing locus of its system of convention equations, a <em>scheme</em> — and develops morphisms, base change, and fibers <strong>between</strong> architectures as theorems. If this grows well, everyday judgments like "does this refactoring preserve the structure?" or "is the architecture the same shape before and after this PR — and if not, where did it change?" may acquire a geometric meaning: a verifiable map between architectures.</p>
<p><strong>SFT (Software Field Theory).</strong> AAT is the statics of architecture: the consistency of one moment in time. SFT is the research program that builds the dynamics of change, branching, and merging on top of that geometry. A merge in version control is a gluing of local changes, and SFT reads it as descent theory. The target proposition has the shape "a software architecture is modular exactly when its future evolution satisfies descent" — and the static comparison SAGA proved is the ground this dynamics stands on at every instant.</p>
<h2>7. How to read it, and where to get it</h2>
<p>A suggested route for a first pass:</p>
<ol>
<li><p><strong>Abstract and Chapters 1–2</strong>: the problem and the AAT approach.</p>
</li>
<li><p><strong>Chapter 7</strong>: the one-cent measurement.</p>
</li>
<li><p><strong>The opening of §5.1</strong>: a plain-prose summary of the central theorem. You can follow the claim without the formulas.</p>
</li>
<li><p><strong>Chapter 9</strong>: the research outlook — including the picture of what a theorem-grounded, deterministic measurement is for in an era when code is written by AI.</p>
</li>
</ol>
<ul>
<li><p><strong>Paper</strong>: <a href="https://doi.org/10.5281/zenodo.21603761">SAGA: A Comparison Theorem for Local-to-Global Software Architecture</a> (Zenodo, CC BY 4.0, PDF + reproduction bundle)</p>
</li>
<li><p><strong>Source</strong>: <a href="https://github.com/iroha1203/AlgebraicArchitectureTheoryV2">AlgebraicArchitectureTheoryV2</a> (MIT license — the Lean proofs, ArchSig, and all measurement inputs live here)</p>
</li>
<li><p><strong>Target codebase</strong>: <a href="https://github.com/FudanSELab/train-ticket">FudanSELab/train-ticket</a></p>
</li>
</ul>
<h2>8. Closing</h2>
<p>"Locally correct, globally broken" has long gone by the name <em>integration hell</em>, handled by folklore. What this paper shows is that the phenomenon has a precise mathematical name; that under that name the obstruction is computable; and that the two ways of measuring it — the language of repairs and the language of equations — are joined by a proved isomorphism.</p>
<p>A voyage that began from an abstract comparison theorem passed through machine verification and arrived at one cent in real code. That one cent is the smallest, most concrete witness to the fact that the sum of local correctness does not reach global correctness.</p>
<p>Questions, objections, and "I think my codebase has one of these triangles" stories are all welcome.</p>
]]></content:encoded></item><item><title><![CDATA[SAGA's Cape of Good Hope: The One Cent That Cohomology Caught]]></title><description><![CDATA[TL;DR

This is the sequel to the SAGA theorem article. Last time, I gave the "locally correct, globally broken" phenomenon a theorem, proved in Lean 4. This time I took that theorem out into the real ]]></description><link>https://blog.iroha1203.dev/saga-cape-of-good-hope</link><guid isPermaLink="true">https://blog.iroha1203.dev/saga-cape-of-good-hope</guid><category><![CDATA[software architecture]]></category><category><![CDATA[geometry]]></category><category><![CDATA[lean]]></category><category><![CDATA[Software Engineering]]></category><category><![CDATA[Microservices]]></category><dc:creator><![CDATA[Hiroyuki Nakahata]]></dc:creator><pubDate>Sat, 18 Jul 2026 19:23:22 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/69ff29ebf239332df4a93c3a/e73b2faa-0fa9-442c-8bee-0529a661dd8b.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2>TL;DR</h2>
<ul>
<li><p>This is the sequel to <a href="https://blog.iroha1203.dev/the-saga-theorem">the SAGA theorem article</a>. Last time, I gave the "locally correct, globally broken" phenomenon a theorem, proved in Lean 4. This time I took that theorem out into the real world.</p>
</li>
<li><p>In a real microservice system that nobody wrote with the theory in mind — the research benchmark <a href="https://github.com/FudanSELab/train-ticket">train-ticket</a> — the exact structure required by the theorem's hypotheses turned out to exist.</p>
</li>
<li><p>Every service follows its own money convention perfectly. Yet a sub-cent refund remainder vanishes without being booked anywhere in the system. The measurement returns both verdicts at once, and a repair plan flips the CI gate from BLOCKED to PASS.</p>
</li>
<li><p>The find itself is almost comically mundane. But the value was never the find. It is the confirmation that the sea route exists.</p>
</li>
</ul>
<h2>0. The story so far</h2>
<p>For readers new to the series: the goal of this project is to move software architecture from something discussed with diagrams, folklore, and review-time taste to a mathematical object studied with definitions, theorems, counterexamples, and certificates.</p>
<table>
<thead>
<tr>
<th>Name</th>
<th>What it is</th>
</tr>
</thead>
<tbody><tr>
<td><strong>AAT</strong> (Algebraic Architecture Theory)</td>
<td>An algebraic-geometric theory of software architecture. On top of typed minimal facts read from code (<strong>Atoms</strong>), it imposes the conventions that should hold as equations (<strong>laws</strong>), and treats an architecture as the geometry of the solutions</td>
</tr>
<tr>
<td><strong>The SAGA theorem</strong></td>
<td>AAT's central result. A comparison theorem establishing that the question "do the local repairs glue into one global repair?" is computable as the vanishing of a quantity called cohomology <code>H^1</code>. Machine-verified in Lean 4. The name is an homage to the GAGA theorem in algebraic geometry — it has nothing to do with the distributed-transaction Saga pattern</td>
</tr>
<tr>
<td><strong>ArchSig</strong></td>
<td>A Rust measurement tool built on AAT. Given observation data (an ArchMap) and laws, it decides gluing obstructions by finite computation and returns certificate-backed conclusions. An instrument, not a grader</td>
</tr>
<tr>
<td><strong>Lean</strong></td>
<td>A theorem prover. AAT's theorems are machine-checked here</td>
</tr>
</tbody></table>
<p>The previous article ended with the proof of the SAGA theorem. The theorem is true. One question remained: does the structure its hypotheses describe exist in real code? This article is that report. If you have been following the series, feel free to skip ahead.</p>
<h2>1. The Lean proof is done. But does it apply to real software?</h2>
<p>The SAGA theorem is proved in Lean 4. No <code>sorry</code> (Lean's placeholder for an unfinished proof), and the only axioms are Lean's standard ones. Whether the theorem is <em>correct</em> is no longer a question.</p>
<p>The question that remained is this one: <strong>does the structure its hypotheses require exist in real software?</strong></p>
<p>However correctly proved, a theorem whose hypotheses are satisfied only inside mathematics quietly swings at nothing in the real world. Here is the bundle of hypotheses, in plain words. From now on I will call each service a "chart" and the convention a service imposes on itself its "law". Every chart obeys its own law. Values are handed between charts. And yet, when you follow the handoffs around a closed loop, a discrepancy remains that has nowhere to be reconciled. The quantity that counts this "discrepancy that survives a full loop" is the cohomology <code>H^1</code> that starred in the previous article.</p>
<p>This bundle could, in the worst case, have been a structure that only assembles in artificial examples. The one-cent demo I published — a small Rust sample deliberately engineered so that a one-cent rounding discrepancy goes missing between modules — demonstrates that <em>this structure produces the obstruction</em>. It does not demonstrate that <em>this structure occurs in reality</em>. Only real data can show that. So I measured an external OSS codebase end to end, using exactly the workflow a real user would. Dogfooding.</p>
<p>What the discovery of the Cape of Good Hope meant was not "we reached India". It meant "the sea route exists". That is precisely what this experiment set out to check. Is AAT a sea that exists only inside mathematics, or does it connect to the sea of real code? Hence the name of this report: SAGA's Cape of Good Hope.</p>
<h2>2. The experiment: measuring an external OSS the regular way</h2>
<p>The target is <a href="https://github.com/FudanSELab/train-ticket">FudanSELab/train-ticket</a>, a standard benchmark for microservice research, built from surveys of industrial systems. It consists of 40-plus Java microservices with ordering, payment, refund, and consignment flows. The repository is roughly 500,000 lines including frontend assets; the measured target is the Java implementation proper, about 28.5 kLOC. What mattered was that this codebase knows nothing about my theory.</p>
<p>The measurement had two stages.</p>
<p><strong>Stage 1 (a full build of the observation data).</strong> From the Java sources of 42 services plus a shared module, I built an ArchMap — the map of minimal observed facts (Atoms) read from the code — through the regular workflow: two independent reading passes, full adjudication of every disagreement, then an audit. The result: 2,118 Atoms across 43 observation units (contexts). All reading was done by lightweight-model subagents (Claude Sonnet), at a measured supply cost of about 4.1 minutes/kLOC (wall clock, parallel runs included).</p>
<p>Already at this stage, the map showed 8 distinct styles of carrying money coexisting across the 24 services observed for that convention: double fields, strings passed through untouched, BigDecimal parsed at the point of use, string concatenation. Each works fine on its own.</p>
<p><strong>Stage 2 (SAGA).</strong> On top of the full build, I extended the laws to the full SAGA diagnostic stack and walked the staircase end to end. This is the main act.</p>
<h2>3. The discovery: the vanishing-cent structure exists in real code</h2>
<p>Digging through the real call graph of the full build, this structure emerged.</p>
<p><strong>Three services — cancel (refunds), inside-payment, and order — form a triangle of actual calls, and the money convention disagrees on all three edges.</strong></p>
<p>The three conventions can be confirmed in the sources (commit <code>313886e9</code>).</p>
<p>First, order. The price is stored as a raw string.</p>
<pre><code class="language-java">// ts-order-service: Order.java
private String price;
</code></pre>
<p>Next, inside-payment. Balance arithmetic parses that string into BigDecimal and computes exactly.</p>
<pre><code class="language-java">// ts-inside-payment-service: InsidePaymentServiceImpl.java
totalExpand = totalExpand.add(new BigDecimal(order.getPrice()));
</code></pre>
<p>And cancel. The refund computation parses the same string into a double, multiplies by 0.8, rounds to two decimals, and stringifies again.</p>
<pre><code class="language-java">// ts-cancel-service: CancelServiceImpl.calculateRefund
double totalPrice = Double.parseDouble(order.getPrice());
double price = totalPrice * 0.8;
DecimalFormat priceFormat = new java.text.DecimalFormat("0.00");
String str = priceFormat.format(price);
</code></pre>
<p>So one and the same "order price" travels through three different worlds: untouched string / exact decimal arithmetic / floating point plus rounding. Whenever 0.8 × price does not land on two decimal places (say 91.33 × 0.8 = 73.064), the sub-cent remainder created by rounding appears in no service's books.</p>
<p>And here is the decisive part. <strong>Nowhere in the measured Java sources is there code that reconciles the three services' money at once.</strong> The pairwise handoffs exist: cancel reads the price from order, cancel sends the refund to inside-payment, inside-payment reads the price from order. But there is no place where all three are checked together.</p>
<p>In the language of the previous article: the three mismatched edges form a closed loop, and the face that would fill it — the membrane sealing the inside of the triangle, i.e. a site where the three are reconciled simultaneously — is missing. This is the SAGA theorem's bundle of hypotheses, verbatim. I did not plant it. Ordinary developers, each making reasonable implementation choices, produced this shape.</p>
<h2>4. The measurement: the diagnostic staircase fires end to end</h2>
<p>The diagnostic staircase is a series of diagnoses stacked from simple to strong. At the bottom, the raw mismatch measurement (Čech); above it, grounding (does each chart obey its own law?), descent (does the discrepancy survive a full loop?), comparison (is the before/after comparison mathematically legitimate?), and finally the CI gate. I assembled the laws over this structure and ran the staircase. Here are the results (monospace tokens are the instrument's verbatim output).</p>
<table>
<thead>
<tr>
<th>Act</th>
<th>Result</th>
</tr>
</thead>
<tbody><tr>
<td>Measuring head (pre-repair)</td>
<td><code>MEASURED_NONGLUING_RESIDUAL_CLASS</code></td>
</tr>
<tr>
<td>├ grounding (does each chart obey its law?)</td>
<td><code>measured_zero</code> — it does</td>
</tr>
<tr>
<td>├ descent (does the discrepancy survive the loop?)</td>
<td><code>measured_nonzero</code> — it does</td>
</tr>
<tr>
<td>└ comparison (legitimacy of before/after comparison)</td>
<td><code>established</code></td>
</tr>
<tr>
<td>Gate decision</td>
<td><code>BLOCKED_BY_GATE_POLICY</code></td>
</tr>
<tr>
<td>Measuring the repaired state</td>
<td><code>REPAIR_GLUES_WITHIN_SELECTED_COMPLEX</code></td>
</tr>
<tr>
<td>Comparing head → repaired</td>
<td><code>MEASURED_OBSTRUCTION_NO_LONGER_RECORDED_AFTER_CHANGE</code></td>
</tr>
<tr>
<td>Gate decision after repair</td>
<td><code>PASS_WITHIN_GATE_POLICY</code></td>
</tr>
</tbody></table>
<p>Reading it in order:</p>
<p><strong>grounding = measured_zero.</strong> This is the trap part of SAGA. cancel, inside-payment, and order each follow their own money convention perfectly. A per-service review — human or AI — has no grounds to reject the mismatch as <em>that service's</em> violation. Locally, everything is lawful.</p>
<p><strong>descent = measured_nonzero.</strong> And yet the discrepancy around the triangle is not zero. Moreover it is the kind of discrepancy that no service can eliminate by re-choosing its own convention: re-labeling one chart only shifts the mismatch along its adjacent edges, and one loop's worth of discrepancy survives. That is what the computation returned. Eliminating it requires a structural change — creating a place where the remainder is booked — not a re-labeling. This is "no file contains a bug, yet the system is broken", as a measurement.</p>
<p><strong>Gate = BLOCKED.</strong> Placed in CI, this PR stops. Even if every module's tests are green, the state "the system can drift by a cent" gets stopped from inside the sea of green — the live version of what the previous article called "a class of analysis beyond linting".</p>
<p><strong>Repair → gluing restored → PASS.</strong> Supply a hypothetical repair — "unify the three services' shared reading on scale-2 BigDecimal and book the rounding remainder explicitly" — and, within the selected complex, the discrepancy becomes zero, the comparison reports the obstruction is no longer recorded, and the gate flips to PASS. The repair can be verified to glue <em>before anyone implements it</em>.</p>
<p>The story designed into the one-cent demo (nonzero obstruction → BLOCKED → repair → PASS) replayed in the same order, on a real codebase instead of a synthetic one. That the minimal failure the theory predicts showed up at its minimal size — under one cent — reads to me as evidence that the theory's resolution matches reality.</p>
<h2>5. What was established</h2>
<p>First, the hypotheses are inhabited. The SAGA theorem moved from "proved, with synthetic examples only" to "proved, with an instance reproduced in real code". The risk of vacuity died with this one example.</p>
<p>Second — and this is the real prize — the faithfulness contract survived. Between the theory and the instrument stands a contract: the instrument speaks only where the theorem can speak. This was the instrument's first contact with adversarial real data, and the chain of implications (locally lawful → loop discrepancy nonzero → repair glues) fired in the same order, under the same conditions, as the theorem chain on the Lean side. The contract never broke. Every refusal was legitimate too: declare, falsely, that the drifting triangle has a reconciling face, and the bookkeeping arithmetic rejects the contradiction.</p>
<h2>6. Couldn't static analysis or LLM review find this?</h2>
<p>A fair question. "Show three files to an LLM that can read whole codebases — surely it would flag this?"</p>
<p>Honestly: finding this particular instance is probably within an LLM's reach. In fact, in this very pipeline, the money conventions were read by LLM subagents (Claude Sonnet). The difference lies in the form of the output after the finding.</p>
<ul>
<li><p><strong>Non-locality as a verdict.</strong> An LLM can say "these three are inconsistent". The measurement returns "this is the kind of discrepancy no service can remove by re-choosing its convention" — as a computation. Narration carries no such guarantee.</p>
</li>
<li><p><strong>Separating benign from structural.</strong> In the full build, mismatches were measured on 5 of the 6 observed handoffs — and the verdict was zero: benign drift that glues after local re-labeling. Nonzero stood only on the triangle. A flood of findings versus one structural point: in the age of review fatigue, that difference is the practical value.</p>
</li>
<li><p><strong>Zero as a statement, and reproducibility.</strong> An LLM cannot assert "no problems found" with a warranty, and its findings shift run to run (our own two independent passes agreed mechanically on only 9.4% of raw observations — see §8). The measurement returns the same verdict from the same input, with provenance traceable to the sources.</p>
</li>
<li><p><strong>Pre-verification of repairs.</strong> Both can propose fixes. Only the measurement can decide, before implementation, "this repair glues".</p>
</li>
</ul>
<p>The cost structure differs too. Whole-codebase review loads the entire codebase into context at every review: you pay O(codebase) every time, and both token cost and misses grow with size.</p>
<p>Building the ArchMap is paid once, and a lightweight model sufficed. Afterwards only diffs need updating (the reproducibility engineering that underwrites this is in progress — §8), and the obstruction computation itself runs on the Rust side at effectively no cost. Pay O(codebase) once, then O(diff). The bigger and longer-lived the codebase, the wider the gap.</p>
<p>So the true relationship is division of labor, not rivalry. The LLM does local observation (reading per-file facts — the shape LLMs are good at); the mathematics does globalization (gluing observations and computing obstructions on loops — the shape that collapses if you lean on long-context attention). ArchSig is best described as an apparatus that gives LLM observations a mathematical spine.</p>
<p>The grounding = measured_zero result is itself the demonstration: within what per-service review can see, everything was lawful. The obstruction lived outside the unit of review, on the overlaps.</p>
<p>One more fair question: "couldn't type systems or static analysis catch it?"</p>
<p>This is where the difference in kind shows most clearly. In this triangle, the price order stores and the refund cancel returns are both typed <code>String</code>. <strong>To a type system or a static analyzer, they look identical.</strong> What disagreed was not the types but the semantic conventions: "this string is an unparsed money amount" versus "this string is an already-rounded refund". The obstruction lived where neither syntax nor types can see.</p>
<p>This is why ArchSig's input is not an AST or type information but Atoms — typed minimal observed facts abstracted away from code and language. Atoms treat structural facts (dependencies, calls) and semantic facts (conventions, responsibilities, state, authority) as the same basic unit. Observing a money convention is the job of a semantic atom: an analysis outside the vocabulary of static analysis.</p>
<p>Putting Atoms in the middle has one more consequence: independence from language and framework. The one-cent demo is Rust; train-ticket is Java; the instrument, the law vocabulary, and the diagnostic staircase are exactly the same. For polyglot systems — which microservices usually are — one map suffices.</p>
<h2>7. What this report does not claim</h2>
<p>Per series custom, the section for not saying what cannot be said.</p>
<ul>
<li><p><strong>The instrument did not find this on its own.</strong> The raw mismatch was captured by the bottom stair, and "which handoffs the discrepancy appears on" is observation data I supplied after reading the sources. The staircase's contribution is not discovery but the diagnostic procedure: certifying that each service is lawful, certifying that the discrepancy survives the loop, verifying the repair in advance, stopping CI — as one machine-decided chain.</p>
</li>
<li><p><strong>The repair is not implemented.</strong> PASS states a pre-verification result — "this fix would restore consistency" — not that fixed code exists.</p>
</li>
<li><p><strong>No damage figure was measured.</strong> That the rounding remainder is booked nowhere is certain from the code; how much and how often it actually drifts requires running the system. Not quoting numbers we did not measure is also part of the discipline.</p>
</li>
<li><p><strong>No criticism of train-ticket is intended.</strong> It is a research benchmark, not a production system. Read this as an observation that an everyday mix of coding styles realizes the theorem's hypotheses — which also suggests the structure is not rare in real code.</p>
</li>
</ul>
<h2>8. The sea beyond the cape: a chart of the headroom</h2>
<p>Incidentally, the Cape of Good Hope was first named the Cape of Storms by its discoverer, Dias, and later renamed for the hope of the route. Discovery sites being unglamorous is apparently a tradition. And from the cape to the opening of the India route took ten more years; the hardest part lay beyond the cape, where crossing the Indian Ocean demanded techniques coastal navigation could not provide.</p>
<p>This research stands at the same point. The route exists; what lies ahead is not theory but supply engineering. The headroom comes in three parts.</p>
<p><strong>First, reproducibility of observation.</strong> This is ArchMap's lifeline. When two independent LLM passes read the same code, only 9.4% of raw observations matched mechanically. The breakdown matters: of 2,819 adjudicated disagreements (full source re-reads by subagents), only 29 were rejected as wrong. Correctness reproduced; what diverged was mostly naming (about nine tenths), then granularity, plus observations only one pass caught. With convergence conventions for naming, granularity, and notation plus a normalized matching key, re-extraction experiments raised key convergence from 0.32 to 0.74. The target is 0.8. Until then, the heavy safety net of dual passes plus full adjudication guarantees quality; once reached, the diff-update economics of §6 kick in properly. I will keep reporting these numbers as they move.</p>
<p><strong>Second, abstraction of supply.</strong> To satisfy SAGA's supply contract this time, I read the evaluator's source code. That is not a procedure real users can be asked to follow. The goal is to give SAGA supply the same abstraction ArchMap supply already has — point at the loop you want repaired, and the required observations assemble.</p>
<p><strong>Third, the next measurements.</strong> Runtime measurement to speak about damage (money-weighted analysis), and a second and third external OSS.</p>
<p>The flag is planted on the cape. "Locally correct, globally broken" is observed — as a measurement — in a real OSS codebase. The mathematics grown from axioms, as the previous article ended, connects to the sea of real code.</p>
<h2>References</h2>
<ul>
<li><p>Repository: <a href="https://github.com/iroha1203/AlgebraicArchitectureTheoryV2">AlgebraicArchitectureTheoryV2</a> (MIT license)</p>
</li>
<li><p>The one-cent demo (synthetic, reproducible): <code>tools/archsig/examples/practical-rust-service/</code></p>
</li>
<li><p>Target codebase: <a href="https://github.com/FudanSELab/train-ticket">FudanSELab/train-ticket</a> (commit <code>313886e9</code>)</p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[The Rising Sea of Software Engineering — Rebuilding the Discipline on Algebraic Geometry]]></title><description><![CDATA[AI now writes code faster than humans can review it. That is not a prediction; it is the daily experience of most engineering teams.
And here is the other daily experience: no matter how much you inve]]></description><link>https://blog.iroha1203.dev/the-rising-sea-of-software-engineering</link><guid isPermaLink="true">https://blog.iroha1203.dev/the-rising-sea-of-software-engineering</guid><category><![CDATA[Software Engineering]]></category><category><![CDATA[software architecture]]></category><category><![CDATA[lean]]></category><category><![CDATA[geometry]]></category><category><![CDATA[AI]]></category><dc:creator><![CDATA[Hiroyuki Nakahata]]></dc:creator><pubDate>Tue, 07 Jul 2026 17:17:08 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/69ff29ebf239332df4a93c3a/5fc6a17f-e595-4b0d-a2e8-fcdc46ee3e2a.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>AI now writes code faster than humans can review it. That is not a prediction; it is the daily experience of most engineering teams.</p>
<p>And here is the other daily experience: no matter how much you invest in tests and CI/CD, end-to-end failures still happen. Every unit test passes. Integration tests pass. CI is green. Production breaks anyway.</p>
<p>I have come to believe this is not a problem of test coverage or pipeline speed. It is a problem of <strong>software engineering itself not having caught up</strong> — and this article is the record of a climb that begins with that diagnosis: one summit reached (the SAGA theorem), the view seen from it, and the mountains still ahead.</p>
<p>For first-time readers, here is a map of the names that appear in this article:</p>
<table>
<thead>
<tr>
<th>Name</th>
<th>What it is</th>
</tr>
</thead>
<tbody><tr>
<td><strong>AAT</strong> (Algebraic Architecture Theory)</td>
<td>An algebraic-geometric theory of software architecture — pure mathematics, axiomatized on atoms</td>
</tr>
<tr>
<td><strong>Atom</strong></td>
<td>AAT's axiomatic primitive: an architectural constituent abstracted away from any code, language, or library</td>
</tr>
<tr>
<td><strong>SAGA theorem</strong></td>
<td>AAT's central theorem (the SAGA Grounding Theorem): a comparison theorem identifying the H^1 grown on the architecture-semantics side with the genuine Čech H^1 on the site generated by atoms. Proved in Lean 4. The name is an homage to Serre's GAGA</td>
</tr>
<tr>
<td><strong>ArchSig</strong></td>
<td>A Rust measurement tool grounded in AAT; detects gluing obstructions from observations of an architecture</td>
</tr>
<tr>
<td><strong>SFT</strong> (Software Field Theory)</td>
<td>The dynamical side: a theory of how software evolves over time. If AAT is statics, SFT is dynamics</td>
</tr>
<tr>
<td><strong>Lean</strong></td>
<td>A proof assistant; the theorems of AAT / SFT are formally verified in it</td>
</tr>
</tbody></table>
<p>I wrote about the SAGA Theorem in this article.</p>
<p><a href="https://blog.iroha1203.dev/the-saga-theorem">The SAGA Theorem: The Day Software Architecture Became Genuine Algebraic Geometry</a></p>
<p>One promise up front: this article contains three kinds of statements — things that are <em>proved</em>, things that are <em>vision</em>, and things that are <em>unproven</em>. I will keep them separate. Not claiming unproven things as results is the core discipline of this project, and this article follows it.</p>
<h2>The foothills — E2E failures have a mathematical name</h2>
<p>Start with the diagnosis.</p>
<p>What tests and CI guarantee is <strong>local correctness</strong>. A unit test verifies one cross-section of one module. Even an integration test only verifies a finite, chosen set of paths.</p>
<p>But E2E failures have a very specific shape: <strong>every part is correct, and the whole breaks anyway.</strong> Fragments that are locally consistent fail to fit together globally.</p>
<p>Mathematics — sheaf theory in particular — has a precise name for this situation: <strong>all local sections exist, but no global section does.</strong> And it has a tool that counts the obstructions to gluing: cohomology, starting with H^1.</p>
<p>The phenomenon we call "all tests green, production on fire" — the thing the industry has named <em>integration hell</em> and fought with folklore — turns out to have an exact mathematical name. The crucial point is that the obstruction does not live in any single part. It lives <strong>on the overlaps between parts</strong>: implicit interface assumptions, shared state, temporal ordering. None of these exist inside a single module. That is why 100% test coverage does not remove them. As long as what you measure is local, you cannot reach them — not in practice, but <em>in principle</em>.</p>
<p>Classical software engineering is built on an unstated composition principle: improve the quality of the parts, and the quality of the whole improves. The gluing problem says precisely that this composition is not automatic. <strong>The sum of the local is not the global.</strong></p>
<h2>Why AI-driven development makes this worse</h2>
<p>It is no accident that AI-driven development is aggravating the problem.</p>
<p>AI is extraordinarily good at local generation, and each generated fragment is individually correct. The faster generation gets, the faster inconsistencies accumulate on the overlaps.</p>
<p>Here is the thing worth noticing: what human review was actually doing was never primarily "checking that the code is correct." It was <strong>mentally re-deriving the gluing</strong> — "does this change still fit the assumptions of that other module?" That invisible computation is what can no longer keep up.</p>
<p>There are, in principle, exactly two ways to close the gap:</p>
<ol>
<li><p>Make the reviewer an AI too.</p>
</li>
<li><p>Make review a matter of <strong>decidable mathematics</strong>.</p>
</li>
</ol>
<p>The first inherits an infinite regress — who watches the watchers? In a world where generated artifacts inspect generated artifacts, no agent's judgment carries more authority than any other agent's. I chose the second.</p>
<h2>The Rising Sea — the method</h2>
<p>Grothendieck approached hard problems in algebraic geometry not by cracking the shell with a hammer and chisel, but by <strong>raising the water level of abstraction until, one day, the shell was simply open</strong>. In his own words: <em>la mer qui monte</em> — the rising sea.</p>
<p>The history of software engineering has been a history of hammers. E2E breaks? Write more tests. Integration breaks? Make CI faster. Boundaries break? Add contract tests. Each tool is individually correct, and the problem persists — not because the tools are bad, but because <strong>the problem is stated at too low a level of abstraction</strong>. We have been attacking a local-versus-global problem with local tools.</p>
<p>My research program, AAT, is that raising of the water. Stop handling the underwater rocks — code, modules — directly, and rise to the level of atoms (the theory's axiomatic primitives), sheaves, covers, and gluing. At that altitude, "tests pass but production breaks" stops being an enemy to fight and becomes <strong>an ordinary object with coordinates: a class in H^1</strong>.</p>
<p>This raising of the water has a practical by-product, too. Because atoms are abstracted away from code, <strong>neither the theory nor the measurement depends on any particular language, framework, or library</strong>. A Rust microservice fleet and a legacy Java monolith are, given their observations, subject to the same theorems and the same obstructions. There is no rebuilding the tool for every ecosystem, the way linters and architecture tests must be rebuilt — and the theory does not age as technology stacks turn over. Abstraction, in this sense, is also portability.</p>
<p>The problem does not disappear. But it stops being a mystery. What is no longer a mystery can be measured. What can be measured becomes a job for tooling.</p>
<p>Lifted to the right level of generality, a hard problem is not so much <em>solved</em> as <em>rendered trivial</em>. That is the point of the rising sea, and it is the method of this project.</p>
<p>One difference from Grothendieck's own sea: his settled into thousands of pages of prose (EGA/SGA), and later generations struggled to keep the water level up. My sea has its level guaranteed by <strong>Lean</strong>, a proof assistant. As long as the theorems are formally verified, the water does not recede. The central theorem of this sea is named the <strong>SAGA theorem</strong> — an homage to Serre's GAGA, the landmark work that established the correspondence between algebraic and analytic geometry. As GAGA bridged two geometries, SAGA is a comparison theorem bridging architecture semantics and algebraic geometry — plus the one layer neither had: machine verification.</p>
<p>And one thing that must not be forgotten: the rising sea was never abstraction as escape. Grothendieck always kept a concrete problem — the Weil conjectures — at the center, as the thing to be submerged. This project has the same shape. <strong>The concrete pain of E2E breakage is the shell at the center, the thing the water is meant to close over.</strong> The abstraction exists not to leave reality behind, but to trivialize it.</p>
<p>So much for the method. The method is a sea — raise the water, submerge the problem. But the journey of the research itself resembles a climb more than a tide: which peaks have been reached, what could be seen from the summit, which peak comes next. What follows is the record of that ascent.</p>
<h2>The ascent — a summit called SAGA (proven)</h2>
<p>This section respects claim boundaries strictly. The following already stand.</p>
<ul>
<li><p><strong>The AAT canon</strong> — the mathematical text of AAT as pure mathematics, axiomatized on atoms, written with no apologies to tooling or formalization: the mathematics speaks only its own language.</p>
</li>
<li><p><strong>The SAGA theorem</strong> — a comparison theorem: the H^1 grown on the architecture-semantics side coincides with the genuine Čech H^1 on the site generated by atoms. Proved in Lean 4. The ascent took 352 cycles of an automated AI research loop — 347 of them spent stacking impossibility theorems, walls that said <em>this vocabulary cannot reach the summit</em>. In the last 5 cycles, a shift of vocabulary — <em>an architectural law is not a predicate but an equation</em> — broke the bedrock.</p>
</li>
<li><p><strong>The Lean tower</strong> — under the discipline of "formalize only what can be honestly stated," definitions, theorems, and examples are stacked without <code>sorry</code>.</p>
</li>
<li><p><strong>ArchSig</strong> — a measurement tool written in Rust. It takes an observation of the architecture (ArchMap), the laws to uphold (LawPolicy), and an evidence contract, and its H^1 obstruction detection is operational. One design principle matters above all: <strong>it speaks only what the given contract lets it speak, and stays silent about everything else.</strong> It never completes, guesses, or extends its inputs.</p>
</li>
<li><p><strong>SFT (Software Field Theory)</strong> — the dynamical side. If AAT is statics (the consistency of an architecture at a moment), SFT describes how change propagates through software over time — and it, too, has begun producing theorems.</p>
</li>
<li><p><strong>The development process itself is the experiment</strong> — this repository is developed by AI agents implementing at speed, adversarial reviews and a Lean gate judging consistency, and a human holding only the questions and the acceptance criteria. In other words: <strong>AAT and ArchSig are themselves being developed in exactly the style of development they are meant to protect.</strong> That self-applicability is, I think, the first evidence that the theory survives contact with real development.</p>
</li>
</ul>
<h2>The view from the summit, 1 — a quiet future</h2>
<p>From here on, vision. Two views seen from the summit of SAGA.</p>
<p>The first:</p>
<blockquote>
<p>An engineer starts the day. She finishes a change and says, "analyze it with ArchSig." A few obstructions are detected. She fixes the code. An E2E failure has been prevented before it ever existed.</p>
</blockquote>
<p>What deserves attention in this scene is its <strong>quietness</strong>. No categories, no sheaves, no cohomology appear in it. The engineer says "analyze," receives one word — <em>obstruction</em> — fixes the code, and moves on. Behaviorally it is indistinguishable from running a linter.</p>
<p>Engineers should never need to learn algebraic geometry — that has been a founding principle of this project, and it is the blueprint of this scene. The closer the theory gets to completion, the less visible it becomes. <strong>When the sea has risen far enough, all you can see is the surface.</strong></p>
<p>There is a deeper shift hidden in that scene. The sentence "an E2E failure was prevented" is a <strong>counterfactual</strong>. A failure that never happened cannot be observed. Today's software engineering has, in principle, no vocabulary for prevented failures — which is why tests can only prove their worth after the fact, and why the quality of an architecture hangs in the air until an incident lands.</p>
<p>Only when obstructions are grounded in theorems does "without this fix, the gluing would have failed" become not folklore but a <strong>consequence</strong>. That quiet scene is a world in which counterfactuals have become mathematics.</p>
<h2>The view from the summit, 2 — a fixed point for a society of agents</h2>
<p>There is a more extreme future.</p>
<blockquote>
<p>The engineer only watches from outside. Multiple AI agents coordinate dynamically and build software at ferocious speed. Mid-stream, an ArchSig alert fires — ArchSig sits at the deployment gate. The agents read its output and correct the code.</p>
</blockquote>
<p>In that future, the code, the tests, the reviews, and the fixes are all AI-generated. Everything stands on the same shifting ground — and the only thing that does not shift is a gate grounded in theorems. Its verdict is nobody's opinion; it is the consequence of formally verified theorems on a chosen contract. In the vortex of high-speed generation, there is exactly one rock that does not move. Only things with that property belong at a deployment gate. ArchSig stops being a human's tool and becomes <strong>the fixed point of a society of agents</strong>.</p>
<p>In that future, the principle of <em>staying silent about what cannot be said</em> stops being a luxury and becomes a <strong>safety device</strong>. A human reader will charitably interpret a vague warning; an agent reads output literally and acts literally. A checker that emits "this looks suspicious somehow" will make an agent loop diverge. The discipline of silence turns out to be the interface specification of the multi-agent era.</p>
<p>So where do the humans go? "Watching from outside" means, precisely, <strong>moving to the legislature</strong>. Who writes the LawPolicy? Who decides which vocabulary may be spoken, and what deserves to be called SAFE? The agents are the executive; the verified gate is the judiciary; humans write the law. You may give up the hands that write code — you have not given up deciding <em>what must be protected</em>. If anything, that becomes the purified human job.</p>
<p>A future in which engineers only watch is not a future in which humans have exited. It is a future in which <strong>humans have finished building the grounds for trust</strong>.</p>
<p>There is an economic tailwind too. <strong>Verification is orders of magnitude cheaper than generation.</strong> ArchSig is deterministic computation — effectively free next to LLM inference; a Lean proof, once established, is cheap to re-check. The scarcer compute becomes, the less affordable "LLMs reviewing LLMs" gets, and the more the economics favor cheap deterministic gates that kill bad generations early. It is the same story as type systems paying for themselves when memory was scarce: resource constraints always raise the value of catching mistakes <em>before</em> execution. In the age of ferocious generation, the most expensive thing is discovering a mistake after you generated it.</p>
<h2>The way down — the next theorem is literally called descent (unproven)</h2>
<p>Mountaineers say the summit is only halfway: a climb is complete only when you are safely down. The ascent of SAGA, too, leaves a way down. And by an almost too-perfect coincidence, the first theorem on that route is literally called the <strong>descent theorem</strong>.</p>
<p>So that aspiration is never dressed up as achievement, here is what remains unproven:</p>
<ul>
<li><p><strong>The descent theorem (the citadel)</strong> — <em>under what conditions does local correctness imply global correctness?</em> This would give, for the first time, explicit validity conditions for the composition principle software engineering has always believed and never proved. The detection direction (a global repair exists ⇒ the obstruction vanishes) is accumulating; the true descent direction (the obstruction vanishes ⇒ a global repair exists) is still open.</p>
</li>
<li><p><strong>H^2 and above</strong> — there is not yet a language for higher obstruction structure. The derived extension is deliberately deferred.</p>
</li>
<li><p><strong>Fidelity of the observation layer</strong> — ArchSig's verdicts can never be more trustworthy than ArchMap's fidelity to the real code. This is the engineering weak point, not the mathematical one, and the defensive line — purifying observation, keeping raw values, excluding judgment from the observation layer — is being built ahead of need.</p>
</li>
<li><p><strong>Validation on real codebases</strong> — H^1 detection finding, on a real project, a gluing failure that humans had missed. The truth of the theory is one question; the value of the tool is decided here.</p>
</li>
</ul>
<p>What changes when descent is proved? ArchSig can turn it into a checkable condition: <em>"this architecture satisfies the hypotheses of descent, therefore passing local tests implies global consistency."</em> And conversely, exactly where the hypotheses fail is where E2E testing should concentrate — <strong>the theory gets to point</strong>. The view from the summit reaches the valley of the real world only once this way down has been safely walked.</p>
<h2>Speaking only what can be spoken</h2>
<p>Finally, let me be explicit about what this tool does <strong>not</strong> say.</p>
<p>What ArchSig can say goes exactly this far: "this change is safe within the given vocabulary and laws." It does not say "this change is good design" — because it cannot. Between the two, there is a deliberately drawn line of silence.</p>
<p>I consider this a condition of trust, not a weakness. A tool that judges beyond what it can judge will, somewhere, inevitably lie. A tool that judges only what it can judge never lies within that range. In an era when AI wants to talk about everything, a tool that speaks only what it can speak is rare — and only such tools belong at a deployment gate.</p>
<p>This article was written under the same discipline: a proven section, a vision section, an unproven section — never mixed.</p>
<h2>Closing</h2>
<p>Software engineering has not failed to catch up. <strong>The necessary mathematics simply had not been imported yet.</strong> This research program is that import — and not smuggling, either: it clears customs through Lean.</p>
<p>The open problems are real: descent, higher obstructions, fidelity of the observation layer, validation on real codebases. None of them is light. But the difference between an unclimbable mountain and a climbable one was never the altitude. It is whether you can draw the route.</p>
<p>The water is rising. And a sea, once it starts rising, rises fast.</p>
<p>It can be done. And this is not an unclimbable mountain.</p>
<h2>References</h2>
<ul>
<li><p>Repository: <a href="https://github.com/iroha1203/AlgebraicArchitectureTheoryV2">AlgebraicArchitectureTheoryV2</a> (MIT license)</p>
</li>
<li><p>Website: <a href="https://iroha1203.dev/">AAT/SFT Research Notes</a></p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[The SAGA Theorem: The Day Software Architecture Became Genuine Algebraic Geometry]]></title><description><![CDATA[TL;DR

Every module passes review, yet the system as a whole is broken. This "locally correct, globally inconsistent" phenomenon has a name in mathematics: cohomology, specifically H^1.

AAT (Algebrai]]></description><link>https://blog.iroha1203.dev/the-saga-theorem</link><guid isPermaLink="true">https://blog.iroha1203.dev/the-saga-theorem</guid><category><![CDATA[software architecture]]></category><category><![CDATA[AI]]></category><category><![CDATA[geometry]]></category><category><![CDATA[lean]]></category><dc:creator><![CDATA[Hiroyuki Nakahata]]></dc:creator><pubDate>Thu, 02 Jul 2026 18:31:33 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/69ff29ebf239332df4a93c3a/0af1531a-2577-4590-b5e7-acd536edc002.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2>TL;DR</h2>
<ul>
<li><p><strong>Every module passes review, yet the system as a whole is broken.</strong> This "locally correct, globally inconsistent" phenomenon has a name in mathematics: cohomology, specifically <code>H^1</code>.</p>
</li>
<li><p>AAT (Algebraic Architecture Theory) treats software architecture as a geometric object: generated from axiomatized facts called Atoms, cut out by laws, with obstructions appearing as cohomology classes.</p>
</li>
<li><p>Within AAT, I proved in Lean 4 a theorem that I call the <strong>SAGA theorem</strong> (SAGA Grounding Theorem). It is a comparison theorem: the <code>H^1</code> grown on the architecture-semantics side coincides with the genuine Čech <code>H^1</code> of the site generated from Atoms.</p>
</li>
<li><p>The proof took 352 cycles. For 347 of them, an automated AI loop accumulated impossibility theorems saying "this vocabulary cannot prove it" — until a single vocabulary shift, <strong>laws are equations, not predicates</strong>, broke the rock face in the final 5 cycles.</p>
</li>
<li><p>This article starts from a minimal map of algebraic geometry, then explains what the SAGA theorem says and why it matters for computer science.</p>
</li>
</ul>
<h2>1. Locally correct, globally broken</h2>
<p>You have probably seen this before.</p>
<ul>
<li><p>Every team's code satisfies its own review standards. Integrated, the system fails.</p>
</li>
<li><p>Each microservice honors its contract. The system-wide invariant is still violated.</p>
</li>
<li><p>Every refactoring step was safe. Stacked together, the original design intent is gone.</p>
</li>
</ul>
<p>Linters, static analysis, and dependency graphs are good at finding <em>local</em> violations. But none of the situations above contain one. The problem lives not in any individual part but in how the parts are <em>glued together</em>.</p>
<p>Mathematics has studied exactly this phenomenon — locally consistent data that fails to glue globally — for the better part of a century. The theory of sheaves and cohomology. The obstruction to gluing a family of local data into global data appears as an element of a cohomology group called <code>H^1</code>. If the class is zero, the pieces glue. If it is nonzero, you are facing the kind of failure <strong>whose cause cannot be found by inspecting any single part</strong>.</p>
<p>AAT is a theory that takes this mathematics and runs it, seriously, on software architecture.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69ff29ebf239332df4a93c3a/99c4310a-2af0-4984-b522-d590ad3af7de.png" alt="" style="display:block;margin:0 auto" />

<h2>2. A minimal map of algebraic geometry for engineers</h2>
<p>Before the main story, let me lay out the algebraic-geometry ideas this article relies on. Algebraic geometry is advanced mathematics, but to read this article you do not need rigorous definitions — just <strong>three ways of seeing, and a small glossary</strong>.</p>
<h3>Way of seeing 1: equations and shapes are two faces of the same thing</h3>
<p>In one sentence, algebraic geometry is <strong>the study of the shapes formed by solutions of equations, through the algebra of the equations themselves</strong>. The equation <code>x² + y² = 1</code> and the circle in the plane are two faces of one object. Operations on the equation side correspond to operations on the shape side — this "algebra ⇄ geometry" dictionary is the heart of the subject.</p>
<h3>Way of seeing 2: not "is it satisfied?" but "how is it imposed?"</h3>
<p>As solution sets, <code>x = 0</code> and <code>x² = 0</code> are indistinguishable — both are "just the origin." But as equations they differ: <code>x² = 0</code> carries the extra information of being "zero twice over." Modern algebraic geometry took off precisely by studying not the <em>predicate-level</em> information (satisfied / not satisfied) but the <em>structural</em> information of the equations themselves. In engineering terms: instead of only checking whether the tests pass, you carry around <em>by what margin and for what reason</em> they pass. This distinction becomes decisive later in the article.</p>
<h3>Way of seeing 3: study locally, then glue</h3>
<p>You cannot map the whole Earth onto one flat sheet, but you can make an atlas — a family of local maps. Algebraic geometry covers a shape with a family of local views, studies each view, and glues the results back into a global understanding. And sometimes <strong>everything works locally, yet the gluing fails</strong>. The tool that measures that failure is cohomology.</p>
<h3>A small glossary</h3>
<table>
<thead>
<tr>
<th>Term</th>
<th>Rough meaning</th>
<th>An engineer's mental image</th>
</tr>
</thead>
<tbody><tr>
<td>ideal</td>
<td>a container holding a set of equations, closed under derivation</td>
<td>a rule set together with every rule derivable from it</td>
</tr>
<tr>
<td>quotient <code>O/I</code></td>
<td>how the world looks after imposing the constraints <code>I</code></td>
<td>the view through an interface after the constraints identify things</td>
</tr>
<tr>
<td>zero locus <code>V(I)</code></td>
<td>the set of points satisfying all the constraints</td>
<td>the space of configurations passing every rule</td>
</tr>
<tr>
<td>cover and site</td>
<td>a way of covering the whole with local views, plus the rules for what counts as a valid covering</td>
<td>module / scope decomposition</td>
</tr>
<tr>
<td>sheaf</td>
<td>an assignment of data to each local view that stays consistent as views shrink</td>
<td>configuration values inherited without contradiction as scope narrows</td>
</tr>
<tr>
<td>cocycle</td>
<td>a consistent record of the mismatches along the boundaries between views</td>
<td>a diff log per view boundary</td>
</tr>
<tr>
<td>coboundary</td>
<td>a mismatch that disappears if you re-adjust the individual views</td>
<td>a diff you can eliminate by local re-tuning</td>
</tr>
<tr>
<td><code>H^1</code></td>
<td>cocycles ÷ coboundaries = the classes of mismatch that cannot be removed</td>
<td>the system-wide inconsistency that survives every local fix</td>
</tr>
<tr>
<td>Nullstellensatz</td>
<td>the theorem making precise the correspondence between predicates (solution sets) and equations (ideals)</td>
<td>a guaranteed correspondence between observed behavior and imposed constraints</td>
</tr>
<tr>
<td>GAGA</td>
<td>the landmark comparison theorem: two differently built cohomologies coincide</td>
<td>a proof that two implementations satisfy the same specification</td>
</tr>
</tbody></table>
<p>You do not need to memorize any of this. Keep the map at hand and read on. Now, the main story.</p>
<h2>3. What is AAT? Architecture as relative geometry</h2>
<p>The starting point of AAT (Algebraic Architecture Theory) is its choice of object. AAT does not study the raw codebase itself.</p>
<pre><code class="language-text">C : Codebase
V : AtomVocabulary   (which facts you choose to observe)
U : LawUniverse      (which laws you choose to impose)
J : CoverageTopology (how you cover the architecture with local views)
k : coefficient ring (what you measure obstructions in)

X_C^{V,U,J,k} : AAT geometry   (this is the object of the theory)
</code></pre>
<p>Instead of asking "is this codebase good?", AAT asks: <strong>given this observation vocabulary, these laws, and this way of covering the system with local views, what geometry arises?</strong> The vocabulary and the laws are explicit, and all mathematics is relative to them. This relativization is also where AAT's discipline of boundaries comes from — internally the project calls it "silence about what cannot be spoken": the theory does not make claims outside its declared inputs.</p>
<h2>4. The Atom axioms — the minimal facts of architecture</h2>
<p>The smallest unit of AAT is the <strong>Atom</strong>: a typed architectural fact that we choose not to decompose further. An Atom has five components:</p>
<pre><code class="language-text">a = (kind, axis, subject, predicate, payload)
</code></pre>
<ul>
<li><p><code>kind</code>: what species of fact this is (component, relation, contract, semantic fact, ...)</p>
</li>
<li><p><code>axis</code>: which structural axis it concerns</p>
</li>
<li><p><code>subject</code>: what it is about</p>
</li>
<li><p><code>predicate</code>: what holds</p>
</li>
<li><p><code>payload</code>: the content — values, names, types, evidence</p>
</li>
</ul>
<p>"Service A depends on service B." "This module owns the compensation logic for payments." "This API requires authentication." All of these are Atoms. Syntactic facts and semantic responsibilities live on the same footing.</p>
<p>Atoms are governed by an axiom system (A0–A8). The most important ones:</p>
<ul>
<li><p><strong>A0 Primitive Existence</strong>: the type of Atoms exists; everything is generated from it.</p>
</li>
<li><p><strong>A2 Single Fact</strong>: one Atom states one fact. Compound claims are families (configurations) of Atoms.</p>
</li>
<li><p><strong>A3 Predicate Stability</strong>: Atom identity is determined by the five components.</p>
</li>
<li><p><strong>A4 Composition</strong>: finite families of Atoms generate configurations, from which architecture objects arise.</p>
</li>
<li><p><strong>A5 Law Non-Generation</strong>: <strong>laws do not generate Atoms.</strong> A law is a constraint over facts, never a source of facts.</p>
</li>
</ul>
<p>A5 is quietly crucial. It separates "what should be" (laws) from "what was observed" (Atoms) at the axiom level, so wishful thinking can never leak into observation.</p>
<h2>5. Making laws into equations — the algebraic-geometry dictionary</h2>
<p>On top of Atom families we impose <strong>laws</strong>: "no dependency cycles," "every compensation handler must be paired," and so on.</p>
<p>Naively, a law is a predicate — it holds or it does not. And indeed, that is how AAT first defined laws, too.</p>
<p>But algebraic geometry discovered a better viewpoint 150 years ago: treat constraints not as <em>predicates</em> but as <strong>equations</strong>. This is where the glossary from Section 2 goes to work:</p>
<pre><code class="language-text">a set of equations        →  an ideal (the algebraic object the equations generate)
points satisfying them    →  the zero locus V(I) (the geometric object)
functions visible under
the constraints           →  the quotient ring O/I
</code></pre>
<p>The key point: <strong>a predicate (satisfied / not satisfied) cannot recover the equations.</strong> As Hilbert's Nullstellensatz made precise, the equation side (the ideal) carries strictly more structure than the solution set — multiplicities, infinitesimals, deformations. And <strong>that extra structure is exactly what you need to compute cohomology.</strong> No coefficients, no cohomology; predicates have no coefficients.</p>
<p>AAT transplants this dictionary to architecture:</p>
<pre><code class="language-text">a law                      →  the witness ideal I_L generated by its violation coordinates
the failure of all laws    →  the obstruction ideal I_Ob = Σ I_L
readings under the laws    →  the quotient O/I_Ob (the coefficients of the obstruction sheaf)
the law holds              ⟺  pulling the reading back kills the ideal (s*I_Ob = 0)
</code></pre>
<p>Note that the last line has exactly the shape of Hilbert's Nullstellensatz: the semantic fact "the law holds" becomes equivalent to the algebraic fact "the ideal vanishes." This is why AAT is algebraic geometry, not merely algebraic-geometry-<em>flavored</em>.</p>
<p>Then we add coverings. Cover the architecture with a family of contexts and observe locally in each. A Grothendieck topology is generated from the Atoms on the category of contexts, giving a <strong>site</strong>. Sheaves live on it, and Čech cohomology. The obstruction to gluing the local readings into a global one appears as a class in <code>H^1</code>.</p>
<h2>6. Before SAGA — the lower floors of the tower</h2>
<p>I have been turning this picture into Lean 4 theorems, one floor at a time.</p>
<ul>
<li><p><strong>The finite descent theorem</strong>: a family of local repairs glues into a global repair exactly when a finite obstruction class vanishes. The first theorem ever attached to "local-pass / global-fail."</p>
</li>
<li><p><strong>The true H¹ theorem</strong>: that obstruction class is not an <code>H^1</code> in name only, but a genuine quotient <code>H^1 = Z^1/B^1</code> — cocycles modulo coboundaries.</p>
</li>
</ul>
<p>At this point AAT was a theory that measures architectural gluing failures with <code>H^1</code>. But one fundamental weakness remained.</p>
<p>That <code>H^1</code> was a <strong>purpose-built finite construction</strong>. The general theory of sites and sheaves was formalized in the very same repository — yet the semantic-repair <code>H^1</code> stood next to it as a separately grafted tower. The claim "AAT is doing algebraic geometry" rested on "AAT owns a construction shaped like algebraic geometry." In principle, that is indistinguishable from calling any ad hoc finite quotient an <code>H^1</code>.</p>
<h2>7. The SAGA theorem — two worlds coincide</h2>
<p>This is the gap I set out to close — the goal that would later be named the <strong>SAGA theorem</strong>. In one sentence:</p>
<blockquote>
<p><strong>The semantic repair</strong> <code>H^1</code><strong>, grown on the architecture-semantics side, is an instance of the general theory's Čech</strong> <code>H^1</code> <strong>(the computable version relative to a chosen cover), on the site generated from Atoms, with coefficients generated from laws. The two zero-tests are equivalent.</strong></p>
</blockquote>
<p>The name honors Serre's <strong>GAGA</strong> (<em>Géométrie Algébrique et Géométrie Analytique</em>), the landmark comparison theorem showing that the cohomologies of two worlds — algebraic and analytic geometry — coincide. SAGA (<strong>S</strong>émantique <strong>A</strong>rchitecturale, <strong>G</strong>éométrie <strong>A</strong>lgébrique) is a comparison theorem in that tradition, for architectural semantics and algebraic geometry. And the name carries a second meaning: the proof was, quite literally, a <em>saga</em>.</p>
<p>With SAGA proved, the whole chain connects:</p>
<pre><code class="language-text">Atoms (axiomatized facts)
  → laws as equations (witness ideals)
  → obstruction coefficients as the quotient O/I_Ob
  → the site and covers generated from Atoms
  → the general theory's Čech H^1
  = the semantic repair H^1 (the one from the true H¹ theorem above)
</code></pre>
<p><strong>AAT's cohomology grows out of AAT's own axioms.</strong> No grafting.</p>
<h2>8. The story of the proof — 347 impossibility theorems and one decision</h2>
<p>The proof of SAGA is a research record worth telling in its own right.</p>
<p>It ran for 352 cycles of what is now fashionably called <strong>loop engineering</strong>: an automated AI agent loop discharged small proof obligations in Lean, one per cycle. Every cycle passed adversarial audits designed to reject the classic cheat of formalization: smuggling a conclusion-equivalent premise into the assumptions. The design emphasis was not on making the loop run fast, but on <strong>auditing what the loop can legitimately claim to have proved</strong>.</p>
<h3>Compression and blockade</h3>
<p>The first hundred or so cycles built the comparison skeleton. Then, for roughly 250 cycles, something strange happened: instead of advancing, the loop began <strong>sealing off every detour, with counterexamples</strong>. "This input surface cannot prove it." "Adding this auxiliary data does not help either." Thirty-six families of impossibility boundaries accumulated as theorems, until the entire goal had been compressed into a single proposition. And in cycles 320–347, the loop proved that this one point was <strong>underivable, in principle, from the current vocabulary</strong>.</p>
<p>The cause was fundamental. Laws had been formalized as <code>holds : Prop</code> — an opaque predicate. Predicates do not determine equations. Cohomology does not grow where there are no coefficients. "Way of seeing 2" from Section 2, and the lesson of Section 5, had come back as a family of impossibility theorems in Lean.</p>
<p>Were those 347 cycles wasted? The opposite. <strong>The theory established the limits of its own vocabulary, as theorems, before extending it.</strong> The fix was narrowed from "somewhere" to "exactly here." There are not many formalization projects that have done this.</p>
<h3>The vocabulary decision</h3>
<p>At this point I made the call: <strong>laws are equations. That is precisely why AAT can become algebraic geometry.</strong></p>
<p>Remarkably, the mathematics needed for the extension already existed in the repository. The AAT canon had contained the toolkit for treating laws as equations — violation coordinates, witness ideals, the lawful locus — all along; the formalization simply had not used it yet. The decision was not the invention of new mathematics; it was <strong>letting the formalization catch up with the editorial intent of the text</strong>.</p>
<p>Once the vocabulary changed, things moved fast. Generate the obstruction coefficients as the quotient by the ideal; generate each local reading as the quotient class of its defect. Then the proposition that had been sealed for 347 cycles — <em>if the required laws hold locally, the restrictions of the readings agree on common refinements</em> — <strong>fell as a theorem</strong>. Five cycles later, everything was in place: the comparison, the equivalence of zero-tests, and a concrete instance where a nonzero class demonstrably lives on both sides.</p>
<h3>A review that produced theorems</h3>
<p>The final act was the review. Just before completion, one of the four adversarial review lanes vetoed: "the <code>H^1</code>-zero part of this composed theorem is constructively trivial, and the law semantics only acts at cohomological degree 0 — the statement invites over-reading." The objection was correct.</p>
<p>The response was not to weaken the claim but to prove <strong>new theorems</strong>: a positive boundary theorem ("the law semantics contributes exactly degree-0 vanishing") and a negative one ("the higher conclusions hold independently of the laws"). The theory deepened its knowledge of itself through the review. The re-review lifted the veto, and the theorem was accepted.</p>
<h2>9. Why this matters for computer science</h2>
<p>This looks like a mathematics story, but SAGA has several CS implications.</p>
<p><strong>(1) A class of analysis beyond linting.</strong> Detecting local violations (lint, static analysis, contract checking) and detecting global gluing failures are mathematically different classes of problems. Let us return to the three examples from the opening.</p>
<ul>
<li><p><strong>"Every team's code passes review, yet integration breaks."</strong> Each team's area of ownership corresponds to a local view of the cover. Among the mismatches along team boundaries (the cocycle), the ones that <strong>no team can eliminate by fixing its own code</strong> — those read as a nonzero <code>H^1</code> class. There was a structural reason the cause could never be found inside any single team.</p>
</li>
<li><p><strong>"Every microservice honors its contract, yet the system invariant is violated."</strong> Contracts correspond to local laws; the system-wide invariant corresponds to a global section. All services can be locally lawful while the global section gluing them together simply <strong>does not exist</strong>. That existence is exactly what <code>H^1</code> decides.</p>
</li>
<li><p><strong>"Every refactoring step was safe, yet the design intent is gone."</strong> Each step corresponds to a local re-adjustment (a coboundary). But stack a full loop of re-adjustments, and a residue can survive that never cancels. In fact, the concrete nonzero-class instance used in the SAGA proof was literally a construction where <strong>going once around a circle leaves you off by 1</strong>. Every step safe, yet you return changed — that déjà vu now has a mathematical identity.</p>
</li>
</ul>
<p>After SAGA, AAT owns, for this class of failures, an obstruction with a <em>proved-sound detector</em>: a nonzero <code>H^1</code> class certifies which law fails over which observed facts, with traceability back to the displayed law support. It is a principled path toward detecting the failure mode where "no file contains a bug, and yet the system is broken."</p>
<p><strong>(2) It matters most in the era of AI writing code at full speed.</strong> From day one, AAT's research goals carried a "rival" field: static analysis, dependency graphs, ADLs (architecture description languages), and AI code review with near-unlimited context — every cycle had to answer what AAT can produce that they cannot. The answer is this <code>H^1</code>. In an era where AI agents mass-produce pull requests, CI can run local checks — tests, lint, contract verification — at high speed. But an <code>H^1</code>-class failure <strong>appears in no diff and in no file</strong>. Every PR is green, every module is lawful, and yet the global section does not exist — this failure mode slips through diff-level and file-level checking <em>in principle</em>. ADLs can describe structure and conformance with great sophistication, but they have no machinery for proving semantic gluing failures as cohomology classes. Even an AI reviewer that reads the entire codebase can articulate unease, but it cannot <strong>pin the existence of the obstruction down as a theorem, backed by detector soundness and counterexamples</strong>. The faster code gets written, the faster gluing failures accumulate, silently. What can give this class of failure a theorem-level detector is a theory that treats the gluing itself as mathematics.</p>
<p><strong>(3) A methodology for vocabulary evolution in formalization.</strong> Every large formalization eventually hits the "our original definitions were wrong" problem. The SAGA proof demonstrates one pattern: first pin down the limits of the old vocabulary as a family of impossibility theorems, so that the extension becomes a <em>forced, minimal move</em> — and afterwards, the old no-go theorems keep serving as audit machinery. Definition change becomes accretion, not restart.</p>
<p><strong>(4) A working example of human–AI division of labor.</strong> 347 cycles of automated exploration and blockade by AI agents; one vocabulary decision made by me, the human in the loop; adversarial multi-agent review with a veto and a re-review. Remove any one of the three and this theorem does not exist in this form. As a template for doing mathematics with AI, the record is arguably as valuable as the theorem.</p>
<p><strong>(5) Everything is machine-checked.</strong> Every theorem in this story — the comparison, the impossibility boundaries, the nonzero-class instance — compiles in Lean 4 and depends only on Lean's standard axioms (<code>propext</code>, <code>Classical.choice</code>, <code>Quot.sound</code>). There are no <code>sorry</code>s.</p>
<h2>10. Honest boundaries — not everything, but enough to stand on</h2>
<p>Finally, in keeping with AAT's own discipline, here is what the SAGA theorem does <strong>not</strong> claim. The bullet points are unavoidably technical, so each comes with a plain-language gloss.</p>
<ul>
<li><p>The theorem is relativized to finite or small sites, selected covers, and vocabularies equipped with an equational realization of their laws. No unconditional generalization to arbitrary Grothendieck sites is claimed.</p>
<ul>
<li>In plain terms: the theorem holds for <strong>finitely many parts, an explicitly chosen decomposition, and laws you can write as equations</strong>. It does not claim to cover every infinitely complex situation mathematics can imagine.</li>
</ul>
</li>
<li><p>No unconditional identification of cover-relative Čech <code>H^1</code> with full sheaf cohomology is claimed — in fact, "this cannot be said unconditionally" is itself a boundary theorem with a counterexample.</p>
<ul>
<li>In plain terms: cohomology comes in a <strong>computable version relative to your chosen decomposition</strong> and a decomposition-independent theoretical version; SAGA works with the former. That the two cannot be unconditionally identified is itself pinned down as a theorem, with a counterexample.</li>
</ul>
</li>
<li><p>The completeness of extracting Atoms from a codebase, and "quality judgments about real code as a whole," are outside the theory. AAT does mathematics relative to observed Atoms and selected laws.</p>
<ul>
<li>In plain terms: <strong>whether you extracted every fact from the code</strong> is the observation tool's job; mathematics does not guarantee it. What AAT guarantees is the correctness of everything <strong>downstream</strong> of the facts you gathered and the laws you imposed.</li>
</ul>
</li>
</ul>
<p>Is this boundary a weakness of the theory? I do not think so. Analyzing software architecture is an inherently finite activity: finitely many modules, finitely many observable facts, finitely many rules imposed in review. <strong>The "finite, explicitly-voiced" world in which the SAGA theorem holds is exactly the world architecture analysis actually lives in.</strong> It is not a theory of everything. But as a foothold for analyzing software architecture, it is more than wide enough.</p>
<p>Not saying what cannot be said. Making the boundary explicit is itself part of the theory.</p>
<h2>11. Closing</h2>
<p>Anyone can say "let's apply algebraic geometry to software architecture" as a metaphor. What the SAGA theorem did was stop it being a metaphor. From the Atom axioms, through laws as equations, obstruction coefficients as quotients, sites, sheaves, and up to <code>H^1</code> — everything is connected by a single chain of machine-checked theorems.</p>
<p>That old phenomenon — locally correct, globally broken — now has mathematics that grows out of axioms.</p>
<h2>References</h2>
<ul>
<li><p>Repository: <a href="https://github.com/iroha1203/AlgebraicArchitectureTheoryV2">AlgebraicArchitectureTheoryV2</a> (MIT license)</p>
</li>
<li><p>Proof record of the SAGA theorem: <code>docs/note/aat_saga_theorem_proof_record.md</code></p>
</li>
<li><p>The AAT mathematical canon: <code>docs/aat/algebraic_geometric_theory/</code></p>
</li>
<li><p>Research goal ledger: <code>research/goals/G-aat-quality-surface-06.md</code>; proof-state ledger: <code>research/reports/G-aat-quality-surface-06.md</code> (inside the repository, the SAGA theorem is tracked under the research-goal ID <code>G-aat-quality-surface-06</code>)</p>
</li>
<li><p>Lean artifacts: <code>research/lean/ResearchLean/AG/QualitySurface/</code> (for SAGA, see <code>SemanticRepairCechGrounding.lean</code> and <code>SemanticRepairLawEquation*.lean</code>)</p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[Atom Is All You Need: Read a Codebase as an Atom Map, Not a Dependency Graph]]></title><description><![CDATA[TL;DR
In the AI era, architecture analysis cannot stop at reading code as files, functions, imports, and dependency graphs.
We need to observe the smallest units that carry architectural meaning. I ca]]></description><link>https://blog.iroha1203.dev/atom-is-all-you-need</link><guid isPermaLink="true">https://blog.iroha1203.dev/atom-is-all-you-need</guid><category><![CDATA[AI]]></category><category><![CDATA[software architecture]]></category><category><![CDATA[Software Engineering]]></category><category><![CDATA[Open Source]]></category><dc:creator><![CDATA[Hiroyuki Nakahata]]></dc:creator><pubDate>Sun, 31 May 2026 16:52:30 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/69ff29ebf239332df4a93c3a/f69958fa-2f77-4654-ba0a-e0122e783bd8.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p><strong>TL;DR</strong></p>
<p>In the AI era, architecture analysis cannot stop at reading code as files, functions, imports, and dependency graphs.</p>
<p>We need to observe the smallest units that carry architectural meaning. I call those units <code>Atom</code>s.</p>
<p>By extracting Atoms from a codebase and reading the algebraic structure they generate, we can analyze architecture beyond ordinary dependency relationships.</p>
<p>The workflow looks like this:</p>
<pre><code class="language-text">codebase
  -&gt; Atom observations
  -&gt; ArchMap
  + LawPolicy
  -&gt; ArchSig analysis packet
  -&gt; architecture reading
</code></pre>
<p>This lets us see design pressure, semantic coupling, missing evidence, and review focus that ordinary linting, static analysis, and dependency graphs often miss.</p>
<p>We built a new tool for this workflow: <a href="https://github.com/iroha1203/AlgebraicArchitectureTheoryV2/tree/main/tools/archsig">ArchSig</a>.</p>
<p>The repository is public and MIT licensed, so you can try it on your own codebases.</p>
<p>The key idea is simple:</p>
<blockquote>
<p>Architecture analysis does not need a bigger natural-language summary.</p>
<p>It needs the right unit of observation.</p>
</blockquote>
</blockquote>
<h2>Why We Need an Architecture Analyzer</h2>
<p>AI agents can write code fast.</p>
<p>But writing code fast is not the same thing as evolving a system well.</p>
<p>For an AI agent, the codebase is the largest prompt.</p>
<p>If the existing codebase contains shortcuts, those shortcuts become examples. If boundaries are vague, new code naturally lands in vague places. If responsibilities are mixed, that mixing becomes part of the next implementation context.</p>
<p>Traditional tools often look at the current shape of code:</p>
<ul>
<li><p>import graphs</p>
</li>
<li><p>dependency cycles</p>
</li>
<li><p>layer violations</p>
</li>
<li><p>lint rules</p>
</li>
<li><p>complexity metrics</p>
</li>
<li><p>test coverage</p>
</li>
<li><p>security rules</p>
</li>
</ul>
<p>All of these matter.</p>
<p>But in AI-era architecture review, we often want to ask deeper questions.</p>
<p>Imagine a business backend where users select input data, run automated processing or generation, persist the result, receive events from external systems, run async jobs, call external providers, and save generated artifacts back into business state.</p>
<p>The important questions are not only:</p>
<blockquote>
<p>Are the dependencies pointing in the right direction?</p>
</blockquote>
<p>We also want to know:</p>
<ul>
<li><p>Who is allowed to use which input for which operation?</p>
</li>
<li><p>Through which validation path does generated output become persisted state?</p>
</li>
<li><p>Where is an external provider failure recorded as durable state?</p>
</li>
<li><p>Do two workflows that produce the same artifact actually have the same effect order?</p>
</li>
<li><p>Do entrypoint-level permissions match the intended business scope?</p>
</li>
<li><p>Are privileged operations separated from ordinary workflow authority?</p>
</li>
</ul>
<p>These are difficult to read from a dependency graph alone.</p>
<p>We need to read code not only as a collection of files and functions, but as a set of observations that carry architectural meaning.</p>
<p>That unit is the <code>Atom</code>.</p>
<h2>Atom: The Smallest Unit of Architectural Meaning</h2>
<p>An Atom is the smallest observation unit I want to use when reading architecture.</p>
<p>It is not necessarily a function, class, module, or file.</p>
<p>In practice, it is the thing you point at during review and say:</p>
<blockquote>
<p>This is a boundary.</p>
<p>This is state.</p>
<p>This is an effect.</p>
<p>This is a contract.</p>
<p>This is where trust changes.</p>
</blockquote>
<p>Examples of Atom families include:</p>
<ul>
<li><p><code>component atom</code>: an architectural unit exists</p>
</li>
<li><p><code>relation atom</code>: two units or atoms are related</p>
</li>
<li><p><code>capability atom</code>: a unit provides a specific capability</p>
</li>
<li><p><code>state atom</code>: something is persisted or held as state</p>
</li>
<li><p><code>effect atom</code>: an external call or side effect occurs</p>
</li>
<li><p><code>authority atom</code>: someone is allowed to do something</p>
</li>
<li><p><code>trust relation atom</code>: some output or provider is trusted up to a boundary</p>
</li>
<li><p><code>contract atom</code>: inputs, outputs, preconditions, or failure behavior are promised</p>
</li>
<li><p><code>semantic atom</code>: something has architectural meaning beyond its code location</p>
</li>
</ul>
<p>The important point is that an Atom is not small in the syntactic sense.</p>
<p>It is small in the architectural sense.</p>
<p>For example, the observation:</p>
<blockquote>
<p>Only an authorized actor may update this resource.</p>
</blockquote>
<p>may cross multiple files.</p>
<p>The observation:</p>
<blockquote>
<p>Generated output must pass through a service-level contract before persistence.</p>
</blockquote>
<p>may cross a route, service, validation layer, job state, and repository.</p>
<p>Still, each observation is one meaningful unit in architecture review.</p>
<p>That is what an Atom records.</p>
<h2>A Small Code Example</h2>
<p>Even a tiny function can contain multiple Atoms.</p>
<pre><code class="language-python">def publish_artifact(actor, artifact_id, repository, gateway):
    artifact = repository.load_for_actor(actor.id, artifact_id)
    gateway.publish(artifact.payload)
    artifact.mark_published()
    repository.save(artifact)
</code></pre>
<p>If we only say "this is one function," we miss most of the architectural content.</p>
<p>Read as Atoms, we can observe at least:</p>
<ul>
<li><p><code>component</code>: <code>repository</code> and <code>gateway</code> are architectural units</p>
</li>
<li><p><code>relation</code>: the handler flows into both the repository and the gateway</p>
</li>
<li><p><code>capability</code>: the repository provides load/save, while the gateway provides publish</p>
</li>
<li><p><code>state</code>: the artifact's published state is persisted</p>
</li>
<li><p><code>effect</code>: <code>gateway.publish</code> changes external state</p>
</li>
<li><p><code>authority</code>: the actor must be allowed to publish this artifact</p>
</li>
<li><p><code>trust_relation</code>: the gateway response and failure behavior must be trusted only through a boundary</p>
</li>
<li><p><code>contract</code>: publish success, save failure, and retry behavior need explicit promises</p>
</li>
<li><p><code>semantic</code>: published is not only a flag; it means externally visible</p>
</li>
</ul>
<p>These nine Atom schemas are the basic vocabulary for treating primitive architectural facts as generators of an architecture object.</p>
<h2>ArchMap: Turning a Codebase into an Atom Map</h2>
<p>An <code>ArchMap</code> is a map of these observations.</p>
<p>It is not a full copy of the codebase. It is not an AST dump. It is not only a dependency graph.</p>
<p>It is a source-grounded map of architectural observations.</p>
<pre><code class="language-text">source code
  observed as
Atom / Molecule / Semantic observations
  recorded as
ArchMap
</code></pre>
<p>If an Atom is one observation, a Molecule is a finite composition of Atoms that acts as one responsibility boundary.</p>
<p>For example, an ArchMap of a backend might contain Molecules such as:</p>
<ul>
<li><p>authenticated request boundary</p>
</li>
<li><p>credential lifecycle</p>
</li>
<li><p>repository transaction boundary</p>
</li>
<li><p>interactive processing workflow</p>
</li>
<li><p>input-to-artifact pipeline</p>
</li>
<li><p>external integration ingress</p>
</li>
<li><p>job-managed generation surface</p>
</li>
<li><p>input-backed artifact boundary</p>
</li>
<li><p>edge policy and observability boundary</p>
</li>
<li><p>privileged governance boundary</p>
</li>
</ul>
<p>The important point is not that the codebase has folders such as <code>api</code>, <code>services</code>, <code>repositories</code>, and <code>models</code>.</p>
<p>The important point is that architectural meaning sits on top of those folders:</p>
<ul>
<li><p>authority</p>
</li>
<li><p>generation</p>
</li>
<li><p>external effect</p>
</li>
<li><p>durable state</p>
</li>
<li><p>artifact lifecycle</p>
</li>
<li><p>semantic contract</p>
</li>
</ul>
<p>ArchMap records that meaning in a machine-readable form.</p>
<h2>AAT: Reading Structure from an Atom Map</h2>
<p>Once we have an ArchMap, we can ask a different kind of question:</p>
<blockquote>
<p>Under which laws does this architecture object have pressure?</p>
</blockquote>
<p>AAT, Algebraic Architecture Theory, reads software architecture as an algebraic structure generated from Atoms.</p>
<p>It studies architecture through objects, operations, laws, invariants, obstructions, and signature axes.</p>
<p>I will leave the detailed theory to <a href="https://iroha1203.dev/aat/">Algebraic Architecture Theory</a>.</p>
<p>For this article, the intuition is enough:</p>
<ul>
<li><p><code>law</code>: a design property we want to preserve</p>
</li>
<li><p><code>witness</code>: observed evidence related to that property</p>
</li>
<li><p><code>obstruction</code>: a structure that blocks the law</p>
</li>
<li><p><code>signature axis</code>: the direction in which pressure appears</p>
</li>
<li><p><code>operation</code>: a design operation such as split, move, protect, or refactor</p>
</li>
<li><p><code>path / homotopy</code>: whether two change paths preserve the same structure</p>
</li>
<li><p><code>curvature / holonomy</code>: when something looks locally natural but becomes inconsistent around a loop</p>
</li>
</ul>
<p>In ordinary review, engineers often process these ideas in their heads:</p>
<blockquote>
<p>This responsibility is mixed.</p>
<p>This external call should not be inside the transaction.</p>
<p>These two job statuses look the same, but the failure paths differ.</p>
<p>This generated output must be validated before it becomes domain state.</p>
</blockquote>
<p>AAT tries to move this review vocabulary toward something we can compute.</p>
<p>The important step is to treat Atoms not as labels, but as primitive facts that generate architecture objects.</p>
<p>Once Atoms carry relation, state, effect, contract, and semantic axes, we can see things a dependency graph cannot show:</p>
<ul>
<li><p>a locally natural change that changes effect order through another path</p>
</li>
<li><p>a split that moves complexity to another boundary</p>
</li>
<li><p>two workflows that look statically similar but differ under a semantic contract</p>
</li>
</ul>
<p>These are non-trivial architecture readings.</p>
<p>They should not live only in a senior engineer's intuition.</p>
<p>They should be tied to a selected law universe and a selected set of witnesses.</p>
<p>That is the role of AAT.</p>
<h2>ArchSig: Reading Structure from ArchMap + LawPolicy</h2>
<p>ArchSig takes an ArchMap and a LawPolicy, then produces an AAT-style analysis packet.</p>
<pre><code class="language-text">ArchMap
  + LawPolicy
  -&gt; ArchSig
  -&gt; analysis packet
</code></pre>
<p><code>LawPolicy</code> is the interpretation profile.</p>
<p>It selects the law universe, witness rules, signature axes, measurement policy, and coverage requirements for the analysis.</p>
<p>The same ArchMap can be read in different ways depending on what you care about:</p>
<ul>
<li><p>authority boundaries</p>
</li>
<li><p>state/effect consistency</p>
</li>
<li><p>generated output mediation</p>
</li>
<li><p>domain cohesion</p>
</li>
<li><p>permission coverage</p>
</li>
<li><p>repair preconditions</p>
</li>
</ul>
<p>ArchSig does not collapse all of this into one "good / bad" score.</p>
<p>It reports pressure and semantic coupling along different axes.</p>
<p>The output can include readings such as:</p>
<ul>
<li><p>nonzero pressure by law axis</p>
</li>
<li><p>workflow risk</p>
</li>
<li><p>transfer bridge pressure</p>
</li>
<li><p>state/effect reconciliation pressure</p>
</li>
<li><p>operation squares</p>
</li>
<li><p>axis-wise monodromy defects</p>
</li>
<li><p>architectural holes</p>
</li>
<li><p>boundary holonomy</p>
</li>
<li><p>repair precondition blockers</p>
</li>
<li><p>review focus</p>
</li>
</ul>
<p>This is not a code quality score.</p>
<p>It is a measurement surface for reading where design pressure and semantic coupling appear.</p>
<h2>Case Study: Analyzing a Backend Repository</h2>
<p>So far, the discussion has been conceptual.</p>
<p>Now let us look at the shape of one backend repository through ArchSig.</p>
<p>This repository has multiple users, authority boundaries, business data, external effects, async jobs, and generated outputs.</p>
<p>Users select input data and run analysis, generation, extraction, or transformation workflows. External systems send events. Async jobs call providers. Results are persisted as business artifacts.</p>
<p>From the ArchMap, ArchSig could read structures like:</p>
<ul>
<li><p>a backend split into API, service, repository, model, schema, and task layers</p>
</li>
<li><p>authority boundaries across users, organizations, and resources</p>
</li>
<li><p>a domain model where multiple artifacts derive from input data</p>
</li>
<li><p>generated output mediated by service contracts and job state</p>
</li>
<li><p>external provider effects paired with durable job state</p>
</li>
<li><p>privileged operations separated from ordinary workflows</p>
</li>
</ul>
<p>This is already different from a dependency graph.</p>
<p>ArchSig reads the repository as a business backend with authority boundaries, generated outputs, external effects, and durable state.</p>
<h2>The Design Pressure It Found</h2>
<p>The strongest pressure appeared at the intersection of authority and generation.</p>
<p>The system is not merely saying:</p>
<blockquote>
<p>An authenticated actor can run generation.</p>
</blockquote>
<p>The real architectural question is:</p>
<blockquote>
<p>Who can use which resource as input, and which generated output can become which domain state?</p>
</blockquote>
<p>That is a central boundary.</p>
<p>Reviewing only authorization is not enough. Reviewing only the generation job is not enough. The two boundaries are bridged.</p>
<p>The next large pressure was state/effect reconciliation.</p>
<p>The repository has external providers, async jobs, event handlers, retries, roundtrips, and compensation paths.</p>
<p>In this kind of system, not everything can live inside one transaction.</p>
<p>The architecture needs durable intermediate state, external effect recovery, explicit failure finalization, and retryable paths.</p>
<p>ArchSig reads this through the relation between state atoms and effect atoms.</p>
<p>The third pressure was generated output mediation.</p>
<p>Generated output is useful, but it should not become domain state without mediation.</p>
<p>You need input construction, structured output, filtering, validation, job failure handling, and persistence gates.</p>
<p>In this repository, the mediation appeared across multiple workflows:</p>
<ul>
<li><p>interactive workflows</p>
</li>
<li><p>analysis execution</p>
</li>
<li><p>content extraction</p>
</li>
<li><p>generated artifact creation</p>
</li>
</ul>
<p>So the design question is not:</p>
<blockquote>
<p>Does the system use generation?</p>
</blockquote>
<p>The real question is:</p>
<blockquote>
<p>Through which contract does generated output pass before it becomes domain state?</p>
</blockquote>
<p>This is not just dependency analysis.</p>
<p>It is a reading of where authority, state, effect, contract, and semantic meaning overlap.</p>
<h2>Same Destination, Different Path</h2>
<p>One of the most interesting ArchSig readings is the monodromy defect.</p>
<p>The intuition is:</p>
<blockquote>
<p>Two paths may appear to arrive at the same place.</p>
<p>But do they preserve the same structure?</p>
</blockquote>
<p>In the repository, several workflows looked similar from the outside.</p>
<p>They produced artifacts or updated state.</p>
<p>But the order of provider calls, repository commits, job state transitions, and status finalization could differ.</p>
<p>A dependency graph may not show that.</p>
<p>Even if the static dependencies point in the same direction, the workflow path can change failure behavior.</p>
<p>Does the system create state before calling the provider? Does it call the provider first? Where is failure finalized? Does retry use the same idempotency key?</p>
<p>ArchSig reads this kind of path difference as operation squares and axis-wise defects.</p>
<p>This is practical in review.</p>
<p>When someone says:</p>
<blockquote>
<p>This follows the existing pattern.</p>
</blockquote>
<p>ArchSig helps ask:</p>
<blockquote>
<p>Does it really?</p>
</blockquote>
<p>It may look like the same endpoint. It may create the same kind of artifact.</p>
<p>But if the effect order differs, the operational behavior differs.</p>
<p>That difference often becomes incident cost or future refactoring cost.</p>
<h2>Architectural Holes: Loops That Do Not Close</h2>
<p>Another important result was the architectural hole.</p>
<p>ArchSig found holes around paths such as:</p>
<ul>
<li><p>API -&gt; service -&gt; repository</p>
</li>
<li><p>async provider jobs</p>
</li>
<li><p>generated output -&gt; domain state promotion</p>
</li>
<li><p>semantic contracts</p>
</li>
</ul>
<p>This does not mean "there is a bug."</p>
<p>It means the evidence is not enough to say the architectural loop is closed.</p>
<p>For example, the path from generated output to domain state may involve:</p>
<ul>
<li><p>input data</p>
</li>
<li><p>context construction</p>
</li>
<li><p>structured output</p>
</li>
<li><p>validation</p>
</li>
<li><p>job state</p>
</li>
<li><p>repository persistence</p>
</li>
</ul>
<p>To say this loop is closed, source reading alone is not enough.</p>
<p>You may need test evidence, runtime traces, provider logs, and entrypoint-by-entrypoint permission audits.</p>
<p>ArchSig does not treat missing evidence as zero.</p>
<p>It preserves it as a hole.</p>
<p>That matters because one of the dangerous failure modes in architecture review is to read "unknown" as "fine."</p>
<p>ArchSig keeps the gap visible.</p>
<p>And the gap becomes a review proposal.</p>
<pre><code class="language-text">gap:
  - permission evidence is incomplete
  - runtime trace is missing
  - provider failure path is not covered

review proposal:
  - human reviewer: check authority boundary and failure handling
  - LLM reviewer: compare the generated output path with the declared contract
  - test reviewer: add evidence for retry and finalization behavior
</code></pre>
<p>In other words, ArchSig does not hide uncertainty.</p>
<p>It turns uncertainty into review focus.</p>
<h2>Spectral Analysis: Where Pressure Comes Back</h2>
<p>One of the most interesting parts of ArchSig is <code>ArchitectureSpectrumReport</code>.</p>
<p>Ordinary review often ends with a list of issues.</p>
<p>But in architecture, pressure does not always appear once and disappear.</p>
<p>An authority mismatch can show up in generated output persistence. From there it can move into job state. Then it can come back through retry behavior or provider failure handling.</p>
<p>Spectral analysis reads this recurrence.</p>
<pre><code class="language-text">curvature support
  -&gt; transfer edge
  -&gt; recurrent mode
  -&gt; hotspot / witness cluster
  -&gt; review focus
</code></pre>
<p>Again, the point is not a single score.</p>
<p>The interesting questions are:</p>
<ul>
<li><p>Where does pressure concentrate?</p>
</li>
<li><p>Which obstructions recur?</p>
</li>
<li><p>Which witness clusters cross multiple law axes?</p>
</li>
<li><p>Which coverage gaps block a zero reading?</p>
</li>
<li><p>Which boundary should be reviewed before a repair candidate is trusted?</p>
</li>
</ul>
<p>From a dependency graph, a workflow may look cleanly separated.</p>
<p>But spectral analysis may show remaining transfer between effect order, semantic contract, job state, and authority boundary.</p>
<p>That is not simply "there is a cycle."</p>
<p>It is a more structural reading:</p>
<blockquote>
<p>Locally separate pressures return to the same support inside the architecture object.</p>
</blockquote>
<p>ArchSig can surface that as a hotspot.</p>
<h2>Repair Candidates Are Not Automatic Fixes</h2>
<p>ArchSig can also produce repair candidates.</p>
<p>In this repository, many candidates were stopped by precondition blockers.</p>
<p>That is important.</p>
<p>Seeing pressure does not mean the system should immediately be split or rewritten.</p>
<p>If you cut one boundary, complexity may move somewhere else. Fixing authority may increase runtime effect pressure. Strengthening generated output mediation may change job state and retry paths.</p>
<p>ArchSig does not treat repair as an automatic safe edit.</p>
<p>It checks preconditions, witnesses, coverage, and transfer risk.</p>
<p>This is close to how experienced engineers review architecture:</p>
<blockquote>
<p>This looks risky.</p>
<p>But before fixing it, we need this evidence.</p>
</blockquote>
<p>That difference matters.</p>
<h2>How This Differs from Existing Tools</h2>
<p>So what exactly is different?</p>
<p>Existing tools are good at reading the current shape of code:</p>
<ul>
<li><p>Does a dependency cycle exist?</p>
</li>
<li><p>Is there a layer violation?</p>
</li>
<li><p>Is test coverage missing?</p>
</li>
<li><p>Is a security rule violated?</p>
</li>
<li><p>Is complexity too high?</p>
</li>
</ul>
<p>ArchSig is trying to read something else:</p>
<ul>
<li><p>Where are responsibility boundaries composed?</p>
</li>
<li><p>On which law axis does pressure appear?</p>
</li>
<li><p>Do two paths with the same destination preserve the same effect order?</p>
</li>
<li><p>Where do generated output, external providers, authority, and durable state intersect?</p>
</li>
<li><p>Would a repair candidate move complexity to another boundary?</p>
</li>
<li><p>Which evidence is missing, and therefore should become review focus?</p>
</li>
</ul>
<p>This is not a code quality metric.</p>
<p>It is an instrument for reading architectural state.</p>
<p>Another important point is that review can happen around the ArchMap artifact.</p>
<p>The source-grounded observation still has to be produced from real code.</p>
<p>But after that, sharing, reviewing, and comparing can happen around ArchMap and the analysis packet.</p>
<p>That matters for teams and companies that cannot freely share full codebases.</p>
<p>There is also an AI angle.</p>
<p>If you simply ask an LLM to review a large codebase, the full context will not fit.</p>
<p>Even if it does fit, it is hard to make the model consistently focus on:</p>
<ul>
<li><p>the right boundary</p>
</li>
<li><p>the missing evidence</p>
</li>
<li><p>the relevant law axis</p>
</li>
<li><p>the path where effect order differs</p>
</li>
</ul>
<p>ArchMap and ArchSig change the shape of the context.</p>
<p>Instead of handing the model all code and asking for a huge summary, you hand it Atoms, Molecules, law axes, holes, and review focus.</p>
<p>Then the model can reason around the architecture boundary that actually matters.</p>
<h2>Atom Is All You Need</h2>
<p>Trying to understand a large codebase all at once breaks down quickly.</p>
<p>There are too many files. Too many functions. Too many dependencies. Too many external providers. Too much code added by fast AI agents.</p>
<p>So start with Atoms.</p>
<pre><code class="language-text">component
relation
capability
state
effect
authority
trust_relation
contract
semantic
</code></pre>
<p>Observe them. Record them as an ArchMap. Choose a LawPolicy. Run ArchSig.</p>
<p>This lets us read meaningful architecture boundaries without pretending we can understand the entire codebase at once.</p>
<p>What AI-era architecture analysis needs is not a larger natural-language summary.</p>
<p>It needs the right unit of observation.</p>
<p>Atom is all you need.</p>
<h2>The Review Experience This Enables</h2>
<p>Future review could look like this.</p>
<p>First, generate an ArchMap delta from a codebase or pull request.</p>
<p>Then run ArchSig using the team's selected LawPolicy.</p>
<p>The reviewer no longer has to read the entire diff as a flat pile of changes.</p>
<p>They can focus on the boundaries that require design judgment.</p>
<pre><code class="language-text">This change touches:
  - authority boundary
  - generated output mediation
  - state/effect reconciliation
  - async provider job path

Measured pressure:
  - nonzero on permission coverage axis
  - positive path continuation defect on effect axis
  - architectural hole on output promotion path

Review focus:
  - entrypoint-level permission evidence
  - job finalization behavior
  - provider failure handling
  - persistence gate before domain promotion
</code></pre>
<p>AI agents can read the same packet.</p>
<p>They can implement with more than a request like:</p>
<blockquote>
<p>Just add the feature.</p>
</blockquote>
<p>They can receive architectural constraints:</p>
<blockquote>
<p>Preserve this Atom.</p>
<p>Do not cross this boundary.</p>
<p>Do not increase pressure on this law axis.</p>
<p>Do not treat these paths as equivalent; their effect order differs.</p>
</blockquote>
<p>That matters because AI agents are strongly shaped by context.</p>
<p>If you give them only the codebase, you also give them every existing shortcut.</p>
<p>If you give them the ArchMap and the ArchSig analysis packet, you also give them the architecture's structural pressure and semantic coupling.</p>
<h2>Closing</h2>
<p>ArchSig is not a tool for grading code.</p>
<p>It is an architecture analyzer for reading a codebase as an Atom map, then using AAT to surface design pressure, semantic coupling, holes, and repair preconditions.</p>
<p>It does not replace dependency graphs, lint, coverage, or security tools.</p>
<p>Those are still important.</p>
<p>But there are questions they do not answer:</p>
<ul>
<li><p>Where do authority and generated output intersect?</p>
</li>
<li><p>Where does state/effect order diverge?</p>
</li>
<li><p>Are two paths that create the same artifact really the same architecture operation?</p>
</li>
<li><p>Would this repair candidate transfer complexity to another boundary?</p>
</li>
</ul>
<p>Those are the questions ArchSig is built to make visible.</p>
<p>Start from Atoms. Record them in ArchMap. Read them through AAT. Analyze them with ArchSig.</p>
<p>That is the architecture analysis shape I wanted to introduce.</p>
<h2>What Comes Next: SFT and FieldSig</h2>
<p>We are also working on the next layer of this research.</p>
<p>AAT makes software architecture locally algebraic.</p>
<p>ArchSig becomes an observation instrument for that local algebra: it takes Atom observations and law-relative structure from a codebase and turns them into an analysis packet.</p>
<p>Beyond that is SFT, Software Field Theory.</p>
<p>SFT is about software evolution.</p>
<p>It does not only ask about the current architecture state.</p>
<p>It asks how review, AI agents, CI, operational feedback, and organizational decisions change the space of possible future software states.</p>
<p>FieldSig is the next measurement surface.</p>
<p>It reads ArchSig analysis packets and workflow evidence in order to make software evolution more computable.</p>
<p>ArchSig is the entrance.</p>
<p>That said, ArchSig is still at v0.3.1.</p>
<p>I do not want it to be judged as a finished product yet.</p>
<p>I want people to try it on many different repositories and tell us what it reads well, what it misses, and where the model feels wrong.</p>
<p>Good reads and bad reads are both valuable for the research.</p>
<p>You can get the ArchSig release bundles here:</p>
<p><a href="https://github.com/iroha1203/AlgebraicArchitectureTheoryV2/releases">AlgebraicArchitectureTheoryV2 Releases</a></p>
]]></content:encoded></item><item><title><![CDATA[Forecast Cone: A Grand Theorem for Computable Software Evolution]]></title><description><![CDATA[TL;DR
This article starts from the ForecastCone in Software Field Theory (SFT), then reads modularity, technical debt, review, governance, and learning as parts of one research program.
The goal is si]]></description><link>https://blog.iroha1203.dev/forecast-cone-a-grand-theorem-for-computable-software-evolution</link><guid isPermaLink="true">https://blog.iroha1203.dev/forecast-cone-a-grand-theorem-for-computable-software-evolution</guid><category><![CDATA[Software Engineering]]></category><category><![CDATA[architecture]]></category><category><![CDATA[Computer Science]]></category><category><![CDATA[AI]]></category><dc:creator><![CDATA[Hiroyuki Nakahata]]></dc:creator><pubDate>Fri, 22 May 2026 17:31:32 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/69ff29ebf239332df4a93c3a/0ed6963b-b4fe-4a02-870e-32a800d91e82.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<hr />
<blockquote>
<p><strong>TL;DR</strong></p>
<p>This article starts from the <code>ForecastCone</code> in Software Field Theory (SFT), then reads modularity, technical debt, review, governance, and learning as parts of one research program.</p>
<p>The goal is simple to state:</p>
<blockquote>
<p>Make it possible to see what futures a change opens, close dangerous futures early, and shape the development environment so good futures become easier to reach.</p>
</blockquote>
<p>If this theorem-shaped program works, architecture, technical debt, review, governance, and AI coding safety can all be reread around the same object: the <code>ForecastCone</code>.</p>
<p>We are also formalizing this direction in Lean by breaking concepts such as <code>ForecastCone</code> and <code>ConsequenceEnvelope</code> into small records and theorem packages.</p>
</blockquote>
<h2>For the Bigger AAT / SFT Picture</h2>
<p>I introduced the broader AAT / SFT framing here:</p>
<ul>
<li><p><a href="https://blog.iroha1203.dev/software-architecture-as-a-field">Software Architecture as a Field: Asking Better Questions About Software Evolution</a></p>
</li>
<li><p><a href="https://iroha1203.dev/aat/">Algebraic Architecture Theory</a></p>
</li>
<li><p><a href="https://iroha1203.dev/sft/">Software Field Theory</a></p>
</li>
</ul>
<h2>The Problem: Fast Code Is Not the Same as Healthy Evolution</h2>
<p>For an AI agent, the codebase itself is the largest prompt.</p>
<p>If the existing code contains many shortcuts, the agent can easily learn those patterns. If boundaries are vague, the agent will naturally add the next change in the vague place. A good structure attracts good changes. A bad structure repeatedly makes bad shortcuts look natural.</p>
<p>PRDs, issues, review policies, and CI rules do not only decide "this change." They shape what the next PR tends to look like, which modules feel natural to touch, which boundaries are easy to miss, and which shortcuts are likely to pass.</p>
<p>In vibe coding, for example, an AI agent can quickly produce a plausible implementation from an underspecified PRD.</p>
<p>A PRD that only says "make coupons usable" may turn into a small <code>if</code> inside the checkout flow. The first demo works. But if that change leaves pricing policy, refunds, usage limits, and audit logs as vague boundaries, the next PR will often build on the same shortcut.</p>
<p>The problem is not that AI writes bad code. The problem is that vague artifacts plus fast generation can open bad future paths very quickly. A bad field can be amplified by AI into larger technical debt.</p>
<p>Traditional review, CI, and metrics often look at the current diff or at outcomes that already happened. In the AI era, that is not enough.</p>
<p>The question is no longer only:</p>
<blockquote>
<p>Does this PR pass right now?</p>
</blockquote>
<p>The deeper question is:</p>
<blockquote>
<p>What future does this PRD, this agent, this codebase, and this review rule open next?</p>
</blockquote>
<p>SFT needs <code>ForecastCone</code> in order to ask that question. It gives us an object for making software evolution computable, not as a mere sequence of changes, but as a space of reachable futures.</p>
<h2>The Grand Theorem: A Fundamental Theorem of Software Evolution</h2>
<pre><code class="language-text">Modularity
  = ForecastCone descent

Technical debt
  = descent obstruction

Review
  = minimal decision-preserving envelope

Governance
  = desired-cone-preserving obstruction cutting

Learning
  = closed-loop boundary-explicit fixed point
</code></pre>
<p>In this article, I will call this theorem-shaped picture the <strong>Fundamental Theorem of Software Evolution</strong>.</p>
<p>In everyday engineering language, it says something like this:</p>
<blockquote>
<p>A good architecture boundary lets teams and AI agents work separately, then combine their changes safely later.</p>
</blockquote>
<blockquote>
<p>Technical debt is the ability to explain, with a concrete cause, why integration keeps breaking at the same place.</p>
</blockquote>
<blockquote>
<p>In review, we do not want every diff line or every possible future. We want the dangerous futures that matter for the decision.</p>
</blockquote>
<blockquote>
<p>Governance should not just add rules that stop development. It should close bad shortcuts and make good implementation paths easier to choose.</p>
</blockquote>
<blockquote>
<p>Learning means using actual PRs, incidents, and review outcomes to update the next forecast and the next rule.</p>
</blockquote>
<p>In more mathematical language:</p>
<blockquote>
<p>A good architecture boundary is not merely a static dependency cut. It is a boundary across which future evolution paths can be computed locally and glued into a global future under compatibility conditions.</p>
</blockquote>
<blockquote>
<p>Technical debt is an obstruction that appears when that gluing fails.</p>
</blockquote>
<blockquote>
<p>Review is not the act of seeing the entire ForecastCone. It is the act of seeing an envelope that preserves the distinctions needed for a decision.</p>
</blockquote>
<blockquote>
<p>Governance is a support transformation that removes bad futures while preserving desired futures.</p>
</blockquote>
<blockquote>
<p>Learning is a closed-loop update that moves a field estimate toward a boundary-explicit fixed point using PRs, incidents, reviews, and operational outcomes.</p>
</blockquote>
<p>If such a theorem can be proved, the way we reason about software evolution changes.</p>
<p>Whether an architecture is good, where technical debt sits, what a reviewer needs to see, which governance rule is effective, and how the field model should be updated next can be studied over the same computational object.</p>
<p>That object is the <code>ForecastCone</code>.</p>
<h2>ForecastCone: The Range of Reachable Futures</h2>
<p>SFT reads the development environment as a <code>field</code>.</p>
<pre><code class="language-text">codebase
  + artifacts
  + practices
  + agents
  + governance
  + feedback
  -&gt; reachable software futures
</code></pre>
<p>The range of futures reachable under that field is what SFT calls a <code>ForecastCone</code>.</p>
<img src="https://cdn.hashnode.com/uploads/covers/69ff29ebf239332df4a93c3a/f45b18b7-7431-4b69-8b85-c6e948956d61.png" alt="image" style="display:block;margin:0 auto" />

<p>A ForecastCone is not a prophecy.</p>
<p>SFT does not say:</p>
<blockquote>
<p>This PRD will definitely produce this PR.</p>
</blockquote>
<p>Instead, it asks:</p>
<blockquote>
<p>Under this field model, operation support, policy, observation boundary, and horizon, which future paths are reachable?</p>
</blockquote>
<p>Imagine an e-commerce service receives a PRD: "Add limited-time coupons."</p>
<p>That PRD does not open just one future.</p>
<ul>
<li><p>One future adds a small branch to the checkout flow.</p>
</li>
<li><p>One future adds a discount policy to the pricing service.</p>
</li>
<li><p>One future introduces an independent coupon domain.</p>
</li>
<li><p>One future expands into campaign rules and audit logs.</p>
</li>
<li><p>One future adds a database column for now and leaves cleanup for later.</p>
</li>
</ul>
<p>All of these are, in some sense, "coupon support." But they open very different next futures.</p>
<p>The ForecastCone is the object that lets us look at that difference. The same PRD can make different futures easy or hard to reach depending on the current codebase, module boundaries, past workarounds, review rules, CI, and AI agent behavior.</p>
<h2>Modularity = ForecastCone Descent</h2>
<pre><code class="language-text">Modularity
  = ForecastCone descent
</code></pre>
<p>The intuition is:</p>
<blockquote>
<p>The global future of a software system can be computed by gluing compatible local futures.</p>
</blockquote>
<p>Traditional modularity is often described in terms of APIs, dependency direction, and responsibility separation. SOLID, Layered Architecture, Clean Architecture, and Design Patterns have often been used as ways to help humans understand responsibilities, localize change, and control dependencies.</p>
<p>SFT extends that line of thought by asking:</p>
<blockquote>
<p>Do the futures glue?</p>
</blockquote>
<p>If a boundary is truly modular, we should be able to reason about the future of <code>Pricing</code> and the future of <code>Checkout</code> locally, then glue them into one checkout future under the right compatibility conditions.</p>
<p>The boundary is not just a line. It is a place where future evolution should cross without breaking.</p>
<p>In practice, following SOLID and Layered Architecture already gets us a lot of the way toward this kind of modularity.</p>
<h2>Technical Debt = Descent Obstruction</h2>
<pre><code class="language-text">Technical debt
  = descent obstruction
</code></pre>
<p>The intuition is that technical debt can be read as a failure of futures to glue.</p>
<p>local tests pass. local reviews pass. the AI proposal looks reasonable. then integration breaks.</p>
<p>SFT does not want to stop at "the design is bad." It wants to record the failure as an obstruction.</p>
<pre><code class="language-text">DescentFailure:
  missing interface invariant

Witness:
  Pricing returns a discounted total,
  but Checkout treats it as a tax-included final charge.
</code></pre>
<p>Now technical debt is no longer just a feeling.</p>
<blockquote>
<p>Each team-local change looks reasonable, but the whole system cannot combine them into a valid global future. The reason is that the boundary rule that should have been preserved was never defined.</p>
</blockquote>
<p>That is the role of the <code>Descent Obstruction Theorem</code>.</p>
<h2>Review = Minimal Decision-Preserving Envelope</h2>
<pre><code class="language-text">Review
  = minimal decision-preserving envelope
</code></pre>
<p>A ForecastCone can be large. A reviewer cannot look at every future path. But a diff alone is often too small.</p>
<p>Suppose an AI agent produces a PR like this:</p>
<pre><code class="language-text">if coupon_code.present?
  total = total - discount
end
</code></pre>
<p>The diff is small. But the reviewer does not only care about these three lines.</p>
<p>Will this add an unobserved branch to the refund path? Will usage limits collide with retry behavior? Is the tax boundary between <code>Pricing</code> and <code>Checkout</code> still undefined?</p>
<p>What we need is not the whole ForecastCone, but the part needed for a review decision. SFT calls this a <code>ConsequenceEnvelope</code>.</p>
<p>The goal for review tooling is not to show everything. It is to show the future-relevant differences needed for the decision, no more and no less.</p>
<h2>Governance = Desired-Cone-Preserving Obstruction Cutting</h2>
<pre><code class="language-text">Governance
  = desired-cone-preserving obstruction cutting
</code></pre>
<p>When a risky future appears, the simplest response is to add another rule.</p>
<blockquote>
<p>Anything coupon-related must be reviewed by a senior engineer.</p>
</blockquote>
<p>That is a guardrail. But it is often heavy, and it may not say what future it is actually closing.</p>
<p>The kind of governance SFT wants is more specific: close bad futures while preserving desired ones.</p>
<ul>
<li><p>Introduce separate <code>DiscountedTotal</code> and <code>FinalCharge</code> types across the <code>Pricing</code> / <code>Checkout</code> boundary.</p>
</li>
<li><p>Make coupon usage updates idempotent.</p>
</li>
<li><p>Require coupon invariant checks when touching the refund path.</p>
</li>
<li><p>Guide AI agents away from ad hoc discount branches under checkout and toward policy objects.</p>
</li>
</ul>
<p>We want to close the future where discount logic spreads across checkout. We still want to preserve the future where coupon policy can evolve independently.</p>
<p>That difference is what the <code>Governance Synthesis Theorem</code> is about. It is not just about adding guardrails. It is about shaping the field.</p>
<h2>Learning = Closed-Loop Boundary-Explicit Fixed Point</h2>
<pre><code class="language-text">Learning
  = closed-loop boundary-explicit fixed point
</code></pre>
<p>SFT is not a theory where we compute one ForecastCone and stop.</p>
<p>Predicted future paths are compared with actual PRs, incidents, review comments, CI failures, and runtime observations. When the forecast is wrong, that is not just a failure. It becomes evidence for updating the model.</p>
<ul>
<li><p>Was the field estimate too coarse?</p>
</li>
<li><p>Was the observation boundary too narrow?</p>
</li>
<li><p>Did the policy model fail to represent actual review behavior?</p>
</li>
<li><p>Should the unknown remainder have been made explicit?</p>
</li>
</ul>
<p>As this closed loop progresses, an SFT workbench becomes more than an analyzer. It becomes a system for continuously calibrating the field model of software evolution.</p>
<h2>Attractor Engineering and ArchSig</h2>
<p>This grand theorem becomes practical only when it connects to AAT and ArchSig.</p>
<p>AAT gives us a local theory for reading what a change preserves and what it breaks. ArchSig gives us a signature layer for observing preservation and breakage from repositories, PRs, reviews, CI, and incidents. SFT uses those observations to compute reachable futures and connect them to governance and learning.</p>
<pre><code class="language-text">AAT
  -&gt; local laws / invariants / obstruction witnesses

ArchSig
  -&gt; observed signatures / measured axes / evidence boundaries

SFT
  -&gt; ForecastCone / ConsequenceEnvelope / governance update
</code></pre>
<p>Attractor Engineering fits here as well.</p>
<p>A good architecture attracts good changes. A bad architecture repeatedly makes the same shortcut look natural.</p>
<p>In SFT language, this means the shape of reachable futures changes. Governance interventions close bad future paths, type boundaries make good local evolution more natural, and review rules make risky paths observable.</p>
<p>Attractor Engineering is the practice of shaping the ForecastCone. ArchSig is a tool for observing that change. SFT connects those observations back to the theorem family.</p>
<h2>A Sketch of a Future Development Workflow</h2>
<p>SFT is not trying to build a system that decides the future instead of developers. It is trying to make a development environment where developers, AI agents, reviews, and CI can see the future opened by a change on the same map.</p>
<p>An ambiguous PRD arrives. An AI agent quickly proposes an implementation. At the same time, a workbench sketches the futures that change seems to open and shows only the differences needed for review.</p>
<pre><code class="language-text">PRD
  -&gt; implementation candidates
  -&gt; reachable future range
  -&gt; review-relevant differences
  -&gt; review / CI / governance
  -&gt; observed outcome
  -&gt; next forecast update
</code></pre>
<p>Before reading every line of the diff, the reviewer sees which futures look risky. CI checks not only whether tests pass, but whether a risk we wanted to close is still present. The AI agent prioritizes implementation candidates that open better futures instead of copying the nearest shortcut.</p>
<p>Actual PRs, incidents, and review comments then become evidence for the next forecast.</p>
<p>That is the development workflow SFT is aiming toward.</p>
<h2>Impact on Computer Science and Software Engineering</h2>
<p>Lehman raised a core question of software evolution: long-lived software keeps changing as it adapts to its environment, and that change tends to increase complexity.</p>
<p>SFT revisits that question in the age of AI-assisted development.</p>
<p>Software engineering has long developed design and architecture theory around human cognition.</p>
<p>That makes sense. Software is complex, and if humans cannot read it, they cannot maintain it. So we developed ideas such as separation of concerns, information hiding, cohesion, coupling, layering, Clean Architecture, and bounded contexts to make complexity manageable for people.</p>
<p>That axis remains important.</p>
<p>But once AI coding agents enter the loop, the bottleneck shifts. When code can be produced faster, the question is no longer only:</p>
<blockquote>
<p>Can humans understand the current structure?</p>
</blockquote>
<p>The next question is:</p>
<blockquote>
<p>What futures does this structure make reachable?</p>
</blockquote>
<p>A good design is not only one that makes the current code easier to read. It is also one that makes good futures easier to reach and bad futures harder to reach.</p>
<p>From this point of view, architecture expands from human comprehensibility to future reachability.</p>
<ul>
<li><p>Can this boundary glue future paths locally?</p>
</li>
<li><p>Which direction does this PR make the next change more likely to take?</p>
</li>
<li><p>Does this metric preserve distinctions that matter for future evolution?</p>
</li>
<li><p>Does this review rule close bad futures while preserving good ones?</p>
</li>
<li><p>Which gluing failure does this technical debt represent?</p>
</li>
</ul>
<p>These questions already exist in practice. They are just scattered across experience, intuition, review comments, incident memory, and organizational culture.</p>
<p>SFT tries to move them toward computable theory.</p>
<p>If this direction works, software engineering moves from:</p>
<blockquote>
<p>techniques for making complexity understandable to humans</p>
</blockquote>
<p>toward:</p>
<blockquote>
<p>a science for observing, computing, and governing software evolution</p>
</blockquote>
<p>The central concepts begin to look different:</p>
<pre><code class="language-text">architecture
  -&gt; shape of reachable futures

modularity
  -&gt; descent of future paths

technical debt
  -&gt; obstruction to future gluing

review
  -&gt; minimal consequence envelope

metrics
  -&gt; cone-conservative observations

governance
  -&gt; support transformation

refactoring
  -&gt; evolutionary invariance

AI coordination
  -&gt; agentic confluence

lifecycle
  -&gt; bifurcation of repair feasibility
</code></pre>
<h2>Lean Formalization</h2>
<p>We are also formalizing this research program in Lean.</p>
<p>The point is to keep SFT vocabulary from remaining only a metaphor.</p>
<p>Terms such as <code>field</code>, <code>ForecastCone</code>, <code>ConsequenceEnvelope</code>, and <code>governance update</code> are being broken into small objects that can become records, types, and theorem packages. The big research picture gradually becomes something we can touch formally.</p>
<p>Current formalization work includes:</p>
<pre><code class="language-text">SoftwareFieldEstimate
OperationSupport / StepRelation
ForecastCone / ClockedForecastCone
ConsequenceEnvelope
FieldUpdate
</code></pre>
<p>For example, <code>ForecastCone</code> is treated as a supported path within a finite horizon. <code>ClockedForecastCone</code> introduces a shared clock and idle / stutter steps for descent. <code>ConsequenceEnvelope</code> treats review output as a projection from a cone family.</p>
<p>The final grand theorem is also being assembled from components: descent, obstruction, review, governance, calibration, and agentic confluence. Under those explicit components, the Lean-side assembly has the shape:</p>
<pre><code class="language-text">computably governed
  or
typed boundary failure
</code></pre>
<p>This formalization makes SFT a research program where we can track what is a theorem, what belongs to modeling, and what remains on the tooling or empirical side.</p>
<h2>Closing</h2>
<p>ForecastCone is not a tool for predicting the future.</p>
<p>It is a way to make reachable software futures computable under explicit modeling boundaries, operation support, policies, observation boundaries, and horizons.</p>
<p>SFT's bet is that we can rebuild central software engineering concepts from there.</p>
<pre><code class="language-text">Architecture is not only the shape of present code.
It is the shape of reachable futures.
</code></pre>
<p>Architecture is not only the shape of the code we have now.</p>
<p>It is also the shape of the futures that codebase makes reachable.</p>
]]></content:encoded></item><item><title><![CDATA[Software Architecture as a Field: Asking Better Questions About Software Evolution]]></title><description><![CDATA[AI coding agents are making us faster at writing code. That is a major shift.
But writing code faster is not the same as helping software evolve in a healthy way. The faster we stack up changes, the m]]></description><link>https://blog.iroha1203.dev/software-architecture-as-a-field</link><guid isPermaLink="true">https://blog.iroha1203.dev/software-architecture-as-a-field</guid><category><![CDATA[Computer Science]]></category><category><![CDATA[software architecture]]></category><category><![CDATA[Software Engineering]]></category><category><![CDATA[AI coding]]></category><category><![CDATA[software development]]></category><dc:creator><![CDATA[Hiroyuki Nakahata]]></dc:creator><pubDate>Wed, 13 May 2026 15:59:21 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/69ff29ebf239332df4a93c3a/e9c0e75d-e514-4137-bb37-ce4b22b8e229.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>AI coding agents are making us faster at writing code. That is a major shift.</p>
<p>But writing code faster is not the same as helping software evolve in a healthy way. The faster we stack up changes, the more important it becomes to ask what each change leaves behind in the codebase, and how it changes what can be changed next.</p>
<p>Traditional tooling usually looks at things that have already happened. Code state, PR diffs, test results, static dependencies, and runtime errors are all important objects of observation.</p>
<p>But in an era where AI coding agents can generate pull requests one after another, we need to ask one step further.</p>
<ul>
<li><p>What kinds of changes does this change make possible next?</p>
</li>
<li><p>What kinds of pull requests does this PRD make more likely?</p>
</li>
<li><p>What kinds of shortcuts does this review rule make expensive?</p>
</li>
<li><p>What observation axes should this incident leave behind?</p>
</li>
</ul>
<p>At that point, we are no longer interested only in the current state of software. We also want to reason about software evolution itself.</p>
<p>In this article, I introduce AAT, or Algebraic Architecture Theory, and SFT, or Software Field Theory, as theories for asking questions about software evolution. I will start by drawing a rough map of how AAT/SFT tries to look at software that keeps changing.</p>
<h2>Software Keeps Changing</h2>
<p>Software is not something that is completed and then slowly decays. It connects to the real world through use, and as that real world changes, the software continually drifts out of alignment.</p>
<p>Users, business processes, organizations, operations, regulations, libraries, and infrastructure all keep changing. Today, AI coding agents are also changing the development flow itself.</p>
<p>This view is not new. Lehman's laws of software evolution formalized the view that software connected to real-world problems, often called E-type software, requires continual adaptation as long as it is used. They also observed that evolving software tends to become more complex unless explicit work is done to control that complexity. And the process of evolution should not be seen as a mere sequence of changes, but as a multi-layered, multi-loop, multi-actor feedback system.</p>
<p>The important point here is not to jump immediately to a prescription. The answer is not simply "so we should refactor," or "so we should strengthen design review," or "so we should control AI more strictly."</p>
<p>The question after Lehman is a little deeper:</p>
<pre><code class="language-text">If software keeps changing,
what can we ask about that change?
</code></pre>
<p>AAT/SFT starts from this question.</p>
<h2>Decomposing the Question of "Good Design"</h2>
<p>In design discussions, we often encounter questions like these:</p>
<ul>
<li><p>Is this design good?</p>
</li>
<li><p>Is this change safe?</p>
</li>
<li><p>Should we accept this PR?</p>
</li>
<li><p>Is this AI agent's proposal risky?</p>
</li>
<li><p>Should we still repair this system, or should we migrate?</p>
</li>
</ul>
<p>All of these are natural questions. But as they stand, they are too large.</p>
<ul>
<li><p>What does "good" mean we are preserving?</p>
</li>
<li><p>What range of future changes does "safe" refer to?</p>
</li>
<li><p>On which observation axis, and through which kind of failure, is something "risky"?</p>
</li>
<li><p>When we say "we should migrate," which change paths are we closing, and which ones are we opening?</p>
</li>
</ul>
<p>In AAT/SFT, we break the question down a little further.</p>
<ul>
<li><p>What object are we cutting out for analysis?</p>
</li>
<li><p>What do we want to preserve?</p>
</li>
<li><p>Which invariant failed?</p>
</li>
<li><p>How is that failure observed?</p>
</li>
<li><p>What kinds of changes does this change make more natural next?</p>
</li>
</ul>
<p>This is not about turning design review into a checklist. It is about taking the judgments we already make implicitly in practice and reframing them as theoretical objects, so we can reason about changing software.</p>
<h2>AAT (Algebraic Architecture Theory): Reading Changes Locally</h2>
<p>AAT is a theory for reading software architecture locally.</p>
<p>Here, architecture is not treated as the entire codebase all at once. First, we cut out the object needed to answer the question at hand, and call it an <code>ArchitectureObject</code>.</p>
<p>We do not have to handle a huge codebase all at once. We can focus on one boundary, one dependency relation, one runtime interaction, or one semantic contract. What matters is making explicit what we chose as the object.</p>
<p>Next, for that object, we ask which property we want to preserve. This is called an <code>Invariant</code>.</p>
<p>What do we want to preserve? Dependency direction? A boundary? An abstraction? Substitutability? Runtime protection? Consistency of state transitions?</p>
<p>The phrase "good design" mixes together many such invariants. AAT separates them so that we can inspect them one by one.</p>
<p>If a property we wanted to preserve fails, we need evidence explaining that failure. This is called an <code>Obstruction</code>.</p>
<p>An obstruction is not just an error. It is structural evidence, a witness, showing why the property does not hold. Hidden dependencies, boundary crossings, abstraction leaks, mismatched operation orders that ought to commute, and runtime exposure can all be read as obstructions.</p>
<p>Finally, instead of collapsing those observations into a single score, we read them across multiple axes. This is the role of <code>ArchitectureSignature</code>.</p>
<p>In short, the AAT way of reading looks like this:</p>
<ul>
<li><p>Which object is this change operating on?</p>
</li>
<li><p>Which invariant do we want to preserve?</p>
</li>
<li><p>Which obstruction has appeared?</p>
</li>
<li><p>On which signature axis does it appear?</p>
</li>
<li><p>Within what boundary can we make that claim?</p>
</li>
</ul>
<p>AAT does not reduce "architectural quality" to a single number. It decomposes design judgment into object, preserved property, violation, observation axis, and boundary.</p>
<h2>What Does It Mean to See Architecture Algebraically?</h2>
<p>Why call this algebraic?</p>
<p>In AAT, we do not only look at architecture as a static diagram. We treat it as an object that can be operated on. There is an architecture object, and there are operations such as split, replace, abstract, protect, migrate, and repair. We ask what those operations preserve, what they fail to preserve, and how they compose with other operations.</p>
<p>The object of interest has this shape:</p>
<pre><code class="language-text">object
  + operation
  + preservation
  + obstruction
  + composition
</code></pre>
<p>AAT is not merely evaluation. There is an object, there are operations, there is structure that is preserved, there are obstructions where preservation fails, and there are sequences of operations. To study that structure, AAT treats architecture algebraically.</p>
<p>For example, one change may preserve dependency direction. Another change may preserve an abstraction boundary.</p>
<ul>
<li><p>If we apply those two changes in sequence, does their preservation compose?</p>
</li>
<li><p>Does an obstruction appear in the middle?</p>
</li>
<li><p>If two change paths appear to reach the same result, do they have the same signature trajectory?</p>
</li>
</ul>
<p>This is the kind of structure AAT wants to study.</p>
<h2>What Do Design Principles Preserve?</h2>
<p>From this point of view, familiar design principles in software engineering look a little different.</p>
<p>For example, SOLID is not an all-purpose design principle in AAT. It is better read mainly as a family of principles for preserving local contracts.</p>
<ul>
<li><p>SRP tries to preserve responsibility boundaries.</p>
</li>
<li><p>OCP asks for extension without breaking existing contracts.</p>
</li>
<li><p>LSP asks that an abstraction be observable in the same way regardless of which concrete implementation is substituted.</p>
</li>
<li><p>ISP tries to separate unnecessary dependencies from interfaces.</p>
</li>
<li><p>DIP tries to map dependencies on concrete details into dependencies on abstractions.</p>
</li>
</ul>
<p>What SOLID mainly deals with are invariants such as local contracts, abstractions, substitutability, and interface separation.</p>
<p>Layered Architecture deals with a different layer. What Layered Architecture tries to preserve is not so much the responsibility of each individual class, but the dependency direction of the whole system.</p>
<ul>
<li><p>Is there a ranking between upper and lower layers, and do dependencies follow that direction?</p>
</li>
<li><p>Are there dependencies that skip across layers?</p>
</li>
<li><p>Are cycles being introduced?</p>
</li>
<li><p>Is the system kept in a decomposable form?</p>
</li>
</ul>
<p>In AAT terms, SOLID mainly handles invariants in the local contract layer, while Layered Architecture handles invariants in the global structure layer.</p>
<pre><code class="language-text">SOLID
  -&gt; local contract / abstraction / substitutability

Layered Architecture
  -&gt; dependency direction / ranking / acyclicity / decomposability
</code></pre>
<p>The advantage of this classification is that we do not have to treat design principles as competing single answers.</p>
<p>A system can follow SOLID and still fail to decompose cleanly as a whole. Conversely, Layered Architecture can be preserved while individual abstractions fail to be substitutable.</p>
<p>If the invariants they preserve are different, then their failure modes are different, and the signature axes we should observe are different as well.</p>
<ul>
<li><p>For Clean Architecture, we look at boundary preservation, inward dependencies, and abstraction consistency.</p>
</li>
<li><p>For Event Sourcing, we look at replay, projection, and the relation between history and current state.</p>
</li>
<li><p>For Circuit Breaker, we look at runtime protection and failure locality.</p>
</li>
</ul>
<p>AAT does not rank design principles by asking which one is correct. It classifies which invariant family each principle carries, which obstructions it prevents, and which signature axes it appears on.</p>
<h2>The Architecture Zero-Curvature Theorem: Connecting Good Design to Measurement</h2>
<p>AAT takes this idea one step further and expresses it using the vocabulary of curvature.</p>
<p>Here, curvature means the obstruction that remains relative to a selected invariant. If there is a structure we want to preserve, and a witness that violates that structure remains, then there is curvature.</p>
<p>Intuitively, we can read it this way:</p>
<pre><code class="language-text">There is curvature
  = somewhere, the structure we wanted to preserve is violated

Curvature is zero
  = within the selected scope, no required obstruction witness remains
</code></pre>
<p>Up to this point, this is almost definitional. The important part comes next.</p>
<p>AAT is not trying to restate "good design is design without violations." Here, a <em>law</em> should be read as an explicit rule describing a property we want to preserve. The goal is to connect lawfulness with respect to selected laws to finitely observable obstruction witnesses and to the required axes of an <code>ArchitectureSignature</code> being zero.</p>
<pre><code class="language-text">lawfulness for the selected laws
  &lt;-&gt; no required obstruction witness
  &lt;-&gt; required signature axes are zero
</code></pre>
<p>AAT calls this connection the Architecture Zero-Curvature Theorem.</p>
<p>The point is that three layers become connected.</p>
<pre><code class="language-text">semantics:
  lawful under the selected law universe

witness:
  no required obstruction is finitely detected

measurement:
  required signature axes are observed as zero
</code></pre>
<p>In ordinary design reviews, phrases such as "the boundary is preserved," "responsibilities are separated," or "this is easy to extend" are often used ambiguously. The zero-curvature theorem offers a bridge: relative to which law, which witness, which observation, and which signature are we making that judgment?</p>
<p>Good design in AAT is not merely design that looks elegant. It is design where, within the selected laws, required obstruction witnesses are absent, and this can also be observed as zero on the signature axes.</p>
<p>The value of the theorem is not the slogan "if there are no violations, it is good." Its value is that it lets us move a design judgment between semantics, witnesses, and measurable signature axes.</p>
<h2>SFT (Software Field Theory): Making Software Evolution Computable</h2>
<p>If AAT asks, "What did this change preserve, and what did it fail to preserve?", SFT goes one step further and asks, "What kinds of changes does this change make more natural next?" On top of the local algebra built by AAT, SFT builds a framework for reasoning about software evolution.</p>
<p>AAT looks at the local structure of one change. It asks which object the change operates on, what it preserved, where obstructions remain, what can be said on which observation axes, and where those claims stop.</p>
<p>SFT looks at the field in which that change is placed. It asks how the change guides the next changes, which paths it makes cheaper, which paths it makes harder to see, which feedback remains as memory, and which futures become reachable.</p>
<p>In SFT, this whole context is called a <code>field</code>.</p>
<p>A <code>field</code> is not only the codebase. It includes requirements, design documents, PRDs, issues, review rules, CI, type checkers, runtime feedback, AI agent policies, and everything else that acts on the codebase. It determines which changes look natural, which changes become difficult, what becomes observable, and which feedback remains for the next judgment.</p>
<pre><code class="language-text">field
  = codebase
  + artifacts
  + practices
  + agents
  + governance
  + feedback
</code></pre>
<p>SFT is not a theory of the codebase alone. It is a theory of the whole development organization that writes requirements, designs systems, creates issues, opens PRs, reviews them, runs CI, and receives operational feedback.</p>
<p>Then what is a <code>force</code>?</p>
<p>In this article, I use force for the bundle of candidate updates that artifacts such as PRDs, specs, issues, AI proposals, and incident reports create in the field. A single PRD does not determine exactly one PR. But that PRD changes which issue decomposition feels natural, which PRs are likely to be created, and which architecture regions are likely to be touched.</p>
<pre><code class="language-text">force
  = candidate updates that an artifact gives to the field
  = changes in operation support / observation boundary / selection policy
</code></pre>
<p>Here, operation support means which operations are possible, natural, and low-cost. Observation boundary means what is visible and what is not. Selection policy means which choices are likely to pass through the process.</p>
<p>The field is the state. Force is artifact-mediated change. Future is the range of paths reachable from that field.</p>
<p>Even if two codebases have the same module graph, their next natural changes can differ if their fields differ. Past incidents, old workarounds, implicit ownership boundaries, and local patterns that an AI agent can easily mimic all change future operation support and selection policy.</p>
<h2>What Does "Computation" Mean in SFT?</h2>
<p>Computable here does not mean that we can predict the future of software as one exact outcome. It means that, under an explicit field model, operation support, observation boundary, and horizon, we can treat the range of reachable futures as a bounded problem.</p>
<p>For example, when an artifact enters a field, SFT views questions like the following as computational problems.</p>
<pre><code class="language-text">input:
  current field
  + artifact
  + operation support
  + observation axes
  + horizon

output:
  reachable path classes
  + affected architecture regions
  + changed signature axes
  + obstruction witness candidates
  + missing invariants / boundaries
  + review / CI recommendations
</code></pre>
<p>On the theoretical side, this set of reachable paths is called a <code>ForecastCone</code>. On the practical side, the readable report summarizing it is called a <code>ConsequenceEnvelope</code>.</p>
<p>The important point is that SFT does not claim, "this PRD will necessarily produce this PR." What SFT wants to see is which paths become closer in this field, which paths become farther away, and which obstructions become visible.</p>
<h2>PRDs Shape Future PRs</h2>
<p>The intuition of SFT is easier to grasp if we look at the flow from PRD to PR.</p>
<p>A PRD is not merely a requirements document. It is an artifact that partially shapes the form of future pull requests. Even for the same request, "add this feature," the PRs that become likely differ depending on whether the PRD states boundaries, responsibilities, and properties that should be observed. And in many cases, PRDs are written by people who do not directly write code, such as product managers, designers, and domain experts. Non-engineers also apply force to the codebase through artifacts.</p>
<pre><code class="language-text">PRD
  -&gt; possible issue decomposition
  -&gt; possible PR shapes
  -&gt; possible architecture changes
  -&gt; possible signature changes
</code></pre>
<p>In SFT, we ask what force this PRD applies to the codebase and what kind of <code>ForecastCone</code> it opens. But that cone does not represent one predetermined future. It is closer to the cone of uncertainty in a weather forecast: it represents the range of futures that may become reachable.</p>
<ul>
<li><p>What kinds of PRs are likely to emerge from this PRD?</p>
</li>
<li><p>Which architecture regions are those PRs likely to affect?</p>
</li>
<li><p>Which invariants might they preserve, and which obstructions might they create?</p>
</li>
<li><p>Within the <code>ForecastCone</code>, which future paths become closer and which become farther away?</p>
</li>
</ul>
<p>For this reason, SFT asks questions in the following form:</p>
<ul>
<li><p>Which <code>ForecastCone</code> opens in this field?</p>
</li>
<li><p>Which paths become natural, and which paths move farther away?</p>
</li>
<li><p>What is observable, and what remains unobserved?</p>
</li>
</ul>
<p>In this sense, SFT is a theory that tries to treat software evolution as a computable object.</p>
<h2>Conway's Law: Systems Reflect Organizational Communication Structures</h2>
<p>Conway's Law can be read naturally in the vocabulary of SFT. As is well known, Conway's Law is usually described as the empirical rule that the design of a system reflects the communication structure of the organization that built it.</p>
<p>In SFT, we read this not merely as a metaphor, but as a phenomenon where an organization field shapes the architecture future.</p>
<p>The organizational structure does not directly command the codebase to have a particular architecture. But team boundaries, ownership, review routes, approval flows, on-call boundaries, and issue decomposition all change which modifications look natural, which PRs are low-cost, and which changes are likely to pass review.</p>
<pre><code class="language-text">organization structure
  -&gt; communication paths
  -&gt; ownership boundaries
  -&gt; issue decomposition
  -&gt; PR shape
  -&gt; operation support
  -&gt; architecture future
</code></pre>
<p>Organizational structure changes operation support, and operation support changes day-to-day design changes. Through repetition, that pattern settles into the codebase as architecture.</p>
<p>For example, if an organization is split into Frontend Team, Backend Team, Data Team, and Infra Team, issues and PRs are likely to follow those boundaries. As a result, the architecture is also likely to split along those boundaries. On the other hand, if the organization is structured around product capabilities such as Search, Checkout, and Billing, changes that preserve those capability boundaries become more natural.</p>
<p>In SFT terms, Conway's Law can be read as follows:</p>
<pre><code class="language-text">organization field
  -&gt; recurrent PR shape
  -&gt; recurrent architecture operation
  -&gt; architecture structure
</code></pre>
<p>If we want a desirable architecture to become a natural future, we need to design the organization field so that changes preserving that architecture are low-cost and repeatable.</p>
<p>Conway's Law is not a story about system structure accidentally resembling organization structure. It is a story about organizations shaping the ease of day-to-day changes, and the repetition of those changes settling into architecture.</p>
<h2>ArchSig: A Lens for Observation</h2>
<p>To connect AAT/SFT to real development, we need an observation layer. ArchSig is the concept for that layer.</p>
<ul>
<li><p>AAT turns architecture into a local algebra.</p>
</li>
<li><p>ArchSig makes architecture observable.</p>
</li>
<li><p>SFT makes software evolution computable.</p>
</li>
</ul>
<p>ArchSig is a lens for reading artifacts such as codebases, PRs, issues, reviews, and incident traces. It asks which signature axes changed, which obstructions appeared, and what becomes input to the next field update.</p>
<p>I plan to discuss ArchSig in a separate article. For now, I only want to emphasize that AAT/SFT is meant to connect to observation and tooling.</p>
<h2>Attractor Engineering: Designing Fields Where Good Changes Become Natural</h2>
<p>One especially important idea in SFT is attractor engineering.</p>
<p>Here, an attractor is a direction of change that becomes repeatedly likely within a field. Good design decisions, good abstractions, good tests, good review rules, and good PRDs make it easier for the next good change to follow. Conversely, easy shortcuts, ambiguous responsibilities, broken boundaries, and invisible runtime coupling make the next easy shortcut more likely.</p>
<p>A codebase has a bias in how it is likely to be changed next. SFT treats that bias as a property of the field.</p>
<p>Attractor engineering means designing that bias.</p>
<p>Arrange the field so that good changes are:</p>
<ul>
<li><p>easy to find,</p>
</li>
<li><p>easy to write,</p>
</li>
<li><p>easy to review,</p>
</li>
<li><p>protected by CI,</p>
</li>
<li><p>and updated by operational feedback.</p>
</li>
</ul>
<p>This is somewhat different from the idea of simply placing strong restrictions on AI agents. Of course, prohibitions and guardrails are necessary. But by themselves, they only keep piling rules on top of an undesirable field.</p>
<p>The goal of attractor engineering is to build a field where good paths are naturally selected, and bad shortcuts are expensive, observable, and detectable in review.</p>
<p>This becomes even more important in the age of AI coding agents. AI agents tend to choose the path that looks most natural from the existing codebase and surrounding artifacts. In this sense, a codebase is not only an implementation; it is also a prompt for AI agents. If the field is undesirable, AI agents can amplify bad local patterns quickly. If the field is well designed, AI agents can also amplify good structure quickly.</p>
<p>In SFT terms, attractor engineering means designing future operation support.</p>
<ul>
<li><p>Which changes look natural?</p>
</li>
<li><p>Which changes become low-cost?</p>
</li>
<li><p>Which violations become observable?</p>
</li>
<li><p>Which feedback remains in the next field?</p>
</li>
</ul>
<p>This question is not merely quality control. It is the design of the direction of software evolution.</p>
<h2>What AAT/SFT Is Trying to Do</h2>
<p>AAT/SFT is trying to do three main things.</p>
<p>The first is to change the question.</p>
<pre><code class="language-text">Is this design good?
</code></pre>
<p>Instead of trying to answer that question as-is, we decompose it:</p>
<ul>
<li><p>What object are we talking about?</p>
</li>
<li><p>What do we want to preserve?</p>
</li>
<li><p>Which invariant failed?</p>
</li>
<li><p>What are we failing to observe?</p>
</li>
<li><p>What kinds of changes does this change make more natural next?</p>
</li>
</ul>
<p>This is not meant to make practical design judgment lighter. It is meant to make implicit judgment more tractable.</p>
<p>The second is to formalize the core of the theory in the Lean theorem prover.</p>
<p>We do not need to formalize all of AAT/SFT at once. The first step is to make the foundations of AAT, such as local algebra, invariants, obstructions, signatures, and the zero-curvature theorem, verifiable in Lean.</p>
<pre><code class="language-text">architecture object
  + operation
  + invariant
  + obstruction
  + signature
  + theorem boundary
</code></pre>
<p>Once this core is formalized, we can mechanically check what follows from which assumptions, and where the consequences stop.</p>
<p>The third is to connect the theory to practical tools and methods.</p>
<p>AAT reads design judgment in terms of invariants and obstructions. ArchSig makes those observable from codebases, PRs, issues, reviews, and incident traces. SFT uses those observations to reason about which <code>ForecastCone</code> opens and which paths become natural.</p>
<pre><code class="language-text">AAT
  -&gt; theoretical core

Lean
  -&gt; formalization of the core

ArchSig / tooling
  -&gt; observation of real artifacts

SFT
  -&gt; computation of software evolution
</code></pre>
<p>The eventual goal is to have tools for observing, questioning, and improving requirements, design, issues, PRs, review, CI, operational feedback, and the behavior of AI agents on top of the same theory.</p>
<h2>Closing</h2>
<p>Lehman saw that software keeps changing. Software connected to the real world continues to drift from its environment as long as it is used. It is changed to close that gap, and through being changed, it gains complexity and creates new feedback loops.</p>
<p>AAT/SFT sits on the same line of concern.</p>
<p>AAT asks about the local structure of that change. What object are we looking at, what do we preserve, which invariant failed, and on which axes can we observe it?</p>
<p>SFT asks how that change shapes the next change. It asks which futures are pulled closer, and which futures are pushed farther away, by requirements, design, issues, PRs, review, CI, operational feedback, and AI agents.</p>
<p>A good theory does not necessarily give all the answers immediately. But it gives us better questions, and it tells us how far those questions can be answered.</p>
<p>As Lehman showed, software keeps changing. If so, we should not merely accept that change. We should make it something we can ask about.</p>
<p>AAT/SFT is an attempt to define questions for software that keeps changing.</p>
<hr />
<h2>Further Reading</h2>
<p>If you want to read more about the mathematical definitions of AAT/SFT, theorem boundaries, and the connection to SFT, see the following primary documents:</p>
<ul>
<li><p><a href="https://iroha1203.dev/aat/">Algebraic Architecture Theory</a></p>
</li>
<li><p><a href="https://iroha1203.dev/sft/">Software Field Theory</a></p>
</li>
</ul>
<p>For a practical example of attractor engineering, and as an example of a multi-agent system for automatically maintaining a codebase, you may also want to read the article on Gotanda Style:</p>
<ul>
<li><a href="https://blog.iroha1203.dev/ai-agents-don-t-need-meetings-gotanda-style-for-stigmergic-software-maintenance">AI Agents Don't Need Meetings: Gotanda Style for Stigmergic Software Maintenance</a></li>
</ul>
]]></content:encoded></item><item><title><![CDATA[AI Agents Don't Need Meetings: Gotanda Style for Stigmergic Software Maintenance]]></title><description><![CDATA[Most multi-agent systems make agents talk to each other.
We tried something different: our agents do not talk to each other at all.
They leave traces in a shared environment. Other agents read those t]]></description><link>https://blog.iroha1203.dev/ai-agents-don-t-need-meetings-gotanda-style-for-stigmergic-software-maintenance</link><guid isPermaLink="true">https://blog.iroha1203.dev/ai-agents-don-t-need-meetings-gotanda-style-for-stigmergic-software-maintenance</guid><category><![CDATA[AI]]></category><category><![CDATA[ai agents]]></category><category><![CDATA[Software Engineering]]></category><category><![CDATA[automation]]></category><category><![CDATA[multi-agent]]></category><dc:creator><![CDATA[Hiroyuki Nakahata]]></dc:creator><pubDate>Mon, 11 May 2026 12:51:46 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/69ff29ebf239332df4a93c3a/3fd46f71-1628-493b-8c5b-d428fa1f5fc1.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Most multi-agent systems make agents talk to each other.</p>
<p>We tried something different: our agents do not talk to each other at all.</p>
<p>They leave traces in a shared environment. Other agents read those traces later, combine them with new evidence, turn the right ones into GitHub issues, and sometimes produce pull requests.</p>
<p>This pattern is called <strong>stigmergy</strong>: coordination through changes left in the environment rather than direct communication between individuals. In this article, I will call our version of the pattern <strong>Gotanda Style</strong>.</p>
<p>This is not just a thought experiment. We already use this workflow to maintain a Python repository with roughly 200,000 lines of code. Sentry alerts deposit "pheromones." Aggregated signals become issues. Some of those issues are small and well-scoped enough for an implementation agent to turn into pull requests.</p>
<p>The result is a multi-agent maintenance loop that is asynchronous, leaderless, token-efficient, and built for real software operations rather than demo-friendly agent conversations.</p>
<h2>TL;DR</h2>
<ul>
<li><p>Faster coding agents increase maintenance pressure: production errors, performance regressions, test gaps, and architectural drift all grow with change velocity.</p>
</li>
<li><p>Gotanda Style coordinates agents through a shared pheromone field instead of direct agent-to-agent conversation.</p>
</li>
<li><p>Observer agents deposit structured positive and negative signals; an integrator turns only the right clusters of evidence into issues.</p>
</li>
<li><p>This lets maintenance agents run asynchronously, spend fewer tokens, avoid supervisor bottlenecks, and route only safe, well-scoped work to implementation agents.</p>
</li>
</ul>
<h2>Why maintenance matters more as coding agents get better</h2>
<p>AI coding agents are making it much faster to write code.</p>
<p>That is a real shift. But when code creation gets faster, the maintenance burden grows too. More code reaches production in less time. More changes need monitoring, debugging, refactoring, testing, and design review.</p>
<p>It is like doubling the speed of a car. Higher speed is useful, but only if the tires, brakes, suspension, and safety systems can handle it. Otherwise, speed just turns small failures into bigger ones.</p>
<p>In AI-assisted development, speeding up implementation is not enough. The maintenance system has to scale with the new velocity.</p>
<p>Software work is not just "writing code." Over the long run, a large share of the cost comes from work like this:</p>
<ul>
<li><p>Investigating production errors</p>
</li>
<li><p>Detecting performance regressions</p>
</li>
<li><p>Filling test gaps</p>
</li>
<li><p>Finding architectural drift</p>
</li>
<li><p>Repairing broken boundaries between modules</p>
</li>
<li><p>Converting small improvement opportunities into reviewable pull requests</p>
</li>
</ul>
<p>The faster AI helps us produce code, the more important these loops become.</p>
<p>If we want AI-driven development to scale, we need more than automated code generation. We need agentic maintenance.</p>
<p>Gotanda Style came from that problem. The goal is not to hand all product or architecture decisions to AI. The goal is to use multiple agents to continuously support the parts of software maintenance that are repetitive, observable, and evidence-driven.</p>
<h2>Why conversational multi-agent systems are hard to scale</h2>
<p>When people hear "multi-agent system," they often imagine a group of specialist agents solving a problem by talking to each other.</p>
<p>A typical setup looks like this:</p>
<ul>
<li><p>A planner agent decomposes the task</p>
</li>
<li><p>A research agent investigates context</p>
</li>
<li><p>A coding agent implements the change</p>
</li>
<li><p>A reviewer agent reviews the result</p>
</li>
<li><p>A supervisor agent decides what happens next</p>
</li>
</ul>
<p>This can work well for small tasks. Many current agent frameworks are built around patterns like supervisors, handoffs, routers, and subagents.</p>
<p>But software maintenance is different. It is continuous, asynchronous, broad in scope, and tied to production evidence. For that kind of work, conversation-centered coordination has several problems:</p>
<ol>
<li><p>As the number of agents grows, the communication graph gets harder to manage.</p>
</li>
<li><p>Agents need to read each other's context, which increases token usage.</p>
</li>
<li><p>The supervisor becomes an information bottleneck and a potential single point of failure.</p>
</li>
<li><p>In a large codebase, having every agent read the same context is wasteful.</p>
</li>
<li><p>Temporary opinions and noisy reasoning can stay in the chat history and bias later decisions.</p>
</li>
</ol>
<p>Human organizations have the same failure mode. A team that keeps everyone in every meeting slows down as it grows.</p>
<p>The same thing happens with LLM agents. After a certain point, coordination itself becomes the cost.</p>
<h2>Gotanda Style: coordinate through the environment, not through chat</h2>
<p>The core rule of Gotanda Style is simple:</p>
<blockquote>
<p>Agents do not talk to each other. They leave traces in a shared environment.</p>
</blockquote>
<p>Each agent observes only its own slice of the system. When it finds a signal, it writes that signal into a shared environment.</p>
<p>We call that shared environment the <strong>pheromone field</strong>.</p>
<p>For example:</p>
<ul>
<li><p><strong>Sentry worker</strong>: observes runtime errors</p>
</li>
<li><p><strong>Datadog worker</strong>: observes slow requests, slow SQL, 5xx spikes, and cost spikes</p>
</li>
<li><p><strong>Quality worker</strong>: looks for layering violations, missing exception handling, test gaps, and API contract drift</p>
</li>
<li><p><strong>Refactor worker</strong>: reads the pheromone field, combines related signals, and creates issues</p>
</li>
<li><p><strong>Code worker</strong>: picks up Gotanda-labeled issues and opens pull requests</p>
</li>
</ul>
<p>The important part is that observer workers do not directly create a flood of GitHub issues.</p>
<p>The Sentry worker leaves a trace that says, in effect, "this file is involved in a production error." The Datadog worker leaves a trace that says, "this endpoint is slow." The Quality worker leaves a trace that says, "this function may have an error-handling problem."</p>
<p>They do not negotiate with each other in chat.</p>
<p>Later, the Refactor worker reads the accumulated pheromone field and decides which clusters of evidence are worth turning into issues.</p>
<p>The workflow has three stages:</p>
<ol>
<li><p><strong>Observer</strong>: observes the outside world or the codebase and deposits pheromones</p>
</li>
<li><p><strong>Integrator</strong>: reads the pheromone field, merges related signals, and creates issues</p>
</li>
<li><p><strong>Implementer</strong>: turns safe, well-scoped issues into pull requests</p>
</li>
</ol>
<pre><code class="language-text">Sentry worker   ----+
Datadog worker  ----+--&gt;  Pheromone field  --&gt;  Refactor worker  --&gt;  GitHub Issue  --&gt;  Code worker  --&gt;  Pull Request
Quality worker  ----+
</code></pre>
<h2>What is a pheromone?</h2>
<p>A pheromone is a structured signal that an agent leaves in the shared environment.</p>
<p>A minimal model looks like this:</p>
<pre><code class="language-text">(scope, location, worker, strength, half_life, metadata)
</code></pre>
<p>Each field has a specific role:</p>
<ul>
<li><p><code>scope</code>: the granularity of the signal, such as <code>file</code>, <code>function</code>, <code>endpoint</code>, or <code>sql</code></p>
</li>
<li><p><code>location</code>: the actual target, such as a file path, function name, API route, or SQL fingerprint</p>
</li>
<li><p><code>worker</code>: the agent that deposited the signal</p>
</li>
<li><p><code>strength</code>: how strong the signal is</p>
</li>
<li><p><code>half_life</code>: how quickly the signal decays</p>
</li>
<li><p><code>metadata</code>: supporting details such as error category, environment, evidence, or classification</p>
</li>
</ul>
<p>If the Sentry worker finds a production error, it might deposit a pheromone like this:</p>
<pre><code class="language-json">{
  "scope": "file",
  "location": "app/services/invoices.py",
  "worker": "sentry-worker",
  "strength": 2.0,
  "half_life_days": 14,
  "metadata": {
    "category": "runtime_error",
    "environment": "production",
    "error_type": "IntegrityError"
  }
}
</code></pre>
<p>If the Quality worker finds a test gap in the same file, it might deposit a separate pheromone:</p>
<pre><code class="language-json">{
  "scope": "file",
  "location": "app/services/invoices.py",
  "worker": "quality-worker",
  "strength": 1.0,
  "half_life_days": 21,
  "metadata": {
    "category": "test_gap",
    "severity": "medium"
  }
}
</code></pre>
<p>The Refactor worker does not make decisions from a single deposit in isolation. It reads the aggregated field.</p>
<p>When multiple workers deposit signals around the same location, that location becomes a hotspot worth inspecting.</p>
<h2>Positive and negative pheromones</h2>
<p>In Gotanda Style, pheromones are not always positive.</p>
<p>A positive pheromone is an attraction signal: "look here."</p>
<p>A negative pheromone is an inhibition signal: "we looked at this, and for now we should not pursue it."</p>
<p>For example, if the Refactor worker investigates a candidate and decides that it is an accepted design exception, it can deposit a negative pheromone:</p>
<pre><code class="language-json">{
  "scope": "fingerprint",
  "location": "layering_violation:abc123",
  "worker": "refactor-worker",
  "strength": -1.5,
  "half_life_days": 60,
  "metadata": {
    "reason": "accepted design exception"
  }
}
</code></pre>
<p>This prevents the same candidate from becoming a new issue every time a worker sees it.</p>
<p>But the negative pheromone is not permanent. It decays over time. If Sentry or Datadog later deposits a strong signal in the same area, the candidate can resurface.</p>
<p>That property matters in maintenance work. "Won't fix right now" is not the same thing as "ignore forever."</p>
<h2>Why this scales</h2>
<h3>1. More agents do not create a communication explosion</h3>
<p>In a conversational design, every new agent raises a coordination question: who needs to talk to whom, when, and with how much context?</p>
<p>In Gotanda Style, agents do not need to know about each other. They only need to write signals into the shared environment using a known schema.</p>
<p>Adding a new worker is mostly a contract question: what kind of pheromone does it deposit?</p>
<p>That makes the system plugin-like. If you want a Security worker, it deposits security signals. If you want a Performance worker, it deposits performance signals. The Refactor worker can read both as part of the same field.</p>
<h3>2. Large codebases can be explored more efficiently</h3>
<p>In a large codebase, reading every file on every run is not realistic.</p>
<p>The real question is how to spend a limited exploration budget.</p>
<p>With a pheromone field, exploration is not purely random, and it is not limited to "recently changed files" either. A worker can prioritize:</p>
<ul>
<li><p>Recently changed files</p>
</li>
<li><p>A random sample of files</p>
</li>
<li><p>Hotspots from Sentry or Datadog</p>
</li>
<li><p>Areas with strong negative pheromones, at a lower priority</p>
</li>
<li><p>Locations where multiple workers have deposited signals</p>
</li>
</ul>
<p>The search budget adapts to observed evidence.</p>
<p>That is a good fit for continuous AI maintenance over a large codebase.</p>
<h3>3. It is token-efficient</h3>
<p>Because agents do not have long conversations with each other, they do not need to read each other's full reasoning traces or chat histories.</p>
<p>What gets shared is a small structured signal:</p>
<pre><code class="language-json">{
  "scope": "endpoint",
  "location": "GET /api/reports",
  "worker": "datadog-worker",
  "strength": 1.2,
  "metadata": {
    "category": "slow_request",
    "p95_ms": 1800
  }
}
</code></pre>
<p>That is far cheaper than thousands of tokens of conversation.</p>
<p>Only when the Integrator needs to make a decision does it dig into the code, logs, issues, and previous decisions.</p>
<h3>4. It runs asynchronously</h3>
<p>Workers do not need to run at the same time.</p>
<p>The Sentry worker can run every 10 minutes. The Datadog worker can run once a day. The Quality worker can run overnight. The Code worker can poll for labeled issues every few minutes.</p>
<p>Each worker observes the environment and deposits pheromones at its own pace.</p>
<p>That is useful in production. External systems like GitHub, CI, Sentry, and Datadog all have different rate limits, failure modes, and latency profiles. Independent workers localize failures instead of turning every dependency hiccup into a global coordination problem.</p>
<h3>5. Noise can be handled over time</h3>
<p>LLM agents are noisy. A weak signal from one worker should not always become an issue.</p>
<p>In Gotanda Style, pheromones decay.</p>
<p>A one-off weak signal fades away. Signals that recur, signals that come from multiple workers, and signals tied to production impact remain stronger.</p>
<p>This helps the system prioritize persistent problems over one-time noise.</p>
<h2>A simple sum is not enough</h2>
<p>There is an important catch.</p>
<p>If you simply add pheromones together, you can lose information.</p>
<p>Imagine a location has these two signals:</p>
<pre><code class="language-text">sentry-worker: +2.0
refactor-worker: -2.0
</code></pre>
<p>The simple sum is zero.</p>
<p>But this is not the same as a location where nothing is happening.</p>
<p>It means something closer to: "there is a production error here, but there is also a previous won't-fix decision."</p>
<p>If we treat both cases as zero, we miss an important conflict.</p>
<p>So Gotanda Style tracks positive mass, negative mass, total variation, and conflict separately:</p>
<ul>
<li><p><code>current_strength</code>: net strength</p>
</li>
<li><p><code>positive_strength</code>: total positive signal</p>
</li>
<li><p><code>negative_mass</code>: total negative signal</p>
</li>
<li><p><code>total_variation</code>: total signal without cancellation</p>
</li>
<li><p><code>conflict_ratio</code>: how strongly positive and negative signals disagree</p>
</li>
</ul>
<p>The practical rule is simple:</p>
<p><strong>Distinguish silence from conflict.</strong></p>
<p>This lets the Refactor worker make better decisions:</p>
<ul>
<li><p>Strong positive signal only: possibly safe to turn into an implementation issue</p>
</li>
<li><p>Strong negative signal only: do not pursue right now</p>
</li>
<li><p>Strong positive and negative conflict: likely needs human review</p>
</li>
<li><p>No signal: lower exploration priority</p>
</li>
</ul>
<h2>How issues are created in Gotanda Style</h2>
<p>Observer workers generally should not create issues directly.</p>
<p>They usually do not have enough context at observation time.</p>
<p>The Sentry worker knows about an error, but it may not know whether the fix is local, architectural, already accepted, or intentionally deferred.</p>
<p>The Datadog worker knows about a slow SQL query, but it may not know whether the query is unacceptable, part of a tolerated batch job, or tied to a product requirement.</p>
<p>The Quality worker may find something that looks like a layering violation, but it may be an intentional design exception.</p>
<p>So observer workers deposit pheromones. Issue creation belongs to the Integrator.</p>
<p>The Integrator reads multiple pheromones, the current code, existing issues, and previous won't-fix decisions. Then it classifies the candidate:</p>
<table>
<thead>
<tr>
<th>Class</th>
<th>Meaning</th>
<th>Destination</th>
</tr>
</thead>
<tbody><tr>
<td>A</td>
<td>No issue, or known accepted exception</td>
<td>Do not file</td>
</tr>
<tr>
<td>B1</td>
<td>Safe local fix</td>
<td>Code worker</td>
</tr>
<tr>
<td>B2</td>
<td>Design decision needed</td>
<td>Human</td>
</tr>
<tr>
<td>C</td>
<td>Cause unclear; investigation needed</td>
<td>Human</td>
</tr>
</tbody></table>
<p>Only B1 goes to the Code worker.</p>
<p>That boundary is intentional. Issues sent to an implementation agent should have a clear intent, a limited scope, and enough evidence that a reviewer can trace the pull request back to the original problem.</p>
<p>Large design decisions stay with humans. Once a human decides the direction, the local follow-up work can be split into smaller issues for the Code worker.</p>
<h2>What is already working</h2>
<p>Gotanda Style is not just a research sketch.</p>
<p>We are using this pattern on a Python repository with about 200,000 lines of code.</p>
<p>The current loop works like this:</p>
<ol>
<li><p>The Sentry worker detects a production alert.</p>
</li>
<li><p>It classifies the alert and deposits pheromones for cases that appear to need either a local fix or deeper remediation.</p>
</li>
<li><p>The Refactor worker reads the pheromone field and combines the alert with other observations and previous decisions.</p>
</li>
<li><p>It creates improvement issues at a level of detail that can be implemented automatically.</p>
</li>
<li><p>The Code worker reads the issue, creates a branch, makes the change, and opens a pull request.</p>
</li>
</ol>
<p>Not every alert becomes an automated fix. Anything that needs a design decision, has an unclear cause, or has a large blast radius is routed to a human.</p>
<p>But the closed loop from Sentry alert to pheromone deposit to issue to improvement PR is already running in practice.</p>
<p>That is the key point: Gotanda Style did not come from abstract multi-agent theory. It came from operating and maintaining a real large codebase.</p>
<h2>What is new here?</h2>
<p>Multi-agent systems are not new.</p>
<p>There are many existing patterns: supervisors, handoffs, routers, blackboards, shared memory, and more.</p>
<p>The interesting part of Gotanda Style is the combination:</p>
<ol>
<li><p>It is specialized for software maintenance.</p>
</li>
<li><p>It separates observation, integration, and implementation.</p>
</li>
<li><p>Agents do not talk to each other directly.</p>
</li>
<li><p>Agents deposit positive and negative pheromones into a shared environment.</p>
</li>
<li><p>Pheromones decay over time.</p>
</li>
<li><p>Hotspots and conflicts across observers drive issue creation.</p>
</li>
<li><p>Only safe, automatable issues are passed to the Code worker.</p>
</li>
</ol>
<p>This is not a general-purpose chatty multi-agent system.</p>
<p>It is an asynchronous, leaderless, token-efficient workflow pattern for continuously maintaining a large codebase.</p>
<h2>The hard parts</h2>
<p>This pattern has real challenges.</p>
<p>The biggest one is the quality of the pheromone field.</p>
<p>If the field fills with noise, the whole system follows that noise. If inhibition is too strong, the system misses real problems.</p>
<p>Several parts are especially tricky.</p>
<h3>Normalizing locations</h3>
<p>If workers refer to the same place using different location strings, signals will not aggregate.</p>
<p>For example, these may all refer to the same API:</p>
<pre><code class="language-text">GET /api/users/{id}
/api/users/:id
app/api/users.py:get_user
</code></pre>
<p>Deciding when these should collapse into one location is an important design problem.</p>
<h3>Calibrating strength</h3>
<p>A Sentry production error with strength <code>+2.0</code> should not carry the same meaning as a low-confidence Quality worker concern with strength <code>+0.5</code>.</p>
<p>The system needs ongoing calibration across worker reliability, category severity, environment, and production impact.</p>
<h3>Defining negative pheromone semantics</h3>
<p>Negative pheromones are useful, but they are also dangerous.</p>
<p>"Do not pursue right now" is different from "ignore forever."</p>
<p>Negative pheromones need reasons, fingerprints, half-lives, and resurfacing conditions.</p>
<h3>Auditability</h3>
<p>As automation increases, the system has to explain itself.</p>
<p>Operators need to trace why an issue was created, why a pull request was opened, which worker run contributed which signal, and which previous decisions were considered.</p>
<p>Without that audit trail, the workflow will not be trusted in production.</p>
<h2>What we want to improve next</h2>
<p>Gotanda Style is still evolving.</p>
<p>The next areas we care about most are:</p>
<ul>
<li><p>Reliability weights per worker</p>
</li>
<li><p>Category-specific weights</p>
</li>
<li><p>Location alias normalization</p>
</li>
<li><p>Audit logs tied to <code>run_id</code></p>
</li>
<li><p>Self-stop conditions</p>
</li>
<li><p>Canary operation</p>
</li>
<li><p>Human-in-the-loop boundaries</p>
</li>
<li><p>Reallocating exploration based on the pheromone field</p>
</li>
</ul>
<p>The last point is especially important. The field should not only be something agents read when making issues. It should also shape what agents inspect next.</p>
<p>For example, instead of letting the Quality worker explore completely at random, it can divide its budget like this:</p>
<ul>
<li><p>Recent files: 40%</p>
</li>
<li><p>Random files: 30%</p>
</li>
<li><p>Pheromone hotspots: 20%</p>
</li>
<li><p>Cooling follow-up: 10%</p>
</li>
</ul>
<p>This keeps some randomness while adapting to what the system has already observed.</p>
<h2>Gotanda Style as attractor engineering</h2>
<p>Another way to describe Gotanda Style is practical attractor engineering for a codebase.</p>
<p>By "attractor," I mean the structure or state that a codebase naturally drifts toward as changes accumulate.</p>
<p>In a codebase with clear boundaries, good types, good tests, and good examples, the next change is more likely to fit the same pattern. In a codebase with a giant <code>common</code> module, vague services, overly convenient helpers, and bad nearby examples, changes tend to drift in that direction.</p>
<p>AI coding agents amplify this dynamic.</p>
<p>AI does not write code in a vacuum. It reads existing code, neighboring files, names, tests, previous implementations, and docs. The whole codebase becomes part of the prompt.</p>
<p>If the codebase contains bad local grammar, AI will often reproduce it as the natural answer. When development speeds up, the drift toward bad attractors can speed up too.</p>
<p>Attractor engineering means shaping where future changes are likely to land.</p>
<p>Gotanda Style uses the pheromone field to observe signals like:</p>
<ul>
<li><p>Which files or endpoints accumulate production errors</p>
</li>
<li><p>Where performance regressions show up</p>
</li>
<li><p>Where tests are missing or boundaries are weakening</p>
</li>
<li><p>Which locations are repeatedly flagged by multiple workers</p>
</li>
<li><p>Which candidates were previously marked won't-fix</p>
</li>
<li><p>Where positive and negative signals are in conflict</p>
</li>
</ul>
<p>This is more than alert aggregation.</p>
<p>It is a way to observe where the codebase is drifting, identify areas that are becoming bad basins, and use issues and repair PRs to change the trajectory.</p>
<pre><code class="language-text">codebase field
  -&gt; AI / human PR force
  -&gt; pheromone observation
  -&gt; issue / repair PR
  -&gt; updated codebase field
</code></pre>
<p>In that sense, Gotanda Style is not a system for making AI write more code.</p>
<p>It is an operating model for directing AI's increased change velocity toward a codebase that remains maintainable and observable.</p>
<h2>A mathematical view</h2>
<p>Intuitively, the pheromone field is a set of weighted signals per <code>(scope, location)</code>.</p>
<p>More formally, each worker deposits a signed weight at <code>(worker, scope, location)</code>, and the system aggregates those weights by <code>(scope, location)</code>.</p>
<p>Positive weights attract attention. Negative weights inhibit attention.</p>
<p>Over time, each weight decays exponentially:</p>
<pre><code class="language-text">current = strength * 0.5 ^ (elapsed / half_life)
</code></pre>
<p>A simple sum can hide conflicts because positive and negative signals cancel each other out.</p>
<p>So the system keeps positive mass, negative mass, total variation, and conflict ratio:</p>
<pre><code class="language-text">net = positive - negative
total_variation = positive + negative
conflict_ratio = 1 - abs(net) / total_variation
</code></pre>
<p>These additional values let the system distinguish a quiet location from a contested one.</p>
<h2>Conclusion</h2>
<p>In LLM multi-agent systems, coordination is one of the core design problems.</p>
<p>Conversation-based coordination is easy to understand, but as the number of agents, the size of the codebase, and the duration of operations grow, communication and context-management costs become significant.</p>
<p>Gotanda Style avoids direct agent-to-agent conversation.</p>
<p>Instead, each agent deposits pheromones into a shared environment. Other agents read those pheromones, integrate them with evidence, create issues, and turn only the safe, automatable ones into pull requests.</p>
<p>The pattern has several advantages:</p>
<ul>
<li><p>It is easier to add more agents.</p>
</li>
<li><p>It supports efficient exploration of large codebases.</p>
</li>
<li><p>It reduces inter-agent communication and token usage.</p>
</li>
<li><p>It works asynchronously.</p>
</li>
<li><p>It handles noise through time decay.</p>
</li>
<li><p>It can detect hotspots and conflicts that are hard to see in direct conversation.</p>
</li>
</ul>
<p>We think this can become a useful design pattern for LLM-based software maintenance.</p>
<p>And for us, it is not a future idea. It is already running on a Python repository with about 200,000 lines of code, closing the loop from Sentry alert to improvement pull request.</p>
<p>If AI coding agents increase development velocity, maintenance has to become stronger at the same time. We need agents that observe the codebase, detect anomalies, combine improvement signals, create issues at safe granularity, and turn those issues into pull requests.</p>
<p>As more agents continuously observe, maintain, and improve codebases, coordination models that do not depend on constant conversation will matter more.</p>
<p>Gotanda Style is one experiment in that direction.</p>
]]></content:encoded></item><item><title><![CDATA[Attractor Engineering: Seeing Software Development as Field Dynamics]]></title><description><![CDATA[TL;DR

A codebase can be read as a field that attracts future changes, and a pull request can be read as a force applied to that field.

A good field makes good changes easier to make. A bad field rep]]></description><link>https://blog.iroha1203.dev/attractor-engineering-seeing-software-development-as-field-dynamics</link><guid isPermaLink="true">https://blog.iroha1203.dev/attractor-engineering-seeing-software-development-as-field-dynamics</guid><category><![CDATA[architecture]]></category><category><![CDATA[Software Engineering]]></category><category><![CDATA[AI]]></category><category><![CDATA[programing]]></category><category><![CDATA[Computer Science]]></category><dc:creator><![CDATA[Hiroyuki Nakahata]]></dc:creator><pubDate>Sat, 09 May 2026 12:47:44 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/69ff29ebf239332df4a93c3a/0b97ec46-a91c-4220-8302-874616dcceec.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<hr />
<blockquote>
<p><strong>TL;DR</strong></p>
<ul>
<li><p>A codebase can be read as a field that attracts future changes, and a pull request can be read as a force applied to that field.</p>
</li>
<li><p>A good field makes good changes easier to make. A bad field repeatedly makes bad shortcuts look natural. In an era where AI can produce PRs quickly, this attraction becomes stronger.</p>
</li>
<li><p>I call the practice of designing where future changes are pulled <strong>Attractor Engineering</strong>.</p>
</li>
<li><p>CI/CD, tests, reviews, and harnesses can be read as dissipative systems: they remove unwanted force and shape the trajectory.</p>
</li>
<li><p>ArchSig, or Architecture Signature, is a tool for observing that trajectory along multiple axes.</p>
</li>
</ul>
</blockquote>
<p>The first half of this article is written for practitioners: it explains the intuition in terms of codebases, PRs, review, CI, and AI-assisted development. The second half is more mathematical: it connects the same intuition to AAT, Architecture Signature, Lean formalization, and finite counterexamples.</p>
<h2>The First Discovery</h2>
<p>The starting point was a simple thought experiment.</p>
<p>What if we look at software architecture not only as a set of directories, design rules, or conventions, but as an <strong>algebraic structure</strong>?</p>
<p>From that point of view, everyday changes such as feature additions, refactorings, splits, migrations, repairs, protections, deletions, and integrations become operations acting on the structure we call architecture.</p>
<pre><code class="language-text">current codebase
  + feature addition
  + refactoring
  + review fix
  + migration
  + repair
  -&gt; next codebase
</code></pre>
<p>If we take one more step, we can ask: if these operations are not applied once, but repeated dozens or hundreds of times, can the whole development process be read as a kind of dynamics?</p>
<p>Each individual PR is small.</p>
<p>But after enough PRs, the codebase gradually moves in some direction.</p>
<p>When a good structure already exists, the next change tends to fit into a good place.</p>
<p>When a bad structure exists, a locally natural change tends to take the same bad shortcut again.</p>
<p>Can we treat "where changes tend to go" as something we design?</p>
<p>I decided to call this way of thinking <strong>Attractor Engineering</strong>.</p>
<h2>A Codebase Is a Field, and a PR Is a Force</h2>
<p>The central interpretation is this:</p>
<pre><code class="language-text">codebase = a field that attracts changes
PR       = a force applied to that field
ArchSig  = an observer for the movement
</code></pre>
<p>A codebase is not a neutral space that passively receives future changes.</p>
<p>Existing names, types, responsibility boundaries, tests, directory structure, previous implementation examples, and review culture all shape which next change feels natural.</p>
<p>A PR is a force applied to that field. Each one may be small, but repeated PRs create a trajectory of change.</p>
<pre><code class="language-text">current codebase
  -&gt; a PR is applied
  -&gt; an architectural change is observed
  -&gt; a trajectory of change emerges
  -&gt; this becomes the next codebase
</code></pre>
<p>The important point is that a PR changes the codebase, and the changed codebase then changes what the next PR is likely to look like.</p>
<h2>People and Systems Create the Field</h2>
<p>This field is not created only by engineers.</p>
<p>Product managers, product owners, engineers, reviewers, AI agents, CI, tests, design documents, coding standards, and existing examples all participate in it.</p>
<p>Everyone and everything involved in development affects which changes become likely next.</p>
<table>
<thead>
<tr>
<th>Participant / mechanism</th>
<th>Effect on the field</th>
</tr>
</thead>
<tbody><tr>
<td>Product manager</td>
<td>Decides which values and demands are repeatedly injected into the system.</td>
</tr>
<tr>
<td>Product owner</td>
<td>Shapes PRs through requirement granularity, priorities, and acceptance criteria.</td>
</tr>
<tr>
<td>Engineer / architect</td>
<td>Creates paths for change through boundaries, abstractions, standard patterns, and reference implementations.</td>
</tr>
<tr>
<td>Reviewer</td>
<td>Pushes back bad force and redirects it toward better directions.</td>
</tr>
<tr>
<td>CI / tests / types</td>
<td>Rejects, weakens, and narrows inappropriate force.</td>
</tr>
<tr>
<td>AI agent</td>
<td>Reads the existing field and quickly proposes changes.</td>
</tr>
</tbody></table>
<p>The way requirements are sliced, prioritized, scheduled, and accepted changes how later PRs are produced. Even people who do not write code apply indirect force to the field of the codebase.</p>
<p>This becomes especially important in AI-assisted development. Vague requirements can quickly become vague PRs. If boundaries and non-goals are clear, an AI system is more likely to produce useful changes within those boundaries.</p>
<h2>What Changes in AI-Assisted Development</h2>
<p>The essence of AI-assisted development is not simply that code can be written faster.</p>
<p>The more important change is that <strong>the distribution of selected change operations</strong> changes.</p>
<p>An AI system reads existing code, neighboring files, names, types, tests, previous implementation examples, READMEs, and design documents, and then generates the next proposed change.</p>
<p>In other words, the whole codebase becomes input context for the AI.</p>
<pre><code class="language-text">the codebase becomes input context for AI
  -&gt; AI proposes a change
  -&gt; the proposal becomes a PR
  -&gt; review / CI / merge process handles it
  -&gt; the codebase is updated
  -&gt; the next input context changes
</code></pre>
<p>If a good reference implementation is nearby, the AI is likely to imitate it.</p>
<p>If a bad shortcut already exists, the AI is also likely to treat it as a natural option.</p>
<p>In this sense, AI rapidly reproduces the local style already present in the field. That is why, in the AI era, what matters is not only the capability of an individual AI agent. The design of the field in which the AI participates matters just as much.</p>
<h2>What Is an Attractor?</h2>
<p>When a codebase contains a huge <code>common</code> module, an overly convenient helper, or an ambiguous service, changes tend to be pulled there.</p>
<p>Conversely, when good responsibility boundaries and clear implementation examples exist, the next change tends to follow them.</p>
<p>This destination toward which changes are pulled is what I call an attractor. When something moves repeatedly, it often tends to approach certain places or states.</p>
<p>The surrounding region from which things are likely to fall into that attractor is what I call a basin.</p>
<p>Technical debt can be read as a bad basin.</p>
<p>Once a codebase falls into it, locally natural changes keep adding to the same place, and the cost of refactoring out of it grows higher and higher.</p>
<p>The important point is that attractors can be good or bad.</p>
<p>A good attractor pulls in good changes.</p>
<p>A bad attractor makes bad shortcuts get selected again and again.</p>
<h2>What Is Attractor Engineering?</h2>
<p>Attractor Engineering is the idea that we should deliberately design these attractors.</p>
<p>Its target is not just the codebase.</p>
<p>It includes the whole development organization: product managers, product owners, engineers, reviewers, AI agents, CI/CD, tests, design documents, and coding standards.</p>
<p>The goal is not only to block bad changes from the outside. The goal is to create a field where good changes are naturally easier to select.</p>
<table>
<thead>
<tr>
<th>Part of Attractor Engineering</th>
<th>What it shapes</th>
<th>Examples</th>
</tr>
</thead>
<tbody><tr>
<td>Create the field</td>
<td>Which changes feel natural to propose.</td>
<td>Requirements, priorities, design boundaries, types, APIs, examples, templates.</td>
</tr>
<tr>
<td>Dissipate bad force</td>
<td>Which proposed changes are rejected, weakened, or redirected.</td>
<td>Harnesses, CI, tests, reviews, PR granularity.</td>
</tr>
<tr>
<td>Observe the trajectory</td>
<td>How architectural movement becomes visible over time.</td>
<td>ArchSig, architecture features, drift reports.</td>
</tr>
</tbody></table>
<p>Attractor Engineering is an integrated design theory for the era of AI-assisted development. It treats the entire development organization as part of the system.</p>
<h2>Harness Engineering as a Dissipative System</h2>
<p>We cannot simply take changes produced by AI and put them directly into the codebase.</p>
<p>Harnesses, CI, tests, type checking, static analysis, and review divide proposed changes into "accept", "fix and check again", and "do not merge".</p>
<p>In Attractor Engineering, this behavior can be read as dissipation.</p>
<p>Dissipation is the mechanism that removes unwanted components of the force entering the field.</p>
<p>If dissipation is too weak, the fast change force produced by AI enters the codebase directly. If it is too strong, nothing moves. A good harness weakens the force that increases debt while preserving the force that moves the product forward.</p>
<p>In this view, CI/CD is not merely a checklist. It is more like brakes, rails, signals, and safety equipment that convert fast PR generation into safe productivity.</p>
<p>What matters in AI-assisted development is not only making the engine stronger.</p>
<p>It is also preparing the field and the dissipative system that can receive a stronger engine.</p>
<h2>What ArchSig Observes</h2>
<p>To design the field, we need to observe what is happening.</p>
<p>That is the role of ArchSig.</p>
<p>ArchSig is short for Architecture Signature.</p>
<p>In my <a href="https://github.com/iroha1203/AlgebraicArchitectureTheoryV2">repository</a>, I use it to mean an observation framework for reading changes in a codebase or PR along multiple axes. Dependencies, boundaries, abstractions, runtime exposure, semantic drift, and test observability are not collapsed into a single score. They are treated as multiple features.</p>
<p>ArchSig is an observer for seeing which direction a PR moves the architecture.</p>
<p>For example, we may observe axes like these:</p>
<table>
<thead>
<tr>
<th>Axis</th>
<th>What we want to observe</th>
</tr>
</thead>
<tbody><tr>
<td>Static dependencies</td>
<td>Dependency direction and violations of forbidden dependencies.</td>
</tr>
<tr>
<td>Boundary rules</td>
<td>Connections that cross boundaries or bypass rules.</td>
</tr>
<tr>
<td>Abstraction leakage</td>
<td>Concrete dependencies that jump over abstractions.</td>
</tr>
<tr>
<td>Semantic drift</td>
<td>Whether responsibilities or meanings have shifted away from what was intended.</td>
</tr>
<tr>
<td>Test observability</td>
<td>Whether the change can be observed through tests.</td>
</tr>
<tr>
<td>Per-PR change</td>
<td>How each axis moves in a single PR.</td>
</tr>
</tbody></table>
<p>The important point is that we do not compress good and bad into one score.</p>
<p>What we want to know is which axes got worse, which axes improved, and what kind of force each change applies.</p>
<pre><code class="language-text">PR
  -&gt; observe change with ArchSig
  -&gt; observe the trajectory of change
  -&gt; see whether it is moving toward a good or bad region
</code></pre>
<p>ArchSig becomes an observer for the AI PR era.</p>
<p>Are AI-generated changes moving toward good attractors?</p>
<p>Are they falling into a pool of technical debt?</p>
<p>Is the harness dissipating enough bad force?</p>
<p>ArchSig gives us shared language for discussing those questions.</p>
<h2>PRs Become More Important, Not Less</h2>
<p>When AI reduces the cost of generating code, it may look as if PRs become less important.</p>
<p>From a dynamical systems viewpoint, the opposite is true.</p>
<p>A PR is not just a work unit.</p>
<p>A PR has the following roles:</p>
<ul>
<li><p>It cuts continuous change into observable units.</p>
</li>
<li><p>It lets us separate the directions in which a change acts.</p>
</li>
<li><p>It embeds the dissipative process of review, CI, and approval.</p>
</li>
<li><p>It creates a boundary for rollback and reversibility.</p>
</li>
<li><p>It creates a unit for decision-making and discussion.</p>
</li>
</ul>
<p>What AI lowers is mainly the cost of producing a PR.</p>
<p>But the value of PRs as units of observation, decomposition, dissipation, reversibility, and decision-making increases. In the AI era, PRs do not become unnecessary. They become more important as units for observing and controlling architectural movement.</p>
<h2>Future Development Organizations</h2>
<p>In future development organizations, the central problem will not be only how fast we can write code. It will be how to design a field that can safely receive that speed.</p>
<p>A fast train cannot run safely just because it has a powerful motor.</p>
<p>It needs rails, brakes, signals, safety equipment, and operations control.</p>
<p>Software development is similar. AI is a powerful motor, but by itself it can create semantic drift, responsibility drift, degradation of design properties we wanted to preserve, merge confusion, and flow toward technical debt.</p>
<p>What we need is a field with properties like these:</p>
<ul>
<li><p>Small and observable PRs.</p>
</li>
<li><p>Fast feedback.</p>
</li>
<li><p>Reliable CI.</p>
</li>
<li><p>A useful type system.</p>
</li>
<li><p>Architecture tests.</p>
</li>
<li><p>Carefully selected reference implementations.</p>
</li>
<li><p>Isolation of legacy code.</p>
</li>
<li><p>Clear demands, requirements, and design boundaries.</p>
</li>
<li><p>Human-designed boundaries for value and acceptance criteria.</p>
</li>
</ul>
<p>The safest AI coding environment is not the one with the strongest external harness. It is the one where good changes are natural, easy to imitate, observable, and less likely to fall into bad shortcuts.</p>
<h2>Summary So Far</h2>
<p>The success or failure of AI-assisted development is not determined only by how fast AI can write code.</p>
<p>The direction of the next PR changes depending on the field created by the codebase, requirements, design boundaries, reference implementations, review, CI/CD, and ArchSig.</p>
<p>A good field attracts good changes. A bad field repeatedly makes bad changes look natural. That is why architecture design in the AI era becomes the design of where future changes are attracted.</p>
<p>So far, this has been Attractor Engineering in practical engineering language. In the second half, I translate the same intuition into the language of AAT, Algebraic Architecture Theory, and dynamical systems.</p>
<h2>From Here, in Mathematical Language</h2>
<p>The rest of this article uses more mathematical formulation. If you only want the practical view first, it is fine to skip to the <a href="#conclusion">conclusion</a>.</p>
<p>Around AI development, there are many heuristics: "this prompt worked", "this workflow made us faster", and so on. These heuristics are valuable. But by themselves, they make it hard to separate why something worked, how far it generalizes, and under what conditions it breaks.</p>
<p>So I decompose the flow: a change is selected, becomes a PR, passes review / CI, is merged, and then the updated codebase changes the distribution of future changes. By separating state, operation, observation, invariant, obstruction witness, and proof obligation, we can turn heuristics into something easier to test.</p>
<h2>A Short Introduction to AAT</h2>
<p>The background theory for this discussion is AAT, or Algebraic Architecture Theory. Here I introduce only the minimal vocabulary needed for the rest of the article.</p>
<p>The Lean snippets below are excerpts from implemented APIs, adjusted for readability. Namespaces, imports, and some proof bodies are omitted.</p>
<p>AAT treats software development not merely as a sequence of code changes, but as a theory of architectural extension, decomposition, repair, and composition.</p>
<p>Its central proposition does not appear in Lean as one large equation. It appears as packages that combine operations and proof obligations.</p>
<pre><code class="language-lean">structure OperationProofObligation (State : Type u) (Witness : Type v) where
  kind : ArchitectureOperationKind
  obligation : ProofObligation State Witness
  precondition : Prop
  nonConclusion : Prop
</code></pre>
<p>Operations are first classified as an operation catalog on the Lean side.</p>
<pre><code class="language-lean">inductive ArchitectureOperationKind where
  | compose
  | refine
  | abstract
  | replace
  | split
  | merge
  | isolate
  | protect
  | migrate
  | reverse
  | contract
  | repair
  | synthesize
  deriving DecidableEq, Repr
</code></pre>
<p>The important point is that names such as <code>split</code> or <code>repair</code> prove nothing by themselves. An operation kind is a classification for theorem packages. Claims about preservation, improvement, or repair must be stated separately as proof obligations.</p>
<p>From this viewpoint, a design review is not only the question "is this design good or bad?"</p>
<pre><code class="language-text">- Is the existing structure embedded after extension?
- Can the new feature be split out from the existing structure?
- Do interactions pass through declared interfaces?
- Which invariants are preserved, and which invariants were broken?
- If a split is not possible, which obstruction witness blocks it?
</code></pre>
<p>The smallest object in AAT is <code>ArchitectureCore</code>.</p>
<pre><code class="language-lean">structure ArchitectureCore (C : Type u) (A : Type v)
    (StaticObs : Type w) (SemanticExpr : Type q)
    (SemanticObs : Type r) where
  flatness :
    ArchitectureFlatnessModel C A StaticObs SemanticExpr SemanticObs
  staticUniverse : ComponentUniverse flatness.static
  componentDecidableEq : DecidableEq C
  staticEdgeDecidable : DecidableRel flatness.static.edge
  runtimeEdgeDecidable : DecidableRel flatness.runtime.edge
  boundaryPolicyDecidable : DecidableRel flatness.boundaryAllowed
  abstractionPolicyDecidable : DecidableRel flatness.abstractionAllowed
  runtimeRole : C -&gt; C -&gt; RuntimeDependencyRole
  semanticRequiredDecidable :
    ∀ d : RequiredDiagram SemanticExpr,
      Decidable (flatness.requiredSemantic d)
</code></pre>
<p>Here it is important that <code>ArchitectureCore</code> is not the whole real-world codebase itself. It is a finite or bounded object extracted from code, specifications, reviews, and operational observations so that the theory can handle it.</p>
<p>Feature addition is read as an operation that extends an existing architecture into a larger one while preserving the existing architecture.</p>
<pre><code class="language-text">ExistingCore X
  -&gt; ExtendedArchitecture X'
  -&gt; FeatureView F
</code></pre>
<p>In a good feature extension, the existing core is preserved inside the extension, the feature view can be extracted in an explainable way, and interactions from the feature to the core pass through declared interfaces. Conversely, hidden dependencies, boundary policy violations, abstraction leakage, runtime exposure, and semantic mismatch are treated not as impressions, but as <code>ObstructionWitness</code> values.</p>
<p>To count, remove, preserve, or explicitly decline to conclude about these obstructions, AAT makes <code>ProofObligation</code> and <code>Certificate</code> explicit.</p>
<pre><code class="language-lean">structure ProofObligation (State : Type u) (Witness : Type v) where
  formalUniverse : Prop
  requiredLaws : Prop
  invariantFamily : State -&gt; Prop
  witnessUniverse : Witness -&gt; Prop
  coverageAssumptions : Prop
  exactnessAssumptions : Prop
  operationPreconditions : Prop
  conclusion : Prop
  nonConclusions : Prop
</code></pre>
<p>An obligation is not discharged merely because it exists. It is discharged only when the visible assumptions imply the conclusion.</p>
<pre><code class="language-lean">def AssumptionsHold (P : ProofObligation State Witness) : Prop :=
  P.formalUniverse ∧
  P.requiredLaws ∧
  P.coverageAssumptions ∧
  P.exactnessAssumptions ∧
  P.operationPreconditions

def Discharged (P : ProofObligation State Witness) : Prop :=
  AssumptionsHold P -&gt; P.conclusion
</code></pre>
<p>The same is true for certificates. For example, in repair synthesis, if we say "there is no solution", solver failure alone is not enough. Only when a valid certificate exists do we use soundness to conclude that no satisfying architecture exists.</p>
<pre><code class="language-lean">structure NoSolutionCertificate
    {State : Type u} {Constraint : Type c} (Certificate : Type v)
    (C : SynthesisConstraintSystem State Constraint)
    (cert : Certificate) where
  valid : Prop
  sound : valid -&gt; NoArchitectureSatisfies C
  coverageAssumptions : Prop
  exactnessAssumptions : Prop
  nonConclusions : Prop

theorem sound_of_valid
    (pkg : NoSolutionCertificate Certificate C cert)
    (hValid : ValidNoSolutionCertificate pkg) :
    NoArchitectureSatisfies C
</code></pre>
<p><code>nonConclusions</code> is not decoration. Even if a static split is proved, runtime flatness or semantic flatness does not automatically follow. Even if no obstruction is found in one observation universe, that does not imply there is no obstruction in every universe. Making this boundary explicit is necessary if we want to treat the theory as testable rather than as a collection of engineering anecdotes.</p>
<p><code>ArchitectureSignature</code> is also not intended to collapse architecture quality into a single score. It is a multi-axis diagnosis for reading multiple invariant and obstruction families axis by axis.</p>
<pre><code class="language-lean">structure ArchitectureSignatureV1 where
  core : ArchitectureSignatureV1Core
  weightedSccRisk : Option Nat
  projectionSoundnessViolation : Option Nat
  lspViolationCount : Option Nat
  nilpotencyIndex : Option Nat
  runtimePropagation : Option Nat
  relationComplexity : Option Nat
  empiricalChangeCost : Option Nat
  deriving DecidableEq, Repr
</code></pre>
<p>Some axes are <code>Option Nat</code> because an unmeasured value must not be treated as zero. <code>none</code> does not mean "no problem". It means "not measured in this universe / extractor / bridge".</p>
<pre><code class="language-lean">theorem not_axisAvailableAndZero_of_axisValue_none
    {sig : ArchitectureSignatureV1} {axis : ArchitectureSignatureV1Axis}
    (hNone : axisValue sig axis = none) :
    ¬ axisAvailableAndZero sig axis
</code></pre>
<p>From this perspective, a signature is not a convenient bag of metrics. It is a multi-axis invariant relative to a law universe. For selected required law axes, there are also bridge theorems connecting lawfulness and zero signature axes.</p>
<pre><code class="language-lean">theorem architectureLawful_iff_requiredSignatureAxesZero
    {C : Type u} {A : Type v} {Obs : Type w}
    (X : ArchitectureLawModel C A Obs)
    [DecidableEq C] [DecidableEq A] [DecidableEq Obs]
    [DecidableRel X.G.edge] [DecidableRel X.GA.edge]
    [DecidableRel X.boundaryAllowed]
    [DecidableRel X.abstractionAllowed] :
    ArchitectureLawful X ↔
      RequiredSignatureAxesZero (ArchitectureLawModel.signatureOf X)
</code></pre>
<p>AAT does not treat every claim at the same level. It separates definitions, proved theorem packages, bounded bridge theorems, tooling-side evidence, and empirical hypotheses. It also records which universe, observation, coverage, and exactness assumptions each claim is relative to.</p>
<p>The dynamics part below follows the same discipline. The important point is that AAT is not using mathematical vocabulary for atmosphere. It is trying to make the assumptions, conclusions, and non-conclusions part of the type-level structure.</p>
<p>So far, AAT gives us vocabulary for a single architectural state and operations acting on it.</p>
<p>But in AI-assisted development, the central issue is not only a single operation. Requirements, existing code, review, CI, and AI agents change which operation is likely to be selected next, and this selection is repeated many times.</p>
<p>So we need to view AAT operations not only as one-time proof targets, but also as transformations repeatedly selected over time. This is where a chaos-game-like reading enters.</p>
<h2>Formalizing Attractor Dynamics</h2>
<p>From here, I use AAT vocabulary to rewrite "field", "force", "dissipation", and "observation" in a more mathematical form.</p>
<p>This is not merely a metaphor that says "AI development is kind of like a chaos game". It is an attempt to place PR force, operation support, trajectory, and basin candidates on top of the AAT vocabulary of state, operation, invariant, obstruction, proof obligation, certificate, and signature.</p>
<p>At this stage, I should be careful: this is not a finished theorem of real-world software development. It is a way to organize phenomena that practitioners can feel, in a structure that may eventually support measurement and verification.</p>
<p>The minimal loop of the dynamics can be read as follows:</p>
<pre><code class="language-text">architecture field
  -&gt; operation distribution
  -&gt; accepted / rejected transitions
  -&gt; signature trajectory
  -&gt; updated architecture field
</code></pre>
<p>The position is that architecture quality is not only a property of a snapshot. It is also a property of the future operation distribution and the signature trajectory.</p>
<h3>1. State, Operation, Observation</h3>
<p>Let the architectural state be <code>X_t</code>.</p>
<p>Feature addition, repair, split, protection, migration, and refactoring are operations acting on that state.</p>
<pre><code class="language-text">X_{t+1} = op_t(X_t)
</code></pre>
<p>The operation <code>op_t</code> is not selected uniformly at random.</p>
<p>The current codebase, requirements, prompt, review policy, CI, design boundaries, and organizational judgment all change which operations are likely to be selected.</p>
<pre><code class="language-text">op_t ~ P(operation | X_t, control_t)
</code></pre>
<p>This probability expression is notation for the practical reading. The formal core of AAT is not a probability distribution. It is finite or bounded operation support, bounded scripts, accepted transition predicates, and explicit preservation assumptions.</p>
<p>In Lean, for example, operation support is represented not as a probability distribution, but as a finite list of candidate operations for each state.</p>
<pre><code class="language-lean">structure FiniteOperationKernel
    (State : Type u) (OperationId : Type w) where
  support : State -&gt; List OperationId
  coverageAssumptions : Prop
  weightSourceBoundary : Prop
  normalizationBoundary : Prop
  nonConclusions : Prop
</code></pre>
<p>Weights, normalization, and completeness of AI-generated proposals are not mixed into the theorem here. Boundaries such as <code>weightSourceBoundary</code> and <code>normalizationBoundary</code> record what is outside the formal claim, and the probabilistic reading remains outside that core.</p>
<p>Likewise, repeated operation sequences are first treated as bounded scripts.</p>
<pre><code class="language-lean">structure BoundedOperationScript (OperationId : Type w) where
  operations : List OperationId
  operationFamily : OperationId -&gt; Prop
  operationsInFamily :
    ∀ op, op ∈ operations -&gt; operationFamily op
  coverageAssumptions : Prop
  nonConclusions : Prop
</code></pre>
<p>This boundary helps avoid confusing probabilistic interpretation with preservation claims over finite support.</p>
<p>Instead of observing the entire state directly, we map it into signature space through an observation function <code>Obs</code>.</p>
<pre><code class="language-text">sigma_t = Obs(X_t)
</code></pre>
<p>This <code>sigma_t</code> is the Architecture Signature.</p>
<p>It contains multi-axis observations such as dependency direction, boundaries, abstraction, runtime exposure, and semantic mismatch.</p>
<p>In Lean, even the observation itself is packaged. The package contains not only the observation function, but also coverage and non-conclusion boundaries.</p>
<pre><code class="language-lean">structure SignatureObservation (State : Type u) (Sig : Type v) where
  observe : State -&gt; Sig
  coverageAssumptions : Prop
  nonConclusions : Prop
</code></pre>
<p>When architectural evolution is mapped into observation space, we get a signature trajectory.</p>
<pre><code class="language-lean">def SignatureTrajectory (O : SignatureObservation State Sig) :
    {X Y : State} -&gt; ArchitectureEvolution State X Y -&gt; List Sig
  | X, _, ArchitecturePath.nil _ =&gt; [O.observe X]
  | X, _, ArchitecturePath.cons _step rest =&gt;
      O.observe X :: SignatureTrajectory O rest
</code></pre>
<p>A change moves the signature.</p>
<pre><code class="language-text">Delta_t = sigma_{t+1} - sigma_t
</code></pre>
<p>This <code>Delta_t</code> can be read as the force applied by the PR or operation.</p>
<p>However, not every axis admits simple subtraction. For numeric axes, we may read it as a signed delta. For other axes, we read it as a before / after comparison, the appearance of a witness, or a change in state classification.</p>
<h3>2. PR Force Model</h3>
<p>With this structure, a PR becomes more than a diff.</p>
<p>A PR can be read as a force applied to the codebase in the selected Architecture Signature space.</p>
<pre><code class="language-text">PRForce(PR) = sigma(after(PR)) - sigma(before(PR))
</code></pre>
<p>The word force here does not mean physical force. It means an observed change in signature, relative to which axes are observed and which differences are defined.</p>
<p>Delta sequences, like trajectories, are relative to finite paths in Lean.</p>
<pre><code class="language-lean">def SignatureDeltaSequence
    (O : SignatureObservation State Sig) (D : SignatureDelta Sig Delta) :
    {X Y : State} -&gt; ArchitectureEvolution State X Y -&gt; List Delta
  | _, _, ArchitecturePath.nil _ =&gt; []
  | X, _, ArchitecturePath.cons (Y := Y) _step rest =&gt;
      D.between (O.observe X) (O.observe Y) ::
        SignatureDeltaSequence O D rest
</code></pre>
<p>For selected additive deltas, the sum of step deltas agrees with the endpoint delta. This is proved as a theorem.</p>
<pre><code class="language-lean">theorem netSignatureDelta_telescopes [Zero Delta] [Add Delta]
    (O : SignatureObservation State Sig) (D : SignatureDelta Sig Delta)
    (law : AdditiveSignatureDeltaLaw D) :
    {X Y : State} -&gt; (plan : ArchitectureEvolution State X Y) -&gt;
      NetSignatureDelta (SignatureDeltaSequence O D plan) =
        EndpointSignatureDelta O D plan
</code></pre>
<p>But this theorem is finite path calculus under the assumption that the selected delta satisfies an additive law. It does not claim that unobserved axes, incident risk, review quality, or actual PR outcomes can all be added this way.</p>
<p>The force has multiple components.</p>
<table>
<thead>
<tr>
<th>Force component</th>
<th>Meaning</th>
</tr>
</thead>
<tbody><tr>
<td>Feature force</td>
<td>The force that moves product functionality forward.</td>
</tr>
<tr>
<td>Repair force</td>
<td>The force that repairs existing breakage.</td>
</tr>
<tr>
<td>Coupling force</td>
<td>The force that increases or decreases coupling.</td>
</tr>
<tr>
<td>Boundary force</td>
<td>The force that preserves or violates boundaries.</td>
</tr>
<tr>
<td>Type force</td>
<td>The force that adds type information or creates type holes.</td>
</tr>
<tr>
<td>Test force</td>
<td>The force that increases or decreases observability through tests.</td>
</tr>
<tr>
<td>Debt force</td>
<td>The force that pushes the system toward a bad basin.</td>
</tr>
<tr>
<td>Refactor force</td>
<td>The force that helps it escape a bad basin.</td>
</tr>
</tbody></table>
<p>A good PR has not only feature force, but also stabilizing force.</p>
<pre><code class="language-text">v_PR = v_feature + v_stabilize
</code></pre>
<p>A risky PR moves the feature forward locally while quietly adding small debt force.</p>
<pre><code class="language-text">v_PR = v_feature + v_debt
</code></pre>
<p>In AI-generated PRs, the especially important case is one where tests pass and the specification is satisfied, but small <code>v_debt</code>, <code>v_coupling</code>, <code>v_type_hole</code>, or <code>v_entropy</code> accumulates repeatedly. It may be hard to see in a single PR. But as a trajectory, the system is moving toward a bad basin. I call this the Local Correctness Trap.</p>
<h3>3. Three Classes of Force</h3>
<p>The earlier discussion about product managers, product owners, review, and CI/CD becomes clearer if we separate force by observability.</p>
<p>Force can be divided into three classes.</p>
<table>
<thead>
<tr>
<th>Force class</th>
<th>Meaning</th>
<th>Main evidence</th>
</tr>
</thead>
<tbody><tr>
<td>ObservedForce</td>
<td>Before / after signature delta of PRs that were actually merged.</td>
<td>PRs, ArchSig reports, drift ledger.</td>
</tr>
<tr>
<td>LatentForce</td>
<td>Invisible force by which requirements, design, prompts, and local code style shape which PRs are proposed.</td>
<td>Requirements, prompts, proposal logs, case studies.</td>
</tr>
<tr>
<td>DissipatedForce</td>
<td>Raw force that was rejected, corrected, or weakened by review / CI / types / policy.</td>
<td>CI failures, requested changes in review, rejected proposals.</td>
</tr>
</tbody></table>
<p>This classification makes AI-assisted development more concrete.</p>
<p>If we look only at merged PRs, we see only ObservedForce.</p>
<p>But in an era where AI can generate many proposals, the force that was not merged also matters. Force removed by review, rejected by CI, or reshaped before merge matters.</p>
<p>To understand how well a dissipative system is working, we need DissipatedForce.</p>
<p>To understand what kind of PR distribution is created by upstream requirements or prompts, we need LatentForce.</p>
<p>In Lean, the separation between accepted and rejected changes appears as a damping / control schema.</p>
<pre><code class="language-lean">structure DampingControlSchema (State : Type u) (Sig : Type v) where
  observation : SignatureObservation State Sig
  invariant : SafeRegion Sig
  accepted :
    {X Y : State} -&gt; ArchitectureTransition State X Y -&gt; Prop
  rejected :
    {X Y : State} -&gt; ArchitectureTransition State X Y -&gt; Prop
  acceptedPreservesInvariant :
    ∀ {X Y : State} (t : ArchitectureTransition State X Y),
      accepted t -&gt; StepPreservesSafeRegion observation invariant t
  coverageAssumptions : Prop
  nonConclusions : Prop
</code></pre>
<p>What this proves is limited: transitions classified as <code>accepted</code> preserve the explicitly stated <code>invariant</code>. The existence of rejected changes does not prove that the whole future of the codebase is safe.</p>
<p>On top of this schema, it is proved that accepted finite evolutions create trajectories inside the selected invariant.</p>
<pre><code class="language-lean">theorem acceptedEvolution_preserves_selectedInvariant
    (control : DampingControlSchema State Sig) :
    {X Y : State} -&gt; (plan : ArchitectureEvolution State X Y) -&gt;
      StateInSafeRegion control.observation control.invariant X -&gt;
      control.AcceptedEvolution plan -&gt;
        SignatureTrajectoryInSafeRegion
          control.invariant (SignatureTrajectory control.observation plan)
</code></pre>
<h3>4. A Chaos-Game-Like Correspondence</h3>
<p>This is where the chaos-game-like reading appears.</p>
<p>The similarity is that we have multiple transformations, one of them is selected at each step, and a state trajectory is produced.</p>
<p>The difference is that, in software development, neither the set of transformations nor their likelihood is fixed. Requirements, review, CI, design boundaries, existing examples, and AI agent behavior all affect which operation is likely to be selected next.</p>
<p>So the goal is not to claim that software development literally is the classical chaos game. The goal is to use AAT vocabulary to handle the structure where multiple operations are repeatedly selected and the resulting trajectory tends to move toward certain regions.</p>
<p>The correspondence is:</p>
<table>
<thead>
<tr>
<th>Chaos-game side</th>
<th>AAT / development side</th>
</tr>
</thead>
<tbody><tr>
<td>State <code>X_t</code></td>
<td>Architecture state / codebase field.</td>
</tr>
<tr>
<td>Transformation <code>f_i</code></td>
<td>Architecture operation / PR / patch.</td>
</tr>
<tr>
<td>Transformation selection</td>
<td>Operation selection by developer / AI / requirement / review.</td>
</tr>
<tr>
<td>Trajectory</td>
<td>Architecture Signature trajectory.</td>
</tr>
<tr>
<td>Attractor</td>
<td>Signature region where the system tends to stay.</td>
</tr>
<tr>
<td>Basin</td>
<td>Initial or surrounding states likely to fall into that attractor.</td>
</tr>
<tr>
<td>Control input</td>
<td>Prompt, review policy, CI, type checker, architecture rule.</td>
</tr>
</tbody></table>
<p>As a formula:</p>
<pre><code class="language-text">X_{t+1} = f_{i_t}(X_t)
i_t ~ P(. | X_t, control_t)
Y_t = sigma(X_t)
</code></pre>
<p>The important point is not to assert probability or attractors as strong theorems too early.</p>
<p>What we should handle in practice is first an attractor candidate or basin candidate relative to a finite observation window, bounded horizon, selected signature axes, and selected operation support.</p>
<pre><code class="language-text">finite observed trajectory
  + selected signature region
  + bounded operation support
  -&gt; attractor / basin candidate
</code></pre>
<p>This is not an escape into weak claims. It is a boundary that makes measurement and falsification possible.</p>
<h3>5. Support Shaping</h3>
<p>The mathematical core of Attractor Engineering is support shaping.</p>
<p>This is not just about "blocking bad PRs". It is about changing the set of operations that can naturally be selected next, and changing how likely they are to be selected.</p>
<p>In short, Attractor Engineering is a theory of designing the geometry of the operation distribution.</p>
<pre><code class="language-lean">def Supports
    (kernel : FiniteOperationKernel State OperationId)
    (X : State) (op : OperationId) : Prop :=
  op ∈ kernel.support X
</code></pre>
<p>For the current architecture state <code>X</code>, the set of operations that can naturally be selected is called operation support.</p>
<p>Good design changes this support.</p>
<pre><code class="language-lean">def SupportOperationsPreserveSafeRegion
    (kernel : FiniteOperationKernel State OperationId)
    (sem : OperationTransitionSemantics State OperationId)
    (O : SignatureObservation State Sig) (R : SafeRegion Sig) : Prop :=
  ∀ {X : State} (op : OperationId),
    kernel.Supports X op -&gt; sem.OperationPreservesSafeRegion O R op
</code></pre>
<p>The strong form of support shaping says: operations that remain in support preserve the selected safe region. In practical terms, we reduce bad operations, increase good operations, make correct paths easier to choose, and make dangerous shortcuts less convenient.</p>
<p>This idea is packaged on the Attractor Engineering side as follows:</p>
<pre><code class="language-lean">structure AttractorEngineeringSupportPackage
    (State : Type u) (Sig : Type v) (OperationId : Type w) where
  observation : SignatureObservation State Sig
  kernel : FiniteOperationKernel State OperationId
  semantics : OperationTransitionSemantics State OperationId
  targetRegion : SafeRegion Sig
  supportPreserves :
    kernel.SupportOperationsPreserveSafeRegion semantics observation targetRegion
  coverageAssumptions : Prop
  measurementBoundary : Prop
  nonConclusions : Prop
</code></pre>
<p>With this package, if a bounded script uses only operations from finite support, and the starting point is in the target region, then the observed trajectory remains in the target region. This is stated as a theorem.</p>
<pre><code class="language-lean">theorem supportPackage_preserves_targetTrajectory
    (package : AttractorEngineeringSupportPackage State Sig OperationId)
    (script : BoundedOperationScript OperationId)
    {X Y : State} (plan : ArchitectureEvolution State X Y)
    (hStart :
      StateInSafeRegion package.observation package.targetRegion X)
    (hRealizes : script.RealizesEvolution package.semantics plan)
    (hSupport :
      package.kernel.ScriptUsesSupport script.operations plan) :
    SignatureTrajectoryInSafeRegion package.targetRegion
      (package.TargetTrajectory plan)
</code></pre>
<p>For example, good APIs, good examples, clear ownership, narrow modules, and domain states represented in types increase the local discoverability of good operations.</p>
<p>Conversely, a huge common module, implicit global context, ambiguous services, and overly convenient helpers increase the local convenience of bad operations.</p>
<p>From this viewpoint, refactoring is not only cleaning up the current structure. It is also a basin-reshaping operation that changes the future operation distribution.</p>
<p>In the measurement layer, one tooling-side metric candidate to watch here is <code>SupportRiskMass</code>.</p>
<pre><code class="language-text">SupportRiskMass(C) =
  sum over op in support(C) of weight(op) * risk(op, C)
</code></pre>
<p>Here again, it is important not to reduce <code>risk</code> to a simple 0 / 1.</p>
<p>In AAT terms, we should distinguish at least:</p>
<pre><code class="language-text">safe-preserving proved
safe-preserving measured
safe-preserving estimated
unsafe witness measured
unmeasured
unavailable
private
notComparable
outOfScope
</code></pre>
<p>Unmeasured must not be read as zero. This is a central principle in both ArchSig and AAT.</p>
<h3>6. The Same Signature Does Not Imply the Same Future</h3>
<p>An important point is that two states can have the same current Architecture Signature but different future operation distributions.</p>
<p>For example, two modules might show the same number of dependency violations, the same test coverage, and the same complexity. But one may have a good canonical example nearby, while the other may contain many bad shortcuts.</p>
<p>Even if the current observation values are the same, future PRs may be attracted in different directions.</p>
<pre><code class="language-text">Obs(X) = Obs(Y)
  does not imply
OperationSupport(X) = OperationSupport(Y)
</code></pre>
<p>This is why architecture quality should not be judged by snapshot metrics alone. The current value can be the same while the future force field is different. Attractor Engineering treats this future force field as a design object.</p>
<h3>7. Accepted Preservation and Support Preservation Are Different</h3>
<p>There is another important separation.</p>
<p>Suppose review and CI ensure that merged PRs preserve a safe region.</p>
<p>Even then, unsafe operations may still remain inside operation support.</p>
<p>This separation is not just a warning. It appears on the Lean side as a finite counterexample. Accepted-step invariant preservation can hold, and an accepted safe step can exist, while operations that do not preserve the safe region remain in support.</p>
<pre><code class="language-lean">theorem acceptedPreservation_not_supportPreservation_counterexample :
    (∃ (t : ArchitectureTransition ExampleState safeState safeState),
      control.AcceptedStep t ∧
        StepPreservesSafeRegion control.observation control.invariant t) ∧
    (∀ {X Y : ExampleState} (t : ArchitectureTransition ExampleState X Y),
      control.AcceptedStep t -&gt;
        StepPreservesSafeRegion control.observation control.invariant t) ∧
    (∃ X op,
      kernel.Supports X op ∧
        ¬ semantics.OperationPreservesSafeRegion control.observation
          control.invariant op)
</code></pre>
<p>This is the difference between guardrails and attractor engineering.</p>
<p>Strong guardrails may stop bad PRs.</p>
<p>But a field where bad PRs are produced in large numbers every time is still not a good field.</p>
<p>A good field is one where bad operations are less likely to appear in the first place, and good operations are natural, easy to imitate, observable, and low-friction.</p>
<h3>8. PRs Are Non-Commutative</h3>
<p>PRs are generally non-commutative.</p>
<pre><code class="language-text">PR_2 o PR_1 != PR_1 o PR_2
</code></pre>
<p>Of course, this only makes sense when both orders can be applied. Even then, the same two PRs can produce different final signatures depending on merge order.</p>
<p>This matters in an era where AI agents can generate multiple PRs in parallel.</p>
<p>Even when individual PRs are locally correct, their order can change boundaries, types, tests, and semantic alignment.</p>
<p>I call this merge order sensitivity.</p>
<pre><code class="language-text">MergeOrderSensitivity(a, b, X) =
  distance(
    sigma(b(a(X))),
    sigma(a(b(X)))
  )
</code></pre>
<p>This is not merely a merge conflict issue. It is the non-commutativity of operation algebra branching the signature trajectory. We will need this viewpoint when evaluating teams of AI agents as well.</p>
<h3>9. Observe the Shape of the Trajectory</h3>
<p>Architecture Signature should be read not only as a current value, but also as a trajectory.</p>
<p>Even if the endpoint is safe, the path may have passed through a bad region.</p>
<p>Even if net delta is zero, there may have been large churn inside the path.</p>
<pre><code class="language-text">endpoint safe
  does not imply path safe

net force zero
  does not imply no excursion
</code></pre>
<p>This is also proved in Lean as a small finite counterexample. In a trajectory such as <code>0 -&gt; 2 -&gt; 0</code>, both endpoints are the same safe state. The endpoint delta and net delta can both appear to be zero, while the path passed through an unsafe region.</p>
<pre><code class="language-lean">theorem netSignatureDelta_eq_zero :
    NetSignatureDelta (SignatureDeltaSequence observation signedNatDelta
      excursionPlan) = 0

theorem endpointSafe_and_zeroDelta_but_not_pathSafe :
    EndpointSignatureDelta observation signedNatDelta excursionPlan = 0 ∧
      StateInSafeRegion observation safeRegion 0 ∧
      StateInSafeRegion observation safeRegion 0 ∧
      ¬ SignatureTrajectoryInSafeRegion safeRegion
          (SignatureTrajectory observation excursionPlan)
</code></pre>
<p>Trajectories have shapes.</p>
<table>
<thead>
<tr>
<th>Trajectory type</th>
<th>Meaning</th>
</tr>
</thead>
<tbody><tr>
<td>Stable Orbit</td>
<td>The system returns to a safe region after small changes.</td>
</tr>
<tr>
<td>Drift</td>
<td>The system slowly shifts toward a bad region.</td>
</tr>
<tr>
<td>Spiral Debt</td>
<td>It appears to return, but over the long run moves closer to a bad basin.</td>
</tr>
<tr>
<td>Sudden Phase Shift</td>
<td>A signature changes sharply after a particular PR.</td>
</tr>
<tr>
<td>Oscillation</td>
<td>Feature additions and refactorings alternate between good and bad.</td>
</tr>
<tr>
<td>Basin Capture</td>
<td>After some point, the system gets captured by a bad structure.</td>
</tr>
</tbody></table>
<p>What ArchSig really wants to observe is this trajectory.</p>
<p>Not just the evaluation of one PR, but the resulting motion produced by a group of PRs.</p>
<h3>10. Expanding Observation Can Suddenly Reveal Badness</h3>
<p>With coarse observation, a codebase may look safe.</p>
<p>But if we add more observation axes, a hidden obstruction may suddenly appear as nonzero.</p>
<pre><code class="language-text">coarse observation:
  safe

refined observation:
  hidden obstruction appears
</code></pre>
<p>We can call this an observability expansion shock.</p>
<p>The important point is that this does not necessarily mean the architecture got worse. It may simply mean that an axis that used to be invisible has become visible.</p>
<p>That is why ArchSig must distinguish <code>unmeasured</code> from <code>zero</code>. When something that was not measured becomes visible, we must separate "the architecture got worse" from "the observation became better".</p>
<h3>About the Lean Formalization</h3>
<p>The structure above is not built only from metaphor. Some of the core vocabulary of AAT has been implemented as Lean definitions and theorems under <code>Formal/Arch</code>.</p>
<p>The repository is <a href="https://github.com/iroha1203/AlgebraicArchitectureTheoryV2">AlgebraicArchitectureTheoryV2</a>. The proved API is summarized in the <a href="https://github.com/iroha1203/AlgebraicArchitectureTheoryV2/blob/main/docs/aat/lean_theorem_index.md">Lean definition and theorem index</a>.</p>
<p>The vocabulary used in the second half of this article mainly corresponds to <code>Formal/Arch/Evolution/SignatureDynamics.lean</code> and <code>Formal/Arch/Evolution/AttractorEngineering.lean</code>.</p>
<p>The role of Lean formalization is not to give this theory an aura of correctness. Its role is to record, with boundaries, what can be said under which universe, observation, coverage, and exactness assumptions.</p>
<p>It is important that counterexamples live in the same place as proved theorems.</p>
<pre><code class="language-text">proved:
  accepted evolution preserves selected invariant
  bounded sampled support-preserving script stays in target region
  selected additive delta telescopes over finite path

proved counterexamples:
  endpoint safe + zero delta does not imply path safe
  accepted preservation does not imply support preservation
  unmeasured axis is not available-zero evidence
</code></pre>
<p>Conversely, the fact that something is proved in Lean does not mean that a real-world code extractor is complete, or that every runtime / semantic obstruction has already been observed. With this boundary, AAT can separate what is formally known, what depends on measurement, and what remains an empirical research question.</p>
<h2>Conclusion</h2>
<p>The discovery of Attractor Engineering changes how I see software architecture: from a static blueprint to a field that guides future changes.</p>
<p>If software architecture is read as an algebraic structure, feature additions and refactorings become operations.</p>
<p>When those operations are repeated, the architecture state draws a trajectory.</p>
<p>We can then ask where that trajectory tends to go, and where it tends to stay. This is where attractors and basins enter the picture.</p>
<p>Architecture design in the era of AI-assisted development can be described as creating a field where future changes gather in good places and can escape bad places.</p>
<p>Harness engineering becomes the engineering of receiving AI's change force, dissipating unwanted components, and guiding the system toward good attractors.</p>
<p>ArchSig is the tool for observing that trajectory.</p>
<p>The essence of AI-assisted development is not only producing PRs faster.</p>
<p>It is deciding where fast force should converge.</p>
<p>A codebase is a field.</p>
<p>A PR is a force.</p>
<p>CI/CD is a dissipative system.</p>
<p>Product managers, product owners, engineers, reviewers, and AI agents are participants in the field.</p>
<p>ArchSig is an observer of the trajectory.</p>
<p>With this framing, development in the AI era is no longer just automation. It becomes field design.</p>
<p>As a design theory for that purpose, Attractor Engineering may be a useful direction for both practice and research.</p>
]]></content:encoded></item></channel></rss>