sdlc 0.1.6 發佈:四階段收尾一律回報 #22

Merged
admin merged 2 commits from develop into master 2026-08-26 01:33:54 +00:00
10 changed files with 350 additions and 11 deletions
Showing only changes of commit 4a4feaf6ef - Show all commits
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "jsc-sdlc",
"version": "0.1.5",
"version": "0.1.6",
"description": "開發生命週期:規劃、分析、實作、維護(wiki 追蹤)",
"skills": "./skills",
"author": {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "jsc-sdlc",
"version": "0.1.5",
"version": "0.1.6",
"description": "開發生命週期:規劃、分析、實作、維護(wiki 追蹤)",
"skills": "./skills"
}
+6 -4
View File
@@ -23,6 +23,7 @@ Marketplace 統一為 `jsc`(https://gitea.jsc.idv.tw/plugins/meta.git),安
| 腳本 | 用途 |
| --- | --- |
| `tools/wp-gate.sh` | 工作包 PR 閘門,把「PR 沒合併就不開下一包」從內文敘述變成程式判定,共兩個用法。`check {owner}/{repo} {index} [--since {ISO 時間}]` 查 PR:已合併就呼叫 `jsc-hooks` 的 `sdlc-gate.sh wp-unlock` 解鎖並印 `status=merged`;沒合併就印 `status=open` 或 `status=closed-unmerged`(被關掉但沒合併不算完成),接著把 issue 留言、審查評語、行內留言全部逐行印出(`--since` 只印更新的,值取上一輪的 `latest=`),最後一行印 `latest={最新一筆留言的時間戳}` 供寫回分析頁。`lock {owner}/{repo} {index}` 在 PR 開好後轉呼叫 `sdlc-gate.sh wp-lock` 記下未結清。第一行固定 `status=...` 供程式判讀,其後為繁中說明;結束碼 `0`=已合併或無阻擋、`1`=未合併(擋住,呼叫端必須停下來逐筆修留言)、`2`=用法錯誤、`3`=相依工具或 PR 查不到(**查不到就擋,不放行**)。狀態檔由 `jsc-hooks` 管(`$JSC_HOME/wp/{owner}-{repo}.pr`,不綁 session,開新對話照樣擋),本檔只負責查詢與結清 |
| `tools/stage-report.sh` | 階段收尾回報,四個階段共用。`stage-report.sh {plan\|analyze\|implement\|maintain}` 加 `--page TYPE:PAGE`(本階段寫過的每一頁,可重複)產出繁中回報:模型閘門判定(轉述 `sdlc-gate.sh report`,不自評)、工作日誌連結(`--worklog`、`--worklog-heading` 組出導向條目標題的錨點)、所有寫入的 wiki 絕對網址。實作階段再加 `--worktree`、`--source-branch`、`--work-branch`、`--target-branch`、`--pr`,來源分支在不在遠端、工作分支幾個 commit、推送了沒,都由腳本現查。沒有工作日誌時警告使用者檢查,並用 `--pending-file`、`--log-hash` 把內容交給 `jsc-log/tools/worklog-pending.sh` 暫存,下次寫日誌一併寫入。結束碼 `0`=完整、`1`=有警告(**警告不是阻擋**)、`2`=用法錯誤、`3`=相依工具找不到 |
## Skills 目錄
@@ -32,19 +33,19 @@ Marketplace 統一為 `jsc`(https://gitea.jsc.idv.tw/plugins/meta.git),安
### `plan`
規劃:讀計畫目錄 → 補充或新建計畫 → 決策樹持續提問,補全目標、範圍、可行性到達成共識(判定規則見 `references/consensus.md`,一輪不算問完)→ 產生使用者故事 → 寫回 `PLAN_{HASH}`。純邏輯,禁止程式碼與修改檔案。
規劃:讀計畫目錄 → 補充或新建計畫 → 決策樹持續提問,補全目標、範圍、可行性到達成共識(判定規則見 `references/consensus.md`,一輪不算問完)→ 產生使用者故事 → 寫回 `PLAN_{HASH}` → 階段回報(`tools/stage-report.sh`)。純邏輯,禁止程式碼與修改檔案。
### `analyze`
分析:先確認來源分支 → 持續提問到達成共識(`references/consensus.md`)→ 搭配現況(工作目錄與 `REPO_{HASH}` 盤點複用)分析使用者故事 → WBS 產生編號工作包,`WP-01` 固定是獨立的交付、交接工作包,實作工作包相依於它 → CPM 估工時與天數 → TDD 拆待辦 → 寫回 `ANALYZE_{HASH}`。純邏輯,禁止程式碼與修改檔案。
分析:先確認來源分支 → 持續提問到達成共識(`references/consensus.md`)→ 搭配現況(工作目錄與 `REPO_{HASH}` 盤點複用)分析使用者故事 → WBS 產生編號工作包,`WP-01` 固定是獨立的交付、交接工作包,實作工作包相依於它 → CPM 估工時與天數 → TDD 拆待辦 → 寫回 `ANALYZE_{HASH}` → 階段回報(`tools/stage-report.sh`)。純邏輯,禁止程式碼與修改檔案。
### `implement`
實作:先確認來源分支(同時是 PR 目標)→ 產生工作證鎖定「未完成、無相依、無工作證」的工作包(交付工作包優先)→ 交付工作包開工前先確認交付內容(API 文件、由使用者輸入,見 `references/deliver-formats.md`)→ **動程式碼前先從 `origin/{source-branch}` 建立 worktree**(`.worktree/{analysis-HASH}/{repo}`,分支處理依決策樹詢問)→ 在 worktree 內逐項 TDD 實作、每完成一項立即更新 wiki → 程式碼審查 → **每完成一個工作包就 commit、push、PR 回來源分支**(一包一 PR),接著 `tools/wp-gate.sh lock` 上鎖 → **PR 未合併就不開下一包,判定在程式層**:領工作包前先跑 `tools/wp-gate.sh check`,未合併就把全部留言逐筆印出並擋住,以 sub agent 回原 worktree 逐筆修正、推同一條工作分支(不開第二個 PR),修不動或純討論的留言忽略但要在回報裡列出,處理過的時間戳寫回分析頁的 PR 欄位當下一輪的 `--since`;合併後解鎖並移除 worktree → 詢問交付文件格式(`DELIVER_{HASH}` wiki 頁或 Gitea 議題留言)並產出 → 詢問是否加入維護目錄。
實作:先確認來源分支(同時是 PR 目標)→ 產生工作證鎖定「未完成、無相依、無工作證」的工作包(交付工作包優先)→ 交付工作包開工前先確認交付內容(API 文件、由使用者輸入,見 `references/deliver-formats.md`)→ **動程式碼前先從 `origin/{source-branch}` 建立 worktree**(`.worktree/{analysis-HASH}/{repo}`,分支處理依決策樹詢問)→ 在 worktree 內逐項 TDD 實作、每完成一項立即更新 wiki → 程式碼審查 → **每完成一個工作包就 commit、push、PR 回來源分支**(一包一 PR),接著 `tools/wp-gate.sh lock` 上鎖 → **PR 未合併就不開下一包,判定在程式層**:領工作包前先跑 `tools/wp-gate.sh check`,未合併就把全部留言逐筆印出並擋住,以 sub agent 回原 worktree 逐筆修正、推同一條工作分支(不開第二個 PR),修不動或純討論的留言忽略但要在回報裡列出,處理過的時間戳寫回分析頁的 PR 欄位當下一輪的 `--since`;合併後解鎖並移除 worktree → 詢問交付文件格式(`DELIVER_{HASH}` wiki 頁或 Gitea 議題留言)並產出 → 詢問是否加入維護目錄 → 階段回報(`tools/stage-report.sh`,多報工作目錄與來源、工作、目標三條分支)。
### `maintain`
維護:讀取維護期內的專案,每個專案一個 sub agent:fetch 後切 develop、master 並對齊 `origin/{branch}` → 提出至少五種維護方法,依決策樹讓使用者挑要做哪些 → commit / push / PR → 更新前次維護時間。僅適用於維護期內已交付的專案;尚在實作中或未登記於 `MAINTAIN_CONTENTS` 的專案不適用。
維護:讀取維護期內的專案,每個專案一個 sub agent:fetch 後切 develop、master 並對齊 `origin/{branch}` → 提出至少五種維護方法,依決策樹讓使用者挑要做哪些 → commit / push / PR → 更新前次維護時間 → 階段回報(`tools/stage-report.sh`)。僅適用於維護期內已交付的專案;尚在實作中或未登記於 `MAINTAIN_CONTENTS` 的專案不適用。
<!-- JSC-SKILLS:END -->
@@ -57,6 +58,7 @@ Marketplace 統一為 `jsc`(https://gitea.jsc.idv.tw/plugins/meta.git),安
| `templates/repo-page.md`、`templates/repo-contents.md` | 存取庫盤點頁(功能與端點,附 commit sha)與盤點目錄 |
| `templates/deliver-page.md`、`templates/deliver-contents.md` | 交付頁(API 文件、新舊參數標示、驗證方式)與交付目錄 |
| `templates/maintain-contents.md` | 維護目錄(截止日 NULL = 永久維護) |
| `references/stage-report.md` | 階段回報:四階段都要交的三項(模型能力標籤、工作日誌連結、所有寫入的 wiki 連結)、實作階段多交的四項、沒寫日誌時的暫存規則、提前停止也要回報 |
| `references/model-gate.md` | 模型閘門:執行順序、各階段必要標籤、阻擋與回報的鐵則 |
| `references/tdd.md` | 接縫、紅綠循環規則、反模式 |
| `references/branch.md` | 分支規則:**一律以遠端 `origin/{branch}` 為準、動作前先 fetch**、分析前確認來源分支、實作沿用同一條來源分支作為 PR 目標(一包一 PR、PR 未合併不開下一包,閘門分工見該檔)、實作一律在 `.worktree/{HASH}/{repo}` 內進行(建立前問分支、PR 合併後才移除)、判定遠端預設分支、不破壞未提交變更 |
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "jsc-sdlc",
"version": "0.1.5",
"version": "0.1.6",
"description": "開發生命週期:規劃、分析、實作、維護(wiki 追蹤)",
"skills": "./skills/"
}
+49
View File
@@ -0,0 +1,49 @@
# 階段回報 — 四個階段收尾都要交的東西
規劃、分析、實作、維護跑完,最後一件事一定是階段回報。彙整由 `tools/stage-report.sh` 產出,
技能只負責把事實餵進去:寫過哪些 wiki 頁、有沒有寫工作日誌、實作階段的工作目錄與三條分支。
## 什麼時候回報
階段結束就回報,**提前停下來也算結束**:模型閘門擋下、工作包閘門擋下、沒有可選的計畫或工作包、
來源分支在遠端找不到——這些情況一樣要回報,而且更需要。停在半路卻沒有回報,看起來就跟沒跑過一樣。
## 每個階段都要回報的三項
| 項目 | 來源 | 沒有時怎麼辦 |
| --- | --- | --- |
| 模型能力標籤 | `jsc-hooks/hooks/sdlc-gate.sh report`,腳本自己讀 | 回報「查不到閘門狀態」,並要求重跑 `lock {stage}` |
| 工作日誌連結 | `--worklog` 給頁名、`--worklog-heading` 給條目標題,組成導向該條目的錨點連結 | 警告使用者檢查,並把內容暫存(見下節) |
| 所有寫入的 wiki 連結 | 每寫一頁就記一筆,收尾時用 `--page TYPE:PAGE` 全部餵進去 | 沒寫任何頁就據實回報「本階段沒有寫入任何 wiki 頁」 |
`--page` 的 `TYPE` 是頁名前綴(`PLAN`、`ANALYZE`、`DELIVER`、`REPO`、`MAINTAIN`、`LOG`)。腳本用它解析
該類型的 wiki 存取庫,再換成絕對網址,所以跨存取庫的頁面也連得到。目錄頁(`*_CONTENTS`)也算寫入,要列。
## 實作階段多回報四項
| 項目 | 選項 | 腳本自己查的部分 |
| --- | --- | --- |
| 工作目錄 | `--worktree PATH` | 無,原樣列出 |
| 來源分支 | `--source-branch NAME` | 遠端有沒有這一條。遠端找不到就標「只有本機」 |
| 工作分支 | `--work-branch NAME` | 相對來源分支的 commit 數、推送狀態(未 push、或還有幾個 commit 沒 push) |
| 目標分支 | `--target-branch NAME`、`--pr URL` | 省略目標分支時等同來源分支;沒有 PR 連結就列為警告 |
## 沒寫工作日誌就暫存
階段跑完卻沒有工作日誌,內容只留在對話裡,換一個工作階段就沒了。所以:
1. 把這個階段要記的日誌內容寫成一個檔案。
2. 收尾時一起餵進去:`--pending-file {檔案} --log-hash {LOG_{HASH} 的 HASH}`。
3. 腳本轉呼叫 `jsc-log/tools/worklog-pending.sh add`,把內容存進 `$JSC_HOME/worklog-pending/{HASH}/`,並在回報裡印出存放路徑。
4. 下次 `jsc-log:worklog` 寫入時,會先把暫存區的內容一併寫進日誌頁,寫成功才清掉暫存。
`{HASH}` 用 `jsc-gitea/tools/hash-id` 對程式碼存取庫的 `{owner}/{repo}` 算出來,和 `LOG_{HASH}` 同一個值。
## 結束碼
| 碼 | 意思 | 呼叫端要做的事 |
| --- | --- | --- |
| 0 | 回報完整 | 把輸出原樣貼給使用者 |
| 1 | 有警告(缺工作日誌、或實作階段沒有 PR) | 一樣把輸出貼給使用者。**這是警告不是阻擋**,階段的工作已經做完了 |
| 2 | 用法錯誤 | 修正參數重跑 |
| 3 | 找不到 `gitea.sh` 或 `sdlc-gate.sh` | 修好相依關係再重跑 |
+2 -1
View File
@@ -1,6 +1,6 @@
---
name: analyze
description: SDLC analysis stage. Gate on capability tags enforced in code by sdlc-gate (analyze requires reasoning-max), confirm the source branch, then pick a plan from PLAN_CONTENTS and question until consensus per references/consensus.md. Analyze its user stories against the current state (working directory plus REPO_{HASH} inventory for reuse), then run WBS with a standalone delivery/handover WP-01, CPM estimates and TDD todos. Write wiki page ANALYZE_{HASH} with real sample data; logic only - never write code or modify files. Use after planning and before implementation; not for writing code (that is implement), and not before a plan exists in PLAN_CONTENTS.
description: SDLC analysis stage. Gate on capability tags enforced in code by sdlc-gate (analyze requires reasoning-max), confirm the source branch, then pick a plan from PLAN_CONTENTS and question until consensus per references/consensus.md. Analyze its user stories against the current state (working directory plus REPO_{HASH} inventory for reuse), then run WBS with a standalone delivery/handover WP-01, CPM estimates and TDD todos. Write wiki page ANALYZE_{HASH} with real sample data, then close with tools/stage-report.sh - model tag verdict, worklog link, every wiki link written; logic only - never write code or modify files. Use after planning and before implementation; not for writing code (that is implement), and not before a plan exists in PLAN_CONTENTS.
---
# analyze
@@ -35,6 +35,7 @@ All wiki reads and writes go through `jsc-gitea:wiki`.
9. Split every work package into todos with **Test-Driven Development (TDD)**, formatted `[ ]` (open) / `[x]` (done). Each todo is one vertical slice: one seam, one test, one minimal implementation. Seams and anti-patterns: `references/tdd.md`. Completion condition: every work package carries at least one `[ ]` todo, and every todo names its seam and the behaviour its test asserts.
10. Apply `templates/analyze-page.md` to create or update the analysis page and write it back via `jsc-gitea:wiki`. The page content is Traditional Chinese, exactly as the template dictates. Completion condition: the page is saved on the wiki and carries every section the template dictates, including the source branch, the head sha and the 未決項 section (「無」 when there is none).
11. If the analysis page is new: add it to `ANALYZE_CONTENTS` per `templates/analyze-contents.md`, and flip the plan's status in `PLAN_CONTENTS` to the literal 「已分析」. Completion condition: `ANALYZE_CONTENTS` shows the new row and `PLAN_CONTENTS` shows the literal 「已分析」, both saved on the wiki.
12. **Stage report — the last thing this stage does, including every early stop** (the model gate blocked, the working tree did not match `origin/{source-branch}`, no plan was selectable). Run `tools/stage-report.sh analyze` with one `--page TYPE:{page}` per wiki page this run wrote — `ANALYZE_{HASH}`, `ANALYZE_CONTENTS`, `PLAN_CONTENTS`, and `REPO_{HASH}` plus `REPO_CONTENTS` when a re-inventory happened — plus `--worklog` and `--worklog-heading` when a work log entry exists. No work log yet: write this stage's log content to a file and pass `--pending-file {file} --log-hash {HASH}` so it is held for the next `jsc-log:worklog` run. Rules and exit codes: `references/stage-report.md`. Exit 1 is a warning, never a block. Completion condition: the script's output is reported to the user verbatim, and every wiki page this run wrote appears in it.
## Delivery package is WP-01
+7 -1
View File
@@ -1,6 +1,6 @@
---
name: implement
description: SDLC implementation stage. Gate on capability tags enforced in code by sdlc-gate (implement requires coding), then confirm the analysis page's source branch - it is both the worktree base and the PR target. An unmerged work package PR is a hard gate enforced in code by tools/wp-gate.sh: it reads every PR comment, sub agents fix them in the original worktree, and no next package starts until that PR merges. Claim a ready work package from ANALYZE_CONTENTS with a work ticket, confirm a delivery package's content type, then complete its TDD todos one at a time inside a worktree built from origin/{source-branch}, updating the wiki after every item, closing with jsc-review code-review, one PR back to the source branch, the chosen delivery document and an optional MAINTAIN_CONTENTS entry. Use when analysis is done and code must be written; not for planning or analysis.
description: SDLC implementation stage. Gate on capability tags enforced in code by sdlc-gate (implement requires coding), then confirm the analysis page's source branch - it is both the worktree base and the PR target. An unmerged work package PR is a hard gate enforced in code by tools/wp-gate.sh: it reads every PR comment, sub agents fix them in the original worktree, and no next package starts until that PR merges. Claim a ready work package from ANALYZE_CONTENTS with a work ticket, confirm a delivery package's content type, then complete its TDD todos one at a time inside a worktree built from origin/{source-branch}, updating the wiki after every item, closing with jsc-review code-review, one PR back to the source branch, the chosen delivery document, an optional MAINTAIN_CONTENTS entry, and a tools/stage-report.sh report covering the model tag verdict, the worklog link, every wiki link written, the worktree and the three branches. Use when analysis is done and code must be written; not for planning or analysis.
---
# implement
@@ -49,6 +49,12 @@ All wiki reads and writes go through `jsc-gitea:wiki`.
4. Issue comment: confirm the issue number with the user (propose the one referenced by the work package or the PR; never guess), then post via `jsc-gitea/tools/gitea.sh api POST /repos/{owner}/{repo}/issues/{n}/comments` with the body passed in as a UTF-8 file — real newlines, never a literal `\n`.
5. Completion condition: the chosen format has actually been produced, and you have reported where it landed (wiki page name, or the comment URL).
13. Ask per `jsc-ask:ask` rules whether to register this project for maintenance: append to `MAINTAIN_CONTENTS` with `templates/maintain-contents.md`. Required: repository `{owner}/{repo}`, maintenance method, start date. Optional: end date (NULL = maintain forever), last-maintained time. Completion condition: the user has answered, and a chosen registration is saved on the wiki.
14. **Stage report — the last thing this stage does, including every early stop** (the work package gate blocked, no work package was selectable, the source branch was missing from the remote). Run `tools/stage-report.sh implement` with:
- one `--page TYPE:{page}` per wiki page this run wrote — `ANALYZE_{HASH}`, `DELIVER_{HASH}` and `DELIVER_CONTENTS`, `MAINTAIN_CONTENTS`;
- `--worklog` and `--worklog-heading` when a work log entry exists; otherwise write this stage's log content to a file and pass `--pending-file {file} --log-hash {HASH}`, which holds it for the next `jsc-log:worklog` run;
- `--worktree {path} --source-branch {name} --work-branch {name} --pr {url}` — the script reads the commit count, the push state and whether the source branch exists on the remote by itself, so pass the names, not your own count.
Rules and exit codes: `references/stage-report.md`. Exit 1 is a warning, never a block. Completion condition: the script's output is reported to the user verbatim, and it names the worktree, all three branches and every wiki page this run wrote.
## Rules
+2 -1
View File
@@ -1,6 +1,6 @@
---
name: maintain
description: SDLC maintenance stage. Gate on capability tags enforced in code by sdlc-gate (maintenance requires no specific tag, but the actual model id must be determinable from the transcript), read projects still inside their maintenance window from MAINTAIN_CONTENTS, then run one sub agent per project: switch to develop or master, propose at least five maintenance actions, commit to a new branch, push, and PR. Update the last-maintained timestamp afterward. Use for periodic upkeep of delivered projects inside their maintenance window; not for projects still mid-implementation or not yet registered in MAINTAIN_CONTENTS.
description: SDLC maintenance stage. Gate on capability tags enforced in code by sdlc-gate (maintenance requires no specific tag, but the actual model id must be determinable from the transcript), read projects still inside their maintenance window from MAINTAIN_CONTENTS, then run one sub agent per project: switch to develop or master, propose at least five maintenance actions, commit to a new branch, push, and PR. Update the last-maintained timestamp afterward, then close with tools/stage-report.sh - model tag verdict, worklog link, every wiki link written. Use for periodic upkeep of delivered projects inside their maintenance window; not for projects still mid-implementation or not yet registered in MAINTAIN_CONTENTS.
---
# maintain
@@ -26,3 +26,4 @@ All wiki reads and writes go through `jsc-gitea:wiki`.
3. Commit the changes to a new branch per `jsc-git:commit`, push, then open a PR per `jsc-git:pr` back to the branch of step 3.1, passing it explicitly as the base. Completion condition: the PR exists, and you have reported its URL and number.
4. Update the project's last-maintained field (the zh-TW column 「前次維護時間」) in `MAINTAIN_CONTENTS` to today. Completion condition: `MAINTAIN_CONTENTS` shows today's date in 「前次維護時間」 for that project, saved on the wiki.
4. The main agent reports the summary: maintenance methods applied per project, PR links, and failure reasons. The report and all generated wiki content, commits, and PR descriptions stay Traditional Chinese per the STE100 rule. Completion condition: the summary names every project read in step 2, each with its applied methods and either a PR link or the reason it was skipped.
5. **Stage report — the last thing this stage does, including when no project was in window.** Run `tools/stage-report.sh maintain` with one `--page MAINTAIN:{page}` per wiki page this run wrote (`MAINTAIN_CONTENTS` counts), plus `--worklog` and `--worklog-heading` when a work log entry exists. No work log yet: write this stage's log content to a file and pass `--pending-file {file} --log-hash {HASH}` so it is held for the next `jsc-log:worklog` run. Rules and exit codes: `references/stage-report.md`. Exit 1 is a warning, never a block. Completion condition: the script's output is reported to the user verbatim, and every wiki page this run wrote appears in it.
+2 -1
View File
@@ -1,6 +1,6 @@
---
name: plan
description: SDLC planning stage. Gate on capability tags enforced in code by sdlc-gate (plan requires reasoning-max, verified against the transcript's actual model id), pick or create a plan from PLAN_CONTENTS, then run a decision tree until goal, scope, and feasibility reach consensus. Produce user stories into wiki page PLAN_{HASH}. Logic only - never write code or modify files. Use when the user wants to start or refine a plan; not for analysis or implementation.
description: SDLC planning stage. Gate on capability tags enforced in code by sdlc-gate (plan requires reasoning-max, verified against the transcript's actual model id), pick or create a plan from PLAN_CONTENTS, then run a decision tree until goal, scope, and feasibility reach consensus. Produce user stories into wiki page PLAN_{HASH}, then close with tools/stage-report.sh - model tag verdict, worklog link, every wiki link written. Logic only - never write code or modify files. Use when the user wants to start or refine a plan; not for analysis or implementation.
---
# plan
@@ -27,6 +27,7 @@ All wiki reads and writes go through `jsc-gitea:wiki`.
5. Turn the consensus into **user stories** (the zh-TW pattern 「身為⋯⋯我想要⋯⋯以便⋯⋯」), one per line. Completion condition: every consensus item is covered by at least one user story in that pattern, and no user story rests on an unanswered question.
6. Apply `templates/plan-page.md` to create or update the plan page, and write it back via `jsc-gitea:wiki`. The page content is Traditional Chinese, exactly as the template dictates. Completion condition: the page is saved on the wiki and carries every section the template dictates — goal, scope, feasibility, user stories and the consensus summary — with no placeholder left unfilled.
7. If the plan page is new, add it to `PLAN_CONTENTS` using the entry format of `templates/plan-contents.md`, with status set to the literal 「未分析」. Completion condition: `PLAN_CONTENTS` shows the plan's row with the literal 「未分析」, saved on the wiki.
8. **Stage report — the last thing this stage does, including every early stop** (the model gate blocked, no plan was selectable). Run `tools/stage-report.sh plan` with one `--page PLAN:{page}` per wiki page this run wrote (`PLAN_{HASH}` and `PLAN_CONTENTS` both count), plus `--worklog` and `--worklog-heading` when a work log entry exists. No work log yet: write this stage's log content to a file and pass `--pending-file {file} --log-hash {HASH}` so it is held for the next `jsc-log:worklog` run. Rules and exit codes: `references/stage-report.md`. Exit 1 is a warning, never a block. Completion condition: the script's output is reported to the user verbatim, and every wiki page this run wrote appears in it.
## Hard limits
+279
View File
@@ -0,0 +1,279 @@
#!/usr/bin/env sh
# stage-report.sh — SDLC 階段收尾回報(規劃、分析、實作、維護共用)。
#
# 為什麼要有這支腳本:階段結束要回報的東西是固定的——模型閘門判定、工作日誌連結、所有寫入的
# wiki 連結,實作階段再加上工作目錄與三條分支。寫在技能內文裡靠模型自己記,少一項看不出來;
# 所以彙整搬到程式層:頁名換成絕對網址、commit 數與推送狀態由 git 現查、沒有工作日誌就當場
# 警告並把內容存進暫存區,全部由本檔產出,技能只負責把事實餵進來。
#
# 用法:
# stage-report.sh <stage> [選項]
# <stage> = plan | analyze | implement | maintain
#
# 共用選項:
# --page TYPE:PAGE 本階段寫入的 wiki 頁,可重複(例:--page ANALYZE:ANALYZE_D3F1A2B0)
# --worklog PAGE 已寫入的工作日誌頁名(例:LOG_D3F1A2B0)
# --worklog-heading TEXT 日誌條目標題,用來組出導向該條目的錨點連結
# --pending-file FILE 沒有工作日誌時,要暫存起來的日誌內容檔
# --log-hash HASH 暫存歸屬的工作日誌 hash(即 LOG_{HASH} 的 HASH)
#
# 實作階段選項:
# --worktree PATH 工作目錄(worktree 路徑)
# --source-branch NAME 來源分支(本檔自行判定遠端有沒有這一條)
# --work-branch NAME 工作分支(本檔自行算 commit 數與推送狀態)
# --pr URL 目標分支的 PR 連結
# --target-branch NAME 目標分支(省略時等同來源分支)
#
# 輸出: 繁體中文 markdown 階段回報,直接貼給使用者。
# 結束碼: 0=回報完整 1=有警告(缺工作日誌,內容已暫存) 2=用法錯誤 3=相依工具找不到
#
# 陷阱:
# - 結束碼 1 是警告,不是阻擋:階段的工作已經做完了,擋下去只會讓使用者拿不到回報。
# - 缺工作日誌時一定要給 --pending-file 與 --log-hash,否則內容留在對話裡,換一個工作階段就沒了。
# - 模型閘門那一列只轉述 sdlc-gate.sh report 的結果,不自己判定標籤。
set -u
script_dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
plugin_root="${CLAUDE_PLUGIN_ROOT:-$script_dir/..}"
usage() {
cat >&2 <<'EOF'
用法:
stage-report.sh <plan|analyze|implement|maintain> [選項]
--page TYPE:PAGE 本階段寫入的 wiki 頁,可重複
--worklog PAGE 已寫入的工作日誌頁名
--worklog-heading TEXT 日誌條目標題(組錨點用)
--pending-file FILE 沒有工作日誌時要暫存的內容檔
--log-hash HASH 暫存歸屬的工作日誌 hash
--worktree PATH 實作:工作目錄
--source-branch NAME 實作:來源分支
--work-branch NAME 實作:工作分支
--target-branch NAME 實作:目標分支(省略時同來源分支)
--pr URL 實作:PR 連結
結束碼: 0=完整 1=缺工作日誌(已暫存,警告) 2=用法錯誤 3=相依工具找不到
EOF
exit 2
}
# 找相依腳本。兩種版面都要顧到:並排存取庫(開發用)與已安裝 plugin(各版本一個目錄)。
resolve_dep() { # $1=並排目錄名(gitea、hooks、log) $2=plugin 內的相對路徑
_name="$1"; _rel="$2"
for _c in "$plugin_root/../$_name/$_rel" "$plugin_root/../jsc-$_name/$_rel"; do
[ -f "$_c" ] && { printf '%s\n' "$_c"; return 0; }
done
_c=$(ls -d "$plugin_root"/../../"jsc-$_name"/*/"$_rel" \
"$plugin_root"/../../"$_name"/*/"$_rel" \
"$HOME"/.claude/plugins/cache/*/"jsc-$_name"/*/"$_rel" 2>/dev/null \
| sort | tail -n1)
[ -n "$_c" ] && [ -f "$_c" ] && { printf '%s\n' "$_c"; return 0; }
return 1
}
gitea_sh() {
if [ -n "${JSC_GITEA_TOOLS:-}" ] && [ -f "$JSC_GITEA_TOOLS/gitea.sh" ]; then
printf '%s\n' "$JSC_GITEA_TOOLS/gitea.sh"; return 0
fi
resolve_dep gitea tools/gitea.sh
}
sdlc_gate_sh() {
if [ -n "${JSC_HOOKS_DIR:-}" ] && [ -f "$JSC_HOOKS_DIR/sdlc-gate.sh" ]; then
printf '%s\n' "$JSC_HOOKS_DIR/sdlc-gate.sh"; return 0
fi
resolve_dep hooks hooks/sdlc-gate.sh
}
stage="${1:-}"
case "$stage" in
plan|analyze|implement|maintain) shift ;;
*) usage ;;
esac
pages=''
worklog=''
worklog_heading=''
pending_file=''
log_hash=''
worktree=''
source_branch=''
work_branch=''
target_branch=''
pr_url=''
while [ "$#" -gt 0 ]; do
case "$1" in
--page) [ "$#" -ge 2 ] || usage; pages="$pages$2
"; shift 2 ;;
--worklog) [ "$#" -ge 2 ] || usage; worklog="$2"; shift 2 ;;
--worklog-heading) [ "$#" -ge 2 ] || usage; worklog_heading="$2"; shift 2 ;;
--pending-file) [ "$#" -ge 2 ] || usage; pending_file="$2"; shift 2 ;;
--log-hash) [ "$#" -ge 2 ] || usage; log_hash="$2"; shift 2 ;;
--worktree) [ "$#" -ge 2 ] || usage; worktree="$2"; shift 2 ;;
--source-branch) [ "$#" -ge 2 ] || usage; source_branch="$2"; shift 2 ;;
--work-branch) [ "$#" -ge 2 ] || usage; work_branch="$2"; shift 2 ;;
--target-branch) [ "$#" -ge 2 ] || usage; target_branch="$2"; shift 2 ;;
--pr) [ "$#" -ge 2 ] || usage; pr_url="$2"; shift 2 ;;
*) echo "[jsc][階段回報][ERR]:不認得的選項「$1」。" >&2; usage ;;
esac
done
GITEA=$(gitea_sh) || { echo '[jsc][階段回報][ERR]:找不到 jsc-gitea/tools/gitea.sh,無法把頁名換成網址。' >&2; exit 3; }
GATE=$(sdlc_gate_sh) || { echo '[jsc][階段回報][ERR]:找不到 jsc-hooks/hooks/sdlc-gate.sh,無法轉述閘門判定。' >&2; exit 3; }
stage_zh() {
case "$1" in
plan) echo '規劃' ;;
analyze) echo '分析' ;;
implement) echo '實作' ;;
maintain) echo '維護' ;;
esac
}
required_tag() {
case "$1" in
plan|analyze) echo 'reasoning-max' ;;
implement) echo 'coding' ;;
maintain) echo '無(模型 id 可判定即通過)' ;;
esac
}
# 錨點:Gitea wiki 的標題錨點比照 GitHub——轉小寫、空白換連字號、去掉標點;中文原樣保留。
anchor_of() {
printf '%s' "$1" \
| tr 'A-Z' 'a-z' \
| sed 's/[][()#。,、:;!?,.:;!?]//g; s/[[:space:]]\{1,\}/-/g'
}
page_url() { # TYPE PAGE -> 絕對網址;查不到就回空字串
_type="$1"; _page="$2"
_repo=$("$GITEA" wiki-repo "$_type" 2>/dev/null) || return 1
"$GITEA" wiki-url "$_repo" "$_page" 2>/dev/null || return 1
}
# ---- 模型能力標籤 ----
gate_line=$("$GATE" report 2>/dev/null || true)
gate_stage=$(printf '%s' "$gate_line" | awk '{print $2}')
gate_tag=$(printf '%s' "$gate_line" | awk '{print $3}')
gate_model=$(printf '%s' "$gate_line" | awk '{print $4}')
if [ -z "$gate_line" ]; then
gate_result="查不到閘門狀態(未上鎖或狀態檔不在),請重跑 sdlc-gate.sh lock $stage"
elif [ "$gate_stage" != "$stage" ]; then
gate_result="不符:本階段為 $stage,鎖上的卻是 $gate_stage,請重跑 sdlc-gate.sh lock $stage"
else
gate_result="通過(必要標籤 $(required_tag "$stage"),實際模型 ${gate_model:-未知},鎖上時的標籤 ${gate_tag:-無})"
fi
# ---- 工作日誌 ----
warn=0
worklog_cell=''
if [ -n "$worklog" ]; then
wl_url=$(page_url LOG "$worklog" || true)
if [ -n "$wl_url" ]; then
if [ -n "$worklog_heading" ]; then
worklog_cell="[$worklog_heading]($wl_url#$(anchor_of "$worklog_heading"))"
else
worklog_cell="[$worklog]($wl_url)"
fi
else
worklog_cell="$worklog(取不到網址,請確認 LOG 的 wiki 存取庫設定)"
fi
else
warn=1
worklog_cell='**尚未寫入**'
fi
# ---- 暫存 ----
pending_note=''
if [ "$warn" -eq 1 ]; then
if [ -n "$pending_file" ] && [ -n "$log_hash" ]; then
PENDING=$(resolve_dep log tools/worklog-pending.sh || true)
if [ -n "$PENDING" ]; then
if saved=$("$PENDING" add "$log_hash" "$pending_file" 2>&1); then
pending_note="已暫存到 $saved,下次 jsc-log:worklog 寫入時一併寫進去。"
else
pending_note="暫存失敗:$saved"
fi
else
pending_note='找不到 jsc-log/tools/worklog-pending.sh,這次的內容沒有暫存起來。'
fi
else
pending_note='沒有給 --pending-file 與 --log-hash,這次的內容沒有暫存起來。'
fi
fi
# ---- 分支資訊(實作階段) ----
branch_rows=''
if [ "$stage" = implement ]; then
wt="${worktree:-}"
git_in() { # 在工作目錄裡跑 git
if [ -n "$wt" ]; then git -C "$wt" "$@" 2>/dev/null; else git "$@" 2>/dev/null; fi
}
src_cell='未提供'
if [ -n "$source_branch" ]; then
if git_in show-ref --verify --quiet "refs/remotes/origin/$source_branch"; then
src_cell="origin/$source_branch(遠端)"
else
src_cell="$source_branch(**遠端找不到**,只有本機)"
fi
fi
work_cell='未提供'
if [ -n "$work_branch" ]; then
base="origin/$source_branch"
git_in show-ref --verify --quiet "refs/remotes/origin/$source_branch" || base="$source_branch"
count=$(git_in rev-list --count "$base..$work_branch" || true)
[ -n "$count" ] || count='?'
if git_in show-ref --verify --quiet "refs/remotes/origin/$work_branch"; then
ahead=$(git_in rev-list --count "origin/$work_branch..$work_branch" || true)
if [ "${ahead:-0}" = 0 ]; then push_cell='已 push'; else push_cell="尚有 ${ahead} 個 commit 未 push"; fi
else
push_cell='**未 push**'
fi
work_cell="$work_branch($count commit、$push_cell)"
fi
tgt="${target_branch:-$source_branch}"
if [ -n "$pr_url" ]; then
tgt_cell="${tgt:-未提供}(PR $pr_url)"
else
tgt_cell="${tgt:-未提供}(**尚無 PR**)"
warn=1
fi
branch_rows=$(cat <<EOF
| 工作目錄 | ${worktree:-未提供} |
| 來源分支 | $src_cell |
| 工作分支 | $work_cell |
| 目標分支 | $tgt_cell |
EOF
)
fi
# ---- 輸出 ----
printf '## 階段回報:%s\n\n' "$(stage_zh "$stage")"
printf '| 項目 | 內容 |\n| --- | --- |\n'
printf '| 模型能力標籤 | %s |\n' "$gate_result"
printf '| 工作日誌 | %s |\n' "$worklog_cell"
[ -n "$branch_rows" ] && printf '%s\n' "$branch_rows"
printf '\n### 寫入的 wiki 頁\n\n'
if [ -n "$pages" ]; then
printf '| 頁面 | 連結 |\n| --- | --- |\n'
printf '%s' "$pages" | while IFS= read -r item; do
[ -n "$item" ] || continue
type=${item%%:*}
page=${item#*:}
url=$(page_url "$type" "$page" || true)
printf '| %s | %s |\n' "$page" "${url:-(取不到網址)}"
done
else
printf '本階段沒有寫入任何 wiki 頁。\n'
fi
if [ "$warn" -eq 1 ]; then
printf '\n### 警告\n\n'
[ -n "$worklog" ] || printf -- '- 這個階段還沒有寫入工作日誌,請自行檢查是不是漏了。%s\n' "$pending_note"
[ "$stage" = implement ] && [ -z "$pr_url" ] && printf -- '- 目標分支還沒有 PR,工作尚未交出去。\n'
exit 1
fi
exit 0