feat: 移除計時並停用報表預覽
This commit is contained in:
+17
-287
@@ -3,307 +3,37 @@ description: 僅由 /sdlc-analyze 指令叫用。對一顆需求議題執行可
|
||||
|
||||
# sdlc-analyze
|
||||
|
||||
對一顆需求議題執行可行性檢查,把疑點一題一題問到雙方有共識,再把共識變成一批工作包議題。
|
||||
|
||||
分成三段:**可行性分析**到共識摘要為止,除了起錶之外完全不寫入 Gitea;使用者看過摘要
|
||||
點頭之後,才進入**產生工作包**建立議題;最後**排上時程與看板**,把相依、截止日、
|
||||
Milestone、看板與人天估算補上。
|
||||
|
||||
這份檔案是流程正本。各平台的轉接檔只是指回這裡,不要把規則抄過去。
|
||||
|
||||
## 輸入
|
||||
|
||||
一個需求議題編號。
|
||||
|
||||
## 計時範圍
|
||||
|
||||
錶起在**它分析的那顆需求議題**上:在「起錶」那一步起,在「停錶並回報」那一步停,
|
||||
涵蓋整道 /sdlc-analyze。
|
||||
|
||||
起點放在第一段開頭,因為分析最耗時的正是共識之前那一段——四類疑點逐題問到收斂。
|
||||
從第二段才起錶的話,那段時間永遠是零,而報表上「分析不花時間」會直接餵給下一次估算。
|
||||
|
||||
錶已經跑在同一顆議題上時起錶什麼都不做,所以 plan 接著跑 analyze 不會把累積的時間
|
||||
切成兩段。**錶不跨階段跑**,也**不碰別顆議題上的錶**。
|
||||
|
||||
## 〔可委派〕的意思
|
||||
|
||||
標題後綴 `〔可委派〕` 的步驟只在意結果:**你的環境若能把工作交給子代理,就交出去,
|
||||
只把結果帶回來;不能就自己做。** 沒有這個後綴的步驟一律自己做。
|
||||
|
||||
怎麼挑、為什麼這樣挑,見 `references/delegation.md`——判準只有那一份,這裡不複述。
|
||||
一個需求議題編號。先用 `scripts/issue-extract.js` 讀取結構化內容;先看 `未處理留言數`。若有留言,直接走 `/sdlc-sync` 的流程,完成後自動接回這裡;不要要求使用者重打指令。接回前重新抽取一次拿到更新後的描述;若使用者不整併,繼續並在摘要註明。
|
||||
|
||||
## 第一段:可行性分析
|
||||
|
||||
### 1. 讀議題
|
||||
依序讀取並逐條對照:
|
||||
|
||||
```
|
||||
node scripts/issue-extract.js --repo <owner/name> --index <編號>
|
||||
```
|
||||
1. `references/feasibility-architecture.md`
|
||||
2. `references/feasibility-logic.md`
|
||||
3. `references/feasibility-data.md`
|
||||
4. `references/feasibility-schedule.md`
|
||||
|
||||
拿到的是結構化欄位,不必再讀整份議題全文。
|
||||
能從程式碼查證的事項自行查證;只有需要使用者決策的事項才提問。架構、邏輯、資料、時程四類依序完成,每次只問一題,每題提供建議與手動輸入。最後輸出共識摘要、變更假設、未決事項與人天估算;摘要只印終端,不寫入 Gitea。
|
||||
|
||||
**先看 `未處理留言數`。** 只要不是 0,就代表議題描述可能是過期的——留言裡有決策還沒被
|
||||
整併回描述。這時**先停下來**告訴使用者有幾則未整併的留言,問他要不要現在整併。
|
||||
|
||||
要整併的話**直接走 `/sdlc-sync` 的流程**(`prompts/sdlc-sync.md`),做完**自動接回這裡**:
|
||||
重新抽取一次拿到更新後的描述,再往下走。**不要要求使用者重打指令**——他已經說要整併了。
|
||||
|
||||
使用者選擇不整併就繼續,但要記下這件事,並在共識摘要裡註明「分析基於未整併留言前的描述」。
|
||||
|
||||
### 2. 起錶
|
||||
|
||||
議題確認存在之後,在**這顆需求議題**上起錶:
|
||||
|
||||
```
|
||||
node scripts/timer.js --repo <owner/name> --index <需求議題編號> --dry-run
|
||||
```
|
||||
|
||||
確認無誤後拿掉 `--dry-run` 再跑一次。錶已經跑在同一顆上時它什麼都不做,重跑不會把
|
||||
已經累積的時間切成兩段。
|
||||
|
||||
錶跑在別顆議題上時會被擋下(`STOPWATCH_ON_OTHER_ISSUE`)。**照實告訴使用者是哪一顆,
|
||||
請他自己去停**,不要代勞:那一段時間該記在哪顆議題上只有他知道。順帶說明停錶不會動到
|
||||
任何既有的工作樹——碼錶只管時間、工作樹只管檔案。
|
||||
|
||||
### 3. 對四份清單列出疑點 〔可委派〕
|
||||
|
||||
依序讀這四份規則正本,逐條對照議題內容:
|
||||
|
||||
1. `references/feasibility-architecture.md` — 架構:放錯 repo、循環相依、穿越邊界。
|
||||
2. `references/feasibility-logic.md` — 邏輯:既有功能是不是已經做過同一件事。
|
||||
3. `references/feasibility-data.md` — 資料:schema 變更、遷移、交易邊界。
|
||||
4. `references/feasibility-schedule.md` — 時程:相依鏈最長路徑、未知數最大的一項。
|
||||
|
||||
每一條檢查若在議題裡找不到答案,就轉成一個問題。**能在程式碼裡查證的就自己去查,
|
||||
不要拿去問使用者**——把問題留給只有人能回答的事。
|
||||
|
||||
### 4. 逐題問到共識
|
||||
|
||||
**一次問一題。** 問完等使用者回答,再問下一題,讓他能看著前一題的答案回答下一題。
|
||||
不要一次丟出五個問題,也不要把多個問題包成一題的多個選項。
|
||||
|
||||
順序固定為**架構 → 邏輯 → 資料 → 時程**,前一類的問題全部清空才進入下一類。
|
||||
前面的答案常常會讓後面的問題消失或改寫;每問完一題,重新檢視剩下的問題還成不成立。
|
||||
|
||||
每一題固定給兩個選項:
|
||||
|
||||
- **建議** — 你的答案,附上理由。理由要寫「為什麼是這個」,不是複述問題。
|
||||
- **手動輸入** — 讓使用者自己寫。任何一題都必須能手動作答,不被選項限制。
|
||||
|
||||
問題本身要具體到能用一句話回答。問不出收斂答案的問題,多半是問題本身太大,拆開再問。
|
||||
|
||||
### 5. 輸出共識摘要
|
||||
|
||||
全部問完後,輸出一份摘要讓使用者做最後確認,內容包含:
|
||||
|
||||
- **每一類的結論** — 架構/邏輯/資料/時程各自問出了什麼,逐條列出「問題 → 答案」。
|
||||
- **改變了什麼** — 分析過程中翻掉或修正了需求議題裡的哪些假設。
|
||||
- **仍然未決的事** — 問了但沒有答案、或使用者明確說「之後再說」的事。
|
||||
- **人天估算** — 每一項的估算與最沒把握的那一項。
|
||||
|
||||
摘要只印在終端,**不寫回議題、不建立任何東西**。使用者看過點頭之後,才進入下一段。
|
||||
使用者確認共識摘要後,**先列出要交付或驗收的項目,逐項向使用者確認**。使用者未確認或拒絕時,立即停止,不建立工作包、不排程、不寫入後續資料。
|
||||
|
||||
## 第二段:產生工作包
|
||||
|
||||
**使用者對共識摘要點頭之後才開始。** 摘要沒有經過確認就不要往下走。
|
||||
確認交付/驗收項目後,依共識切出可獨立完成的工作包,套用 `templates/work-package-issue.md`。每顆工作包的待辦與驗收都要可逐項勾選;對應已確認交付項目的待辦置於第一項。工作包整體也依交付優先排序,但不得違反先決關係。
|
||||
|
||||
### 6. 切出工作包
|
||||
每顆工作包先以 `scripts/issue-create.js --dry-run` 檢查,再移除旗標實跑。只使用既有標籤。建立後回報編號、標題與網址。
|
||||
|
||||
把需求切成幾顆工作包。一顆工作包是**開發者拿了就能動手、做完有明確結果**的單位:
|
||||
它有自己的驗收標準,做完能單獨被檢視,不必等別的工作包一起才看得出成果。
|
||||
## 第三段:排程
|
||||
|
||||
切的依據是第一段問出來的共識,特別是時程清單那份暫定拆法——那本來就是這一段的草稿。
|
||||
以 `startDate`、`days` 與 `depends` 組成計畫檔,執行 `scripts/schedule.js` 計算截止日;依序用 `issue-link.js`、`issue-update.js` 與 `project-add.js` 補上既有相依、Milestone、看板、截止日與人天估算。各腳本先 dry-run,再實跑。日期與人天是排程資料,不是耗時統計。
|
||||
|
||||
**標題格式為「{動詞}{名詞}」**,例如「建立工作包的抽取契約」、「產生圖解版總覽網頁」。
|
||||
**禁止流水編號與任何無意義代號**(`WP-01`、`任務三`、`第一階段`):命名本身就要說明用途,
|
||||
看標題就知道這顆在做什麼,不必點進去。
|
||||
|
||||
### 7. 組出每顆工作包的內容
|
||||
|
||||
套用 `templates/work-package-issue.md`,依序填滿九個段落:
|
||||
|
||||
1. **這個工作包在做什麼** — 一句話。讓人掃過標題與這一行就決定要不要點進來。
|
||||
2. **描述** — 從使用者的角度說這顆做完之後什麼事變得可能,不要寫成逐層的實作清單。
|
||||
3. **架構圖** — 見下方「架構圖的限制」。
|
||||
4. **範圍邊界** — 明列**不做什麼**。這一段的用途是抵抗範圍蔓延,寫得越具體越有用。
|
||||
5. **介面契約** — 表格,四欄:介面/產出者/消費者/形狀。讓人知道自己產出的東西誰會消費。
|
||||
這顆不產出對外介面就寫一列「無」,不要留空表。
|
||||
6. **待辦** — 巢狀結構:每一項待辦底下掛**它自己的**驗收標準,讓人知道這一項做到什麼程度算完成。
|
||||
|
||||
```
|
||||
- [ ] 建立共用函式庫
|
||||
- [ ] 具名 flag 解析可拒絕未知參數
|
||||
- [ ] 單行 JSON 輸出格式固定
|
||||
- [ ] 加上前置檢查
|
||||
- [ ] 四層各自回傳可區分的錯誤碼
|
||||
```
|
||||
|
||||
上層是待辦、縮排一層是該項的驗收,不要再往下巢狀。兩者都用 checkbox,實作時會被逐項勾選。
|
||||
7. **整體驗收** — 整顆工作包做完才驗得出來的事,與個別待辦的驗收不重複。
|
||||
8. **repo 列表** — 這顆會動到哪些 repo。
|
||||
9. **關聯** — 至少要有一行 `需求議題:#<編號>` 指回來源。阻擋、先決與人天估算由後續流程補上。
|
||||
|
||||
### 8. 先試跑,再寫入
|
||||
|
||||
每顆工作包各寫一個暫存檔,然後逐顆:
|
||||
|
||||
```
|
||||
node scripts/issue-create.js --repo <owner/name> --title "<標題>" --body-file <暫存檔> \
|
||||
--labels "<標籤>" --dry-run
|
||||
```
|
||||
|
||||
`--dry-run` 會印出將送出的請求、把標籤名稱換成 id,並在標題已存在時如實顯示「實跑會是
|
||||
no-op」。確認無誤後拿掉該旗標再跑一次。
|
||||
|
||||
標籤一樣只能從 `scripts/labels-list.js` 回傳的既有標籤裡挑,**不得自行建立新標籤**。
|
||||
|
||||
中斷後重跑不會產生重複工作包:`issue-create` 以標題查重,發現同名議題就回傳既有那一顆
|
||||
並把 `created` 設為 `false`。
|
||||
|
||||
### 9. 回報
|
||||
|
||||
列出每顆工作包的編號、標題與網址。不要把議題內容再貼一次。
|
||||
|
||||
**這裡不停錶**——指令還沒跑完,第三段還要排時程。停錶在最後的「停錶並回報」那一步。
|
||||
|
||||
## 第三段:排上時程與看板
|
||||
|
||||
工作包建好之後,把它們之間的關係與時程補上。做完這一段,看板上呈現的才是真實的
|
||||
開發順序,而不是一堆平鋪的議題。
|
||||
|
||||
### 10. 算出截止日 〔可委派〕
|
||||
|
||||
把每顆工作包的編號、人天估算與先決關係寫成一份計畫檔:
|
||||
|
||||
```json
|
||||
{
|
||||
"startDate": "2026-09-21",
|
||||
"workPackages": [
|
||||
{ "index": 12, "title": "建立共用函式庫", "days": 3 },
|
||||
{ "index": 13, "title": "建立抽取契約", "days": 2, "depends": [12] }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
```
|
||||
node scripts/schedule.js --plan-file <計畫檔>
|
||||
```
|
||||
|
||||
它依相依關係做拓撲排序,保證**任一工作包的截止日都不早於它的先決**——人工排時程
|
||||
最常見的矛盾就是前置工作比後續還晚到期。相依成環時它會直接報錯並指出環上的成員,
|
||||
那代表拆法有問題,回頭改拆法,不要硬排。
|
||||
|
||||
日期以日曆日累加,不跳週末也不扣假日。要跳的話自己把 `startDate` 或人天調整過再算。
|
||||
|
||||
### 11. 逐顆補上關係與時程
|
||||
|
||||
對每一顆工作包,依序:
|
||||
|
||||
```
|
||||
node scripts/issue-link.js --repo <owner/name> --index <編號> --depends <先決編號清單>
|
||||
node scripts/issue-update.js --repo <owner/name> --index <編號> \
|
||||
--milestone "<既有 Milestone 名稱>" --due-date <schedule 算出的日期> --estimate-days <人天>
|
||||
node scripts/project-add.js --repo <owner/name> --index <編號> --project "<看板名稱或網址>"
|
||||
```
|
||||
|
||||
三支都先用 `--dry-run` 看過再實跑。三支都是冪等的:相依已存在就跳過、已在看板上就不重發、
|
||||
估算沒變就不改 body。
|
||||
|
||||
**Milestone 與看板都只掛既有的。** 指到不存在的 Milestone 會中止並列出可選項目;
|
||||
看板名稱靠掃最近 50 筆議題反查 id,反查不到就會請你直接貼專案網址(結尾即 id)。
|
||||
本流程不建立 Milestone,也不建立專案。
|
||||
|
||||
### 12. 產生分析版的圖解總覽 〔可委派〕
|
||||
|
||||
排程完成後重新抽取需求與全部工作包,組成 `schemaVersion: 1` 的需求級 JSON。
|
||||
工作包依賴與截止日必須來自重新抽取的實際資料,不使用模型記憶中的暫定值。
|
||||
執行:
|
||||
|
||||
```
|
||||
node scripts/overview-render.js --input <json> --output <html> --manifest <manifest>
|
||||
```
|
||||
|
||||
HTML 內重新產生 SVG 與 HTML/CSS 視圖;不能直接搬用議題 Mermaid。預覽優先使用平台
|
||||
能力,否則啟動短命 Node server 供瀏覽器截圖。產生 full-page 與局部圖 PNG 後執行:
|
||||
|
||||
```
|
||||
node scripts/issue-assets.js --repo <owner/name> --index <需求議題編號> --manifest <manifest>
|
||||
```
|
||||
|
||||
截圖全部上傳成附件,需求議題正文只保留附件索引。只有真正可用的 preview URL 才使用
|
||||
既有 `--overview-url`;不可把 `.tmp/` 或 `0.0.0.0` 寫回 Gitea。
|
||||
產出 HTML 可委派;預覽截圖、附件上傳與任何 `issue-assets`/`issue-update` 寫回都不委派,
|
||||
由主流程執行並處理錯誤。
|
||||
若平台 preview 不可用,執行 `node scripts/overview-capture.js --backend auto`;
|
||||
它依序嘗試 Firefox,再嘗試 `rsvg-convert`/`resvg`。所有 backend 都不可用時必須
|
||||
停止並保留 `.tmp/` 的 HTML/SVG,不得宣稱截圖完成。
|
||||
### 13. 停錶並回報
|
||||
|
||||
回報之前先停錶,這一段計時到此為止:
|
||||
|
||||
```
|
||||
node scripts/timer.js --repo <owner/name> --index <需求議題編號> --stop
|
||||
```
|
||||
|
||||
它**只停這一顆**上的錶,與「起錶」那一步起的是同一顆。錶本來就沒在跑不算失敗
|
||||
(`碼錶已停` 會是 `false` 並附一句說明),回報照樣做完。
|
||||
|
||||
列出每顆工作包的編號、標題、截止日與所屬 Milestone,並指出**相依鏈最長路徑**上的那幾顆
|
||||
——那條路徑決定整體交期。
|
||||
|
||||
## 已知限制:人天估算只寫得進 body
|
||||
|
||||
Gitea 1.27 的 API 沒有任何請求定義接受 `time_estimate`,該欄位只出現在議題的回應裡。
|
||||
也就是說**議題的估算欄位無法由 API 寫入**,只能靠人在網頁上填。
|
||||
|
||||
因此 `issue-update --estimate-days` 只把估算寫成議題 body 裡人類可讀的一行
|
||||
(`估算人天:N`,放在「關聯」段落)。之後 `sdlc-report` 要比對估算與實際工時時,
|
||||
讀的也是這一行。
|
||||
|
||||
## 架構圖的限制
|
||||
|
||||
圖表以抽象 `kind`、`direction`、`nodes`、`edges` 保存。renderer 產生議題 Mermaid
|
||||
與 HTML SVG;不能讓模型直接維護兩套圖表語法。節點超過 12、文字超過 8 字或無法
|
||||
安全轉換時,拆圖或記錄 `omitted` 原因。
|
||||
## 邊界
|
||||
|
||||
- **共識摘要之前不對 Gitea 寫入任何內容**:不建議題、不改描述、不貼標籤、不留留言。
|
||||
**碼錶除外**——它記的是工時,不是內容;而分析最耗時的正是共識之前那一段,不從那裡
|
||||
起錶,那段時間就永遠是零。
|
||||
- 第二段只建立工作包議題。不建相依、不掛 Milestone、不加看板、不寫人天估算——那是第三段的事。
|
||||
- 第三段只掛既有的 Milestone 與看板。不自行建立標籤、Milestone 或專案看板。
|
||||
- 不修改使用者的專案檔案。查證既有功能時只讀不寫。
|
||||
- 不替使用者決定他沒回答的事。問不到答案就進「仍然未決的事」。
|
||||
- 計時只動這顆需求議題:在它上面起錶、在它上面停錶。**不停別顆議題上的錶**,
|
||||
被別顆的錶擋下時交還給使用者決定,不繞過去。
|
||||
- 不關閉或刪除任何既有議題。
|
||||
# Artifact 交接與視圖
|
||||
|
||||
完成第三段排程後,依 `references/artifact-contract.md` 重新抽取需求與全部工作包,
|
||||
組成一份 `schemaVersion: 1` 的 JSON 暫存檔。這份 JSON 是 HTML renderer 的輸入,
|
||||
不是 Gitea 的第二份正本。
|
||||
|
||||
先執行:
|
||||
|
||||
```
|
||||
node scripts/overview-render.js --input <json> --output <html> --manifest <manifest>
|
||||
```
|
||||
|
||||
renderer 驗證失敗時,不截圖、不上傳、不更新議題,保留 `.tmp/` 的輸入與錯誤供修正。
|
||||
HTML 必須自包含,不依賴 CDN;圖表由抽象節點/邊資料重新產生 SVG,不能直接搬用議題
|
||||
Mermaid。公式是可選欄位,單一公式無法渲染時保留原文並標示未渲染。
|
||||
|
||||
預覽優先使用執行環境提供的預覽能力。沒有預覽能力時,使用 `0.0.0.0` 綁定的
|
||||
ephemeral Node 靜態伺服器,只為瀏覽器截圖,完成後一定停止。產生 full-page 與所有
|
||||
局部圖 PNG,為每張圖填入 manifest 的角色、固定檔名與 SHA-256。
|
||||
|
||||
截圖全部是需求議題附件,不嵌入圖片;執行:
|
||||
|
||||
```
|
||||
node scripts/issue-assets.js --repo <owner/name> --index <需求議題編號> --manifest <manifest>
|
||||
```
|
||||
|
||||
這支腳本負責 hash 重用、附件上傳與「圖解版總覽附件」索引的冪等更新。上傳或索引
|
||||
更新失敗就是流程失敗。只有預覽平台提供真正可用的 URL 時,才另外使用既有的
|
||||
`--overview-url`;不可把 `.tmp/` 或 `0.0.0.0` 寫回 Gitea。
|
||||
- 不產生 HTML、SVG、manifest、截圖、附件或任何平台 preview。
|
||||
- 不建立 Milestone 或專案看板。
|
||||
- 不修改使用者專案檔案。
|
||||
- 不關閉或刪除既有議題。
|
||||
- 共識摘要與交付確認前不建立任何工作包。
|
||||
|
||||
+7
-366
@@ -1,375 +1,16 @@
|
||||
name: sdlc-feat
|
||||
description: 僅由 /sdlc-feat 指令叫用。領取一顆工作包、備妥工作樹、起錶,逐項實作並勾選待辦,最後分批提交並開立 PR。
|
||||
description: 僅由 /sdlc-feat 指令叫用。領取工作包、開分支、逐項實作並開 PR。
|
||||
|
||||
# sdlc-feat
|
||||
|
||||
拿一顆工作包,從領取到開出 PR。
|
||||
輸入一個工作包議題編號。若抽取結果有 `未處理留言數`,直接走 `/sdlc-sync` 的流程,完成後自動接回這裡;不要要求使用者重打指令。接回前重新抽取一次拿到更新後的描述。
|
||||
|
||||
第一段**領取與開工準備**:把工作包安全地認領下來,備妥一棵屬於它的工作樹,然後開始計時。
|
||||
這一段不改任何一行程式碼——它只負責讓後面的實作有個乾淨的起點。
|
||||
依工作包 `repos` 逐一準備 worktree;不在主工作區切換分支。逐項完成待辦並立即勾選對應驗收。交付優先項目位於待辦第一項時先完成;仍須遵守工作包的依賴與範圍邊界。
|
||||
|
||||
第二段**逐項實作**:一項一項把待辦做完並即時勾選,讓議題頁的進度條隨時反映真實狀態。
|
||||
|
||||
第三段**提交與開立 PR**:把變更整理成讀得懂的歷史,開出 PR,停錶。
|
||||
|
||||
這份檔案是流程正本。各平台的轉接檔只是指回這裡,不要把規則抄過去。
|
||||
|
||||
## 輸入
|
||||
|
||||
一個工作包議題編號。使用者直接給的,或 `/sdlc-fix` 收到議題後交棒過來的——
|
||||
兩者一樣處理,**不要因為是交棒來的就要求他再打一次指令**。
|
||||
|
||||
## 〔可委派〕的意思
|
||||
|
||||
標題後綴 `〔可委派〕` 的步驟只在意結果:**你的環境若能把工作交給子代理,就交出去,
|
||||
只把結果帶回來;不能就自己做。** 沒有這個後綴的步驟一律自己做。
|
||||
|
||||
怎麼挑、為什麼這樣挑,見 `references/delegation.md`——判準只有那一份,這裡不複述。
|
||||
|
||||
## 第一段:領取與開工準備
|
||||
|
||||
### 1. 讀工作包
|
||||
|
||||
```
|
||||
node scripts/wp-extract.js --repo <owner/name> --index <編號>
|
||||
```
|
||||
|
||||
拿到的是結構化欄位:待辦與它自己的驗收、範圍邊界、介面契約、相依、repo 列表。
|
||||
不必再讀整份議題全文。
|
||||
|
||||
**先看 `未處理留言數`。** 只要不是 0,就代表議題描述可能是過期的——留言裡有決策還沒被
|
||||
整併回描述。這時**先停下來**告訴使用者有幾則未整併的留言,問他要不要現在整併。
|
||||
|
||||
要整併的話**直接走 `/sdlc-sync` 的流程**(`prompts/sdlc-sync.md`),做完**自動接回這裡**:
|
||||
重新抽取一次拿到更新後的描述,再往下走。**不要要求使用者重打指令**——他已經說要整併了。
|
||||
|
||||
使用者選擇不整併就繼續,但要記下這件事,並在最後的 PR 描述裡註明「實作基於未整併留言前的描述」。
|
||||
|
||||
**再看 `相依.depends`。** 裡面還有沒關閉的議題,代表這顆的前置還沒做完。照樣先說出來,
|
||||
讓使用者決定要不要現在做。
|
||||
|
||||
### 2. 領取工作包
|
||||
|
||||
先試跑,看清楚會做什麼:
|
||||
|
||||
```
|
||||
node scripts/claim.js --repo <owner/name> --index <編號> --dry-run
|
||||
```
|
||||
|
||||
確認無誤後拿掉旗標再跑一次。放行時它會設 assignee、貼「進行中」標籤——這兩件事一起
|
||||
構成領取鎖。**錶不在這一步起**:它等工作樹建好之後才起(第 6 步)。工作樹建立失敗會
|
||||
中止整個領取,錶要是先起了,使用者就被計了一段什麼都沒做的時間。
|
||||
|
||||
領取鎖有四種狀態,三種擋、一種放行。被擋下來時**不要繞過去**,照著錯誤碼告訴使用者
|
||||
發生什麼事、下一步是什麼:
|
||||
|
||||
| 狀態 | 錯誤碼 | 下一步 |
|
||||
| --- | --- | --- |
|
||||
| 別人已經認領這顆 | `CLAIMED_BY_OTHER` | 改領別顆,或先跟對方確認 |
|
||||
| 你的錶已經跑在這顆上 | `STOPWATCH_ON_THIS_ISSUE` | 這顆你正在做;要重新計時請先手動停錶 |
|
||||
| 你的錶跑在別的議題上 | `STOPWATCH_ON_OTHER_ISSUE` | 多半是忘了停上一顆;先去停掉再回來 |
|
||||
|
||||
被錶擋下來時**要順帶說明停錶不會動到既有的工作樹**:碼錶只管時間、工作樹只管檔案。
|
||||
不講清楚,使用者會以為停錶等於放棄那顆工作包,於是寧可不停——工時就記到別顆去了。
|
||||
| 沒有鎖 | —— | 放行。自己已認領但沒起錶也算沒有鎖,那正是中斷後重跑的情形 |
|
||||
|
||||
**別顆議題上的錶一律由使用者自己停。** 哪一段時間該記在哪顆議題上只有他知道,代勞會把
|
||||
工時記錯地方。每道指令停掉的只有自己起的那一支——這一道停在「開 PR 並停錶」那一步。
|
||||
|
||||
鎖以外還有一個前置條件:repo 上要有「進行中」標籤。缺了會得到 `LABEL_NOT_FOUND`,
|
||||
請使用者自己去建立——**不要自己建**,標籤體系不該在多個 repo 之間長出雜草。
|
||||
|
||||
### 3. 問來源分支
|
||||
|
||||
**一次問一題。** 新分支要從哪裡長出來,只有使用者知道,不要替他決定。
|
||||
給兩個選項,並附上你判斷的理由:
|
||||
|
||||
- **建議** — 你的答案。多數情況是開發分支(`master`/`main`/`develop`);
|
||||
但若這顆工作包明顯是某個既有功能分支的一部分,就建議那一支,並說明為什麼。
|
||||
- **手動輸入** — 讓使用者自己填分支名。
|
||||
|
||||
不論哪一種,來源分支都必須**已經在遠端上**:工作樹的起點一律取自 `origin/{來源分支}`。
|
||||
|
||||
### 4. 把議題標題翻成英文 〔可委派〕
|
||||
|
||||
分支名的中段要用英文,中文會讓 CI 與 URL 出問題。把工作包議題的標題翻成
|
||||
**小寫英文 kebab、40 字元以內**,例如「建立工作包的抽取契約」→ `wp-extract-contract`。
|
||||
|
||||
翻譯要保留原意而不是逐字直譯,寧可用一個更短的說法,也不要把長句截斷成看不懂的字串。
|
||||
|
||||
### 5. 備妥工作樹
|
||||
|
||||
工作樹開在**工作包的 `repos` 列出的那些 repo** 上,不是開在本 plugin 的目錄裡。
|
||||
`repos` 只有一顆就用那一顆;**有多顆時逐一確認**要在哪幾個開分支,
|
||||
再對每一個各跑一次 `branch-prep`,分支名在每個 repo 都相同。
|
||||
|
||||
```
|
||||
node scripts/branch-prep.js --repo <owner/name> --path <目標專案路徑> \
|
||||
--source <來源分支> --slug <英文-kebab> [--type feat] --dry-run
|
||||
```
|
||||
|
||||
`--type` 只在來源是開發分支時要給(`feat`/`fix`/`chore`…);從功能分支長出時,
|
||||
類型與需求描述沿用來源,不必也不能再指定。
|
||||
|
||||
試跑會印出將執行的 git 指令、算出來的分支名與工作樹路徑。確認無誤後拿掉旗標再跑一次。
|
||||
|
||||
**不在原地切換分支,一律開一棵獨立的工作樹。** 每顆工作包有自己的目錄、自己的建置
|
||||
產物、自己的未提交變更,彼此看不見對方。這件事對 agent 特別重要:它是非同步的,
|
||||
可能在分支已經被切走之後才去讀檔,而它**不會察覺**自己讀到的是別顆工作包的內容——
|
||||
產出看起來完全合理,只是接錯了上下文。
|
||||
|
||||
**工作樹一律建立,沒有例外。** 建不起來就照實中止,**不要改成在原地切分支**:
|
||||
使用者會以為自己在隔離環境裡,其實在原地改。
|
||||
|
||||
工作樹路徑由 `owner/repo/分支名` 推導而得,印在輸出的 `worktree` 欄位。
|
||||
**後面幾段的實作、測試與提交都在那棵工作樹裡做**,不要回到主工作區動手。
|
||||
|
||||
它保證三件事:
|
||||
|
||||
- **起點一律是遠端的來源分支**(`origin/{來源分支}`),不是本機同名分支——後者可能
|
||||
落後好幾天。遠端沒有那一支時得到 `SOURCE_NOT_FOUND`,把訊息念給使用者,讓他決定
|
||||
是先把來源分支推上去,還是改指定一個別的來源——**不要自己換一個**。
|
||||
- **目標分支已經存在時接上去而不是蓋掉**;工作樹已經在了就沿用,不動裡面還沒提交的東西。
|
||||
- **失敗時不留半成品**:不會出現有分支沒工作樹、或有工作樹沒分支的狀態。
|
||||
|
||||
推導出的路徑被別的東西佔住時(`WORKTREE_PATH_TAKEN`,多半是別的 clone 留下的),
|
||||
把路徑念給使用者,請他確認裡面沒有還沒保存的東西再移除——**不要自己刪**。
|
||||
|
||||
工作樹是乾淨的:**沒有安裝依賴,也沒有任何建置產物**,`.env` 這類機密檔案更不會被
|
||||
複製過去。把輸出的 `提示.安裝指令` 念給使用者,機密檔案請他自己放一份。
|
||||
|
||||
### 6. 起錶
|
||||
|
||||
工作樹建好之後才起錶:
|
||||
|
||||
```
|
||||
node scripts/timer.js --repo <owner/name> --index <編號> --dry-run
|
||||
```
|
||||
|
||||
確認無誤後拿掉旗標再跑一次。錶已經跑在這顆議題上時它什麼都不做——那正是中斷後重跑
|
||||
的情形,重新起錶會把已經累積的時間切成兩段。
|
||||
|
||||
### 7. 回報
|
||||
|
||||
印出一份開工前的現況,不寫回議題:
|
||||
|
||||
- 工作包標題與網址、這一顆有幾項待辦
|
||||
- 認領結果(是否本來就是自己的)、碼錶已起
|
||||
- 來源分支、新分支名、分支是新建還是接上既有
|
||||
- 工作樹路徑,以及它是乾淨的、要先跑哪一行安裝指令
|
||||
- 未處理留言數與未關閉的先決議題(若有)
|
||||
|
||||
## 第二段:逐項實作
|
||||
|
||||
### 8. 認出語言,讀規則正本
|
||||
|
||||
改任何一個檔案之前,先依專案檔認出這是什麼語言,再讀兩份規則正本:
|
||||
|
||||
- `references/coding-standards.md` — 分層判定與各層要寫什麼註解
|
||||
- `references/comment-styles.md` — 該語言的註解格式
|
||||
|
||||
規則以那兩份為準,這裡不複述——抄過來就會有兩份各自演化的規則。只強調兩件最常被跳過的:
|
||||
**認不出語言就停下來問、不要猜**,以及**規則只存在於本 plugin 裡**,
|
||||
不寫進目標專案的任何檔案。
|
||||
|
||||
屬性的資料範例**優先從 MCP 取得**;取不到就以邏輯推理,並照 `comment-styles.md` 的寫法
|
||||
在註解裡註明「由邏輯推理、未經驗證」。這句註明不能省,否則後面的人會照著沒對過的格式寫解析。
|
||||
|
||||
### 9. 一項一項做
|
||||
|
||||
**改的是工作樹裡的檔案**,路徑就是 `branch-prep` 印出來的 `worktree`,不是主工作區——
|
||||
主工作區可能停在別的分支上,在那裡動手會把改動落到別顆工作包的分支去。
|
||||
|
||||
依 `wp-extract` 給的 `待辦` 順序做。每一項的做法:
|
||||
|
||||
1. 讀它底下的 `驗收`——那是「這一項做到什麼程度算完成」的定義。
|
||||
2. 實作,照 `coding-standards.md` 的分層與註解規範。
|
||||
3. 這一項的驗收都成立了,才算完成。
|
||||
|
||||
**過程不打斷。** 不要每做完一項就問一次「可以繼續嗎」——二十項待辦不該按二十次同意。
|
||||
只印進度,例如 `[3/12] 已完成:解析九個段落`。
|
||||
|
||||
真正需要停下來問的只有三種:語言認不出來、待辦的意思有歧義、做下去會超出工作包的
|
||||
`範圍邊界`。除此之外一路做完。
|
||||
|
||||
### 10. 做完一項就勾一項
|
||||
|
||||
```
|
||||
node scripts/issue-update.js --repo <owner/name> --index <編號> \
|
||||
--tick '<wp-extract 給的那一行 raw>' --section 待辦
|
||||
```
|
||||
|
||||
`--tick` 收的是抽取契約交出的**那一整行 `raw`**,逐字包含縮排;它只把那一行的方框換成
|
||||
已勾,議題其餘部分一字不動。待辦與它底下的驗收各自是一行,各勾各的。
|
||||
|
||||
`--section` 是那一項所在的段落:勾 `待辦` 裡的項目就給 `待辦`,勾 `整體驗收` 就給
|
||||
`整體驗收`。**一定要給**——兩個段落常有一模一樣的一句話,不給就分不出要勾哪一個。
|
||||
|
||||
**不要自己拼那一行**,一律用 `wp-extract` 給的 `raw`。四種擋下來的情況都照實說,不要繞過去:
|
||||
|
||||
| 錯誤碼 | 意思 | 下一步 |
|
||||
| --- | --- | --- |
|
||||
| `RAW_NOT_FOUND` | 議題上找不到這一行 | 手上的抽取結果過期了(議題被改過);重跑 `wp-extract` 再試 |
|
||||
| `RAW_AMBIGUOUS` | 這一行在同一個段落裡出現不只一次 | 分不出要勾哪個;請使用者把重複的那幾項改寫成看得出差別的說法 |
|
||||
| `NOT_A_CHECKBOX` | 議題上那一項沒有方框 | 請使用者把它補成 `- [ ] …`;**不要自己改寫議題** |
|
||||
| `SECTION_NOT_FOUND` | `--section` 的段落不存在 | 對照 `wp-extract` 的輸出確認段落名稱 |
|
||||
|
||||
**不要為了勾選在議題上留留言。** 勾選改的是 body,進度條自己會動;逐項留言會把議題洗版,
|
||||
reviewer 得從一堆「已完成第 N 項」裡找真正的討論。
|
||||
|
||||
### 11. 中斷後重跑
|
||||
|
||||
進度完全由 Gitea 上的勾選狀態推導,**不看任何本機檔案**。重跑這一段時:
|
||||
|
||||
1. 重新 `wp-extract`,看 `待辦` 裡哪些 `done` 已經是 `true`。
|
||||
2. 從第一個還沒勾的接下去做。
|
||||
3. 已經勾過的項目再 `--tick` 一次是安靜的 no-op(回傳 `已經勾過: true`,不發 PATCH),
|
||||
所以不確定某一項有沒有勾到時,直接再勾一次即可,不必先查。
|
||||
|
||||
### 12. 回報
|
||||
|
||||
全部待辦完成後印一份小結,不寫回議題:
|
||||
|
||||
- 幾項待辦、幾項驗收,全部勾選完成
|
||||
- 改了哪些檔案,各屬於哪一層
|
||||
- 有沒有待辦因為 `範圍邊界` 而被刻意不做
|
||||
- 語言與註解格式用的是哪一份對照
|
||||
- **哪些資料範例是推理來的**(MCP 取不到的那些),讓 reviewer 知道哪幾個格式還沒人對過
|
||||
|
||||
## 第三段:提交與開立 PR
|
||||
|
||||
### 13. 分批提交 〔可委派〕
|
||||
|
||||
全部待辦都勾完之後才進這一段。變更依類型分批。
|
||||
|
||||
委派的是**方案計算**:變更分成哪幾批、每一批收哪些檔案、各自的 `--type` 與描述怎麼寫。
|
||||
**實際提交不委派**——底下那支 `commit-split.js` 由主流程執行(判準第四條)。
|
||||
|
||||
```
|
||||
node scripts/commit-split.js --path <工作樹路徑> --type feat \
|
||||
--subject '<繁中描述>' [--scope <功能名>] --dry-run
|
||||
```
|
||||
|
||||
`--path` 給的是第一段建出來的那棵**工作樹**——commit 要落在它的分支上。
|
||||
|
||||
`--type` 是**這次程式碼變更**的類型(`feat`/`fix`/`refactor`…);測試、文件與設定檔
|
||||
由腳本自己認出來,各自成批,不必也不能指定。`--body` 寫「為什麼這樣做」,那一段會接在
|
||||
每一顆 commit 的首行之後——本 repo 的歷史靠它讀得懂。
|
||||
|
||||
某一批提交失敗時,錯誤會列出**前面已經建立的那幾顆 commit**。修掉原因之後重跑即可,
|
||||
已建立的不會重複;不要自己去回捲歷史。
|
||||
|
||||
`--scope` 只在某一批有多個檔案時才需要:單檔那批的 scope 就是檔名。試跑會印出將建立的
|
||||
每一顆 commit 與它各自的檔案,確認無誤後拿掉旗標再跑一次。
|
||||
|
||||
**描述用繁體中文。** 日後回顧時看得懂的是中文;夾雜英文的專有名詞(函式名、旗標名)
|
||||
保留原文即可。
|
||||
|
||||
一次變更橫跨兩個不相干的功能時,用 `--files` **分兩次跑**:
|
||||
|
||||
```
|
||||
node scripts/commit-split.js ... --files scripts/claim.js,test/claim.test.js
|
||||
```
|
||||
|
||||
一顆 commit 的描述只說得清楚一件事,硬湊在一起就失去了分批的意義。
|
||||
|
||||
### 14. 寫 PR 描述
|
||||
|
||||
固定八個段落,順序不能換——reviewer 每次都在同一個位置找到要找的資訊:
|
||||
|
||||
1. **摘要** — 這個 PR 做完之後,什麼事變得可能。
|
||||
2. **需求議題** — `#<編號>`。
|
||||
3. **工作包議題** — `#<編號>`。
|
||||
4. **變更內容** — 改了什麼。commit 一覽加上新增/修改的檔案。
|
||||
5. **設計重點** — 為什麼這樣做。取捨與理由,不是實作步驟的複述。
|
||||
6. **解決的問題** — 這次修掉了什麼。有具體觸發條件的就寫出來。
|
||||
7. **影響的功能** — 誰會被影響、既有行為有沒有改變。
|
||||
8. **測試結果** — 見下。
|
||||
|
||||
**「測試結果」放實際跑過的輸出**,原樣貼上,不要改寫成「已測試通過」——那句話看不出
|
||||
跑過什麼,reviewer 沒辦法據以判斷。沒有自動化測試時,寫出 reviewer 自己能重現的手動
|
||||
驗證步驟(跑什麼指令、看到什麼算對)。
|
||||
|
||||
`pr-create` 會擋下缺段落、順序不對、以及測試結果只有空話的描述。被擋下來時**補真的內容**,
|
||||
不要為了通過而拼湊。
|
||||
|
||||
### 15. 開 PR 並停錶
|
||||
|
||||
```
|
||||
node scripts/pr-create.js --repo <目標專案 owner/name> --head <分支名> \
|
||||
--base <來源分支> --body-file <描述檔> \
|
||||
--issue-repo <工作包議題的 owner/name> --index <工作包編號> --dry-run
|
||||
```
|
||||
|
||||
`--repo` 是**程式碼所在的 repo**(PR 開在那裡),`--issue-repo` 是**工作包議題所在的
|
||||
repo**(錶停在那裡)。兩者常常不是同一個——議題在需求的 repo,程式碼在 `repos` 列的
|
||||
那幾個。同一個 repo 時 `--issue-repo` 可以省略。
|
||||
|
||||
`--base` 就是第一段問到的那支來源分支,要明講——腳本不替你猜 `master` 還是 `main`。
|
||||
標題由腳本設為分支名,不必也不能另外指定。
|
||||
|
||||
順序是**先開 PR 再停錶**,而且 PR 沒開成就不停錶——工時要記在真的有做事的那段時間上。
|
||||
錶本來就沒在跑不算失敗(`碼錶已停` 會是 `false` 並附一句說明),PR 仍然開出去了。
|
||||
|
||||
重跑不會開出第二顆 PR:同一個 head 已經有開著的 PR 就回傳它(`created` 為 `false`),
|
||||
然後照樣停錶——那一步可能正是上次中斷的地方。
|
||||
|
||||
### 16. 回報
|
||||
|
||||
- PR 的網址與編號、標題(等同分支名),以及它是這次新開的還是接上既有的
|
||||
- 建立了哪幾顆 commit
|
||||
- 碼錶是否已停;沒停的話把腳本回的那句說明一起帶出來
|
||||
- 議題上還有沒有沒勾完的待辦(理論上應該沒有;有的話要說出來)
|
||||
|
||||
### 17. 告訴使用者之後怎麼查
|
||||
|
||||
PR 開出去之後就交給 reviewer 了。**把下面這件事講給使用者聽,不要自己反覆跑**:
|
||||
|
||||
```
|
||||
node scripts/pr-watch.js --repo <owner/name> --index <PR 編號>
|
||||
```
|
||||
|
||||
問一次答一次:PR 狀態、還有幾則留言沒處理、工作樹在哪、裡面有沒有沒提交的東西,
|
||||
以及固定列舉值的 `suggestedAction`(`run-sdlc-fix`/`cleanup`/`nothing-to-do`/
|
||||
`blocked-dirty`)。多久跑一次由使用者自己排(cron 或他自己的循環機制),
|
||||
本工具不長出排程器。
|
||||
|
||||
PR 合併或關閉時它會順手清掉那棵工作樹,**本機分支與遠端分支都留著**;工作樹裡還有
|
||||
沒提交的東西就會擋下來(`blocked-dirty`),由使用者自己處理。永遠不會被合併也不會被
|
||||
關閉的那些 PR,用手動出口清:
|
||||
|
||||
```
|
||||
node scripts/worktree-remove.js --repo <owner/name> --branch <分支名>
|
||||
```
|
||||
完成後依既有 commit 分類規則提交,先用 `scripts/pr-create.js --dry-run` 檢查,再實跑開 PR。回報工作包、分支、worktree、完成待辦、commit 與 PR。
|
||||
|
||||
## 邊界
|
||||
|
||||
- 第一段**不改任何一行程式碼**、不勾待辦、不提交、不開 PR——那些是後面幾段的事。
|
||||
- 第二段只實作與勾選。**不提交、不開 PR、不停錶**——那是第三段的事。
|
||||
- 第三段不改任何一行程式碼。到這裡實作已經結束,要改就回第二段改完再來。
|
||||
- **不在主工作區動手。** 第二段與第三段的每一個動作都在 `branch-prep` 建出來的那棵
|
||||
工作樹裡進行,包含跑測試與 `--path`。
|
||||
- 不把依賴、建置產物或 `.env` 這類機密檔案複製到工作樹裡,也不做連結——
|
||||
兩棵工作樹共用同一份依賴,正好把工作樹要隔離的東西又接回去。
|
||||
- 工作樹建不起來時中止,**不退回原地切分支**。
|
||||
- 不把「已測試通過」這種空話寫進 PR 描述,也不為了通過檢查而拼湊內容。
|
||||
- 不代替使用者決定 commit 的類型與描述;`--type` 與 `--subject` 都要是這次真的做了什麼。
|
||||
- 不把實作規範或註解格式寫進目標專案的任何檔案。
|
||||
- 不改與待辦無關的程式碼;順手想修的東西記下來說出來,不要摸進這次的變更裡。
|
||||
- 不為了勾選在議題上留留言。
|
||||
- **不自動反覆執行 `pr-watch`**,也不因為它建議了 `run-sdlc-fix` 就自己去跑 `/sdlc-fix`——
|
||||
流程只由使用者明確叫用。
|
||||
- 不自行建立標籤。缺「進行中」標籤時中止並請使用者建立。
|
||||
- 不代替使用者停錶,也不在被鎖擋下時繞過去。
|
||||
- 不替使用者決定來源分支。
|
||||
- **不寫任何本機狀態檔。** 進度完全由 Gitea 上的 assignee、標籤、碼錶與 git 本身推導,
|
||||
換一台機器或換一個 agent 都要能直接接手。
|
||||
|
||||
## 交接規格閘門
|
||||
|
||||
讀取工作包後,若它有介面契約,先完成第一個規格待辦:填妥介面、產出者、
|
||||
消費者、形狀四欄,並附上該 `interfaceType` 的範例資料。四欄表格與範例資料
|
||||
是同一待辦下的兩個驗收;兩者完成前不得進入後續程式實作。
|
||||
|
||||
資料、架構、排程工作包依其 `type` 先完成對應規格待辦;純內部小型實作可直接
|
||||
進入下一個待辦。規格仍寫回工作包議題既有段落,不另建本機正本。
|
||||
- 不修改工作包範圍外的檔案。
|
||||
- 不切換主工作區分支。
|
||||
- 不操作碼錶、不補登工時、不產生耗時統計。
|
||||
|
||||
+15
-163
@@ -3,178 +3,30 @@ description: 僅由 /sdlc-plan 指令叫用。把一段口語需求轉成結構
|
||||
|
||||
# sdlc-plan
|
||||
|
||||
把使用者給的一段需求,變成一顆結構完整、下游指令讀得動的需求議題。
|
||||
|
||||
這份檔案是流程正本。各平台的轉接檔只是指回這裡,不要把規則抄過去。
|
||||
|
||||
## 輸入
|
||||
|
||||
使用者給的東西可能是下列任一種,也可能三種混用:
|
||||
|
||||
- **自由文字** — 一段口語描述。
|
||||
- **規格檔** — 一個檔案路徑,內容是既有的規格或筆記。
|
||||
- **議題編號** — 既有議題的編號,用來補充脈絡或作為延伸的起點。
|
||||
|
||||
先把三種來源讀齊,再開始問問題。規格檔用檔案讀取工具讀;議題編號用
|
||||
`scripts/issue-extract.js` 取(若該腳本尚未可用,改用 `scripts/issue-create.js` 以外的
|
||||
既有讀取途徑,並在摘要中註明資料來源)。
|
||||
|
||||
## 計時範圍
|
||||
|
||||
這份正本把整個 /sdlc-plan 的耗時記成兩段,合起來就是這道指令實際花掉的時間:
|
||||
|
||||
- **議題建立之前** — 在「記下開始時間」記下起點,在「補登規劃時間,然後起錶」補上去。
|
||||
議題還不存在,沒有標的可起錶。
|
||||
- **議題建立之後** — 在「補登規劃時間,然後起錶」起錶,在「停錶並回報」停錶。
|
||||
|
||||
起與停都寫在這一份裡,**錶不跨階段跑**:跑完就去開會而錶跑一整天,報表當場失真。
|
||||
反過來,別顆議題上的錶一律不碰——那一段時間該記在哪顆議題上只有使用者知道。
|
||||
|
||||
## 〔可委派〕的意思
|
||||
|
||||
標題後綴 `〔可委派〕` 的步驟只在意結果:**你的環境若能把工作交給子代理,就交出去,
|
||||
只把結果帶回來;不能就自己做。** 沒有這個後綴的步驟一律自己做。
|
||||
|
||||
怎麼挑、為什麼這樣挑,見 `references/delegation.md`——判準只有那一份,這裡不複述。
|
||||
把自由文字、規格檔或既有議題整理成需求議題。
|
||||
|
||||
## 步驟
|
||||
|
||||
### 1. 記下開始時間
|
||||
|
||||
讀齊輸入、逐項詢問、組出議題內容,往往是整個 plan 最耗時的一段,而它發生在議題建立
|
||||
**之前**——那時候沒有標的可起錶。所以先把此刻的時間記下來,等議題建立之後補登上去:
|
||||
1. 讀齊輸入,列出九個段落中已有依據與缺漏。
|
||||
2. 一次問一題補齊缺漏;未獲回答的內容放入「未決事項」,不得自行編造。
|
||||
3. 套用 `templates/requirement-issue.md`,填入總覽、背景、目標、非目標、領域名詞表、流程圖、驗收標準、影響範圍與未決事項。
|
||||
4. 用 `scripts/labels-list.js` 取得既有標籤,只能選既有標籤。
|
||||
5. 寫入前先執行:
|
||||
|
||||
```
|
||||
node -e "console.log(new Date().toISOString())"
|
||||
node scripts/issue-create.js --repo <owner/name> --title "<標題>" --body-file <暫存檔> --labels "<標籤>" --dry-run
|
||||
```
|
||||
|
||||
記下它,一路帶到「補登規劃時間,然後起錶」那一步。**不要憑印象回推**:補登的長度就是
|
||||
報表上規劃階段的數字。
|
||||
確認內容後移除 `--dry-run` 實跑。重跑以標題查重,不建立重複議題。
|
||||
|
||||
### 2. 讀齊輸入,列出還缺什麼
|
||||
## 流程圖限制
|
||||
|
||||
把九個段落逐一對照使用者給的材料,列出哪些段落已經有依據、哪些沒有。
|
||||
|
||||
### 3. 逐項詢問
|
||||
|
||||
**一次問一題**,等使用者回答完再問下一題,讓他能看著前一題的答案回答下一題。
|
||||
|
||||
每一題都附上你的建議與理由,讓使用者多數時候只要點頭;同時保留讓他自己寫答案的餘地。
|
||||
|
||||
**未獲得答覆的欄位不得自行編造。** 使用者沒說過的目標、沒提過的驗收標準,一個字都不能自己
|
||||
填。問不到就放進「未決事項」,那一段本來就是給未決的東西用的。
|
||||
|
||||
### 4. 組出議題內容
|
||||
|
||||
套用 `templates/requirement-issue.md`,依序填滿九個段落:
|
||||
|
||||
1. **總覽** — 一句話講完這件事在做什麼,讓非技術的利害關係人不必讀完技術細節。圖解版總覽的
|
||||
連結此時先留空,由後續流程回填。
|
||||
2. **背景** — 不超過三行。為什麼現在要做這件事。
|
||||
3. **目標** — 可量測。寫得出「怎樣算達成」才算數。
|
||||
4. **非目標** — 明列這次不做什麼,用來抵抗範圍蔓延。
|
||||
5. **領域名詞表** — 這份需求裡會反覆出現的詞,各給一行定義,讓團隊對同一個詞的理解一致。
|
||||
6. **流程圖** — 見下方「流程圖的限制」。
|
||||
7. **驗收標準** — 逐條列出,每一條都要能被驗證。
|
||||
8. **影響範圍** — 會動到哪些 repo、哪些既有功能。
|
||||
9. **未決事項** — 問不到答案、或需要他人拍板的事。
|
||||
|
||||
### 5. 挑標籤
|
||||
|
||||
先用 `scripts/labels-list.js --repo <owner/name>` 取得該 repo 的既有標籤,**只能從這份清單裡
|
||||
挑**。找不到合適的就不貼。**不得自行建立新標籤** —— 標籤體系由專案維護者決定,不該在多個
|
||||
repo 之間長出雜草。
|
||||
|
||||
### 6. 先試跑,再寫入
|
||||
|
||||
把組好的內容寫到一個暫存檔,然後:
|
||||
|
||||
```
|
||||
node scripts/issue-create.js --repo <owner/name> --title "<標題>" --body-file <暫存檔> \
|
||||
--labels "<標籤1,標籤2>" --dry-run
|
||||
```
|
||||
|
||||
`--dry-run` 會印出將要送出的請求而不真的寫入。確認無誤後拿掉該旗標再跑一次。
|
||||
|
||||
同一段需求重跑不會產生第二顆議題:`issue-create` 以標題查重,發現同名議題就回傳既有那一顆
|
||||
並把 `created` 設為 `false`。
|
||||
|
||||
### 7. 補登規劃時間,然後起錶
|
||||
|
||||
議題有了,計時才有標的。**先補登、再起錶**,兩步指向同一顆議題。
|
||||
|
||||
先把「記下開始時間」到議題建立那一段補上去:
|
||||
|
||||
```
|
||||
node scripts/time-log.js --repo <owner/name> --index <編號> --since <記下的開始時間> --dry-run
|
||||
```
|
||||
|
||||
長度由腳本自己算,不必自己做減法,也不會讓兩邊的時鐘各算一次。終點看議題是不是這一輪
|
||||
建立的:是就補到**議題建立那一刻**,不是(對既有議題重跑)就補到**現在**——那一輪的
|
||||
規劃時間照樣要進報表。確認無誤後拿掉 `--dry-run` 再跑一次。
|
||||
|
||||
**不設時間上限,照實補登。** 中途去開會的那兩個小時會一起被算進去,這是刻意的:換來
|
||||
這個流程不必為此多長一題出來問使用者。時間記多了看得出來,記不到就永遠找不回來。
|
||||
|
||||
補登完才起錶:
|
||||
|
||||
```
|
||||
node scripts/timer.js --repo <owner/name> --index <編號> --dry-run
|
||||
```
|
||||
|
||||
一樣先試跑,確認無誤後拿掉 `--dry-run` 再跑一次。
|
||||
|
||||
順序不能反過來:錶一旦跑在這顆議題上,`time-log` 就會跳過不補——那一段已經有錶在記了,
|
||||
再補一次會與錶涵蓋的區間重疊。**重跑是累計不是覆蓋**,每一輪各記一筆,報表上加總起來
|
||||
才是這顆議題真正花掉的規劃時間。
|
||||
|
||||
錶已經跑在別顆議題上時起錶會被擋下(`STOPWATCH_ON_OTHER_ISSUE`)。**照實告訴使用者
|
||||
是哪一顆,請他自己去停**,不要代勞:那一段時間該記在哪顆議題上只有他知道。順帶說明
|
||||
停錶不會動到任何既有的工作樹——碼錶只管時間、工作樹只管檔案。
|
||||
|
||||
### 8. 產生圖解版總覽 〔可委派〕
|
||||
|
||||
依 `references/artifact-contract.md` 從需求議題抽取結果組成 `schemaVersion: 1` JSON,
|
||||
執行 `node scripts/overview-render.js` 產生自包含 HTML 與 manifest。HTML 只給使用者檢視,
|
||||
不取代需求議題的詳細內容。若平台有 preview 能力就使用它;否則以短命 Node server
|
||||
服務 `.tmp/` 檔案供瀏覽器截圖。只有真正可用的 preview URL 才執行既有
|
||||
`node scripts/issue-update.js --overview-url <網址>`。
|
||||
產出 HTML 可委派;預覽截圖、附件上傳與任何 `issue-update` 寫回都不委派,由主流程
|
||||
執行並處理錯誤。
|
||||
|
||||
規劃階段沒有工作包,因此 `workPackages` 為空陣列,不產生工作包依賴圖。
|
||||
|
||||
### 9. 停錶並回報
|
||||
|
||||
回報之前先停錶,這一段計時到此為止:
|
||||
|
||||
```
|
||||
node scripts/timer.js --repo <owner/name> --index <編號> --stop
|
||||
```
|
||||
|
||||
它**只停這一顆**上的錶。錶本來就沒在跑不算失敗(`碼錶已停` 會是 `false` 並附一句
|
||||
說明),回報照樣做完。
|
||||
|
||||
把議題編號與網址告訴使用者。不要把整份議題內容再貼一次 —— 連結點進去就看得到。
|
||||
## 流程圖的限制
|
||||
|
||||
流程圖的抽象節點與邊保存於 JSON,由 renderer 重新產生 SVG;議題若需要保存圖表,
|
||||
由同一個 renderer 產生 Mermaid。節點數超過 12 或文字超過 8 字時拆圖或記錄
|
||||
`omitted` 原因,不由模型任意壓縮語意。
|
||||
流程圖只保留抽象節點與邊的文字描述;本流程不產生 HTML、SVG、manifest、截圖、附件或平台 preview。
|
||||
|
||||
## 邊界
|
||||
|
||||
- 不修改使用者的專案檔案。這個流程只讀輸入、寫 Gitea 議題。
|
||||
- 不建立標籤、不建立 Milestone、不建立專案看板。
|
||||
- 不關閉或刪除任何既有議題。
|
||||
- 規劃階段本身已含問題釐清,因此寫入 Gitea 前不再設額外的確認點;`--dry-run` 就是那道關卡。
|
||||
- 計時只動這顆需求議題:在它上面起錶、在它上面停錶、把規劃時間補登在它上面。
|
||||
**不停別顆議題上的錶**,被別顆的錶擋下時交還給使用者決定,不繞過去。
|
||||
|
||||
## Artifact 產物規則
|
||||
|
||||
圖解總覽不再套用 HTML 模板或依賴 Mermaid CDN。若此階段需要產生預覽,
|
||||
先依 `references/artifact-contract.md` 組成需求級 JSON,再執行
|
||||
`node scripts/overview-render.js`。HTML、SVG 與 manifest 只寫入 `.tmp/`;
|
||||
只有真正可用的 preview URL 才能使用既有 `--overview-url` 回寫。規劃階段沒有
|
||||
工作包時,`workPackages` 必須是空陣列,工作包全景不產生。
|
||||
- 不修改使用者專案檔案。
|
||||
- 不建立標籤、Milestone 或專案看板。
|
||||
- 不關閉或刪除既有議題。
|
||||
- 不產生任何預覽或 artifact。
|
||||
- 回報議題編號與網址,不重貼全文。
|
||||
|
||||
+7
-82
@@ -1,91 +1,16 @@
|
||||
name: sdlc-report
|
||||
description: 僅由 /sdlc-report 指令叫用。產出本週、指定月份或指定年份的工時報表,只印在終端。
|
||||
description: 僅由 /sdlc-report 指令叫用。週報、月報與年報目前不可用。
|
||||
|
||||
# sdlc-report
|
||||
|
||||
把 Gitea 上的碼錶紀錄整理成一份可以直接在週會上使用的工時報表。
|
||||
週報、月報、年報目前不可用;時間追蹤功能已移除。
|
||||
|
||||
這份檔案是流程正本。各平台的轉接檔只是指回這裡,不要把規則抄過去。
|
||||
## 入口契約
|
||||
|
||||
## 輸入
|
||||
執行 `scripts/report.js` 時仍使用既有腳本 JSON envelope,但固定回傳:
|
||||
|
||||
- **repo** — `owner/name`。沒給就問,不要猜。
|
||||
- **期間** — 三選一,沒給就是本週:
|
||||
- `--week` 本週一至今日(預設)
|
||||
- `--month YYYY-MM` 指定月份,含 W1–W5 分段小計
|
||||
- `--year YYYY` 指定年份,以月份分段小計
|
||||
|
||||
## 步驟
|
||||
|
||||
### 1. 取數字
|
||||
|
||||
```
|
||||
node scripts/report.js --repo <owner/name> [--week | --month YYYY-MM | --year YYYY]
|
||||
```json
|
||||
{"ok":false,"error":{"code":"REPORT_UNAVAILABLE","message":"週報、月報、年報目前不可用;時間追蹤功能已移除。"}}
|
||||
```
|
||||
|
||||
腳本回傳一行 JSON,裡面已經算好總計、分段小計與逐議題明細,**時分格式也一併算好了**
|
||||
(`實際工時`、`落差工時`)。直接取用那些字串,不要自己再乘一次三千六百 —— 報表上的數字
|
||||
自己算錯,比沒有報表更糟。
|
||||
|
||||
要回頭補印過去的某一週,加 `--today YYYY-MM-DD` 指定「今天」是哪一天。
|
||||
|
||||
### 2. 套模板印出
|
||||
|
||||
套用 `templates/report.md`,佔位對應如下:
|
||||
|
||||
- `{{期間}}` 期間標籤(`期間.標籤`)
|
||||
- `{{範圍}}` 一行說明這份報表涵蓋哪個 repo、哪段日期、以幾小時當一人天
|
||||
- `{{實際工時}}`、`{{估算人天}}`、`{{已估實際}}`、`{{落差}}` 取自 `總計`
|
||||
- `{{分段}}` 每個分段一列表格列;**週報沒有分段,連同「分段小計」標題整段不印**——
|
||||
markdown 表格只留表頭不留資料列,在終端上看起來像壞掉,不像「本來就沒有」
|
||||
- `{{議題}}` 每顆議題一列表格列,議題欄寫成指回該議題的連結
|
||||
- `{{附註}}` 見下方「怎麼讀落差」;沒有要提醒的就填「無」
|
||||
|
||||
報表**只印在終端**。不要張貼到議題、PR、聊天室或任何其他管道——這份要給誰看,是使用者的
|
||||
決定,不是這個流程的。
|
||||
|
||||
### 3. 回報
|
||||
|
||||
印完就結束。不要順手去改議題、不要替使用者補登漏掉的工時。
|
||||
|
||||
## 期間怎麼切
|
||||
|
||||
三句話,沒有例外:
|
||||
|
||||
1. **一週為週一至週日。**
|
||||
2. **跨月的那一週依「該週週五所屬月份」歸屬。** 一筆工時因此只會落在一個月裡,
|
||||
不會被前後兩個月各算一次。
|
||||
3. **W1–W5 指該週五是當月第幾個週五。** 當月有幾個週五就有幾段,有五個就排到 W5。
|
||||
|
||||
舉例:2026-01 的第一個週五是 01-02,所以 2025-12-29(週一)那天的工時算在 2026 年 1 月的
|
||||
W1;2026-02-01(週日)那天的工時,它那一週的週五是 01-30,所以算在 2026 年 1 月的 W5,
|
||||
而不是 2 月。
|
||||
|
||||
年報同理:跨年的那一週也依週五歸屬,2025-12-29 的工時會出現在 2026 年的報表裡。
|
||||
|
||||
## 怎麼讀落差
|
||||
|
||||
落差 = 實際工時 − 估算。**正數代表超出估算,負數代表還有餘裕。**
|
||||
|
||||
**總計的落差只涵蓋有估算的議題。** 分子是 `已估實際秒`(那些議題的實際工時)而不是 `實際秒`
|
||||
(全部)——拿全部實際去比只有部分議題的估算,沒估算的工時會整批變成「超出估算」,落差就永遠
|
||||
是灌水的正數。報表上把 `實際工時` 與 `已估實際` 並排印出來,兩者差多少就是沒估算的部分有多大。
|
||||
|
||||
估算讀的是議題「關聯」段落裡的「估算人天」那一行。換算時一人天預設為 8 小時,團隊若不是
|
||||
這樣算,用 `--day-hours` 換掉。
|
||||
|
||||
有三件事要在 `{{附註}}` 裡講清楚,否則落差會被讀錯:
|
||||
|
||||
- **沒寫估算的議題,落差是空的,不是零。** 輸出裡是 `null`;一顆估算都沒有時,總計的落差也是
|
||||
`null`,不要印成 0。
|
||||
- **工作包還沒做完時,落差本來就會是負的。** 估算是整顆工作包的,實際卻只是這段期間內的
|
||||
那一部分;只有工作包在這段期間內收掉,兩者才真的可以比。
|
||||
- **`略過` 不為零時要說出來。** 那是查不到議題資訊的工時筆數,它們沒有被算進任何數字裡。
|
||||
|
||||
## 邊界
|
||||
|
||||
- 不張貼。報表只印在終端。
|
||||
- 不寫入 Gitea:不改議題、不補登工時、不動碼錶。腳本唯一的非 GET,是四層前置檢查打在不存在的
|
||||
議題 0 上那支寫入權探針,它不改動任何東西。
|
||||
- 不替使用者決定跳過哪些日子。腳本只算實際記錄到的工時,不扣假日、不補上沒按碼錶的時間。
|
||||
- 不跨 repo 彙總。一次一個 repo,要看別的就再跑一次。
|
||||
不讀取 Gitea 工時、不計算期間、不產生 Markdown,不寫入議題、PR、聊天室或其他 artifact。
|
||||
|
||||
Reference in New Issue
Block a user