What:PLAN、ANALYZE、DELIVER、MAINTAIN、REPO 五個目錄頁改由 wiki-repo CONTENTS 解析並透過 wiki-contents.sh upsert 寫入,內容頁仍各走自己的型別。MAINTAIN 只有 目錄頁,整個型別都在專用存取庫。 Why:目錄頁與內容頁不再同庫,跨庫沒有 wiki 連結語法可用,一律改 wiki-url 的絕對 網址。原本四處取網址都沒有退出碼分流,5 被讀成空字串就寫出空連結,7 被讀成 4 就 把活著的頁當成沒寫成。 How:plan 與 analyze 會上階段鎖,而寫入閘門只看鎖不看路徑,所以流程要產的列檔與 暫存檔會被自己的閘門擋掉。兩支的限制段明寫這些檔一律用 heredoc 或 mktemp 產出。 wp-gate.sh 讀的是分析內容頁,維持走 ANALYZE,原地註明不得改成 CONTENTS。 Who:jsc-sdlc
84 lines
19 KiB
Markdown
84 lines
19 KiB
Markdown
# jsc-sdlc — 開發生命週期
|
||
|
||
jsc 技能組的 sdlc domain:規劃 → 分析 → 實作 → 維護四個階段,全程以 wiki 頁追蹤(`PLAN_CONTENTS`、`PLAN_{HASH}`、`ANALYZE_CONTENTS`、`ANALYZE_{HASH}`、`REPO_CONTENTS`、`REPO_{HASH}`、`DELIVER_CONTENTS`、`DELIVER_{HASH}`、`MAINTAIN_CONTENTS`)。**五個 `*_CONTENTS` 目錄頁住在 `JSC_WIKI_REPO_CONTENTS` 解出的專用存取庫,內容頁各走自己的頁型變數,兩者不同存取庫**(詳見「環境變數」)。工作包完成即交付:實作階段會詢問交付文件要產生成 `DELIVER_{HASH}` wiki 頁或 Gitea 議題留言。hook 或流程失敗記在異常頁(`ERROR_CONTENTS`、`ERROR_{HASH}`),那組頁面與範本由 `jsc-hooks` 擁有,本 domain 不放副本。每次切換階段先過模型閘門,判定全在程式層,由 `jsc-hooks/hooks/sdlc-gate.sh lock {stage}` 執行;各階段必要標籤、阻擋與回報的鐵則見 `references/model-gate.md`。分析前先與使用者確認來源分支;實作沿用同一條來源分支作為 worktree 基準與 PR 目標。**所有參考與來源分支一律取遠端的 `origin/{branch}`,動作前先 `git fetch --prune origin`;本地分支不可作為基準,本地與遠端不一致就停下回報。**
|
||
|
||
## 安裝、更新、移除
|
||
|
||
Marketplace 統一為 `jsc`(https://gitea.jsc.idv.tw/plugins/meta.git),安裝 token 為 `jsc-sdlc@jsc`。每個指令一行:
|
||
|
||
| CLI | 安裝 | 更新 | 移除 |
|
||
| --- | --- | --- | --- |
|
||
| claude | `claude plugin marketplace add https://gitea.jsc.idv.tw/plugins/meta.git && claude plugin install jsc-sdlc@jsc` | `claude plugin marketplace update jsc && claude plugin update jsc-sdlc@jsc` | `claude plugin uninstall jsc-sdlc@jsc` |
|
||
| codex | `codex plugin marketplace add https://gitea.jsc.idv.tw/plugins/meta.git && codex plugin add jsc-sdlc@jsc` | `codex plugin marketplace upgrade jsc` | `codex plugin remove jsc-sdlc@jsc` |
|
||
| copilot | `copilot plugin marketplace add https://gitea.jsc.idv.tw/plugins/meta.git && copilot plugin install jsc-sdlc@jsc` | `copilot plugin marketplace update jsc && copilot plugin update jsc-sdlc@jsc` | `copilot plugin uninstall jsc-sdlc@jsc` |
|
||
| antigravity | `git clone https://gitea.jsc.idv.tw/plugins/sdlc.git ~/plugins/sdlc && agy plugin install ~/plugins/sdlc` | `git -C ~/plugins/sdlc pull && agy plugin uninstall jsc-sdlc && agy plugin install ~/plugins/sdlc` | `agy plugin uninstall jsc-sdlc` |
|
||
| kiro | `kiro-cli plugin marketplace add https://gitea.jsc.idv.tw/plugins/meta.git && kiro-cli plugin install jsc-sdlc@jsc` | `kiro-cli plugin marketplace update jsc && kiro-cli plugin update jsc-sdlc@jsc` | `kiro-cli plugin uninstall jsc-sdlc@jsc` |
|
||
|
||
> antigravity 不支援 gitea URL 安裝,改用本地 clone 路徑。批次操作五個 CLI:使用 `/jsc-cli:deploy`。
|
||
|
||
> 舊入口 `plugins/jsc` 已移除,marketplace 正本移到 `plugins/meta`。marketplace 名稱仍是 `jsc`(取自 marketplace.json 的 `name` 欄位,與存取庫名無關),安裝 token 不變;已從舊入口安裝過的人先執行 `claude plugin marketplace remove jsc`,再依上表重新 add。
|
||
|
||
## 工具
|
||
|
||
| 腳本 | 用途 |
|
||
| --- | --- |
|
||
| `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={最新一筆留言的時間戳}` 供寫回分析頁。`check-deps {owner}/{repo} {wp-number} --analyze {分析頁頁名}` 查某個候選工作包能不能挑:活抓呼叫端指定分析頁的 WBS 表相依欄,逐一核對每個相依工作包的狀態與 PR 是否已合併,只認同一張分析頁上的 `WP-NN` 編號,指到別份計畫的文字項目查不了就列出來要求人工確認,不當成擋人的理由。`claim {owner}/{repo} {wp-number} [--analyze {分析頁頁名}]` 在領包當下轉呼叫 `sdlc-gate.sh wp-claim` 登錄歸屬。`lock {owner}/{repo} {index} [--wp {wp-number}]` 在 PR 開好後轉呼叫 `sdlc-gate.sh wp-lock` 記下未結清,`--wp` 把 PR 掛到該工作包名下。`owns {owner}/{repo} {index} [--wp {wp-number}]` 比對這支 PR 是不是自己這一包的:`owned` 放行、`foreign` 擋住並指出它掛在哪一包名下、查無歸屬(沒有領取紀錄、工作包還沒掛上 PR)一律 `unowned` 放行只提醒。第一行固定 `status=...` 供程式判讀,其後為繁中說明;結束碼 `0`=已合併或無阻擋(含 `check-deps` 的 `ready`、`claim` 的 `claimed`、`owns` 的 `owned` 與 `unowned`)、`1`=未合併、`check-deps` 判定 `blocked` 或 `owns` 判定 `foreign`、`2`=用法錯誤、`3`=相依工具或 PR/分析頁查不到(**查不到就擋,不放行**),或歸屬登錄不了。狀態檔由 `jsc-hooks` 產生(領取檔 `$JSC_HOME/wp/{owner}-{repo}.claim`、鎖檔 `$JSC_HOME/wp/{owner}-{repo}-{index}.pr`,純文字 key=value 一行一欄),不綁 session,開新對話照樣擋;本檔只讀不寫,寫入一律走 `sdlc-gate.sh` 的子命令 |
|
||
| `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 目錄
|
||
|
||
呼叫方式:Claude / Antigravity `/jsc-sdlc:{name}`;Codex `${name}`;Copilot / Kiro 描述需求自動觸發。
|
||
|
||
<!-- JSC-SKILLS:START -->
|
||
|
||
### `plan`
|
||
|
||
規劃:讀計畫目錄 `PLAN_CONTENTS`(CONTENTS 存取庫)→ 補充或新建計畫 → 決策樹持續提問,補全目標、範圍、可行性到達成共識(判定規則見 `references/consensus.md`,一輪不算問完)→ 產生使用者故事 → 寫回 `PLAN_{HASH}`(PLAN 存取庫)→ 用 `jsc-gitea/tools/wiki-contents.sh` 把計畫目錄那一列 upsert,計畫頁連結用 `wiki-url` 的絕對網址 → 階段回報(`tools/stage-report.sh`)。純邏輯,禁止程式碼與修改檔案;claude 另有 `jsc-hooks/hooks/write-guard.sh` 的階段寫入閘門把關,其餘四支 CLI 沒有 `PreToolUse`,只靠內文約束。工作包閘門對 `plan` 只提醒不擋(`analyze` 與 `maintain` 仍擋),放棄的是「手上工作包沒結清就別開新計畫」這道在製品上限。
|
||
|
||
### `analyze`
|
||
|
||
分析:先併行讀 `PLAN_CONTENTS` 與 `ANALYZE_CONTENTS`(兩頁都在 CONTENTS 存取庫),沒有計畫可選就立刻停(不先問分支,因為分支隨計畫而變)→ 選定要擴充的分析或要分析的計畫 → 確認來源分支並檢查工作目錄與 `origin/{來源分支}` 一致 → 持續提問到達成共識(`references/consensus.md`)→ 搭配現況(工作目錄與 `REPO_{HASH}` 盤點複用)分析使用者故事 → WBS 產生編號工作包,`WP-01` 固定是獨立的交付、交接工作包,實作工作包相依於它 → CPM 估工時與天數 → 先產生**使用者故事驗收計畫**(每個情境標明 `真實資料` 或 `邏輯推論`,並寫出輸入、預期結果、資料來源與對應工作包)→ 再拆 TDD 待辦 → 寫回 `ANALYZE_{HASH}`(ANALYZE 存取庫),再用 `jsc-gitea/tools/wiki-contents.sh` 把 `ANALYZE_CONTENTS` 與 `PLAN_CONTENTS` 各自單列 upsert,分析頁連結用 `wiki-url` 的絕對網址;重新盤點時 `REPO_{HASH}` 寫進 REPO 存取庫、`REPO_CONTENTS` 走同一支腳本 → 階段回報(`tools/stage-report.sh`)。純邏輯,禁止程式碼與修改檔案;claude 另有 `jsc-hooks/hooks/write-guard.sh` 的階段寫入閘門把關,其餘四支 CLI 沒有 `PreToolUse`,只靠內文約束。
|
||
|
||
### `implement`
|
||
|
||
實作:**閘門先跑,白工才不會發生**——先讀分析頁,把每個已開 PR 但沒合併的工作包批次跑 `tools/wp-gate.sh check`(各支 PR 互不相依,併發預取後才開始逐則問),動手前先跑 `tools/wp-gate.sh owns` 確認那支 PR 是自己這一包的,再依 `jsc-ask:ask` 決策樹與使用者對每一則留言達成共識才修(以 sub agent 回原 worktree、推同一條工作分支,不開第二個 PR),**這一步只結清舊 PR 的留言,不擋別的工作包**——每則處理過的留言都要用 `jsc-gitea/tools/gitea.sh comment-reply` 回覆,純討論或讚美才可忽略並在回報中列出理由,處理過的時間戳寫回分析頁**既有**的 PR 欄位當下一輪的 `--since` → 列出「未完成、無工作證」的候選工作包,沒有可挑的就停 → **提選項前先對每個候選各跑一次 `tools/wp-gate.sh check-deps --analyze ANALYZE_{HASH}`(候選之間可併行),只有 `ready` 排進選項**:活查它在分析頁上的相依工作包是否都已合併,只有相依於它的包才會被一支未合併的 PR 擋住,跟它無關的工作包可以平行進行(交付工作包排在選項最前)→ 領到包立刻跑 `tools/wp-gate.sh claim` 記下歸屬,**沒登錄成功就不得開工** → 確認來源分支(同時是 PR 目標):分析頁已記的值直接顯示並單鍵確認,只有「分析頁沒記錄」與「`origin/{來源分支}` 遠端不存在」兩種情況才走完整決策樹 → 產生工作證並寫回該工作包的「工作證」欄 → 交付工作包開工前先確認交付內容(API 文件、由使用者輸入,見 `references/deliver-formats.md`)→ **動程式碼前先從 `origin/{source-branch}` 建立 worktree**(`.worktree/{analysis-HASH}/{wp-number}/{repo}`,一個工作包一個 worktree,分支處理依決策樹詢問;多個存取庫的 worktree 併行建立)→ 在 worktree 內逐項 TDD 實作、每完成一項立即更新 wiki → **收尾稽核兩關同時啟動、並列進行,兩關都過才算工作包完成**:一關是 `jsc-review:code-review` 程式碼審查,另一關是 API 文件稽核——先跑 `jsc-review/tools/swagger-detect.sh` 判定專案支不支援 Swagger(退出碼 `0` 支援、`1` 不支援、`2` 參數或路徑有錯),支援才呼叫 `jsc-review:api-doc` 把控制器文件補到過關(沒過就以 sub agent 修完再稽核一次),不支援就**明確跳過並回報**,跳過算通過 → **每完成一個工作包就 commit、push、PR 回來源分支**(一包一 PR),接著 `tools/wp-gate.sh lock --wp` 上鎖並登記 PR 歸屬 → **用 `jsc-gitea/tools/pr-watch.sh` 盯到合併**(預設 60 秒輪詢、不自動退場;退出碼 `0` 已合併或關閉、`10` 有新留言就回頭跑同一套留言修正、`3` 查不到該 PR、`2` 參數或環境有問題),合併後解鎖並移除 worktree → 詢問交付文件格式(`DELIVER_{HASH}` wiki 頁寫進 DELIVER 存取庫,或 Gitea 議題留言)並產出 → 詢問是否加入維護目錄;`DELIVER_CONTENTS` 與 `MAINTAIN_CONTENTS` 都在 CONTENTS 存取庫,一律用 `jsc-gitea/tools/wiki-contents.sh` 單列 upsert,交付頁連結用 `wiki-url` 的絕對網址 → 階段回報(`tools/stage-report.sh`,多報工作目錄與來源、工作、目標三條分支),PR 回報使用 `jsc-meta/references/pr-report.md` 的表格。**每完成一個任務就寫一筆工作日誌**(`jsc-log:worklog`):一個工作包、一輪 PR 留言修正、一個獨立的修正提交各算一個任務,不等到階段結束才補一次;`stage-report.sh --pending-file` 暫存的內容併進同一次寫入,寫入成功才清除。寫程式碼時註解只寫「為什麼這樣寫」,工作包編號、分析頁編號、待辦編號、分支名、PR 編號一律不寫進註解,完整清單與白名單見 `jsc-review` 的 `references/comment-scope.md`。
|
||
|
||
### `maintain`
|
||
|
||
維護:從 `MAINTAIN_CONTENTS`(CONTENTS 存取庫;`MAINTAIN` 只有目錄頁)讀取維護期內的專案,**先整批併行 `git fetch` 並把每個專案切到 develop、master 並對齊 `origin/{branch}`**(專案之間互不相依),接著逐專案循序、每個專案一個 sub agent:提出至少五種維護方法,依決策樹讓使用者挑要做哪些 → commit / push / PR → 用 `jsc-meta/references/pr-report.md` 的表格回報 PR → **PR 開好立刻寫一筆工作日誌**(`jsc-log:worklog`,一個專案一筆,下一個專案開工前要先寫完)→ 用 `jsc-gitea/tools/wiki-contents.sh` 單列 upsert 更新前次維護時間 → 階段回報(`tools/stage-report.sh`)。sub agent 改程式碼時註解只寫「為什麼這樣寫」,議題編號、commit hash、分支名、人名與 `@` 提及一律不寫進註解,完整清單與白名單見 `jsc-review` 的 `references/comment-scope.md`;把關交給 `comment-scope.sh` 與提交前的 commit sweep,不再多做一輪人工全 diff 自查。**只有 claude 在寫檔當下就收到警告**;codex、copilot、antigravity 的 hook 要到回合或工作階段結束才掃,提交前的 commit sweep 是那三支唯一來得及的一道。僅適用於維護期內已交付的專案;尚在實作中或未登記於 `MAINTAIN_CONTENTS` 的專案不適用。
|
||
|
||
<!-- JSC-SKILLS:END -->
|
||
|
||
## 範本與參考
|
||
|
||
| 檔案 | 用途 |
|
||
| --- | --- |
|
||
| `templates/plan-page.md`、`templates/plan-contents.md` | 計畫頁與計畫目錄 |
|
||
| `templates/analyze-page.md`、`templates/analyze-contents.md` | 分析頁(WBS、CPM、TDD 待辦)與分析目錄 |
|
||
| `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 沒合併只擋相依於它的工作包,不擋整份分析,閘門分工與工作包隔離的狀態檔格式見該檔)、分支階梯(表本身在 `jsc-meta` 的 `references/guidelines.md`「PR 分支階梯」,該檔只寫 sdlc 拿到 `jsc-git/tools/base-branch.sh --derive` 的回應之後要做什麼、推不出就中止問使用者)、實作一律在 `.worktree/{HASH}/{wp-number}/{repo}` 內進行——一個工作包一個 worktree,讓互不相依的工作包能平行進行不互相搶路徑(建立前問分支、PR 合併後才移除)、判定遠端預設分支、不破壞未提交變更 |
|
||
| `references/consensus.md` | 規劃與分析的提問規則:一輪不算問完、共識的兩個判定條件、未決項處理 |
|
||
| `references/deliver-formats.md` | 交付內容型別:API 文件必備欄位、範例資料優先序、既有端點的新舊參數標示 |
|
||
|
||
## 環境變數
|
||
|
||
Wiki 位置:**目錄頁與內容頁分屬不同存取庫**。五個目錄頁 `PLAN_CONTENTS`、`ANALYZE_CONTENTS`、`REPO_CONTENTS`、`DELIVER_CONTENTS`、`MAINTAIN_CONTENTS` 一律由 `gitea.sh wiki-repo CONTENTS` 解析:只讀 `JSC_WIKI_REPO_CONTENTS`,再退回 `JSC_WIKI_REPO`,兩者都沒設定就 exit 3,**不退回各自的頁型變數**。內容頁各走自己的頁型:`PLAN_{HASH}` 只讀 `JSC_WIKI_REPO_PLAN`、`ANALYZE_{HASH}` 只讀 `JSC_WIKI_REPO_ANALYZE`、`REPO_{HASH}` 只讀 `JSC_WIKI_REPO_REPO`、`DELIVER_{HASH}` 只讀 `JSC_WIKI_REPO_DELIVER`,各自再退回 `JSC_WIKI_REPO`。`MAINTAIN` 是唯一只有目錄頁、沒有內容頁的型別,整個型別都在 CONTENTS 存取庫。不同類型不可互相代用;兩者都未設定才詢問(見 `jsc-gitea`)。
|
||
|
||
目錄頁寫入與連結:一律用 `jsc-gitea/tools/wiki-contents.sh upsert {TYPE} {鍵欄} {鍵} {列檔} {範本}` 單列 upsert,禁止手工整頁覆蓋(結束碼 `0`=已更新或已新增、`1`=寫入失敗、`2`=用法錯誤、`3`=CONTENTS 存取庫未設定、`4`=頁不存在且沒給範本、`7`=金鑰失效、`8`=其他 API 失敗)。目錄頁指向內容頁的連結一律用 `gitea.sh wiki-url` 給的絕對網址,`[[...]]` 只在同一個 wiki 內解析,跨存取庫會變死連結。階段回報 `tools/stage-report.sh --page TYPE:PAGE` 的目錄頁一律填 `CONTENTS:` 這個型別,填舊型別會解到錯的存取庫、印不出網址。
|
||
|
||
HASH 規則:`{owner}/{repo}` 的共用 wiki hash 一律由 `jsc-gitea/tools/hash-id` 計算(見 `jsc-gitea:wiki`),此 domain 不重複實作演算法。長度與大小寫的正本也在那裡(現行為完整 40 碼大寫十六進位),本檔不複述規格,只要求一件事:`hash-id` 印出什麼就原樣用,不得自行截短。工作證 `TICKET_{yyyyMMdd}_{HHmmss}_{HASH}` 不是 wiki 頁,但用的是同一支 `hash-id`,同樣原樣帶著走。
|
||
|
||
## 相關 domain
|
||
|
||
- [`jsc-cli`](https://gitea.jsc.idv.tw/plugins/cli):模型能力標籤與階段閘門判定(`tools/model-tags.sh`、`references/model-tags.md`、`jsc-cli:models`)、偏好模型鏈(`tools/model-config.sh`)
|
||
- [`jsc-hooks`](https://gitea.jsc.idv.tw/plugins/hooks):sdlc-gate 階段模型鎖定
|
||
- [`jsc-gitea`](https://gitea.jsc.idv.tw/plugins/gitea):wiki 讀寫、實作階段等 PR 合併的輪詢(`tools/pr-watch.sh`)
|
||
- [`jsc-review`](https://gitea.jsc.idv.tw/plugins/review):實作完成後並列的兩關收尾稽核——程式碼審查(`jsc-review:code-review`)、API 文件稽核(`jsc-review:api-doc`,專案支不支援 Swagger 由 `tools/swagger-detect.sh` 判定,稽核項目也只寫在該技能)
|
||
- [`jsc-git`](https://gitea.jsc.idv.tw/plugins/git) / [`jsc-pkg`](https://gitea.jsc.idv.tw/plugins/pkg):維護階段的 commit / PR 與套件更新
|
||
- [`jsc-log`](https://gitea.jsc.idv.tw/plugins/log):工作日誌,每完成一個任務就寫一筆
|