feat/issue-extract-contract/main #22

Merged
admin merged 7 commits from feat/issue-extract-contract/main into master 2026-09-17 05:04:33 +00:00
Member

摘要

建立需求議題的抽取契約:下游只要一支指令就能拿到它需要的欄位,不必吞下整份議題全文。

需求議題

#1 — tea-sdlc:以 tea 驅動 SDLC 全流程的跨平台指令組

工作包議題

#5 — 建立需求議題的抽取契約

變更內容

  • scripts/issue-body.js — 議題 body 的 markdown 解析(段落、清單、兩欄表格),純函式,#8 的 wp-extract 會共用。
  • scripts/issue-extract.js — 抽取契約的十四個欄位,外加未整併留言則數。
  • scripts/lib.js — 新增 pages 非同步產生器,逐頁走訪收斂成一套規則。
  • 測試 110 個案例(本 PR 新增 29 個)。

設計重點

  • 解析與 I/O 分家。 解析是純函式、沒有網路也沒有檔案系統,所以獨立成一支模組而不是塞進 lib。#8 的工作包抽取會用同一套。
  • 「圍欄裡的東西不是內容」只判斷一次。 議題裡放 mermaid 或程式碼是常態,那裡面的井字號不是標題、減號不是清單項、直線不是表格。這件事收斂到 eachLine,段落切分/清單/表格三者都靠它。
  • 缺段落回空值而非報錯。 缺段落是模板的正常變體,不是解析失敗。
  • 議題不存在與沒有讀取權分成兩個錯誤碼。 前者是輸入錯,後者要去改權限,處置不同就不該共用一個碼。
  • 「必須看完整份清單」的走訪一律讀完或報錯。 半份清單比報錯危險:查重漏掉既有議題會重建一顆,數留言少算會讓下游拿著過期描述做事。

解決的問題

下游(分析、實作)原本得重讀整份議題才能取出兩三個欄位,既浪費額度也不可靠。抽取契約把這件事變成一次呼叫、一份精簡 JSON。

影響的功能

新增。findIssueByTitle 改用共用的 pages,行為不變(錯誤碼仍為 DEDUPE_LIMIT),既有測試全數通過。

測試結果

npm test:

ℹ tests 110
ℹ suites 0
ℹ pass 110
ℹ fail 0
ℹ duration_ms 2837

七顆 commit 逐一 checkout 後跑測試,每一顆都是綠的(102/102/102/102/102/102/110)。

Code review 抓到五個解析缺陷,全部先重現再修,並確認新測試抓得到:

1) ~~~ 圍欄:          [ '流程圖', '假標題' ]        ← 圍欄內的井字號被當成標題
2) 圍欄內的清單:       [ '程式碼裡的假項目', '真正項目' ]  ← 輸出多出沒人寫過的驗收標準
3) 逸脫的直線:         [{"term":"a\\","def":"b"}]    ← 欄位被切斷、內容消失
4) 第二條分隔列:       [...,{"term":"---","def":"---"},...]
5) 未閉合圍欄:         歸屬未定義

把 issue-body.js 還原成修正前的版本,新測試有五條失敗;改回修正版則 29 條全綠——確認測試真的抓得到,不是陪跑。另外 countUnmergedComments 原本只讀第一頁留言,超過一頁就少算,也已修正並加上分頁測試。

真實 Gitea 驗證(皆為讀取):

$ node scripts/issue-extract.js --repo plugins/tea-sdlc --index 5 --dry-run
{"ok":true,"data":{"dryRun":true,...,"note":"每則留言還會各查一次 reaction..."}}

# 直接把議題 #5 的真實 body 餵給解析器
段落: 母議題、要做出什麼、驗收標準、阻擋於
驗收標準:
  - 輸出欄位與契約完全一致,缺少的段落回傳空值而非報錯
  - `未處理留言數` 反映尚未被標記為已整併的留言則數
  - 只讀議題 body,不把留言內容納入輸出
  - 模板變體(缺段落、巢狀項目為空、checkbox 已勾、中英混排)各有測試案例並通過
  - 議題不存在或無讀取權時回傳可區分的錯誤碼

完整實跑路徑仍未在真實 repo 上執行——該 repo 的時間追蹤還沒開,第四層前置檢查會擋下。

待確認

驗收標準寫的「巢狀項目為空」在需求議題上不成立:templates/requirement-issue.md 的五個清單段落都是平的,巢狀只存在於 #8 的工作包契約裡。我改成測「巢狀出現時一律攤平、不無聲吃掉內容」,把這條的意圖補在能成立的地方。若你要的是別的語意,說一聲。


🤖 Generated with Claude Code

## 摘要 建立需求議題的抽取契約:下游只要一支指令就能拿到它需要的欄位,不必吞下整份議題全文。 ## 需求議題 #1 — tea-sdlc:以 tea 驅動 SDLC 全流程的跨平台指令組 ## 工作包議題 #5 — 建立需求議題的抽取契約 ## 變更內容 - `scripts/issue-body.js` — 議題 body 的 markdown 解析(段落、清單、兩欄表格),純函式,#8 的 `wp-extract` 會共用。 - `scripts/issue-extract.js` — 抽取契約的十四個欄位,外加未整併留言則數。 - `scripts/lib.js` — 新增 `pages` 非同步產生器,逐頁走訪收斂成一套規則。 - 測試 110 個案例(本 PR 新增 29 個)。 ## 設計重點 - **解析與 I/O 分家。** 解析是純函式、沒有網路也沒有檔案系統,所以獨立成一支模組而不是塞進 lib。#8 的工作包抽取會用同一套。 - **「圍欄裡的東西不是內容」只判斷一次。** 議題裡放 mermaid 或程式碼是常態,那裡面的井字號不是標題、減號不是清單項、直線不是表格。這件事收斂到 `eachLine`,段落切分/清單/表格三者都靠它。 - **缺段落回空值而非報錯。** 缺段落是模板的正常變體,不是解析失敗。 - **議題不存在與沒有讀取權分成兩個錯誤碼。** 前者是輸入錯,後者要去改權限,處置不同就不該共用一個碼。 - **「必須看完整份清單」的走訪一律讀完或報錯。** 半份清單比報錯危險:查重漏掉既有議題會重建一顆,數留言少算會讓下游拿著過期描述做事。 ## 解決的問題 下游(分析、實作)原本得重讀整份議題才能取出兩三個欄位,既浪費額度也不可靠。抽取契約把這件事變成一次呼叫、一份精簡 JSON。 ## 影響的功能 新增。`findIssueByTitle` 改用共用的 `pages`,行為不變(錯誤碼仍為 `DEDUPE_LIMIT`),既有測試全數通過。 ## 測試結果 `npm test`: ``` ℹ tests 110 ℹ suites 0 ℹ pass 110 ℹ fail 0 ℹ duration_ms 2837 ``` 七顆 commit 逐一 checkout 後跑測試,每一顆都是綠的(102/102/102/102/102/102/110)。 **Code review 抓到五個解析缺陷,全部先重現再修,並確認新測試抓得到:** ``` 1) ~~~ 圍欄: [ '流程圖', '假標題' ] ← 圍欄內的井字號被當成標題 2) 圍欄內的清單: [ '程式碼裡的假項目', '真正項目' ] ← 輸出多出沒人寫過的驗收標準 3) 逸脫的直線: [{"term":"a\\","def":"b"}] ← 欄位被切斷、內容消失 4) 第二條分隔列: [...,{"term":"---","def":"---"},...] 5) 未閉合圍欄: 歸屬未定義 ``` 把 `issue-body.js` 還原成修正前的版本,新測試有五條失敗;改回修正版則 29 條全綠——確認測試真的抓得到,不是陪跑。另外 `countUnmergedComments` 原本只讀第一頁留言,超過一頁就少算,也已修正並加上分頁測試。 真實 Gitea 驗證(皆為讀取): ``` $ node scripts/issue-extract.js --repo plugins/tea-sdlc --index 5 --dry-run {"ok":true,"data":{"dryRun":true,...,"note":"每則留言還會各查一次 reaction..."}} # 直接把議題 #5 的真實 body 餵給解析器 段落: 母議題、要做出什麼、驗收標準、阻擋於 驗收標準: - 輸出欄位與契約完全一致,缺少的段落回傳空值而非報錯 - `未處理留言數` 反映尚未被標記為已整併的留言則數 - 只讀議題 body,不把留言內容納入輸出 - 模板變體(缺段落、巢狀項目為空、checkbox 已勾、中英混排)各有測試案例並通過 - 議題不存在或無讀取權時回傳可區分的錯誤碼 ``` 完整實跑路徑仍未在真實 repo 上執行——該 repo 的時間追蹤還沒開,第四層前置檢查會擋下。 ## 待確認 驗收標準寫的「巢狀項目為空」在需求議題上不成立:`templates/requirement-issue.md` 的五個清單段落都是平的,巢狀只存在於 #8 的工作包契約裡。我改成測「巢狀出現時一律攤平、不無聲吃掉內容」,把這條的意圖補在能成立的地方。若你要的是別的語意,說一聲。 --- 🤖 Generated with [Claude Code](https://claude.com/claude-code)
jiantw83 added 7 commits 2026-09-17 05:01:24 +00:00
段落切分、列表、兩欄表格三種解析,需求議題與工作包議題共用同一套,
所以獨立成一支不碰網路也不碰檔案系統的模組,而不是塞進 lib。

段落切分會追蹤圍欄狀態:mermaid 或程式碼區塊裡的井字號不得被當成標題,
否則流程圖一畫,後面的段落就全被切碎。

缺段落回傳空值而非報錯——缺段落是模板的正常變體,不是解析失敗。
名詞表以分隔列為界,之後才是資料列,避免把表頭當成一筆名詞。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
下游(分析、實作)唯一的議題讀取管道,存在的理由是「不必吞下整份議題全文」。
輸出契約上的十四個欄位,缺少的段落回傳空值。

只讀 body,不把留言內容納入輸出,但回報未整併的留言則數,好讓下游知道自己是
不是在拿過期的描述做事。已整併的留言由 sdlc-sync 打上 +1 reaction,而 Gitea
的留言物件不含 reaction,只能逐則再查一次——請求數會隨留言數增長,但這個數字
要準。

議題不存在與沒有讀取權分成兩個錯誤碼:前者是輸入錯,後者要去改權限,處置不同
就不該共用一個碼。

Closes #5

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
解析是整條鏈的上游,解析錯則下游全錯,所以模板變體餵得比別處雜:缺段落、
段落在但列表是空的、checkbox 已勾與未勾、中英混排、編號清單、名詞表只有
表頭、body 全空、出現契約外的段落。

留言的部分驗兩件事:未整併則數只算沒有 +1 的,以及留言內容一個字都不得出現
在輸出裡——後者用整份 stdout 做子字串比對,比逐欄檢查更難繞過。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
code review 逐項驗出來的,全部可重現:

- `~~~` 圍欄完全沒被認出來,裡面的井字號會被當成段落標題。
- 段落內的圍欄不影響清單解析,於是程式碼範例裡的減號變成假的驗收標準。
  這是最嚴重的一個——輸出多出一條沒有人寫過的標準。
- 表格欄位裡逸脫的直線 `\|` 會把欄位切斷,內容整段消失。
- 同一段落裡若出現第二條分隔列,它會變成一筆 {term:'---'} 的假名詞。
- 圍欄開了沒關時,其後內容的歸屬沒有明確定義。

改法是把「圍欄裡的東西不是內容」這件事收斂到 eachLine 處理一次,段落切分、
清單、表格三者都靠它,而不是各自寫一份半套的判斷。圍欄需同種標記才算關閉;
沒關就到結尾時,其後內容一律算在圍欄內,這與 markdown 的實際渲染一致。

巢狀清單改為明確攤平並寫進註解:需求議題的模板沒有巢狀,真的出現時寧可多帶
一項,也不要無聲吃掉內容。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
findIssueByTitle 原本自己寫了一份「翻到短頁為止、超過上限就報錯」的迴圈,
而新的留言走訪需要同一套規則。抽成非同步產生器之後,呼叫端仍能在找到目標時
提早離開,規則卻只寫一次。

錯誤碼與訊息由呼叫端指定:查重讀不完要講的是「可能重建議題」,數留言讀不完
要講的是「數不完未整併的則數」,處置不同就不該共用一句話。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
原本只打一次留言端點就收工,留言超過一頁時未整併的則數會少算——而少算的後果
是下游以為描述是最新的,照著過期的描述做事。改用 lib.pages 走完所有頁,讀不完
就以 COMMENT_LIMIT 報錯,不無聲回傳半份。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
八條新案例,每一條都對應一個實際重現過的缺陷:~~~ 圍欄、圍欄內的假清單項、
混用圍欄標記、未閉合圍欄的歸屬、逸脫的直線、第二條分隔列、巢狀攤平,以及
留言分頁。

把修正還原成舊版解析器後,這批測試有五條會失敗;修正回來則全綠——確認測試
真的抓得到,不是陪跑。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
admin approved these changes 2026-09-17 05:04:19 +00:00
admin merged commit 752c3825bf into master 2026-09-17 05:04:33 +00:00
admin deleted branch feat/issue-extract-contract/main 2026-09-17 05:04:33 +00:00
Sign in to join this conversation.
No Reviewers
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: plugins/tea-sdlc#22