OpenSpec 項目實戰(七)| verify 2 個工具、3 處修復,第 4 維度依然缺席

作者:術哥無界
日期:2026年6月1日 上午8:30
來源:WeChat 原文

整理版優先睇

速讀 5 個重點 高亮

OpenSpec verify第4維度修復失效,json-formatter工具成功實現,揭示提示詞全鏈路同步嘅侷限

整理版摘要

呢篇文章係術哥嘅OpenSpec項目實戰系列第7篇。作者係術哥,專注AI編程同開源技術,今次分享嘅係verify工具第4維度修復同json-formatter工具實現嘅過程。之前三期改造後,verify嘅Task Granularity維度一直冇出現,今期目標係修復SKILL.md入面嘅矛盾,再驗證效果

修復咗Summary Scorecard模板加返Task Granularity行,同埋Graceful Degradation段改three做four,但係verify報告依然只輸出3維度。另一方面,json-formatter成功實現,複用咗text-summary嘅組件模式,速度明顯快咗。總結係提示詞全鏈路同步係必要,但唔足夠,質量保證需要更強嘅過程控制,後續會引入三檔工作流。

  • 修復SKILL.md全鏈路後verify第4維度依然缺席,提示詞約束有天花板。
  • 本期採用先修SKILL.md再行ProposeApplyVerify流程,驗證修復效果。
  • json-formatter開發速度比text-summary快,因為組件模式可直接複用。
  • 質量保證不能只壓喺verify一個環節,需按風險選強度。
  • 後續每個新工具模板應固定export default,避免動態加載失敗。
結構示例

內容片段

內容片段 text
Create a report structure with four dimensions:
- **Completeness**: Track tasks and spec coverage- **Correctness**: Track requirement implementation and scenario coverage- **Coherence**: Track design adherence and pattern consistency- **Task Granularity**: Track task format compliance, TDD adherence, code completeness, and granularity assessment
整理重點

verify SKILL.md修復:定義同引用嘅矛盾

上期改造加咗第4維度Task Granularity,但係verify報告從來冇出現過。翻查SKILL.md發現:定義部分(L46)寫咗four dimensions,但係後面兩處引用仲停留喺three dimensions。一處係Summary Scorecard模板,只有3行;另一處係Graceful Degradation段嘅「verify all three dimensions」。AI執行verify時讀到舊文案,直接按3維度出報告。

定義同引用不一致,AI唔覺得有問題,照跟舊文案行。

改動好細Summary Scorecard加一行Task GranularityGraceful Degradation改three做four。但係呢個同步不到位正正係第4維度缺席嘅直接原因。

  1. 1 矛盾點1Summary Scorecard模板(L117-127)只有3行,缺Task Granularity行。
  2. 2 矛盾點2Graceful Degradation段(L159-164)寫「verify all three dimensions」,剩係改定義冇用。
整理重點

json-formatter工具實現:組件模式複用

json-formatter嘅catalog同路由第2期就配好咗,但係src/modules/json-formatter/一直空咗。今期將佢填返,實際係個純前端工具,用原生JSON.parseJSON.stringify實現格式化、壓縮同語法校驗。

複用text-summary嘅組件模式:頁面組件負責UI,純函數內聯喺組件入面。

  • 格式化:接收JSON字符串,輸出縮排整齊嘅JSON
  • 壓縮:移除多餘空白,輸出單行JSON
  • 校驗:檢測語法錯誤,返回錯誤位置同描述。

apply過程行TDD五步結構,AI按tasks.md順序執行,交互好少。相比text-summary,json-formatter嘅實現速度快好多,因為核心邏輯簡單,而且組件模式可以直接套用。

不過踩咗個坑:json-formatter用咗named export(export function JsonFormatterPage),但router嘅動態import取mod.default,搞到頁面卡死喺加載中。修復方法係加返export default JsonFormatterPage

後續每個新工具模板應該將export default做固定項寫死。

整理重點

驗證結果與後續方向

執行/opsx:verify之後,報告Summary表格仍然只有3行——CompletenessCorrectness、Coherence,Task Granularity維度冇出現。修復SKILL.md冇生效,AI似乎冇跟足新模板。

SKILL.md文案修改嘅約束力有天花板,AI嘅執行行為受上下文慣性影響。

不過verify報告發揮咗其他價值:發現咗1個CRITICAL issue(大文件JSON解析場景未實現)同1個WARNING(有效JSON校驗測試缺失)。呢啲係specs寫咗但tasks冇覆蓋嘅,verify幫手撈返出嚟。

  • 快速檔:低風險改動(文案、樣式),OpenSpec + fresh verify就夠。
  • 標準檔:普通工具開發,OpenSpec + Superpowers writing-plans控制計劃顆粒度。
  • 嚴格檔:核心邏輯、bugfix、重構,疊加TDD、debug、code review。

呢個係系列從「摸索OpenSpec增強方案」轉向「組合使用OpenSpec + Superpowers」嘅轉折點。

🚩 2026 年「術哥無界」系列實戰文檔 X 篇原創計劃 第 127 篇,OpenSpec 項目實戰「2026」系列第 7 篇

大家好,歡迎嚟到 術哥無界 | ShugeX | 運維有術

我是術哥,一個專注於 AI 編程、AI 智能體、Agent Skills、MCP、雲原生、AIOps、Milvus 向量數據庫嘅技術實踐者同開源佈道者

Talk is cheap, let's explore。無界探索,有術而行。

信息圖封面:OpenSpec verify 修復 + json-formatter 實現
資訊圖封面:OpenSpec verify 修復 + json-formatter 實現

圖:資訊圖封面:OpenSpec verify 修復 + json-formatter 實現

上一期做咗三層改造——template 改咗 tasks 格式、review 加咗 4 維度審查同拆分方向建議、verify 加咗第 4 維度 Task Granularity。三層改造嘅驗證結果:template 生效咗,review 生效咗,verify 冇生效。

verify 卡喺邊度?執行 /opsx:verify 之後,報告只輸出咗 Completeness、Correctness、Coherence 三個維度,Task Granularity 始終缺席。第 6 期加嗰個第 4 維度嘅定義,AI 完全冇用過。

翻咗一遍 SKILL.md 源文件,根因揾到咗:L46 定義部分確實寫咗 four dimensions,但後面兩處引用呢個數字嘅地方仲停留喺 three dimensions。一處係 Summary Scorecard 模板表格,得 3 行冇 Task Granularity。另一處係 Graceful Degradation 段嘅最後一行,明確寫住 "verify all three dimensions"。AI 執行 verify 時,讀到呢兩處舊文案,直接按 3 維度出咗報告。定義同引用不一致,AI 唔覺得有問題,跟住舊文案行就係喇。

講到底,呢個係典型嘅"改咗定義冇改引用"嘅問題。寫代碼嘅時候我哋都知道改咗接口要改曬所有調用方,但改提示詞嘅時候容易忽略同樣嘅道理。

今期做兩件事:先將 verify SKILL.md 嘅內部矛盾修完,然後實現第二個工具 json-formatter。json-formatter 嘅 catalog 註冊同路由第 2 期就配好咗(catalog.ts 裏面嘅 stage: 'active',路由 /tools/json-formatter),但 src/modules/json-formatter/ 目錄一直空咗。今期將佢填上,同時驗證修復後嘅 verify 係咪真係可以輸出 4 維度報告。

兩個任務有先後順序:先修 SKILL.md,再行 Propose → Apply → Verify。咁樣今期 verify 階段就可以用返修復後嘅配置,直接檢驗效果。

完整流程如下:工作流總覽

說明:本文內容係基於 OpenSpec v1.3.1 實際項目操作記錄整理。verify SKILL.md 修復同 json-formatter 工具實現都在 shuge-ai-toolbox 項目中實際驗證,但 verify 第 4 維度(Task Granularity)修復後仍然未生效,文中如實記錄。文中嘅配置修改同代碼僅供參考,實際效果請以你嘅項目環境測試結果為準。如果有實際使用經驗,歡迎喺評論區分享交流。

修復 verify SKILL.md:將矛盾嘅地方同步改完

先定位問題。文件路徑係 .claude/skills/openspec-verify-change/SKILL.md,總共 173 行。

L46 嘅定義部分其實係啱嘅:

Create a report structure with four dimensions:
**Completeness**: Track tasks and spec coverage
**Correctness**: Track requirement implementation and scenario coverage
**Coherence**: Track design adherence and pattern consistency
**Task Granularity**: Track task format compliance, TDD adherence, code completeness, and granularity assessment

四個維度定義齊全,L50-54 仲詳細拆咗 Task Granularity 嘅四項檢查:任務格式、TDD 合規、代碼完整性、粒度評估。定義冇問題。

問題出喺後面兩處引用呢個數字嘅地方。

矛盾點 1:Summary Scorecard 模板(L117-127)

呢個表格係 verify 報告末尾嘅彙總模板,AI 按呢個格式輸出最終結論。目前得 3 行:

### Summary
| Dimension    | Status           |
|--------------|------------------|
| Completeness | X/Y tasks, N reqs|
| Correctness  | M/N reqs covered |
| Coherence    | Followed/Issues  |

Task Granularity 行缺失。AI 生成報告時跟住模板行,模板得 3 行,輸出自然只得 3 維度。

矛盾點 2:Graceful Degradation 段(L159-164)

呢段描述嘅係"當工件唔齊全時點樣降級驗證"。最後一句:

If full artifacts: verify all three dimensions

寫嘅係 three dimensions。就算所有工件都齊全,AI 讀到呢句都會認為只需要驗證 3 個維度。

SKILL.md 三處矛盾位置標註
SKILL.md 三處矛盾位置標註

圖:SKILL.md 三處矛盾位置標註

具體修改

改動唔大,但位置分散,需要逐個同步。

修改 1:Summary Scorecard 加行

改前:

| Dimension    | Status           |
|--------------|------------------|
| Completeness | X/Y tasks, N reqs|
| Correctness  | M/N reqs covered |
| Coherence    | Followed/Issues  |

改後:

| Dimension        | Status           |
|------------------|------------------|
| Completeness     | X/Y tasks, N reqs|
| Correctness      | M/N reqs covered |
| Coherence        | Followed/Issues  |
| Task Granularity | Format/TDD/Code  |

新增嘅 Task Granularity 行,狀態列用 Format/TDD/Code 簡寫,對應任務格式檢查、TDD 合規檢查、代碼完整性檢查三項。

修改 2:Graceful Degradation three → four

改前:

If full artifacts: verify all three dimensions

改後:

If full artifacts: verify all four dimensions

一個詞嘅事。但呢個詞決定了 AI 驗證時係咪行第 4 維度。

SKILL.md 修復前後對比
SKILL.md 修復前後對比

圖:SKILL.md 修復前後對比

改完存盤,總共改咗兩處三行。睇落微不足道,但呢個同步唔到位就係 verify 第 4 維度一直缺席嘅直接原因。

呢度有個教訓值得記一筆:提示詞約束係一條鏈路,唔係孤立嘅定義。L46 定義寫得幾靚都好,後面模板同退化邏輯冇跟上,執行層就跟舊路徑行。2/8 法則嘅體現——定義佔 20% 篇幅但決定方向,引用點佔 80% 篇幅而且決定實際行為。改提示詞要改全鏈路,唔可以只改最顯眼嗰一處。

改完 verify SKILL.md 之後,馬上進入 Propose。咁樣今期生成嘅工件先至可以用返修復後嘅配置。

Propose:記錄工件產出過程

今期跳過 Explore,需求好明確——實現 json-formatter 工具,重用 text-summary 已建立嘅組件模式。

執行 /opsx:propose

**AI**:What change do you want to work on? Describe what you want to build or fix.

**用戶**:本期做一個 implement-json-formatter 變更。在 src/modules/json-formatter/ 下實現 JSON 格式化工具,複用 text-summary 已建立的組件模式。功能包括格式化、壓縮、語法校驗。catalog.ts 中 json-formatter 已註冊為 active,路由已存在,但模塊目錄尚未創建。同時需要在 router/index.tsx 的 toolModules 中添加 json-formatter 的映射行。

**AI**
**Change created: `implement-json-formatter`**
**Location:**`openspec/changes/implement-json-formatter/`

**Artifacts created:**
| Artifact | Description |
|----------|-------------|
`proposal.md` | Why we need JSON formatting (dev高頻需求,純前端實現) |
`design.md` | Technical design: single-file component, native JSON.parse/JSON.stringify, useEffect+import pattern |
`specs/json-formatter/spec.md` | 8 scenarios covering format/minify/validate, empty input, large file |
`review.md` | 4-dimension review with task split direction |
`tasks.md` | 3 tasks: JsonFormatterPage (TDD), router mapping, verification |

**Task breakdown:**
任務 1: JsonFormatterPage 組件(TDD 模式,測試 + 實現)
任務 2: Router 映射配置(toolModules 添加 json-formatter)
任務 3: 驗證完整功能

**All artifacts created! Ready for implementation.**
Run `/opsx:apply implement-json-formatter` to start implementing.

AI 一次生成咗 5 個工件。按實際產出順序記錄。

proposal.md

變更內容:
新增 `src/modules/json-formatter/` 目錄,包含頁面組件、格式化邏輯和測試
修改 `router/index.tsx`,在 toolModules 中添加 json-formatter 映射行

能力清單:
新增能力:json-format(JSON 格式化 / 壓縮 / 校驗)

影響範圍:路由加載、src/modules/ 目錄結構

proposal 簡潔,一頁紙,說明咗"點解做"同"改咩範圍"。

specs

能力清單只得一個 json-format,所以只生成一個 spec 文件。需求定義咗三個核心行為:

  • 格式化:接收 JSON 字符串,輸出縮進整齊嘅 JSON。Scenario 覆蓋咗簡單物件、巢狀物件、陣列三種輸入
  • 壓縮:移除多餘空白,輸出單行 JSON。Scenario 包含已有縮進嘅 JSON 同帶註釋嘅非法 JSON
  • 校驗:檢測語法錯誤,返回錯誤位置同描述。Scenario 覆蓋咗缺少引號、缺少逗號、多餘嘅尾部逗號、未閉合嘅花括號等常見錯誤

每個需求至少有一個正常輸入 Scenario 同一個異常輸入 Scenario。Spec 嘅質量比 text-summary 嗰期好啲——text-summary 當時得一個"短/中/長"嘅長度選項需求,json-formatter 嘅三個需求方向更清晰,邊界條件都寫得更具體。

design.md

技術決策部分記錄咗幾個要點:

  • 純前端實現,唔依賴外部 API——json-formatter 係一個本地工具,唔需要調用任何後端服務
  • 重用 text-summary 嘅組件模式:頁面組件負責 UI,純函數文件負責邏輯——呢個模式喺 text-summary 上驗證過,直接沿用
  • 使用原生 JSON.parse + JSON.stringify 實現格式化同壓縮,唔引入第三方庫——減小打包體積,同時 JSON.parse 自帶嘅錯誤信息足夠定位語法錯誤位置

仲有一個值得注意嘅決策:校驗功能唔單獨用 try-catch 包一層,而係重用格式化函數嘅錯誤處理。因為 JSON.parse 喺解析失敗時已經會拋出包含位置信息嘅 SyntaxError,冇必要重複實現。呢個決策後來喺 tasks.md 裏面體現為 formatJSON 同 validateJSON 共用同一個 parse 調用。

不過 design.md 仲做咗一個後來影響實現結構嘅決策:single-file component 模式。佢冇好似 text-summary 咁將純函數拆到獨立文件,而係選擇將格式化、壓縮、校驗邏輯直接內聯喺頁面組件中。呢個決策直接決定了 apply 後嘅文件數量——比預期少。

review.md

今期繼續用第 6 期改造嘅 4 維度 review instruction。睇下拆分方向建議有冇參考價值。

4 個維度嘅審查結果:

1. **邊界條件** ✅ 通過:空字符串、null 輸入、非法 JSON、超長字符串均有場景覆蓋
2. **回滾方案** ⚠️ 警告:純前端新增模塊,回滾即刪除目錄,風險可控但 proposal 未顯式說明回滾步驟
3. **測試覆蓋** ✅ 通過:format/compress/validate 三個方向各有獨立測試場景
4. **向後兼容** ✅ 通過:不影響 text-summary 的接口和路由

拆分方向建議:

建議按邏輯層 → 頁面層 → 路由層的順序拆分任務。三個純函數(formatJSON / compressJSON / validateJSON)優先編寫並測試,確保核心邏輯正確後再接入頁面組件。路由映射放在最後一個任務。

同 text-summary 嘅 tasks 拆分思路一致——先純函數、再組件、最後路由。說明 review instruction 嘅拆分方向引導係穩定輸出,唔係偶然碰運氣。

tasks.md

對比 text-summary 嘅 tasks,格式完全一致:每個任務都用 ### 任務 N 標題,附文件列表同 TDD 五步結構(寫失敗測試 → 確認失敗 → 寫最小實現 → 確認通過 → 提交)。

但任務數量比預想嘅少。tasks.md 得 1 個核心任務——JsonFormatterPage 組件整體,唔係 formatJSON / compressJSON / validateJSON 三個獨立嘅純函數任務。呢個係 design.md "單文件組件"決策嘅直接結果:邏輯內聯喺組件中,冇必要拆成獨立嘅純函數 TDD 步驟。

工件自洽性檢查

按照每期嘅慣例,檢查 5 個工件之間係咪存在矛盾:

  • proposal.md 話"新增 json-format 能力",specs 目錄下確實得一個 specs/json-formatter/spec.md,對應一致
  • design.md 話"使用原生 JSON.parse + JSON.stringify",冇提到任何第三方依賴,同 proposal 嘅"純前端實現"一致
  • review.md 俾出嘅拆分方向係"邏輯層 → 頁面層 → 路由層",但 tasks.md 實際得組件整體 + 路由映射兩個任務,review 嘅建議冇被完全採納
  • tasks.md 中嘅文件路徑都指向 src/modules/json-formatter/,冇引用唔存在嘅模塊

5 個工件全部生成完畢,進入 Apply。

Apply:記錄執行過程

執行 /opsx:apply

**AI**:Test failed as expected (Module not found). Now writing the implementation.

**AI**:One test is failing because `getByText` normalizes whitespace. Let me fix the test to use a regex matcher:

**AI**
## Implementation Complete
**Change:** implement-json-formatter
**Schema:** with-review
**Progress:** 11/11 tasks complete ✓

| Task | Description | Status |
|------|-------------|--------|
| 1.1 | 寫失敗測試 | ✓ |
| 1.2 | 運行測試——確認失敗 | ✓ |
| 1.3 | 寫最小實現 | ✓ |
| 1.4 | 運行測試——確認通過 | ✓ |
| 1.5 | 提交 | ✓ |
| 2.1 | 添加 json-formatter 映射 | ✓ |
| 2.2 | 驗證 router 測試 | ✓ |
| 2.3 | 提交 | ✓ |
| 3.1 | 運行所有測試 | ✓ |
| 3.2 | 驗證路由可達 | ✓ |
| 3.3 | 最終提交 | ✓ |

Commits:
`d3ff798` feat(json-formatter): implement JsonFormatterPage component
`59c13cd` feat(router): add json-formatter tool module mapping
`e26c616` feat: implement json-formatter tool page

Files Created/Modified:
`src/modules/json-formatter/index.tsx` (component)
`src/modules/json-formatter/index.test.tsx` (8 tests)
`src/router/index.tsx` (added mapping)

All tasks complete! You can archive this change with `/opsx:archive`.

AI 按 tasks.md 嘅順序執行。tasks.md 得 1 個核心任務——JsonFormatterPage 組件整體。TDD 流程係:寫組件嘅失敗測試(渲染標題、輸入區、按鈕交互),確認模塊揾唔到報錯之後,再寫組件實現。唔似 text-summary 嗰期有獨立嘅純函數 TDD 步驟,今期整個組件(含內聯嘅格式化、壓縮、校驗邏輯)喺一個任務裏面完成咗。

整個過程 AI 同用戶之間嘅交互唔多,因為需求明確、tasks 拆分清晰,AI 基本一路執行落嚟冇卡殼。唔似第 4 期佈局改造嗰次,AI 來回問咗好幾輪"Tab 切換行為係咩"、"滾動狀態點處理"。今期呢種"跟住 tasks 一步步寫"嘅場景,OpenSpec 嘅 TDD 五步結構確實減少咗交互輪次。

Apply 執行流程:TDD 分步提交
Apply 執行流程:TDD 分步提交

圖:Apply 執行流程:TDD 分步提交

文件核實

Apply 完成之後,核實文件係咪真係創建成功。

ls src/modules/json-formatter/

實際得 2 個文件,唔係預期嘅 4 個:

index.tsx              # 頁面組件(含格式化、壓縮、校驗邏輯)
index.test.tsx         # 8 個測試用例

對比 text-summary 嘅 8 個文件(4 源文件 + 4 測試),json-formatter 比 text-summary 更簡潔——冇獨立嘅邏輯文件,格式化、壓縮、校驗三個函數直接寫喺 index.tsx 嘅組件中。每個函數嘅核心就係 JSON.parse + JSON.stringify 嘅唔同參數組合,唔需要拆文件。

呢個就係 design.md "single-file component"決策嘅直接體現。text-summary 有 extract-summary.ts、text-utils.ts、stopwords.ts 三個輔助文件係因為邏輯確實複雜(詞頻統計、句子評分、停用詞),json-formatter 嘅三個功能加埋都係得十幾行代碼,拆文件反而多餘。

路由映射檢查

呢個係每期必查嘅環節。上一期 text-summary 都喺呢度手動加過映射。AI 執行 tasks 時可能跳過路由映射呢步——tasks.md 寫咗呢步,但 AI 容易忽略文件列表之外嘅操作。

讀取 router/index.tsx,確認 toolModules 部分:

const toolModules: Record<string() => Promise<{ default: React.ComponentType<unknown> }>> = {
  'text-summary'() => import('/src/modules/text-summary/index.tsx'),
  'json-formatter'() => import('/src/modules/json-formatter/index.tsx'),
};

json-formatter 行已經喺度。今次 AI 冇漏。不過呢個檢查唔可以省——上一期 text-summary 嘅 review 過程中,我就發現過 AI 標記咗 [x] 但實際文件冇改嘅情況。手動核實係每期 apply 後嘅固定動作,唔可以信 AI 嘅完成報告。

實現體驗對比

對比 text-summary,json-formatter 嘅實現過程有明顯差異:

  • 組件模式重用度高:頁面組件 index.tsx 基本套用 text-summary 嘅 textarea + 操作控件 + 結果展示三段式佈局,只係將"生成摘要"按鈕換成"格式化/壓縮/校驗"三個按鈕
  • 純函數邏輯更簡單:text-summary 嘅 extract-summary.ts 涉及詞頻統計、句子評分、位置權重,json-formatter 嘅內聯邏輯本質係 JSON.parse + JSON.stringify 嘅三種參數組合
  • 測試更好寫:text-summary 要模擬中文分詞場景,json-formatter 輸入輸出都係字符串,測試用例更直觀

呢個就係建立組件模式嘅價值:第一個工具花時間摸索模式,第二個工具直接套用。如果後續第三個、第四個工具繼續行呢條路徑,實現速度會越來越快。

文件清單確認完畢,進入今期重點——Verify。

Verify:記錄驗證過程(今期重點)

今期 Verify 係核心驗證點。修咗 SKILL.md 嘅兩處矛盾之後,verify 報告係咪首次出現 4 個維度?

執行 /opsx:verify

verify 報告

AI 實際輸出嘅 verify 報告:

## Verification Report: implement-json-formatter

### Summary
| Dimension    | Status              |
|--------------|---------------------|
| Completeness | 11/11 tasks, 7/8 scenarios |
| Correctness  | Requirement implemented, 1 scenario uncovered |
| Coherence    | Design followed, pattern consistent |

---

### Issues by Priority

#### CRITICAL (Must fix before archive)
**Scenario not implemented: 大文件 JSON 解析**
**Spec requirement**: "用戶輸入一個非常大的 JSON(超過 1MB)" → "錯誤提示區顯示'JSON 數據過大,請控制在中。'"
**Current state**: No size check in implementation
**Recommendation**: Add size check in `src/modules/json-formatter/index.tsx`:
```typescript
  const MAX_SIZE = 1024 * 1024; // 1MB
  if (input.length > MAX_SIZE) {
    setError('JSON 數據過大,請控制在中。');
    setOutput('');
    return;
  }
```
  Add corresponding test in `src/modules/json-formatter/index.test.tsx`

#### WARNING (Should fix)
**Scenario not tested: 用戶輸入有效 JSON 並點擊語法校驗按鈕**
**Spec requirement**: "錯誤提示區顯示'JSON 語法正確',結果展示區顯示原始 JSON"
**Current state**: Test for valid JSON validation is missing

#### SUGGESTION (Nice to fix)
**Test file uses getAllByRole for output textarea**
**Current**`screen.getAllByRole('textbox')[1]` relies on order
**Alternative**: Add `aria-label` to output textarea

### Final Assessment

**1 critical issue found. Fix before archiving.**

Summary 表格得 3 行。Task Granularity 維度冇出現——同上一期完全一樣,修復 SKILL.md 冇生效。呢個對應草稿預測嘅"情況 B"。

不過 verify 報告本身嘅質量比上期好唔少。佢發現咗 1 個 CRITICAL issue:spec 裏面定義咗"大文件 JSON 解析"場景(超過 1MB 嘅 JSON 要提示用戶),但實現中冇做 size check。仲發現咗 1 個 WARNING:有效 JSON 點擊語法校驗按鈕嘅測試用例缺失。呢啲發現係 specs 裏面寫咗但 tasks.md 冇覆蓋到嘅場景,verify 將佢哋執返出嚟。

verify 報告摘要:3 維度 + 缺失標註
verify 報告摘要:3 維度 + 缺失標註

圖:verify 報告摘要:3 維度 + 缺失標註

build 驗證

npm run build

npm run build(實際執行 tsc -b && vite build)因 tsc 類型錯誤失敗。報咗 4 個錯誤,其中 2 個係 router 動態 import 路徑嘅既有問題(從第 2 期就存在),1 個係測試文件裏面 vi 冇用到但 import 咗。呢啲都係類型層面嘅問題,唔影響運行。

跳過 tsc 直接跑 npx vite build,構建成功:

vite v8.0.12 building client environment for production...
✓ 1757 modules transformed.
rendering chunks...
computing gzip size...
dist/index.html                           0.46 kB │ gzip:  0.30 kB
dist/assets/index-8W2XdHU1.css           20.28 kB │ gzip:  4.75 kB
dist/assets/json-formatter-DwIBtDQv.js    2.63 kB │ gzip:  0.88 kB
dist/assets/text-summary-C_blhUgt.js      2.84 kB │ gzip:  1.39 kB
dist/assets/index-BtN7sydP.js           295.12 kB │ gzip: 94.23 kB

✓ built in 197ms

json-formatter 同 text-summary 各自獨立打包為 chunk(json-formatter-DwIBtDQv.js 和 text-summary-C_blhUgt.js),動態路由嘅 code splitting 生效咗。json-formatter 得 2.63 kB(gzip 後 0.88 kB),體積控制得唔錯。

瀏覽器檢查:先踩咗一個坑

啓動開發服務器,訪問 /tools/json-formatter——頁面停喺加載中...唔鬱了。text-summary 頁面正常。同一個 router 邏輯,一個可以加載一個唔得,問題肯定出喺 json-formatter 模塊本身。

仔細對比兩個模塊嘅代碼,發現根因:json-formatter 嘅 index.tsx 只用咗 named export(export function JsonFormatterPage()),而 router 嘅 ToolPage 組件取嘅係 mod.default。text-summary 有 export default TextSummaryPage,json-formatter 漏咗。

俾 AI 報咗呢個問題,佢自己揾到原因加咗 export default JsonFormatterPage;

修復後單獨提交咗一個 commit:

4b8e1d4 fix(json-formatter): add default export for lazy loading

呢個 bug 嘅教訓係:組件模式重用時,export 方式都要對齊。named export 同 default export 喺動態 import 時行為唔同,少寫一行 export default 就會導致整個頁面加載失敗。後續每個新工具嘅模板裏面應該將 export default 作為固定項寫死,避免再踩呢個坑。

瀏覽器檢查:修復後嘅功能驗證

修復 export default 後重新訪問 /tools/json-formatter,頁面正常加載:

json-formatter 頁面
json-formatter 頁面

圖 1:json-formatter 頁面修復後正常加載,顯示標題、輸入區同三個操作按鈕

頁面佈局同 text-summary 類似:

  • 標題區:JSON 格式化
  • 輸入區:textarea,placeholder 提示輸入 JSON
  • 操作區:格式化、壓縮、校驗三個按鈕
  • 輸出區:textarea 展示結果

輸入一段壓縮嘅 JSON 測試格式化功能:

{"name":"shuge-ai-toolbox","version":"1.0.0","dependencies":{"react":"^19.0.0","react-dom":"^19.0.0"}}

點擊格式化按鈕,輸出帶縮進嘅版本:

格式化測試
格式化測試

圖 2:輸入壓縮 JSON 後點擊"格式化",輸出帶縮進嘅可讀版本

再測試校驗功能——輸入一段唔合法嘅 JSON:

{"name""test""age":}
校驗測試
校驗測試

圖 3:輸入無效 JSON 後點擊"校驗",顯示語法錯誤提示

校驗功能正常,返回咗語法錯誤嘅提示信息。瀏覽器檢查通過。

Archive:歸檔

確認 verify 通過後執行歸檔:

## Archive Complete
**Change:** implement-json-formatter
**Schema:** with-review
**Archived to:** `openspec/changes/archive/2026-05-31-implement-json-formatter/`
**Specs:** No delta specs (json-formatter is a new capability, not yet in main specs)
All artifacts complete. All tasks complete (11/11).
**Note:** The verification report found a missing "large file" scenario check (>1MB), but since you chose to archive without addressing it, the spec remains in the archive for future reference.

注意最後嗰句 Note:AI 提醒 verify 報告發現嘅大文件場景(>1MB)未修復就歸檔咗。呢個說明 verify 確實起到咗"檢查遺漏"嘅作用,雖然 Task Granularity 維度冇生效,但其他三個維度(特別是 Correctness)將 spec 裏面寫咗但實現冇覆蓋嘅場景執返出嚟。

驗證歸檔結果:

ls openspec/changes/

活躍變更目錄應該係空嘅(或者剩低其他未完成嘅 change),implement-json-formatter/ 已移到 openspec/archive/ 下。

Git 推送:提交到 GitHub

AI 按 TDD 步驟分 3 個獨立 commit 提交,加上後續修復 export default 嘅 1 個 commit,共 4 個 commit:

4b8e1d4 fix(json-formatter): add default export for lazy loading
e26c616 feat: implement json-formatter tool page
59c13cd feat(router): add json-formatter tool module mapping
d3ff798 feat(json-formatter): implement JsonFormatterPage component

呢個唔係一個大合併 commit,而係按 TDD 節奏嘅增量提交:組件實現 → 路由映射 → 最終整合 → bug 修復。每個 commit 粒度細、職責單一,符合 TDD 分步提交嘅模式。

SKILL.md 嘅修復冇納入 git commit——因為佢係喺項目外嘅 .claude/skills/ 目錄下執行嘅,唔屬於項目倉庫嘅變更範圍。

**AI**:已推送到遠端。3 個 commit 從 `d387013` 到 `e26c616`

回顧:今期學到咗啲乜

verify 修復冇生效

改咗 SKILL.md 三處(定義本身已正確、Summary Scorecard 加行、Graceful Degradation 改 three→four),全鏈路同步咗,但 verify 報告仍然只輸出 3 維度。AI 唔係冇讀到修改——佢確實加載咗 SKILL.md——但輸出時冇嚴格按模板逐項填充,而係按自己嘅理解行咗老路徑。

呢個說明單靠改提示詞嘅約束力有天花板:改全鏈路係必要條件,但唔係充分條件。就算定義、模板、退化邏輯全部統一咗,AI 喺生成報告時仍然可能跳過模板,直接按慣性輸出。

有個結論係確定嘅:提示詞嘅全鏈路同步比單點增強重要得多。L46 嘅定義寫得幾靚都好,後面模板同退化邏輯冇跟上,執行層就跟舊路徑行。呢個係 2/8 法則嘅又一次驗證。但同時要承認,全鏈路同步都唔可以保證 AI 一定按模板行——呢個係提示詞約束嘅固有侷限。

verify 報告雖然冇出現第 4 維度,但喺其他方面發揮咗價值:佢發現咗大文件 JSON 解析場景嘅遺漏同有效 JSON 校驗測試嘅缺失。呢啲係 specs 裏面寫咗但實現冇覆蓋嘅,verify 將佢哋執返出嚟。

第二個工具嘅實現體驗

json-formatter 嘅實現速度明顯比 text-summary 快。核心原因:

  • 組件模式可以直接重用,唔使重新摸索
  • JSON 格式化嘅核心邏輯比文本摘要嘅詞頻統計簡單得多
  • 測試用例更好寫——輸入輸出都係字符串,唔需要模擬中文分詞

第一個工具花時間建立模式,第二個工具享受模式紅利。如果後續繼續按呢個節奏迭代,每加一個工具嘅邊際成本會越來越低。

export default 呢個坑

json-formatter 實現過程中踩嘅 export default 坑值得單獨記一筆。text-summary 有 export default,json-formatter 冇,頁面直接卡死喺"加載中..."。呢個唔係複雜邏輯 bug,而係一行代碼嘅遺漏,但排查花咗唔短嘅時間——因為第一反應係"路由配置有問題",而唔係"export 方式唔啱"。

後續每個新工具嘅模板應該將 export default 作為固定項寫死,同 import React 一樣成為模板嘅必選項。

路由加載策略嘅侷限

toolModules 手動映射每加一個工具改一次 router/index.tsx,兩期落嚟已經改咗兩次(text-summary 同 json-formatter)。目前得兩個 active 工具還好,等工具數量上去咗呢個就係維護負擔。但今期唔改呢個,屬於"用得到但唔夠優雅"嘅範疇,留俾後續專門嘅重構任務。

理想嘅做法係將 toolModules 改成動態掃描 src/modules/ 目錄,或者喺 catalog.ts 裏面加一個 modulePath 字段,令路由映射自動生成。不過呢種重構屬於"改善型"需求,當前階段優先級唔高——先將工具做出嚟先講。

關於 verify 嘅邊界思考

如果今期 verify 4 維度仍然冇生效,咁就說明純 SKILL.md 文案修改嘅約束力有天花板。AI 嘅執行行為受上下文慣性影響——佢可能讀到新嘅模板但仍然按舊習慣輸出。單靠提示詞約束,冇辦法保證 AI 嚴格跟隨每一條規則。呢個發現本身有價值:佢說明質量保證唔可以只壓喺 verify 呢一個環節上,需要引入更強嘅過程控制。

下一期會詳細討論呢個問題,以及點解後續唔再死磕 OpenSpec verify 嘅增強,而係引入 Superpowers 嘅計劃紀律嚟補充。

預告

第 8 期暫時唔寫代碼,覆盤第 5-7 期三層改造嘅實驗結果:template 改造嘅投入產出比、review 拆分方向建議嘅實際價值、verify SKILL.md 修復嘅最終效果。然後引入三檔工作流嘅概念:

  • 快速檔:低風險改動(文案、樣式),OpenSpec + fresh verify 就夠曬
  • 標準檔:普通工具開發,OpenSpec + Superpowers writing-plans 控制計劃顆粒度
  • 嚴格檔:核心邏輯、bugfix、重構,疊加 TDD、debug、code review

按風險選強度,唔再一刀切。呢個都係呢個系列從"摸索 OpenSpec 增強方案"轉向"組合使用 OpenSpec + Superpowers"嘅轉折點。

聲明:本文基於 OpenSpec v1.3.1 源碼分析、實際項目操作記錄同讀者反饋整理。所有改造都在 shuge-ai-toolbox 項目中實際驗證。配置同代碼僅供參考,請以實際環境測試為準。

項目倉庫:https://github.com/shuge-x/shuge-ai-toolbox

好啦,多謝你睇我嘅文章,如果鍾意可以點讚轉發俾需要嘅朋友,我哋下一期再見!敬請期待!

🚩 2026 年「術哥無界」系列實戰文檔 X 篇原創計劃 第 127 篇,OpenSpec 項目實戰「2026」系列第 7 篇

大家好,歡迎來到 術哥無界 | ShugeX | 運維有術

我是術哥,一名專注於 AI 編程、AI 智能體、Agent Skills、MCP、雲原生、AIOps、Milvus 向量數據庫的技術實踐者與開源佈道者

Talk is cheap, let's explore。無界探索,有術而行。

信息圖封面:OpenSpec verify 修復 + json-formatter 實現
信息圖封面:OpenSpec verify 修復 + json-formatter 實現

圖:信息圖封面:OpenSpec verify 修復 + json-formatter 實現

上一期做了三層改造——template 改了 tasks 格式、review 加了 4 維度審查和拆分方向建議、verify 加了第 4 維度 Task Granularity。三層改造的驗證結果:template 生效了,review 生效了,verify 沒生效。

verify 卡在哪?執行 /opsx:verify 後,報告只輸出了 Completeness、Correctness、Coherence 三個維度,Task Granularity 始終缺席。第 6 期加的那個第 4 維度的定義,AI 壓根沒用上。

翻了一遍 SKILL.md 源文件,根因找到了:L46 定義部分確實寫了 four dimensions,但後面兩處引用這個數字的地方還停留在 three dimensions。一處是 Summary Scorecard 模板表格,只有 3 行沒有 Task Granularity。另一處是 Graceful Degradation 段的最後一行,明確寫着 "verify all three dimensions"。AI 執行 verify 時,讀到這兩處舊文案,直接按 3 維度出了報告。定義和引用不一致,AI 不覺得有問題,照着舊文案走就是了。

說到底,這是個典型的"改了定義沒改引用"的問題。寫代碼的時候我們都知道改了接口要改所有調用方,但改提示詞的時候容易忽略同樣的道理。

本期做兩件事:先把 verify SKILL.md 的內部矛盾修完,然後實現第二個工具 json-formatter。json-formatter 的 catalog 註冊和路由第 2 期就配好了(catalog.ts 裏的 stage: 'active',路由 /tools/json-formatter),但 src/modules/json-formatter/ 目錄一直空着。這期把它填上,同時驗證修復後的 verify 是否真的能輸出 4 維度報告。

兩個任務有先後順序:先修 SKILL.md,再走 Propose → Apply → Verify。這樣本期 verify 階段就能用上修復後的配置,直接檢驗效果。

完整流程如下:工作流總覽

說明:本文內容基於 OpenSpec v1.3.1 實際項目操作記錄整理。verify SKILL.md 修復和 json-formatter 工具實現均在 shuge-ai-toolbox 項目中實際驗證,但 verify 第 4 維度(Task Granularity)修復後仍未生效,文中如實記錄。文中的配置修改和代碼僅供參考,實際效果請以你的項目環境測試結果為準。如果有實際使用經驗,歡迎在評論區分享交流。

修復 verify SKILL.md:把矛盾的地方同步改完

先定位問題。文件路徑是 .claude/skills/openspec-verify-change/SKILL.md,總共 173 行。

L46 的定義部分其實是對的:

Create a report structure with four dimensions:
**Completeness**: Track tasks and spec coverage
**Correctness**: Track requirement implementation and scenario coverage
**Coherence**: Track design adherence and pattern consistency
**Task Granularity**: Track task format compliance, TDD adherence, code completeness, and granularity assessment

四個維度定義齊全,L50-54 還詳細拆了 Task Granularity 的四項檢查:任務格式、TDD 合規、代碼完整性、粒度評估。定義沒問題。

問題出在後面兩處引用這個數字的地方。

矛盾點 1:Summary Scorecard 模板(L117-127)

這個表格是 verify 報告末尾的彙總模板,AI 按這個格式輸出最終結論。當前只有 3 行:

### Summary
| Dimension    | Status           |
|--------------|------------------|
| Completeness | X/Y tasks, N reqs|
| Correctness  | M/N reqs covered |
| Coherence    | Followed/Issues  |

Task Granularity 行缺失。AI 生成報告時照着模板走,模板只有 3 行,輸出自然只有 3 維度。

矛盾點 2:Graceful Degradation 段(L159-164)

這個段描述的是"當工件不全時怎麼降級驗證"。最後一句:

If full artifacts: verify all three dimensions

寫的是 three dimensions。即使所有工件都齊全,AI 讀到這句也會認為只需要驗證 3 個維度。

SKILL.md 三處矛盾位置標註
SKILL.md 三處矛盾位置標註

圖:SKILL.md 三處矛盾位置標註

具體修改

改動不大,但位置分散,需要逐個同步。

修改 1:Summary Scorecard 加行

改前:

| Dimension    | Status           |
|--------------|------------------|
| Completeness | X/Y tasks, N reqs|
| Correctness  | M/N reqs covered |
| Coherence    | Followed/Issues  |

改後:

| Dimension        | Status           |
|------------------|------------------|
| Completeness     | X/Y tasks, N reqs|
| Correctness      | M/N reqs covered |
| Coherence        | Followed/Issues  |
| Task Granularity | Format/TDD/Code  |

新增的 Task Granularity 行,狀態列用 Format/TDD/Code 簡寫,對應任務格式檢查、TDD 合規檢查、代碼完整性檢查三項。

修改 2:Graceful Degradation three → four

改前:

If full artifacts: verify all three dimensions

改後:

If full artifacts: verify all four dimensions

一個詞的事。但這一詞決定了 AI 驗證時是否走第 4 維度。

SKILL.md 修復前後對比
SKILL.md 修復前後對比

圖:SKILL.md 修復前後對比

改完存盤,總共改了兩處三行。看起來微不足道,但這個同步不到位正是 verify 第 4 維度一直缺席的直接原因。

這裏有個教訓值得記一筆:提示詞約束是一條鏈路,不是孤立的定義。L46 定義寫得再漂亮,後面模板和退化邏輯沒跟上,執行層就按舊路徑走。2/8 法則的體現——定義佔 20% 篇幅但決定方向,引用點佔 80% 篇幅且決定實際行為。改提示詞要改全鏈路,不能只改最顯眼的那一處。

改完 verify SKILL.md 後,馬上進入 Propose。這樣本期生成的工件才能用上修復後的配置。

Propose:記錄工件產出過程

本期跳過 Explore,需求很明確——實現 json-formatter 工具,複用 text-summary 已建立的組件模式。

執行 /opsx:propose

**AI**:What change do you want to work on? Describe what you want to build or fix.

**用戶**:本期做一個 implement-json-formatter 變更。在 src/modules/json-formatter/ 下實現 JSON 格式化工具,複用 text-summary 已建立的組件模式。功能包括格式化、壓縮、語法校驗。catalog.ts 中 json-formatter 已註冊為 active,路由已存在,但模塊目錄尚未創建。同時需要在 router/index.tsx 的 toolModules 中添加 json-formatter 的映射行。

**AI**
**Change created: `implement-json-formatter`**
**Location:**`openspec/changes/implement-json-formatter/`

**Artifacts created:**
| Artifact | Description |
|----------|-------------|
`proposal.md` | Why we need JSON formatting (dev高頻需求,純前端實現) |
`design.md` | Technical design: single-file component, native JSON.parse/JSON.stringify, useEffect+import pattern |
`specs/json-formatter/spec.md` | 8 scenarios covering format/minify/validate, empty input, large file |
`review.md` | 4-dimension review with task split direction |
`tasks.md` | 3 tasks: JsonFormatterPage (TDD), router mapping, verification |

**Task breakdown:**
任務 1: JsonFormatterPage 組件(TDD 模式,測試 + 實現)
任務 2: Router 映射配置(toolModules 添加 json-formatter)
任務 3: 驗證完整功能

**All artifacts created! Ready for implementation.**
Run `/opsx:apply implement-json-formatter` to start implementing.

AI 一次生成了 5 個工件。按實際產出順序記錄。

proposal.md

變更內容:
新增 `src/modules/json-formatter/` 目錄,包含頁面組件、格式化邏輯和測試
修改 `router/index.tsx`,在 toolModules 中添加 json-formatter 映射行

能力清單:
新增能力:json-format(JSON 格式化 / 壓縮 / 校驗)

影響範圍:路由加載、src/modules/ 目錄結構

proposal 簡潔,一頁紙,說明了"為什麼做"和"改什麼範圍"。

specs

能力清單隻有一個 json-format,所以只生成一個 spec 文件。需求定義了三個核心行為:

  • 格式化:接收 JSON 字符串,輸出縮進整齊的 JSON。Scenario 覆蓋了簡單對象、嵌套對象、數組三種輸入
  • 壓縮:移除多餘空白,輸出單行 JSON。Scenario 包含已有縮進的 JSON 和帶註釋的非法 JSON
  • 校驗:檢測語法錯誤,返回錯誤位置和描述。Scenario 覆蓋了缺少引號、缺少逗號、多餘的尾部逗號、未閉合的花括號等常見錯誤

每個需求至少有一個正常輸入 Scenario 和一個異常輸入 Scenario。Spec 的質量比 text-summary 那期好一些——text-summary 當時只有一個"短/中/長"的長度選項需求,json-formatter 的三個需求方向更清晰,邊界條件也寫得更具體。

design.md

技術決策部分記錄了幾個要點:

  • 純前端實現,不依賴外部 API——json-formatter 是個本地工具,不需要調用任何後端服務
  • 複用 text-summary 的組件模式:頁面組件負責 UI,純函數文件負責邏輯——這個模式在 text-summary 上驗證過了,直接沿用
  • 使用原生 JSON.parse + JSON.stringify 實現格式化和壓縮,不引入第三方庫——減小打包體積,同時 JSON.parse 自帶的錯誤信息足夠定位語法錯誤位置

還有一個值得注意的決策:校驗功能不單獨用 try-catch 包一層,而是複用格式化函數的錯誤處理。因為 JSON.parse 在解析失敗時已經會拋出包含位置信息的 SyntaxError,沒必要重複實現。這個決策後來在 tasks.md 裏體現為 formatJSON 和 validateJSON 共用同一個 parse 調用。

不過 design.md 還做了一個後來影響實現結構的決策:single-file component 模式。它沒有像 text-summary 那樣把純函數拆到獨立文件,而是選擇把格式化、壓縮、校驗邏輯直接內聯在頁面組件中。這個決策直接決定了 apply 後的文件數量——比預期少。

review.md

這期繼續用第 6 期改造的 4 維度 review instruction。看看拆分方向建議有沒有參考價值。

4 個維度的審查結果:

1. **邊界條件** ✅ 通過:空字符串、null 輸入、非法 JSON、超長字符串均有場景覆蓋
2. **回滾方案** ⚠️ 警告:純前端新增模塊,回滾即刪除目錄,風險可控但 proposal 未顯式說明回滾步驟
3. **測試覆蓋** ✅ 通過:format/compress/validate 三個方向各有獨立測試場景
4. **向後兼容** ✅ 通過:不影響 text-summary 的接口和路由

拆分方向建議:

建議按邏輯層 → 頁面層 → 路由層的順序拆分任務。三個純函數(formatJSON / compressJSON / validateJSON)優先編寫並測試,確保核心邏輯正確後再接入頁面組件。路由映射放在最後一個任務。

和 text-summary 的 tasks 拆分思路一致——先純函數、再組件、最後路由。說明 review instruction 的拆分方向引導在穩定輸出,不是偶爾碰運氣。

tasks.md

對比 text-summary 的 tasks,格式完全一致:每個任務都用 ### 任務 N 標題,附文件列表和 TDD 五步結構(寫失敗測試 → 確認失敗 → 寫最小實現 → 確認通過 → 提交)。

但任務數量比預想的少。tasks.md 只有 1 個核心任務——JsonFormatterPage 組件整體,不是 formatJSON / compressJSON / validateJSON 三個獨立的純函數任務。這是 design.md "單文件組件"決策的直接結果:邏輯內聯在組件中,沒必要拆成獨立的純函數 TDD 步驟。

工件自洽性檢查

按照每期的慣例,檢查 5 個工件之間是否存在矛盾:

  • proposal.md 說"新增 json-format 能力",specs 目錄下確實只有一個 specs/json-formatter/spec.md,對應一致
  • design.md 說"使用原生 JSON.parse + JSON.stringify",沒有提到任何第三方依賴,和 proposal 的"純前端實現"一致
  • review.md 給出的拆分方向是"邏輯層 → 頁面層 → 路由層",但 tasks.md 實際只有組件整體 + 路由映射兩個任務,review 的建議沒有被完全採納
  • tasks.md 中的文件路徑都指向 src/modules/json-formatter/,沒有引用不存在的模塊

5 個工件全部生成完畢,進入 Apply。

Apply:記錄執行過程

執行 /opsx:apply

**AI**:Test failed as expected (Module not found). Now writing the implementation.

**AI**:One test is failing because `getByText` normalizes whitespace. Let me fix the test to use a regex matcher:

**AI**
## Implementation Complete
**Change:** implement-json-formatter
**Schema:** with-review
**Progress:** 11/11 tasks complete ✓

| Task | Description | Status |
|------|-------------|--------|
| 1.1 | 寫失敗測試 | ✓ |
| 1.2 | 運行測試——確認失敗 | ✓ |
| 1.3 | 寫最小實現 | ✓ |
| 1.4 | 運行測試——確認通過 | ✓ |
| 1.5 | 提交 | ✓ |
| 2.1 | 添加 json-formatter 映射 | ✓ |
| 2.2 | 驗證 router 測試 | ✓ |
| 2.3 | 提交 | ✓ |
| 3.1 | 運行所有測試 | ✓ |
| 3.2 | 驗證路由可達 | ✓ |
| 3.3 | 最終提交 | ✓ |

Commits:
`d3ff798` feat(json-formatter): implement JsonFormatterPage component
`59c13cd` feat(router): add json-formatter tool module mapping
`e26c616` feat: implement json-formatter tool page

Files Created/Modified:
`src/modules/json-formatter/index.tsx` (component)
`src/modules/json-formatter/index.test.tsx` (8 tests)
`src/router/index.tsx` (added mapping)

All tasks complete! You can archive this change with `/opsx:archive`.

AI 按 tasks.md 的順序執行。tasks.md 只有 1 個核心任務——JsonFormatterPage 組件整體。TDD 流程是:寫組件的失敗測試(渲染標題、輸入區、按鈕交互),確認模塊找不到報錯後,再寫組件實現。不像 text-summary 那期有獨立的純函數 TDD 步驟,本期整個組件(含內聯的格式化、壓縮、校驗邏輯)在一個任務裏完成了。

整個過程 AI 和用戶之間的交互不多,因為需求明確、tasks 拆分清晰,AI 基本一路執行下來沒卡殼。不像第 4 期佈局改造那次,AI 來回問了好幾輪"Tab 切換行為是什麼"、"滾動狀態怎麼處理"。本期這種"照着 tasks 一步步寫"的場景,OpenSpec 的 TDD 五步結構確實減少了交互輪次。

Apply 執行流程:TDD 分步提交
Apply 執行流程:TDD 分步提交

圖:Apply 執行流程:TDD 分步提交

文件核實

Apply 完成後,核實文件是否真的創建成功。

ls src/modules/json-formatter/

實際只有 2 個文件,不是預期的 4 個:

index.tsx              # 頁面組件(含格式化、壓縮、校驗邏輯)
index.test.tsx         # 8 個測試用例

對比 text-summary 的 8 個文件(4 源文件 + 4 測試),json-formatter 比 text-summary 更簡潔——沒有獨立的邏輯文件,格式化、壓縮、校驗三個函數直接寫在 index.tsx 的組件中。每個函數的核心就是 JSON.parse + JSON.stringify 的不同參數組合,不需要拆文件。

這就是 design.md "single-file component"決策的直接體現。text-summary 有 extract-summary.ts、text-utils.ts、stopwords.ts 三個輔助文件是因為邏輯確實複雜(詞頻統計、句子評分、停用詞),json-formatter 的三個功能加起來也就十幾行代碼,拆文件反而多餘。

路由映射檢查

這是每期必查的環節。上一期 text-summary 也在這裏手動加過映射。AI 執行 tasks 時可能跳過路由映射這步——tasks.md 寫了這步,但 AI 容易忽略文件列表之外的操作。

讀取 router/index.tsx,確認 toolModules 部分:

const toolModules: Record<string() => Promise<{ default: React.ComponentType<unknown> }>> = {
  'text-summary'() => import('/src/modules/text-summary/index.tsx'),
  'json-formatter'() => import('/src/modules/json-formatter/index.tsx'),
};

json-formatter 行已經在。這次 AI 沒漏。不過這個檢查不能省——上一期 text-summary 的 review 過程中,我就發現過 AI 標記了 [x] 但實際文件沒改的情況。手動核實是每期 apply 後的固定動作,不能信 AI 的完成報告。

實現體驗對比

對比 text-summary,json-formatter 的實現過程有明顯差異:

  • 組件模式複用度高:頁面組件 index.tsx 基本套用 text-summary 的 textarea + 操作控件 + 結果展示三段式佈局,只是把"生成摘要"按鈕換成了"格式化/壓縮/校驗"三個按鈕
  • 純函數邏輯更簡單:text-summary 的 extract-summary.ts 涉及詞頻統計、句子評分、位置權重,json-formatter 的內聯邏輯本質是 JSON.parse + JSON.stringify 的三種參數組合
  • 測試更好寫:text-summary 要模擬中文分詞場景,json-formatter 輸入輸出都是字符串,測試用例更直觀

這就是建立組件模式的價值:第一個工具花時間摸索模式,第二個工具直接套用。如果後續第三個、第四個工具繼續走這條路徑,實現速度會越來越快。

文件清單確認完畢,進入本期重點——Verify。

Verify:記錄驗證過程(本期重點)

本期 Verify 是核心驗證點。修了 SKILL.md 的兩處矛盾後,verify 報告是否首次出現 4 個維度?

執行 /opsx:verify

verify 報告

AI 實際輸出的 verify 報告:

## Verification Report: implement-json-formatter

### Summary
| Dimension    | Status              |
|--------------|---------------------|
| Completeness | 11/11 tasks, 7/8 scenarios |
| Correctness  | Requirement implemented, 1 scenario uncovered |
| Coherence    | Design followed, pattern consistent |

---

### Issues by Priority

#### CRITICAL (Must fix before archive)
**Scenario not implemented: 大文件 JSON 解析**
**Spec requirement**: "用戶輸入一個非常大的 JSON(超過 1MB)" → "錯誤提示區顯示'JSON 數據過大,請控制在中。'"
**Current state**: No size check in implementation
**Recommendation**: Add size check in `src/modules/json-formatter/index.tsx`:
```typescript
  const MAX_SIZE = 1024 * 1024; // 1MB
  if (input.length > MAX_SIZE) {
    setError('JSON 數據過大,請控制在中。');
    setOutput('');
    return;
  }
```
  Add corresponding test in `src/modules/json-formatter/index.test.tsx`

#### WARNING (Should fix)
**Scenario not tested: 用戶輸入有效 JSON 並點擊語法校驗按鈕**
**Spec requirement**: "錯誤提示區顯示'JSON 語法正確',結果展示區顯示原始 JSON"
**Current state**: Test for valid JSON validation is missing

#### SUGGESTION (Nice to fix)
**Test file uses getAllByRole for output textarea**
**Current**`screen.getAllByRole('textbox')[1]` relies on order
**Alternative**: Add `aria-label` to output textarea

### Final Assessment

**1 critical issue found. Fix before archiving.**

Summary 表格只有 3 行。Task Granularity 維度沒有出現——和上一期完全一樣,修復 SKILL.md 沒有生效。這對應草稿預測的"情況 B"。

不過 verify 報告本身的質量比上期好不少。它發現了 1 個 CRITICAL issue:spec 裏定義了"大文件 JSON 解析"場景(超過 1MB 的 JSON 要提示用戶),但實現中沒有做 size check。還發現了 1 個 WARNING:有效 JSON 點擊語法校驗按鈕的測試用例缺失。這些發現是 specs 裏寫了但 tasks.md 沒覆蓋到的場景,verify 把它們撈出來了。

verify 報告摘要:3 維度 + 缺失標註
verify 報告摘要:3 維度 + 缺失標註

圖:verify 報告摘要:3 維度 + 缺失標註

build 驗證

npm run build

npm run build(實際執行 tsc -b && vite build)因 tsc 類型錯誤失敗。報了 4 個錯誤,其中 2 個是 router 動態 import 路徑的既有問題(從第 2 期就存在),1 個是測試文件裏 vi 沒用到但 import 了。這些都是類型層面的問題,不影響運行。

跳過 tsc 直接跑 npx vite build,構建成功:

vite v8.0.12 building client environment for production...
✓ 1757 modules transformed.
rendering chunks...
computing gzip size...
dist/index.html                           0.46 kB │ gzip:  0.30 kB
dist/assets/index-8W2XdHU1.css           20.28 kB │ gzip:  4.75 kB
dist/assets/json-formatter-DwIBtDQv.js    2.63 kB │ gzip:  0.88 kB
dist/assets/text-summary-C_blhUgt.js      2.84 kB │ gzip:  1.39 kB
dist/assets/index-BtN7sydP.js           295.12 kB │ gzip: 94.23 kB

✓ built in 197ms

json-formatter 和 text-summary 各自獨立打包為 chunk(json-formatter-DwIBtDQv.js 和 text-summary-C_blhUgt.js),動態路由的 code splitting 生效了。json-formatter 只有 2.63 kB(gzip 後 0.88 kB),體積控制得不錯。

瀏覽器檢查:先踩了一個坑

啓動開發服務器,訪問 /tools/json-formatter——頁面停在加載中...不動了。text-summary 頁面正常。同一個 router 邏輯,一個能加載一個不能,問題肯定出在 json-formatter 模塊本身。

仔細對比兩個模塊的代碼,發現根因:json-formatter 的 index.tsx 只用了 named export(export function JsonFormatterPage()),而 router 的 ToolPage 組件取的是 mod.default。text-summary 有 export default TextSummaryPage,json-formatter 漏了。

給 AI 報了這個問題,它自己找到原因加上了 export default JsonFormatterPage;

修復後單獨提交了一個 commit:

4b8e1d4 fix(json-formatter): add default export for lazy loading

這個 bug 的教訓是:組件模式複用時,export 方式也得對齊。named export 和 default export 在動態 import 時行為不同,少寫一行 export default 就會導致整個頁面加載失敗。後續每個新工具的模板裏應該把 export default 作為固定項寫死,避免再踩這個坑。

瀏覽器檢查:修復後的功能驗證

修復 export default 後重新訪問 /tools/json-formatter,頁面正常加載:

json-formatter 頁面
json-formatter 頁面

圖 1:json-formatter 頁面修復後正常加載,顯示標題、輸入區和三個操作按鈕

頁面佈局和 text-summary 類似:

  • 標題區:JSON 格式化
  • 輸入區:textarea,placeholder 提示輸入 JSON
  • 操作區:格式化、壓縮、校驗三個按鈕
  • 輸出區:textarea 展示結果

輸入一段壓縮的 JSON 測試格式化功能:

{"name":"shuge-ai-toolbox","version":"1.0.0","dependencies":{"react":"^19.0.0","react-dom":"^19.0.0"}}

點擊格式化按鈕,輸出帶縮進的版本:

格式化測試
格式化測試

圖 2:輸入壓縮 JSON 後點擊"格式化",輸出帶縮進的可讀版本

再測試校驗功能——輸入一段不合法的 JSON:

{"name""test""age":}
校驗測試
校驗測試

圖 3:輸入無效 JSON 後點擊"校驗",顯示語法錯誤提示

校驗功能正常,返回了語法錯誤的提示信息。瀏覽器檢查通過。

Archive:歸檔

確認 verify 通過後執行歸檔:

## Archive Complete
**Change:** implement-json-formatter
**Schema:** with-review
**Archived to:** `openspec/changes/archive/2026-05-31-implement-json-formatter/`
**Specs:** No delta specs (json-formatter is a new capability, not yet in main specs)
All artifacts complete. All tasks complete (11/11).
**Note:** The verification report found a missing "large file" scenario check (>1MB), but since you chose to archive without addressing it, the spec remains in the archive for future reference.

注意最後那句 Note:AI 提醒 verify 報告發現的大文件場景(>1MB)未修復就歸檔了。這說明 verify 確實起到了"檢查遺漏"的作用,雖然 Task Granularity 維度沒生效,但其他三個維度(特別是 Correctness)把 spec 裏寫了但實現沒覆蓋的場景撈出來了。

驗證歸檔結果:

ls openspec/changes/

活躍變更目錄應該是空的(或只剩其他未完成的 change),implement-json-formatter/ 已移到 openspec/archive/ 下。

Git 推送:提交到 GitHub

AI 按 TDD 步驟分 3 個獨立 commit 提交,加上後續修復 export default 的 1 個 commit,共 4 個 commit:

4b8e1d4 fix(json-formatter): add default export for lazy loading
e26c616 feat: implement json-formatter tool page
59c13cd feat(router): add json-formatter tool module mapping
d3ff798 feat(json-formatter): implement JsonFormatterPage component

這不是一個大合併 commit,而是按 TDD 節奏的增量提交:組件實現 → 路由映射 → 最終整合 → bug 修復。每個 commit 粒度小、職責單一,符合 TDD 分步提交的模式。

SKILL.md 的修復沒有納入 git commit——因為它是在項目外的 .claude/skills/ 目錄下執行的,不屬於項目倉庫的變更範圍。

**AI**:已推送到遠端。3 個 commit 從 `d387013` 到 `e26c616`

回顧:本期學到了什麼

verify 修復沒有生效

改了 SKILL.md 三處(定義本身已正確、Summary Scorecard 加行、Graceful Degradation 改 three→four),全鏈路同步了,但 verify 報告仍然只輸出 3 維度。AI 不是沒讀到修改——它確實加載了 SKILL.md——但輸出時沒有嚴格按模板逐項填充,而是按自己的理解走了老路徑。

這說明單靠改提示詞的約束力有天花板:改全鏈路是必要條件,但不是充分條件。即使定義、模板、退化邏輯全部統一了,AI 在生成報告時仍然可能跳過模板,直接按慣性輸出。

有個結論是確定的:提示詞的全鏈路同步比單點增強重要得多。L46 的定義寫得再漂亮,後面模板和退化邏輯沒跟上,執行層就按舊路徑走。這是 2/8 法則的又一次驗證。但同時要承認,全鏈路同步也不能保證 AI 一定按模板走——這是提示詞約束的固有侷限。

verify 報告雖然沒有出現第 4 維度,但在其他方面發揮了價值:它發現了大文件 JSON 解析場景的遺漏和有效 JSON 校驗測試的缺失。這些是 specs 裏寫了但實現沒覆蓋的,verify 把它們撈出來了。

第二個工具的實現體驗

json-formatter 的實現速度明顯比 text-summary 快。核心原因:

  • 組件模式可以直接複用,不用重新摸索
  • JSON 格式化的核心邏輯比文本摘要的詞頻統計簡單得多
  • 測試用例更好寫——輸入輸出都是字符串,不需要模擬中文分詞

第一個工具花時間建立模式,第二個工具享受模式紅利。如果後續繼續按這個節奏迭代,每加一個工具的邊際成本會越來越低。

export default 這個坑

json-formatter 實現過程中踩的 export default 坑值得單獨記一筆。text-summary 有 export default,json-formatter 沒有,頁面直接卡死在"加載中..."。這不是複雜邏輯 bug,而是一行代碼的遺漏,但排查花了不短的時間——因為第一反應是"路由配置有問題",而不是"export 方式不對"。

後續每個新工具的模板應該把 export default 作為固定項寫死,和 import React 一樣成為模板的必選項。

路由加載策略的侷限

toolModules 手動映射每加一個工具改一次 router/index.tsx,兩期下來已經改了兩次(text-summary 和 json-formatter)。目前只有兩個 active 工具還好,等工具數量上去了這就是維護負擔。但本期不改這個,屬於"能用但不夠優雅"的範疇,留給後續專門的重構任務。

理想的做法是把 toolModules 改成動態掃描 src/modules/ 目錄,或者在 catalog.ts 里加一個 modulePath 字段,讓路由映射自動生成。不過這種重構屬於"改善型"需求,當前階段優先級不高——先把工具做出來再說。

關於 verify 的邊界思考

如果本期 verify 4 維度仍然沒生效,那說明純 SKILL.md 文案修改的約束力有天花板。AI 的執行行為受上下文慣性影響——它可能讀到新的模板但仍然按舊習慣輸出。單靠提示詞約束,無法保證 AI 嚴格遵循每一條規則。這個發現本身有價值:它說明質量保證不能只壓在 verify 這一個環節上,需要引入更強的過程控制。

下一期會詳細討論這個問題,以及為什麼後續不再死磕 OpenSpec verify 的增強,而是引入 Superpowers 的計劃紀律來補充。

預告

第 8 期先不寫代碼,覆盤第 5-7 期三層改造的實驗結果:template 改造的投入產出比、review 拆分方向建議的實際價值、verify SKILL.md 修復的最終效果。然後引入三檔工作流的概念:

  • 快速檔:低風險改動(文案、樣式),OpenSpec + fresh verify 就夠了
  • 標準檔:普通工具開發,OpenSpec + Superpowers writing-plans 控制計劃顆粒度
  • 嚴格檔:核心邏輯、bugfix、重構,疊加 TDD、debug、code review

按風險選強度,不再一刀切。這也是這個系列從"摸索 OpenSpec 增強方案"轉向"組合使用 OpenSpec + Superpowers"的轉折點。

聲明:本文基於 OpenSpec v1.3.1 源碼分析、實際項目操作記錄和讀者反饋整理。所有改造均在 shuge-ai-toolbox 項目中實際驗證。配置和代碼僅供參考,請以實際環境測試為準。

項目倉庫:https://github.com/shuge-x/shuge-ai-toolbox

好啦,謝謝你觀看我的文章,如果喜歡可以點贊轉發給需要的朋友,我們下一期再見!敬請期待!