---
title: "Sumitsubo：把規格做成 Linter"
date: 2026-08-26T00:00:00+08:00
publishDate: 2026-08-26T00:00:00+08:00
lastmod: 2026-08-24T20:52:28+08:00
tags: ["LLM","AI","經驗","Ruby","SDD"]
toc: true
permalink: "https://blog.aotoki.me/posts/2026/08/26/sumitsubo-spec-as-linter/"
language: "zh-tw"
---



開發 [Kobako](https://github.com/elct9620/kobako) 的過程到中後期，一直讓我對於「規格有沒有對齊」這樣的問題困擾著我。理論上規格驅動開發（Spec-Driven Development）是很理想的，但語言模型本身就不是確定性的設計，即使有規格參照也還是會偏移，或者遺漏。

因此 [Sumitsubo](https://github.com/elct9620/sumitsubo) 這個專案就在最近被設計出來，作為一個通用的檢查工具。

<!--more-->

## 理想跟現實{#ideal-and-real}

想要證明一個規格被實作，除了實際跑過跟閱讀原始碼之外，基本上很難有效率的確認，尤其是規模變大、變複雜之後更是如此。

如果想要讓這種無法機械化的工作有機會機械化處理，結構化的資料是一個很不錯的選項，在去年 Anthropic 的 [Effective harnesses for long-running agents](https://www.anthropic.com/engineering/effective-harnesses-for-long-running-agents) 剛好展示了一種不錯的可能性，用 JSON 登記規格，然後讓 Claude Code 去把規格實作。

實際上這仍不能保證完美的覆蓋，但至少證明維護 JSON 對 AI 已經不再困難，按照 JSON 的內容維護功能，表現的基本上也算穩定，至少可以順利的運作到大部分的功能都被實作出來。

理想上是有一種神奇的方法可以把一段自然語言，對應到某段實作，但現實很難做到這點，往後退一步用「聲明（Claim）」的方式，讓 AI 宣稱某段規格有被實作，倒是在「完全不檢查」跟「完美檢查」之間權衡下，可接受的考量。

## 妥善引導{#guide-the-agent}

在 Sumitsubo 推出之前，Kobako 會用「錨點」的機制來檢查，像是規格定義行為 `B-25` 時，至少要在測試的註解看到一次 `B-25` 才算通過，這樣就可以一定程度促進 AI 去確認為什麼某個規格提過的功能沒有測試覆蓋。

轉換到 Sumitsubo 後，採用的是「行為標記」的方式，利用 BDD（Behavior-Driven Development）風格搭配編號來實作，這並不是新概念 Gherkin 也有 `@B-25` 的標籤機制，但我把人工維護轉為 AI 輔助處理。

```json
{
  "name": "Behavior",
  "description": "The scenarios a project declares, and where each one sits.",
  "include": [
    "test/behavior_test.rb"
  ],
  "scenarios": [
    {
      "id": "B-001",
      "title": "What the directory declares, and where",
      "given": [
        "a behavior directory holding more than one specification"
      ],
      "when": "the directory is loaded",
      "then": "every scenario answers with the line of the file that declares it"
    }
  ]
}
```

上方是 Sumitsubo 自己維護的一條行為規則，放在 `.spec/behavior/` 目錄下，編號 `B-001` 會確認所有的規格都有被載入，因此在 `behavior_test.rb` 中可以看到像這樣的標記。

```ruby
# @behavior B-001
puts "--- what the directory declares, and where ---"
Sumitsubo::Behavior.load("test/fixtures/behavior/.spec/behavior").each do |feature|
  puts "#{feature.name} #{feature.includes.inspect}"
  feature.scenarios.each do |scenario|
    puts "  #{scenario.path}:#{scenario.line} #{scenario.id} #{scenario.title}"
    scenario.given.each { |state| puts "    given #{state}" }
    puts "    when  #{scenario.action}"
    puts "    then  #{scenario.outcome}"
  end
end
```

這樣就讓 AI 自己證明了「我有測試過這條規則」而不是毫無根據，如果沒有做行為標記，就會在 `sumi verify` 看到這樣的訊息

```bash
$ sumi verify
.spec/behavior/behavior.json:6 @behavior B-001 is claimed nowhere in test/*_test.rb
1 differences
```

那麼實作過程中，AI 就會自己發現少了對應的處理，進一步的補上一條測試，那就可以一定程度的確認某條規格，至少被測試過一次。

實際上標記只說明測試宣稱涵蓋了這條規格，並不保證測試內容真的驗證了那件事，仍然是「聲明」而非證明，但比起沒有對應關係，落差至少變得看得見。

> 上方的測試是因為使用 Matz 今年在日本 RubyKaigi 公開的新專案 [Spinel](https://github.com/matz/spinel) 採用預先編譯 Ruby 為 C 後，轉成靜態執行檔的設計，因此測試是真實跑執行檔輸出比對，而不是使用斷言驗證

## 自主規格化{#auto-spec}

開發 Sumitsubo 的過程中，讓我懷疑規格驅動開發是否還是必要的，或者真的會隨著駕馭工程（Harness Engineering）的成熟而消失，因為當 Sumitsubo 用來自我檢查時，我發現了蠻有趣的現象。

原本我們走向先規格後實作的理由，是因為只靠程式碼太難理解，而且在過去軟體開發的經驗中，文件跟實作是很難對齊的，至少在 AI 時代這件事情一定程度可以做到，所以先規格後實作確實變成很合理的機制，尤其是在 AI 能力較差時可以發揮引導的作用。

然而 Sumitsubo 在開發時，我為了讓他用自己來維護自己的規格，就沒有刻意從 `SPEC.md`（我偏好的規格文件）開始，而是完全的從頭進行，當「行為標記」的機制搭配 Claude Code 的 Hook 被設定到專案上後，AI 自己就足夠發現規格差異進行修正。

例如 Sumitsubo 有一個「標記」的機制記載了「註解後面沒有程式碼，則不看作有標記」的規則，當我開始為 Kobako 增加 Rust 支援後，抽象化了 `Language`（語言）的概念，此時 AI 自己注意到這條規則跟著「語言行為」而非「標記行為」更合理，就自己完成標記的調整、行為的修訂等等。

當工具到位（適當的設計）時，也許原本很多依靠提示詞的機制對 AI 來說才是限制，假設我們提供一套有系統的規格維護工具，也許只需要用「使用 `sumi verify` 檢查規格」這樣簡單的指示，就可以把大部分規格維護好。

雖然我是蠻堅定的規格驅動開發支持者，但是當我把規格變成一種可驗證工具（Linter）時，我確實對此開始懷疑，即使我還不確定未來會怎麼發展，但也確實是一條值得嘗試的新路線。
