feat: 移除計時並停用報表預覽

This commit is contained in:
2026-09-21 17:41:44 +08:00
parent 5a259a95af
commit 7f723adff8
34 changed files with 100 additions and 6113 deletions
+17 -287
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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。