<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:media="http://search.yahoo.com/mrss/"><channel><title>SDD on 弦而時習之</title><link>https://blog.aotoki.me/tags/SDD/</link><description>Recent content in SDD on 弦而時習之</description><generator>Hugo -- gohugo.io</generator><language>zh-TW</language><managingEditor>contact@aotoki.me (蒼時弦也)</managingEditor><webMaster>contact@aotoki.me (蒼時弦也)</webMaster><lastBuildDate>Mon, 24 Aug 2026 20:52:28 +0800</lastBuildDate><atom:link href="https://blog.aotoki.me/tags/SDD/index.xml" rel="self" type="application/rss+xml"/><item><title>Sumitsubo：把規格做成 Linter</title><link>https://blog.aotoki.me/posts/2026/08/26/sumitsubo-spec-as-linter/</link><category>LLM</category><category>AI</category><category>經驗</category><category>Ruby</category><category>SDD</category><pubDate>Wed, 26 Aug 2026 00:00:00 +0800</pubDate><author>contact@aotoki.me (蒼時弦也)</author><guid>https://blog.aotoki.me/posts/2026/08/26/sumitsubo-spec-as-linter/</guid><description>&lt;p&gt;開發 &lt;a href="https://github.com/elct9620/kobako"&gt;Kobako&lt;/a&gt; 的過程到中後期，一直讓我對於「規格有沒有對齊」這樣的問題困擾著我。理論上規格驅動開發（Spec-Driven Development）是很理想的，但語言模型本身就不是確定性的設計，即使有規格參照也還是會偏移，或者遺漏。&lt;/p&gt;
&lt;p&gt;因此 &lt;a href="https://github.com/elct9620/sumitsubo"&gt;Sumitsubo&lt;/a&gt; 這個專案就在最近被設計出來，作為一個通用的檢查工具。&lt;/p&gt;</description><content:encoded>&lt;p&gt;開發 &lt;a href="https://github.com/elct9620/kobako"&gt;Kobako&lt;/a&gt; 的過程到中後期，一直讓我對於「規格有沒有對齊」這樣的問題困擾著我。理論上規格驅動開發（Spec-Driven Development）是很理想的，但語言模型本身就不是確定性的設計，即使有規格參照也還是會偏移，或者遺漏。&lt;/p&gt;
&lt;p&gt;因此 &lt;a href="https://github.com/elct9620/sumitsubo"&gt;Sumitsubo&lt;/a&gt; 這個專案就在最近被設計出來，作為一個通用的檢查工具。&lt;/p&gt;
&lt;h2 id="ideal-and-real"&gt;理想跟現實&lt;/h2&gt;
&lt;p&gt;想要證明一個規格被實作，除了實際跑過跟閱讀原始碼之外，基本上很難有效率的確認，尤其是規模變大、變複雜之後更是如此。&lt;/p&gt;
&lt;p&gt;如果想要讓這種無法機械化的工作有機會機械化處理，結構化的資料是一個很不錯的選項，在去年 Anthropic 的 &lt;a href="https://www.anthropic.com/engineering/effective-harnesses-for-long-running-agents"&gt;Effective harnesses for long-running agents&lt;/a&gt; 剛好展示了一種不錯的可能性，用 JSON 登記規格，然後讓 Claude Code 去把規格實作。&lt;/p&gt;
&lt;p&gt;實際上這仍不能保證完美的覆蓋，但至少證明維護 JSON 對 AI 已經不再困難，按照 JSON 的內容維護功能，表現的基本上也算穩定，至少可以順利的運作到大部分的功能都被實作出來。&lt;/p&gt;
&lt;p&gt;理想上是有一種神奇的方法可以把一段自然語言，對應到某段實作，但現實很難做到這點，往後退一步用「聲明（Claim）」的方式，讓 AI 宣稱某段規格有被實作，倒是在「完全不檢查」跟「完美檢查」之間權衡下，可接受的考量。&lt;/p&gt;
&lt;h2 id="guide-the-agent"&gt;妥善引導&lt;/h2&gt;
&lt;p&gt;在 Sumitsubo 推出之前，Kobako 會用「錨點」的機制來檢查，像是規格定義行為 &lt;code&gt;B-25&lt;/code&gt; 時，至少要在測試的註解看到一次 &lt;code&gt;B-25&lt;/code&gt; 才算通過，這樣就可以一定程度促進 AI 去確認為什麼某個規格提過的功能沒有測試覆蓋。&lt;/p&gt;
&lt;p&gt;轉換到 Sumitsubo 後，採用的是「行為標記」的方式，利用 BDD（Behavior-Driven Development）風格搭配編號來實作，這並不是新概念 Gherkin 也有 &lt;code&gt;@B-25&lt;/code&gt; 的標籤機制，但我把人工維護轉為 AI 輔助處理。&lt;/p&gt;
&lt;div class="codeblock not-prose" data-controller="codeblock"&gt;
&lt;div data-codeblock-target="code"&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Behavior&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;description&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;The scenarios a project declares, and where each one sits.&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;include&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;test/behavior_test.rb&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;scenarios&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;id&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;B-001&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;title&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;What the directory declares, and where&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;given&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;a behavior directory holding more than one specification&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;when&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;the directory is loaded&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;then&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;every scenario answers with the line of the file that declares it&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;button class="codeblock__copy"
data-action="codeblock#copy"
data-codeblock-target="button"
data-copy-text="複製"
data-copied-text="已複製"
data-copy-label="複製程式碼"
data-copied-label="程式碼已複製"
aria-label="複製程式碼"&gt;
複製
&lt;/button&gt;
&lt;/div&gt;
&lt;p&gt;上方是 Sumitsubo 自己維護的一條行為規則，放在 &lt;code&gt;.spec/behavior/&lt;/code&gt; 目錄下，編號 &lt;code&gt;B-001&lt;/code&gt; 會確認所有的規格都有被載入，因此在 &lt;code&gt;behavior_test.rb&lt;/code&gt; 中可以看到像這樣的標記。&lt;/p&gt;
&lt;div class="codeblock not-prose" data-controller="codeblock"&gt;
&lt;div data-codeblock-target="code"&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ruby" data-lang="ruby"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# @behavior B-001&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;--- what the directory declares, and where ---&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="no"&gt;Sumitsubo&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Behavior&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;test/fixtures/behavior/.spec/behavior&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;feature&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;feature&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;feature&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;includes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;inspect&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;feature&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;scenarios&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;scenario&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34; &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;scenario&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;scenario&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;scenario&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;scenario&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;scenario&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;given&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;each&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34; given &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34; when &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;scenario&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;action&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34; then &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;scenario&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;outcome&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;end&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;button class="codeblock__copy"
data-action="codeblock#copy"
data-codeblock-target="button"
data-copy-text="複製"
data-copied-text="已複製"
data-copy-label="複製程式碼"
data-copied-label="程式碼已複製"
aria-label="複製程式碼"&gt;
複製
&lt;/button&gt;
&lt;/div&gt;
&lt;p&gt;這樣就讓 AI 自己證明了「我有測試過這條規則」而不是毫無根據，如果沒有做行為標記，就會在 &lt;code&gt;sumi verify&lt;/code&gt; 看到這樣的訊息&lt;/p&gt;
&lt;div class="codeblock not-prose" data-controller="codeblock"&gt;
&lt;div data-codeblock-target="code"&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;$ sumi verify
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;.spec/behavior/behavior.json:6 @behavior B-001 is claimed nowhere in test/*_test.rb
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="m"&gt;1&lt;/span&gt; differences&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;button class="codeblock__copy"
data-action="codeblock#copy"
data-codeblock-target="button"
data-copy-text="複製"
data-copied-text="已複製"
data-copy-label="複製程式碼"
data-copied-label="程式碼已複製"
aria-label="複製程式碼"&gt;
複製
&lt;/button&gt;
&lt;/div&gt;
&lt;p&gt;那麼實作過程中，AI 就會自己發現少了對應的處理，進一步的補上一條測試，那就可以一定程度的確認某條規格，至少被測試過一次。&lt;/p&gt;
&lt;p&gt;實際上標記只說明測試宣稱涵蓋了這條規格，並不保證測試內容真的驗證了那件事，仍然是「聲明」而非證明，但比起沒有對應關係，落差至少變得看得見。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;上方的測試是因為使用 Matz 今年在日本 RubyKaigi 公開的新專案 &lt;a href="https://github.com/matz/spinel"&gt;Spinel&lt;/a&gt; 採用預先編譯 Ruby 為 C 後，轉成靜態執行檔的設計，因此測試是真實跑執行檔輸出比對，而不是使用斷言驗證&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2 id="auto-spec"&gt;自主規格化&lt;/h2&gt;
&lt;p&gt;開發 Sumitsubo 的過程中，讓我懷疑規格驅動開發是否還是必要的，或者真的會隨著駕馭工程（Harness Engineering）的成熟而消失，因為當 Sumitsubo 用來自我檢查時，我發現了蠻有趣的現象。&lt;/p&gt;
&lt;p&gt;原本我們走向先規格後實作的理由，是因為只靠程式碼太難理解，而且在過去軟體開發的經驗中，文件跟實作是很難對齊的，至少在 AI 時代這件事情一定程度可以做到，所以先規格後實作確實變成很合理的機制，尤其是在 AI 能力較差時可以發揮引導的作用。&lt;/p&gt;
&lt;p&gt;然而 Sumitsubo 在開發時，我為了讓他用自己來維護自己的規格，就沒有刻意從 &lt;code&gt;SPEC.md&lt;/code&gt;（我偏好的規格文件）開始，而是完全的從頭進行，當「行為標記」的機制搭配 Claude Code 的 Hook 被設定到專案上後，AI 自己就足夠發現規格差異進行修正。&lt;/p&gt;
&lt;p&gt;例如 Sumitsubo 有一個「標記」的機制記載了「註解後面沒有程式碼，則不看作有標記」的規則，當我開始為 Kobako 增加 Rust 支援後，抽象化了 &lt;code&gt;Language&lt;/code&gt;（語言）的概念，此時 AI 自己注意到這條規則跟著「語言行為」而非「標記行為」更合理，就自己完成標記的調整、行為的修訂等等。&lt;/p&gt;
&lt;p&gt;當工具到位（適當的設計）時，也許原本很多依靠提示詞的機制對 AI 來說才是限制，假設我們提供一套有系統的規格維護工具，也許只需要用「使用 &lt;code&gt;sumi verify&lt;/code&gt; 檢查規格」這樣簡單的指示，就可以把大部分規格維護好。&lt;/p&gt;
&lt;p&gt;雖然我是蠻堅定的規格驅動開發支持者，但是當我把規格變成一種可驗證工具（Linter）時，我確實對此開始懷疑，即使我還不確定未來會怎麼發展，但也確實是一條值得嘗試的新路線。&lt;/p&gt;</content:encoded></item></channel></rss>