<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Yura Solovey</title>
    <description>The latest articles on DEV Community by Yura Solovey (@yura_solovey).</description>
    <link>https://dev.to/yura_solovey</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4036708%2F65bf04a3-c6fb-43ec-8f01-3d3be4fc6d4a.jpg</url>
      <title>DEV Community: Yura Solovey</title>
      <link>https://dev.to/yura_solovey</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/yura_solovey"/>
    <language>en</language>
    <item>
      <title>Your AI Wrote the Spec. Why Does the Team Still Disagree on What to Build?</title>
      <dc:creator>Yura Solovey</dc:creator>
      <pubDate>Tue, 29 Sep 2026 14:24:19 +0000</pubDate>
      <link>https://dev.to/yura_solovey/your-ai-wrote-the-spec-why-does-the-team-still-disagree-on-what-to-build-28m5</link>
      <guid>https://dev.to/yura_solovey/your-ai-wrote-the-spec-why-does-the-team-still-disagree-on-what-to-build-28m5</guid>
      <description>&lt;h1&gt;
  
  
  Your AI Wrote the Spec. Why Does the Team Still Disagree on What to Build?
&lt;/h1&gt;

&lt;p&gt;AI is very good at making documents look finished.&lt;/p&gt;

&lt;p&gt;Give it meeting notes, a few old documents, a customer request, and a short explanation from a product manager. A few minutes later, there is a PRD. It has a problem statement, goals, user stories, acceptance criteria, edge cases, and sometimes even a list of risks.&lt;/p&gt;

&lt;p&gt;Not long ago, preparing that package could take half a week.&lt;/p&gt;

&lt;p&gt;That is exactly why it is so easy to feel that the work is already done.&lt;/p&gt;

&lt;p&gt;But a well-written specification does not mean the team has agreed on what it is building.&lt;/p&gt;

&lt;h2&gt;
  
  
  The document looked ready
&lt;/h2&gt;

&lt;p&gt;Consider a fairly ordinary change.&lt;/p&gt;

&lt;p&gt;A company wants to change its order-approval rules. The meeting notes say that orders above a certain limit need an additional review. Old documentation says VIP customers have a simplified path. A message from Finance mentions that the limit depends on the contract type.&lt;/p&gt;

&lt;p&gt;A product manager asks AI to turn all of that into one specification.&lt;/p&gt;

&lt;p&gt;The model produces a perfectly readable rule:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If an order exceeds the approval threshold, block processing until a manager approves it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Then come the user story, the acceptance criteria, and QA scenarios. The developer sees a clear rule. The tester sees clear conditions. Work begins.&lt;/p&gt;

&lt;p&gt;The problem is that the sentence above already contains several decisions nobody actually made.&lt;/p&gt;

&lt;p&gt;What is the approval threshold? Is it one limit for everyone, or a separate limit for each contract? Should an order really be blocked, or merely marked for review after processing? Does the rule apply to VIP customers? Who is a manager in this process? What happens if approval does not arrive before the end of the business day?&lt;/p&gt;

&lt;p&gt;The AI did not necessarily invent something obviously wrong. It turned incomplete information into coherent language.&lt;/p&gt;

&lt;p&gt;And teams are very good at mistaking coherence for a decision.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem is not that the model wrote the text
&lt;/h2&gt;

&lt;p&gt;A product specification has never been just text.&lt;/p&gt;

&lt;p&gt;Behind it are sources, contradictions, clarifications, agreements, versions, and people who can say: yes, this is what we mean.&lt;/p&gt;

&lt;p&gt;When a person writes the document, part of that work often happens naturally. An analyst notices an inconsistency. A product manager goes back to the customer. A developer asks what “block” means. Someone takes ownership of the decision, and the decision stays somewhere in the history of the work.&lt;/p&gt;

&lt;p&gt;AI can ask clarifying questions too. But it is also very good at continuing when no one asks it to stop.&lt;/p&gt;

&lt;p&gt;The faster it produces polished artifacts, the cheaper it becomes to scale ambiguity.&lt;/p&gt;

&lt;p&gt;Before AI, an unclear requirement might have sat in a draft for several days and triggered a discussion. Now the same unclear requirement can become a PRD, a technical design, a Jira ticket, acceptance criteria, and QA scenarios in an hour.&lt;/p&gt;

&lt;p&gt;Every document may be internally consistent.&lt;/p&gt;

&lt;p&gt;Every document may also be built on an assumption nobody approved.&lt;/p&gt;

&lt;h2&gt;
  
  
  AI accelerates bad decisions too
&lt;/h2&gt;

&lt;p&gt;At this point, people often say that product managers and analysts will not be replaced because AI does not understand the business. That is too simple.&lt;/p&gt;

&lt;p&gt;AI can understand a surprising amount of described business context. It can find relationships across documents, formulate alternatives, surface missing edge cases, and produce a much better first draft than a person staring at a blank page.&lt;/p&gt;

&lt;p&gt;But there is a difference between proposing a decision and having the authority to finish one.&lt;/p&gt;

&lt;p&gt;The model does not know which of two conflicting requirements the company is willing to postpone. It is not accountable to the customer for a costly interpretation. It does not own the trade-off between speed, risk, revenue, and technical debt.&lt;/p&gt;

&lt;p&gt;Sometimes the right response to a requirement is not a specification.&lt;/p&gt;

&lt;p&gt;Sometimes the right response is: this needs a decision from the owner of the process.&lt;/p&gt;

&lt;h2&gt;
  
  
  What becomes the new work of product management
&lt;/h2&gt;

&lt;p&gt;AI removes part of the mechanical work of drafting and structuring.&lt;/p&gt;

&lt;p&gt;But that makes the things that used to disappear between meetings more important:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;which sources are canonical;&lt;/li&gt;
&lt;li&gt;where requirements conflict;&lt;/li&gt;
&lt;li&gt;what is a fact and what is an assumption;&lt;/li&gt;
&lt;li&gt;who has the authority to approve a decision;&lt;/li&gt;
&lt;li&gt;which artifacts become outdated after a change;&lt;/li&gt;
&lt;li&gt;under which conditions the team is allowed to start implementation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is not bureaucracy for its own sake.&lt;/p&gt;

&lt;p&gt;If one rule changes and the Jira ticket, technical design, and QA scenarios remain untouched, the team is already working with several versions of reality. AI simply helps make that problem faster and more neatly formatted.&lt;/p&gt;

&lt;p&gt;The minimum state for such a process can be very small:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;decision&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;question&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Should&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;VIP&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;orders&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;bypass&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;manual&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;approval?"&lt;/span&gt;
  &lt;span class="na"&gt;sources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;customer_meeting_2026_09_15&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;finance_policy_v3&lt;/span&gt;
  &lt;span class="na"&gt;owner&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;product_manager&lt;/span&gt;
  &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unresolved&lt;/span&gt;

&lt;span class="na"&gt;dependent_artifacts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;prd&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;draft&lt;/span&gt;
  &lt;span class="na"&gt;technical_design&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;blocked&lt;/span&gt;
  &lt;span class="na"&gt;qa_scenarios&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;blocked&lt;/span&gt;

&lt;span class="na"&gt;completion_allowed&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is nothing intelligent about this fragment. It does not solve the problem for a person.&lt;/p&gt;

&lt;p&gt;But it does not allow the process to pretend that the problem has been solved.&lt;/p&gt;

&lt;h2&gt;
  
  
  From a good prompt to a controlled process
&lt;/h2&gt;

&lt;p&gt;You can put all these rules into a long prompt: do not make assumptions, ask clarifying questions, check contradictions, do not move on without approval.&lt;/p&gt;

&lt;p&gt;But after a few iterations, those rules get mixed with examples, corrections, and earlier responses.&lt;/p&gt;

&lt;p&gt;Then one requirement changes. The model updates the PRD but not the QA scenarios. Or it keeps generating artifacts even though the key question still has no answer.&lt;/p&gt;

&lt;p&gt;For work like this, we need more than better instruction text.&lt;/p&gt;

&lt;p&gt;We need to make state, sources, dependencies, checks, and human decision points explicit.&lt;/p&gt;

&lt;p&gt;That is the problem &lt;a href="https://github.com/solo-vey/Ordo" rel="noopener noreferrer"&gt;Ordo&lt;/a&gt; is trying to address: a way to describe AI-assisted processes so a model can help create and validate artifacts without turning unapproved assumptions into apparently finished decisions.&lt;/p&gt;

&lt;p&gt;AI can write the specification.&lt;/p&gt;

&lt;p&gt;But if the team still does not know what it is building after the specification appears, the problem is not the quality of the prose.&lt;/p&gt;

&lt;p&gt;The problem is that the document finished before the decision did.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/solo-vey/Ordo" rel="noopener noreferrer"&gt;Explore Ordo on GitHub&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>productmanagement</category>
      <category>architecture</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>When a Prompt Stops Being Just a Prompt</title>
      <dc:creator>Yura Solovey</dc:creator>
      <pubDate>Fri, 25 Sep 2026 11:08:08 +0000</pubDate>
      <link>https://dev.to/yura_solovey/when-a-prompt-stops-being-just-a-prompt-205g</link>
      <guid>https://dev.to/yura_solovey/when-a-prompt-stops-being-just-a-prompt-205g</guid>
      <description>&lt;h1&gt;
  
  
  When a Prompt Stops Being Just a Prompt
&lt;/h1&gt;

&lt;p&gt;Most AI processes begin in the same way.&lt;/p&gt;

&lt;p&gt;There is a task. There is a chat. There is a long instruction that gradually accumulates clarifications, exceptions, references to earlier answers, and phrases such as “as we agreed before.” After a few iterations, the model produces something useful. Usually, that is where the story ends.&lt;/p&gt;

&lt;p&gt;But at some point, a useful result is no longer enough.&lt;/p&gt;

&lt;p&gt;Not because the model suddenly became worse. And not necessarily because the prompt is bad. The problem starts when the same work needs to be repeated, handed over to another person, or changed in one place without breaking everything else.&lt;/p&gt;

&lt;p&gt;At that point, we no longer have just a prompt.&lt;/p&gt;

&lt;p&gt;We have a program that was never explicitly described.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the process hides
&lt;/h2&gt;

&lt;p&gt;Imagine a model helping prepare a package for a new feature: a short task description, technical requirements, acceptance criteria, QA scenarios, and a list of documentation changes.&lt;/p&gt;

&lt;p&gt;At first, everything looks fine. The chat already contains context. The model knows the component names. It remembers that some fields were agreed to be mandatory earlier. After one or two corrections, the document package looks perfectly reasonable.&lt;/p&gt;

&lt;p&gt;Now try asking a few simple questions.&lt;/p&gt;

&lt;p&gt;Which data was the original input?&lt;br&gt;&lt;br&gt;
What response or user action counts as approval?&lt;br&gt;&lt;br&gt;
What should happen if a key value changes after approval?&lt;br&gt;&lt;br&gt;
Which documents can no longer be considered current after that change?&lt;br&gt;&lt;br&gt;
Can someone else repeat the process without reading the entire chat from the beginning?&lt;/p&gt;

&lt;p&gt;If the answers are hidden somewhere between the twentieth and fortieth message, then the process already exists. It is simply buried inside the conversation.&lt;/p&gt;

&lt;p&gt;And that is exactly why it is difficult to control.&lt;/p&gt;

&lt;h2&gt;
  
  
  “It worked once” is a weak criterion
&lt;/h2&gt;

&lt;p&gt;With AI, it is easy to treat one successful run as proof that an instruction works.&lt;/p&gt;

&lt;p&gt;The model received complete input, asked the right questions, generated all the expected documents — so everything must be fine. But such a run checks only one route: the most convenient one.&lt;/p&gt;

&lt;p&gt;It does not show what happens when a mandatory fact is missing. It does not show whether the model returns to dependent documents after a previously approved value changes. It does not show whether QA can use a scenario without asking the author for extra explanation. And it certainly does not show whether the model will finish the process with a plausible assumption when it should have stopped.&lt;/p&gt;

&lt;p&gt;This does not mean we need the model to expose its internal reasoning. For most practical tasks, that would not be useful.&lt;/p&gt;

&lt;p&gt;What we need to make visible is what matters to the process: sources, versions, mandatory checks, approvals, dependencies between artifacts, and the conditions under which the process is allowed to finish at all.&lt;/p&gt;

&lt;h2&gt;
  
  
  From instruction to contract
&lt;/h2&gt;

&lt;p&gt;The difference can be stated quite simply.&lt;/p&gt;

&lt;p&gt;A prompt tells the model what to do.&lt;/p&gt;

&lt;p&gt;A process also defines under which conditions the result can be accepted.&lt;/p&gt;

&lt;p&gt;For example, it is not enough to require a &lt;code&gt;rollback&lt;/code&gt; section in the final output. Formally, one sentence may be enough: “restore the previous value.” But for someone who needs to execute the test independently, that may be nowhere near sufficient.&lt;/p&gt;

&lt;p&gt;They need to know which data changed, which events were created, what must be cleaned up after the scenario, and which state the system should be in before the next run.&lt;/p&gt;

&lt;p&gt;The field exists in the document. The working instruction does not.&lt;/p&gt;

&lt;p&gt;This is the kind of distinction that gets lost when we validate only the structure of the result instead of the process that produced it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you can do now
&lt;/h2&gt;

&lt;p&gt;You do not need to invent a new language or build a platform for every AI task.&lt;/p&gt;

&lt;p&gt;But for a process that will be repeated, it is worth making a few things explicit:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;what counts as input;&lt;/li&gt;
&lt;li&gt;which results must be produced;&lt;/li&gt;
&lt;li&gt;which facts the model is not allowed to invent;&lt;/li&gt;
&lt;li&gt;which changes make earlier results outdated;&lt;/li&gt;
&lt;li&gt;what must be checked before completion;&lt;/li&gt;
&lt;li&gt;who approves the transition to the next step, and when.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once these things are written down, a long instruction stops being just text for a model. It starts taking on the properties of a process: state, transitions, checks, and completion rules.&lt;/p&gt;

&lt;p&gt;That question is where &lt;a href="https://github.com/solo-vey/Ordo" rel="noopener noreferrer"&gt;Ordo&lt;/a&gt; began: as a project for designing, validating, executing, testing, and improving structured AI-assisted processes. Not to replace human judgment with another layer of automation, but to keep important rules from remaining hidden inside a chat.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/solo-vey/Ordo" rel="noopener noreferrer"&gt;Explore the Ordo repository&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>architecture</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Can We Debug AI Models?</title>
      <dc:creator>Yura Solovey</dc:creator>
      <pubDate>Mon, 27 Jul 2026 12:58:59 +0000</pubDate>
      <link>https://dev.to/yura_solovey/can-we-debug-ai-models-587j</link>
      <guid>https://dev.to/yura_solovey/can-we-debug-ai-models-587j</guid>
      <description>&lt;p&gt;We are not really working with AI models yet — we are playing with them like children who have been allowed to press buttons on a supercomputer. We already know a few commands. We can produce an impressive result and repeat a sequence that sometimes works. But we do not understand what happens inside, why the model chose this particular answer, or what will break after the next edit.&lt;/p&gt;

&lt;p&gt;Until we learn, at least well enough, how a model moves through an instruction and reaches a decision, we will use only the small fraction of its capabilities that we happened to notice from the outside. We will keep pressing buttons instead of controlling the process. We will obtain isolated results instead of using the system to its full potential.&lt;/p&gt;

&lt;p&gt;In the first three parts of this series, a long instruction became a hidden program, a correct output stopped being proof of a correct process, and local validators proved unable to guarantee global control. One final question remains: can we step through a model’s execution like a program in a debugger and see exactly where it took the wrong turn?&lt;/p&gt;

&lt;h2&gt;
  
  
  We Are Still Changing AI Instructions Like a Black Box
&lt;/h2&gt;

&lt;p&gt;In a conventional program, a bug may not be fixed on the first attempt, but a debugger radically narrows the search space. We set a breakpoint, run the program, inspect variable values, step through transitions, and see which condition fired. With an AI process, we often work differently: we add a rule about approval, run the process, see the same failure, repeat the rule in the correction section, get a pass — and then discover that another branch has broken. From the outside, this looks like debugging. In practice, it is black-box testing by trial and error.&lt;/p&gt;

&lt;p&gt;The worst part is not that one change can cause a regression. The real problem is that we often do not know why the second run passed. Did the model actually start applying the new rule? Did it happen to choose another branch? Did the new wording outweigh another part of the context? Or did the old failure simply not appear in this run? When all we can see is &lt;code&gt;input → output&lt;/code&gt;, a positive result explains the mechanism of improvement no better than a negative result localises the mechanism of failure.&lt;/p&gt;

&lt;h2&gt;
  
  
  One Wrong Result Can Have Several Different Causes
&lt;/h2&gt;

&lt;p&gt;Let us return to the running example with the &lt;code&gt;PREFERENCE_CHANNEL_CHANGED&lt;/code&gt; event. After a correction, the technical specification moved to version 4, but Jira, QA, and approval remained on version 3. In the final package, the result looked the same regardless of where the failure actually occurred:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;expected&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;technical_spec_version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt;
  &lt;span class="na"&gt;jira_version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt;
  &lt;span class="na"&gt;qa_version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt;
  &lt;span class="na"&gt;approval_version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt;

&lt;span class="na"&gt;actual&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;technical_spec_version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt;
  &lt;span class="na"&gt;jira_version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;
  &lt;span class="na"&gt;qa_version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;
  &lt;span class="na"&gt;approval_version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From that output alone, we can form at least five different hypotheses:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the model did not notice the correction;&lt;/li&gt;
&lt;li&gt;the correction was noticed but classified as non-material;&lt;/li&gt;
&lt;li&gt;the invalidation rule was not selected;&lt;/li&gt;
&lt;li&gt;dependent steps were not rerun;&lt;/li&gt;
&lt;li&gt;the completion check ignored the &lt;code&gt;stale&lt;/code&gt; status.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All of them can produce almost the same final package, but they require different fixes. If the model failed to notice the correction, we need to change the change-detection mechanism. If it saw the change but classified it as insignificant, we need to refine the materiality rules. If the classification was correct but the invalidation rule was not applied, the problem lies between decision and action. If invalidation happened but the workflow reused cached artifacts, rewriting the prompt may be the wrong layer of intervention altogether. And if everything was updated correctly but the completion check ignored &lt;code&gt;stale&lt;/code&gt;, then the final invariant is wrong.&lt;/p&gt;

&lt;p&gt;Without a debugger, we see one failure and five possible places to patch. So we add a rule where it seems most logical and hope we guessed correctly.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Should a Debugger for an AI Process Show?
&lt;/h2&gt;

&lt;p&gt;In a conventional debugger, we care about more than the final exception. We want to know which data entered a function, what values the variables held, which condition returned &lt;code&gt;true&lt;/code&gt;, which branch was selected, and what state existed after the step. For an AI process, the questions are remarkably similar:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;which node was executing;&lt;/li&gt;
&lt;li&gt;what data the model received;&lt;/li&gt;
&lt;li&gt;what answer the human provided;&lt;/li&gt;
&lt;li&gt;what state the model believed was current;&lt;/li&gt;
&lt;li&gt;what change it detected;&lt;/li&gt;
&lt;li&gt;how it classified that change;&lt;/li&gt;
&lt;li&gt;which rules it considered;&lt;/li&gt;
&lt;li&gt;which rule it applied;&lt;/li&gt;
&lt;li&gt;which path it selected;&lt;/li&gt;
&lt;li&gt;which alternatives it rejected;&lt;/li&gt;
&lt;li&gt;what state existed after the decision.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That record already looks much closer to step-by-step debugging:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;debug_step&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;node&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;classify_correction&lt;/span&gt;

  &lt;span class="na"&gt;input&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;old_trigger&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;old_value&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;==&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;email&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;and&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;new_value&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;==&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;sms"&lt;/span&gt;
    &lt;span class="na"&gt;new_trigger&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;new_value&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;==&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;sms"&lt;/span&gt;

  &lt;span class="na"&gt;state_before&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;technical_spec_version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;
    &lt;span class="na"&gt;jira_version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;
    &lt;span class="na"&gt;qa_version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;
    &lt;span class="na"&gt;approval_version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;

  &lt;span class="na"&gt;detected_change&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;field&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;trigger_condition&lt;/span&gt;
    &lt;span class="na"&gt;detected&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

  &lt;span class="na"&gt;decision&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;classification&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;non_material&lt;/span&gt;
    &lt;span class="na"&gt;selected_rule&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;non_material_change_keeps_approval&lt;/span&gt;

  &lt;span class="na"&gt;state_after&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;technical_spec_version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt;
    &lt;span class="na"&gt;jira_status&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;current&lt;/span&gt;
    &lt;span class="na"&gt;qa_status&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;current&lt;/span&gt;
    &lt;span class="na"&gt;approval_status&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;current&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can see a specific breakpoint. The model did not miss the correction. It noticed the change in the correct field, classified it as &lt;code&gt;non_material&lt;/code&gt;, and then applied a rule that kept dependent artifacts current. That is a completely different quality of diagnosis. Instead of the vague “the model updated the approval incorrectly,” we get a specific place to change: explicitly define every change to &lt;code&gt;trigger_condition&lt;/code&gt; as a material correction.&lt;/p&gt;

&lt;h2&gt;
  
  
  But State and Transition Still Do Not Explain Why the Model Chose That Path
&lt;/h2&gt;

&lt;p&gt;Recording that the model selected &lt;code&gt;non_material&lt;/code&gt; is useful, but it is not always enough to make a precise change to the instruction. We also need to see which alternatives it considered, which rule it selected, which facts it relied on, and what it ignored. That is why the &lt;code&gt;debug_record&lt;/code&gt; below contains &lt;code&gt;considered_rules&lt;/code&gt;, &lt;code&gt;selected_rule&lt;/code&gt;, &lt;code&gt;short_reason&lt;/code&gt;, and &lt;code&gt;evidence_refs&lt;/code&gt;: they show not only the external transition, but also the working representation of the decision at that node.&lt;/p&gt;

&lt;p&gt;In our example, the model may have given too much weight to the fact that the event name and schema did not change, while underweighting the change to &lt;code&gt;trigger_condition&lt;/code&gt;, which expanded the population of affected events. Without this layer, we fix the symptom and risk adding an overly broad rule such as “every correction invalidates approval.” That rule would close this failure, but it would also force Jira, QA, and approval to be regenerated after a cosmetic typo correction. Debugging is valuable precisely because it lets us change the classification boundary instead of merely strengthening the prohibition.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Debug Log Should Not Be One Large Explanation at the End
&lt;/h2&gt;

&lt;p&gt;The easiest way to obtain an explanation is to ask the model after the failure: “Why did you reuse the old approval?” It will almost certainly produce a coherent answer. But that answer may have been created after the decision. The model sees the output, sees our question, and generates a plausible story that fits the result. That is useful as a hypothesis, but weak as a debugger.&lt;/p&gt;

&lt;p&gt;A stronger approach is to record the decision while the model is passing through the critical node and to separate execution facts from the model’s own explanation immediately:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;debug_record&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;sequence&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;17&lt;/span&gt;
  &lt;span class="na"&gt;node&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;classify_correction&lt;/span&gt;
  &lt;span class="na"&gt;recorded_before_transition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

  &lt;span class="na"&gt;execution&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;state_before_ref&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;snapshot_16&lt;/span&gt;
    &lt;span class="na"&gt;input_refs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;correction_message_12&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;technical_spec_v3&lt;/span&gt;
    &lt;span class="na"&gt;selected_transition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;update_technical_spec_only&lt;/span&gt;
    &lt;span class="na"&gt;state_after_ref&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;snapshot_17&lt;/span&gt;

  &lt;span class="na"&gt;model_report&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;classification&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;non_material&lt;/span&gt;
    &lt;span class="na"&gt;considered_rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;material_change_invalidates_dependents&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;non_material_change_preserves_approval&lt;/span&gt;
    &lt;span class="na"&gt;selected_rule&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;non_material_change_preserves_approval&lt;/span&gt;
    &lt;span class="na"&gt;short_reason&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Event&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;and&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;output&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;schema&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;did&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;not&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;change"&lt;/span&gt;
    &lt;span class="na"&gt;evidence_refs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;field:event_name&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;field:event_schema&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;field:trigger_condition&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this record, &lt;code&gt;execution&lt;/code&gt; says what the system actually did, while &lt;code&gt;model_report&lt;/code&gt; says how the model described its own decision. The first layer proves which transition was selected and what state resulted. The second helps us localise the rule or criterion that may need to change. They must not be merged, because otherwise a polished explanation can silently replace the factual execution record.&lt;/p&gt;

&lt;h2&gt;
  
  
  State Diff Works Like an Inspection Window
&lt;/h2&gt;

&lt;p&gt;During a long process run, a full snapshot is still necessary as a recovery point, but for a human reader the diff is usually more useful. It immediately shows what changed after the node and what should have changed but did not.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;state_diff&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;snapshot_16&lt;/span&gt;
  &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;snapshot_17&lt;/span&gt;

  &lt;span class="na"&gt;changed&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;canonical.trigger_condition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;old_value&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;==&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;email&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;and&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;new_value&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;==&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;sms"&lt;/span&gt;
      &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;new_value&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;==&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;sms"&lt;/span&gt;
    &lt;span class="na"&gt;canonical.version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;
      &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt;

  &lt;span class="na"&gt;unchanged&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;artifacts.jira.status&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;artifacts.qa.status&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;approval.status&lt;/span&gt;

  &lt;span class="na"&gt;expected_but_missing&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;artifacts.jira.status&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;stale&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;artifacts.qa.status&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;stale&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;approval.status&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;stale&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The failure is visible here almost without explanation. The canonical value changed, but the dependent statuses did not. The &lt;code&gt;debug_record&lt;/code&gt; then helps explain why invalidation did not happen.&lt;/p&gt;

&lt;h2&gt;
  
  
  Knowledge Trace Shows What the Model Actually Used
&lt;/h2&gt;

&lt;p&gt;In a large instruction, one rule may be described in several places. In addition, the model sees chat history, user responses, examples, templates, and previous artifacts. When it makes a decision, we need to know not only the rationale it gives, but also which sources it treated as relevant.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;knowledge_trace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;node&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;classify_correction&lt;/span&gt;

  &lt;span class="na"&gt;rules_used&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;rule&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;approval_required_before_completion&lt;/span&gt;
      &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;section.approval_rules&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;rule&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;non_material_changes_preserve_approval&lt;/span&gt;
      &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;section.correction_handling&lt;/span&gt;

  &lt;span class="na"&gt;user_evidence_used&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;correction_message_12&lt;/span&gt;

  &lt;span class="na"&gt;artifacts_consulted&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;technical_spec_v3&lt;/span&gt;

  &lt;span class="na"&gt;available_but_not_used&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;dependency_map&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;material_change_examples&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the improvement can be even more precise. Perhaps the necessary rule already existed in the instruction, but the model did not use it because the dependency map was too far from the correction section or was not explicitly bound to this node. In that case, copying the rule again may be worse than restructuring the context or binding the rule explicitly to the step.&lt;/p&gt;

&lt;h2&gt;
  
  
  Accumulating Knowledge by Node
&lt;/h2&gt;

&lt;p&gt;One debug trace can help fix one run. The real value appears when failures accumulate not as a random bug list, but as knowledge about specific nodes, transitions, and rules.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;node_improvement_record&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;node&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;classify_correction&lt;/span&gt;

  &lt;span class="na"&gt;observed_failures&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run_017&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;failure&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;trigger_change_classified_as_non_material&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run_024&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;failure&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dependency_impact_underweighted&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run_031&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;failure&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cosmetic_example_overgeneralized&lt;/span&gt;

  &lt;span class="na"&gt;suspected_pattern&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Materiality&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;is&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;inferred&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;mainly&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;from&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;schema&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;compatibility"&lt;/span&gt;

  &lt;span class="na"&gt;proposed_change&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Add&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;explicit&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;semantic-impact&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;criteria&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;before&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;structural&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;compatibility"&lt;/span&gt;

  &lt;span class="na"&gt;regression_risk&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;May&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;over-classify&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;wording-only&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;edits&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;as&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;material"&lt;/span&gt;

  &lt;span class="na"&gt;required_tests&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;trigger_condition_change&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;event_name_typo&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;description_only_change&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;schema_extension&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is no longer just a log. It is improvement memory for one node. When we change the instruction, we can see not only one failure, but the history of how this node failed, which fixes were already tried, which regressions they caused, and which scenarios must be rerun.&lt;/p&gt;

&lt;h2&gt;
  
  
  Replay Turns Debugging into a Controlled Experiment
&lt;/h2&gt;

&lt;p&gt;In a conventional debugger, after a patch we repeat the same execution path. With an AI process, that is harder because a new run may receive different wording, a different context, or reach a different decision before the problematic node. If we simply restart the entire playbook, we are not always testing the change we intended to test.&lt;/p&gt;

&lt;p&gt;We need replay with fixed inputs and the state captured immediately before the failure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;replay&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;source_run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;run_017&lt;/span&gt;
  &lt;span class="na"&gt;start_from&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;node&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;classify_correction&lt;/span&gt;
    &lt;span class="na"&gt;state_ref&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;snapshot_16&lt;/span&gt;

  &lt;span class="na"&gt;fixed_inputs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;correction_message&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;correction_message_12&lt;/span&gt;
    &lt;span class="na"&gt;technical_spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;technical_spec_v3&lt;/span&gt;
    &lt;span class="na"&gt;dependency_map&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dependency_map_v2&lt;/span&gt;

  &lt;span class="na"&gt;changed_instruction&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;material_change_rules_version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;
      &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;6&lt;/span&gt;

  &lt;span class="na"&gt;expected&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;classification&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;material&lt;/span&gt;
    &lt;span class="na"&gt;transition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;invalidate_dependents&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The improvement loop now begins to resemble real debugging: locate the node, inspect state and decision, change one rule, replay from the same snapshot, and only then run the regression suite.&lt;/p&gt;

&lt;p&gt;Without replay, we change a rule and restart the entire process, but we do not know whether the new result was caused by that change.&lt;/p&gt;

&lt;h2&gt;
  
  
  Does Such a Trace Show the Model’s Real Thinking?
&lt;/h2&gt;

&lt;p&gt;This is the hardest question in the article — and it is where the analogy with a conventional debugger begins to break down.&lt;/p&gt;

&lt;p&gt;In a traditional program, a variable really does hold a concrete value at a concrete moment. A condition either returned &lt;code&gt;true&lt;/code&gt; or it did not. A function received certain arguments and selected a particular branch. A debugger does not ask the program to explain itself; it reads the actual state of the mechanism.&lt;/p&gt;

&lt;p&gt;With a model, the situation is not so simple. When it writes &lt;code&gt;short_reason&lt;/code&gt;, &lt;code&gt;considered_rules&lt;/code&gt;, or &lt;code&gt;factors_used&lt;/code&gt;, we are not necessarily revealing a hidden variable that existed inside all along. We are obtaining a structured reconstruction of how its behaviour can be described in the language of our playbook. That reconstruction may be useful, accurate, and sufficient to localise a problem. But it does not become a fact merely because the model wrote it into a debug log.&lt;/p&gt;

&lt;p&gt;That is why &lt;code&gt;debug_record&lt;/code&gt; must separate two layers rigidly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;execution&lt;/code&gt; — what the system actually received, did, and changed;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;model_report&lt;/code&gt; — how the model described the cause of its decision.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The first layer can serve as the foundation of control. The second must be treated as a hypothesis.&lt;/p&gt;

&lt;p&gt;Suppose the model claims that its decision depended on &lt;code&gt;correction_material: false&lt;/code&gt;. We change only that factor and rerun the node:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;causal_probe&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;baseline&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;correction_material&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
    &lt;span class="na"&gt;selected_action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;reuse_approval&lt;/span&gt;

  &lt;span class="na"&gt;intervention&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;correction_material&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

  &lt;span class="na"&gt;expected&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;selected_action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;invalidate_approval&lt;/span&gt;

  &lt;span class="na"&gt;observed&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;selected_action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;reuse_approval&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the action does not change, the claimed factor was not a sufficient cause. The behaviour may have been determined by another part of the context, cached state, or a combination of signals that does not fit a discrete scheme such as “the model chose rule A instead of rule B.”&lt;/p&gt;

&lt;p&gt;That distinction matters. Sometimes the problem is not that the real decision is hidden from us. The problem may be that no clear decision existed in the form assumed by a traditional debugger. In that case, &lt;code&gt;model_report&lt;/code&gt; is not an exposed internal mechanism. It is our best behavioural model.&lt;/p&gt;

&lt;p&gt;A good AI debugger should therefore not promise full internal transparency. Its job is more modest and more practical: turn the model’s explanation into a testable hypothesis, and prevent that hypothesis from replacing the facts of execution.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Do We Actually Get?
&lt;/h2&gt;

&lt;p&gt;Such a debugger does not turn us into “the adult who finally understands the supercomputer.” It does not guarantee full access to the model’s internal mechanism, and it does not prove that each decision existed as a clean sequence of rules, alternatives, and causes.&lt;/p&gt;

&lt;p&gt;It gives us something else — and for engineering, that is more important: an instrument panel around a system that may remain partly opaque.&lt;/p&gt;

&lt;p&gt;We can reliably record:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;which inputs arrived;&lt;/li&gt;
&lt;li&gt;which node executed;&lt;/li&gt;
&lt;li&gt;what state existed before and after the step;&lt;/li&gt;
&lt;li&gt;which transition was selected;&lt;/li&gt;
&lt;li&gt;which artifacts changed;&lt;/li&gt;
&lt;li&gt;which gates passed;&lt;/li&gt;
&lt;li&gt;which path can be reproduced through replay.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Separately, we can ask the model to describe the rules, factors, and alternatives it supposedly relied on. But that description is not the foundation of control. It is a diagnostic hypothesis that must be compared with the execution record, state diff, and causal probes.&lt;/p&gt;

&lt;p&gt;The real transition from play to engineering does not happen when the black box finally explains itself completely. It happens when the process remains controllable even when the model’s explanation is incomplete, simplified, or wrong.&lt;/p&gt;

&lt;p&gt;At that point, improving a long instruction stops being an endless cycle of “rewrite the prompt and see what happens.” It becomes a process of accumulating evidence: this node fails under this combination of inputs; here is the rule it selected; here is the factor that, according to its own report, influenced the decision; here is the causal probe; here is the patch; here is the replay; here is the regression suite; here is the new improvement record.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Boundary of the First Series
&lt;/h2&gt;

&lt;p&gt;At the beginning of this series, we had a large prompt that behaved like a hidden program. Then we learned to distinguish a polished output from a correct process. Next, we made global state explicit and saw that local &lt;code&gt;pass&lt;/code&gt; results do not guarantee correct completion. The final step is to make the execution path visible enough that the process can be debugged and deliberately improved, not merely tested.&lt;/p&gt;

&lt;p&gt;If, after every failure, all we can see is the prompt at the beginning and the document at the end, are we really debugging an AI model — or merely asking the black box for another attempt?&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>architecture</category>
      <category>programming</category>
    </item>
    <item>
      <title>Every Step Passed. Who Was Responsible for the Process?</title>
      <dc:creator>Yura Solovey</dc:creator>
      <pubDate>Thu, 23 Jul 2026 13:38:18 +0000</pubDate>
      <link>https://dev.to/yura_solovey/every-step-passed-who-was-responsible-for-the-process-3eba</link>
      <guid>https://dev.to/yura_solovey/every-step-passed-who-was-responsible-for-the-process-3eba</guid>
      <description>&lt;p&gt;In the second part, I wrote about a package that passed formal validation, looked complete, and still turned out to be a weak handoff. That problem was relatively easy to explain: our validators checked whether sections existed, but not whether the next person could actually do the work. The natural response was to add more checks, tighten the schema, strengthen the checklist, and assume that control had been restored. We did exactly that — and almost immediately ran into a worse case, where the problem was no longer the quality of the documents.&lt;/p&gt;

&lt;h2&gt;
  
  
  Every Step Was Completed
&lt;/h2&gt;

&lt;p&gt;Let’s use the same adapted domain with the &lt;code&gt;PREFERENCE_CHANNEL_CHANGED&lt;/code&gt; event. The workflow completed every step in the correct order. Each local validator approved its document. A person confirmed the key decision, and the final package contained every required artifact. The original requirement was simple: when &lt;code&gt;profile.notificationChannel&lt;/code&gt; changes from &lt;code&gt;email&lt;/code&gt; to &lt;code&gt;sms&lt;/code&gt;, the system should emit one event and record the old and new values in the audit log. The process had several stages: clarify the requirement, create the technical spec, prepare Jira, generate QA scenarios, obtain approval, and assemble the final handoff. At the orchestration level, everything looked completely correct:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;workflow&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;define_requirement&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;generate_technical_spec&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;generate_jira&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;generate_qa&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;request_approval&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;assemble_handoff&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The workflow skipped nothing and preserved the intended order. The technical spec passed its validator, Jira passed its own, and QA passed another. The approval was recorded as well. Then the user made a small correction: the event should no longer be emitted only for the transition &lt;code&gt;email -&amp;gt; sms&lt;/code&gt;, but for any change where the new value equals &lt;code&gt;sms&lt;/code&gt;. The technical spec was updated, passed validation again, and the process continued from where it had stopped. At the level of individual operations, everything still looked logical: the correction had been applied, the updated document was valid, an approval already existed, and every step had completed. We didn’t discover the mistake during validation. We found it only when the implementer started working from Jira and asked why the trigger in the ticket didn’t match the technical spec. At first, we looked for a defect in Jira, then in the validator, and only after several repeated checks did it become clear that the documents were individually valid but belonged to different versions of the decision. Untangling that mismatch took longer than making the correction itself. Jira and QA were still based on the old rule, while the approval referred to the previous version of the technical spec.&lt;/p&gt;

&lt;p&gt;The local technical-spec validator saw only the current document:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;technical_spec_validator&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;event_name&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;trigger_condition&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;audit_fields&lt;/span&gt;
  &lt;span class="na"&gt;result&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pass&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Jira validator saw that the ticket contained acceptance criteria, implementation notes, and test expectations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;jira_validator&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;required_sections&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;acceptance_criteria&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;implementation_notes&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;test_expectations&lt;/span&gt;
  &lt;span class="na"&gt;result&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pass&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The QA validator had no obvious reason to reject its artifact either:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;qa_validator&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;every_case_has&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;setup&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;action&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;verification&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;cleanup&lt;/span&gt;
  &lt;span class="na"&gt;result&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pass&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each of these results was locally correct. The technical spec really was valid. Jira really did contain the required sections. The QA scenarios really were self-contained. The problem wasn’t that one of the validators had “done its job badly.” The problem was that none of them answered a different question: did all of these artifacts belong to the same version of the decision?&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the State Disappeared
&lt;/h2&gt;

&lt;p&gt;After the correction, the global picture should have looked roughly like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;process_state&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;canonical_requirement&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt;
    &lt;span class="na"&gt;trigger_condition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;new_value&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;==&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;sms"&lt;/span&gt;

  &lt;span class="na"&gt;approvals&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;technical_spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;approved_version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;
      &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;stale&lt;/span&gt;

  &lt;span class="na"&gt;artifacts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;technical_spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;based_on_version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt;
      &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;current&lt;/span&gt;
    &lt;span class="na"&gt;jira&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;based_on_version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;
      &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;stale&lt;/span&gt;
    &lt;span class="na"&gt;qa&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;based_on_version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;
      &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;stale&lt;/span&gt;

  &lt;span class="na"&gt;completion_allowed&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But in practice, the process stored only separate successful outcomes: technical spec valid, Jira valid, QA valid, approval present. It had no single place that recorded which version of the canonical requirement had produced each artifact, which version the approval applied to, and which dependencies had become invalid after the correction. As a result, the system combined correct local facts into an incorrect global conclusion:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;completion_check&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;technical_spec_valid&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;jira_valid&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;qa_valid&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;approval_present&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;result&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;complete&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There’s no obvious false statement in this check. All four values can be true at the same time. They’re simply insufficient to justify &lt;code&gt;complete&lt;/code&gt; because they ignore versions, causal dependencies, and invalidation. That distinction matters. Sometimes an AI process ends incorrectly not because of a hallucination, not because a step was skipped, and not even because the output was poor. It fails because the system can’t combine locally correct results into a correct process state.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Workflow Is Not the Same as Process Control
&lt;/h2&gt;

&lt;p&gt;A workflow is good at answering, “What should run next?” It can enforce order, retries, timeouts, branches, and even returns to an earlier step. But a sequence of steps does not, by itself, prove that every dependent object remains current after a change. In our example, the workflow correctly ran &lt;code&gt;generate_jira&lt;/code&gt;, then &lt;code&gt;generate_qa&lt;/code&gt;, then &lt;code&gt;request_approval&lt;/code&gt;. After the correction, it just as correctly ran &lt;code&gt;generate_technical_spec&lt;/code&gt; again. If its model contains no rule saying, “A change to the canonical requirement invalidates Jira, QA, and the approval,” then it has no reason to rerun them. From the workflow’s perspective, that isn’t a failure. It’s a missing rule.&lt;/p&gt;

&lt;p&gt;A minimal orchestration fragment might look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;on_correction&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;rerun&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;generate_technical_spec&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But the real process needs something stronger:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;on_correction&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;invalidate&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;technical_spec_approval&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;jira&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;qa&lt;/span&gt;
  &lt;span class="na"&gt;rerun&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;generate_technical_spec&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;generate_jira&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;generate_qa&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;request_approval&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The difference between these two blocks is not the quality of the wording, and it is not the “intelligence” of the model. In the second case, the process dependencies are explicit. In the first, they’re left as assumptions. The model may infer them, or it may not. Even if it infers them correctly nine times in a row, the tenth run still does not become deterministic.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Validator Is Not the Same as Process Control
&lt;/h2&gt;

&lt;p&gt;A validator answers, “Does this object satisfy a specific rule?” That is a strong and necessary mechanism, but its scope is usually local. It can check a schema, business constraints, completeness, consistency within a document, and even compare two artifacts. But if it is not given the current canonical version, previous approvals, and the dependency graph, it cannot determine whether a document is current in the context of the entire process.&lt;/p&gt;

&lt;p&gt;For example, this validator checks Jira against the technical spec:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;jira_consistency&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;compare&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;jira.acceptance_criteria&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;technical_spec.trigger_condition&lt;/span&gt;
  &lt;span class="na"&gt;result&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pass&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It works only if it receives the current technical spec. If the orchestration layer passes cached version 3, Jira version 3 will successfully validate against technical-spec version 3 even though the canonical requirement is already at version 4. Local consistency is preserved; global correctness is not. That is why “let’s add another validator” does not always solve the problem. First, you have to define which state the validator is supposed to see and why that state is considered current.&lt;/p&gt;

&lt;h2&gt;
  
  
  An Evaluator Does Not Own the Process Either
&lt;/h2&gt;

&lt;p&gt;An evaluator usually scores the quality of the result: completeness, clarity, alignment with criteria, risks, and sometimes an overall score. That is useful, especially when the rules cannot be expressed fully through a strict schema. But an evaluator works with the package it is given. If the package contains mutually consistent but stale Jira and QA artifacts, the evaluator may score them very highly. It is not required to know that a correction existed and should have invalidated them.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;evaluation&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;clarity&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.92&lt;/span&gt;
  &lt;span class="na"&gt;completeness&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.88&lt;/span&gt;
  &lt;span class="na"&gt;internal_consistency&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.95&lt;/span&gt;
  &lt;span class="na"&gt;handoff_quality&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0.90&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That score can be honest and still fail to answer the main question: is this the final package for the current decision? Quality evaluation does not replace provenance, version tracking, or process invariants.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Does “Predictable Behavior” Mean Here?
&lt;/h2&gt;

&lt;p&gt;When people talk about making an AI model deterministic, they sometimes mean getting literally the same response to the same prompt. That is not the strict meaning I’m using here. For generative models, full textual determinism is often both unrealistic and not especially useful: wording may change, arguments may appear in a different order, and the same idea may be expressed in different ways. For a long-running work process, another form of predictability matters more. We do not need identical text, but we do need consistent enforcement of process invariants: after a correction, old approvals become invalid; stale artifacts never enter the final package; missing mandatory facts stop the process; every final artifact has known provenance; and completion is allowed only when all dependencies are current.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;process_invariants&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;correction_invalidates_dependent_artifacts&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;approval_applies_to_exact_version&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;stale_artifacts_block_completion&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;mandatory_missing_data_causes_hard_stop&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;every_final_artifact_has_provenance&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A system like this does not make the model deterministic in the mathematical sense. It makes the boundaries of acceptable behavior predictable. The model can still propose different wording, choose another document structure, or ask an additional question. But it should not be able to complete the process with a stale approval or an unnoticed artifact version.&lt;/p&gt;

&lt;h2&gt;
  
  
  Who Needs to See the Whole Picture?
&lt;/h2&gt;

&lt;p&gt;In practice, you need a layer that owns not one document and not one step, but the state of the process as a whole. That layer may be called a process engine, state machine, execution controller, protocol runtime, or simply an explicit control layer. The name matters less than the responsibility. It needs to see:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;canonical values and their versions;&lt;/li&gt;
&lt;li&gt;the provenance of every artifact;&lt;/li&gt;
&lt;li&gt;the dependency graph;&lt;/li&gt;
&lt;li&gt;the validity of approvals;&lt;/li&gt;
&lt;li&gt;allowed transitions;&lt;/li&gt;
&lt;li&gt;invalidation rules;&lt;/li&gt;
&lt;li&gt;hard stops;&lt;/li&gt;
&lt;li&gt;completion conditions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The workflow can use this state, validators can read it, the evaluator can score the package in its context, and the model can handle individual judgment-heavy tasks. But the conclusion “the process is truly complete” should not appear as a side effect of every local component independently returning &lt;code&gt;pass&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This does not mean every AI process needs to become a complex engine. For a short creative task, that level of control would be excessive. But when a process is long, contains branches, approvals, corrections, several dependent documents, and a requirement for repeatability, global state already exists whether or not we describe it. If we fail to make it explicit, it remains scattered across the prompt, chat history, user memory, and the model’s assumptions.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Practical Check
&lt;/h2&gt;

&lt;p&gt;Before calling an AI process controlled, I would check a few things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Does every artifact record the version of the source it was created from?&lt;/li&gt;
&lt;li&gt;Does a correction invalidate all dependent documents and approvals?&lt;/li&gt;
&lt;li&gt;Can a validator determine that it is checking the current version?&lt;/li&gt;
&lt;li&gt;Is there an explicit distinction between &lt;code&gt;valid&lt;/code&gt;, &lt;code&gt;current&lt;/code&gt;, and &lt;code&gt;approved&lt;/code&gt;?&lt;/li&gt;
&lt;li&gt;Does a stale artifact block final completion?&lt;/li&gt;
&lt;li&gt;Is provenance preserved after retries and backtracking?&lt;/li&gt;
&lt;li&gt;Is completion a separate rule rather than merely the fact that the last step finished?&lt;/li&gt;
&lt;li&gt;Can one layer explain why the process is in its current state?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If those questions do not have clear answers, the process may be well orchestrated, well validated, and even produce high-quality output, but that still does not mean it is controlled.&lt;/p&gt;

&lt;p&gt;If no layer can see the full state of the process, are we controlling the behavior of the AI model — or merely hoping that its locally correct decisions happen to add up to the right result?&lt;/p&gt;

</description>
      <category>ai</category>
      <category>architecture</category>
      <category>testing</category>
      <category>productivity</category>
    </item>
    <item>
      <title>You Validated the Output. Who Validated the Process?</title>
      <dc:creator>Yura Solovey</dc:creator>
      <pubDate>Wed, 22 Jul 2026 16:13:34 +0000</pubDate>
      <link>https://dev.to/yura_solovey/you-validated-the-output-who-validated-the-process-18i8</link>
      <guid>https://dev.to/yura_solovey/you-validated-the-output-who-validated-the-process-18i8</guid>
      <description>&lt;p&gt;In the first part, I wrote about the point where a long instruction stops being just a prompt and starts behaving like an undocumented program. It develops state, transitions, recovery paths, dependencies between documents, and rules that are already difficult to change locally. But even if you accept that, an uncomfortable question remains: how do you know the model actually followed the process you needed? The easiest thing is to look at the result. There’s a Jira ticket, a technical description, acceptance criteria, and several test scenarios. The format is correct, the required sections are present, and the names match. During a quick review, the package looks complete — and that’s exactly where we went wrong more than once. A result that looks correct doesn’t prove that the model used the right sources, performed every required check, and avoided skipping a mandatory step. It also doesn’t prove that the next person won’t have to reconstruct part of the work on their own. The clearest way to see this is through a concrete handoff.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Package Looked Ready
&lt;/h2&gt;

&lt;p&gt;The example below is adapted from a controlled run. I changed the domain, names, fields, and values completely, but kept the failure mechanism intact. Imagine a team adding an internal event called &lt;code&gt;PREFERENCE_CHANNEL_CHANGED&lt;/code&gt;, which should be created when &lt;code&gt;profile.notificationChannel&lt;/code&gt; changes from &lt;code&gt;email&lt;/code&gt; to &lt;code&gt;sms&lt;/code&gt;. At the data level, the requirement might look roughly like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;event&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PREFERENCE_CHANNEL_CHANGED&lt;/span&gt;
  &lt;span class="na"&gt;trigger&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;field&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;profile.notificationChannel&lt;/span&gt;
    &lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;email&lt;/span&gt;
    &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sms&lt;/span&gt;
  &lt;span class="na"&gt;expected&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;emit_once&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;persist_audit_record&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The model follows a long process and produces a Jira ticket, a technical description, acceptance criteria, manual QA scenarios, a short rollback, and a requirement to update tests and documentation. At first glance, everything looks fine: there’s a positive scenario, a negative case, a duplicate check, and a description of the expected event. The names are consistent, the document structure is correct, and the process ends successfully. We’d accepted the package with almost no comments at first. Only when the tester started reading it as a working instruction rather than a neatly assembled document did they stop at the second scenario and ask, “What data am I supposed to prepare here, and what do I clean up afterwards?” The package didn’t answer that, and figuring out what the model had meant took longer than reading the document itself.&lt;/p&gt;

&lt;p&gt;The first gap appeared in Jira. The requirement looked perfectly normal at first, but it was already missing the level of detail needed for implementation. It said something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;technical_requirements&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;update unit tests&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;update documentation&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Formally, the requirement exists, but in practice it doesn’t answer several basic questions: which component needs coverage, which tests are mandatory, which fixtures should be used, where the audit record is stored, and which documentation page should be updated. QA had the same problem. The first scenario was detailed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"case"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"channel changes from email to sms"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"setup"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"create user with notificationChannel=email"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"clear previous audit events"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"update notificationChannel to sms"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"verify"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"event PREFERENCE_CHANNEL_CHANGED exists"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"event was emitted once"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"audit record contains old and new values"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"cleanup"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"restore notificationChannel=email"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"remove generated audit event"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The second scenario already started with “repeat the previous steps, but change the value,” and the third referred back to the first two. The tester had to decide which data was inherited, what needed to be cleaned up between runs, and whether the duplicate check should use the same profile or a new one. There was a rollback too, but only one generic instruction: “return the notification channel to its previous value.” That wasn’t enough for real testing. If a scenario creates an event, changes a profile, and leaves an audit record behind, the rollback needs to explain what to do with each of those effects. Otherwise, the next run already starts from a different state than the one described in setup. The package didn’t look obviously wrong: it covered the right topics, contained every expected section, and even read convincingly. The problem only became visible when someone tried to use it as a handoff. That was when it became clear that the document was simply pushing part of the work onto the next person.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Valid Format Isn’t the Same as a Ready Handoff
&lt;/h2&gt;

&lt;p&gt;In many AI processes, result checking starts with schema validation. That’s useful: a schema can confirm that required fields exist, value types are correct, the document has the expected structure, and the JSON or YAML can be parsed programmatically. For example, this check really will catch an empty &lt;code&gt;rollback&lt;/code&gt; and prevent a required field from being omitted:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"required"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"test_cases"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"rollback"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"properties"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"rollback"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"string"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"minLength"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But it doesn’t know whether that rollback is sufficient for an independent tester. The string &lt;code&gt;"restore previous value"&lt;/code&gt; will pass the check just as easily as a complete instruction that cleans up the profile, events, and audit records. The same is true for &lt;code&gt;test_cases&lt;/code&gt;: an array with five elements is formally better than an empty array, but the number of elements says nothing about whether those tests are self-contained. A Jira ticket can also contain acceptance criteria while still failing to define all the technical deliverables. We wrote our first validators at exactly this superficial level: does the section exist, is the field filled in, is there a list of tests? The package passed them honestly because all the required elements were formally present. The problem was in our criteria, not just the model’s behavior. After that, we had to split what we had previously treated as one question into three separate ones:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Does the result have the correct form?&lt;/li&gt;
&lt;li&gt;Does it contain enough information for the next person?&lt;/li&gt;
&lt;li&gt;Was it produced through the right process?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The answers don’t always match. A document can be formally correct and still be a weak handoff. A handoff can be sufficient even if the model skipped some of its internal checks. The model can follow every step and still produce a badly written document. When all of that is collapsed into one score or one final gate, some classes of failure simply disappear from view.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Does “the Right Process” Actually Mean?
&lt;/h2&gt;

&lt;p&gt;It’s easy to go too far in the other direction and demand that the model repeat every internal step word for word, but for most tasks that would be unnecessary. I’m not interested in exposing the model’s hidden chain of thought or controlling every internal operation. A practical process is what can be recorded externally: which sources were used, which mandatory checks were completed, which decisions a person approved, what happened after a correction, and why the process ended in that particular final state. If a document changes after approval, the old approval should no longer count as valid. If the model goes back to an earlier step and changes a canonical value, dependent documents should be regenerated or explicitly rechecked. If mandatory data is missing, the process should stop rather than fill the gap with a plausible assumption. You can think of this as a very simple process state. There’s nothing complicated here: a few versions, statuses for dependent artifacts, and an explicit answer to whether completion is allowed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;canonical_value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;sms&lt;/span&gt;
  &lt;span class="na"&gt;approved_version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;
  &lt;span class="na"&gt;current_version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt;
  &lt;span class="na"&gt;dependent_artifacts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;jira&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;stale&lt;/span&gt;
    &lt;span class="na"&gt;qa&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;stale&lt;/span&gt;
    &lt;span class="na"&gt;technical_spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;current&lt;/span&gt;
  &lt;span class="na"&gt;completion_allowed&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This fragment doesn’t explain how the model “thought.” It only records what matters for execution: the current version, the status of dependent documents, and whether the process may finish. An ordinary prompt often doesn’t make those things explicit. They exist somewhere in the text, the chat history, and previous responses, but not necessarily as controlled state.&lt;/p&gt;

&lt;h2&gt;
  
  
  One Successful Run Proves Almost Nothing
&lt;/h2&gt;

&lt;p&gt;Another trap is testing a long process only on the happy path. The model receives complete input, the user answers every question, the documents are approved on the first attempt, and the package is created successfully. After a run like that, it’s easy to conclude that the instruction works. In the first part, I already named a minimum set of five scenarios, and each one catches a different class of problem. A normal successful run checks the base route. A branch-heavy scenario shows whether the model gets lost when alternatives and returns appear. An invalid-or-irrelevant-input scenario tests whether it can refuse to build a plausible but incorrect package. A correction scenario shows whether earlier documents and approvals are invalidated. A hard stop checks the most uncomfortable case: whether the model invents a mandatory fact simply because the process “has to” finish. In our case, the happy path produced a polished package, while the scenario in which an independent person had to use the document showed something else: part of the process still had to be reconstructed manually. That’s the difference between “generation completed” and “the process is usable.”&lt;/p&gt;

&lt;h2&gt;
  
  
  Why the Model Could Finish the Package Successfully
&lt;/h2&gt;

&lt;p&gt;In the example above, the model didn’t ignore the task. It produced every expected artifact and touched on the main topics. If the check had asked only whether there was a Jira ticket, QA, rollback, a mention of unit tests, and a full set of documents, the package would have passed without trouble. In fact, it did pass our first validators. But the criterion &lt;code&gt;rollback exists&lt;/code&gt; is much weaker than a real check of rollback quality. For this case, a stronger criterion might look roughly like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;rollback_quality&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;applies_to_each_mutating_case&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;restores_initial_state&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;removes_generated_events&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;clears_audit_records&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;executable_by_independent_tester&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The same applies to QA: having scenarios doesn’t guarantee that each one contains setup, action, verification, and cleanup. The model optimizes for what you describe. If the criterion is defined at the level of section presence, the model can satisfy it at exactly that level. This isn’t unique to AI: a person can also write a formally complete but practically weak document. The difference is scale. A model can generate a large package very quickly, and it can scale incompleteness just as quickly.&lt;/p&gt;

&lt;h2&gt;
  
  
  What You Can Check Right Now
&lt;/h2&gt;

&lt;p&gt;Before accepting an AI-generated package, I’d suggest asking at least a few questions. They don’t replace a full review, but they quickly reveal whether the same gaps we saw in the handoff are hiding behind a correct structure:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Can the next person do the work without an extra explanation from the author?&lt;/li&gt;
&lt;li&gt;Is every test case self-contained?&lt;/li&gt;
&lt;li&gt;Are specific technical deliverables defined, rather than just general wishes?&lt;/li&gt;
&lt;li&gt;Does every state-changing action have its own rollback or cleanup?&lt;/li&gt;
&lt;li&gt;Is it clear which sources support the key values?&lt;/li&gt;
&lt;li&gt;Do earlier approvals become invalid after the document changes?&lt;/li&gt;
&lt;li&gt;Are dependent artifacts updated after a correction?&lt;/li&gt;
&lt;li&gt;Can the process still end successfully when a mandatory fact is missing?&lt;/li&gt;
&lt;li&gt;Are you checking not only the format, but whether the handoff is actually usable?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This checklist doesn’t require a new platform or language. It can be added to code review, QA review, or a normal document review. Sometimes that’s already enough to reveal the difference between a document that looks good and one people can actually work from.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where Should the Control Live?
&lt;/h2&gt;

&lt;p&gt;After cases like this, the natural response is to add more validators. One checks the Jira structure, another checks QA completeness, and a third checks consistency across documents. The workflow controls the order of steps, while a person approves critical decisions. That works to a point, and for a while it can feel as though the problem is solved. Then another question appears: who is responsible for the process as a whole? A workflow knows which step comes next, a validator checks a specific document, an evaluator scores the result, and a person can stop the process. But none of those layers necessarily sees the whole picture on its own: state, sources, approvals, correction paths, final process states, and dependencies between artifacts.&lt;/p&gt;

&lt;p&gt;What if the workflow completed every step, the validators approved every document, and the result was still wrong — who was actually in control of the process?&lt;/p&gt;

</description>
      <category>ai</category>
      <category>testing</category>
      <category>architecture</category>
      <category>productivity</category>
    </item>
    <item>
      <title>When Does a Prompt Become an Undocumented Program?</title>
      <dc:creator>Yura Solovey</dc:creator>
      <pubDate>Mon, 20 Jul 2026 09:47:13 +0000</pubDate>
      <link>https://dev.to/yura_solovey/when-does-a-prompt-become-an-undocumented-program-4i6b</link>
      <guid>https://dev.to/yura_solovey/when-does-a-prompt-become-an-undocumented-program-4i6b</guid>
      <description>&lt;p&gt;When we first decided to bring AI into analysts’ work, the task seemed fairly down-to-earth. The company has several development teams and roughly fifteen analysts. They collect requirements, prepare tasks, describe changes in Confluence, think through testing, and help align future implementation with both business and development. A lot of this work is repetitive, so giving analysts a tool that could prepare first drafts felt like an obvious idea.&lt;/p&gt;

&lt;p&gt;The problem is that a document in this kind of process almost never stands on its own. The same change exists in Jira, in Confluence, in mockups, in test scenarios, and in technical notes. Sometimes there are also separate instructions for making changes in the codebase. Each artifact has its own purpose, but all of them describe the same future system behavior. If the wording drifts apart, that can go unnoticed for several days, until different people begin working from different versions.&lt;/p&gt;

&lt;p&gt;A typical case looked roughly like this (the details and names are changed, but the mechanism is real). Jira said that a &lt;code&gt;status&lt;/code&gt; field could have three values: &lt;code&gt;draft&lt;/code&gt;, &lt;code&gt;active&lt;/code&gt;, and &lt;code&gt;archived&lt;/code&gt;. Confluence still contained an older table with only two values, while the test scenario also checked for &lt;code&gt;disabled&lt;/code&gt;, which had been discussed early on and later rejected. The developer implemented what was written in Jira. The tester opened the scenario and filed a defect because &lt;code&gt;disabled&lt;/code&gt; was missing. Only after that did the analyst compare the documents and realize that each of them preserved a different version of the decision.&lt;/p&gt;

&lt;p&gt;Nothing catastrophic happened. We simply had to go through the documentation again, update the tests, clarify the task, explain to the developer that the code did not need to change, and send the package through review one more time. It’s exactly these “nothing serious” moments that add up to delays, when the same task travels two or three times between an analyst, a developer, and a tester. At some point, it becomes difficult even to say where the original mistake was, because everyone is already working on the consequences.&lt;/p&gt;

&lt;p&gt;So our original goal was practical: help analysts assemble a consistent package of materials more quickly and reduce the number of such returns. We didn’t begin with a theory about formal languages or AI process architecture. We simply wrote prompts and tried to make them good enough for real work.&lt;/p&gt;

&lt;p&gt;This is the first article in a four-part series. The second will look at situations where the final result appears correct even though the process was carried out incorrectly. In the third, I want to examine what workflows, validators, and evaluators actually control, and where a gap remains between them. The last part will review existing approaches. Here, I will focus on how a simple instruction gradually turns into something much more complicated.&lt;/p&gt;

&lt;h2&gt;
  
  
  At First, the Model Only Helped with Drafts
&lt;/h2&gt;

&lt;p&gt;The first scenarios were small. The model asked clarifying questions, helped structure the information, prepared a Jira draft, suggested text for Confluence, and produced several test scenarios. The analyst reviewed the result, added what was missing, and asked the model to correct inaccuracies.&lt;/p&gt;

&lt;p&gt;AI still works well in that format. A short conversation, one or two documents, and a quick human review are enough for many tasks. Building a separate mechanism around every such interaction would simply create unnecessary work.&lt;/p&gt;

&lt;p&gt;Technically, these were ordinary chats with sets of files that we passed to the model together with the instructions. Later, we moved such processes into a controlled environment without direct access to production Jira or Confluence.&lt;/p&gt;

&lt;p&gt;The complexity grew gradually. Even a relatively small change could involve forty or fifty analytical steps. First we collected the input information, then clarified data structures and parameters, prepared several related drafts, sent them for review, collected comments, made corrections, and checked the documents against one another again. At the end, we had to add real links to the pages and tasks that had been created. Technical operations were happening in parallel: values were copied between files, required fields were checked, dependent artifacts were updated, and validation was run again.&lt;/p&gt;

&lt;p&gt;In the end, the model had to follow a long route and prepare around seven artifacts. Each individual result could look acceptable, but that was not enough. We also had to make sure that names, parameters, rules, and constraints did not drift apart across documents after several rounds of edits.&lt;/p&gt;

&lt;p&gt;We still called it a prompt because, technically, we were running one large textual instruction. At the same time, it already contained a sequence of actions, dependencies between steps, transition conditions, returns to previous stages, repeated checks, and rules for handling errors. I didn’t see the significance right away. For quite a while, I thought it was simply a very large prompt that needed better organization.&lt;/p&gt;

&lt;h2&gt;
  
  
  Maintenance Turned Out to Be Harder Than the First Version
&lt;/h2&gt;

&lt;p&gt;It’s almost impossible to write a process like this correctly on the first attempt. Even when the business logic is well understood, there is still the question of how the model will interpret each phrase in a particular context.&lt;/p&gt;

&lt;p&gt;We ran the instruction and looked for where it failed. If the model skipped a check, we added a clarification. If it filled a field incorrectly, we added an example. When two documents started contradicting one another, another validation step appeared in the process. The next run exposed a new problem, and the instruction grew by several more paragraphs.&lt;/p&gt;

&lt;p&gt;Every change had a specific reason, so for a long time it didn’t feel as though we were doing anything dangerous. The text was simply becoming more detailed. After dozens of iterations, however, it contained both the description of the work and traces of all the failures we had encountered before. In one of the larger experiments, the total instruction set grew to almost one megabyte. It was spread across several files and included many sections, examples, and checks that had accumulated over a long period.&lt;/p&gt;

&lt;p&gt;That was when local changes stopped staying local. A rule added for one document could alter behavior in another. A new check could affect how the model interpreted a step that appeared much earlier. After fixing one problem, a new one sometimes appeared in a place that had previously been stable.&lt;/p&gt;

&lt;p&gt;Splitting the instructions into files helped people: the material became easier to read and edit. The model, however, did not treat those files as real boundaries. It saw one large shared context, found connections between fragments, and drew conclusions that we hadn’t intended.&lt;/p&gt;

&lt;p&gt;What made this especially unpleasant was that the model’s explanations often sounded convincing. It could find another rule, connect it to the current step, and explain why it had chosen a particular behavior. The reasoning was not random. It really did follow from the way the model had read the full text. But for our process, that connection was wrong, and it became harder and harder to notice such interactions in advance.&lt;/p&gt;

&lt;p&gt;At some point, we couldn’t confidently predict the consequences of every new clarification. The instruction still worked, but maintaining it began to feel like editing a large system without a dependency map.&lt;/p&gt;

&lt;p&gt;And, to be honest, I kept trying for quite a while to solve the problem with the same method that had created it: adding a few more carefully written paragraphs.&lt;/p&gt;

&lt;h2&gt;
  
  
  We Tried to Explain Everything More Clearly
&lt;/h2&gt;

&lt;p&gt;The most obvious response to incorrect model behavior is to describe the requirement in more detail. Early on, that works: an unclear phrase can be rewritten, a weak example can be replaced, and a missing exception can be added to the rules.&lt;/p&gt;

&lt;p&gt;The difficulty begins when a new paragraph has to coexist with dozens of older ones. The author sees it as a local correction. The model receives one more piece of context that can be connected to every other part. This increases both the size of the text and the number of possible ways its parts can be interpreted together.&lt;/p&gt;

&lt;p&gt;We regrouped rules, moved examples closer to the steps they belonged to, separated checks, and removed repetition. Some of those changes had a noticeable effect. The main difficulty remained: editing one fragment of a large instruction didn’t guarantee a change in only one part of the model’s behavior.&lt;/p&gt;

&lt;p&gt;For a long time, I thought we simply needed a better way to write prompts. Eventually, I saw a different picture. The task already had state, transitions, conditions, dependencies between artifacts, and recovery after errors. We were trying to maintain all of that as ordinary prose, even though in terms of complexity it already resembled a small program.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Way the Model Worked with Code Changed How I Saw the Problem
&lt;/h2&gt;

&lt;p&gt;By that point, I had been using language models in software development for more than two and a half years. They made plenty of mistakes there too, but the environment was very different.&lt;/p&gt;

&lt;p&gt;When a model changes a function in a large project, it sees the signature, types, call sites, surrounding code, tests, and the rules of the language itself. Changing an interface affects concrete implementations and usage points. Type violations can be caught during compilation or static analysis. Even a poor change is attached to a specific structure, so its consequences are easier to find and inspect.&lt;/p&gt;

&lt;p&gt;A large textual instruction offers far fewer such anchors. The same requirement can be expressed in dozens of ways, and each phrasing leaves room for interpretation. In a short conversation, that’s usually fine: a person quickly clarifies the intent. In a long process, the decision made at one step becomes context for the next, so a small ambiguity can pass through several stages and surface in a different document.&lt;/p&gt;

&lt;p&gt;There is a temptation to draw too broad a conclusion from this, and I’d rather avoid that. Code is not a universally better way to describe every kind of work. Natural language lets us discuss unclear tasks, change direction, explore options, and avoid defining the whole structure in advance. For analytical work, that is one of its main advantages. In our case, the problem appeared where the model was expected to follow a repeatable procedure with a large number of mandatory conditions.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Happens as Models Become More Capable?
&lt;/h2&gt;

&lt;p&gt;Modern models are increasingly good at solving underspecified tasks, and for analysis or exploration that is a major advantage. A repeatable process is different: the model may see a “better” route where the team needs the agreed one. I’ve already caught myself describing two separate modes of work — “think for yourself” and “follow this literally.” I don’t know yet whether new control mechanisms will reduce that tension.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where I Draw the Boundary Now
&lt;/h2&gt;

&lt;p&gt;I don’t tie the boundary to a specific number of steps. Ten steps can be harder than one hundred if they contain many branches, returns, and dependencies. A long linear process can be easier to control than a short one in which every decision changes several later actions.&lt;/p&gt;

&lt;p&gt;I now test such instructions with a few simple questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Does the process need to preserve state between stages?&lt;/li&gt;
&lt;li&gt;Do earlier decisions affect later ones?&lt;/li&gt;
&lt;li&gt;Are there branches, error recovery paths, or several possible final outcomes?&lt;/li&gt;
&lt;li&gt;Do several documents need to change together?&lt;/li&gt;
&lt;li&gt;Does a correction make earlier approvals or artifacts obsolete?&lt;/li&gt;
&lt;li&gt;Will someone later need to retrace how the result was produced?&lt;/li&gt;
&lt;li&gt;Would one skipped step force another person to reconstruct a missing part of the process?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If most answers are yes, I no longer treat the text as an ordinary prompt. It is a process specification, even when it lives in a Markdown file. That doesn’t mean it must be rewritten in a formal language: sometimes it is enough to move part of the control into code, a workflow, validators, or human review. But testing it with only one successful run is certainly not enough. The minimum we now check is a normal scenario, a branch-heavy scenario, invalid input, recovery after correction, and a hard stop when mandatory facts are missing.&lt;/p&gt;

&lt;p&gt;For now, the practical observation I want to preserve is simple: a large text containing transitions, state, exceptions, and dependencies between artifacts becomes difficult to maintain in the same way as an ordinary instruction.&lt;/p&gt;

&lt;p&gt;Have you had a moment when a prompt stopped feeling like plain text? What caused it: the number of steps, branching, the number of documents, the cost of an error, or something else? And did you manage to keep such a process under control without additional formalization?&lt;/p&gt;

&lt;p&gt;In the next part, I will move to another aspect of the same problem: a final document can look correct and pass formal validation even though the model didn’t follow the process that was expected of it. Later in the series, I will show which control mechanisms we tried, where they helped, and where they merely moved the problem to another layer.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>programming</category>
      <category>architecture</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
