OpenSpec 項目實戰(六) | review 拆分 + verify 增強 + 實現第一個工具
整理版優先睇
OpenSpec實戰(六):重劃review同verify職責,實現第一個工具組件
呢篇文章係「術哥無界」OpenSpec項目實戰系列嘅第六篇,作者術哥係AI編程同開源技術嘅實踐者。文章嘅核心係解決一個實戰中發現嘅問題:原本review嘅第五維度「任務粒度」聲稱審查tasks.md,但實際上review喺tasks之前生成,根本睇唔到tasks.md,導致職責錯位。
作者透過改造三個文件——schema.yaml入面嘅review instruction、verify SKILL.md同review.md模板——將review由5維度改為4維度,去掉任務粒度審查,改為提供拆分方向建議;同時將verify由3維度增強到4維度,新增Task Granularity維度,負責後置嘅任務粒度驗證。改造之後,作者用「實現第一個工具組件text-summary」作為測試案例,完整執行咗propose→apply→verify→archive流程,過程中發現verify嘅第4維度因為SKILL.md內部矛盾冇生效,同埋React.lazy喺生產環境有兼容性bug,最終用useEffect+import方式修復。
整體結論係:改instruction同template唔夠,仲要同步更新SKILL.md入面所有相關嘅引用,先形成完整約束閉環。另外,任何改動都要做瀏覽器檢查,build同test通過唔代表運行時冇問題。
- review同verify嘅職責邊界唔清晰:review前置但聲稱審查後置嘅tasks.md,verify後置但冇粒度維度。改造後review提供拆分方向,verify做實際粒度驗證。
- 改造方法:review instruction由5維度減到4維度,verify SKILL.md由3維度增加到4維度,review.md模板同步更新。
- 改造未能完全生效:verify第4維度因為SKILL.md內部矛盾(L46寫four dimensions但L163仍寫three dimensions)而冇被AI執行。
- 實現text-summary組件時遇到React.lazy+build+test全部通過但瀏覽器仍顯示Suspense fallback嘅bug,最終用useEffect+import取代React.lazy。
- 實戰教訓:改instruction要同步改曬所有相關模板同參考;產出必須做瀏覽器檢查,唔可以信build同test測試結果。
結構示例
##
5. 任務粒度**狀態**:⚠️ 警告**發現**:
- 視覺類任務難以按 TDD 節奏嚴格拆分- 部分任務包含多個子步驟,需要拆分- CSS 變量重建如果一次性替換所有 primary 變量,涉及文件多但邏輯簡單,可合併為一個大步驟**建議**:
- CSS 變量更新(tokens.css)→ 合併為一個任務- Home.tsx 圖標 → 拆分為:Category 圖標任務 + Badge 圖標任務- Tab 滑動指示器 → 拆分為:JS 計算邏輯任務 + CSS transition 樣式任務- Stagger 動畫 → 拆分為:keyframes 定義任務 + nth-child 動畫應用任務
發現問題:review嘅第五維度根本唔應該存在
系列第五期改咗template之後,tasks.md格式開始生效,但一位讀者發現一個邏輯漏洞:review維度5叫「任務粒度」,聲稱審查tasks.md每個step係咪達到2-5分鐘粒度,但review喺tasks之前生成——依賴鏈係proposal→specs→design→review→tasks,review生成時tasks.md仲未存在。
維度5嘅建議係基於design同proposal做嘅預測,唔係對實際tasks.md嘅審查
作者承認呢個問題「藏咗5期冇人發現」,因為維度5每次都有輸出,內容睇落充實,但本質上係前置指導定後置驗證嘅混淆。
改造過程:拆走職責,補齊缺口
改造涉及三個文件:schema.yaml中review嘅instruction、verify SKILL.md同review.md模板。核心思路係將任務粒度審查從review移到verify,令兩者各司其職。
- 1 review instruction:由5維度改為4維度,去掉任務粒度,改為提供「拆分方向建議」。新增明確聲明:「本審查喺tasks.md生成之前執行,唔好聲稱審查咗tasks.md」。
- 2 verify SKILL.md:由3維度(Completeness/Correctness/Coherence)改為4維度,新增Task Granularity維度,包含任務格式、TDD合規、代碼完整性同粒度評估四項檢查。
- 3 review.md模板:去掉維度5嘅段落,整體評估改為「拆分方向建議 + 優先級排序 + 粒度標準」,定位從前置審查變為前置指導。
改造後對比:review instruction由5維度變4維度+拆分方向建議;verify SKILL.md由3維度變4維度;review模板由5維度變4維度
呢個改造唔依賴具體項目,無論前端定後端,review喺tasks之前生成、verify喺apply之後執行嘅依賴關係唔會變。職責劃清,兩個工具先可以各司其職。
實現第一個工具組件:text-summary
為咗驗證改造成果,作者揀咗第一個工具組件text-summary。原因有三:純前端實現、catalog已註冊、功能邊界清晰。透過propose生成咗5個工件:proposal、specs、design、review同tasks。
review嘅AI回覆明確寫「四維審查」——改造後嘅4維度instruction生效咗
apply階段5個任務共24個step全部自動完成,但作者汲取第三期教訓,唔信AI完成報告,抽查咗關鍵文件:src/modules/text-summary/目錄存在、router/index.tsx無變更、組件唔係PlaceholderPage。
- 嘗試1:加export default TextSummaryPage → 冇效,Suspense仍然唔解析。
- 嘗試2:將lazy()移到組件內用useMemo包裹 → 冇效。
- 最終方案:放棄React.lazy,改用useEffect + import()手動加載,成功渲染。
React.lazy + Vite動態import + 變量路徑嘅組合喺運行時存在兼容性問題,可能同React 19 + createBrowserRouter + Vite HMR嘅交互有關
verify改造未完全生效:習慣比instruction更強
verify執行後,報告仍然係3維度,新增嘅Task Granularity維度完全缺失。作者分析三個原因:Skill指導唔係硬性約束,AI會參考前5期訓練出嘅3維度模式;SKILL.md內部矛盾(L46寫four dimensions但L163 Graceful Degradation段仍然寫verify all three dimensions);輸出格式冇約束,Final Assessment模板同Graceful Degradation都冇更新。
改instruction解決內容問題,改template解決格式問題——但改SKILL.md解決唔到行為慣性問題
作者話「呢個發現比改造成功更有價值」。後續需要同步更新Graceful Degradation段同Final Assessment模板,先形成完整嘅約束閉環。
歸檔同Git提交:留意archive階段嘅行為
Archive階段AI冇加載Skill工具,直接用bash命令mv歸檔。歸檔後確認變更文件已全部移走。但Git推送到遠程倉庫時遇到443端口超時,5個commit準備好但未能推送。
archive階段AI直接用bash命令mv歸檔,冇用到Skill工具——呢個行為同propose/apply/verify階段唔同
最終修復commit係ba8a080,用useEffect+import取代React.lazy。完整git log顯示3輪修復過程:先加default export,再試useMemo,最後先成功。
🚩 2026 年「術哥無界」系列實戰文檔 X 篇原創計劃 第 117 篇,OpenSpec 項目實戰「2026」系列第 6 篇
大家好,歡迎嚟到 術哥無界 | ShugeX | 運維有術。
我是術哥,一個專注於 AI 編程、AI 智能體、Agent Skills、MCP、雲原生、AIOps、Milvus 向量數據庫嘅技術實踐者同開源佈道者!
Talk is cheap, let's explore。無界探索,有術而行。

封面:review 拆分 + verify 增強 + 實現第一個工具
說明:本文內容係基於 OpenSpec(Fission-AI/OpenSpec)v1.3.1 同 React 19 + TypeScript + Tailwind CSS v4 嘅實際操作記錄整理出嚟嘅。文入面嘅配置模板同參數建議只係僅供參考,實際效果請以你嘅業務數據同環境測試結果為準。如果有實際使用經驗,歡迎喺評論區分享交流。
1. 讀者發現咗一個問題
第 5 期改咗 template,tasks.md 格式終於生效。標題從 ## 1. 變成了 ### 任務 N:,涉及文件列表成為標配,代碼塊數量創下系列新高。
但呢一期要講嘅,唔係 template 嘅後續優化,而係另一個問題——一位讀者發現嘅。
呢位讀者仔細讀咗第 5 期歸檔嘅 review.md,留意到一個細節:review 維度 5 叫任務粒度,聲稱審查 tasks.md 嘅每個 step 是否達到 2-5 分鐘粒度。但 review 喺 tasks 之前生成——依賴鏈係 proposal → specs → design → review → tasks——review 生成時 tasks.md 仲唔存在。
多謝呢位讀者。 咁仔細嘅源碼閲讀非常有意義。一個人嘅盲區,往往係另一個人嘅發現。開源項目嘅 review instruction 寫咗審查 tasks.md 粒度,但冇人質疑過 review 生成時 tasks 到底存唔存在——包括我自己。
睇下實際發生咗啲乜。引用第 5 期歸檔嘅 openspec/changes/archive/2026-05-17-ui-visual-polish/review.md 中維度 5 嘅內容:
## 5. 任務粒度
**狀態**:⚠️ 警告
**發現**:
- 視覺類任務難以按 TDD 節奏嚴格拆分
- 部分任務包含多個子步驟,需要拆分
- CSS 變量重建如果一次性替換所有 primary 變量,涉及文件多但邏輯簡單,可合併為一個大步驟
**建議**:
- CSS 變量更新(tokens.css)→ 合併為一個任務
- Home.tsx 圖標 → 拆分為:Category 圖標任務 + Badge 圖標任務
- Tab 滑動指示器 → 拆分為:JS 計算邏輯任務 + CSS transition 樣式任務
- Stagger 動畫 → 拆分為:keyframes 定義任務 + nth-child 動畫應用任務
內容睇落都幾充實。但問題係——呢個時候 tasks.md 仲未生成。 維度 5 嘅建議係基於 design 同 proposal 做嘅預測,唔係對實際 tasks.md 嘅審查。佢話視覺類任務難以按 TDD 節奏拆分,但 tasks.md 仲唔存在,佢點知任務會點拆?
根因喺依賴鏈。逐字引用 openspec/schemas/with-review/schema.yaml 中 review 嘅 requires:
- id: review
requires:
- proposal
- specs
- design
review 依賴 proposal、specs、design,唔依賴 tasks。維度 5 嘅定位從根本上就係錯嘅——佢聲稱審查 tasks 粒度,但 tasks 仲未出世。
老實講,呢個問題藏咗 5 期冇人發現,係因為維度 5 每次看起來都正常運作。佢確實輸出咗建議、俾咗 ⚠️ 警告,內容亦唔係亂寫嘅——基於 design 同 proposal 嘅分析,建議往往仲有參考價值。但有參考價值嘅預測和對實際產物嘅審查係兩回事。前者係前置指導,後者係後置驗證。將後置驗證塞咗去前置位置,職責就亂咗。
呢期做三件事:
review instruction 改造:5 維度 → 4 維度,去掉維度 5,改為俾 tasks 提供拆分方向 verify SKILL.md 增強:3 維度 → 4 維度,新增 Task Granularity 維度 實現第一個工具組件(text-summary),驗證所有改造效果
三個改造同時驗證:template(第 5 期改嘅)+ review instruction(呢期改嘅)+ verify(呢期改嘅)。
完整流程唔變:

2. 改造過程:review 同 verify 嘅職責重新劃分
問題本質
review 同 verify 嘅職責邊界唔清晰。review 聲稱要審查 tasks 粒度,但佢生成時 tasks 仲未存在;verify 應該做粒度檢查,但佢得 3 個宏觀維度——完整性、正確性、一致性。真正嘅任務粒度審查,兩邊都冇做。
改造前現狀
先睇 review instruction。逐字引用 openspec/schemas/with-review/schema.yaml 中 review 嘅 instruction 關鍵段落:
instruction: |
從五個維度審查所有工件的完整性和質量。
審查維度:
1.**邊界條件**:Spec是否覆蓋null、空值、越界等異常場景
2.**回滾方案**:數據庫變更是否包含回滾策略
3.**測試覆蓋**:Design是否明確了需要測試的場景和用例
4.**向後兼容**:是否分析了現有接口和數據的兼容性影響
5.**任務粒度**(最重要):tasks.md的每個step是否達到2-5分鐘粒度、是否附帶完整代碼、是否有佔位符
對維度5的檢查標準:
-✅通過:每個step有完整代碼塊+運行命令+預期輸出
-⚠️警告:部分step缺少代碼或命令
-❌失敗:存在佔位符(TBD/TODO)或step粒度超過10分鐘
維度 5 標註咗(最重要),檢查標準列得相當詳細。但呢一切都係建立喺一個前提上:review 睇到 tasks.md。實際上佢睇唔到。
再睇 verify SKILL.md。逐字引用 .claude/skills/openspec-verify-change/SKILL.md 中維度定義嘅段落:
4. **Initialize verification report structure**
Create a report structure with three dimensions:
- **Completeness**: Track tasks and spec coverage
- **Correctness**: Track requirement implementation and scenario coverage
- **Coherence**: Track design adherence and pattern consistency
3 個維度,全部都係宏觀層面嘅檢查。任務格式啱唔啱、TDD 合唔合規、代碼完唔完整、粒度評估準唔準確——verify 都唔理。
最後睇 review 嘅模板文件。逐字引用 openspec/schemas/with-review/templates/review.md:
## 5. 任務粒度
**狀態**:
**發現**:
**建議**:
---
## 整體評估
**對 tasks.md 的關鍵建議**:
模板入面俾維度 5 留咗空位,整體評估下面亦專門有對 tasks.md 嘅關鍵建議。呢個模板強化了 review 應該指導 tasks 拆分嘅定位——定位本身冇問題,但維度 5 叫任務粒度就有啲誇張,叫拆分方向先準確。
思考過程
講白啲:review 喺 tasks 之前,應該做前置指導——基於 proposal、specs、design 預判任務拆分方向。verify 喺 apply 之後,呢個時候 tasks.md 已經存在、代碼亦寫完,先至係做後置審查嘅正確位置。
之前嘅配置搞反咗:review 聲稱審查 tasks 粒度(前置位置做後置嘅事),verify 反而冇粒度維度(後置位置冇做後置嘅事)。兩邊都打咗折扣。
換個角度諗:review 嘅 4 個維度(邊界條件、回滾方案、測試覆蓋、向後兼容)都係審查 proposal/specs/design 呢三個佢能看到依賴嘅工件。唯獨維度 5 跳出咗呢個範圍,去審查一個佢看不到唔依賴嘅工件。呢個係唯一一個越界嘅維度。去掉維度 5,review 嘅職責就乾淨曬——只審查佢依賴嘅三個工件,順便為後續嘅 tasks 拆分提供方向建議。
咁 tasks 粒度審查應該放邊度?verify。verify 喺 apply 之後執行,tasks.md 已經存在,代碼已經寫完。呢個時候檢查任務格式、TDD 合規性、代碼完整性、粒度評估,每一項都有實際內容可以查。
動手改造
三個文件,逐個改。
改造 1:schema.yaml 中 review 嘅 instruction
templates/review-instruction.yaml 已經更新為 4 維度版本,直接用佢嘅內容取代 schema.yaml 中 review 嘅 instruction。逐字引用改造後嘅 instruction:
instruction: |
從四個維度審查 proposal、specs 和 design 的完整性和質量。
為即將生成的 tasks.md 提供拆分方向和優先級建議。
審查維度:
1.**邊界條件**:Spec是否覆蓋null、空值、越界等異常場景
2.**回滾方案**:變更是否包含回滾策略(數據庫遷移、API變更等)
3.**測試覆蓋**:Design是否明確了需要測試的場景和用例
4.**向後兼容**:是否分析了現有接口和數據的兼容性影響
輸出格式:
-每個維度給出明確狀態:✅通過/⚠️警告/❌失敗
-警告和失敗必須給出具體建議
-給出整體評估+針對tasks拆分方向的具體建議(任務分組建議、優先級排序、粒度標準)
注意:本審查在tasks.md生成之前執行。不要聲稱審查了tasks.md,而是基於specs和design預判任務拆分方向。任務粒度的實際驗證由/opsx:verify在apply之後執行。
關鍵變化三個:
維度數從 5 減到 4,去咗任務粒度 加咗明確聲明:本審查喺 tasks.md 生成之前執行。唔好聲稱審查咗 tasks.md 整體評估從審查 tasks 粒度變為提供拆分方向建議
改造 2:verify SKILL.md
在 .claude/skills/openspec-verify-change/SKILL.md 嘅第 4 步 Initialize verification report structure 中,從 three dimensions 改為 four dimensions,新增第 4 維度 Task Granularity:
- **Task Granularity**: Track task format compliance, TDD adherence, code completeness, and granularity assessment
- Task format: Verify each task uses `### 任務 N` with file list and TDD five-step structure
- TDD compliance: Verify each task follows RED/GREEN/REFACTOR rhythm
- Code completeness: Verify no TBD/TODO placeholders exist in any task
- Granularity assessment: Evaluate if each task's estimated effort falls within 2-5 minute range
改造 3:review.md template 同步更新
openspec/schemas/with-review/templates/review.md 去掉維度 5 嘅段落,保留 4 個維度 + 整體評估(包括拆分方向建議)。改造後嘅模板:
## 整體評估
**拆分方向建議**:
**優先級排序**:
**粒度標準**:
從對 tasks.md 嘅關鍵建議變為拆分方向建議 + 優先級排序 + 粒度標準——定位從前置審查變為前置指導。
改造後對比
呢個改造唔依賴具體項目。無論你做前端定後端,review 喺 tasks 之前生成、verify 喺 apply 之後執行呢個依賴關係唔會變。職責劃清咗,兩個工具先可以各司其職。
3. Propose:記錄工件產出過程
需求明確,直接 Propose,唔經 Explore。
執行過程
**AI**:What change do you want to work on? Please describe what you want to build or fix.
**用戶**:本期做一個 implement-text-summary 變更。實現第一個工具組件——文本摘要工具。catalog.ts 中已註冊 id 為 text-summary,category 為 文本處理,stage 為 active。需要創建完整的工具頁面組件、路由配置和測試。純前端實現,不依賴外部 API。
**AI**:Using **openspec:propose** to create the `implement-text-summary` change.
Let me first explore the existing catalog and project structure to understand the context.
AI 探索項目結構之後,生成了 5 個工件:
**AI**:---
**Change created: `implement-text-summary`**
Location: `openspec/changes/implement-text-summary/`
**Artifacts created:**
| Artifact | Description |
|----------|-------------|
| `proposal.md` | 變更提案:文本摘要工具,純前端規則提取實現 |
| `design.md` | 技術設計:摘要算法策略、組件結構、狀態管理方案 |
| `specs/text-summary/spec.md` | 詳細規格:5 個 requirement,15 個 scenario |
| `review.md` | 四維審查:邊界條件✅、回滾方案✅、測試覆蓋⚠️、向後兼容✅ |
| `tasks.md` | 5 個任務組,覆蓋停用詞→句子提取→摘要算法→UI組件→完整驗證 |
**任務分組:**
1. 停用詞列表 `stopwords.ts`
2. 句子提取工具 `text-utils.ts`
3. 摘要核心算法 `extract-summary.ts`(核心)
4. TextSummaryPage UI 組件 `index.tsx`
**All artifacts created! Ready for implementation.**
Run `/opsx:apply` to start implementing.
注意 review 嘅 AI 回覆入面明確寫咗四維審查——說明改造後嘅 4 維度 instruction 生效咗。維度 3 測試覆蓋俾咗 ⚠️ 警告,建議將工具函數測試同 UI 組件測試分兩類處理。
工件產出
5 個工件按實際產出順序:proposal.md → specs/ → design.md → review.md → tasks.md。
揀 text-summary 作為第一個工具組件,有三個原因:
純前端:唔依賴外部 API,唔需要 API Key,唔涉及網絡請求。實現成本低,驗證改造效果更集中 catalog 已註冊: catalog.ts中 text-summary 嘅 stage 已經係active,路由已透過getTools()+tools.map()動態生成,唔需要改註冊表功能邊界清晰:輸入文本 → 輸出摘要,互動簡單,適合作為工具組件嘅 baseline 模式
review.md 重點觀察
改為 4 維度後,review 唔再出現維度 5任務粒度。整體評估部分俾咗拆分方向建議:先搭組件骨架同路由、再實現核心邏輯、然後做 UI 層、最後補測試同邊界處理。呢個正正係前置指導應該做嘅事——基於 proposal/specs/design 預判拆分方向,而唔係聲稱審查咗唔存在嘅 tasks.md。
tasks.md 重點觀察
template(第 5 期改嘅)+ 新 review 嘅雙重效果驗證。tasks.md 格式確認使用 ### 任務 N + 涉及文件 + TDD 五步結構,任務拆分參照咗 review 嘅方向建議——5 個任務組分別對應停用詞、句子提取、摘要算法、UI 組件、完整驗證。
逐字引用 src/tool-registry/catalog.ts 中 text-summary 嘅註冊信息:
{
id: 'text-summary',
name: '文本摘要',
route: '/tools/text-summary',
category: '文本處理',
description: '快速提取長文本的核心觀點',
stage: 'active',
}
路由已透過 catalog.ts 嘅 getTools() + tools.map() 動態註冊為 /tools/text-summary,category 係文本處理,stage 係 active。proposal.md 亦明確聲明咗:路由已透過 catalog.ts 動態註冊,無需修改 router/index.tsx。
4. Apply:記錄執行過程
執行過程
**AI**:Using change: `implement-text-summary` (schema: with-review)
Progress: 0/24 tasks complete
Let me read the context files first.
AI 逐任務自動執行,呢期 5 個任務共 24 個 step 全部自動完成,冇用戶中斷。
**AI**:## Implementation Complete
**Change:**`implement-text-summary`
**Schema:** with-review
**Progress:** 5/5 tasks complete ✓
### Completed This Session
| Task | Files Created |
|------|--------------|
| 1. 停用詞列表 | `stopwords.ts`, `stopwords.test.ts` |
| 2. 句子提取工具 | `text-utils.ts`, `text-utils.test.ts` |
| 3. 摘要核心算法 | `extract-summary.ts`, `extract-summary.test.ts` |
| 4. TextSummaryPage 組件 | `index.tsx`, `index.test.tsx` |
| 5. 驗證完整功能 | All 22 tests passing |
### Test Results
```
src/modules/text-summary/ → 4 test files, 22 tests passed
src/router/index.test.tsx → 3 tests passed
```
AI 嘅完成報告好靚——5/5 tasks complete ✓,22 tests passed。但呢份報告背後藏咗一個 bug,陣間瀏覽器檢查時先講。
實際涉及嘅變更
同 apply 之前預期嘅唔同,實際變更如下:
新建:工具組件頁面( src/modules/text-summary/index.tsx)新建:摘要算法等工具函數( src/modules/text-summary/下的stopwords.ts、text-utils.ts、extract-summary.ts)新建:對應嘅 4 個測試文件 路由配置無需修改—— router/index.tsx已通過catalog.ts的getTools()+lazy(() => import('../modules/${tool.id}/index.tsx'))動態生成路由,text-summary 喺 catalog.ts 中註冊為active後路由自動生效
apply 後 git diff 確認 router/index.tsx 冇任何變更,同 proposal.md 嘅聲明一致。
Apply 完成後抽查
按第 3 期總結嘅教訓——唔好信 AI 嘅完成報告——抽查關鍵文件:
src/modules/text-summary/目錄下存在index.tsx、stopwords.ts、text-utils.ts、extract-summary.ts+ 4 個測試文件 ✅router/index.tsx冇變更 ✅組件唔係 PlaceholderPage,而係真正嘅工具頁面 ✅
5. Verify:記錄驗證過程(呢期重點)
呢期 verify 係改造後嘅第一次執行。新增嘅第 4 維度Task Granularity係咪生效?呢個係驗證改造效果嘅關鍵環節。
執行 verify
**AI**:Using change: `implement-text-summary` (schema: with-review)
Progress: 0/24 tasks complete
Let me read the context files first.
AI 讀取咗 4 個工件 + 4 個實現文件之後,輸出咗驗證報告:
**AI**:## Verification Report: implement-text-summary
### Summary
| Dimension | Status |
|--------------|---------------------|
| Completeness | 24/24 tasks complete |
| Correctness | 5/5 requirements implemented |
| Coherence | Design followed |
### Completeness ✅
**Task Completion**: 24/24 tasks marked complete
- All task checkboxes verified as checked in tasks.md
- All files created and committed as specified
**Spec Coverage**: 5/5 requirements implemented
| Requirement | Implementation |
|-------------|----------------|
| text-summary-ui-layout | `index.tsx` - h1, textarea, radio controls, result area |
| text-summary-summary-algorithm | `extract-summary.ts` - position + frequency scoring |
| text-summary-empty-input-handling | `index.tsx:12-14` - empty input handling |
| text-summary-stopwords | `stopwords.ts` - 35 stopwords, `extract-summary.ts:18` filtering |
| text-summary-lazy-loading | `router/index.tsx` - React.lazy + Suspense fallback |
### Correctness ⚠️
**Issues Found:**
1. **WARNING** - Test may not catch case sensitivity bug
- **Scenario**: `case-insensitive` test in `extract-summary.test.ts:208-213`
- **Problem**: The test asserts `toContain('Apple')` but implementation uses `toLowerCase()` internally
- **Recommendation**: The test passes because `vi.mock()` returns fixed string. Acceptable for now.
2. **WARNING** - Input area height specification
- **Scenario**: `input-area` spec says "最小 5 行"
- **Finding**: Implementation uses `min-h-[120px]` which approximates 5 rows but is not exactly 5 rows
- **Recommendation**: Acceptable - CSS cannot precisely guarantee "5 rows" without explicit line-height.
### Coherence ✅
**Design Adherence**:
- Decision: "單文件組件 + Tailwind classes" - ✅ (index.tsx is single file with Tailwind)
- Decision: "useState 本地管理" - ✅ (inputText, summary, length all useState)
- Decision: "基於位置和詞頻的混合策略" - ✅ (extract-summary.ts has both scoring mechanisms)
### Final Assessment
**No critical issues.** 2 warning(s) to consider.
**All checks passed. Ready for archive.**
第 4 維度:Task Granularity
呢個係呢期改造嘅核心驗證點。之前嘅 verify 得三維度報告——Completeness(完整性)、Correctness(正確性)、Coherence(一致性)。新增嘅 Task Granularity 維度應該包括以下四項檢查:
### 任務 N + 涉及文件 | |
但實際執行後發現:verify 報告仍然係 3 維度。
睇上面嘅實際輸出——Summary 表格得三行:Completeness、Correctness、Coherence。Task Granularity 維度完全缺失。
點解冇生效? 三個原因:
Skill 指導 ≠ 硬性約束。SKILL.md 係 AI 嘅參考文本,唔係編譯器。AI 見到 "verify" 就激活咗前 5 期訓練出嚟嘅 3 維度報告模式——習慣比 instruction 更強。
SKILL.md 內部矛盾。L46 寫 "four dimensions",但 L163 Graceful Degradation 段仍然寫 "verify all three dimensions"。兩處衝突時 AI 傾向於更具體嘅執行指引(Graceful Degradation 係出錯時嘅兜底策略,AI 會優先參考)。
缺少輸出格式約束。SKILL.md 只係喺 step 4 描述咗維度,但 Final Assessment 模板同 Graceful Degradation 都冇更新。AI 按模板輸出時返咗去 3 維度。
呢個發現比「改造成功」更有價值。改 instruction 解決內容問題,改 template 解決格式問題——但改 SKILL.md 解決唔到行為慣性問題。後續需要同步更新 Graceful Degradation 段同 Final Assessment 模板,先可以形成完整嘅約束閉環。
講白啲:我哋喺 SKILL.md 入面加咗第 4 維度嘅描述,但唔記得將其他引用 "three dimensions" 嘅地方都改成 "four dimensions"。AI 讀到矛盾信息時,揀咗佢更熟悉嘅舊模式。呢個同第 5 期改 template 嘅教訓一樣——改一處唔夠,要將所有關聯嘅地方都同步改。
Build 輸出
執行 npm run build,構建通過:
> tsc -b && vite build
vite v8.0.12 building client environment for production...
✓ 1757 modules transformed.
dist/index.html 0.46 kB │ gzip: 0.30 kB
dist/assets/index-8W2XdHU1.css 20.28 kB │ gzip: 4.75 kB
dist/assets/text-summary-DP5GMCjc.js 2.83 kB │ gzip: 1.39 kB
dist/assets/index-CDtkiTKN.js 295.14 kB │ gzip: 94.33 kB
✓ built in 190ms
注意 text-summary-DP5GMCjc.js(2.83 kB)係 text-summary 嘅獨立 chunk——代碼分割生效咗。React.lazy 嘅動態 import 喺構建時被 Vite 拆成咗獨立文件。
但 build 通過唔代表頁面可以正常渲染。lazy 加載喺構建時唔檢查 export 格式——呢個坑,瀏覽器檢查時踩到咗。
測試輸出
執行 npm test,52 個測試全部通過:
✓ src/modules/text-summary/text-utils.test.ts (4 tests) 5ms
✓ src/tool-registry/catalog.test.ts (7 tests) 10ms
✓ src/modules/text-summary/extract-summary.test.ts (8 tests) 3ms
✓ src/modules/text-summary/stopwords.test.ts (4 tests) 5ms
✓ src/layout/TopNav.test.tsx (3 tests) 30ms
✓ src/layout/Layout.test.tsx (2 tests) 41ms
✓ src/modules/text-summary/index.test.tsx (6 tests) 116ms
✓ src/app/views/PlaceholderPage.test.tsx (5 tests) 101ms
✓ src/app/views/Home.test.tsx (10 tests) 222ms
✓ src/router/index.test.tsx (3 tests) 2ms
Test Files 10 passed (10)
Tests 52 passed (52)
52 個測試全部通過,包括 text-summary 嘅 22 個新測試。但注意 index.test.tsx 嘅測試 mock 咗 extractSummary(vi.mock),所以測試通過唔代表 lazy 加載邏輯正確——又係 build 同 test 都通過但運行時有 bug 嘅典型案例。
瀏覽器檢查
verify 聲稱 All checks passed. Ready for archive. build 通過,52 個測試通過。一切睇落好完美。
然後打開瀏覽器訪問 http://localhost:5173/tools/text-summary。

圖 1:頁面顯示 Suspense fallback 「加載中...」,工具組件因 React.lazy 加載問題未渲染
頁面顯示**「加載中...」**——Suspense fallback 永遠掛喺嗰度,工具組件冇渲染出嚟。
根因:router/index.tsx 使用 React.lazy() 加載工具組件:
const ToolComponent = lazy(() => import(`../modules/${tool.id}/index.tsx`));
React.lazy() 要求模塊使用 default export,但 apply 生成嘅 index.tsx 使用嘅係 named export:
export function TextSummaryPage() { ... } // named export
冇 default export → lazy 加載模塊後揾唔到組件 → Suspense fallback 永遠顯示。
呢個 bug verify 冇發現(verify 只做靜態代碼分析,唔做瀏覽器測試),build 亦冇發現(構建成功但 lazy 加載喺運行時先執行),52 個測試亦冇發現(測試 mock 咗 import)。只有實際打開瀏覽器先睇到問題——呢個亦係點解瀏覽器檢查唔可以省略。
修復過程:
嘗試 1:添加 export default TextSummaryPage; 到 index.tsx — 無效。模塊加載正常(網絡請求 200),但 React 嘅 Suspense 機制仍然唔解析 lazy component。
嘗試 2:將 lazy() 從組件外移到組件內,用 useMemo 包裹 — 無效。
嘗試 3(最終方案):徹底放棄 React.lazy(),改用 useEffect + import() 手動加載:
// router/index.tsx 最終修復方案
const toolModules: Record<string, () =>Promise<{ default: React.ComponentType<unknown> }>> = {
'text-summary': () =>import('/src/modules/text-summary/index.tsx'),
};
function ToolPage({ toolId }: { toolId: string }) {
const [Component, setComponent] = useState<React.ComponentType<unknown> | null>(null);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
const loader = toolModules[toolId];
if (!loader) { setError(`Tool not found: ${toolId}`); return; }
loader()
.then(mod => setComponent(() => mod.default))
.catch(e => setError(e.message));
}, [toolId]);
if (error) return <div className="p-4">Error: {error}</div>;
if (!Component) return <div className="p-4">加載中...</div>;
return <Component />;
}
React.lazy() + Vite 動態 import() + 變量路徑 ../modules/${tool.id}/index.tsx 嘅組合喺運行時存在兼容性問題。Vite 可以正確解析模塊(網絡請求 200),模塊有正確嘅 default export,但 React 19 嘅 Suspense 機制始終唔解析 lazy component。可能與 React 19 + createBrowserRouter + Vite HMR 嘅交互有關。最終用 useEffect + import() 繞過了 React.lazy() 機制。
修復後,頁面正常渲染:

圖 2:放棄 React.lazy 改用 useEffect + import() 後,頁面正常渲染 - 標題「文本摘要」、文本輸入區、長度選擇器(短/中/長)、「生成摘要」按鈕
修復 commit:ba8a080 fix(router): replace React.lazy with useEffect+import for dynamic tool loading
睇下完整 git log,3 輪修復過程一目瞭然:
ba8a080 fix(router): replace React.lazy with useEffect+import for dynamic tool loading
6dd77ea fix(router): move lazy import inside LazyTool with useMemo
13e76b1 fix(text-summary): add default export for React.lazy
d387013 chore: mark tasks.md complete for text-summary change
2174e28 feat(text-summary): implement TextSummaryPage component
9bbef73 feat(text-summary): implement extract summary algorithm
bffbb6d feat(text-summary): add sentence splitting utility
e06c780 feat(text-summary): add stopwords list for text summarization
前 4 條係原始 apply 嘅 commit,後 3 條係修復 React.lazy 嘅 3 輪嘗試。
6. Archive:歸檔
**AI**:Delta specs analysis:
- Change has `specs/text-summary/spec.md` (ADDED requirements for text-summary)
- Main specs has `specs/text-summary/spec.md` - already synced during apply
- No difference detected
Specs are already synced. Proceeding with archive.
注意:archive 階段 AI 冇加載 Skill 工具,而係直接透過 Bash 命令執行歸檔操作。session 日誌中得 3 次 Skill 調用(propose/apply/verify),archive 係 AI 直接 mv 命令完成嘅。
**AI**:## Archive Complete
**Change:** implement-text-summary
**Schema:** with-review
**Archived to:** `openspec/changes/archive/2026-05-19-implement-text-summary/`
**Specs:** Already synced (main specs/text-summary/spec.md exists from apply)
**All artifacts complete. All tasks complete.**
Summary:
- 5 artifacts (proposal, design, specs, review, tasks) all done
- 24/24 tasks completed with TDD workflow
- Delta specs synced to main specs during apply
- Implementation: `src/modules/text-summary/` with 4 source files + 4 test files (22 tests passing)
歸檔後確認:
ls openspec/changes/
輸出得 archive/ 目錄,變更文件已全部移走 ✅
7. Git Push:提交到 GitHub
**用戶**:請幫我推送到遠程倉庫
**AI**:無法連接到 GitHub(443端口超時)。
**5 個 commits 已準備好待推送:**
- `e06c780` - stopwords list
- `bffbb6d` - sentence splitting utility
- `9bbef73` - extract summary algorithm
- `2174e28` - TextSummaryPage component
- `d387013` - tasks.md complete
**待推送的文件變更:**
- `openspec/changes/archive/` - 兩個已歸檔的變更
- `openspec/specs/text-summary/` - 同步後的主 specs
- `openspec/schemas/` - schema 文件修改
- `openspec/changes/ui-visual-polish/` - 清理遺留目錄
AI 第二次嘗試推送,仍然 443 超時。最終由用戶手動執行 git push 完成推送。
呢個插曲說明 AI 編程助手喺網絡環境唔穩定時仍有侷限性。代碼變更本身冇問題,push 嘅 commit 都準備好曬,只係網絡連接呢個 AI 控制唔到嘅因素卡住咗。
8. 回顧:呢期學到咗啲乜
三層改造形成閉環(但 verify 只閉環咗一半)
呢期同時驗證咗三層改造嘅效果:
Template(第 5 期改嘅):管格式。 ### 任務 N+ 涉及文件 + TDD 五步 ✅ 繼續生效Review instruction(呢期改嘅):管前置指導。從審查任務粒度變為提供拆分方向 ✅ 生效 Verify(呢期改嘅):管後置審查。新增 Task Granularity 維度 ❌ 未生效
三層形成閉環——但只閉環咗 2/3。Template 同 Review 各司其職,Verify 嘅行為改造冇跟上。原因之前分析過:SKILL.md 內部有矛盾,AI 按習慣行咗 3 維度嘅老路。
review 從擺設變成有用
維度 5 係擺設,直接講咗。佢聲稱審查 tasks 粒度,但 review 生成時 tasks.md 仲未存在——一係寫空話,一係做預測。改成 4 維度後,review 唔再扮可以審查 tasks,而係踏踏實實噉基於 proposal/specs/design 提供拆分方向。呢個方向建議 tasks 生成時可以參照——雖然唔係硬性約束,但至少有參考價值。
verify 改造未生效嘅教訓
改 SKILL.md 唔等於改行為。SKILL.md 係俾 AI 睇嘅參考文本,唔係編譯器。要確保 AI 真係跟新規則執行,要做到三點:
指令一致:SKILL.md 內部唔可以有矛盾(L46 寫 4 維度,L163 寫 3 維度,AI 會揀更具體嗰個) 模板同步:Final Assessment 同 Graceful Degradation 都要更新,否則 AI 按舊模板輸出 行為慣性:前 5 期嘅 3 維度模式已經被 AI「記住」咗,光改 instruction 未必可以覆蓋習慣
呢個同第 5 期改 template 嘅教訓一脈相承:改一處唔夠,要將所有關聯嘅地方都同步改。
build + test 全過 ≠ 運行時冇問題
呢期最戲劇性嘅一幕:build 通過、52 個測試通過、verify 話 All checks passed——結果打開瀏覽器一睇,頁面永遠顯示「加載中...」。
React.lazy 嘅 named export vs default export 問題,靜態分析睇唔出嚟,構建睇唔出嚟,單元測試睇唔出嚟。只有運行時、喺瀏覽器入面、行一次真實嘅 lazy 加載路徑先可以發現。呢個教訓再次印證咗第 3 期總結嗰句話:唔好信 AI 嘅完成報告。
致謝讀者
呢個問題係讀者發現嘅。一個人埋頭做咗 5 期,改 instruction、改 template、調參數,review 維度 5 嘅職責錯位一直冇注意到。因為每次 review 都成功輸出咗維度 5 嘅內容——睇落好似正常運作。但仔細諗下就知道唔啱:review 連 tasks.md 都睇唔到,佢喺審查啲乜?
這種睇落好似正常運作但職責錯咗嘅問題,比完全唔運作更難發現。完全唔運作你會即刻注意到——維度 5 係空嘅,或者標咗錯誤。但佢每次都有輸出,每次內容都有參考價值,所以你唔會覺得有問題。直到有人追問:等等,review 生成嘅時候 tasks.md 喺邊?
開源社區嘅價值就喺呢度。一個人嘅盲區,係另一個人嘅發現。讀者花時間讀咗源碼、讀咗 review.md 嘅實際輸出、對照咗依賴鏈,發現咗呢個職責錯位。呢種貢獻比寫代碼更有價值——因為佢修正嘅係方法論層面嘅錯誤。
2/8 法則嘅進一步修正
第 5 期講嘅 2/8 法則係:改 instruction 佔 20% 投入獲 80% 質量提升,改 template 補上結構性格式問題。呢期再加一層:review/verify 嘅職責劃分亦係關鍵配置。 review 做前置指導、verify 做後置審查,呢個邊界劃唔清,兩個工具都會打折扣。
但呢期嘅發現亦修正咗 2/8 法則本身——改咗 instruction 同 SKILL.md(投入咗 80%),verify 嘅行為改造仍然冇生效(只收獲咗 20% 嘅效果)。剩低嘅 80% 效果需要同步更新模板同兜底策略。改配置唔只係改一個地方,而係改一整條鏈路。
9. 預告
第 7 期繼續工具迭代。text-summary 係第一個上線嘅工具組件,後面嘅工具(JSON 格式化、代碼解釋等)可以複用呢期建立嘅組件模式。同時關注一個新問題:多個工具共享相似嘅頁面結構(輸入區、操作按鈕、輸出區)時,係咪需要抽象出通用組件模板。如果需要,又應該由邊個去定義呢個模板——template 定 instruction?
仲有呢期遺留嘅技術債:
React.lazy 兼容性:useEffect + import() 係臨時方案,後續工具多咗需要更優雅嘅動態加載策略
相關資源
GitHub:https://github.com/shuge-x/shuge-ai-toolbox
好啦,多謝你睇我嘅文章,如果鍾意可以點讚轉發俾需要嘅朋友,我哋下一期再見!敬請期待!
🚩 2026 年「術哥無界」系列實戰文檔 X 篇原創計劃 第 117 篇,OpenSpec 項目實戰「2026」系列第 6 篇
大家好,歡迎來到 術哥無界 | ShugeX | 運維有術。
我是術哥,一名專注於 AI 編程、AI 智能體、Agent Skills、MCP、雲原生、AIOps、Milvus 向量數據庫的技術實踐者與開源佈道者!
Talk is cheap, let's explore。無界探索,有術而行。

封面:review 拆分 + verify 增強 + 實現第一個工具
說明:本文內容基於 OpenSpec(Fission-AI/OpenSpec)v1.3.1 和 React 19 + TypeScript + Tailwind CSS v4 的實際操作記錄整理而成。文中的配置模板和參數建議僅供參考,實際效果請以你的業務數據和環境測試結果為準。如果有實際使用經驗,歡迎在評論區分享交流。
1. 讀者發現了一個問題
第 5 期改了 template,tasks.md 格式終於生效。標題從 ## 1. 變成了 ### 任務 N:,涉及文件列表成了標配,代碼塊數量創下系列新高。
但這一期要說的,不是 template 的後續優化,而是另一個問題——一位讀者發現的。
這位讀者仔細讀了第 5 期歸檔的 review.md,注意到了一個細節:review 維度 5 叫任務粒度,聲稱審查 tasks.md 的每個 step 是否達到 2-5 分鐘粒度。但 review 在 tasks 之前生成——依賴鏈是 proposal → specs → design → review → tasks——review 生成時 tasks.md 還不存在。
感謝這位讀者。 這種細緻的源碼閲讀非常有價值。一個人的盲區,往往是另一個人的發現。開源項目的 review instruction 寫了審查 tasks.md 粒度,但沒人質疑過 review 生成時 tasks 到底存不存在——包括我自己。
看看實際發生了什麼。引用第 5 期歸檔的 openspec/changes/archive/2026-05-17-ui-visual-polish/review.md 中維度 5 的內容:
## 5. 任務粒度
**狀態**:⚠️ 警告
**發現**:
- 視覺類任務難以按 TDD 節奏嚴格拆分
- 部分任務包含多個子步驟,需要拆分
- CSS 變量重建如果一次性替換所有 primary 變量,涉及文件多但邏輯簡單,可合併為一個大步驟
**建議**:
- CSS 變量更新(tokens.css)→ 合併為一個任務
- Home.tsx 圖標 → 拆分為:Category 圖標任務 + Badge 圖標任務
- Tab 滑動指示器 → 拆分為:JS 計算邏輯任務 + CSS transition 樣式任務
- Stagger 動畫 → 拆分為:keyframes 定義任務 + nth-child 動畫應用任務
內容看起來挺充實。但問題是——這時候 tasks.md 還沒生成。 維度 5 的建議是基於 design 和 proposal 做的預測,不是對實際 tasks.md 的審查。它說視覺類任務難以按 TDD 節奏拆分,但 tasks.md 還不存在,它怎麼知道任務會怎麼拆?
根因在依賴鏈。逐字引用 openspec/schemas/with-review/schema.yaml 中 review 的 requires:
- id: review
requires:
- proposal
- specs
- design
review 依賴 proposal、specs、design,不依賴 tasks。維度 5 的定位從根本上就是錯的——它聲稱審查 tasks 粒度,但 tasks 還沒出生。
說實話,這個問題藏了 5 期沒人發現,是因為維度 5 每次看起來都在正常工作。它確實輸出了建議、給出了 ⚠️ 警告,內容也不是瞎寫的——基於 design 和 proposal 的分析,建議往往還有參考價值。但有參考價值的預測和對實際產物的審查是兩回事。前者是前置指導,後者是後置驗證。把後置驗證塞到前置位置,職責就亂了。
本期做三件事:
review instruction 改造:5 維度 → 4 維度,去掉維度 5,改為為 tasks 提供拆分方向 verify SKILL.md 增強:3 維度 → 4 維度,新增 Task Granularity 維度 實現第一個工具組件(text-summary),驗證全部改造效果
三個改造同時驗證:template(第 5 期改的)+ review instruction(本期改的)+ verify(本期改的)。
完整流程不變:

2. 改造過程:review 和 verify 的職責重劃分
問題本質
review 和 verify 的職責邊界不清晰。review 聲稱要審查 tasks 粒度,但它生成時 tasks 還不存在;verify 應該做粒度檢查,但它只有 3 個宏觀維度——完整性、正確性、一致性。真正的任務粒度審查,兩邊都沒做。
改造前現狀
先看 review instruction。逐字引用 openspec/schemas/with-review/schema.yaml 中 review 的 instruction 關鍵段落:
instruction: |
從五個維度審查所有工件的完整性和質量。
審查維度:
1.**邊界條件**:Spec是否覆蓋null、空值、越界等異常場景
2.**回滾方案**:數據庫變更是否包含回滾策略
3.**測試覆蓋**:Design是否明確了需要測試的場景和用例
4.**向後兼容**:是否分析了現有接口和數據的兼容性影響
5.**任務粒度**(最重要):tasks.md的每個step是否達到2-5分鐘粒度、是否附帶完整代碼、是否有佔位符
對維度5的檢查標準:
-✅通過:每個step有完整代碼塊+運行命令+預期輸出
-⚠️警告:部分step缺少代碼或命令
-❌失敗:存在佔位符(TBD/TODO)或step粒度超過10分鐘
維度 5 標註了(最重要),檢查標準列得相當詳細。但這一切都建立在一個前提上:review 能看到 tasks.md。實際上它看不到。
再看 verify SKILL.md。逐字引用 .claude/skills/openspec-verify-change/SKILL.md 中維度定義的段落:
4. **Initialize verification report structure**
Create a report structure with three dimensions:
- **Completeness**: Track tasks and spec coverage
- **Correctness**: Track requirement implementation and scenario coverage
- **Coherence**: Track design adherence and pattern consistency
3 個維度,全是宏觀層面的檢查。任務格式對不對、TDD 合不合規、代碼完不完整、粒度評估準不準確——verify 都不管。
最後看 review 的模板文件。逐字引用 openspec/schemas/with-review/templates/review.md:
## 5. 任務粒度
**狀態**:
**發現**:
**建議**:
---
## 整體評估
**對 tasks.md 的關鍵建議**:
模板裏給維度 5 留了空位,整體評估下面也專門有對 tasks.md 的關鍵建議。這個模板強化了 review 應該指導 tasks 拆分的定位——定位本身沒問題,但維度 5 叫任務粒度就過了,叫拆分方向才準確。
思考過程
說直白點:review 在 tasks 之前,應該做前置指導——基於 proposal、specs、design 預判任務拆分方向。verify 在 apply 之後,這時 tasks.md 已經存在、代碼也寫完了,才是做後置審查的正確位置。
之前的配置搞反了:review 聲稱審查 tasks 粒度(前置位置做後置的事),verify 反而沒有粒度維度(後置位置沒做後置的事)。兩邊都打折扣。
換個角度想:review 的 4 個維度(邊界條件、回滾方案、測試覆蓋、向後兼容)都是在審查 proposal/specs/design 這三個它能看到的工件。唯獨維度 5 跳出了這個範圍,去審查一個它看不到的工件。這是唯一一個越界的維度。去掉維度 5,review 的職責就乾淨了——只審查它依賴的三個工件,順便為後續的 tasks 拆分提供方向建議。
那 tasks 粒度審查該放哪?verify。verify 在 apply 之後執行,tasks.md 已經存在,代碼已經寫完。這時候檢查任務格式、TDD 合規性、代碼完整性、粒度評估,每一項都有實際內容可查。
動手改造
三個文件,逐個改。
改造 1:schema.yaml 中 review 的 instruction
templates/review-instruction.yaml 已經更新為 4 維度版本,直接用它的內容替換 schema.yaml 中 review 的 instruction。逐字引用改造後的 instruction:
instruction: |
從四個維度審查 proposal、specs 和 design 的完整性和質量。
為即將生成的 tasks.md 提供拆分方向和優先級建議。
審查維度:
1.**邊界條件**:Spec是否覆蓋null、空值、越界等異常場景
2.**回滾方案**:變更是否包含回滾策略(數據庫遷移、API變更等)
3.**測試覆蓋**:Design是否明確了需要測試的場景和用例
4.**向後兼容**:是否分析了現有接口和數據的兼容性影響
輸出格式:
-每個維度給出明確狀態:✅通過/⚠️警告/❌失敗
-警告和失敗必須給出具體建議
-給出整體評估+針對tasks拆分方向的具體建議(任務分組建議、優先級排序、粒度標準)
注意:本審查在tasks.md生成之前執行。不要聲稱審查了tasks.md,而是基於specs和design預判任務拆分方向。任務粒度的實際驗證由/opsx:verify在apply之後執行。
關鍵變化三個:
維度數從 5 減到 4,去掉了任務粒度 加了明確聲明:本審查在 tasks.md 生成之前執行。不要聲稱審查了 tasks.md 整體評估從審查 tasks 粒度變為提供拆分方向建議
改造 2:verify SKILL.md
在 .claude/skills/openspec-verify-change/SKILL.md 的第 4 步 Initialize verification report structure 中,從 three dimensions 改為 four dimensions,新增第 4 維度 Task Granularity:
- **Task Granularity**: Track task format compliance, TDD adherence, code completeness, and granularity assessment
- Task format: Verify each task uses `### 任務 N` with file list and TDD five-step structure
- TDD compliance: Verify each task follows RED/GREEN/REFACTOR rhythm
- Code completeness: Verify no TBD/TODO placeholders exist in any task
- Granularity assessment: Evaluate if each task's estimated effort falls within 2-5 minute range
改造 3:review.md template 同步更新
openspec/schemas/with-review/templates/review.md 去掉維度 5 的段落,保留 4 個維度 + 整體評估(含拆分方向建議)。改造後的模板:
## 整體評估
**拆分方向建議**:
**優先級排序**:
**粒度標準**:
從對 tasks.md 的關鍵建議變為拆分方向建議 + 優先級排序 + 粒度標準——定位從前置審查變為前置指導。
改造後對比
這個改造不依賴具體項目。不管你做前端還是後端,review 在 tasks 之前生成、verify 在 apply 之後執行這個依賴關係不會變。職責劃清了,兩個工具才能各司其職。
3. Propose:記錄工件產出過程
需求明確,直接 Propose,不走 Explore。
執行過程
**AI**:What change do you want to work on? Please describe what you want to build or fix.
**用戶**:本期做一個 implement-text-summary 變更。實現第一個工具組件——文本摘要工具。catalog.ts 中已註冊 id 為 text-summary,category 為 文本處理,stage 為 active。需要創建完整的工具頁面組件、路由配置和測試。純前端實現,不依賴外部 API。
**AI**:Using **openspec:propose** to create the `implement-text-summary` change.
Let me first explore the existing catalog and project structure to understand the context.
AI 探索項目結構後,生成了 5 個工件:
**AI**:---
**Change created: `implement-text-summary`**
Location: `openspec/changes/implement-text-summary/`
**Artifacts created:**
| Artifact | Description |
|----------|-------------|
| `proposal.md` | 變更提案:文本摘要工具,純前端規則提取實現 |
| `design.md` | 技術設計:摘要算法策略、組件結構、狀態管理方案 |
| `specs/text-summary/spec.md` | 詳細規格:5 個 requirement,15 個 scenario |
| `review.md` | 四維審查:邊界條件✅、回滾方案✅、測試覆蓋⚠️、向後兼容✅ |
| `tasks.md` | 5 個任務組,覆蓋停用詞→句子提取→摘要算法→UI組件→完整驗證 |
**任務分組:**
1. 停用詞列表 `stopwords.ts`
2. 句子提取工具 `text-utils.ts`
3. 摘要核心算法 `extract-summary.ts`(核心)
4. TextSummaryPage UI 組件 `index.tsx`
**All artifacts created! Ready for implementation.**
Run `/opsx:apply` to start implementing.
注意 review 的 AI 回覆中明確寫了四維審查——說明改造後的 4 維度 instruction 生效了。維度 3 測試覆蓋給了 ⚠️ 警告,建議將工具函數測試和 UI 組件測試分兩類處理。
工件產出
5 個工件按實際產出順序:proposal.md → specs/ → design.md → review.md → tasks.md。
選擇 text-summary 作為第一個工具組件,有三個原因:
純前端:不依賴外部 API,不需要 API Key,不涉及網絡請求。實現成本低,驗證改造效果更聚焦 catalog 已註冊: catalog.ts中 text-summary 的 stage 已經是active,路由已通過getTools()+tools.map()動態生成,不需要改註冊表功能邊界清晰:輸入文本 → 輸出摘要,交互簡單,適合作為工具組件的 baseline 模式
review.md 重點觀察
改為 4 維度後,review 不再出現維度 5任務粒度。整體評估部分給出了拆分方向建議:先搭組件骨架和路由、再實現核心邏輯、然後做 UI 層、最後補測試和邊界處理。這正是前置指導該做的事——基於 proposal/specs/design 預判拆分方向,而不是聲稱審查了不存在的 tasks.md。
tasks.md 重點觀察
template(第 5 期改的)+ 新 review 的雙重效果驗證。tasks.md 格式確認使用 ### 任務 N + 涉及文件 + TDD 五步結構,任務拆分參照了 review 的方向建議——5 個任務組分別對應停用詞、句子提取、摘要算法、UI 組件、完整驗證。
逐字引用 src/tool-registry/catalog.ts 中 text-summary 的註冊信息:
{
id: 'text-summary',
name: '文本摘要',
route: '/tools/text-summary',
category: '文本處理',
description: '快速提取長文本的核心觀點',
stage: 'active',
}
路由已通過 catalog.ts 的 getTools() + tools.map() 動態註冊為 /tools/text-summary,category 是文本處理,stage 是 active。proposal.md 也明確聲明瞭:路由已通過 catalog.ts 動態註冊,無需修改 router/index.tsx。
4. Apply:記錄執行過程
執行過程
**AI**:Using change: `implement-text-summary` (schema: with-review)
Progress: 0/24 tasks complete
Let me read the context files first.
AI 逐任務自動執行,本期 5 個任務共 24 個 step 全部自動完成,無用戶中斷。
**AI**:## Implementation Complete
**Change:**`implement-text-summary`
**Schema:** with-review
**Progress:** 5/5 tasks complete ✓
### Completed This Session
| Task | Files Created |
|------|--------------|
| 1. 停用詞列表 | `stopwords.ts`, `stopwords.test.ts` |
| 2. 句子提取工具 | `text-utils.ts`, `text-utils.test.ts` |
| 3. 摘要核心算法 | `extract-summary.ts`, `extract-summary.test.ts` |
| 4. TextSummaryPage 組件 | `index.tsx`, `index.test.tsx` |
| 5. 驗證完整功能 | All 22 tests passing |
### Test Results
```
src/modules/text-summary/ → 4 test files, 22 tests passed
src/router/index.test.tsx → 3 tests passed
```
AI 的完成報告很漂亮——5/5 tasks complete ✓,22 tests passed。但這份報告背後藏着一個 bug,後面瀏覽器檢查時再說。
實際涉及的變更
與 apply 之前預期的不同,實際變更如下:
新建:工具組件頁面( src/modules/text-summary/index.tsx)新建:摘要算法等工具函數( src/modules/text-summary/下的stopwords.ts、text-utils.ts、extract-summary.ts)新建:對應的 4 個測試文件 路由配置無需修改—— router/index.tsx已通過catalog.ts的getTools()+lazy(() => import('../modules/${tool.id}/index.tsx'))動態生成路由,text-summary 在 catalog.ts 中註冊為active後路由自動生效
apply 後 git diff 確認 router/index.tsx 無任何變更,和 proposal.md 的聲明一致。
Apply 完成後抽查
按第 3 期總結的教訓——不要信 AI 的完成報告——抽查關鍵文件:
src/modules/text-summary/目錄下存在index.tsx、stopwords.ts、text-utils.ts、extract-summary.ts+ 4 個測試文件 ✅router/index.tsx無變更 ✅組件不是 PlaceholderPage,而是真正的工具頁面 ✅
5. Verify:記錄驗證過程(本期重點)
本期 verify 是改造後的第一次執行。新增的第 4 維度Task Granularity是否生效?這是驗證改造效果的關鍵環節。
執行 verify
**AI**:Using change: `implement-text-summary` (schema: with-review)
Progress: 0/24 tasks complete
Let me read the context files first.
AI 讀取了 4 個工件 + 4 個實現文件後,輸出了驗證報告:
**AI**:## Verification Report: implement-text-summary
### Summary
| Dimension | Status |
|--------------|---------------------|
| Completeness | 24/24 tasks complete |
| Correctness | 5/5 requirements implemented |
| Coherence | Design followed |
### Completeness ✅
**Task Completion**: 24/24 tasks marked complete
- All task checkboxes verified as checked in tasks.md
- All files created and committed as specified
**Spec Coverage**: 5/5 requirements implemented
| Requirement | Implementation |
|-------------|----------------|
| text-summary-ui-layout | `index.tsx` - h1, textarea, radio controls, result area |
| text-summary-summary-algorithm | `extract-summary.ts` - position + frequency scoring |
| text-summary-empty-input-handling | `index.tsx:12-14` - empty input handling |
| text-summary-stopwords | `stopwords.ts` - 35 stopwords, `extract-summary.ts:18` filtering |
| text-summary-lazy-loading | `router/index.tsx` - React.lazy + Suspense fallback |
### Correctness ⚠️
**Issues Found:**
1. **WARNING** - Test may not catch case sensitivity bug
- **Scenario**: `case-insensitive` test in `extract-summary.test.ts:208-213`
- **Problem**: The test asserts `toContain('Apple')` but implementation uses `toLowerCase()` internally
- **Recommendation**: The test passes because `vi.mock()` returns fixed string. Acceptable for now.
2. **WARNING** - Input area height specification
- **Scenario**: `input-area` spec says "最小 5 行"
- **Finding**: Implementation uses `min-h-[120px]` which approximates 5 rows but is not exactly 5 rows
- **Recommendation**: Acceptable - CSS cannot precisely guarantee "5 rows" without explicit line-height.
### Coherence ✅
**Design Adherence**:
- Decision: "單文件組件 + Tailwind classes" - ✅ (index.tsx is single file with Tailwind)
- Decision: "useState 本地管理" - ✅ (inputText, summary, length all useState)
- Decision: "基於位置和詞頻的混合策略" - ✅ (extract-summary.ts has both scoring mechanisms)
### Final Assessment
**No critical issues.** 2 warning(s) to consider.
**All checks passed. Ready for archive.**
第 4 維度:Task Granularity
這是本期改造的核心驗證點。之前的 verify 只有三維度報告——Completeness(完整性)、Correctness(正確性)、Coherence(一致性)。新增的 Task Granularity 維度應該包含以下四項檢查:
### 任務 N + 涉及文件 | |
但實際執行後發現:verify 報告仍然是 3 維度。
看上面的實際輸出——Summary 表格只有三行:Completeness、Correctness、Coherence。Task Granularity 維度完全缺失。
為什麼沒生效? 三個原因:
Skill 指導 ≠ 硬性約束。SKILL.md 是 AI 的參考文本,不是編譯器。AI 看到 "verify" 就激活了前 5 期訓練出的 3 維度報告模式——習慣比 instruction 更強。
SKILL.md 內部矛盾。L46 寫 "four dimensions",但 L163 Graceful Degradation 段仍然寫 "verify all three dimensions"。兩處衝突時 AI 傾向於更具體的執行指引(Graceful Degradation 是出錯時的兜底策略,AI 會優先參考)。
缺少輸出格式約束。SKILL.md 只在 step 4 描述了維度,但 Final Assessment 模板和 Graceful Degradation 都沒更新。AI 按模板輸出時回到了 3 維度。
這個發現比"改造成功"更有價值。改 instruction 解決內容問題,改 template 解決格式問題——但改 SKILL.md 解決不了行為慣性問題。後續需要同步更新 Graceful Degradation 段和 Final Assessment 模板,才能形成完整的約束閉環。
說直白點:我們在 SKILL.md 里加了第 4 維度的描述,但忘了把其他引用 "three dimensions" 的地方也改成 "four dimensions"。AI 讀到矛盾信息時,選擇了它更熟悉的舊模式。這和第 5 期改 template 的教訓一樣——改一處不夠,得把所有關聯的地方都同步改。
Build 輸出
執行 npm run build,構建通過:
> tsc -b && vite build
vite v8.0.12 building client environment for production...
✓ 1757 modules transformed.
dist/index.html 0.46 kB │ gzip: 0.30 kB
dist/assets/index-8W2XdHU1.css 20.28 kB │ gzip: 4.75 kB
dist/assets/text-summary-DP5GMCjc.js 2.83 kB │ gzip: 1.39 kB
dist/assets/index-CDtkiTKN.js 295.14 kB │ gzip: 94.33 kB
✓ built in 190ms
注意 text-summary-DP5GMCjc.js(2.83 kB)是 text-summary 的獨立 chunk——代碼分割生效了。React.lazy 的動態 import 在構建時被 Vite 拆成了獨立文件。
但 build 通過不代表頁面能正常渲染。lazy 加載在構建時不檢查 export 格式——這個坑,瀏覽器檢查時踩到了。
測試輸出
執行 npm test,52 個測試全部通過:
✓ src/modules/text-summary/text-utils.test.ts (4 tests) 5ms
✓ src/tool-registry/catalog.test.ts (7 tests) 10ms
✓ src/modules/text-summary/extract-summary.test.ts (8 tests) 3ms
✓ src/modules/text-summary/stopwords.test.ts (4 tests) 5ms
✓ src/layout/TopNav.test.tsx (3 tests) 30ms
✓ src/layout/Layout.test.tsx (2 tests) 41ms
✓ src/modules/text-summary/index.test.tsx (6 tests) 116ms
✓ src/app/views/PlaceholderPage.test.tsx (5 tests) 101ms
✓ src/app/views/Home.test.tsx (10 tests) 222ms
✓ src/router/index.test.tsx (3 tests) 2ms
Test Files 10 passed (10)
Tests 52 passed (52)
52 個測試全部通過,包含 text-summary 的 22 個新測試。但注意 index.test.tsx 的測試 mock 了 extractSummary(vi.mock),所以測試通過不代表 lazy 加載邏輯正確——又是 build 和 test 都通過但運行時有 bug 的典型案例。
瀏覽器檢查
verify 聲稱 All checks passed. Ready for archive. build 通過,52 個測試通過。一切看起來很完美。
然後打開瀏覽器訪問 http://localhost:5173/tools/text-summary。

圖 1:頁面顯示 Suspense fallback "加載中...",工具組件因 React.lazy 加載問題未渲染
頁面顯示**"加載中..."**——Suspense fallback 永遠掛在那裏,工具組件沒有渲染出來。
根因:router/index.tsx 使用 React.lazy() 加載工具組件:
const ToolComponent = lazy(() => import(`../modules/${tool.id}/index.tsx`));
React.lazy() 要求模塊使用 default export,但 apply 生成的 index.tsx 使用的是 named export:
export function TextSummaryPage() { ... } // named export
沒有 default export → lazy 加載模塊後找不到組件 → Suspense fallback 永遠顯示。
這個 bug verify 沒發現(verify 只做靜態代碼分析,不做瀏覽器測試),build 也沒發現(構建成功但 lazy 加載在運行時才執行),52 個測試也沒發現(測試 mock 了 import)。只有實際打開瀏覽器才能看到問題——這也是為什麼瀏覽器檢查不可省略。
修復過程:
嘗試 1:添加 export default TextSummaryPage; 到 index.tsx — 無效。模塊加載正常(網絡請求 200),但 React 的 Suspense 機制仍然不解析 lazy component。
嘗試 2:將 lazy() 從組件外移到組件內,用 useMemo 包裹 — 無效。
嘗試 3(最終方案):徹底放棄 React.lazy(),改用 useEffect + import() 手動加載:
// router/index.tsx 最終修復方案
const toolModules: Record<string, () =>Promise<{ default: React.ComponentType<unknown> }>> = {
'text-summary': () =>import('/src/modules/text-summary/index.tsx'),
};
function ToolPage({ toolId }: { toolId: string }) {
const [Component, setComponent] = useState<React.ComponentType<unknown> | null>(null);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
const loader = toolModules[toolId];
if (!loader) { setError(`Tool not found: ${toolId}`); return; }
loader()
.then(mod => setComponent(() => mod.default))
.catch(e => setError(e.message));
}, [toolId]);
if (error) return <div className="p-4">Error: {error}</div>;
if (!Component) return <div className="p-4">加載中...</div>;
return <Component />;
}
React.lazy() + Vite 動態 import() + 變量路徑 ../modules/${tool.id}/index.tsx 的組合在運行時存在兼容性問題。Vite 能正確解析模塊(網絡請求 200),模塊有正確的 default export,但 React 19 的 Suspense 機制始終不解析 lazy component。可能與 React 19 + createBrowserRouter + Vite HMR 的交互有關。最終用 useEffect + import() 繞過了 React.lazy() 機制。
修復後,頁面正常渲染:

圖 2:放棄 React.lazy 改用 useEffect + import() 後,頁面正常渲染 - 標題"文本摘要"、文本輸入區、長度選擇器(短/中/長)、"生成摘要"按鈕
修復 commit:ba8a080 fix(router): replace React.lazy with useEffect+import for dynamic tool loading
看下完整 git log,3 輪修復過程一目瞭然:
ba8a080 fix(router): replace React.lazy with useEffect+import for dynamic tool loading
6dd77ea fix(router): move lazy import inside LazyTool with useMemo
13e76b1 fix(text-summary): add default export for React.lazy
d387013 chore: mark tasks.md complete for text-summary change
2174e28 feat(text-summary): implement TextSummaryPage component
9bbef73 feat(text-summary): implement extract summary algorithm
bffbb6d feat(text-summary): add sentence splitting utility
e06c780 feat(text-summary): add stopwords list for text summarization
前 4 條是原始 apply 的 commit,後 3 條是修復 React.lazy 的 3 輪嘗試。
6. Archive:歸檔
**AI**:Delta specs analysis:
- Change has `specs/text-summary/spec.md` (ADDED requirements for text-summary)
- Main specs has `specs/text-summary/spec.md` - already synced during apply
- No difference detected
Specs are already synced. Proceeding with archive.
注意:archive 階段 AI 沒有加載 Skill 工具,而是直接通過 Bash 命令執行歸檔操作。session 日誌中只有 3 次 Skill 調用(propose/apply/verify),archive 是 AI 直接 mv 命令完成的。
**AI**:## Archive Complete
**Change:** implement-text-summary
**Schema:** with-review
**Archived to:** `openspec/changes/archive/2026-05-19-implement-text-summary/`
**Specs:** Already synced (main specs/text-summary/spec.md exists from apply)
**All artifacts complete. All tasks complete.**
Summary:
- 5 artifacts (proposal, design, specs, review, tasks) all done
- 24/24 tasks completed with TDD workflow
- Delta specs synced to main specs during apply
- Implementation: `src/modules/text-summary/` with 4 source files + 4 test files (22 tests passing)
歸檔後確認:
ls openspec/changes/
輸出只有 archive/ 目錄,變更文件已全部移走 ✅
7. Git Push:提交到 GitHub
**用戶**:請幫我推送到遠程倉庫
**AI**:無法連接到 GitHub(443端口超時)。
**5 個 commits 已準備好待推送:**
- `e06c780` - stopwords list
- `bffbb6d` - sentence splitting utility
- `9bbef73` - extract summary algorithm
- `2174e28` - TextSummaryPage component
- `d387013` - tasks.md complete
**待推送的文件變更:**
- `openspec/changes/archive/` - 兩個已歸檔的變更
- `openspec/specs/text-summary/` - 同步後的主 specs
- `openspec/schemas/` - schema 文件修改
- `openspec/changes/ui-visual-polish/` - 清理遺留目錄
AI 第二次嘗試推送,仍然 443 超時。最終由用戶手工執行 git push 完成推送。
這個插曲說明 AI 編程助手在網絡環境不穩定時仍有侷限性。代碼變更本身沒問題,push 的 commit 也都準備好了,只是網絡連接這個 AI 控制不了的因素卡住了。
8. 回顧:本期學到了什麼
三層改造形成閉環(但 verify 只閉環了一半)
本期同時驗證了三層改造的效果:
Template(第 5 期改的):管格式。 ### 任務 N+ 涉及文件 + TDD 五步 ✅ 繼續生效Review instruction(本期改的):管前置指導。從審查任務粒度變為提供拆分方向 ✅ 生效 Verify(本期改的):管後置審查。新增 Task Granularity 維度 ❌ 未生效
三層形成閉環——但只閉環了 2/3。Template 和 Review 各司其職,Verify 的行為改造沒跟上。原因前面分析過了:SKILL.md 內部有矛盾,AI 按習慣走了 3 維度的老路。
review 從擺設變成有用
維度 5 是擺設,直說了。它聲稱審查 tasks 粒度,但 review 生成時 tasks.md 還不存在——要麼寫空話,要麼做預測。改成 4 維度後,review 不再假裝能審查 tasks,而是踏踏實實地基於 proposal/specs/design 提供拆分方向。這個方向建議 tasks 生成時可以參照——雖然不是硬性約束,但至少有參考價值。
verify 改造未生效的教訓
改 SKILL.md 不等於改行為。SKILL.md 是給 AI 看的參考文本,不是編譯器。要確保 AI 真的按新規則執行,得做到三點:
指令一致:SKILL.md 內部不能有矛盾(L46 寫 4 維度,L163 寫 3 維度,AI 會選更具體的那個) 模板同步:Final Assessment 和 Graceful Degradation 都要更新,否則 AI 按舊模板輸出 行為慣性:前 5 期的 3 維度模式已經被 AI "記住"了,光改 instruction 未必能覆蓋習慣
這和第 5 期改 template 的教訓一脈相承:改一處不夠,得把所有關聯的地方都同步改。
build + test 全過 ≠ 運行時沒問題
本期最戲劇性的一幕:build 通過、52 個測試通過、verify 說 All checks passed——結果打開瀏覽器一看,頁面永遠顯示"加載中..."。
React.lazy 的 named export vs default export 問題,靜態分析看不出來,構建看不出來,單元測試看不出來。只有運行時、在瀏覽器裏、走一遍真實的 lazy 加載路徑才能發現。這個教訓再次印證了第 3 期總結的那句話:不要信 AI 的完成報告。
致謝讀者
這個問題是讀者發現的。一個人埋頭做了 5 期,改 instruction、改 template、調參數,review 維度 5 的職責錯位一直沒注意到。因為每次 review 都成功輸出了維度 5 的內容——看起來像是正常工作。但仔細想想就知道不對:review 連 tasks.md 都看不到,它在審查什麼?
這種看起來在正常工作但職責錯了的問題,比完全不工作更難發現。完全不工作你會立刻注意到——維度 5 是空的,或者標了錯誤。但它每次都有輸出,每次內容都有參考價值,所以你不會覺得有問題。直到有人追問:等一下,review 生成的時候 tasks.md 在哪?
開源社區的價值就在這裏。一個人的盲區,是另一個人的發現。讀者花時間讀了源碼、讀了 review.md 的實際輸出、對照了依賴鏈,發現了這個職責錯位。這種貢獻比寫代碼更有價值——因為它修正的是方法論層面的錯誤。
2/8 法則的進一步修正
第 5 期說的 2/8 法則是:改 instruction 佔 20% 投入獲 80% 質量提升,改 template 補上結構性格式問題。本期再加一層:review/verify 的職責劃分也是關鍵配置。 review 做前置指導、verify 做後置審查,這個邊界劃不清,兩個工具都會打折扣。
但本期的發現也修正了 2/8 法則本身——改了 instruction 和 SKILL.md(投入了 80%),verify 的行為改造仍然沒生效(只收獲了 20% 的效果)。剩下的 80% 效果需要同步更新模板和兜底策略。改配置不只是改一個地方,而是改一整條鏈路。
9. 預告
第 7 期繼續工具迭代。text-summary 是第一個上線的工具組件,後面的工具(JSON 格式化、代碼解釋等)可以複用本期建立的組件模式。同時關注一個新問題:多個工具共享相似的頁面結構(輸入區、操作按鈕、輸出區)時,是否需要抽象出通用組件模板。如果需要,又該由誰來定義這個模板——template 還是 instruction?
還有本期遺留的技術債:
React.lazy 兼容性:useEffect + import() 是臨時方案,後續工具多了需要更優雅的動態加載策略
相關資源
GitHub:https://github.com/shuge-x/shuge-ai-toolbox
好啦,謝謝你觀看我的文章,如果喜歡可以點贊轉發給需要的朋友,我們下一期再見!敬請期待!