<?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: Takashi Abe</title>
    <description>The latest articles on DEV Community by Takashi Abe (@sho7650).</description>
    <link>https://dev.to/sho7650</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%2F3713571%2F465bf58b-8167-4210-a239-7c75f861a94c.png</url>
      <title>DEV Community: Takashi Abe</title>
      <link>https://dev.to/sho7650</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sho7650"/>
    <language>en</language>
    <item>
      <title>My Obsidian Vault Made 21 Permanent Notes in 3.5 Years, So I Redesigned It</title>
      <dc:creator>Takashi Abe</dc:creator>
      <pubDate>Sun, 11 Oct 2026 03:50:09 +0000</pubDate>
      <link>https://dev.to/sho7650/my-obsidian-vault-made-21-permanent-notes-in-35-years-so-i-redesigned-it-2229</link>
      <guid>https://dev.to/sho7650/my-obsidian-vault-made-21-permanent-notes-in-35-years-so-i-redesigned-it-2229</guid>
      <description>&lt;h2&gt;
  
  
  What I use Obsidian for, and what went wrong
&lt;/h2&gt;

&lt;p&gt;I adopted Obsidian to build knowledge for myself.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Take in lots of personal knowledge through all kinds of methods, and turn it, one by one, into Permanent Notes: finished pieces of my own wisdom&lt;/li&gt;
&lt;li&gt;Build up those Permanent Notes and hand them down as a body of explicit knowledge for those who come after me&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Above all, I believed Obsidian's job was to keep producing solid Permanent Notes out of the collective knowledge I'd gathered. Or so I thought.&lt;/p&gt;

&lt;p&gt;But the Permanent Notes just weren't growing. At all. It was a disaster. Obsidian had been reduced to a plain old diary.&lt;/p&gt;

&lt;p&gt;I'd also assumed, on my own, that the reason Permanent Notes weren't growing was that "the notes aren't connected to each other." But when I dug into the vault with Claude Code's help, the cause turned out to be somewhere else. What was missing was a step where I write down my own claims, and the time to actually do it every day.&lt;/p&gt;

&lt;h2&gt;
  
  
  21 notes in three and a half years, zero in the last 90 days
&lt;/h2&gt;

&lt;p&gt;What I want from Obsidian is to keep producing solid Permanent Notes, one at a time, from the knowledge I collect. Each note is meant to be about the size of an index card, something my future self or people coming after me can reuse.&lt;/p&gt;

&lt;p&gt;When I actually counted, here's what I found:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;Actual&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Number of Permanent Notes&lt;/td&gt;
&lt;td&gt;21 (over roughly three and a half years since February 2023)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Last Permanent Note update&lt;/td&gt;
&lt;td&gt;August 2025. Zero in the last 90 days&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Inflow over the last 90 days&lt;/td&gt;
&lt;td&gt;79 Clippings, 142 AI conversations and summaries, 1 Fleeting Note, 6 Inbox&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;What comes in is other people's writing and AI's writing. There are almost none of my own insights in Obsidian. My Daily Note has a section called "Thoughts in my mind," but I've kept it empty the whole time. Meanwhile, the KPI section where I log sleep hours and running distance gets filled in every day. The sections measured in numbers stuck; the free-writing section didn't.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding links didn't grow my Permanent Notes
&lt;/h2&gt;

&lt;p&gt;In March 2026, I built a Claude skill, &lt;code&gt;/zettelkasten&lt;/code&gt;, that automatically adds tags and links to Fleeting Notes, and ran it on 99 of 281 notes. Each note also got a list of related notes appended at the end. That was seven months ago. Not a single note has been promoted since.&lt;/p&gt;

&lt;p&gt;The reason was obvious the moment I opened the Fleeting Notes. Most of them are just a title. Terrible. One note I wrote in February 2024, "Transcribing from memos is what really matters, so set up a time to transcribe," had an empty body too. I'd written down the answer myself two years earlier, then left it there without ever making time to transcribe. Truly terrible.&lt;/p&gt;

&lt;p&gt;The notes that did have a body had problems too. "Architecture is just one hypothesis" still contained an AI reply, "Thank you for your understanding," verbatim. Worse and worse. Before I could even connect anything, the things I'd be connecting weren't in my own words.&lt;/p&gt;

&lt;h2&gt;
  
  
  The bar of "unshakable intelligence" was what kept me from writing
&lt;/h2&gt;

&lt;p&gt;I had selfishly defined a &lt;code&gt;Permanent Note&lt;/code&gt; as "definitive, unshakable intelligence." With that bar, the day I could call a note finished was never going to come.&lt;/p&gt;

&lt;p&gt;My existing &lt;code&gt;適応度関数.md&lt;/code&gt; (fitness function) note was a textbook example. The title says "fitness" (適応度), yet the body says "goodness of fit" (適合度) nine times, for some mysterious reason. The title and the content don't even fit each other. It cited no sources, and the second half just restated the first. I hadn't touched it since May 2024. The note that was supposed to be unshakable had, in reality, become a note nobody revisits, or even looks at.&lt;/p&gt;

&lt;p&gt;So I lowered the bar to "correct as of the verification date, and revisable later," and set six criteria:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The title is a single-sentence claim&lt;/li&gt;
&lt;li&gt;The body is in my own words and covers the claim, the reasoning, a concrete example, and the scope (around 200–400 Japanese characters)&lt;/li&gt;
&lt;li&gt;There's at least one piece of supporting evidence&lt;/li&gt;
&lt;li&gt;The verification result and the verification date are recorded&lt;/li&gt;
&lt;li&gt;The note makes sense when read on its own&lt;/li&gt;
&lt;li&gt;Its visibility (&lt;code&gt;visibility&lt;/code&gt;) is set&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I added the last one to check whether the content trips any confidentiality concerns: words meant only for me should be explicitly marked as never to be seen by anyone else. If you're going to let arbitrary readers see your notes, you need to decide, note by note, whether each one is okay to publish.&lt;/p&gt;

&lt;h2&gt;
  
  
  I write the claims; AI handles verification
&lt;/h2&gt;

&lt;p&gt;I chose who writes the body from three options. Letting AI write it and just approving would be the fastest and produce the most notes. But that's the same as building one more "storehouse of AI-written text," just like what I already had. I chose the option where I write the claims and reasoning myself. Obviously. &lt;code&gt;Claude Code&lt;/code&gt; can't be expected to understand what's obvious to me, but I think I should be grateful that it catches the biases humans like me let slip by.&lt;/p&gt;

&lt;p&gt;Since these are my claims and my own knowledge in the first place, I narrowed the AI's role down to the minimum:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Check factual statements against primary sources&lt;/li&gt;
&lt;li&gt;Present the strongest counterargument to the claim&lt;/li&gt;
&lt;li&gt;Suggest related notes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I don't let the AI rewrite the body. It only points out what to fix; I'm the one who rewrites. With this setup, about 100 notes a year is the ceiling, but only knowledge I can explain myself gets properly kept.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep the writing entirely inside Obsidian
&lt;/h2&gt;

&lt;p&gt;The first system that came out of this was one Claude Code proposed to suit itself: a flow driven by conversation with Claude Code. It asks me questions, I answer, and I get verification results on the spot. It's comfortable enough when you use it, but you can't run it from inside Obsidian. That didn't match my premise: right after reviewing my Daily Note, spend 15 minutes writing, right there in Obsidian.&lt;/p&gt;

&lt;p&gt;Breaking the process into steps, the only part of this flow that actually needs AI is verification. The questions are the same every time, so I realized I could just put them in a template. So I moved the writing into Obsidian and turned verification into a batch job that runs in the background.&lt;/p&gt;

&lt;p&gt;My daily flow looks like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Look at the "Today's one" section I newly added to my Daily Note. It lists notes awaiting review of their verification results, drafts in progress, and Permanent Note candidates.&lt;/li&gt;
&lt;li&gt;If anything is awaiting review, read that note's "Verification" section. If something needs fixing, fix the body, and for each counterargument, check either "reflected in scope" or "not reflected."&lt;/li&gt;
&lt;li&gt;If nothing is awaiting review, pick one candidate and write a new note from the Permanent Note template.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In the template, right after the field for the claim, I put three questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Why do I think so?&lt;/li&gt;
&lt;li&gt;What experience of mine led me to this, or matched it?&lt;/li&gt;
&lt;li&gt;In what cases does it not apply?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For verification, I run the following command in the vault folder. It inspects all drafts at once and writes the results into each note's "Verification" section.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;claude &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"/zettelkasten verify --all"&lt;/span&gt; &lt;span class="nt"&gt;--allowedTools&lt;/span&gt; &lt;span class="s2"&gt;"Read,Glob,Grep,Edit,Write,WebSearch,WebFetch"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verification takes time, so I stop working for the moment, and the back-and-forth in the moment goes away. In exchange, writing can continue even when the AI isn't running, and I can write from my iPhone or iPad. I set it up so it doesn't depend on where I am or what device I'm on.&lt;/p&gt;

&lt;p&gt;For Permanent Note candidates, besides Fleeting Notes and the Inbox, I also included the directory where I save my AI conversations. I'm starting to feel this is the downside of splitting things into too many directories. If you're just starting out with Obsidian, I recommend keeping directories to a minimum. A single inbox is the easiest place to operate for stashing information and dropping insights.&lt;/p&gt;

&lt;h2&gt;
  
  
  The first note was carved out of a published article
&lt;/h2&gt;

&lt;p&gt;Alongside writing new Permanent Notes from candidates, I decided to also carve them out of articles I've already published. Those articles are already in my own words. They're backed by research and already in publishable shape, so they become finished notes much faster than writing from a candidate from scratch.&lt;/p&gt;

&lt;p&gt;I made the very first one from &lt;a href="https://zenn.dev/sho7650/articles/332f305b24fa68" rel="noopener noreferrer"&gt;my Zenn article on Copier&lt;/a&gt;. Verification confirmed two points. First, a repository created from a GitHub template can't merge updates. Second, Copier can pull updates into a project after it has been generated.&lt;/p&gt;

&lt;p&gt;Two counterarguments came back. One was that the update mechanism itself takes effort to maintain. I reflected that by adding "when there are few downstream projects" to the scope. The other pointed out that the update mechanism hasn't yet been tested with a major change. I finalized that one as-is, and I'll revise the note after trying updates with TypeScript 7.1 or 7.2. Being able to assume revision is thanks to lowering the bar. I'm grateful for that. There are times I just can't help wanting perfection, and at times like that, it becomes a shackle.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I dropped, and what I haven't confirmed yet
&lt;/h2&gt;

&lt;p&gt;There are two things I decided not to do:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Processing the remaining 182 Fleeting Notes. Two-year-old title-only memos can't be decoded even by the person who wrote them. I'll start from new insights and only pick up old memos when they become relevant.&lt;/li&gt;
&lt;li&gt;Consolidating directories. I do think I split things too much, but stages can be managed with a property called &lt;code&gt;status&lt;/code&gt;, so I leaned on &lt;a href="https://community.obsidian.md/plugins/dataview" rel="noopener noreferrer"&gt;Dataview&lt;/a&gt; to run things without moving any folders.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;There are also things I haven't confirmed yet. The template and the list are brand new, and I haven't run the whole thing end to end inside Obsidian. Whether everything from entering a claim, to recording the seed, to moving it into a folder works as expected will take a few days of testing. I've also only tried the batch verification with zero drafts. I plan to set up scheduled runs myself once the system has been running for a while.&lt;/p&gt;

&lt;p&gt;I'll judge whether this works by whether I have at least 8 verified Permanent Notes four weeks from now. By then, I should be able to share the verification results too.&lt;/p&gt;

</description>
      <category>obsidian</category>
      <category>productivity</category>
      <category>ai</category>
      <category>claude</category>
    </item>
    <item>
      <title>Minimal Practical Ontology Sample with MCP and Claude. A mechanism to pass Business Definitions, not just data, to AI.</title>
      <dc:creator>Takashi Abe</dc:creator>
      <pubDate>Fri, 09 Oct 2026 13:13:22 +0000</pubDate>
      <link>https://dev.to/sho7650/minimal-practical-ontology-sample-with-mcp-and-claude-a-mechanism-to-pass-business-definitions-227d</link>
      <guid>https://dev.to/sho7650/minimal-practical-ontology-sample-with-mcp-and-claude-a-mechanism-to-pass-business-definitions-227d</guid>
      <description>&lt;p&gt;The word &lt;em&gt;ontology&lt;/em&gt; has suddenly become familiar in recent years. It appears in data platform product descriptions and in discussions of AI agent design. However, I suspect few people have actually worked with one hands-on. In fact, I was one of them.&lt;/p&gt;

&lt;p&gt;What you cannot understand through words alone, you must understand by running it. So I built a minimal &lt;em&gt;ontology-like thing&lt;/em&gt; using only the bare essentials, and prepared a program that lets Claude act as the orchestrator. The program itself is published on GitHub. In this article, I will describe what that sample is, what it can and cannot do, and what insights emerged from it.&lt;/p&gt;




&lt;h2&gt;
  
  
  What &lt;em&gt;Ontology&lt;/em&gt; Means Here
&lt;/h2&gt;

&lt;p&gt;Let me first define the scope of the term. This is not philosophical ontology, nor OWL from the Semantic Web. What I am dealing with here is the more practical meaning used in the context of business systems.&lt;/p&gt;

&lt;p&gt;In this article, &lt;em&gt;ontology&lt;/em&gt; refers to something that declares the following three things:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Object types:&lt;/strong&gt;&lt;br&gt;
The kinds of &lt;em&gt;things&lt;/em&gt; that appear in business—such as customers and orders—and their attributes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Link types:&lt;/strong&gt;&lt;br&gt;
Relationships between things—for example, a customer &lt;em&gt;places&lt;/em&gt; an order.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Action types:&lt;/strong&gt;&lt;br&gt;
Operations permitted on things—for example, &lt;em&gt;canceling&lt;/em&gt; an order. This includes the conditions under which the action can be executed and what changes as a result.&lt;/p&gt;

&lt;p&gt;The third one is the key. If you only have attributes and relationships, that is no different from an ER diagram or a schema. When you include &lt;em&gt;what is allowed to be done&lt;/em&gt; in the declaration, the definition of data transforms into the definition of business. While a database says &lt;em&gt;the order table has a status column&lt;/em&gt;, an ontology says &lt;em&gt;only unshipped orders can be canceled&lt;/em&gt;. This difference is the subject of this article.&lt;/p&gt;

&lt;p&gt;Let me touch on how this differs from similar concepts. Database input rules and validation ensure that a single piece of data has the correct form—for example, &lt;em&gt;status can only be open, shipped, or cancelled&lt;/em&gt;. Business logic holds judgments like &lt;em&gt;if already shipped, never allow cancellation again&lt;/em&gt; inside application code.&lt;/p&gt;

&lt;p&gt;Both are very important for actual business execution, but the former only looks at the shape of data, while the latter buries judgments deep in code where they become a black box. An ontology can combine both into a single declaration—&lt;em&gt;only unshipped orders can be canceled&lt;/em&gt;—and place it outside the code, in a form readable by both humans and AI. The significant difference is that what is being protected is not the database or the application, but the definition of the business itself. This difference in where things are placed is likely the key point that distinguishes the three.&lt;/p&gt;


&lt;h2&gt;
  
  
  Overview of the Sample Program
&lt;/h2&gt;

&lt;p&gt;The configuration consists of only four elements.&lt;/p&gt;

&lt;p&gt;First is the &lt;strong&gt;ontology definition&lt;/strong&gt;. Object types, link types, and action types are written in a single YAML file. This is the main component; everything else is supporting cast.&lt;/p&gt;

&lt;p&gt;Second is the &lt;strong&gt;instance store&lt;/strong&gt;. I use PostgreSQL, but there are only two tables. An &lt;code&gt;objects&lt;/code&gt; table that holds all types of objects as &lt;em&gt;type, ID, attributes (JSONB)&lt;/em&gt;, and a &lt;code&gt;links&lt;/code&gt; table that holds relationships as &lt;em&gt;type, from, to&lt;/em&gt;. Because I do not create a table for each type, adding a type to the YAML does not change the DDL.&lt;/p&gt;

&lt;p&gt;Third is the &lt;strong&gt;ontology MCP server&lt;/strong&gt;. It reads the YAML, wraps the database, and exposes only five generic tools to Claude:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;describe_ontology&lt;/code&gt;: Returns a list of types, links, and actions&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;search_objects&lt;/code&gt;: Searches by attributes&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;get_object&lt;/code&gt;: Retrieves a single item&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;traverse&lt;/code&gt;: Follows links&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;execute_action&lt;/code&gt;: Validates preconditions, applies effects if passed, and records an audit log&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I do not add tools for each type. If I designed it with separate searches for customers and orders, the tools would grow to fifty once types reached ten. By keeping it to five generic tools, the number stays at five no matter how many types are added.&lt;/p&gt;

&lt;p&gt;Fourth is the &lt;strong&gt;orchestrator&lt;/strong&gt;. For this project, I leave this entirely to Claude. Simply by registering the MCP server, Claude can call &lt;code&gt;describe_ontology&lt;/code&gt; first, understand what is possible, and then operate accordingly.&lt;/p&gt;

&lt;p&gt;With Docker Compose, the database and MCP server start up together.&lt;/p&gt;


&lt;h2&gt;
  
  
  What This Program Can Do
&lt;/h2&gt;

&lt;p&gt;First, &lt;strong&gt;exploration&lt;/strong&gt;. If you ask &lt;em&gt;What types and actions does this ontology have?&lt;/em&gt;, Claude will explain the definition directly. For questions like &lt;em&gt;List the Gold customers&lt;/em&gt;, &lt;em&gt;Show me all orders from Yamada Trading&lt;/em&gt;, or &lt;em&gt;Whose order is O1003?&lt;/em&gt;, it combines searching and link traversal to answer.&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="err"&gt;❯&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Show&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;me&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;all&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;orders&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Yamada&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Trading&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="err"&gt;I&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;will&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;search&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Yamada&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Trading&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;orders&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;ontology&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;MCP&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;server.&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="err"&gt;⏺&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;ontology&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;describe_ontology&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;(MCP)&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;"version"&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="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"object_types"&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;"Customer"&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;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"A trading customer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"key"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"customer_id"&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;"name"&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;"required"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&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;"tier"&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;"enum"&lt;/span&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="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"values"&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;"bronze"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="s2"&gt;"silver"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="s2"&gt;"gold"&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="nl"&gt;"email"&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;"required"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&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;span class="nl"&gt;"Order"&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;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"An order from a customer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"key"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"order_id"&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;"amount"&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;"integer"&lt;/span&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="kc"&gt;true&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;"status"&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;"enum"&lt;/span&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="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"values"&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;"open"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="s2"&gt;"shipped"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="s2"&gt;"cancelled"&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="nl"&gt;"note"&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;"required"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&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;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"link_types"&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;"places"&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;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"A customer places an order"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"from"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Customer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"to"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Order"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"cardinality"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"one_to_many"&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="nl"&gt;"action_types"&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;"cancel_order"&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;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Cancel an unshipped order"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Order"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"parameters"&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;"reason"&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;"required"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&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="nl"&gt;"preconditions"&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;span class="nl"&gt;"property"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"op"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"eq"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"open"&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="nl"&gt;"effects"&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;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cancelled"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"note"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"$params.reason"&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="nl"&gt;"ship_order"&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;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Mark an order as shipped"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Order"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"parameters"&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;"preconditions"&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;span class="nl"&gt;"property"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"op"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"eq"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"open"&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="nl"&gt;"effects"&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;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"shipped"&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="nl"&gt;"upgrade_tier"&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;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Upgrade a customer tier by one level"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Customer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"parameters"&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;"preconditions"&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;span class="nl"&gt;"property"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tier"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"op"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ne"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"gold"&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="nl"&gt;"effects"&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;"tier"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"$next_enum"&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;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="err"&gt;⏺&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;ontology&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;search_objects&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;(MCP)(type:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Customer"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;filter:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"Yamada Trading"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&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;"result"&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;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;"Customer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"C001"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Yamada Trading"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"tier"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"gold"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"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;"yamada@example.com"&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;span class="err"&gt;⏺&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;ontology&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;traverse&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;(MCP)(type:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Customer"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;id:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"C001"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;link:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"places"&lt;/span&gt;&lt;span class="err"&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;"result"&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;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;"Order"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"O1001"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"amount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;120000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"shipped"&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="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;"Order"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"O1002"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"amount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;45000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"shipped"&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;span class="err"&gt;Yamada&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Trading&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;(Customer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;ID&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;C&lt;/span&gt;&lt;span class="mi"&gt;001&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;tier&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;gold)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;has&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;orders.&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="err"&gt;┌──────────┬─────────┬────────────┬──────┐&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;│&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Order&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;ID&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;│&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Amount&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="err"&gt;│&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Status&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="err"&gt;│&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Note&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;│&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;├──────────┼─────────┼────────────┼──────┤&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;│&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;O&lt;/span&gt;&lt;span class="mi"&gt;1001&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="err"&gt;│&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;120&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;000&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;│&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;shipped&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="err"&gt;│&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;none&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;│&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;├──────────┼─────────┼────────────┼──────┤&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;│&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;O&lt;/span&gt;&lt;span class="mi"&gt;1002&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="err"&gt;│&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;45&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;000&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="err"&gt;│&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;shipped&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="err"&gt;│&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;none&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;│&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;└──────────┴─────────┴────────────┴──────┘&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="err"&gt;The&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;total&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;amount&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;165&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;000&lt;/span&gt;&lt;span class="err"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;There&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;are&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;no&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;unshipped&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;(open)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;or&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;cancelled&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;orders.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, &lt;strong&gt;action execution&lt;/strong&gt;. If you submit &lt;em&gt;Cancel all unshipped orders from Gold customers due to stock shortage&lt;/em&gt;, Claude searches for customers, follows links to gather orders, and executes &lt;code&gt;cancel_order&lt;/code&gt; one by one. A human only needs to give this instruction in a single line of natural language.&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="err"&gt;❯&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Cancel&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;all&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;unshipped&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;orders&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Gold&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;customers&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;due&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;stock&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;shortage&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="err"&gt;I&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;will&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;search&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Gold&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;customers&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;cancel&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;their&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;unshipped&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;(open)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;orders.&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="err"&gt;⏺&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;ontology&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;search_objects&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;(MCP)(type:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Customer"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;filter:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"tier"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"gold"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;limit:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="err"&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;"result"&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;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;"Customer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"C001"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Yamada Trading"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"tier"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"gold"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"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;"yamada@example.com"&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="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;"Customer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"C003"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Suzuki Manufacturing"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"tier"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"gold"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"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;"suzuki@example.com"&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;span class="err"&gt;⏺&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;ontology&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;traverse&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;(MCP)(type:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Customer"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;id:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"C003"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;link:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"places"&lt;/span&gt;&lt;span class="err"&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;"result"&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;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;"Order"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"O1004"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"amount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;80000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"open"&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="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;"Order"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"O1005"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"amount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;15000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cancelled"&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;span class="err"&gt;⏺&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;ontology&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;execute_action&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;(MCP)(action:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cancel_order"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;target_id:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"O1004"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;params:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"reason"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"Stock shortage"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&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;"ok"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"object"&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;"Order"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"O1004"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"amount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;80000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cancelled"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"note"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Stock shortage"&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="err"&gt;There&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;was&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;only&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="err"&gt;unshipped&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;order&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Gold&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;customers,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;cancellation&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;complete.&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="err"&gt;Status&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;affected&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Gold&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;customers&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;orders&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="err"&gt;┌─────────────────────────┬──────────┬─────────┬────────────┬────────────┐&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;│&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Customer&lt;/span&gt;&lt;span class="w"&gt;                &lt;/span&gt;&lt;span class="err"&gt;│&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Order&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;ID&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;│&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Amount&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="err"&gt;│&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Before&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="err"&gt;│&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;After&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="err"&gt;│&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;├─────────────────────────┼──────────┼─────────┼────────────┼────────────┤&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;│&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Yamada&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Trading&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;(C&lt;/span&gt;&lt;span class="mi"&gt;001&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="err"&gt;│&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;O&lt;/span&gt;&lt;span class="mi"&gt;1001&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="err"&gt;│&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;120&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;000&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;│&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;shipped&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="err"&gt;│&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;No&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;change&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="err"&gt;│&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;├─────────────────────────┼──────────┼─────────┼────────────┼────────────┤&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;│&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Yamada&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Trading&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;(C&lt;/span&gt;&lt;span class="mi"&gt;001&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="err"&gt;│&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;O&lt;/span&gt;&lt;span class="mi"&gt;1002&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="err"&gt;│&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;45&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;000&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="err"&gt;│&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;shipped&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="err"&gt;│&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;No&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;change&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="err"&gt;│&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;├─────────────────────────┼──────────┼─────────┼────────────┼────────────┤&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;│&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Suzuki&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Manufacturing&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="err"&gt;│&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;O&lt;/span&gt;&lt;span class="mi"&gt;1004&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="err"&gt;│&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;000&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="err"&gt;│&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;open&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="err"&gt;│&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;cancelled&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="err"&gt;│&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;│&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;(C&lt;/span&gt;&lt;span class="mi"&gt;003&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;&lt;span class="w"&gt;                  &lt;/span&gt;&lt;span class="err"&gt;│&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="err"&gt;│&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="err"&gt;│&lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="err"&gt;│&lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="err"&gt;│&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;├─────────────────────────┼──────────┼─────────┼────────────┼────────────┤&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;│&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Suzuki&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Manufacturing&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="err"&gt;│&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;O&lt;/span&gt;&lt;span class="mi"&gt;1005&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="err"&gt;│&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;000&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="err"&gt;│&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;cancelled&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="err"&gt;│&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;No&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;change&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="err"&gt;│&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;│&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;(C&lt;/span&gt;&lt;span class="mi"&gt;003&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;&lt;span class="w"&gt;                  &lt;/span&gt;&lt;span class="err"&gt;│&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="err"&gt;│&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="err"&gt;│&lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="err"&gt;│&lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;&lt;span class="err"&gt;│&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;└─────────────────────────┴──────────┴─────────┴────────────┴────────────┘&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="err"&gt;The&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;reason&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Stock shortage"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;has&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;been&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;recorded&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;note&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;O&lt;/span&gt;&lt;span class="mi"&gt;1004&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;it&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;also&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;audit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;log.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;All&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;orders&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;from&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Yamada&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Trading&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;were&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;already&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;shipped,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;so&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;there&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;were&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;no&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;cancellation&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;targets.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And it &lt;strong&gt;properly refuses&lt;/strong&gt; when it understands the situation. For example, if you try to cancel a shipped order, the server-side precondition blocks this action. Claude reports: &lt;em&gt;This order has already been shipped, so it could not be canceled.&lt;/em&gt; If you ask for a cancellation without providing a reason, since &lt;code&gt;reason&lt;/code&gt; is a required parameter, Claude will ask you for the reason.&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="err"&gt;❯&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Cancel&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;O&lt;/span&gt;&lt;span class="mi"&gt;1001&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="err"&gt;⏺&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;ontology&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;-&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;get_object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;(MCP)(type:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Order"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;id:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"O1001"&lt;/span&gt;&lt;span class="err"&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;"Order"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"O1001"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"amount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;120000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"shipped"&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="err"&gt;O&lt;/span&gt;&lt;span class="mi"&gt;1001&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;cannot&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;be&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;canceled.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;The&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;current&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;status&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;shipped.&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="err"&gt;The&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;cancel_order&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;action&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;has&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;precondition&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;defined&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;that&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;it&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;can&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;only&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;be&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;applied&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;orders&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;with&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;status&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;open.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Even&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;executed,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;it&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;would&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;be&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;rejected&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;due&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;precondition&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;failure,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;so&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;no&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;changes&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;have&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;been&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;made.&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="err"&gt;The&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;current&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;state&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;of&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;O&lt;/span&gt;&lt;span class="mi"&gt;1001&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;follows:&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="err"&gt;┌──────────┬───────────────────────┬─────────┬────────────┐&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;│&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Order&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;ID&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;│&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Customer&lt;/span&gt;&lt;span class="w"&gt;              &lt;/span&gt;&lt;span class="err"&gt;│&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Amount&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="err"&gt;│&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Status&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="err"&gt;│&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;├──────────┼───────────────────────┼─────────┼────────────┤&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;│&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;O&lt;/span&gt;&lt;span class="mi"&gt;1001&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="err"&gt;│&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Yamada&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Trading&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;(C&lt;/span&gt;&lt;span class="mi"&gt;001&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;│&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;120&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;000&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;│&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;shipped&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="err"&gt;│&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;└──────────┴───────────────────────┴─────────┴────────────┘&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="err"&gt;If&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;you&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;need&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;reverse&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;shipped&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;order,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;this&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;ontology&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;does&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;not&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;have&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;actions&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;defined&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;returns&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;or&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;shipment&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;reversal,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;so&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;you&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;would&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;need&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;handle&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;it&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;through&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;different&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;business&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;workflow.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What I like most is this &lt;em&gt;refusal&lt;/em&gt;. The anxiety about letting AI touch business data usually boils down to &lt;em&gt;Will it do something unnecessary?&lt;/em&gt; In this mechanism, the means to do unnecessary things simply does not exist. There is no tool to directly rewrite the status; state can only change through actions. It is not safe because the AI is smart—it is safe because the logic side is built to be safe.&lt;/p&gt;




&lt;h2&gt;
  
  
  What This Program Cannot Do
&lt;/h2&gt;

&lt;p&gt;Let me also clarify what it cannot do. This is a mix of things deliberately omitted to define the scope of the demo and things I simply have not gotten around to.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Creating and deleting objects.&lt;/strong&gt; &lt;em&gt;Register a new customer&lt;/em&gt; is not possible. Sample data is loaded via SQL.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Creating and deleting links.&lt;/strong&gt; &lt;em&gt;Link this order to Sato Industries&lt;/em&gt; is also not possible.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Range searches and partial matching.&lt;/strong&gt; For &lt;em&gt;orders over 100,000 yen&lt;/em&gt;, Claude would have to fetch everything and filter it himself. This breaks down as the number of records grows.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Preconditions spanning multiple objects.&lt;/strong&gt; Conditions that look at linked items—like &lt;em&gt;cancellation allowed only if the customer is Gold&lt;/em&gt;—cannot be written.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Transactions for batch execution.&lt;/strong&gt; Execution is one item at a time, so if something fails midway, previous items are not rolled back.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Permissions.&lt;/strong&gt; Who executed something is a fixed value. &lt;em&gt;Who can do what&lt;/em&gt; is not implemented.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This list is also the list of what to do next. Range searches and preconditions that look at linked items in particular can be added just by slightly expanding the YAML vocabulary. I will fill these in as I feel &lt;em&gt;this is missing&lt;/em&gt; while building.&lt;/p&gt;




&lt;h2&gt;
  
  
  Insights Gained from This Program
&lt;/h2&gt;

&lt;p&gt;It is a small sample, but running it revealed three things.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;First:&lt;/strong&gt; What should be given to AI is not &lt;em&gt;data&lt;/em&gt; but &lt;em&gt;the definition of the world&lt;/em&gt;. If you give tables and SQL, the AI can do anything. Being able to do anything also means not knowing what it is allowed to do. When you give types and actions, the AI stops hesitating. Watching Claude read the result of &lt;code&gt;describe_ontology&lt;/code&gt; and then proceed with exactly the steps I expected is just like someone reading a manual before starting work—literally someone who reads instructions and then acts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Second:&lt;/strong&gt; Five generic tools are enough. Initially I considered a design that created tools for each type. I did not do so because if I had to rewrite code every time I added a type to the YAML, there would be no point in separating it into YAML. In fact, I added a &lt;code&gt;Product&lt;/code&gt; type and a &lt;code&gt;restock&lt;/code&gt; action to the YAML, restarted the container, and Claude began handling the new type. Separating definitions from code makes changes easy and fast, and improves manageability.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Third:&lt;/strong&gt; Audit logs should be built in from the start as part of actions, not added later. &lt;code&gt;execute_action&lt;/code&gt; always leaves one line, whether it succeeds or is blocked by a precondition. Who, on what type, tried to do what, and what happened. In a world where AI operates, no one can explain &lt;em&gt;why this happened&lt;/em&gt; without this record. I have not implemented it yet, but once &lt;em&gt;who&lt;/em&gt; is added here, it will become nearly the complete audit record needed.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Is Good About Ontology
&lt;/h2&gt;

&lt;p&gt;Summarizing the discussion so far, the goodness of ontology lies in &lt;em&gt;constraints&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;I believe that turning routine work into programs is the original purpose of computers. Having written programs for nearly forty years, that thinking itself has not changed at all. What changed with the arrival of AI is that &lt;em&gt;non-routine parts&lt;/em&gt; can now also be delegated to computers. For example: which customers, which orders, in what sequence to process. Things that humans normally think about and do manually can now be thought about by AI.&lt;/p&gt;

&lt;p&gt;However, delegation requires boundaries. What is allowed, and what is not. Communicating those boundaries through natural language prompts is unreliable. Rather than asking &lt;em&gt;Please do not cancel shipped orders&lt;/em&gt;, it is more certain to build it so cancellation is impossible. An ontology is a mechanism that writes those boundaries as declarations and has the system enforce them.&lt;/p&gt;

&lt;p&gt;There is another good thing. When you write boundaries, the business becomes visible. To write a single line like &lt;em&gt;only unshipped orders can be canceled&lt;/em&gt;, you need to confirm with the business people. Conversations emerge: &lt;em&gt;Are there cases where shipped orders can also be canceled?&lt;/em&gt; &lt;em&gt;Is that a different action called a return?&lt;/em&gt; This conversation is the most important and most easily skipped part when building systems. A single YAML can also become a tool to draw out that conversation. Also, this YAML itself could be called a business design document.&lt;/p&gt;

&lt;p&gt;I believe the job of an architect is not to choose technology, but to express business as structure. An ontology is a place to put that structure in a form AI can read. That is why, even in the age of AI—no, precisely because it is the age of AI—I believe this tool will be effective.&lt;/p&gt;




&lt;h2&gt;
  
  
  Something to Try
&lt;/h2&gt;

&lt;p&gt;If you have business data at hand, forget the table definitions for now and write out these three things on paper:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What kinds of &lt;em&gt;things&lt;/em&gt; appear there?&lt;/li&gt;
&lt;li&gt;What words connect things to things?&lt;/li&gt;
&lt;li&gt;For those things, who, under what conditions, is allowed to do what?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you can write the third one, that is already an ontology. If you cannot write it, that is where the business is ambiguous. Either way, you gain something.&lt;/p&gt;

&lt;p&gt;The sample is on GitHub. Start it up with Docker Compose and ask Claude: &lt;em&gt;Cancel all unshipped orders from Gold customers.&lt;/em&gt; And watch the shipped orders get properly refused. All the value of this mechanism is packed into that moment.&lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>claude</category>
      <category>mcp</category>
    </item>
    <item>
      <title>I Let Claude Code Grill Me, Then Built a Zenn-to-dev.to Crossposting Pipeline</title>
      <dc:creator>Takashi Abe</dc:creator>
      <pubDate>Fri, 09 Oct 2026 07:20:49 +0000</pubDate>
      <link>https://dev.to/sho7650/i-let-claude-code-grill-me-then-built-a-zenn-to-devto-crossposting-pipeline-2p54</link>
      <guid>https://dev.to/sho7650/i-let-claude-code-grill-me-then-built-a-zenn-to-devto-crossposting-pipeline-2p54</guid>
      <description>&lt;p&gt;I built a pipeline that translates the articles I write on Zenn into English and publishes them on &lt;a href="https://dev.to"&gt;dev.to&lt;/a&gt; too. I translate locally with Claude Code, and when I push to &lt;code&gt;main&lt;/code&gt;, GitHub Actions publishes, updates, or unpublishes the article on dev.to.&lt;/p&gt;

&lt;p&gt;My starting point was asherish's "&lt;a href="https://zenn.dev/asherish/articles/blog-repo-setup" rel="noopener noreferrer"&gt;Blog repository setup&lt;/a&gt;." Its setup, which bridges Zenn and dev.to with GitHub Actions, was a direct reference. But by the time I finished, my setup had made different calls from the original in three places:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No file recording dev.to article IDs; match by &lt;code&gt;canonical_url&lt;/code&gt; every time&lt;/li&gt;
&lt;li&gt;Only the original Japanese article holds the publication state&lt;/li&gt;
&lt;li&gt;Detect a stale translation using a hash of the original article&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The result is a setup where &lt;strong&gt;CI writes nothing back to the repository, and the only state lives on dev.to&lt;/strong&gt;. In this post, I'll walk through the order in which I made those decisions, and how I used Claude Code along the way.&lt;/p&gt;

&lt;h2&gt;
  
  
  I settled the requirements by letting Claude Code ask me 30+ questions
&lt;/h2&gt;

&lt;p&gt;The request I first gave Claude Code was just these three lines (originally in Japanese):&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I want to create a GitHub Actions workflow that, when an article is added to articles/ for zenn.dev and becomes "published", posts a translated version of it to dev.to.&lt;br&gt;
Use this as a reference.&lt;br&gt;
Don't decide anything unclear by guessing; check everything with me.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I ran this request through &lt;code&gt;grill-with-docs&lt;/code&gt; from &lt;a href="https://github.com/mattpocock/skills" rel="noopener noreferrer"&gt;Matt Pocock's skill collection for Claude Code&lt;/a&gt;. As the name suggests, it's a skill that grills the user with questions, closing off design branches one at a time. It doesn't ask everything at once; it &lt;strong&gt;only asks a batch of questions whose prerequisites are already settled&lt;/strong&gt;. For example, until "where does translation happen?" is decided, it won't ask "where do we store the translation API key?"&lt;/p&gt;

&lt;p&gt;Here are some of the questions I actually got:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Question&lt;/th&gt;
&lt;th&gt;Claude's recommendation&lt;/th&gt;
&lt;th&gt;My answer&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Should the 18 existing published articles be crossposted too?&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;If the original is edited after publishing, update dev.to too?&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Post to dev.to as a draft, or publish immediately?&lt;/td&gt;
&lt;td&gt;Draft&lt;/td&gt;
&lt;td&gt;Publish immediately&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Where should images be served from (the repo is private)?&lt;/td&gt;
&lt;td&gt;Make the repo public&lt;/td&gt;
&lt;td&gt;Don't bring images&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Translate in CI, or locally?&lt;/td&gt;
&lt;td&gt;CI&lt;/td&gt;
&lt;td&gt;Local Claude Code&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For several questions, my answer differed from the recommendation. Drafts, for instance, were recommended "so you can review the machine translation once." But since I'd decided to translate locally, the review already happens before commit. That "where does translation happen?" question was one Claude added later, after reading the reference article. The reference didn't translate in CI, which contradicted my request ("translate and post with Actions"). That one question decided half of the design that followed.&lt;/p&gt;

&lt;p&gt;Over five rounds, the questions went past 30. Each time an answer was settled, Claude added the terms to &lt;code&gt;CONTEXT.md&lt;/code&gt;. That file pins down what words like "original article," "English version," "publish," and "crosspost" mean in this repository. You could call it a ubiquitous language, in domain-driven design terms. For decisions that would be hard to reverse later, it also recorded two ADRs (Architecture Decision Records: short documents capturing a decision and its reasoning).&lt;/p&gt;

&lt;p&gt;As an aside, I'm writing a Zenn book about ADRs. I'll publish it once it's done.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;ℹ️ &lt;code&gt;grill-with-docs&lt;/code&gt; attaches a recommended answer to each question. You can keep going by just answering "sounds good" to the recommendation, so it isn't as tiring as the question count suggests. Conversely, if you don't want to accept recommendations blindly, make sure to read the reasoning attached to each one.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Translation stays out of CI: I review locally, then commit
&lt;/h2&gt;

&lt;p&gt;My original, rough request was "translate and post with Actions," but in the end I took translation out of CI. The deciding factor was choosing to publish immediately on dev.to. If CI calls an LLM API to translate, English text that no human has ever read goes straight to publication.&lt;/p&gt;

&lt;p&gt;So I split the roles like this:&lt;br&gt;
&lt;/p&gt;

&lt;pre data-lang="mermaid"&gt;&lt;code&gt;flowchart LR
  A[Write the original article] --&amp;gt; B[/translate into English/]
  B --&amp;gt; C[Read the English version and commit]
  C --&amp;gt; D[Push to main]
  D --&amp;gt; E[Actions crossposts to dev.to]&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;Translation happens through the Claude Code skill &lt;code&gt;/translate &amp;lt;slug&amp;gt;&lt;/code&gt;. The skill writes the translated body, and a script called &lt;code&gt;translate-apply.ts&lt;/code&gt; converts Zenn syntax, adds frontmatter, and writes the result to &lt;code&gt;articles_en/&amp;lt;slug&amp;gt;.md&lt;/code&gt;. The skill doesn't commit. I'm the one who reads the English version and commits it, and that's the only review checkpoint.&lt;/p&gt;

&lt;p&gt;This way, I can safely use Claude Code on my subscription without setting up any translation API key. And there's no extra usage cost. The tradeoff is that if I forget to translate, the article just stays un-crossposted. I decided to catch that with the "on hold" warning described later.&lt;/p&gt;

&lt;h2&gt;
  
  
  Don't store dev.to article IDs; look them up by canonical_url
&lt;/h2&gt;

&lt;p&gt;The reference setup stores the mapping between slugs and dev.to article IDs in &lt;code&gt;.devto-mapping.json&lt;/code&gt;, and Actions commits that file back. From the second run onward, it uses that ID to update (PUT).&lt;/p&gt;

&lt;p&gt;This approach caused me two problems. First, the Actions commit collides with my local &lt;code&gt;main&lt;/code&gt;. Unless I pull after every push, my next push gets rejected. Second, what if the post (POST) to dev.to succeeds but committing the mapping fails? On the next run, the ID isn't found, so the same article gets posted again.&lt;/p&gt;

&lt;p&gt;So in my setup, I chose not to keep a mapping at all. On every crosspost, it fetches my article list from the dev.to API and looks for the one whose &lt;code&gt;canonical_url&lt;/code&gt; matches the URL of the original Zenn article. &lt;code&gt;canonical_url&lt;/code&gt; is the URL that tells search engines "the original of this article is over here." Since every crossposted article always has one set, it works as a matching key.&lt;br&gt;
&lt;/p&gt;

&lt;pre data-lang="mermaid"&gt;&lt;code&gt;sequenceDiagram
  participant GA as GitHub Actions
  participant API as dev.to API
  GA-&amp;gt;&amp;gt;API: GET /api/articles/me/all
  API--&amp;gt;&amp;gt;GA: Article list (including unpublished)
  GA-&amp;gt;&amp;gt;GA: Match against originals by canonical_url
  GA-&amp;gt;&amp;gt;API: POST or PUT (only articles with changes)&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;Whether this works depends on how the list API on the &lt;code&gt;dev.to&lt;/code&gt; side behaves. If unpublished articles weren't included in the list, an article that had been unpublished once would be judged "not on dev.to" when republished, and get posted twice. The documentation alone wasn't enough, so I had Claude read the source of &lt;a href="https://github.com/forem/forem" rel="noopener noreferrer"&gt;Forem&lt;/a&gt;, which runs dev.to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/api/articles/me/all&lt;/code&gt; also returns unpublished articles (the docs say "Unpublished articles will be at the top of the list")&lt;/li&gt;
&lt;li&gt;The response includes &lt;code&gt;published&lt;/code&gt;, &lt;code&gt;body_markdown&lt;/code&gt;, &lt;code&gt;tag_list&lt;/code&gt; (an array), and &lt;code&gt;canonical_url&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;Source: &lt;code&gt;app/views/api/v1/articles/me.json.jbuilder&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;body_markdown&lt;/code&gt; is stored exactly as sent; no frontmatter gets added&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That told me the diff check could also be done with a single list fetch. The costs are one list fetch per crosspost, and that if someone manually edits the canonical_url in the dev.to UI, matching breaks. The latter is written down in an ADR as an operational rule.&lt;/p&gt;

&lt;h2&gt;
  
  
  Only the original Japanese article holds the publication state
&lt;/h2&gt;

&lt;p&gt;In the reference setup, the English version's frontmatter also has &lt;code&gt;published&lt;/code&gt;, and CI verifies that it matches the Japanese version. If they don't match, publishing stops.&lt;/p&gt;

&lt;p&gt;In the option I chose, the English version doesn't have &lt;code&gt;published&lt;/code&gt; at all. Actions always reads the original article's &lt;code&gt;published&lt;/code&gt; and makes dev.to match it. If there's only one source to begin with, there's nothing to check for mismatches. Setting the original back to &lt;code&gt;published: false&lt;/code&gt; is all it takes to unpublish on dev.to as well.&lt;/p&gt;

&lt;p&gt;As a result, the English version's frontmatter has just three fields:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;articles_en/.md&lt;/strong&gt;&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="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Why&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;"Harness":&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;a&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;note'&lt;/span&gt;
&lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ai&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;mcp&lt;/span&gt;
&lt;span class="na"&gt;source_hash&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;edf3ae74a6a6...&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What happens to each article is decided as shown below. Only "slugs that have an English version" are processed. The 18 existing articles have no English version, so they naturally fall out of scope without any exclusion list.&lt;br&gt;
&lt;/p&gt;

&lt;pre data-lang="mermaid"&gt;&lt;code&gt;flowchart TD
  A[Has English version] --&amp;gt; B{Original is published}
  B -- Yes --&amp;gt; C{Exists on dev.to}
  B -- Unpublished or deleted --&amp;gt; D{Published on dev.to}
  C -- No --&amp;gt; E[POST and publish immediately]
  C -- Yes --&amp;gt; F{Content changed}
  F -- Yes --&amp;gt; G[Update with PUT]
  F -- No --&amp;gt; H[Do nothing]
  D -- Yes --&amp;gt; I[Unpublish with PUT]
  D -- No --&amp;gt; H&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;The diff check compares four things: title, body, tags, and publication state. The reference setup PUT every published article on every push, but that needlessly changes the updated timestamp on dev.to as well. It also pushes you closer to &lt;code&gt;dev.to&lt;/code&gt;'s rate limits (by default, 9 article creations and 30 updates per 30 seconds).&lt;/p&gt;

&lt;h2&gt;
  
  
  Catch stale translations with a hash of the original
&lt;/h2&gt;

&lt;p&gt;Sometimes I fix the original after publishing and forget to regenerate the English version. To catch this, I record the original article's hash at the time the English version was made, as &lt;code&gt;source_hash&lt;/code&gt; in the English version. At crosspost time, it computes the current original's hash and warns if they don't match.&lt;/p&gt;

&lt;p&gt;So what should that hash be computed from? The first thing that comes to mind is a hash of the whole original file. But then just flipping &lt;code&gt;published: false&lt;/code&gt; to &lt;code&gt;true&lt;/code&gt; changes the hash. If you prepare the translation ahead of time and then publish, you'd get a "translation is stale" warning every single time.&lt;/p&gt;

&lt;p&gt;So I narrowed the hash input down to the title and body:&lt;/p&gt;

&lt;p&gt;$$&lt;br&gt;
h = \mathrm{SHA256}\left(\mathrm{JSON}({\,\mathit{title},\ \mathit{body}\,})\right)&lt;br&gt;
$$&lt;/p&gt;

&lt;p&gt;Changing &lt;code&gt;published&lt;/code&gt;, &lt;code&gt;topics&lt;/code&gt;, or &lt;code&gt;emoji&lt;/code&gt; leaves $h$ unchanged; it only changes when the body or title changes. The tests state exactly this property:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;scripts/lib/hash.test.ts&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;published を切り替えてもハッシュは変わらない&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;published&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;original&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;published: false&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;published: true&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;computeSourceHash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;published&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;computeSourceHash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;original&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A stale English version is still crossposted as-is, with a warning. If crossposting stopped instead, fixing even a single typo would leave dev.to un-updated until the translation was regenerated.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pin down Zenn syntax conversion with tests
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;dev.to&lt;/code&gt; doesn't understand &lt;code&gt;Zenn&lt;/code&gt;'s own syntax, so conversion is needed.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Zenn&lt;/th&gt;
&lt;th&gt;On dev.to&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;:::details タイトル&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;{% details タイトル %}&lt;/code&gt; … &lt;code&gt;{% enddetails %}&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;:::message&lt;/code&gt; (&lt;code&gt;alert&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;A blockquote prefixed with ℹ️ (⚠️)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fence info &lt;code&gt;ts:src/index.ts&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;A bold &lt;code&gt;**src/index.ts**&lt;/code&gt; right before the code&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@[card](URL)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A plain link&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;@[youtube](ID)&lt;/code&gt; etc.&lt;/td&gt;
&lt;td&gt;&lt;code&gt;{% embed URL %}&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;![alt](/images/...)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A note saying "see the original Zenn article for the image"&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Images are replaced with a note because the repository where I manage my Zenn articles is private, so dev.to can't fetch images from it. Making the repository public was an option, but it would expose unpublished drafts too, so I passed on it.&lt;/p&gt;

&lt;p&gt;The conversion isn't left to Claude Code; it's a regex-based script. With an LLM, there's no guarantee the same input produces the same output every time. A deterministic program or script lets you pin its behavior down with tests.&lt;/p&gt;

&lt;p&gt;The trickiest part of the conversion was the constraint that &lt;strong&gt;nothing inside code blocks gets converted&lt;/strong&gt;. Sometimes, like in this very post, I write code examples that explain Zenn syntax itself:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="p"&gt;```&lt;/span&gt;&lt;span class="nl"&gt;md
&lt;/span&gt;:::details これは記法の説明なので、変換してはいけない
:::
&lt;span class="p"&gt;```&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So I went with a somewhat fiddly mechanism: before conversion, each code block is stashed away and replaced with a one-line placeholder (a number wrapped in the Unicode Private Use Area characters &lt;code&gt;U+E000&lt;/code&gt; and &lt;code&gt;U+E001&lt;/code&gt;), and restored after conversion finishes.&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;
  Stash-and-restore code (excerpt)
  &lt;p&gt;&lt;strong&gt;scripts/lib/convert.ts&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;convertZennToDevto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;markdown&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ConvertOptions&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;blocks&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;stashCodeBlocks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;markdown&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;converted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;convertLocalImages&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;originalUrl&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nx"&gt;convertEmbeds&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;convertDetails&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;convertMessages&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;acc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;convert&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;convert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;acc&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;text&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;converted&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;PLACEHOLDER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_m&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="nx"&gt;prefix&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nf"&gt;quoteLines&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;blocks&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)])&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;blocks&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)],&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;br&gt;
&lt;p&gt;&lt;/p&gt;

&lt;p&gt;This approach has a pitfall. Since &lt;code&gt;:::message&lt;/code&gt; is converted into a blockquote, the placeholder line inside it also starts with &lt;code&gt;&amp;gt;&lt;/code&gt;. But when the placeholder is restored, the second and subsequent lines of the code block don't have &lt;code&gt;&amp;gt;&lt;/code&gt;. The blockquote gets cut off mid-code. On restore, it looks at the leading &lt;code&gt;&amp;gt;&lt;/code&gt; and re-applies it to every line of the code.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you didn't decide shows up during implementation
&lt;/h2&gt;

&lt;p&gt;Writing tests first, gaps surfaced that even 30+ questions hadn't closed. No matter how good the architect on the team is, design can't define every implementation problem up front. Some things you only find out by actually implementing them, and that happens in the real world. It happens just as naturally in AI-driven development.&lt;/p&gt;

&lt;p&gt;The biggest one was how to handle "on hold." During the grilling phase, I'd decided that "a published article without an English version gets its crosspost put on hold, with a warning." But once I wrote the tests, all 18 existing articles matched that condition. As-is, every push would line up 18 warnings.&lt;/p&gt;

&lt;p&gt;Claude Code stopped implementing and came back with four options:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Warn only about articles changed in that push&lt;/li&gt;
&lt;li&gt;Keep an exclusion list&lt;/li&gt;
&lt;li&gt;Don't warn&lt;/li&gt;
&lt;li&gt;Warn about everything every time (as agreed)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I chose 1. It takes the diff against the previous commit with &lt;code&gt;git diff&lt;/code&gt;, and warns only about changed originals that have no English version.&lt;/p&gt;

&lt;p&gt;After implementation, I also had a review agent look it over. Of its 10 findings, I fixed 7. The main four:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If a force push removes the previous commit, &lt;code&gt;git diff&lt;/code&gt; fails and the whole job stops. Since the warning is a supporting feature, crossposting now continues even if it fails&lt;/li&gt;
&lt;li&gt;A broken YAML in just one original article stopped crossposting for every article. It's now treated as a per-article error&lt;/li&gt;
&lt;li&gt;If dev.to saves tags in a different order or changes the trailing newline, articles with no changes get updated every time. Those two are now excluded from the comparison&lt;/li&gt;
&lt;li&gt;Zenn lets you nest &lt;code&gt;:::message&lt;/code&gt; inside &lt;code&gt;::::details&lt;/code&gt;, but the outer one wasn't being converted&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the end there were 41 tests, with 99% line coverage and 93% branch coverage. Things with small impact, or that recover on a rerun, such as retrying on 5xx errors, were skipped, with the reasons written down.&lt;/p&gt;

&lt;h2&gt;
  
  
  On the first crosspost, the tags silently vanished
&lt;/h2&gt;

&lt;p&gt;I tried the first crosspost with "&lt;a href="https://zenn.dev/sho7650/articles/1f2b14df8975ea" rel="noopener noreferrer"&gt;"Is That a Harness Too?" Making Sense of Harness Engineering for AI Agents (Japanese original)&lt;/a&gt;." I committed the English version, merged the PR, and the push to &lt;code&gt;main&lt;/code&gt; triggered Actions. The article is published as "&lt;a href="https://dev.to/sho7650/is-that-a-harness-too-making-sense-of-harness-engineering-for-ai-agents-p15"&gt;"Is That a Harness Too?" Making Sense of Harness Engineering for AI Agents&lt;/a&gt;." The canonical_url pointed to the original Zenn article, and no Zenn syntax was left in the body.&lt;/p&gt;

&lt;p&gt;But when I actually ran it, there wasn't a single tag on it. The English version's frontmatter listed four: &lt;code&gt;ai&lt;/code&gt;, &lt;code&gt;agents&lt;/code&gt;, &lt;code&gt;llm&lt;/code&gt;, and &lt;code&gt;architecture&lt;/code&gt;. The API didn't return an error, so there was nothing in the Actions logs to tip me off.&lt;/p&gt;

&lt;p&gt;The cause was how the tags were sent. The dev.to API docs say &lt;code&gt;tags&lt;/code&gt; is "a comma-separated string." The first implementation followed that and sent &lt;code&gt;"ai,agents,llm,architecture"&lt;/code&gt;. But reading Forem's controller, it only accepts an array:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;app/controllers/concerns/api/articles_controller.rb&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;allowed_params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="ss"&gt;:title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:body_markdown&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:published&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:series&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;:main_image&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:canonical_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:description&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;tags: &lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="c1"&gt;# ...&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When a key is declared as &lt;code&gt;{ tags: [] }&lt;/code&gt;, Rails strong parameters silently drop a string value instead of raising an error. I'd had Claude read the source to verify the list API, yet blindly trusted the docs for the API that sends articles. Honestly, though, this one's on the docs. Leaning on Claude Code this time is what led me to read the source code at all; a human developer would normally have no choice but to trust the docs unless there were prior reports. What a pain.&lt;/p&gt;

&lt;p&gt;This bug had one more annoying side effect. Since the tags on dev.to stayed empty, the diff check saw "tags differ" every time. Even with nothing changed, a PUT would keep firing on every push. I fixed it to send tags as an array and changed the test expectations to arrays too. Thanks to that, the tags are now assigned properly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Looking back
&lt;/h2&gt;

&lt;p&gt;In one sentence, the setup &lt;strong&gt;keeps translation in human hands locally, and leaves CI with just one job: mirroring the original article's publication state to dev.to&lt;/strong&gt;. Since CI writes nothing back, there's no mapping file, no bot commits, and no collisions with my local &lt;code&gt;main&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Starting from the reference setup was the right call. Having that foundation is exactly what let me ask, for each decision like "commit a mapping file" or "keep &lt;code&gt;published&lt;/code&gt; in two places," whether it fit how I work. And the grilling skill is what laid out those questions without missing any.&lt;/p&gt;

&lt;p&gt;If you're thinking about running Zenn and dev.to side by side the same way, I'd suggest deciding two things first: "where does translation happen?" and "where does the dev.to-side state live?" The rest of the decisions should get much easier after that.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>claudecode</category>
      <category>githubactions</category>
      <category>typescript</category>
    </item>
    <item>
      <title>"Is That a Harness Too?" Making Sense of Harness Engineering for AI Agents</title>
      <dc:creator>Takashi Abe</dc:creator>
      <pubDate>Fri, 09 Oct 2026 05:16:52 +0000</pubDate>
      <link>https://dev.to/sho7650/is-that-a-harness-too-making-sense-of-harness-engineering-for-ai-agents-p15</link>
      <guid>https://dev.to/sho7650/is-that-a-harness-too-making-sense-of-harness-engineering-for-ai-agents-p15</guid>
      <description>&lt;h2&gt;
  
  
  When sales says "everything is a harness," they're not wrong. It's just too broad
&lt;/h2&gt;

&lt;p&gt;Lately I keep hearing the same lines from sales folks around me: "Oh, that's harness engineering," or "Are you really thinking about harness engineering?" What exactly do they mean by it? For some people, building an MCP server is harness engineering. So is wiring up RAG. So is adding guardrails. Everything is harness engineering.&lt;/p&gt;

&lt;p&gt;Let's get on the same page first.&lt;/p&gt;

&lt;p&gt;By harness engineering, I don't mean test harnesses, and I don't mean &lt;a href="https://harness.io/" rel="noopener noreferrer"&gt;Harness.io&lt;/a&gt;, the CI/CD product. I mean Harness Engineering in the sense of what it takes to turn an AI agent into a system that actually works in practice.&lt;/p&gt;

&lt;p&gt;With that settled: in the broad sense, the sales instinct isn't wrong. But taken at face value, it's far too broad to be useful as an IT architecture term. It ends up meaning roughly "we're doing something around AI agents."&lt;/p&gt;

&lt;p&gt;So I tried defining it again:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Harness Engineering is the design of the execution environment and feedback loops around a probabilistic AI model, so that it becomes a "work-executing agent" that is "&lt;strong&gt;controllable&lt;/strong&gt;," "&lt;strong&gt;verifiable&lt;/strong&gt;," and "&lt;strong&gt;observable&lt;/strong&gt;."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This isn't a technique for making AI smarter. It's a technique for taking an AI that is admittedly smart but unreliable, and containing it so it can do real work. Below is how I arrived at that one sentence, plus a quick test for when sales tells you "that's a harness."&lt;/p&gt;

&lt;h2&gt;
  
  
  A harness is the whole loop outside the model
&lt;/h2&gt;

&lt;p&gt;As of 2026, there's a formula you see everywhere:&lt;/p&gt;

&lt;p&gt;$$&lt;br&gt;
Agent = Model + Harness&lt;br&gt;
$$&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.langchain.com/blog/the-anatomy-of-an-agent-harness" rel="noopener noreferrer"&gt;LangChain&lt;/a&gt; uses almost exactly this phrasing. &lt;a href="https://learn.microsoft.com/en-us/agent-framework/concepts/harness" rel="noopener noreferrer"&gt;Microsoft&lt;/a&gt; defines an Agent Harness as "the execution foundation that makes a language model function as an AI agent that can actually get work done." It's made up of the pieces responsible for conversation state, context, tool calls, approval policies, multi-step progress, and so on.&lt;/p&gt;

&lt;p&gt;Roughly sketched, it looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;pre data-lang="mermaid"&gt;&lt;code&gt;flowchart TD
    E[User / Event] --&amp;gt; C
    M([Model])
    subgraph Harness
        C[Context]
        A[Action / Tool]
        R[Result / Observation]
        V{Validate / Evaluate}
        RT[Retry]
        F[Finish]
        ES[Escalate]
    end
    C --&amp;gt; M
    M --&amp;gt; A
    A --&amp;gt; R
    R --&amp;gt; V
    V --&amp;gt;|Failed| RT
    V --&amp;gt;|Done| F
    V --&amp;gt;|Risky| ES
    RT --&amp;gt; C&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;This entire outer loop is the harness.&lt;/p&gt;

&lt;p&gt;The model's job is to figure out "what's probably the right next move." The harness's job is to decide what to show it, what to let it do, what to forbid, how to check the results, what to do on failure, and when to stop. The automation I've been building lately by &lt;a href="https://note.com/sho7650/n/n9f21daa9653d" rel="noopener noreferrer"&gt;combining&lt;/a&gt; &lt;a href="https://typesafe.ai/" rel="noopener noreferrer"&gt;Jev&lt;/a&gt; with n8n is, I'd argue, exactly what you'd call harness engineering.&lt;/p&gt;

&lt;h2&gt;
  
  
  The core is the control loop, not the tool connections
&lt;/h2&gt;

&lt;p&gt;Get this wrong and Harness Engineering turns into "every AI-adjacent technology, bundled."&lt;/p&gt;

&lt;p&gt;&lt;a href="https://cloud.google.com/discover/agent-harness" rel="noopener noreferrer"&gt;Google Cloud&lt;/a&gt; describes building a harness as "wrapping a language model in scripts that manage its inputs, outputs, and tool execution." Tools are executed via MCP and the like, the results are appended to the conversation history, and everything is fed back to the model. For enterprise use, it's positioned as the layer that manages state, permissions, and tool execution, and because it owns all input and output, it also makes monitoring and cost tracking easier.&lt;/p&gt;

&lt;p&gt;So if you think about it in stages, the boundary comes into view. This alone is just "API integration":&lt;br&gt;
&lt;/p&gt;

&lt;pre data-lang="mermaid"&gt;&lt;code&gt;flowchart LR
    LLM --&amp;gt; API&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;Once you get here, you're gradually entering harness territory:&lt;br&gt;
&lt;/p&gt;

&lt;pre data-lang="mermaid"&gt;&lt;code&gt;flowchart TD
    LLM --&amp;gt; EX[Call API]
    EX --&amp;gt; CK[Check result]
    CK --&amp;gt; Q{Goal achieved?}
    Q --&amp;gt;|No| NX[Decide next action]
    Q --&amp;gt;|Yes| FIN[Finish]&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;And if you've designed all the way to the third stage below, I think you can pretty clearly call it Harness Engineering:&lt;br&gt;
&lt;/p&gt;

&lt;pre data-lang="mermaid"&gt;&lt;code&gt;flowchart TD
    P[Check permissions] --&amp;gt; E[Execute]
    E --&amp;gt; V[Verify result]
    V --&amp;gt; FX[Fix if failed]
    FX --&amp;gt; ES[Escalate to a human if risky]
    ES --&amp;gt; LG[Store logs and eval data]&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;It doesn't end once things are connected. You look at what came back, decide the next action, and take care of retries and cutoffs. That "taking care of it" part is the core.&lt;/p&gt;

&lt;h2&gt;
  
  
  MCP, RAG, and guardrails are all just parts of a harness
&lt;/h2&gt;

&lt;p&gt;In practice, it helps to break down what a harness decides like this:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Area&lt;/th&gt;
&lt;th&gt;What the harness decides&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Instructions&lt;/td&gt;
&lt;td&gt;What goal the agent acts toward&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Context&lt;/td&gt;
&lt;td&gt;What to show the model this time&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Retrieval&lt;/td&gt;
&lt;td&gt;What to pull from internal docs and DBs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tools&lt;/td&gt;
&lt;td&gt;What it can execute&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Identity / Permission&lt;/td&gt;
&lt;td&gt;Whose permissions it acts with, and how far&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Orchestration&lt;/td&gt;
&lt;td&gt;In what order and loops it runs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;State / Memory&lt;/td&gt;
&lt;td&gt;How state from previous runs is kept&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Guardrails&lt;/td&gt;
&lt;td&gt;What is forbidden&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Verification&lt;/td&gt;
&lt;td&gt;How to check the result is correct&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Retry / Recovery&lt;/td&gt;
&lt;td&gt;How to roll back on failure&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Human approval&lt;/td&gt;
&lt;td&gt;Where a human steps in&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Observability&lt;/td&gt;
&lt;td&gt;What gets kept as logs and traces&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Evaluation&lt;/td&gt;
&lt;td&gt;Whether the agent behaves as expected&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Termination&lt;/td&gt;
&lt;td&gt;When the job counts as done&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://learn.microsoft.com/en-us/agent-framework/concepts/harness" rel="noopener noreferrer"&gt;Microsoft's current Agent Harness&lt;/a&gt; likewise groups planning, file memory, tool approval, OpenTelemetry, web search, skills, background agents, shell execution, looping, and more as harness capabilities.&lt;/p&gt;

&lt;p&gt;Looking at this table, it's clear that MCP is not the harness itself. MCP is one mechanism for connecting the tools a harness uses. RAG is the same: one part of Context / Retrieval. Guardrails are one part. Memory is one part.&lt;/p&gt;

&lt;p&gt;Whether you're talking with sales or with customers, keeping these separate makes life easier as an architect. "We connected MCP" and "we designed a harness" simply don't belong in the same sentence.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prompt Engineering and Context Engineering both live inside the harness
&lt;/h2&gt;

&lt;p&gt;The relationship among the three is easiest to understand as nesting:&lt;br&gt;
&lt;/p&gt;

&lt;pre data-lang="mermaid"&gt;&lt;code&gt;flowchart TD
    H[Harness Engineering] --&amp;gt; P[Prompt / Instruction Engineering]
    H --&amp;gt; C[Context Engineering]
    C --&amp;gt; RAG
    C --&amp;gt; MEM[Memory]
    C --&amp;gt; PD[Progressive Disclosure]
    H --&amp;gt; T[Tool Engineering]
    T --&amp;gt; API
    T --&amp;gt; MCP
    H --&amp;gt; O[Orchestration]
    H --&amp;gt; G[Guardrails / Permissions]
    H --&amp;gt; EV[Evaluation / Verification]
    H --&amp;gt; OB[Observability / Recovery]&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;&lt;a href="https://martinfowler.com/articles/harness-engineering.html" rel="noopener noreferrer"&gt;Birgitta Böckeler's breakdown&lt;/a&gt;, published on everyone's favorite Martin Fowler site, splits Harness Engineering into Guides and Sensors. Her framing is scoped specifically to working with coding agents.&lt;/p&gt;

&lt;p&gt;A Guide is feedforward: it steers the agent in the right direction before it fails. Rules, documentation, system prompts, Skills, and tool specs all fall here.&lt;/p&gt;

&lt;p&gt;A Sensor is feedback: it observes what happened and flags mistakes. Tests, linters, logs, metrics, reviews by another AI, and so on.&lt;br&gt;
&lt;/p&gt;

&lt;pre data-lang="mermaid"&gt;&lt;code&gt;flowchart LR
    G["Guide (feedforward)"] --&amp;gt; AG[Agent]
    GO[Goal] --&amp;gt; AG
    AG --&amp;gt; AC[Action]
    AC --&amp;gt; S["Sensor (feedback)"]
    S --&amp;gt; AG&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;Giving instructions is only half a harness. It becomes a harness only once you've built the part where the agent acts, you observe, and it corrects itself.&lt;/p&gt;

&lt;p&gt;Polishing prompts strengthens the Guide. If that's all you do, nobody will notice when the agent gets something wrong.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why harnesses, why now? Because LLMs got smarter
&lt;/h2&gt;

&lt;p&gt;It's a bit paradoxical, but the reason is that models got smarter.&lt;/p&gt;

&lt;p&gt;Back when models were weak, it looked like this:&lt;br&gt;
&lt;/p&gt;

&lt;pre data-lang="mermaid"&gt;&lt;code&gt;flowchart LR
    HU[Human] --&amp;gt; AI
    AI --&amp;gt; AN[Answer]&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;A human asks, the AI answers, and if it's wrong, the human catches it. That was enough.&lt;/p&gt;

&lt;p&gt;Once models are strong enough, it looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;pre data-lang="mermaid"&gt;&lt;code&gt;flowchart TD
    AI --&amp;gt; S[Investigate]
    S --&amp;gt; J[Decide]
    J --&amp;gt; E[Execute]
    E --&amp;gt; C[Check result]
    C --&amp;gt; N[Next decision]&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;Now that they can act this autonomously, the question has shifted from "can the AI answer?" to "how much can we hand over to the AI?"&lt;/p&gt;

&lt;p&gt;&lt;a href="https://openai.com/index/harness-engineering/" rel="noopener noreferrer"&gt;The Harness Engineering post OpenAI published in February 2026&lt;/a&gt; is about exactly this. When they had Codex write code, early progress was slow not because Codex lacked the ability, but because the environment wasn't described well enough. In the end, they built testing, verification, review, feedback handling, and failure recovery into the system side as part of the development loop.&lt;/p&gt;

&lt;p&gt;In other words, the problem moved from model capability to system engineering. That, I think, is why Harness Engineering is suddenly getting so much attention. And it's good news for architects: we can't touch the model's internals, but system engineering is our home turf.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sales support, refunds, and incident response: where the boundary shows up
&lt;/h2&gt;

&lt;p&gt;Let's walk through some concrete examples.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sales support agent
&lt;/h3&gt;

&lt;p&gt;"Prep me for the next meeting with Company X."&lt;/p&gt;

&lt;p&gt;On its own, that's just using an LLM. Build a harness, and you get a flow like this:&lt;br&gt;
&lt;/p&gt;

&lt;pre data-lang="mermaid"&gt;&lt;code&gt;flowchart TD
    A[Fetch Account from CRM] --&amp;gt; B[Fetch Contacts / Opportunities]
    B --&amp;gt; C[Fetch past meetings]
    C --&amp;gt; D[Check internal access rights]
    D --&amp;gt; E[Search external sources if needed]
    E --&amp;gt; F[Draft meeting hypotheses]
    F --&amp;gt; G[Cross-check against sources]
    G --&amp;gt; H[Draft CRM updates]
    H --&amp;gt; I[Human approves]
    I --&amp;gt; J[Update CRM]&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;The CRM itself is not the harness. The harness is the mechanism that controls when the CRM is accessed, under whose permissions, what gets fetched, and how much the agent is allowed to update.&lt;/p&gt;

&lt;h3&gt;
  
  
  Customer support agent
&lt;/h3&gt;

&lt;p&gt;Say a customer writes, "I was double-charged, refund me."&lt;/p&gt;

&lt;p&gt;A bare model would probably fire back "We're so sorry. We'll refund you" right away. Anyone can say that. An agent that can actually issue a refund looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;pre data-lang="mermaid"&gt;&lt;code&gt;flowchart TD
    A[Verify identity] --&amp;gt; B[Fetch order]
    B --&amp;gt; C[Check payment history]
    C --&amp;gt; D[Fetch refund policy]
    D --&amp;gt; E[Calculate refundable amount]
    E --&amp;gt; F[Threshold check]
    F --&amp;gt; G[Human approval if needed]
    G --&amp;gt; H[Call payment API]
    H --&amp;gt; I[Re-fetch to confirm the refund actually happened]
    I --&amp;gt; J[Update CRM]
    J --&amp;gt; K[Reply to customer]&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;That step near the end, "re-fetch to confirm the refund actually happened," matters enormously. "We called the refund API" and "the refund went through" are two different things.&lt;/p&gt;

&lt;p&gt;In Harness Engineering, verification matters more than action.&lt;/p&gt;

&lt;h3&gt;
  
  
  Incident response agent
&lt;/h3&gt;

&lt;p&gt;For "The website is slow. Find out why," the following flow is relatively safe:&lt;br&gt;
&lt;/p&gt;

&lt;pre data-lang="mermaid"&gt;&lt;code&gt;flowchart TD
    A[Fetch metrics] --&amp;gt; B[Fetch logs]
    B --&amp;gt; C[Inspect traces]
    C --&amp;gt; D[Form hypotheses]
    D --&amp;gt; E[Investigate further]
    E --&amp;gt; F[Identify likely causes]
    F --&amp;gt; G[Draft a fix]&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;Because it only reads and thinks.&lt;/p&gt;

&lt;p&gt;But once you allow actions that affect running processes, the story changes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl rollout restart
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The harness needs to check:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Is this a production change?&lt;/li&gt;
&lt;li&gt;What's the blast radius?&lt;/li&gt;
&lt;li&gt;Can it be rolled back?&lt;/li&gt;
&lt;li&gt;Does it need approval?&lt;/li&gt;
&lt;li&gt;Did things return to normal afterward?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Only when you can answer all five does it hold up as an enterprise system.&lt;/p&gt;

&lt;p&gt;What all three examples share is that between the agent's "read" phase and its "change" phase, there's a layer of permissions, verification, and human approval. That layer in between is the harness itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  A harness is never "done"
&lt;/h2&gt;

&lt;p&gt;Here are the pros and cons of a harness side by side:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Pros&lt;/th&gt;
&lt;th&gt;Cons&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Improves performance independently of model changes&lt;/td&gt;
&lt;td&gt;The system gets considerably more complex&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lets you raise agent autonomy safely&lt;/td&gt;
&lt;td&gt;More to operate: traces, evals, permissions, state, and more&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Improves reproducibility, auditability, explainability&lt;/td&gt;
&lt;td&gt;Model improvements can turn an old harness into a hindrance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reduces human review work&lt;/td&gt;
&lt;td&gt;Excessive rules can kill the model's capabilities&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Feeds failures back into the next Guide / Sensor&lt;/td&gt;
&lt;td&gt;The harness itself needs testing&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The third con is especially easy to miss.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.anthropic.com/engineering/managed-agents" rel="noopener noreferrer"&gt;Anthropic pointed out in April 2026&lt;/a&gt; that a harness encodes assumptions about "what Claude can't do on its own," so as models improve, those assumptions need to be revisited.&lt;/p&gt;

&lt;p&gt;They also gave a real example. They had added context resets to deal with Sonnet 4.5's habit of sensing the context limit and wrapping up work early. But with Opus 4.5, that habit disappeared, and the resets became dead weight.&lt;/p&gt;

&lt;p&gt;That's why a harness is never finished once built. You need to trim it down or rebuild it alongside model updates. What you're maintaining is "the parts built on the assumption that the AI will get things wrong."&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem with "everything is a harness" is that the word stops being useful
&lt;/h2&gt;

&lt;p&gt;In conversations with sales, this is probably the most important part.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://martinfowler.com/articles/harness-engineering.html" rel="noopener noreferrer"&gt;Böckeler's article&lt;/a&gt; itself says that "Agent = Model + Harness is a very broad definition, so it's worth narrowing it down by type of agent." She then explicitly limits her own definition to the bounded context of working with coding agents. I think that's the right approach.&lt;/p&gt;

&lt;p&gt;For example, calling "I built an MCP server" Harness Engineering isn't wrong.&lt;br&gt;
But the moment you label every item below as "Harness Engineering," the term becomes nearly synonymous with "we're doing something around AI agents." A word that doesn't tell you what it refers to is useless in a design conversation.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;REST API development&lt;/li&gt;
&lt;li&gt;IAM&lt;/li&gt;
&lt;li&gt;RAG&lt;/li&gt;
&lt;li&gt;Prompt&lt;/li&gt;
&lt;li&gt;MCP&lt;/li&gt;
&lt;li&gt;Workflow&lt;/li&gt;
&lt;li&gt;Logging&lt;/li&gt;
&lt;li&gt;Monitoring&lt;/li&gt;
&lt;li&gt;Evaluation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So how about distinguishing them like this?&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;What you're doing&lt;/th&gt;
&lt;th&gt;What to call it&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Improving instructions&lt;/td&gt;
&lt;td&gt;Prompt / Instruction Engineering&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Selecting information, RAG, Memory&lt;/td&gt;
&lt;td&gt;Context Engineering&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MCP / API connections&lt;/td&gt;
&lt;td&gt;Tool Integration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Procedures, routing&lt;/td&gt;
&lt;td&gt;Agent Orchestration / Workflow&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Permissions, prohibitions&lt;/td&gt;
&lt;td&gt;Guardrails / Governance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Evaluation&lt;/td&gt;
&lt;td&gt;Agent Evaluation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Traces, metrics&lt;/td&gt;
&lt;td&gt;Agent Observability / AgentOps&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Combining all of these to control autonomous execution&lt;/td&gt;
&lt;td&gt;Harness Engineering&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Harness Engineering isn't so much the name of an individual technology as a higher-level concept: designing the control system that integrates all of these to make an agent's behavior work. Framed that way, everything falls neatly into place.&lt;/p&gt;

&lt;p&gt;It's fine for sales to tell a customer "you need Harness Engineering." The architect's job is to break that one line down into the rows of the table above and show what's covered and what's missing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Eight questions to answer "Is that a harness?"
&lt;/h2&gt;

&lt;p&gt;When sales says "this is Harness Engineering," checking these in order seems to sort things out:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Priority&lt;/th&gt;
&lt;th&gt;Question&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;What does the agent have to achieve to be "done"?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;What can the agent see?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;What can the agent execute?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;What must it not execute?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;How are execution results verified?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;How does it retry / recover when it gets things wrong?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;Where does it escalate to a human?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;Can its decisions and actions be traced afterward?&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If all eight are designed, then "I see, that is Harness Engineering" is a fair verdict.&lt;/p&gt;

&lt;p&gt;If, on the other hand, it's just "we connected the CRM via MCP," that's Tool Integration. It's one of the parts that make up a harness, but it isn't Harness Engineering as a whole. That's how I'd sort it.&lt;/p&gt;

&lt;p&gt;The order of these eight questions matters, too. For an agent with no defined "done" condition (question 1), there's no way to design "verification" (question 5) or "traceability" (question 8). That's why you always start with question 1.&lt;/p&gt;

&lt;h2&gt;
  
  
  Harness Engineering is really just good old system design
&lt;/h2&gt;

&lt;p&gt;Harness Engineering is a new name, but from an architect's point of view, it's not all that foreign. Isn't it what we've been doing all along?&lt;/p&gt;

&lt;p&gt;Let's list what we've always done:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Define boundaries&lt;/li&gt;
&lt;li&gt;Define permissions&lt;/li&gt;
&lt;li&gt;Manage state&lt;/li&gt;
&lt;li&gt;Detect failures&lt;/li&gt;
&lt;li&gt;Retry&lt;/li&gt;
&lt;li&gt;Roll back&lt;/li&gt;
&lt;li&gt;Monitor&lt;/li&gt;
&lt;li&gt;Test&lt;/li&gt;
&lt;li&gt;Escalate to humans&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's system design, plain and simple. Designing batch reruns, compensating transactions, alerting for operations monitoring: all of it fits on this list.&lt;/p&gt;

&lt;p&gt;There is, however, one big difference.&lt;/p&gt;

&lt;p&gt;Traditionally, humans defined a program's behavior as code. In an agentic system, part of that behavior is delegated to a non-deterministic model. That's why you need a strong control system around it. That's the harness.&lt;/p&gt;

&lt;p&gt;I've been writing programs for over 40 years, and I believe that turning routine work into programs is what computers are fundamentally for. That belief hasn't changed in the age of AI. If anything, it's become clearer.&lt;/p&gt;

&lt;p&gt;Whatever can be made routine, you lock down with deterministic, definitive programs, just as before. Only the judgments that can't be made routine get handed to a probabilistic model. And the results of those delegated judgments get verified, again, by deterministic programs. A harness is the design of that "deterministic part." Let computers do what they're good at, and don't let uncertain things run loose as they are. Drawing that line has always been the architect's job.&lt;/p&gt;

&lt;p&gt;Seen this way, the difference among the three terms fits in a single line.&lt;/p&gt;

&lt;p&gt;If Prompt Engineering is "how you instruct the AI" and Context Engineering is "what you show the AI," then Harness Engineering is the craft of designing "what kind of world you put the AI to work in."&lt;/p&gt;

&lt;p&gt;With this definition, I think we can finally push back on the "everything is a harness" problem.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>llm</category>
      <category>architecture</category>
    </item>
    <item>
      <title>[Tailscale] Complete Guide to VPN Connection to Home LAN with Subnet Router</title>
      <dc:creator>Takashi Abe</dc:creator>
      <pubDate>Fri, 09 Oct 2026 02:23:39 +0000</pubDate>
      <link>https://dev.to/sho7650/tailscale-complete-guide-to-vpn-connection-to-home-lan-with-subnet-router-3g67</link>
      <guid>https://dev.to/sho7650/tailscale-complete-guide-to-vpn-connection-to-home-lan-with-subnet-router-3g67</guid>
      <description>&lt;h1&gt;
  
  
  Accessing Your Home LAN from Outside with Tailscale Subnet Router
&lt;/h1&gt;

&lt;p&gt;I want to access my home NAS or server while away from home. But VPN setup is difficult... I do not have a VPN router either... Tailscale solves these problems.&lt;/p&gt;

&lt;p&gt;In this article, I will explain how to build an environment using Docker that allows you to access your entire home LAN from outside using Tailscale Subnet Router functionality.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Requirements Overview
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What We Want to Achieve
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;VPN connection to home LAN (192.168.x.x) from smartphone/PC while away&lt;/li&gt;
&lt;li&gt;Name resolution for internal domains (*.example.local) using home DNS server&lt;/li&gt;
&lt;li&gt;Privacy protection so family members can use it but cannot see each other devices&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Users
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;User&lt;/th&gt;
&lt;th&gt;Usage&lt;/th&gt;
&lt;th&gt;Remarks&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Admin&lt;/td&gt;
&lt;td&gt;Remote management, all LAN access&lt;/td&gt;
&lt;td&gt;Full admin privileges&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Family&lt;/td&gt;
&lt;td&gt;Simple LAN access (NAS, etc.)&lt;/td&gt;
&lt;td&gt;Limited privileges, privacy protection between terminals&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Functional Requirements
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Requirement&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Access to home LAN from outside&lt;/td&gt;
&lt;td&gt;Reachable to all devices in 192.168.x.0/24&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Inter-user communication blocking&lt;/td&gt;
&lt;td&gt;For privacy protection, family devices cannot communicate with each other&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Split DNS&lt;/td&gt;
&lt;td&gt;Only specific domains resolved by home DNS when connected to Tailscale&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Subnet Router only&lt;/td&gt;
&lt;td&gt;Exit Node (all traffic forwarding) not required&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Technical Decisions
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Item&lt;/th&gt;
&lt;th&gt;Decision&lt;/th&gt;
&lt;th&gt;Reason&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Container runtime&lt;/td&gt;
&lt;td&gt;Docker&lt;/td&gt;
&lt;td&gt;Easy management, reproducibility&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Network mode&lt;/td&gt;
&lt;td&gt;host&lt;/td&gt;
&lt;td&gt;Required for Subnet Router&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;State persistence&lt;/td&gt;
&lt;td&gt;Named Volume&lt;/td&gt;
&lt;td&gt;Re-authentication unnecessary on restart&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Health check&lt;/td&gt;
&lt;td&gt;Built-in endpoint&lt;/td&gt;
&lt;td&gt;Easy monitoring integration&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  2. Architecture Overview
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Overall Configuration Diagram
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstatic.shoz.in%2F2025%2FJul%2Ftailscale-arch.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstatic.shoz.in%2F2025%2FJul%2Ftailscale-arch.png" alt="Rough configuration overview" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Communication Flow
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstatic.shoz.in%2F2025%2FJul%2Ftailscale-sequence.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstatic.shoz.in%2F2025%2FJul%2Ftailscale-sequence.png" alt="Not quite accurate but design was based on this diagram" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Component Roles
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;th&gt;Remarks&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Tailscale Container&lt;/td&gt;
&lt;td&gt;Subnet Router, forwards traffic from Tailnet to LAN&lt;/td&gt;
&lt;td&gt;Uses SNAT&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DNS Server (Unbound)&lt;/td&gt;
&lt;td&gt;Internal domain name resolution&lt;/td&gt;
&lt;td&gt;Split DNS target&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;UFW/iptables&lt;/td&gt;
&lt;td&gt;Firewall, controls forwarding&lt;/td&gt;
&lt;td&gt;Important settings required&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Importance of SNAT (Source Network Address Translation)
&lt;/h3&gt;

&lt;p&gt;Tailscale enables SNAT by default (--snat-subnet-routes=true). This is equivalent to MASQUERADE (IP masquerade / NAPT) in Linux iptables.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Setting&lt;/th&gt;
&lt;th&gt;Behavior&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;SNAT enabled (default)&lt;/td&gt;
&lt;td&gt;Source IP becomes Subnet Router IP&lt;/td&gt;
&lt;td&gt;No additional settings needed on LAN devices&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SNAT disabled&lt;/td&gt;
&lt;td&gt;Tailscale IP (100.x.x.x) is sent as-is&lt;/td&gt;
&lt;td&gt;Static routes must be set on LAN routers and devices&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;SNAT enabled is recommended. No additional settings needed on LAN devices.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  3. Design Considerations
&lt;/h2&gt;

&lt;h3&gt;
  
  
  3.1 Understanding Split DNS
&lt;/h3&gt;

&lt;p&gt;Split DNS is a mechanism to resolve only specific domains with specific DNS servers.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstatic.shoz.in%2F2025%2FJul%2Ftailscale-split.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstatic.shoz.in%2F2025%2FJul%2Ftailscale-split.png" alt="If you have domains used only at home, DNS can be directed to specified locations" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Benefits:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Normal internet access uses fast local DNS&lt;/li&gt;
&lt;li&gt;Only home internal domains resolved by home DNS&lt;/li&gt;
&lt;li&gt;No impact on internet speed even when connected to VPN&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3.2 Complex Relationship Between UFW and iptables
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;This is the biggest pitfall.&lt;/strong&gt; In environments using Docker, multiple firewall layers exist:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstatic.shoz.in%2F2025%2FJul%2Ftailscale-iptables.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstatic.shoz.in%2F2025%2FJul%2Ftailscale-iptables.png" alt="Where I got stuck the most" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Settings Requiring Attention&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Setting Location&lt;/th&gt;
&lt;th&gt;What to Set&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;UFW INPUT&lt;/td&gt;
&lt;td&gt;ufw allow&lt;/td&gt;
&lt;td&gt;WireGuard port (41641/udp)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;UFW FORWARD&lt;/td&gt;
&lt;td&gt;/etc/default/ufw&lt;/td&gt;
&lt;td&gt;DEFAULT_FORWARD_POLICY=ACCEPT&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DOCKER-USER&lt;/td&gt;
&lt;td&gt;/etc/ufw/after.rules&lt;/td&gt;
&lt;td&gt;RETURN rules for Tailscale CGNAT&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  3.3 DNS Server ACL Settings
&lt;/h3&gt;

&lt;p&gt;If using DNS servers like Unbound, you need to allow queries from Tailscale CGNAT (100.64.0.0/10).&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;With Split DNS, Tailscale clients send queries directly to the DNS server. At this time, the source IP becomes a CGNAT address (100.x.x.x).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  3.4 Auth Key Management
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; If state is persisted with Docker Named Volume, re-authentication is not required on container restart. Auth Key is only needed in the following cases:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Initial deployment&lt;/li&gt;
&lt;li&gt;When Named Volume is deleted&lt;/li&gt;
&lt;li&gt;When device is deleted from Tailscale Admin Console&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  4. Configuration Details
&lt;/h2&gt;

&lt;p&gt;From here, I will explain specific configuration methods.&lt;/p&gt;

&lt;h3&gt;
  
  
  4.1 Tailscale Admin Console Settings
&lt;/h3&gt;

&lt;h4&gt;
  
  
  4.1.1 Issuing Auth Key
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;Access Tailscale Admin Console&lt;/li&gt;
&lt;li&gt;Settings → Keys → Generate auth key&lt;/li&gt;
&lt;li&gt;Issue with the following settings:&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Item&lt;/th&gt;
&lt;th&gt;Setting&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Description&lt;/td&gt;
&lt;td&gt;home-router&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Expiration&lt;/td&gt;
&lt;td&gt;90 days (or as needed)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reusable&lt;/td&gt;
&lt;td&gt;Off (for security)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ephemeral&lt;/td&gt;
&lt;td&gt;Off (persistent device)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pre-approved&lt;/td&gt;
&lt;td&gt;On&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tags&lt;/td&gt;
&lt;td&gt;tag:subnet-router&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;Save the issued key securely (format: tskey-auth-xxxxx).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  4.1.2 ACL Settings
&lt;/h4&gt;

&lt;p&gt;Set the following in Admin Console → Access Controls:&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;"groups"&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;"group:admin"&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;"your-admin-email@example.com"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"group:family"&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;"family-email@example.com"&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;"tagOwners"&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;"tag:subnet-router"&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;"group:admin"&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;"hosts"&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;"home_network"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"192.168.x.0/24"&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;"acls"&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;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;"accept"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"src"&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;"group:admin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"group:family"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"dst"&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;"home_network:*"&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;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;"accept"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"src"&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;"tag:subnet-router"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"dst"&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;"*:*"&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;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;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstatic.shoz.in%2F2025%2FJul%2Ftailscale-acls.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fstatic.shoz.in%2F2025%2FJul%2Ftailscale-acls.png" alt="This is how the ACL section looks on screen" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Points:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;group:admin and group:family can access home LAN&lt;/li&gt;
&lt;li&gt;By not defining inter-terminal communication rules, default deny is applied and mutual communication is blocked&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  4.1.3 DNS Settings (After Deployment)
&lt;/h4&gt;

&lt;p&gt;Set the following in Admin Console → DNS:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Item&lt;/th&gt;
&lt;th&gt;Setting&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Add Nameserver&lt;/td&gt;
&lt;td&gt;Custom: 192.168.x.53 (home DNS server IP)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Restrict to domain&lt;/td&gt;
&lt;td&gt;example.local&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Override local DNS&lt;/td&gt;
&lt;td&gt;Off&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  4.2 Server-Side Preparation
&lt;/h3&gt;

&lt;h4&gt;
  
  
  4.2.1 Enabling IP Forwarding
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create configuration file&lt;/span&gt;
&lt;span class="nb"&gt;sudo tee&lt;/span&gt; /etc/sysctl.d/99-tailscale.conf &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
net.ipv4.ip_forward = 1
net.ipv6.conf.all.forwarding = 1
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;&lt;span class="c"&gt;# Apply immediately&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;sysctl &lt;span class="nt"&gt;-p&lt;/span&gt; /etc/sysctl.d/99-tailscale.conf

&lt;span class="c"&gt;# Verify&lt;/span&gt;
sysctl net.ipv4.ip_forward
&lt;span class="c"&gt;# Output: net.ipv4.ip_forward = 1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  4.2.2 TUN Device Verification
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Verify TUN device exists&lt;/span&gt;
&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-la&lt;/span&gt; /dev/net/tun

&lt;span class="c"&gt;# Create if it does not exist&lt;/span&gt;
&lt;span class="nb"&gt;sudo mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /dev/net
&lt;span class="nb"&gt;sudo mknod&lt;/span&gt; /dev/net/tun c 10 200
&lt;span class="nb"&gt;sudo chmod &lt;/span&gt;666 /dev/net/tun
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4.3 Docker Configuration
&lt;/h3&gt;

&lt;h4&gt;
  
  
  4.3.1 Directory Creation
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /opt/tailscale
&lt;span class="nb"&gt;cd&lt;/span&gt; /opt/tailscale
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  4.3.2 Creating docker-compose.yml
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo tee&lt;/span&gt; /opt/tailscale/docker-compose.yml &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
services:
  tailscale:
    image: tailscale/tailscale:v1.94.1
    container_name: tailscale
    hostname: home-router
    restart: unless-stopped
    cap_add:
      - NET_ADMIN
      - NET_RAW
    network_mode: host
    volumes:
      - tailscale_data:/var/lib/tailscale
      - /dev/net/tun:/dev/net/tun
    environment:
      - TS_AUTHKEY=tskey-auth-xxxxxxx # Set Auth Key here
      - TS_STATE_DIR=/var/lib/tailscale
      - TS_ROUTES=192.168.x.0/24
      - TS_USERSPACE=false
      - TS_ACCEPT_DNS=false
      - TS_EXTRA_ARGS=--port=41641
      - TS_ENABLE_HEALTH_CHECK=true
      - TS_ENABLE_METRICS=true
      - TS_LOCAL_ADDR_PORT=[::]:9002

volumes:
  tailscale_data:
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Configuration Item Descriptions:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Item&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;image&lt;/td&gt;
&lt;td&gt;tailscale/tailscale:v1.94.1&lt;/td&gt;
&lt;td&gt;Use specific version&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;network_mode&lt;/td&gt;
&lt;td&gt;host&lt;/td&gt;
&lt;td&gt;Required for Subnet Router&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;cap_add&lt;/td&gt;
&lt;td&gt;NET_ADMIN, NET_RAW&lt;/td&gt;
&lt;td&gt;Network operation privileges&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TS_ROUTES&lt;/td&gt;
&lt;td&gt;192.168.x.0/24&lt;/td&gt;
&lt;td&gt;Subnet to advertise&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TS_USERSPACE&lt;/td&gt;
&lt;td&gt;false&lt;/td&gt;
&lt;td&gt;Use kernel mode (recommended)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TS_ACCEPT_DNS&lt;/td&gt;
&lt;td&gt;false&lt;/td&gt;
&lt;td&gt;Do not use Tailscale DNS on server side&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Important Settings:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Environment Variable&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;TS_AUTHKEY&lt;/td&gt;
&lt;td&gt;Authentication key&lt;/td&gt;
&lt;td&gt;Needed only for initial deployment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TS_STATE_DIR&lt;/td&gt;
&lt;td&gt;State storage location&lt;/td&gt;
&lt;td&gt;Authentication state is persisted&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TS_ROUTES&lt;/td&gt;
&lt;td&gt;Advertised routes&lt;/td&gt;
&lt;td&gt;LAN subnet to be accessible&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  4.3.3 Starting the Container
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /opt/tailscale
&lt;span class="nb"&gt;sudo &lt;/span&gt;docker compose up &lt;span class="nt"&gt;-d&lt;/span&gt;

&lt;span class="c"&gt;# Check logs&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;docker compose logs &lt;span class="nt"&gt;-f&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  4.3.4 Operation Verification
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check container status&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;docker ps | &lt;span class="nb"&gt;grep &lt;/span&gt;tailscale

&lt;span class="c"&gt;# Check Tailscale status&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;docker &lt;span class="nb"&gt;exec &lt;/span&gt;tailscale tailscale status

&lt;span class="c"&gt;# Expected output example:&lt;/span&gt;
&lt;span class="c"&gt;# 100.x.x.x  home-router  your-tailnet.ts.net  linux  -&lt;/span&gt;
&lt;span class="c"&gt;# 100.x.x.x  iphone       your-tailnet.ts.net  iOS    idle&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  4.3.5 Route Approval
&lt;/h4&gt;

&lt;p&gt;After starting the container, enable 192.168.x.0/24 in Tailscale Admin Console → Machines → home-router → Edit route settings.&lt;/p&gt;

&lt;h3&gt;
  
  
  4.4 iptables/UFW Configuration
&lt;/h3&gt;

&lt;h4&gt;
  
  
  4.4.1 Changing UFW Routed Policy
&lt;/h4&gt;

&lt;p&gt;Since Subnet Router forwards packets, UFW routed policy needs to be changed to allow.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Check current settings&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw status verbose
&lt;span class="c"&gt;# Default: deny (incoming), allow (outgoing), deny (routed) ← routed is deny&lt;/span&gt;

&lt;span class="c"&gt;# Edit /etc/default/ufw&lt;/span&gt;
&lt;span class="nb"&gt;sudo sed&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s1"&gt;'s/DEFAULT_FORWARD_POLICY="DROP"/DEFAULT_FORWARD_POLICY="ACCEPT"/'&lt;/span&gt; /etc/default/ufw

&lt;span class="c"&gt;# Reload UFW&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw reload

&lt;span class="c"&gt;# Verify&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw status verbose
&lt;span class="c"&gt;# Default: deny (incoming), allow (outgoing), allow (routed) ← OK if allow&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  4.4.2 Allowing WireGuard Port
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw allow 41641/udp comment &lt;span class="s2"&gt;"Tailscale WireGuard"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  4.4.3 DOCKER-USER Chain Configuration (Important)
&lt;/h4&gt;

&lt;p&gt;When using Docker, the DOCKER-USER chain filters traffic from outside. Traffic from Tailscale CGNAT (100.64.0.0/10) needs to be allowed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Edit /etc/ufw/after.rules&lt;/span&gt;
&lt;span class="nb"&gt;sudo tee&lt;/span&gt; &lt;span class="nt"&gt;-a&lt;/span&gt; /etc/ufw/after.rules &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'

# BEGIN TAILSCALE DOCKER INTEGRATION
*filter
:DOCKER-USER - [0:0]
-A DOCKER-USER -j RETURN -s 10.0.0.0/8
-A DOCKER-USER -j RETURN -s 172.16.0.0/12
-A DOCKER-USER -j RETURN -s 192.168.0.0/16
-A DOCKER-USER -j RETURN -s 100.64.0.0/10

-A DOCKER-USER -j DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 192.168.0.0/16
-A DOCKER-USER -j DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 10.0.0.0/8
-A DOCKER-USER -j DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 172.16.0.0/12
-A DOCKER-USER -j DROP -p udp -m udp --dport 0:32767 -d 192.168.0.0/16
-A DOCKER-USER -j DROP -p udp -m udp --dport 0:32767 -d 10.0.0.0/8
-A DOCKER-USER -j DROP -p udp -m udp --dport 0:32767 -d 172.16.0.0/12

-A DOCKER-USER -j RETURN
COMMIT
# END TAILSCALE DOCKER INTEGRATION
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;&lt;span class="c"&gt;# Reload UFW&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw reload

&lt;span class="c"&gt;# Verify&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;iptables &lt;span class="nt"&gt;-L&lt;/span&gt; DOCKER-USER &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; Without &lt;code&gt;-A DOCKER-USER -j RETURN -s 100.64.0.0/10&lt;/code&gt;, UDP packets (including DNS) from Tailscale clients to LAN will be dropped.&lt;/p&gt;

&lt;h3&gt;
  
  
  4.5 Unbound (DNS Server) Configuration
&lt;/h3&gt;

&lt;p&gt;When using Split DNS, the DNS server needs to allow queries from Tailscale CGNAT (100.64.0.0/10).&lt;/p&gt;

&lt;h4&gt;
  
  
  4.5.1 Unbound ACL Configuration
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Edit /etc/unbound/unbound.conf.d/access-control.conf&lt;/span&gt;
&lt;span class="nb"&gt;sudo tee&lt;/span&gt; /etc/unbound/unbound.conf.d/access-control.conf &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;'
server:
    # Localhost
    access-control: 127.0.0.0/8 allow

    # LAN
    access-control: 192.168.x.0/24 allow

    # Docker networks
    access-control: 172.16.0.0/12 allow

    # Tailscale CGNAT (for Split DNS)
    access-control: 100.64.0.0/10 allow

    # Deny all others
    access-control: 0.0.0.0/0 refuse
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;&lt;span class="c"&gt;# Restart Unbound&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl restart unbound

&lt;span class="c"&gt;# Verify&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;systemctl status unbound
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  4.5.2 DNS Server UFW Configuration
&lt;/h4&gt;

&lt;p&gt;If the DNS server is on a separate machine, UFW on that machine also needs settings to allow DNS queries from Tailscale CGNAT.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Run on DNS server side&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw allow from 100.64.0.0/10 to any port 53 proto tcp comment &lt;span class="s2"&gt;"DNS TCP from Tailscale"&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw allow from 100.64.0.0/10 to any port 53 proto udp comment &lt;span class="s2"&gt;"DNS UDP from Tailscale"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  5. Verification Procedures
&lt;/h2&gt;

&lt;h3&gt;
  
  
  5.1 Server-Side Verification
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. Container startup verification&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;docker ps | &lt;span class="nb"&gt;grep &lt;/span&gt;tailscale
&lt;span class="c"&gt;# Expected: tailscale container in Up state&lt;/span&gt;

&lt;span class="c"&gt;# 2. Tailscale authentication verification&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;docker &lt;span class="nb"&gt;exec &lt;/span&gt;tailscale tailscale status
&lt;span class="c"&gt;# Expected: home-router displayed with 192.168.x.0/24 in Routes&lt;/span&gt;

&lt;span class="c"&gt;# 3. IP forwarding verification&lt;/span&gt;
sysctl net.ipv4.ip_forward
&lt;span class="c"&gt;# Expected: net.ipv4.ip_forward = 1&lt;/span&gt;

&lt;span class="c"&gt;# 4. Health check&lt;/span&gt;
curl http://localhost:9002/healthz
&lt;span class="c"&gt;# Expected: 200 OK&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5.2 Client-Side Verification
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. Tailscale connection verification&lt;/span&gt;
tailscale status
&lt;span class="c"&gt;# Expected: Connection successful, 100.x.x.x IP obtained&lt;/span&gt;

&lt;span class="c"&gt;# 2. LAN connectivity verification&lt;/span&gt;
ping 192.168.x.10
&lt;span class="c"&gt;# Expected: Response received&lt;/span&gt;

&lt;span class="c"&gt;# 3. DNS verification (Split DNS)&lt;/span&gt;
dig grafana.example.local
&lt;span class="c"&gt;# Expected: Returns internal IP like 192.168.x.10&lt;/span&gt;

&lt;span class="c"&gt;# 4. Service access&lt;/span&gt;
curl https://grafana.example.local/
&lt;span class="c"&gt;# Expected: Normal response&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5.3 Security Verification
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Inter-terminal communication blocking verification (from Admin device to Family device)&lt;/span&gt;
ping 100.x.x.x  &lt;span class="c"&gt;# Tailscale IP of Family device&lt;/span&gt;
&lt;span class="c"&gt;# Expected: Timeout (communication not possible)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  6. Additional Notes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  6.1 Important Settings Not Documented in Design Documents
&lt;/h3&gt;

&lt;p&gt;Important configuration items discovered during actual construction that were missing from documentation:&lt;/p&gt;

&lt;h4&gt;
  
  
  6.1.1 DNS Server UFW Rules
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Problem:&lt;/strong&gt; DNS queries timeout even with Split DNS configured&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cause:&lt;/strong&gt; DNS server UFW was blocking queries from Tailscale CGNAT (100.64.0.0/10)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Solution:&lt;/strong&gt; Add the following to DNS server UFW
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw allow from 100.64.0.0/10 to any port 53 proto tcp
&lt;span class="nb"&gt;sudo &lt;/span&gt;ufw allow from 100.64.0.0/10 to any port 53 proto udp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  6.1.2 DOCKER-USER Chain Configuration
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Problem:&lt;/strong&gt; ping works but DNS (UDP port 53) times out&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cause:&lt;/strong&gt; DROP rules in DOCKER-USER chain were dropping UDP packets from Tailscale
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-A DOCKER-USER -j DROP -p udp --dport 0:32767 -d 192.168.0.0/16
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Solution:&lt;/strong&gt; Add the following to /etc/ufw/after.rules
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-A DOCKER-USER -j RETURN -s 100.64.0.0/10
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  6.1.3 UFW Routed Policy
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Problem:&lt;/strong&gt; Tailscale connects but all communication to LAN times out&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cause:&lt;/strong&gt; UFW routed policy was deny by default, blocking packet forwarding&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Solution:&lt;/strong&gt; Change to &lt;code&gt;DEFAULT_FORWARD_POLICY="ACCEPT"&lt;/code&gt; in /etc/default/ufw&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  6.2 Troubleshooting
&lt;/h3&gt;

&lt;h4&gt;
  
  
  When Split DNS Does Not Work
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Check client settings&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;iOS/Android: Check if Use Tailscale DNS is enabled in Tailscale app settings&lt;/li&gt;
&lt;li&gt;macOS/Windows: Check for conflicts between system DNS settings and Tailscale settings&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Check Admin Console settings&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is Restrict to domain correctly configured?&lt;/li&gt;
&lt;li&gt;Is Override local DNS Off?&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Check with packet capture&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="c"&gt;# Run on Subnet Router side&lt;/span&gt;
   &lt;span class="nb"&gt;sudo &lt;/span&gt;tcpdump &lt;span class="nt"&gt;-i&lt;/span&gt; any &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s1"&gt;'port 53'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  When ping Works But Cannot Access Specific Services
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Check DOCKER-USER chain&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="nb"&gt;sudo &lt;/span&gt;iptables &lt;span class="nt"&gt;-L&lt;/span&gt; DOCKER-USER &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Check if RETURN rule for 100.64.0.0/10 exists&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Auth Key Expiration
&lt;/h4&gt;

&lt;p&gt;Since state is maintained with Named Volume, this is usually not a problem. However, a new Auth Key is required in the following cases:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When Named Volume is deleted&lt;/li&gt;
&lt;li&gt;When device is deleted from Tailscale Admin Console&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  6.3 Operational Notes
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Auth Key Rotation
&lt;/h4&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario&lt;/th&gt;
&lt;th&gt;Required Action&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Container restart&lt;/td&gt;
&lt;td&gt;None (state is persisted)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Named Volume deletion&lt;/td&gt;
&lt;td&gt;Issue new Auth Key, restart container&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Device deletion from Admin Console&lt;/td&gt;
&lt;td&gt;Issue new Auth Key, restart container&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  Version Update Procedure
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;Check Tailscale Release Notes&lt;/li&gt;
&lt;li&gt;Verify new version in test environment&lt;/li&gt;
&lt;li&gt;Update image tag in docker-compose.yml&lt;/li&gt;
&lt;li&gt;&lt;code&gt;docker compose pull &amp;amp;&amp;amp; docker compose up -d&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  6.4 Reference Links
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://tailscale.com/kb/1019/subnets/" rel="noopener noreferrer"&gt;Tailscale Subnet Routers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tailscale.com/kb/1282/docker/" rel="noopener noreferrer"&gt;Tailscale Docker&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tailscale.com/kb/1054/dns/" rel="noopener noreferrer"&gt;Tailscale DNS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://tailscale.com/kb/1018/acls/" rel="noopener noreferrer"&gt;Tailscale ACLs&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;This completes the Tailscale Subnet Router construction. You can now securely access your home LAN from outside.&lt;/p&gt;

</description>
      <category>docker</category>
      <category>networking</category>
      <category>privacy</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Obsidian AI Exporter: The Current Evolution Supporting Multiple AIs</title>
      <dc:creator>Takashi Abe</dc:creator>
      <pubDate>Fri, 20 Mar 2026 01:51:03 +0000</pubDate>
      <link>https://dev.to/sho7650/obsidian-ai-exporter-the-current-evolution-supporting-multiple-ais-32kn</link>
      <guid>https://dev.to/sho7650/obsidian-ai-exporter-the-current-evolution-supporting-multiple-ais-32kn</guid>
      <description>&lt;p&gt;Opportunities to interact with generative AI like Gemini and Claude in daily life have been increasing. When doing research or brainstorming, you often find brilliant ideas that make you go "aha!" during exchanges with AI, right?&lt;/p&gt;

&lt;p&gt;But the moment you close your browser tab, those precious conversations flow away into the depths of your history. I wanted to keep my dialogues with AI as my own "knowledge asset" that I could search and link to. With that thought in mind, I released the Chrome extension "Obsidian AI Exporter" two months ago.&lt;/p&gt;

&lt;p&gt;Thanks to everyone, many people have started using it, and over these two months I have received bug reports and feature requests and have made various updates. Today, I want to share with you the new features of this greatly evolved extension.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Kind of Tool Is This?
&lt;/h2&gt;

&lt;p&gt;Obsidian AI Exporter is an extension that extracts conversations with AI in your browser with one click and saves them as clean Obsidian documents.&lt;/p&gt;

&lt;p&gt;It originally started as Gemini-only, but since I personally use multiple AIs, it now supports all the generative AI services I use. These days, you don't have to stick to a single platform.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three Major New Features Added
&lt;/h2&gt;

&lt;p&gt;Over the two months since release, it has evolved to be more flexible and more convenient.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Expanded AI Platform Support
&lt;/h3&gt;

&lt;p&gt;In addition to Gemini, it now supports major AI services, including Claude, ChatGPT, and Perplexity. No matter which AI you are conversing with, just press the "Sync" button that appears in the bottom right of the same screen to save your entire conversation or research results.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Support for Saving Methods Other Than Obsidian
&lt;/h3&gt;

&lt;p&gt;Although it bears the name Obsidian, you no longer necessarily need to use Obsidian. By configuring the Markdown function from the settings screen, you have the option to download as a pure Markdown file to your computer or copy to clipboard and paste into another editor.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Appending to Existing Notes and Adding Unique Features
&lt;/h3&gt;

&lt;p&gt;An append mode has been added to write only new conversations to existing notes. Furthermore, fine adjustments tailored to each AI's characteristics have been made, such as saving the report portion of Gemini's Deep Research and the collapsible display of web search results in Claude.&lt;/p&gt;

&lt;p&gt;It also supports the LaTeX format, so formulas are properly recorded.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep Your Important Data in Your Own Hands
&lt;/h2&gt;

&lt;p&gt;What this extension is committed to is balancing privacy and convenience. Without going through external servers, all processing is completed in your browser and local environment. SSL is supported, but Obsidian-side configuration is necessary.&lt;/p&gt;

&lt;p&gt;The more AI becomes our excellent partner, the more the dialogues exchanged there should become irreplaceable assets. Before they fly away and disappear, try pulling them to your hands with one click.&lt;/p&gt;

&lt;p&gt;I am placing links to the Chrome Web Store and GitHub below. I would be delighted if you could install it and try out how it works.&lt;/p&gt;

&lt;p&gt;Chrome Web Store:&lt;/p&gt;

&lt;p&gt;

&lt;/p&gt;
&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
        &lt;div class="c-embed__cover"&gt;
          &lt;a href="https://chromewebstore.google.com/detail/obsidian-ai-exporter/edemgeigfbodiehkjhjflleipabgbdeh" class="c-link align-middle" rel="noopener noreferrer"&gt;
            &lt;img alt="" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Flh3.googleusercontent.com%2Fp9M5kLl7FSEKDysWM8FQvbRigSt6LfDfsn50xohlxB_xj6LeUA2nbmfPR0VQB1CQCHCe7Vk8HVBTQ7rQvpPEfSCJ_g%3Ds128-rj-sc0x00ffffff" height="auto" class="m-0"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="c-embed__body"&gt;
        &lt;h2 class="fs-xl lh-tight"&gt;
          &lt;a href="https://chromewebstore.google.com/detail/obsidian-ai-exporter/edemgeigfbodiehkjhjflleipabgbdeh" rel="noopener noreferrer" class="c-link"&gt;
            Obsidian AI Exporter - Chrome Web Store
          &lt;/a&gt;
        &lt;/h2&gt;
          &lt;p class="truncate-at-3"&gt;
            Export AI conversations from Gemini, Claude, ChatGPT, and Perplexity to Obsidian via Local REST API
          &lt;/p&gt;
        &lt;div class="color-secondary fs-s flex items-center"&gt;
            &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fssl.gstatic.com%2Fchrome%2Fwebstore%2Fimages%2Ficon_48px.png"&gt;
          chromewebstore.google.com
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;




&lt;p&gt;

&lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/sho7650" rel="noopener noreferrer"&gt;
        sho7650
      &lt;/a&gt; / &lt;a href="https://github.com/sho7650/obsidian-AI-exporter" rel="noopener noreferrer"&gt;
        obsidian-AI-exporter
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Obsidian AI Exporter&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;Chrome Extension that exports AI conversations from Google Gemini, Claude AI, ChatGPT, and Perplexity to Obsidian via the Local REST API.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/sho7650/obsidian-AI-exporter/README.ja.md" rel="noopener noreferrer"&gt;日本語版はこちら&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://opensource.org/licenses/MIT" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/fdf2982b9f5d7489dcf44570e714e3a15fce6253e0cc6b5aa61a075aac2ff71b/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c6963656e73652d4d49542d79656c6c6f772e737667" alt="License: MIT"&gt;&lt;/a&gt;
&lt;a href="https://chromewebstore.google.com/detail/obsidian-ai-exporter/edemgeigfbodiehkjhjflleipabgbdeh" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/b9152c7975ff2c59f475e1b8e39bfaa0c35d0e03584233ae41a20e927876473e/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4368726f6d652d457874656e73696f6e2d626c7565" alt="Chrome Web Store"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Features&lt;/h2&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Multi-platform support&lt;/strong&gt;: Export from Google Gemini, Claude AI, ChatGPT, and Perplexity&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One-click export&lt;/strong&gt;: Floating "Sync" button on supported AI pages&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multiple output options&lt;/strong&gt;: Save to Obsidian, download as file, or copy to clipboard&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deep Research support&lt;/strong&gt;: Export Gemini Deep Research and Claude Extended Thinking reports&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Artifact support&lt;/strong&gt;: Extract Claude Artifacts with inline citations and sources&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tool content support&lt;/strong&gt;: Optionally include Claude's web search results and tool activity as collapsible &lt;code&gt;[!ABSTRACT]&lt;/code&gt; callouts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Append mode&lt;/strong&gt;: Only new messages are added to existing notes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Obsidian callouts&lt;/strong&gt;: Formatted output with &lt;code&gt;[!QUESTION]&lt;/code&gt; and &lt;code&gt;[!NOTE]&lt;/code&gt; callouts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;YAML frontmatter&lt;/strong&gt;: Metadata including title, source, URL, dates, and tags&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Auto-scroll&lt;/strong&gt;: Automatically loads all messages in long Gemini conversations&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Platform-based organization&lt;/strong&gt;: Use &lt;code&gt;{platform}&lt;/code&gt;…&lt;/li&gt;
&lt;/ul&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/sho7650/obsidian-AI-exporter" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;




&lt;p&gt;I plan to continue adding convenient features little by little. If you have impressions from trying it or requests for features you want, please feel free to leave a comment on a GitHub issue or note.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>productivity</category>
      <category>showdev</category>
      <category>tooling</category>
    </item>
    <item>
      <title>AI Era: The Power of Writing Meditation - Connecting with Notebooks</title>
      <dc:creator>Takashi Abe</dc:creator>
      <pubDate>Wed, 11 Mar 2026 01:34:12 +0000</pubDate>
      <link>https://dev.to/sho7650/ai-era-the-power-of-writing-meditation-connecting-with-notebooks-1mi4</link>
      <guid>https://dev.to/sho7650/ai-era-the-power-of-writing-meditation-connecting-with-notebooks-1mi4</guid>
      <description>&lt;p&gt;Lately, I have been feeling a bit overwhelmed by the evolution of AI.&lt;/p&gt;

&lt;p&gt;My reliance on AI has made my desires unmanageable. I can immediately start on ideas I have and let troublesome tasks be handled automatically. I feel it is wonderful that getting advice has lowered the hurdle to taking the first step.&lt;/p&gt;

&lt;p&gt;However, it is also true that when I pause to reflect, moments of feeling lost have increased, wondering what exactly I am doing all this for.&lt;/p&gt;

&lt;p&gt;During such times when I am about to be swallowed by waves of information and ideas, what saves me is not a digital screen but an old-fashioned analog notebook.&lt;/p&gt;

&lt;p&gt;I pour my heart into a blank notebook, reconnecting with my life and purpose. What should I be doing now? What should I be thinking about? Should I calm my mind and deal with the task at hand, or should I move toward a future vision? Above all, I need to make a decision about what not to do.&lt;/p&gt;

&lt;p&gt;When it comes to these things, no matter how excellent an AI I ask, answers simply will not come back. Unless you are going to walk a life decided by AI, if there is a path you want to walk on your feet, you absolutely need time to open your notebook and deeply face yourself.&lt;/p&gt;

&lt;p&gt;For me, this time of self-dialogue is similar to meditation.&lt;/p&gt;

&lt;p&gt;Time spent at my desk endlessly writing out characters and time spent slowly running while letting my thoughts wander. Both are beloved moments that I cannot do without.&lt;/p&gt;

&lt;p&gt;However, when thoughts are just circling in my head, working memory fills up, and I am driven by the urge to write them out somewhere. That is precisely why a notebook as a receptacle for thought is optimal.&lt;/p&gt;

&lt;p&gt;You might think you cannot take notes while running.&lt;/p&gt;

&lt;p&gt;That is certainly true, and I have not yet found a good way to record things while running. But I have come to interpret that ideas I have thoroughly thought through while running and still vividly remember when I get home are what is truly important to me right now.&lt;/p&gt;

&lt;p&gt;I may forget trivial triggers, but essential things will surely remain in my heart. Believing that, I only note down the crystals that remain after finishing my run and connect them to the next step.&lt;/p&gt;

&lt;p&gt;I sit at an empty desk, staring at my notebook, tirelessly crafting characters. I love this tranquil time.&lt;/p&gt;

&lt;p&gt;We intentionally pause in a time of abundance and convenience. Writing characters in a notebook allows you to meditate and listen to your own heart's voice. Would you like to try it once as well? I highly recommend it.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>mentalhealth</category>
      <category>productivity</category>
      <category>writing</category>
    </item>
    <item>
      <title>Accelerating AI and the Courage to Pause: Reclaim Your Core Values Through Essential Thinking</title>
      <dc:creator>Takashi Abe</dc:creator>
      <pubDate>Mon, 02 Mar 2026 05:57:02 +0000</pubDate>
      <link>https://dev.to/sho7650/accelerating-ai-and-the-courage-to-pause-reclaim-your-core-values-through-essential-thinking-2eai</link>
      <guid>https://dev.to/sho7650/accelerating-ai-and-the-courage-to-pause-reclaim-your-core-values-through-essential-thinking-2eai</guid>
      <description>&lt;p&gt;Lately, I have been hearing the term "AI fatigue" everywhere.&lt;/p&gt;

&lt;p&gt;Bold claims are flying around that AI will change the world and that conventional tools are no longer needed. However, behind all this, I feel that the amount of activity each of us needs to handle has actually increased to an abnormal degree.&lt;/p&gt;

&lt;p&gt;When I look back on my past personal development experiences, the pace was much slower. For someone like me who does not code as a main profession, touching programs every day is not easy. Creating one class a day was enough, and as the term "weekend programmer" suggested, development was something that progressed little by little, taking time.&lt;/p&gt;

&lt;p&gt;But now, with the help of AI, I have three coding projects running simultaneously. New features are added every day, and issues that come up can be resolved within the same day. This is a speed that my former self could never have imagined.&lt;/p&gt;

&lt;p&gt;However, when I stop and reflect, I begin to wonder if this is really what I wanted to do.&lt;/p&gt;

&lt;p&gt;I started using AI to save time and improve efficiency, but before I knew it, I was being chased by the outputs that AI keeps producing one after another. To keep up with AI, I have been forcing myself to do unnecessary things. As a result, many outputs have been produced, but my mind has been tossed around and exhausted by them. This is not the future I wished for.&lt;/p&gt;

&lt;p&gt;When it comes to work, I have found a certain answer for how to engage with AI. My goal is not mere efficiency but improving quality. As long as I keep that in mind, AI functions as a very reliable partner.&lt;/p&gt;

&lt;p&gt;The problem is how I spend my private time. Being driven by code that AI has written or analysis reports that AI has organized feels somehow wrong.&lt;/p&gt;

&lt;p&gt;So I decided to return to the concept of essentialism. What is truly important to me? In which areas can I use AI to genuinely enhance my motivation and results? I need to reassess these things.&lt;/p&gt;




&lt;p&gt;It is natural to take responsibility for what you have created, but you must not be crushed by that responsibility and lose sight of your original purpose. Being controlled by something you started as a pastime or hobby is putting the cart before the horse.&lt;/p&gt;

&lt;p&gt;Therefore, to protect my own time, I established new rules.&lt;/p&gt;

&lt;p&gt;First, I place what is essentially important to me at the center of my schedule. Then, for everything else that I could do but is not urgent, I confine it to specific time blocks.&lt;/p&gt;

&lt;p&gt;Specifically, I dedicate my morning hours at the start of the day to my most mentally demanding important work and my core activities. I make sure to finish essential tasks within the morning. Then, I allocate tasks that do not require as much concentration and hobby-related activities to afternoon time blocks. If they do not get finished within that time, I simply carry them over to the next day.&lt;/p&gt;




&lt;p&gt;In the past, being controlled by other people was a source of frustration, but now that may have simply transformed into being controlled by AI.&lt;/p&gt;

&lt;p&gt;Precisely because I have obtained such a convenient tool, I want to have a clear sense of what I want to do and what I should do. Rather than matching myself to the speed of AI, I want to incorporate AI into my own comfortable rhythm. In this way, I intend to continue protecting my own way of moving forward.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>coding</category>
      <category>mentalhealth</category>
      <category>productivity</category>
    </item>
    <item>
      <title>When I Hired Multiple Generative AIs as Subordinates, Parallel Development Processing Never Stopped</title>
      <dc:creator>Takashi Abe</dc:creator>
      <pubDate>Wed, 11 Feb 2026 15:00:00 +0000</pubDate>
      <link>https://dev.to/sho7650/when-i-hired-multiple-generative-ais-as-subordinates-parallel-development-processing-never-stopped-390i</link>
      <guid>https://dev.to/sho7650/when-i-hired-multiple-generative-ais-as-subordinates-parallel-development-processing-never-stopped-390i</guid>
      <description>&lt;p&gt;Today is a relaxed introduction to recent Shossan updates.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Major Update to Obsidian AI Exporter
&lt;/h2&gt;

&lt;p&gt;The tool I have been publicly releasing for saving Gemini chat results to Obsidian, called Obsidian AI Exporter, has been significantly powered up.&lt;/p&gt;

&lt;p&gt;In my previous introduction, it was Gemini-only, but now it supports four sites: ChatGPT, Claude (including Deep Research), and Perplexity. In addition to Obsidian integration, you can now save as pure Markdown files or copy to clipboard.&lt;/p&gt;

&lt;p&gt;&lt;a href="//chromewebstore.google.com"&gt;Obsidian AI Exporter - Chrome Web Store&lt;/a&gt;&lt;br&gt;
Export AI conversations from Gemini, Claude, ChatGPT, and Perplexity to Obsidian&lt;/p&gt;

&lt;p&gt;While I think it will be in the maintenance phase unless there are major UI changes, I am still looking to add more useful features. If you have any feature requests, please let me know!&lt;/p&gt;

&lt;p&gt;Currently, I am considering automatic tagging, expanding the append feature, which is still not quite there, and Wiki Link support for Obsidian.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Building Your Own News Curation Site with n8n
&lt;/h2&gt;

&lt;p&gt;I wanted a site where I could read the latest overseas news summarized in a format convenient for me, so I developed my own curation platform.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbxa7mrg59e6rlvcn43i9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbxa7mrg59e6rlvcn43i9.png" alt=" " width="800" height="940"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The mechanism uses n8n to fetch updates from specific RSS feeds and URL lists, then an LLM (generative AI) translates and summarizes the content into Japanese. This is then registered to a display app I built with Docker.&lt;/p&gt;

&lt;p&gt;By managing specific news URLs as unique keys, it prevents duplicate posts with a simple mechanism. The main body is simple and just displays content, but I designed it with loose coupling so that LLMs and feeds can be easily changed. I have also published the source code, so please take a look if you are interested.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyll7oqg4n2t9k51u4bb9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyll7oqg4n2t9k51u4bb9.png" alt=" " width="800" height="154"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Around v2, the check/ingest/post article parts are the developed app portions.&lt;/p&gt;

&lt;p&gt;I named it tech-news as is since it is for personal use, but really the content to post can be anything as long as it is Markdown, so it is like a simple blog. Since I am developing it component by component for easy reuse, I am feeling like it has become more versatile than I expected.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Revamping Home VPN Environment with Tailscale
&lt;/h2&gt;

&lt;p&gt;On the infrastructure side, I am organizing my home access environment.&lt;/p&gt;

&lt;p&gt;&lt;a href="//tailscale.com"&gt;Tailscale | Secure Connectivity for AI, IoT &amp;amp; Multi-Cloud&lt;/a&gt;&lt;br&gt;
The connectivity platform for devs, IT, and security teams.&lt;/p&gt;

&lt;p&gt;I had been using the VPN function of my Yamaha router, but considering security and convenience, I am currently introducing and configuring Tailscale.&lt;/p&gt;

&lt;p&gt;Being able to safely access home resources via NAT traversal without opening VPN ports externally is safe and comfortable. The app is also easy to use, and functionally very detailed settings are possible. I have no idea why it is free for up to 3 users.&lt;/p&gt;

&lt;p&gt;Currently, I am configuring a router with Docker on my home Linux server and managing settings with Ansible. An environment is coming together where I can work without being conscious of the boundary between home and the internet even when out.&lt;/p&gt;

&lt;h2&gt;
  
  
  From the Joy of Researching to the Joy of Realizing
&lt;/h2&gt;

&lt;p&gt;Running multiple projects in parallel like this, I can feel the changes in development style in the AI era firsthand.&lt;/p&gt;

&lt;p&gt;In the past, I found joy in the trial and error of researching technologies one by one. But now, I find it fun to watch my ideas take shape one after another, using multiple generative AIs as my subordinates. Is this a change due to age, or is it new excitement brought about by technological evolution?&lt;/p&gt;

&lt;p&gt;The image of what I wanted to do becomes realized with the help of AI. Grinning at that process, I am giving instructions to my new subordinates again today.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>productivity</category>
      <category>showdev</category>
      <category>tooling</category>
    </item>
    <item>
      <title>How to Transform 3 Years of Thinking into Assets: Accelerate Feedback with Claude Cowork</title>
      <dc:creator>Takashi Abe</dc:creator>
      <pubDate>Wed, 11 Feb 2026 13:30:16 +0000</pubDate>
      <link>https://dev.to/sho7650/how-to-transform-3-years-of-thinking-into-assets-accelerate-feedback-with-claude-cowork-kl0</link>
      <guid>https://dev.to/sho7650/how-to-transform-3-years-of-thinking-into-assets-accelerate-feedback-with-claude-cowork-kl0</guid>
      <description>&lt;h1&gt;
  
  
  Claude Cowork: The Ultimate Engine for Objectively Viewing Your Own Thought Evolution
&lt;/h1&gt;

&lt;p&gt;I had been pondering whether Claude Cowork actually has any practical uses.&lt;/p&gt;

&lt;p&gt;Then I discovered yet another new way to use it. Cowork is a work companion that autonomously executes multiple tasks based on massive amounts of data that surpass what chat can handle.&lt;/p&gt;

&lt;h2&gt;
  
  
  Claude Cowork: The Ultimate Engine for Objectively Viewing Your Own Thought Evolution
&lt;/h2&gt;

&lt;p&gt;As you know, Claude Cowork is not merely a chat tool. It is an AI agent that directly accesses your local file system (in my case, Obsidian) and autonomously completes tasks.&lt;/p&gt;

&lt;p&gt;You can either place Obsidian in a folder that Cowork can access, or use the combination of Local REST API + Obsidian MCP Tools to have Cowork retrieve information via MCP—both approaches work effectively.&lt;/p&gt;

&lt;p&gt;What makes this tool truly disruptive is its ability to execute against three years of my living thought data with precision and speed comparable to the Observe and Orient phases of the OODA loop (Observe, Orient, Decide, Act). Sounds pretentious, right?&lt;/p&gt;

&lt;h2&gt;
  
  
  Enabling High-Dimensional Differential Extraction Across Time—Something Difficult to Do Alone
&lt;/h2&gt;

&lt;p&gt;Why bother using Claude Cowork? Because I know how vague human memory is and how conveniently it gets rewritten.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Breaking Through the Limits of Observe:&lt;/strong&gt; It is humanly impossible to extract every single description on a specific theme from three years of notes numbering in the thousands. Information obtained through word searches is only a fraction. You need to extract information while considering intent as well. Cowork autonomously repeats searching and browsing, comprehensively observing all data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Deepening Orient:&lt;/strong&gt; The thinking habits of my three-years-ago self versus my current self, the subjects of interest, and changes in word choice. By having AI identify these differences, I can confront my own growth or stagnation as objective facts that I could never have noticed on my own.&lt;/p&gt;

&lt;p&gt;This is not for reminiscing about the past, but for gaining an advanced metacognitive environment to use my past self as a stepping stone toward new ideas.&lt;/p&gt;

&lt;p&gt;This is an effective environment that I could not achieve simply by using Obsidian as RAG with Dify. What was limited to merely retrieving information has now evolved into comprehensive information retrieval and analysis.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using Three Years of Obsidian as Fuel to Gain Insights for the Future
&lt;/h2&gt;

&lt;p&gt;I have actually shared my three years of Obsidian data with Claude Cowork and am running workflows like the following:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 1 (Observe):&lt;/strong&gt; Scan all notes containing my views on generative AI from 2023 to the present (2026).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2 (Orient):&lt;/strong&gt; Analyze chronologically at which points and through which technological breakthroughs my thinking changed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3 (Insight):&lt;/strong&gt; From that trajectory of change, have it present blind spots in thinking that I should focus on in the next year.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;[Image of spinning the OOA loop with Cowork]&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Previously, doing this required spending several days reading through past notes. However, with Claude Cowork, while generating intermediate analysis files in the working folder, it writes up a map of my own thinking in just minutes. This is not mere summarization—it is prototyping for the future through dialogue with my past self.&lt;/p&gt;

&lt;p&gt;This is an efficient and effective way to reuse PKM (Personal Knowledge Management).&lt;/p&gt;

&lt;h2&gt;
  
  
  Continuously Updating Your Thinking Together with an AI Colleague
&lt;/h2&gt;

&lt;p&gt;In these uncertain times ahead, there is no time to cling to past success stories. On the other hand, data about what you thought in the past and how you experimented is your own unique asset that belongs to no one else.&lt;/p&gt;

&lt;p&gt;Stop using Claude Cowork as just a summarization tool.&lt;/p&gt;

&lt;p&gt;Cowork is literally a partner for increasing the speed of your thinking and running the OODA loop faster than anyone else. Whether you let three years of data sleep as mere records or turn it into a power plant for your next ideas—that key depends on how you master Cowork as an agent.&lt;/p&gt;

&lt;p&gt;I will continue to gaze into this abyss of the ultimate mirror and refine my thinking day after day.&lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>mcp</category>
      <category>productivity</category>
    </item>
    <item>
      <title>The Illusion of My Own Efficiency: How Claude Exposed My Arrogance and Lack of Planning at Night</title>
      <dc:creator>Takashi Abe</dc:creator>
      <pubDate>Fri, 06 Feb 2026 07:21:29 +0000</pubDate>
      <link>https://dev.to/sho7650/the-illusion-of-my-own-efficiency-how-claude-exposed-my-arrogance-and-lack-of-planning-at-night-43lg</link>
      <guid>https://dev.to/sho7650/the-illusion-of-my-own-efficiency-how-claude-exposed-my-arrogance-and-lack-of-planning-at-night-43lg</guid>
      <description>&lt;h1&gt;
  
  
  Conversations with AI are not for improving skills, but for recognizing your own arrogance
&lt;/h1&gt;

&lt;p&gt;I, Shossan, have a certain pride in having automated my environment reasonably well and controlling it efficiently. However, through analyzing my own shell history while conversing with the trending Claude Cowork, what was thrust before me was an image of myself—utterly unplanned and full of biases. How embarrassing.&lt;/p&gt;

&lt;p&gt;Being prompted by AI to improve is a shock close to defeat as an engineer. However, I am convinced that this very defeat is the only gateway to the next stage of growth. Gemini's words are too harsh.&lt;/p&gt;

&lt;h2&gt;
  
  
  People mistake unconscious repetition for effort
&lt;/h2&gt;

&lt;p&gt;Why couldn't I notice my own inefficiencies? It's because a bias called familiarity had crept into my daily work.&lt;/p&gt;

&lt;p&gt;This time, when I had Claude cowork analyze my past command history, the following three blind spots were exposed:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Collapse of time management:&lt;/strong&gt; Activity peaked at 21:00, a time when I should have been resting. This wasn't diligence—it was merely repaying debt caused by lack of planning during the day.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hollow automation:&lt;/strong&gt; I manually executed ansible-playbook 31 times. While thinking I was using automation tools, the execution process itself was extremely analog and dependent on manual work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tool dependency:&lt;/strong&gt; By using convenient tools (Keyboard Maestro, Stream Deck, Claude Code, etc.), I had arbitrarily decided that there was no more room for improvement.&lt;/p&gt;

&lt;h2&gt;
  
  
  The cold data-driven insights and the automation functions I immediately implemented
&lt;/h2&gt;

&lt;p&gt;According to Claude's analysis, my activity is concentrated at 8:00 and 21:00. The fact that I was hammering away at commands at 21:00 after my bath is evidence of a bad habit that degrades sleep quality and diminishes the next day's performance. I truly never expected such results. I was genuinely surprised.&lt;/p&gt;

&lt;p&gt;You should sleep after your bath. If you have to execute commands at that time, it means your planning has failed. Accepting this fact, I immediately adopted the improvement proposals that Claude suggested.&lt;/p&gt;

&lt;p&gt;For example, here is a function for managing my custom LaunchAgents that start with com.oshiire.. Until now, I had been typing commands while trying to remember paths or searching through history, but now this svc function takes over that cognitive load.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# ~/.config/fish/functions/svc.fish
function svc --description "LaunchAgent service control"
  set -l action $argv[1]
  set -l name $argv[2]
  set -l plist ~/Library/LaunchAgents/com.oshiire.$name.plist

  switch $action
    case start
      launchctl load -w $plist
    case stop
      launchctl unload -w $plist
    case restart
      launchctl unload -w $plist; and launchctl load -w $plist
    case status
      launchctl list | grep $name
    case '*'
      echo "Usage: svc [start|stop|restart|status] [service_name]"
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Additionally, Claude proposed a short function called ap for ansible-playbook. This eliminates small decisions like argument specification mistakes and forgotten options. Such thoughtful consideration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Looking into the mirror called AI, strategically updating yourself continuously
&lt;/h2&gt;

&lt;p&gt;If you just end up praising AI's improvement suggestions as amazing, you are merely a consumer. For me, the learning from this experience lies in how to convert the negative of having AI point things out to me into the positive of solving through systems.&lt;/p&gt;

&lt;p&gt;Abandon the assumption that you are doing well, and set aside time to regularly dissect yourself with objective data. Use AI not just as a code generator but as a behavioral analysis partner.&lt;/p&gt;

&lt;p&gt;From here, I will continue to cut away waste, one step at a time. Why don't you also try exposing your terminal history and behavioral history—your embarrassing parts—to AI?&lt;/p&gt;

</description>
    </item>
    <item>
      <title>AI Era Survival Strategy: From Mimeograph to LLM. What Experienced Professionals Can Do About Technology's Black Box</title>
      <dc:creator>Takashi Abe</dc:creator>
      <pubDate>Thu, 05 Feb 2026 04:16:09 +0000</pubDate>
      <link>https://dev.to/sho7650/ai-era-survival-strategy-from-mimeograph-to-llm-what-experienced-professionals-can-do-about-8ad</link>
      <guid>https://dev.to/sho7650/ai-era-survival-strategy-from-mimeograph-to-llm-what-experienced-professionals-can-do-about-8ad</guid>
      <description>&lt;p&gt;Generative AI has truly enabled us to do so many things. I can no longer imagine a future where we live without being involved with AI.&lt;/p&gt;

&lt;p&gt;This note is a perfect example. The content I am speaking through voice input right now is being rewoven into proper sentences by an LLM, which then drafts this note for me. AI is integral to both work and hobbies. Even the music production I need for my new DJ hobby can be handled by generative AI like Suno, which whips up several tracks in no time.&lt;/p&gt;

&lt;p&gt;I no longer manually click through each Google search link when conducting research. I just ask Perplexity. At work, I use NotebookLM and Gemini Deep Research features to handle everything from analyzing massive amounts of documents to creating reports.&lt;/p&gt;

&lt;p&gt;It may be too early to call this infrastructure, but thanks to generative AI, the areas I can cover by myself have expanded dramatically. By paying a small fee to an AI partner, I can now substitute tasks that previously required hiring specialists at a reasonable quality level. We live in such a world.&lt;/p&gt;

&lt;p&gt;I have already outsourced the tedious work of writing code from scratch to AI. However, there is something we must not misunderstand here. Being able to use AI and having ability as an engineer are completely different things.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Transition from Craftsman to AI Commander
&lt;/h2&gt;

&lt;p&gt;Looking back, Japanese engineers used to be a group of craftsmen. During that era, people valued the long years spent honing specialized skills.&lt;/p&gt;

&lt;p&gt;And now? By lining up excellent generative AI agents, a single person can give them instructions and produce deliverables spanning multiple specialized fields in an unbelievably short time.&lt;/p&gt;

&lt;p&gt;I am constantly amazed by the innovations of our time. At the same time, I feel that what one person should do and can do has exploded.&lt;/p&gt;

&lt;h2&gt;
  
  
  From Mimeograph to TikTok: A History Walking Alongside Tools
&lt;/h2&gt;

&lt;p&gt;When I was still in elementary and middle school, from the end of the Showa era to the beginning of Heisei, it was primarily an analog era.&lt;/p&gt;

&lt;p&gt;We barely had word processors and printers, and I remember struggling to create decent printed materials. Come to think of it, my middle school graduation anthology was mimeographed. Young people today probably do not know what a mimeograph is.&lt;/p&gt;

&lt;p&gt;In 1995, when I joined my company, that atmosphere lingered. Presentations meant writing on transparent OHP sheets with markers and projecting them. Right after joining, company training included analog protocols like how to address internal and external mail. Email addresses were something only a select few had.&lt;/p&gt;

&lt;p&gt;By the end of the 20th century, we could write papers in Word and create slides in PowerPoint. Entering the 21st century brought further refinement, with demands for eye-catching slides using special animation effects. It also became an era requiring design sense from engineers, and I remember the struggle of needing yet another new skill. I was glad I did design work in middle school.&lt;/p&gt;

&lt;p&gt;Then YouTube emerged, making it normal for amateurs to publish videos. Now with just an iPhone, you can livestream on TikTok. Even my daughter does regular streams.&lt;/p&gt;

&lt;p&gt;Every time tools evolved, individual possibilities continued to expand. And now, with the emergence of generative AI, that expansion is reaching its limits. That is an exaggeration, Gemini.&lt;/p&gt;

&lt;h2&gt;
  
  
  Elevated Abilities and the Invisible Current Position
&lt;/h2&gt;

&lt;p&gt;However, with all this convenience comes new worries.&lt;/p&gt;

&lt;p&gt;In the narrow village society without the internet, comparison targets were only nearby, so the goal of how far you needed to go to be recognized seemed visible.&lt;/p&gt;

&lt;p&gt;But now it is different.&lt;/p&gt;

&lt;p&gt;We live in a world where you can see endless geniuses if you look up. Since generative AI can easily elevate your abilities, it becomes even harder to understand your true capabilities and growth. In a sense, it has become a cruel world where it is easy to put on air.&lt;/p&gt;

&lt;p&gt;I was fortunate to experience the transition from analog to digital and older technologies firsthand.&lt;/p&gt;

&lt;p&gt;Old machine language and assembly were 8-bit worlds. The number of instructions was small, and memorizing them was easy. It was possible to grasp everything.&lt;/p&gt;

&lt;p&gt;However, with modern 64-bit instruction sets, developing anything with assembly alone is madness. Things have become far too complex, and the learning method of understanding and building everything from zero is no longer realistic. To produce results in a finite lifetime, we have no choice but to skillfully utilize the black box called AI and hone our skills in compressed time.&lt;/p&gt;

&lt;h2&gt;
  
  
  What We Veterans Can Leave for the Next Generation
&lt;/h2&gt;

&lt;p&gt;AI will do it, so you do not need to know the internals.&lt;/p&gt;

&lt;p&gt;This is correct in a sense, but it can also hinder growth as an engineer. In an era when you can create working things without knowing the internals, what should you do to deliberately acquire skills? This is an extremely difficult challenge to consider.&lt;/p&gt;

&lt;p&gt;So what is the role of old-timers like us?&lt;/p&gt;

&lt;p&gt;Simply passing down stories of past hardships has no meaning. What is important is conveying the background of how each thing and technology was created and how it works.&lt;/p&gt;

&lt;p&gt;The logic behind the code AI spits out, the underlying principles beneath convenient frameworks. Given our intuitive understanding of these concepts, we should be capable of deciphering the hidden meanings and effectively sharing knowledge.&lt;/p&gt;

&lt;p&gt;Not just using existing AI, but how do we create new technologies beyond that? As those who know the principles, we have come to need a certain responsibility in educating the next generation and driving innovation.&lt;/p&gt;

&lt;h2&gt;
  
  
  To Avoid Mass-Producing Working Garbage
&lt;/h2&gt;

&lt;p&gt;Here, the role of people like us who know old technologies changes dramatically. It is not intended to lecture young people about learning assembly. The goal is to use that knowledge to detect code that appears correct but is actually inefficient and dangerous—working garbage produced by AI.&lt;/p&gt;

&lt;p&gt;AI lies without hesitation. It proposes implementations full of security holes and presents architectures that wastefully consume resources. At such times, if you have low-level knowledge—the principles of how computers actually work—in your head, you can instantly detect discomfort, like this implementation feels wrong or this coding style will become technical debt.&lt;/p&gt;

&lt;p&gt;This aesthetic eye is the only value in this era. It is impossible to beat AI in coding speed. However, in quality assurance and architecture design, humans still have an advantage. But that is a privilege only those who know the internals can have.&lt;/p&gt;

&lt;h2&gt;
  
  
  Veteran Survival Strategy: From Coder to Auditor
&lt;/h2&gt;

&lt;p&gt;As a reminder to myself, I defined it this way. The job of engineers in the AI era is not creation but auditing.&lt;/p&gt;

&lt;p&gt;Knowledge of assembly and memory management does not exist for nostalgia. It is a weapon to break through modern highly abstracted layers and expose the flaws AI tries to hide.&lt;/p&gt;

&lt;p&gt;The black-boxing of technology will not stop. Therefore, instead of retelling old war stories, we veterans should delve into the complexity of this technology and analyze its workings.&lt;/p&gt;

&lt;p&gt;It is fine to have an AI write code. Let's also maintain a relaxed attitude. However, the moment we can no longer ask why about that output, we stop being engineers and become mere AI operators.&lt;/p&gt;

&lt;p&gt;I ask with self-reflection.&lt;br&gt;
That code of yours—do you truly understand its contents before deploying it?&lt;/p&gt;

</description>
      <category>ai</category>
      <category>computerscience</category>
      <category>career</category>
      <category>futurechallenge</category>
    </item>
  </channel>
</rss>
