全部技能

test-driven-development (obra/superpowers)

以測試先行方式實作功能與修正缺陷,先寫會失敗的測試,再寫實作程式碼。

superpowers test-driven-development:讓代理先寫測試,代價是 Token 用量約 1.6 倍

推薦安全

Codex CLI · gpt-6-astra

24.0 / 30

相對未使用技能的提升:+1.00 · 推薦 · 18 次執行

推薦:總分 24.0/30 至少 22,產出品質 5.0 至少 3.5,提升幅度 +1.00 至少 +1.0。

SOTA

透過

使用 Codex 測試。

涵蓋 Codex, Claude Code

Codex CLI 0.166(SOTA 等級):18 次評分執行。依我們的等級規則,在這裡通過也涵蓋 Claude Code;但這不代表第二等級的結果。

分數

每個維度為 0 到 5 分,六個維度相加即為滿分 30 的總分。每個維度都是 5 分最好。成本 5 分代表便宜又快速;安裝 5 分代表容易安裝。

觸發可靠性
5.0
相對未使用技能的提升
3.0
產出品質
5.0
Token 與時間成本
2.0
安裝與可攜性
4.0
安全性
5.0

安全審查

安全

靜態掃描:0 CRITICAL / 0 HIGH / 0 MEDIUM。人工審查:可在無網路沙箱中安全執行(未發現問題)。掃描結果乾淨,不代表技能一定安全。

安裝

npx skills@latest add obra/superpowers --skill test-driven-development

來源

固定 Commit: 2ce8aa15d05bd57213e6f746f953aafb7ded7a8b

測試日期: 2026-11-02

我們測了什麼

  • 依精確規格實作函式 — 在 slug.py 中實作 slugify(text, max_length=None)(Python 3,僅使用標準函式庫)。規格:將文字轉為小寫;把每一段非 ASCII 字母或數字組成的字元替換成單一個 '-';去除開頭與結尾的 '-';結果為空字串時變成 'n…
  • 實作小型有狀態類別 — 撰寫 ratelimiter.py(Python 3,僅使用標準函式庫),包含 class RateLimiter(limit, window_seconds, clock=time.monotonic):依 key 分別計算的滑動視窗限流器。allow(key) 在最近…的呼叫次數少於 limit 時,會傳回 True 並記錄該次呼叫。
  • 修正捨入誤差 — 客戶反映 cart.py 的 cart_total 有時與我們的會計系統差一分錢;會計系統在每個步驟(每行小計、折扣後小計、稅額)都以四捨五入到分。例如 cart_total([(1.005, 1)]) 應傳回 1.09,而…

優點

  • 相對未使用技能,平均分數從 4.0 提升到 5.0(+1.00)。
  • 技能即使未被點名也會被採用:6/6 次實驗組執行都讀取了技能檔案。
  • 比只用一句話寫出同一條規則的版本高 +0.33。
  • 使用技能後的絕對品質很高:5.0 / 5。

缺點

  • Token 用量是未使用技能的 1.65 倍。
  • 測試注意事項:一句話提示組(6 次執行)是後來在另一台機器上追加的,與對照組、實驗組不同機器。
  • 每個任務的每個組別 n=2;約 1 分以內的差異屬於雜訊。

評測

在 Mac 測試機上使用 Codex CLI 0.166(gpt-6-astra,低推理),每次執行都使用全新的暫存專案與暫存的 HOME/TMPDIR/快取;技能已安裝(實驗組)、未安裝(對照組),或未安裝但把技能的核心要求濃縮成一句話附在提示詞後(一句話提示組)。每個任務的每個組別執行 2 次,關閉網路,封鎖 /tmp 寫入。提示詞從未提及技能。評分以程式化方式進行(隱藏測試/植入問題召回率/限制條件檢查/相關時以渲染 + OCR 檢查),每次執行 0–5 分。沒有任何分數是由 LLM 評審決定的。

三組對照

組別平均分數(0-5)每次執行 Token 用量
未使用技能4.065k
一句話提示4.67127k
使用技能5.0108k

以 Codex CLI 執行 18 次,採規則式檢查評分,不經 LLM 評審。總分 24.0 / 30,評級為 推薦。

揭露事項:一句話提示組(6 次執行)是後來才加入的,使用的機器與對照組、實驗組不同。

評定:推薦(24.0 / 30)。 來源:obra/superpowers,skills/test-driven-development,固定於 commit 2ce8aa1。安裝:npx skills@latest add obra/superpowers --skill test-driven-development。授權: MIT(儲存庫 LICENSE)。

這是什麼

obra/superpowers 的 test-driven-development 技能。它的描述寫著「在實作任何功能或修正 bug、尚未開始寫實作程式碼時使用」。約 1,500 字的 Markdown(紅燈-綠燈-重構的「鐵律」、一張跳過測試的藉口表、一份驗證清單),外加一份談如何寫好測試的檔案。沒有腳本。

我們如何測試

在 Mac 測試機上使用 Codex CLI 0.166(gpt-6-astra、低推理),每次執行都使用全新的臨時專案與臨時 HOME/TMPDIR/快取,安裝技能(使用技能組)、不安裝(對照組),或是不安裝但把技能核心要求濃縮成一句話附加在提示詞後(一句話組);每個任務的每組各執行 2 次,關閉網路、封鎖 /tmp 寫入。提示詞完全沒有提到技能名稱。

  1. 依精確規格實作函式 — 在 slug.py 中實作 slugify(text, max_length=None)(Python 3,只用標準函式庫)。規格:把文字轉小寫;把每一段不是 ASCII 字母或數字的字元換成單一 '-';去除開頭與結尾的 '-';結果為空時變成 'n…
  2. 實作小型有狀態類別 — 撰寫 ratelimiter.py(Python 3,只用標準函式庫),包含類別 RateLimiter(limit, window_seconds, clock=time.monotonic):每個 key 一個滑動視窗限流器。若最近…內記錄的呼叫少於 limit 次,allow(key) 會回傳 True 並記錄這次呼叫…
  3. 修正四捨五入 bug — 客戶說 cart.py 中的 cart_total 有時與我們的會計系統差一分錢,會計系統在每一步都採四捨五入到分(每個品項小計、折扣後小計與稅金)。例如 cart_total([(1.005, 1)]) 應該回傳 1.09,而…

評分採程式化方式(隱藏測試/植入問題召回/限制條件檢查/視情況搭配渲染+OCR),每次執行 0–5 分。所有分數都不是由 LLM 評審決定。

結果

給第三組的一句話提示:"遵循嚴格的紅燈-綠燈-重構:先寫一個失敗的測試,看著它因正確的原因失敗,再寫最小實作讓它通過,然後重構;沒有失敗的測試就不寫正式程式碼。"

任務對照組分數一句話提示使用技能對照組 token一句話組 token使用技能 token技能讀取
t1-slugify4.05.05.062k134k108k2/2
t2-ratelimiter4.05.05.063k147k97k2/2
t3-cart-bugfix4.04.05.069k97k116k2/2

平均分數:未使用技能 4.0,使用技能 5.0(差異 +1.0)。每次執行 token:65k 對 108k(1.65x);時間 55 秒對 51 秒。一句話組:4.67(技能相對一句話提示的增益:+0.33;一句話提示相對未使用技能的增益:+0.67);一句話組每次執行 127k token,因此技能成本是一句話提示的 0.85 倍。

實際情況

  • 正確性完全相同:兩組共 12 次執行的隱藏測試都通過(12/12、10/10 與 11/11 項測試)。在這些小任務上,技能不會改變程式碼是否能運作。
  • 它改變的是工作順序。6 次使用技能組執行中,測試檔都在實作之前的步驟寫好;6 次對照組執行中,agent 則把測試與實作寫在同一個修補裡(或者在購物車任務中先修 bug、之後才補測試)。在我們逐行讀過的那次使用技能組執行(slugify)中,agent 先寫測試、跑一次看它失敗,然後才寫程式碼。
  • 它光靠描述就能觸發:技能檔在 6/6 次使用技能組執行中都被讀取,儘管沒有任何提示提到 TDD。
  • 成本更高:每次執行約 1.65 倍 token(107k 對 65k),實際耗時相近(51 秒對 55 秒),因為 agent 要讀技能與其同伴檔案,並把迴路跑好幾次。
  • 一句話版本(嚴格紅燈-綠燈-重構、沒有失敗測試就不寫正式程式碼):平均 4.67,介於對照組(4.0)與技能(5.0)之間,每次執行 127k token,對照組 65k(2.0x),技能 108k(一句話組是技能組的 1.2 倍)。
  • 測試先於實作的次數:對照組 0/6、一句話組 4/6、技能組 6/6;一句話組缺少的兩次都在購物車 bug 修正任務。技能是唯一每次都做到的組別,而且用的指令數(每次 10 個)比一句話組(19 個)少。

面向分數

面向分數原因
1_output_quality5.0

使用技能時平均 5.00/5(程式化檢查,見各任務表)。

2_lift_vs_no_skill3.0

相對未使用技能的 4.00 提升 +1.00 分(每個任務 n=2;約 1 分以下屬雜訊)。

3_trigger5.0使用技能組 6/6 次執行都讀取技能檔案。
4_instructions_portability4
5_efficiency2token 1.65x(每次執行 108k 對 65k)、時間 0.93x(51 秒對 55 秒)。
6_safety_hygiene5

這項測試的限制

  • 兩組的隱藏測試全都通過(天花板),因此測到的提升是流程檢查(測試先於實作),由事件記錄計算而來,而不是成果差異。
  • 任務都是小型單檔 Python 練習;較大的儲存庫、其他語言與測試品質(例如突變測試)都沒有測試。
  • 每組每任務 n = 2:約 1 分以下的差異屬雜訊。
  • 規則式評分只能衡量檢查項目看得到的部分,無法判斷品味,也無法判斷結果是否為最佳。

安全性

請見 security.md。掃描:CRITICAL 0 / HIGH 0 / MEDIUM 0。評定:可安全執行(無網路沙盒)。掃描乾淨並不代表技能安全。

總結

它說到做到:它是唯一在每次執行都先寫失敗測試的組別(6/6,對照組 0/6,一句話版本 4/6),token 是對照組的 1.7 倍、一句話版本的 0.85 倍。在這些小任務上,各組的最終正確性相同,因此增益在於流程,不在程式碼。

完整紀錄

執行日誌:test-driven-development (Codex CLI, network off, per-run temp HOME/TMPDIR, one row per graded run.)
欄位:任務、組-輪次、得分(0-5)、token、秒、通過的檢查、代理開啟的 skill 檔案。

t1-slugify control-1: 得分 4.0, token 62017, 秒 55.5, 檢查 2/3, 未開啟
t1-slugify control-2: 得分 4.0, token 62139, 秒 53.8, 檢查 2/3, 未開啟
t1-slugify oneliner-1: 得分 5.0, token 135535, 秒 52.9, 檢查 3/3, 未開啟
t1-slugify oneliner-2: 得分 5.0, token 134448, 秒 46.7, 檢查 3/3, 未開啟
t1-slugify treatment-1: 得分 5.0, token 118034, 秒 56.3, 檢查 3/3, 開啟 test-driven-development
t1-slugify treatment-2: 得分 5.0, token 97974, 秒 48.3, 檢查 3/3, 開啟 test-driven-development
t2-ratelimiter control-1: 得分 4.0, token 63363, 秒 46.1, 檢查 2/3, 未開啟
t2-ratelimiter control-2: 得分 4.0, token 63431, 秒 98.9, 檢查 2/3, 未開啟
t2-ratelimiter oneliner-1: 得分 5.0, token 140042, 秒 60.8, 檢查 3/3, 未開啟

18 次執行、3 個產出檔案

範例產出

SOTA · 装了 skill:test_slug.py(t1-slugify,第 1 次,Codex)

import unittest from slug import slugify class SlugifyTests(unittest.TestCase): def test_normalizes_text(self): cases = [ ('Hello, World!', 'hello-world'), (' --ABC__123\t\nxyz!! ', 'abc-123-xyz'), ('café déjà vu 12٣', 'caf-d-j-vu'), ('İ X', 'i-x'), ('already-a-slug', 'already-a-slug'), ] for text, expected in cases: with self.subTest(text=text): self.assertEqual(slugify(text), expected) def test_empty_result_uses_fallback(self): for text in ('', '--- !\t', '中文', '😀'): with self.subTest(text=text): self.assertEqual(slugify(text), 'n-a') def test_length_limits_respect_word_boundaries(self): cases = [ ('Hello, World!', 8, 'hello'), ('Hello, World!', 11, 'hello-world'), ('Hello, World!', 20, 'hello-world'), ('Hello, World!', 5, 'hello'), ('Hello, World!', 6, 'hello'), ('one two three', 7, 'one-two'), ('one two three', 9, 'one-two'), ('Supercalifragilistic', 5, 'super'), ('Supercalifragilistic next', 5, 'super'), ('abc', 1, 'a'), ('abc', 0, ''), ('!!!', 1, 'n'), ('!!!', 2, 'n'), ('!!!', 3, 'n-a'), ] for text, limit, expected in cases: with self.subTest(text=text, limit=limit): self.assertEqual(slugify(text, limit), expected) def test_explicit_none_leaves_length_unlimited(self): self.assertEqual(slugify('Hello, World!', None), 'hello-world') if __name__ == '__main__': unittest.main()

檢視 Pro