Merge pull request 'sdlc 0.1.5 發佈:工作包 PR 硬閘門與稽核修正' (#20) from develop into master
Reviewed-on: #20 Reviewed-by: 系統管理員 <1+admin@noreply.localhost>
This commit was merged in pull request #20.
This commit is contained in:
@@ -43,7 +43,7 @@
|
|||||||
"source": "url",
|
"source": "url",
|
||||||
"url": "https://gitea.jsc.idv.tw/plugins/hooks.git"
|
"url": "https://gitea.jsc.idv.tw/plugins/hooks.git"
|
||||||
},
|
},
|
||||||
"description": "跨 CLI hooks:STE100 語言強制、工時計時、技能用量記錄"
|
"description": "跨 CLI hooks:STE100 語言強制、工時計時、技能用量記錄、SDLC 模型鎖、版本前置檢查"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "jsc-log",
|
"name": "jsc-log",
|
||||||
@@ -75,7 +75,7 @@
|
|||||||
"source": "url",
|
"source": "url",
|
||||||
"url": "https://gitea.jsc.idv.tw/plugins/review.git"
|
"url": "https://gitea.jsc.idv.tw/plugins/review.git"
|
||||||
},
|
},
|
||||||
"description": "程式碼審查:Refactoring 壞味道六組 + 註解規範 + 淺模組"
|
"description": "程式碼審查:Refactoring 壞味道六組、註解規範、淺模組"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "jsc-sdlc",
|
"name": "jsc-sdlc",
|
||||||
@@ -83,7 +83,7 @@
|
|||||||
"source": "url",
|
"source": "url",
|
||||||
"url": "https://gitea.jsc.idv.tw/plugins/sdlc.git"
|
"url": "https://gitea.jsc.idv.tw/plugins/sdlc.git"
|
||||||
},
|
},
|
||||||
"description": "開發生命週期:規劃/分析/實作/維護(wiki 追蹤)"
|
"description": "開發生命週期:規劃、分析、實作、維護(wiki 追蹤)"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,7 +43,7 @@
|
|||||||
"source": "url",
|
"source": "url",
|
||||||
"url": "https://gitea.jsc.idv.tw/plugins/hooks.git"
|
"url": "https://gitea.jsc.idv.tw/plugins/hooks.git"
|
||||||
},
|
},
|
||||||
"description": "跨 CLI hooks:STE100 語言強制、工時計時、技能用量記錄"
|
"description": "跨 CLI hooks:STE100 語言強制、工時計時、技能用量記錄、SDLC 模型鎖、版本前置檢查"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "jsc-log",
|
"name": "jsc-log",
|
||||||
@@ -75,7 +75,7 @@
|
|||||||
"source": "url",
|
"source": "url",
|
||||||
"url": "https://gitea.jsc.idv.tw/plugins/review.git"
|
"url": "https://gitea.jsc.idv.tw/plugins/review.git"
|
||||||
},
|
},
|
||||||
"description": "程式碼審查:Refactoring 壞味道六組 + 註解規範 + 淺模組"
|
"description": "程式碼審查:Refactoring 壞味道六組、註解規範、淺模組"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "jsc-sdlc",
|
"name": "jsc-sdlc",
|
||||||
@@ -83,7 +83,7 @@
|
|||||||
"source": "url",
|
"source": "url",
|
||||||
"url": "https://gitea.jsc.idv.tw/plugins/sdlc.git"
|
"url": "https://gitea.jsc.idv.tw/plugins/sdlc.git"
|
||||||
},
|
},
|
||||||
"description": "開發生命週期:規劃/分析/實作/維護(wiki 追蹤)"
|
"description": "開發生命週期:規劃、分析、實作、維護(wiki 追蹤)"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "jsc-sdlc",
|
"name": "jsc-sdlc",
|
||||||
"version": "0.1.2",
|
"version": "0.1.5",
|
||||||
"description": "開發生命週期:規劃/分析/實作/維護(wiki 追蹤)",
|
"description": "開發生命週期:規劃、分析、實作、維護(wiki 追蹤)",
|
||||||
"skills": "./skills",
|
"skills": "./skills",
|
||||||
"author": {
|
"author": {
|
||||||
"name": "JSC"
|
"name": "JSC"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "jsc-sdlc",
|
"name": "jsc-sdlc",
|
||||||
"version": "0.1.2",
|
"version": "0.1.5",
|
||||||
"description": "開發生命週期:規劃/分析/實作/維護(wiki 追蹤)",
|
"description": "開發生命週期:規劃、分析、實作、維護(wiki 追蹤)",
|
||||||
"skills": "./skills"
|
"skills": "./skills"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# jsc-sdlc — 開發生命週期
|
# jsc-sdlc — 開發生命週期
|
||||||
|
|
||||||
jsc 技能組的 sdlc domain:規劃 → 分析 → 實作 → 維護四個階段,全程以 wiki 頁追蹤(`PLAN_CONTENTS`、`PLAN_{HASH}`、`ANALYZE_CONTENTS`、`ANALYZE_{HASH}`、`REPO_CONTENTS`、`REPO_{HASH}`、`DELIVER_CONTENTS`、`DELIVER_{HASH}`、`MAINTAIN_CONTENTS`)。工作包完成即交付:實作階段會詢問交付文件要產生成 `DELIVER_{HASH}` wiki 頁或 Gitea 議題留言。另有異常頁(`ERROR_CONTENTS`、`ERROR_{HASH}`)記錄 hook 或流程失敗。每次切換階段先過模型閘門:由 `jsc-hooks/hooks/sdlc-gate.sh lock {stage}` 從 transcript 讀出**實際**模型 id,比對該階段的必要能力標籤(plan、analyze 需 `reasoning-max`;implement 需 `coding`;maintain 任意),不符就拒絕上鎖、該階段不得執行。判定全在程式層,模型不得自評標籤,且**每次都要把閘門結果回報使用者**(階段、必要標籤、實際模型、判定),不論通過或阻擋。通過後鎖定到下一階段的閘門重新上鎖;同一階段內把模型換成不合格的,下一輪提示會被 hook 以 exit 2 擋下。分析前先與使用者確認來源分支;實作沿用同一條來源分支作為 worktree 基準與 PR 目標。**所有參考與來源分支一律取遠端的 `origin/{分支}`,動作前先 `git fetch --prune origin`;本地分支不可作為基準,本地與遠端不一致就停下回報。**
|
jsc 技能組的 sdlc domain:規劃 → 分析 → 實作 → 維護四個階段,全程以 wiki 頁追蹤(`PLAN_CONTENTS`、`PLAN_{HASH}`、`ANALYZE_CONTENTS`、`ANALYZE_{HASH}`、`REPO_CONTENTS`、`REPO_{HASH}`、`DELIVER_CONTENTS`、`DELIVER_{HASH}`、`MAINTAIN_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`;本地分支不可作為基準,本地與遠端不一致就停下回報。**
|
||||||
|
|
||||||
## 安裝、更新、移除
|
## 安裝、更新、移除
|
||||||
|
|
||||||
@@ -18,6 +18,12 @@ Marketplace 統一為 `jsc`(https://gitea.jsc.idv.tw/plugins/meta.git),安
|
|||||||
|
|
||||||
> 舊入口 `plugins/jsc` 已移除,marketplace 正本移到 `plugins/meta`。marketplace 名稱仍是 `jsc`(取自 marketplace.json 的 `name` 欄位,與存取庫名無關),安裝 token 不變;已從舊入口安裝過的人先執行 `claude plugin marketplace remove jsc`,再依上表重新 add。
|
> 舊入口 `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={最新一筆留言的時間戳}` 供寫回分析頁。`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,開新對話照樣擋),本檔只負責查詢與結清 |
|
||||||
|
|
||||||
## Skills 目錄
|
## Skills 目錄
|
||||||
|
|
||||||
呼叫方式:Claude / Antigravity `/jsc-sdlc:{name}`;Codex `${name}`;Copilot / Kiro 描述需求自動觸發。
|
呼叫方式:Claude / Antigravity `/jsc-sdlc:{name}`;Codex `${name}`;Copilot / Kiro 描述需求自動觸發。
|
||||||
@@ -30,15 +36,15 @@ Marketplace 統一為 `jsc`(https://gitea.jsc.idv.tw/plugins/meta.git),安
|
|||||||
|
|
||||||
### `analyze`
|
### `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}`。純邏輯,禁止程式碼與修改檔案。
|
||||||
|
|
||||||
### `implement`
|
### `implement`
|
||||||
|
|
||||||
實作:先確認來源分支(同時是 PR 目標)→ 產生工作證鎖定「未完成、無相依、無工作證」的工作包(交付工作包優先)→ 交付工作包開工前先確認交付內容(API 文件/由使用者輸入,見 `references/deliver-formats.md`)→ **動程式碼前先從 `origin/{分析頁來源分支}` 建立 worktree**(`.worktree/{分析頁 HASH}/{repo}`,分支處理依決策樹詢問)→ 在 worktree 內逐項 TDD 實作、每完成一項立即更新 wiki → 程式碼審查 → **每完成一個工作包就 commit、push、PR 回來源分支**(一包一 PR)→ **PR 未合併就不開下一包**;每次要領工作包前先查 PR 狀態並讀留言,依決策樹問使用者要修正還是等待;合併後才移除 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 議題留言)並產出 → 詢問是否加入維護目錄。
|
||||||
|
|
||||||
### `maintain`
|
### `maintain`
|
||||||
|
|
||||||
維護:讀取維護期內的專案,每個專案一個 sub agent:fetch 後切 develop、master 並對齊 `origin/{該分支}` → 至少五種建議維護方法 → commit / push / PR → 更新前次維護時間。僅適用於維護期內已交付的專案;尚在實作中或未登記於 `MAINTAIN_CONTENTS` 的專案不適用。
|
維護:讀取維護期內的專案,每個專案一個 sub agent:fetch 後切 develop、master 並對齊 `origin/{branch}` → 提出至少五種維護方法,依決策樹讓使用者挑要做哪些 → commit / push / PR → 更新前次維護時間。僅適用於維護期內已交付的專案;尚在實作中或未登記於 `MAINTAIN_CONTENTS` 的專案不適用。
|
||||||
|
|
||||||
<!-- JSC-SKILLS:END -->
|
<!-- JSC-SKILLS:END -->
|
||||||
|
|
||||||
@@ -49,11 +55,11 @@ Marketplace 統一為 `jsc`(https://gitea.jsc.idv.tw/plugins/meta.git),安
|
|||||||
| `templates/plan-page.md`、`templates/plan-contents.md` | 計畫頁與計畫目錄 |
|
| `templates/plan-page.md`、`templates/plan-contents.md` | 計畫頁與計畫目錄 |
|
||||||
| `templates/analyze-page.md`、`templates/analyze-contents.md` | 分析頁(WBS、CPM、TDD 待辦)與分析目錄 |
|
| `templates/analyze-page.md`、`templates/analyze-contents.md` | 分析頁(WBS、CPM、TDD 待辦)與分析目錄 |
|
||||||
| `templates/repo-page.md`、`templates/repo-contents.md` | 存取庫盤點頁(功能與端點,附 commit sha)與盤點目錄 |
|
| `templates/repo-page.md`、`templates/repo-contents.md` | 存取庫盤點頁(功能與端點,附 commit sha)與盤點目錄 |
|
||||||
| `templates/error-page.md`、`templates/error-contents.md` | 異常頁與異常目錄 |
|
|
||||||
| `templates/deliver-page.md`、`templates/deliver-contents.md` | 交付頁(API 文件、新舊參數標示、驗證方式)與交付目錄 |
|
| `templates/deliver-page.md`、`templates/deliver-contents.md` | 交付頁(API 文件、新舊參數標示、驗證方式)與交付目錄 |
|
||||||
| `templates/maintain-contents.md` | 維護目錄(截止日 NULL = 永久維護) |
|
| `templates/maintain-contents.md` | 維護目錄(截止日 NULL = 永久維護) |
|
||||||
|
| `references/model-gate.md` | 模型閘門:執行順序、各階段必要標籤、阻擋與回報的鐵則 |
|
||||||
| `references/tdd.md` | 接縫、紅綠循環規則、反模式 |
|
| `references/tdd.md` | 接縫、紅綠循環規則、反模式 |
|
||||||
| `references/branch.md` | 分支規則:**一律以遠端 `origin/{分支}` 為準、動作前先 fetch**、分析前確認來源分支、實作沿用同一條來源分支作為 PR 目標(一包一 PR、PR 未合併不開下一包)、實作一律在 `.worktree/{HASH}/{repo}` 內進行(建立前問分支、PR 成功後移除)、判定遠端預設分支、不破壞未提交變更 |
|
| `references/branch.md` | 分支規則:**一律以遠端 `origin/{branch}` 為準、動作前先 fetch**、分析前確認來源分支、實作沿用同一條來源分支作為 PR 目標(一包一 PR、PR 未合併不開下一包,閘門分工見該檔)、實作一律在 `.worktree/{HASH}/{repo}` 內進行(建立前問分支、PR 合併後才移除)、判定遠端預設分支、不破壞未提交變更 |
|
||||||
| `references/consensus.md` | 規劃與分析的提問規則:一輪不算問完、共識的兩個判定條件、未決項處理 |
|
| `references/consensus.md` | 規劃與分析的提問規則:一輪不算問完、共識的兩個判定條件、未決項處理 |
|
||||||
| `references/deliver-formats.md` | 交付內容型別:API 文件必備欄位、範例資料優先序、既有端點的新舊參數標示 |
|
| `references/deliver-formats.md` | 交付內容型別:API 文件必備欄位、範例資料優先序、既有端點的新舊參數標示 |
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "jsc-sdlc",
|
"name": "jsc-sdlc",
|
||||||
"version": "0.1.2",
|
"version": "0.1.5",
|
||||||
"description": "開發生命週期:規劃/分析/實作/維護(wiki 追蹤)",
|
"description": "開發生命週期:規劃、分析、實作、維護(wiki 追蹤)",
|
||||||
"skills": "./skills/"
|
"skills": "./skills/"
|
||||||
}
|
}
|
||||||
|
|||||||
+38
-21
@@ -4,22 +4,30 @@
|
|||||||
|
|
||||||
## 總則:一律以遠端為準,不吃本地分支
|
## 總則:一律以遠端為準,不吃本地分支
|
||||||
|
|
||||||
SDLC 各階段引用的參考分支與來源分支,**一律指遠端的 `origin/{分支}`**。本地分支不可作為基準。
|
SDLC 各階段引用的參考分支與來源分支,**一律指遠端的 `origin/{branch}`**。本地分支不可作為基準。
|
||||||
|
|
||||||
理由:本地分支可能落後遠端、可能有沒推上去的 commit、也可能是別的工作留下的狀態。以本地為基準,分析會對著過時的程式碼做,實作會從錯誤的起點長出來,而且錯了不會有任何徵兆。
|
理由:本地分支可能落後遠端、可能有沒推上去的 commit、也可能是別的工作留下的狀態。以本地為基準,分析會對著過時的程式碼做,實作會從錯誤的起點長出來,而且錯了不會有任何徵兆。
|
||||||
|
|
||||||
| 項目 | 規則 |
|
| 項目 | 規則 |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| 動作之前 | 先 `git fetch --prune origin`。**沒 fetch 就用 `origin/{分支}` 等於用快取**,遠端追蹤參照可能已經過時,連分支被刪掉都看不出來 |
|
| 動作之前 | 先 `git fetch --prune origin`。**沒 fetch 就用 `origin/{branch}` 等於用快取**,遠端追蹤參照可能已經過時,連分支被刪掉都看不出來 |
|
||||||
| 列分支 | `git branch -r`,不看 `git branch` |
|
| 列分支 | `git branch -r`,不看 `git branch` |
|
||||||
| 基準與比對 | 一律寫 `origin/{分支}`,例如 `git rev-list --count origin/master..origin/develop` |
|
| 基準與比對 | 一律寫 `origin/{branch}`,例如 `git rev-list --count origin/master..origin/develop` |
|
||||||
| 建立 worktree | 從 `origin/{來源分支}` 建,不從本地同名分支建 |
|
| 建立 worktree | 從 `origin/{source-branch}` 建,不從本地同名分支建 |
|
||||||
| 判定預設分支 | `refs/remotes/origin/HEAD`(見〔判定遠端預設分支〕) |
|
| 判定預設分支 | `refs/remotes/origin/HEAD`(見〔判定遠端預設分支〕) |
|
||||||
|
|
||||||
**本地與遠端不一致時一律停下回報**,不自行 `pull`、不自行 `reset`、不自行切換。要不要同步是使用者的決定。
|
**本地與遠端不一致時一律停下回報**,不自行 `pull`、不自行 `reset`、不自行切換。要不要同步是使用者的決定。
|
||||||
|
|
||||||
`origin` 以外的遠端名稱(例如 `upstream`):先問使用者以哪個遠端為準,不臆測。
|
`origin` 以外的遠端名稱(例如 `upstream`):先問使用者以哪個遠端為準,不臆測。
|
||||||
|
|
||||||
|
### 來源分支在遠端找不到:停下回報,不退回預設分支
|
||||||
|
|
||||||
|
已指定的來源分支,`origin/{source-branch}` 在遠端不存在時,**回報並停止**。不改用 `develop`、不改用 `master`、不套用任何後備分支,也不自行建立同名分支。這條沒有例外。
|
||||||
|
|
||||||
|
理由:來源分支是那份分析的功能分支。悄悄退回 `develop`,單一工作包就會越過自己的功能分支直接進 `develop`,而且沒有任何徵兆看得出來。
|
||||||
|
|
||||||
|
**這是停止條件,不是後備條件。**〔判定遠端預設分支〕那節的後備順序只用在「還沒有指定分支、要挑一條基準」的情況;指定過的來源分支找不到,一律套用本節。
|
||||||
|
|
||||||
## 先確認,再動工
|
## 先確認,再動工
|
||||||
|
|
||||||
| 階段 | 要確認的分支 | 確認時機 |
|
| 階段 | 要確認的分支 | 確認時機 |
|
||||||
@@ -40,21 +48,23 @@ SDLC 各階段引用的參考分支與來源分支,**一律指遠端的 `origi
|
|||||||
2. 取不到時退而用 `git remote show origin`,找輸出中的 `HEAD branch:` 那行。
|
2. 取不到時退而用 `git remote show origin`,找輸出中的 `HEAD branch:` 那行。
|
||||||
3. 兩者都取不到 → 回報並停止,**不臆測** `master`/`main`。
|
3. 兩者都取不到 → 回報並停止,**不臆測** `master`/`main`。
|
||||||
|
|
||||||
需要基準或後備分支時(例如 PR 目標):`origin/develop` 存在就用 `develop`;否則 `origin/master` 存在就用 `master`;兩者皆無則回報並停止。
|
還沒有指定分支、需要挑一條基準或後備分支時(例如維護階段要落腳的分支):`origin/develop` 存在就用 `develop`;否則 `origin/master` 存在就用 `master`;兩者皆無則回報並停止。
|
||||||
|
|
||||||
|
這條後備順序**不適用於已指定的來源分支**。指定過的來源分支在遠端找不到,一律回報並停止,見總則的〔來源分支在遠端找不到〕。
|
||||||
|
|
||||||
## 只讀階段不動工作區
|
## 只讀階段不動工作區
|
||||||
|
|
||||||
`analyze` 是 logic-only 階段:
|
`analyze` 是 logic-only 階段:
|
||||||
|
|
||||||
- **工作目錄的 HEAD 必須與 `origin/{來源分支}` 指向同一個 commit**。落後、超前或分歧都代表讀到的不是遠端現況——停下來回報差距(`git rev-list --left-right --count origin/{來源分支}...HEAD`),請使用者自己處理。
|
- **工作目錄的 HEAD 必須與 `origin/{source-branch}` 指向同一個 commit**。落後、超前或分歧都代表讀到的不是遠端現況——停下來回報差距(`git rev-list --left-right --count origin/{source-branch}...HEAD`),請使用者自己處理。
|
||||||
- 需要換分支才能讀到正確現況時,**停下來請使用者自己切換**。
|
- 需要換分支才能讀到正確現況時,**停下來請使用者自己切換**。
|
||||||
- 不代為 `switch`、不 `stash`、不動工作區、不建分支。
|
- 不代為 `switch`、不 `stash`、不動工作區、不建分支。
|
||||||
|
|
||||||
## 實作階段的分支選擇
|
## 實作階段的分支選擇
|
||||||
|
|
||||||
實作在 worktree 內進行,工作分支從 `origin/{來源分支}` 長出來,做完再 PR 回同一條來源分支。
|
實作在 worktree 內進行,工作分支從 `origin/{source-branch}` 長出來,做完再 PR 回同一條來源分支。
|
||||||
|
|
||||||
- **一個工作包一個 PR**,目標一律是 `origin/{來源分支}`。不把兩個完成的工作包併成一個 PR,也不把完成的工作包留到下一包一起送——拆工作包的意義就是各自能獨立審查。
|
- **一個工作包一個 PR**,目標一律是 `origin/{source-branch}`。不把兩個完成的工作包併成一個 PR,也不把完成的工作包留到下一包一起送——拆工作包的意義就是各自能獨立審查。
|
||||||
- 呼叫 `jsc-git:pr` 時**必須明確帶入來源分支當 base**。該 skill 在沒收到 base 時會自行退回 `develop`,那會讓單一工作包越過它所屬的功能分支直接進 develop。
|
- 呼叫 `jsc-git:pr` 時**必須明確帶入來源分支當 base**。該 skill 在沒收到 base 時會自行退回 `develop`,那會讓單一工作包越過它所屬的功能分支直接進 develop。
|
||||||
- 來源分支整條後續要進 `develop` 時,那是另一個獨立的 PR,不在本階段範圍。
|
- 來源分支整條後續要進 `develop` 時,那是另一個獨立的 PR,不在本階段範圍。
|
||||||
- 新分支名稱要可讀且不覆蓋既有分支;本地或遠端已存在同名分支時,換一個時間戳或短 hash。
|
- 新分支名稱要可讀且不覆蓋既有分支;本地或遠端已存在同名分支時,換一個時間戳或短 hash。
|
||||||
@@ -62,15 +72,15 @@ SDLC 各階段引用的參考分支與來源分支,**一律指遠端的 `origi
|
|||||||
|
|
||||||
## 實作一律在 worktree 內進行
|
## 實作一律在 worktree 內進行
|
||||||
|
|
||||||
`implement` 動任何程式碼之前,先 `git fetch --prune origin`,再從**分析頁記錄的來源分支的遠端版本**(`origin/{來源分支}`)建立 git worktree。所有修改都在 worktree 內,主工作目錄的分支與工作區完全不動。
|
`implement` 動任何程式碼之前,先 `git fetch --prune origin`,再從**分析頁記錄的來源分支的遠端版本**(`origin/{source-branch}`)建立 git worktree。所有修改都在 worktree 內,主工作目錄的分支與工作區完全不動。
|
||||||
|
|
||||||
### 路徑
|
### 路徑
|
||||||
|
|
||||||
```
|
```
|
||||||
{工作目錄}/.worktree/{分析頁 HASH}/{repo}
|
{cwd}/.worktree/{analysis-HASH}/{repo}
|
||||||
```
|
```
|
||||||
|
|
||||||
- `{分析頁 HASH}`:`ANALYZE_{HASH}` 的 HASH 部分,不含 `ANALYZE_` 前綴。
|
- `{analysis-HASH}`:`ANALYZE_{HASH}` 的 HASH 部分,不含 `ANALYZE_` 前綴。
|
||||||
- `{repo}`:存取庫名稱,不含 owner(`HP/WebService.Buy` → `WebService.Buy`)。不同 owner 的同名存取庫同時出現時,才改用 `{owner}-{repo}` 避免蓋掉,並在輸出中說明。
|
- `{repo}`:存取庫名稱,不含 owner(`HP/WebService.Buy` → `WebService.Buy`)。不同 owner 的同名存取庫同時出現時,才改用 `{owner}-{repo}` 避免蓋掉,並在輸出中說明。
|
||||||
- 一份分析涉及多個存取庫時,每個存取庫各一個 worktree,並列在同一個 HASH 目錄下。
|
- 一份分析涉及多個存取庫時,每個存取庫各一個 worktree,並列在同一個 HASH 目錄下。
|
||||||
|
|
||||||
@@ -80,22 +90,23 @@ SDLC 各階段引用的參考分支與來源分支,**一律指遠端的 `origi
|
|||||||
|
|
||||||
| 選項 | 指令 | 影響 |
|
| 選項 | 指令 | 影響 |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| 以來源分支為基準開新工作分支 | `git worktree add -b {工作分支} {路徑} "origin/{來源分支}"` | commit 落在新分支,來源分支不動 |
|
| 以來源分支為基準開新工作分支 | `git worktree add -b {work-branch} {path} "origin/{source-branch}"` | commit 落在新分支,來源分支不動 |
|
||||||
| 直接簽出來源分支 | `git worktree add --track -b {來源分支} {路徑} "origin/{來源分支}"` | 建立追蹤遠端的本地分支再簽出;本地已存在同名分支時指令會失敗,此時先確認它與遠端一致才可改用 `git worktree add {路徑} {來源分支}`,不一致就停下回報 |
|
| 直接簽出來源分支 | `git worktree add --track -b {source-branch} {path} "origin/{source-branch}"` | 建立追蹤遠端的本地分支再簽出;本地已存在同名分支時指令會失敗,此時先確認它與遠端一致才可改用 `git worktree add {path} {source-branch}`,不一致就停下回報 |
|
||||||
|
|
||||||
**不得自行預設**,也不得跳過詢問。
|
**不得自行預設**,也不得跳過詢問。
|
||||||
|
|
||||||
### 建立時的鐵則
|
### 建立時的鐵則
|
||||||
|
|
||||||
- **分支名與路徑一律加引號**:來源分支可能含中文、空白或多層斜線(例如 `feat/一址通/查地址/完整版/P2`),不加引號會被切斷。
|
- **分支名與路徑一律加引號**:既有的來源分支可能含空白、多層斜線或非 ASCII 字元(例如 `feat/addr-lookup/query/full/P2` 這種多層命名,或更舊的分支帶空白),不加引號會被切斷。
|
||||||
|
- **jsc 自己開的分支只用 ASCII**(`a-z0-9` 與 `/`、`-`)。中文標題先翻譯成英文短語,再 slug 化當分支名。
|
||||||
- 找不到該存取庫的本地 clone → **停下來問使用者路徑**,不自行 clone、不臆測位置。
|
- 找不到該存取庫的本地 clone → **停下來問使用者路徑**,不自行 clone、不臆測位置。
|
||||||
- 目標路徑已存在 → 不覆蓋。先確認它是不是同一份工作的 worktree(`git worktree list`),是就沿用,不是就回報並停止。
|
- 目標路徑已存在 → 不覆蓋。先確認它是不是同一份工作的 worktree(`git worktree list`),是就沿用,不是就回報並停止。
|
||||||
- 把 `.worktree/` 加進該存取庫的 `.git/info/exclude`(不動使用者的 `.gitignore`,那是專案共用檔)。
|
- 把 `.worktree/` 加進該存取庫的 `.git/info/exclude`(不動使用者的 `.gitignore`,那是專案共用檔)。
|
||||||
- 建立後在輸出中明確列出:worktree 路徑、簽出的分支、來源分支,以及 `origin/{來源分支}` 當下的 commit sha——那是這次實作的起點,要能事後追。
|
- 建立後在輸出中明確列出:worktree 路徑、簽出的分支、來源分支,以及 `origin/{source-branch}` 當下的 commit sha——那是這次實作的起點,要能事後追。
|
||||||
|
|
||||||
### 移除時機
|
### 移除時機
|
||||||
|
|
||||||
**PR 合併之後才移除**:`git worktree remove {路徑}`,接著 `git worktree prune`。
|
**PR 合併之後才移除**:`git worktree remove {path}`,接著 `git worktree prune`。
|
||||||
|
|
||||||
不是 PR 建立後就移除——審查留言可能要求修改,而修改要回到同一個 worktree、推到同一條工作分支(PR 會自己更新,不開第二個 PR)。先移除就得為了改一行重建整個 worktree。
|
不是 PR 建立後就移除——審查留言可能要求修改,而修改要回到同一個 worktree、推到同一條工作分支(PR 會自己更新,不開第二個 PR)。先移除就得為了改一行重建整個 worktree。
|
||||||
|
|
||||||
@@ -105,12 +116,18 @@ SDLC 各階段引用的參考分支與來源分支,**一律指遠端的 `origi
|
|||||||
|
|
||||||
### PR 未合併就不開下一個工作包
|
### PR 未合併就不開下一個工作包
|
||||||
|
|
||||||
一個工作包的 PR 還沒合併,就不得開始下一包。每次要領工作包之前先確認:
|
一個工作包的 PR 還沒合併,就不得開始下一包。修正一律回原 worktree、推同一條工作分支,**不為同一包開第二個 PR**。
|
||||||
|
|
||||||
1. `gitea.sh pr-status {owner}/{repo} {index}` 看 `{state} {merged} {mergeable}`。
|
這條規則在程式層強制,不靠內文自律,分工是兩層:
|
||||||
2. 未合併 → **同時**用 `gitea.sh pr-comments {owner}/{repo} {index}` 讀留言(issue 留言、審查評語、行內留言,含沒有文字的 `APPROVED`/`REQUEST_CHANGES`),原文轉述給使用者。
|
|
||||||
3. 依 `jsc-ask:ask` 詢問:依留言修正(回原 worktree 改、推同一條工作分支)或先等待。**不自行決定,也不為同一包開第二個 PR。**
|
| 層 | 誰執行 | 做什麼 |
|
||||||
4. 已關閉但未合併 → 回報並詢問,不得當成完成。
|
| --- | --- | --- |
|
||||||
|
| 工具閘門 | `tools/wp-gate.sh` | 唯一去問 Gitea 的一方,也是唯一有權解鎖的一方。`check` 查合併狀態、印出全部留言、印 `latest=` 時間戳;`lock` 在 PR 開好後記下未結清。結束碼 0 放行、1 擋住、2 用法錯誤、3 查不到(查不到就擋,不放行) |
|
||||||
|
| hook 提醒 | `jsc-hooks` 的 `sdlc-gate.sh wp-check` | 只讀 `$JSC_HOME/wp/` 的狀態檔,不打網路。`prompt` 模式注入提醒但**不擋提示**;`skill` 模式擋掉 `plan`、`analyze`、`maintain`,放行 `implement`——擋住的是「跳去做別的階段」,不是「回來把這一包做完」 |
|
||||||
|
|
||||||
|
狀態檔不綁 session:PR 沒合併就是沒合併,開新對話照樣擋。
|
||||||
|
|
||||||
|
查驗程序的細節(何時帶 `--since`、留言逐筆的處理結果、修不動的留言怎麼辦、時間戳寫回哪裡)由 `skills/implement/SKILL.md` 步驟 4 擁有,本檔不重複。
|
||||||
|
|
||||||
## 不破壞既有工作
|
## 不破壞既有工作
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,12 @@
|
|||||||
# 交付內容型別
|
# 交付內容型別
|
||||||
|
|
||||||
交付/交接工作包開工時,先跟使用者確認這份交付要產出什麼內容。預設選項固定兩個,其餘由使用者輸入。
|
詢問時機與鐵則(不得自行預設、不得跳過詢問)由 `skills/implement/SKILL.md` 步驟 7 擁有。本檔只定義每個選項要產出什麼內容。
|
||||||
|
|
||||||
| 選項 | 內容 |
|
| 選項 | 內容 |
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| 1. API 文件 | 端點路徑、輸入參數(**全部**)、輸出參數(**全部**);見下節 |
|
| 1. API 文件 | 端點路徑、輸入參數(**全部**)、輸出參數(**全部**);見下節 |
|
||||||
| 2. 由使用者輸入 | 使用者自己講要什麼內容;照使用者說的做,不套 API 文件格式 |
|
| 2. 由使用者輸入 | 使用者自己講要什麼內容;照使用者說的做,不套 API 文件格式 |
|
||||||
|
|
||||||
選項一律依 `jsc-ask:ask` 規則呈現,並標明影響範圍。**不得自行預設,也不得跳過詢問。**
|
|
||||||
|
|
||||||
## API 文件
|
## API 文件
|
||||||
|
|
||||||
### 必備欄位
|
### 必備欄位
|
||||||
@@ -23,7 +21,7 @@
|
|||||||
|
|
||||||
### 參數範例:真實資料優先
|
### 參數範例:真實資料優先
|
||||||
|
|
||||||
1. 先找真實來源:資料庫欄位與實際資料列、實際 API 回應、既有設定檔或 fixture、日誌。標成 `真實:{來源}`。
|
1. 先找真實來源:資料庫欄位與實際資料列、實際 API 回應、既有設定檔或 fixture、日誌。標成 `真實:{source}`(`{source}` 換成實際來源名稱)。
|
||||||
2. 找不到來源才由邏輯推理,標成 `推論:無來源`,讓接手者知道那個值還沒被驗證。
|
2. 找不到來源才由邏輯推理,標成 `推論:無來源`,讓接手者知道那個值還沒被驗證。
|
||||||
3. **不得**編一個看起來合理的值當成真實資料。
|
3. **不得**編一個看起來合理的值當成真實資料。
|
||||||
4. 範例只保留**結構與格式**。個資一律遮蔽或改寫成格式描述,不把真實個資寫進交付文件。
|
4. 範例只保留**結構與格式**。個資一律遮蔽或改寫成格式描述,不把真實個資寫進交付文件。
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
# 模型閘門 — 各階段動工前的能力標籤判定
|
||||||
|
|
||||||
|
SDLC 每個階段動工前先過模型閘門。判定全在程式層,由 `jsc-hooks/hooks/sdlc-gate.sh` 執行;模型不得自評標籤。
|
||||||
|
|
||||||
|
## 執行順序
|
||||||
|
|
||||||
|
1. `jsc-cli/tools/model-tags.sh sync`:把 `jsc-cli/references/model-tags.md` 的標籤表同步到 `$JSC_HOME/model-tags.tsv`。
|
||||||
|
2. `jsc-hooks/hooks/sdlc-gate.sh lock {stage}`:腳本從 transcript 讀出**實際**模型 id,比對該階段的必要標籤,相符才上鎖。
|
||||||
|
|
||||||
|
## 各階段必要標籤
|
||||||
|
|
||||||
|
| 階段 | 必要標籤 |
|
||||||
|
| --- | --- |
|
||||||
|
| `plan` | `reasoning-max` |
|
||||||
|
| `analyze` | `reasoning-max` |
|
||||||
|
| `implement` | `coding` |
|
||||||
|
| `maintain` | 無。實際模型 id 可判定就通過 |
|
||||||
|
|
||||||
|
## 鐵則
|
||||||
|
|
||||||
|
| 項目 | 規則 |
|
||||||
|
| --- | --- |
|
||||||
|
| 標籤來源 | 只認腳本的判定。不得宣稱自己沒驗證過的標籤,也不得用自己的判斷取代腳本結論 |
|
||||||
|
| 非零退出 | 一律視為阻擋:原文轉述腳本訊息、停止該技能、該回合不做別的事 |
|
||||||
|
| `unlock` | 不得用來繞過閘門。要不要解鎖是使用者的決定 |
|
||||||
|
| 回報 | **每次都要回報**:階段、必要標籤、腳本從 transcript 讀到的實際模型 id、判定結果。通過與阻擋都要講——安靜通過看起來跟跳過檢查一樣,而判定搬進程式層的理由就是「宣稱有檢查」不可信 |
|
||||||
|
| 退出 0 | 該階段已上鎖。到下一階段的閘門重新上鎖之前,同一階段內把模型換成不合格的,下一輪提示會被 sdlc-gate hook 以 exit 2 擋下 |
|
||||||
|
| 上鎖時機 | 只在階段變換時上鎖。同一階段內逐項或逐專案跑時不重新上鎖 |
|
||||||
+21
-30
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: analyze
|
name: analyze
|
||||||
description: SDLC analysis stage. Gate on capability tags enforced in code by sdlc-gate (analyze requires reasoning-max, verified against the transcript's actual model id), confirm the source branch with the user, pick a plan from PLAN_CONTENTS, analyze user stories against the current state (working directory plus REPO_{HASH} inventory for reuse). Keep questioning until consensus per references/consensus.md, run WBS with the delivery/handover package as a standalone WP-01 that implementation packages depend on, estimate them with CPM, split each into TDD todos, and write wiki page ANALYZE_{HASH} with real sample data. Logic only - never write code or modify files. Use after planning and before implementation.
|
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.
|
||||||
---
|
---
|
||||||
|
|
||||||
# analyze
|
# analyze
|
||||||
@@ -13,51 +13,42 @@ All wiki reads and writes go through `jsc-gitea:wiki`.
|
|||||||
|
|
||||||
## Steps
|
## Steps
|
||||||
|
|
||||||
1. **Model gate and stage lock** (capability tags, enforced in code — never self-assessed):
|
1. **Model gate and stage lock** — run `jsc-cli/tools/model-tags.sh sync`, then `jsc-hooks/hooks/sdlc-gate.sh lock analyze`. This stage requires the `reasoning-max` capability tag. Rules: `references/model-gate.md`. Completion condition: the script exited 0, and you have reported the stage, the required tag, the actual model id it read from the transcript, and the verdict.
|
||||||
1. Run `jsc-cli/tools/model-tags.sh sync` to refresh `$JSC_HOME/model-tags.tsv` from `jsc-cli/references/model-tags.md`.
|
|
||||||
2. Run `jsc-hooks/hooks/sdlc-gate.sh lock analyze`. The script reads the **actual** model id from the transcript, compares it against this stage's required tags (`reasoning-max`), and locks the stage only when they match.
|
|
||||||
3. **Never claim a capability tag you have not verified with that script**, and never substitute your own judgement for its verdict. Non-zero exit = blocked: relay the script's message verbatim, stop the skill, do nothing else this turn. Do not run `unlock` to get past the gate; only the user may decide that.
|
|
||||||
4. **Report the gate result to the user every time — whether it passed or blocked.** State the stage, the required tags, the actual model id the script read from the transcript, and the verdict. A silent pass looks identical to a skipped check, and the whole point of moving this into code was that a claimed check cannot be trusted.
|
|
||||||
5. Exit 0 means the stage is locked. From now until the next stage's gate runs, the sdlc-gate hook blocks every prompt whose model stops satisfying the tags.
|
|
||||||
2. **Confirm the source branch** — the branch whose code counts as the current state:
|
2. **Confirm the source branch** — the branch whose code counts as the current state:
|
||||||
1. Run `git fetch --prune origin` first — without it, every `origin/...` reference is stale cache. Then report the working directory's current branch, the **remote** branches available (`git branch -r`; never `git branch`) and whether the working tree is clean.
|
1. Run `git fetch --prune origin` first — without it, every `origin/...` reference is stale cache. Then report the working directory's current branch, the **remote** branches available (`git branch -r`; never `git branch`) and whether the working tree is clean.
|
||||||
2. Ask per `jsc-ask:ask` rules which branch the analysis reads from; state the impact scope on every option (analysing the wrong branch produces work packages for code that does not exist).
|
2. Ask per `jsc-ask:ask` rules which branch the analysis reads from; state the impact scope on every option (analysing the wrong branch produces work packages for code that does not exist).
|
||||||
3. **The current state is always the remote branch `origin/{來源分支}`, never the local one.** The working directory's HEAD must point at the same commit as `origin/{來源分支}`; behind, ahead or diverged all mean you would be analysing code that is not what the remote holds. Report the gap (`git rev-list --left-right --count origin/{來源分支}...HEAD`) and stop — this stage never switches branches, never stashes, never pulls and never touches the working tree. Rules in `references/branch.md`.
|
3. **The current state is always the remote branch `origin/{source-branch}`, never the local one.** The working directory's HEAD must point at the same commit as `origin/{source-branch}`; behind, ahead or diverged all mean you would be analysing code that is not what the remote holds. Report the gap (`git rev-list --left-right --count origin/{source-branch}...HEAD`) and stop — this stage never switches branches, never stashes, never pulls and never touches the working tree. Rules in `references/branch.md`.
|
||||||
4. Record the confirmed branch and the head sha **of `origin/{來源分支}`** on the analysis page. Completion condition: the user has confirmed the branch explicitly; never infer it from the current checkout alone.
|
4. Record the confirmed branch and the head sha **of `origin/{source-branch}`** on the analysis page. Completion condition: the user has confirmed the branch explicitly; never infer it from the current checkout alone.
|
||||||
3. Read `PLAN_CONTENTS` via `jsc-gitea:wiki` for plans whose status is the literal 「未分析」 (name and HASH), and read `ANALYZE_CONTENTS` for existing analyses.
|
3. Read `PLAN_CONTENTS` via `jsc-gitea:wiki` for plans whose status is the literal 「未分析」 (name and HASH), and read `ANALYZE_CONTENTS` for existing analyses. Completion condition: you have listed every 未分析 plan with its name and HASH plus every existing analysis, or reported that a list is empty.
|
||||||
4. Let the user choose per `jsc-ask:ask` rules: **extend an existing analysis** or **analyze a new plan**. State the impact scope on every option.
|
4. Let the user choose per `jsc-ask:ask` rules: **extend an existing analysis** or **analyze a new plan**. State the impact scope on every option. Completion condition: the user has picked one option explicitly, and you have named the target — the existing `ANALYZE_{HASH}` page, or the plan the new analysis covers.
|
||||||
5. Analyze the plan page's user stories one by one against the **current state**, **questioning until consensus** per `references/consensus.md` (the single authority for both planning and analysis): every answer produces the next question, and consensus needs both no output-changing unknown **and** the user's explicit confirmation. Never assume a missing detail, and never start the WBS while any item is still open. Current state means:
|
5. Analyze the plan page's user stories one by one against the **current state**, **questioning until consensus** per `references/consensus.md` (the single authority for both planning and analysis): every answer produces the next question, and consensus needs both no output-changing unknown **and** the user's explicit confirmation. Never assume a missing detail, and never start the WBS while any item is still open. Current state means:
|
||||||
1. Every file in the working directory, at the commit `origin/{來源分支}` points to (verified in step 2).
|
1. Every file in the working directory, at the commit `origin/{source-branch}` points to (verified in step 2).
|
||||||
2. **Reuse existing methods and endpoints whenever possible**:
|
2. **Reuse an existing method or endpoint unless its logic cannot satisfy the requirement**:
|
||||||
- Check the `REPO_{HASH}` inventory page first. Re-inventory when the feature or endpoint is missing, or when the recorded commit sha differs from the current one.
|
- Check the `REPO_{HASH}` inventory page first. Re-inventory when the feature or endpoint is missing, or when the recorded commit sha differs from the current one.
|
||||||
- Re-inventory **MUST run as a sub agent**: analyze the repository's features and endpoints, attach the current commit sha, write back to `REPO_{HASH}` with `templates/repo-page.md`, and update `REPO_CONTENTS` per `templates/repo-contents.md`.
|
- Re-inventory **MUST run as a sub agent**: analyze the repository's features and endpoints, attach the current commit sha, write back to `REPO_{HASH}` with `templates/repo-page.md`, and update `REPO_CONTENTS` per `templates/repo-contents.md`.
|
||||||
- For each reuse candidate, confirm the file path and method name first, then analyze whether its logic fits the requirement.
|
- For each reuse candidate, confirm the file path and method name first, then analyze whether its logic fits the requirement. Reject a candidate only for a stated reason, and record both the candidate and that reason in the analysis page's 複用決策 field.
|
||||||
6. Run a **Work Breakdown Structure (WBS)**: split the user stories into work packages, number them sequentially (`WP-01`, `WP-02`, ...) and mark dependencies.
|
|
||||||
7. **`WP-01` is always the delivery/handover work package** — see 「交付工作包最優先」 below. It stands alone, never merged into an implementation package, and every implementation package that consumes its spec depends on it.
|
|
||||||
8. Estimate every work package's effort in hours and days with the **Critical Path Method (CPM)**, and mark the critical path.
|
|
||||||
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`.
|
|
||||||
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.
|
|
||||||
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 「已分析」.
|
|
||||||
|
|
||||||
## 交付工作包最優先(delivery package is WP-01)
|
Completion condition: every user story has reached consensus under both conditions of `references/consensus.md`, and every reuse decision — reused, or rejected with its reason — is recorded in 複用決策.
|
||||||
|
6. Run a **Work Breakdown Structure (WBS)**: split the user stories into work packages, number them sequentially (`WP-01`, `WP-02`, ...) and mark dependencies. Completion condition: every user story on the plan page maps to at least one numbered work package, and every dependency edge is recorded in the WBS table's 相依 column.
|
||||||
|
7. **`WP-01` is always the delivery/handover work package** — see "Delivery package is WP-01" below. It stands alone, never merged into an implementation package, and every implementation package that consumes its spec depends on it. Completion condition: `WP-01` is marked 交付 `是` and holds only spec-shaped items, and every implementation package that consumes its spec names `WP-01` in its 相依 column — or the no-handover case below is confirmed with the user and its reason is written on the page.
|
||||||
|
8. Estimate every work package's effort in hours and days with the **Critical Path Method (CPM)**, and mark the critical path. Completion condition: every work package carries an hours figure and a days figure, and the critical path plus its total days are written on the page.
|
||||||
|
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.
|
||||||
|
|
||||||
|
## Delivery package is WP-01
|
||||||
|
|
||||||
A work package is a **delivery/handover package** when someone else — another person, another CLI, another team — has to act on its output. Interfaces, schemas, spec documents, acceptance criteria and sample payloads are delivery output; endpoint bodies and UI wiring are not.
|
A work package is a **delivery/handover package** when someone else — another person, another CLI, another team — has to act on its output. Interfaces, schemas, spec documents, acceptance criteria and sample payloads are delivery output; endpoint bodies and UI wiring are not.
|
||||||
|
|
||||||
- **`WP-01` is the delivery/handover package, always first, always standalone.** Pull every spec-shaped item out of the implementation packages and put it here. Never merge a spec into the package that implements it — merging removes the ability to hand the spec over early, which is the whole point.
|
- **`WP-01` is the delivery/handover package, always first, always standalone.** Pull every spec-shaped item out of the implementation packages and put it here. Never merge a spec into the package that implements it — merging removes the ability to hand the spec over early, which is the whole point.
|
||||||
- **Implementation packages depend on `WP-01`** when they consume its spec, and they are the backup queue (實作候補): still numbered, still estimated, still on the page, just ranked after it.
|
- **Implementation packages depend on `WP-01`** when they consume its spec, and they are the backup queue: still numbered, still estimated, still on the page, just ranked after it.
|
||||||
- Mark every work package as 交付 `是` / `否` in the WBS table, and record `WP-01`'s intended content type in the 交付型別 column when the user has already decided it (options in `references/deliver-formats.md`; the type is confirmed for real when `implement` starts that package).
|
- Mark every work package as 交付 `是` / `否` in the WBS table, and record `WP-01`'s intended content type in the 交付型別 column when the user has already decided it (options in `references/deliver-formats.md`; the type is confirmed for real when `implement` starts that package).
|
||||||
- Dependencies still win among the rest: never place a package before one it depends on. Within the same dependency level, delivery-shaped packages come first.
|
- Dependencies still win among the rest: never place a package before one it depends on. Within the same dependency level, delivery-shaped packages come first.
|
||||||
- **When nothing is genuinely deliverable** (say, an internal refactor with no interface change), do not invent an empty `WP-01`: confirm with the user per `jsc-ask:ask` that this analysis has no handover output, then note the reason on the page and number the implementation packages from `WP-01`.
|
- **When nothing is genuinely deliverable** (say, an internal refactor with no interface change), do not invent an empty `WP-01`: confirm with the user per `jsc-ask:ask` that this analysis has no handover output, then note the reason on the page and number the implementation packages from `WP-01`.
|
||||||
|
|
||||||
## 範例資料(sample data)
|
## Sample data
|
||||||
|
|
||||||
Every sample value on the analysis page — request and response payloads, field values, config snippets, test fixtures — follows this order:
|
Every sample value on the analysis page — request and response payloads, field values, config snippets, test fixtures — follows `references/deliver-formats.md`, which owns the source order, the labelling and the API-document layout. Record each value's origin in the WBS table's 資料來源 column. Completion condition: every sample value on the page carries a 資料來源 label of either 「真實:{source}」 or 「推論:無來源」, and no sample value contains personal data.
|
||||||
|
|
||||||
1. **Real data first.** Take values from an actual source: the database schema and rows, a real API response, an existing config or fixture file, logs. Record where each sample came from in the WBS table's 資料來源 column (for example `真實:dbo.Member`).
|
|
||||||
2. **Only when no source exists**, derive the value by reasoning from the surrounding logic — and label it as inferred (for example `推論:無來源`), so the receiving side knows it is unverified.
|
|
||||||
3. Never invent a plausible-looking value and present it as real, and never leave the origin of a sample unstated.
|
|
||||||
4. Real data goes on the page as **structure and format only**: field names, types, shapes. Never copy personal data onto the wiki — mask any personal identifier, and prefer describing the format over pasting the value.
|
|
||||||
5. When `WP-01`'s content type is an API document, the required fields and the new-versus-existing parameter marking are defined in `references/deliver-formats.md`; follow it rather than inventing a layout.
|
|
||||||
|
|
||||||
## Hard limits
|
## Hard limits
|
||||||
|
|
||||||
|
|||||||
+36
-50
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: implement
|
name: implement
|
||||||
description: SDLC implementation stage. Gate on capability tags enforced in code by sdlc-gate (implement requires coding), confirm the analysis page's source branch (which is both the worktree base and the PR target), claim a ready work package from ANALYZE_CONTENTS with a work ticket, create a worktree from origin/{來源分支} under .worktree/{分析頁 HASH}/{repo}, and complete its TDD todos one by one inside it, updating the wiki after every item. Every finished work package gets its own commit, push and PR back to the source branch, then the stage stops: no further package may start until that PR merges, and each check of it also reads the PR comments and asks the user whether to fix accordingly. A delivery package confirms its content type (API document or user-defined) before its first todo, and at the end you ask which delivery-document format to produce (DELIVER_{HASH} wiki page or Gitea issue comment) before optionally registering the project in MAINTAIN_CONTENTS. 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 and an optional MAINTAIN_CONTENTS entry. Use when analysis is done and code must be written; not for planning or analysis.
|
||||||
---
|
---
|
||||||
|
|
||||||
# implement
|
# implement
|
||||||
@@ -10,63 +10,49 @@ All wiki reads and writes go through `jsc-gitea:wiki`.
|
|||||||
|
|
||||||
## Steps
|
## Steps
|
||||||
|
|
||||||
1. **Model gate and stage lock** (capability tags, enforced in code — never self-assessed):
|
1. **Model gate and stage lock** — run `jsc-cli/tools/model-tags.sh sync`, then `jsc-hooks/hooks/sdlc-gate.sh lock implement`. This stage requires the `coding` capability tag. Rules: `references/model-gate.md`. Completion condition: the script exited 0, and you have reported the stage, the required tag, the actual model id it read from the transcript, and the verdict.
|
||||||
1. Run `jsc-cli/tools/model-tags.sh sync` to refresh `$JSC_HOME/model-tags.tsv` from `jsc-cli/references/model-tags.md`.
|
|
||||||
2. Run `jsc-hooks/hooks/sdlc-gate.sh lock implement`. The script reads the **actual** model id from the transcript, compares it against this stage's required tags (`coding`), and locks the stage only when they match.
|
|
||||||
3. **Never claim a capability tag you have not verified with that script**, and never substitute your own judgement for its verdict. Non-zero exit = blocked: relay the script's message verbatim, stop the skill, do nothing else this turn. Do not run `unlock` to get past the gate; only the user may decide that.
|
|
||||||
4. **Report the gate result to the user every time — whether it passed or blocked.** State the stage, the required tags, the actual model id the script read from the transcript, and the verdict. A silent pass looks identical to a skipped check, and the whole point of moving this into code was that a claimed check cannot be trusted.
|
|
||||||
5. Exit 0 means the stage is locked. From now until the next stage's gate runs, the sdlc-gate hook blocks every prompt whose model stops satisfying the tags.
|
|
||||||
2. **Confirm the source branch — it is also this stage's PR target**:
|
2. **Confirm the source branch — it is also this stage's PR target**:
|
||||||
1. Run `git fetch --prune origin` first — every `origin/...` reference is stale cache without it. Then read the source branch from the analysis page and report it, along with the current branch and whether the working tree is clean. **Branches are always the remote ones; local branches are never the basis.**
|
1. Run `git fetch --prune origin`, then read the source branch from the analysis page and report it, along with the current branch and whether the working tree is clean.
|
||||||
2. Ask per `jsc-ask:ask` rules to confirm that `origin/{來源分支}` is both the worktree's base and the PR target for every work package in this analysis. State the impact scope: each finished work package merges back into the source branch, and that branch as a whole reaches `develop` later as its own separate PR.
|
2. Ask per `jsc-ask:ask` rules to confirm that `origin/{source-branch}` is both the worktree's base and the PR target for every work package in this analysis. State the impact scope: each finished work package merges back into the source branch, and that branch as a whole reaches `develop` later as its own separate PR.
|
||||||
3. `origin/{來源分支}` does not exist on the remote → report and stop. Never fall back to `develop` silently.
|
3. **A source branch missing from the remote is a stop-and-report condition, never a silent fallback.** That rule (section 「來源分支在遠端找不到」), the remote-only basis and the uncommitted-changes rules: `references/branch.md`.
|
||||||
4. Uncommitted changes in the main working directory → warn first and let the user commit or back them up; never discard them. Implementation happens in a worktree (step 7), so the main working directory stays untouched anyway.
|
4. Completion condition: the user has confirmed the source branch explicitly, and it is recorded on the analysis page next to the work ticket.
|
||||||
5. Record the confirmed source branch on the analysis page next to the work ticket. Completion condition: the user has confirmed it explicitly.
|
3. **Generate a work ticket**: format `TICKET_{yyyyMMdd}_{HHmmss}_{HASH}`. `{HASH}` = the shared wiki hash for `{owner}/{repo}`, computed by `jsc-gitea/tools/hash-id` (see `jsc-gitea:wiki`). Rename the current session to the ticket name; skip the rename only when the CLI exposes no rename command. Completion condition: the ticket string exists, and you have reported it together with which branch applied — renamed, or skipped because this CLI has no rename command.
|
||||||
3. **Generate a work ticket**: format `TICKET_{yyyyMMdd}_{HHmmss}_{HASH}`. `{HASH}` = the shared wiki hash for `{owner}/{repo}`, computed by `jsc-gitea/tools/hash-id` (see `jsc-gitea:wiki`). Try to rename the current session to the ticket name (skip when the CLI does not support it).
|
4. **Settle the previous work package's PR before picking anything — the gate lives in code, not in this text**:
|
||||||
4. **Before picking anything, settle the previous work package's PR.** A work package whose PR is still open blocks the next one — no exceptions:
|
1. Read the analysis page's PR column. For every work package holding a PR that is not marked merged, run `jsc-sdlc/tools/wp-gate.sh check {owner}/{repo} {index} --since {the comment timestamp recorded in that PR column}`. Drop `--since` when that package has no recorded timestamp yet.
|
||||||
1. Read the analysis page's PR column. Any work package holding a PR that is not merged → run `jsc-gitea/tools/gitea.sh pr-status {owner}/{repo} {index}` (prints `{state} {merged} {mergeable}`).
|
2. Exit 0 (`status=merged`) clears that package: remove its worktree (`references/branch.md`) and mark the package done on the analysis page.
|
||||||
2. `merged=true` → clear the block: remove that package's worktree (`git worktree remove {路徑}` then `git worktree prune`; `.worktree/{HASH}/` empty → delete it too), mark the package done on the analysis page, and continue.
|
3. Exit 1 (`status=open` or `status=closed-unmerged`) blocks. Fix every comment the script printed — issue comments, review verdicts and inline comments alike. **Each round of fixes MUST run as a sub agent** inside the original worktree, pushing to the same work branch, so the PR updates itself. Never open a second PR for the same package, and never ask whether to fix or wait: the gate already decided.
|
||||||
3. Not merged → **read the comments in the same breath**: `jsc-gitea/tools/gitea.sh pr-comments {owner}/{repo} {index}` returns issue comments, review verdicts (including `APPROVED` / `REQUEST_CHANGES` with no text) and inline code comments, sorted by time. Show them to the user verbatim — author, time, and what they said.
|
4. Give every printed comment an outcome — fixed, no fix needed, or cannot fix. Ignore what you cannot fix, plus pure discussion and praise: do not reply on the PR and do not stop the flow, and list each ignored comment with its reason in your final report.
|
||||||
4. Ask per `jsc-ask:ask` rules what to do, with the options: **fix per the comments** (go back into that package's worktree, implement the fix, push to the same working branch — the PR updates itself, no new PR), or **leave it and wait**. State the impact scope on each. **Never decide this yourself, and never open a second PR for the same package.**
|
5. Write the script's `latest=` value into that work package's PR column, appended after the existing PR link as `#{index} 已處理留言 {ISO time}`, and save the page back to the wiki. Next run passes it as `--since`, so handled comments stay handled. Reuse the existing PR column; the analysis page's columns belong to `analyze`.
|
||||||
5. Closed but not merged → report it and ask the user how to proceed; do not silently treat it as done.
|
6. Exit 3 means the gate could not decide (a missing dependency, or the PR could not be found). Report it and stop — an undecidable gate never counts as merged.
|
||||||
6. **Only when no unmerged PR remains may you go on.** Stop here otherwise.
|
7. Completion condition: no work package holds an unmerged PR; stop here otherwise.
|
||||||
5. Read `ANALYZE_CONTENTS` via `jsc-gitea:wiki` and list what is unfinished: plan name, HASH, work package number, count of open items. A selectable work package must satisfy all three: **unfinished, dependency-free (or all dependencies done), and not holding a work ticket**.
|
5. Read `ANALYZE_CONTENTS` via `jsc-gitea:wiki` and list what is unfinished: plan name, HASH, work package number, count of open items. A selectable work package must satisfy all three: **unfinished, dependency-free (or all dependencies done), and not holding a work ticket**. Completion condition: you have listed every selectable work package, or reported that none is selectable and stopped.
|
||||||
6. Let the user pick a work package per `jsc-ask:ask` rules (options state open-item count and estimated effort). **List delivery packages (交付 `是`) first** — the analysis page makes `WP-01` the standalone delivery package, so keep that order in the options. Write the ticket into that work package's ticket column (the zh-TW field 「工作證」) on the analysis page and save it back to the wiki. **Only after the ticket is saved successfully may you proceed.**
|
6. **Step 4's gate comes first: while `wp-gate.sh check` exits 1, no work package may be picked.** Let the user pick a work package per `jsc-ask:ask` rules (options state open-item count and estimated effort). **List delivery packages (交付 `是`) first** — the analysis page makes `WP-01` the standalone delivery package, so keep that order in the options. Write the ticket into that work package's ticket column (the zh-TW field 「工作證」) on the analysis page and save it back to the wiki. Completion condition: the ticket is saved on the wiki; only then may you proceed.
|
||||||
7. **When the package taken is a delivery/handover package, confirm its content before doing any of its todos**:
|
7. **A delivery/handover package confirms its content before its first todo**:
|
||||||
1. Ask per `jsc-ask:ask` rules what this delivery must contain. The default options are fixed: **1. API 文件** (endpoint path, **every** input parameter, **every** output parameter) and **2. 由使用者輸入** (the user states the content themselves). State the impact scope on each. **Never assume the type, and never skip this — the answer decides what the whole package produces.**
|
1. Ask per `jsc-ask:ask` rules what this delivery must contain. The options are fixed: **1. API 文件** and **2. 由使用者輸入**. State the impact scope on each. **Never assume the type, and never skip this — the answer decides what the whole package produces.**
|
||||||
2. Chose API 文件 → follow `references/deliver-formats.md`: all parameters listed (not just the main ones), each with name, type, required flag, example, data source and new-versus-existing status; sample values take real sources first and are labelled `真實:{來源}` or `推論:無來源`; **an existing endpoint must mark new versus old parameters both ways** — the status column (🆕 新增 / ⚠️ 變更 / ❌ 移除 / blank for untouched) and a `diff` code block for colour. HTML `style` attributes get filtered by the wiki, so never rely on them.
|
2. Required fields, sample-data order and the new-versus-existing parameter marking: `references/deliver-formats.md`.
|
||||||
3. Chose 由使用者輸入 → produce exactly what the user described; do not force the API document layout onto it.
|
3. Completion condition: the confirmed type is written into the analysis page's 交付型別 column and saved back to the wiki before the first todo starts.
|
||||||
4. Record the confirmed type in the analysis page's 交付型別 column and save it before starting the todos.
|
8. **Create the worktree — before touching any code.** Run `git fetch --prune origin`, read the source branch and the repositories involved from the analysis page, then ask per `jsc-ask:ask` rules how to handle the branch. Path layout, the two `git worktree add` options, quoting, `.git/info/exclude` and the reporting duty: `references/branch.md`. Completion condition: every repository the analysis page names has a worktree built from `origin/{source-branch}`, and you have reported each worktree's path, checked-out branch, source branch and starting commit sha.
|
||||||
8. **Create the worktree — before touching any code**. Full rules in `references/branch.md`:
|
9. **Complete the work package's open items one at a time. Every item MUST run as a sub agent**, working inside the worktree:
|
||||||
1. Run `git fetch --prune origin`, then read the **source branch** and the repositories involved from the analysis page. The worktree is built from `origin/{來源分支}` — **never from a local branch of the same name**, which may be behind, ahead or diverged. Every code change happens inside the worktree; the main working directory's branch and working tree stay untouched.
|
1. One sub agent takes one item and follows the TDD loop: red before green, one vertical slice. Rules and anti-patterns: `references/tdd.md` (refactoring belongs to the review stage).
|
||||||
2. Path: `{工作目錄}/.worktree/{分析頁 HASH}/{repo}` — the HASH without the `ANALYZE_` prefix, the repo name without its owner. One worktree per repository, all under the same HASH directory.
|
2. The main agent keeps only the wiki bookkeeping: when the sub agent reports the item done, flip its `[ ]` to `[x]` on the analysis page and save to the wiki.
|
||||||
3. **Ask per `jsc-ask:ask` rules how to handle the branch**, with the two fixed options: create a new working branch off the remote source branch (`git worktree add -b {工作分支} {路徑} "origin/{來源分支}"`), or check the source branch out with remote tracking (`git worktree add --track -b {來源分支} {路徑} "origin/{來源分支}"`). State the impact scope on each. **Never assume, never skip the question.**
|
3. Completion condition: every item of the package shows `[x]` on the saved analysis page, and each save happened before the next item's sub agent started.
|
||||||
4. **Quote every branch name and path**: source branches carry Chinese characters, spaces and multiple slashes (for example `feat/一址通/查地址/完整版/P2`), and an unquoted argument gets split.
|
10. When all items are done, call `jsc-review:code-review` and wait for the verdict. Each round of fixes **MUST run as a sub agent** inside the same worktree. Completion condition: the review passes.
|
||||||
5. No local clone of that repository → stop and ask the user for its path; never clone on your own initiative and never guess the location.
|
|
||||||
6. Add `.worktree/` to that repository's `.git/info/exclude` — never edit the user's shared `.gitignore`.
|
|
||||||
7. Report the worktree path, the checked-out branch, the source branch and the commit sha `origin/{來源分支}` pointed at — that sha is this implementation's starting point and must stay traceable. Completion condition: every involved repository has a worktree and you have reported all of them.
|
|
||||||
9. List every open item of the work package and implement them **one at a time**, **inside the worktree**:
|
|
||||||
- Follow the TDD loop: red before green, one slice at a time; rules and anti-patterns in `references/tdd.md` (refactoring belongs to the review stage).
|
|
||||||
- After each item, flip its `[ ]` to `[x]` on the analysis page and save to the wiki before starting the next item.
|
|
||||||
10. When all items are done, call `jsc-review:code-review` and wait for the review; on failure, fix and re-review until it passes.
|
|
||||||
11. **One work package finished → commit, push, PR back to the source branch. Then stop and wait for that PR**:
|
11. **One work package finished → commit, push, PR back to the source branch. Then stop and wait for that PR**:
|
||||||
1. **One work package, one PR.** Never let two finished packages share a PR, and never carry a finished package over to the next one — the point of splitting packages is that each lands reviewable on its own.
|
1. Call `jsc-git:pr` from inside the worktree, **passing `{source-branch}` as the base branch**. One package, one PR; the branch rules behind that are in `references/branch.md`.
|
||||||
2. Call `jsc-git:pr` from inside the worktree, **passing `{來源分支}` as the base branch**. It commits everything (via `jsc-git:commit`), creates the working branch, pushes, and opens the PR. `jsc-git:pr` falls back to `develop` when no base is passed in, so passing it explicitly is what keeps a single work package from merging straight past its feature branch.
|
2. Write the PR URL and number into that work package's PR column on the analysis page and save it back to the wiki, so the next run of this skill can find it (step 4).
|
||||||
3. Write the PR URL and number into that work package's PR column on the analysis page and save it back to the wiki, so the next run of this skill can find it (step 4).
|
3. Run `jsc-sdlc/tools/wp-gate.sh lock {owner}/{repo} {index}`. The lock is what makes step 4's gate hold across work sessions — a new session starts blocked until that PR merges.
|
||||||
4. **Keep the worktree.** It is removed only after the PR is merged — comments may ask for changes, and rebuilding a worktree to make them is wasted work.
|
4. **Do not start another work package.** Completion condition: the PR exists, its URL is saved on the analysis page and reported to the user, the lock exists (`status=locked`), and the skill has stopped.
|
||||||
5. **Do not start another work package.** Report the PR URL and stop. The next run picks up at step 4, which checks whether this PR merged and reads its comments.
|
|
||||||
12. **Delivery document** — a finished work package is a delivery, so **always ask before producing it; never pick a format silently and never skip this step**:
|
12. **Delivery document** — a finished work package is a delivery, so **always ask before producing it; never pick a format silently and never skip this step**:
|
||||||
1. Ask per `jsc-ask:ask` rules which format to produce. The options are fixed: **a `DELIVER_{HASH}` wiki page** or **a Gitea issue comment**. State the impact scope on each (the wiki page lives beside the plan and analysis pages; the issue comment reaches whoever follows that issue).
|
1. Ask per `jsc-ask:ask` rules which format to produce. The options are fixed: **a `DELIVER_{HASH}` wiki page** or **a Gitea issue comment**. State the impact scope on each (the wiki page lives beside the plan and analysis pages; the issue comment reaches whoever follows that issue).
|
||||||
2. Both formats use the same structure — `templates/deliver-page.md`, in Traditional Chinese. Only the destination differs.
|
2. Both formats use the same structure — `templates/deliver-page.md`, in Traditional Chinese. Only the destination differs. Sample values and personal-data handling: `references/deliver-formats.md`.
|
||||||
3. Wiki page: write `DELIVER_{HASH}` through `jsc-gitea:wiki`, where `{HASH}` comes from `jsc-gitea/tools/hash-id` over `{owner}/{repo}` plus the work package number (for example `plugins/sdlc#WP-01`), so each work package gets its own page instead of overwriting the previous one. A new page is added to `DELIVER_CONTENTS` per `templates/deliver-contents.md`.
|
3. Wiki page: write `DELIVER_{HASH}` through `jsc-gitea:wiki`, where `{HASH}` comes from `jsc-gitea/tools/hash-id` over `{owner}/{repo}` plus the work package number (for example `plugins/sdlc#WP-01`), so each work package gets its own page instead of overwriting the previous one. A new page is added to `DELIVER_CONTENTS` per `templates/deliver-contents.md`.
|
||||||
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 `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`.
|
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. Sample values follow the analysis page's 資料來源 column: `真實:{來源}` for real sources, `推論:無來源` for reasoned ones. Personal data never goes in — keep the field and format, drop the value.
|
5. Completion condition: the chosen format has actually been produced, and you have reported where it landed (wiki page name, or the comment URL).
|
||||||
6. 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.
|
||||||
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.
|
|
||||||
|
|
||||||
## Rules
|
## Rules
|
||||||
|
|
||||||
- The work ticket is a mutex: always skip work packages that already hold a ticket; never take one over.
|
- The work ticket is a mutex: always skip work packages that already hold a ticket; never take one over.
|
||||||
- Never batch wiki updates across items; one item, one update.
|
- Never batch wiki updates across items; one item, one update.
|
||||||
- Sample data written into code or fixtures follows the analysis page's 資料來源 column: real data where a source exists, reasoned values labelled as inferred where none does. Never invent a value and present it as real, and never copy personal data into fixtures — keep the format, drop the identity.
|
- Sample data written into code or fixtures follows the analysis page's 資料來源 column, under the rules in `references/deliver-formats.md`.
|
||||||
- Everything the skill writes out (wiki content, commit messages, PR descriptions) stays Traditional Chinese per the STE100 rule.
|
- Everything the skill writes out (wiki content, commit messages, PR descriptions) stays Traditional Chinese per the STE100 rule.
|
||||||
|
|||||||
+10
-13
@@ -10,22 +10,19 @@ All wiki reads and writes go through `jsc-gitea:wiki`.
|
|||||||
|
|
||||||
## Steps
|
## Steps
|
||||||
|
|
||||||
1. **Model gate and stage lock** (capability tags, enforced in code — never self-assessed):
|
1. **Model gate and stage lock** — run `jsc-cli/tools/model-tags.sh sync`, then `jsc-hooks/hooks/sdlc-gate.sh lock maintain`. This stage requires no specific capability tag; the gate passes as long as the script can determine the actual model id. Rules: `references/model-gate.md`. Completion condition: the script exited 0, and you have reported the stage, the required tag, the actual model id it read from the transcript, and the verdict.
|
||||||
1. Run `jsc-cli/tools/model-tags.sh sync` to refresh `$JSC_HOME/model-tags.tsv` from `jsc-cli/references/model-tags.md`.
|
2. Read `MAINTAIN_CONTENTS` via `jsc-gitea:wiki` and filter projects **still inside their maintenance window**: start date ≤ today, and (end date is NULL or ≥ today). Completion condition: you have listed every in-window project with its `{owner}/{repo}` and window dates, or reported that none is in window and stopped.
|
||||||
2. Run `jsc-hooks/hooks/sdlc-gate.sh lock maintain`. The script reads the **actual** model id from the transcript and checks it against this stage's required tags — maintenance requires no specific tag, so the gate passes as long as the actual model can be determined at all.
|
3. Every project **MUST run as a sub agent** with this flow. Completion condition: every project listed in step 2 has its sub agent finished, and each one ends in either a PR link or a recorded skip reason.
|
||||||
3. **Never claim a capability tag you have not verified with that script**, and never substitute your own judgement for its verdict. Non-zero exit = blocked: relay the script's message verbatim, stop the skill, do nothing else this turn. Do not run `unlock` to get past the gate; only the user may decide that.
|
1. Run `git fetch --prune origin`, then put the project on its maintenance branch and align it with `origin/{branch}`. Which branch that is, the remote-is-the-basis rule, the diverged case and the never-pull-never-reset rule all live in `references/branch.md`; never guess the branch name. Completion condition: the project's HEAD points at the same commit as `origin/{branch}`, or you have reported the gap and skipped this project.
|
||||||
4. **Report the gate result to the user every time — whether it passed or blocked.** State the stage, the required tags, the actual model id the script read from the transcript, and the verdict. A silent pass looks identical to a skipped check, and the whole point of moving this into code was that a claimed check cannot be trusted.
|
2. Propose **at least five** maintenance methods, then let the user pick per `jsc-ask:ask` rules — every option states its impact scope (which files it touches, whether it can break the build, how much review it costs). Candidates:
|
||||||
5. Exit 0 means the stage is locked. Run this gate only when the stage changes; inside the maintenance stage the lock already covers every project, so never re-gate between projects.
|
|
||||||
2. Read `MAINTAIN_CONTENTS` via `jsc-gitea:wiki` and filter projects **still inside their maintenance window**: start date ≤ today, and (end date is NULL or ≥ today).
|
|
||||||
3. Every project **MUST run as a sub agent** with this flow:
|
|
||||||
1. Run `git fetch --prune origin`, then switch the project to `develop`, falling back to `master`, and bring it up to `origin/{該分支}` — the remote is the basis, never the local branch. Local and remote diverged → report and skip that project; never `pull --rebase`, `reset` or discard anything on your own.
|
|
||||||
2. Propose **at least five** maintenance methods and apply the ones that fit the project, for example:
|
|
||||||
- dependency updates (reuse `jsc-pkg:pkg-update`)
|
- dependency updates (reuse `jsc-pkg:pkg-update`)
|
||||||
- security vulnerability scan and patching
|
- security vulnerability scan and patching
|
||||||
- dead code and stale comment cleanup
|
- dead code and stale comment cleanup
|
||||||
- test coverage reinforcement
|
- test coverage reinforcement
|
||||||
- docs and README synchronization
|
- docs and README synchronization
|
||||||
- build warning elimination
|
- build warning elimination
|
||||||
3. Commit the changes to a new branch per `jsc-git:commit`, push, then open a PR back to develop or master per `jsc-git:pr`.
|
|
||||||
4. Update the project's last-maintained field (the zh-TW column 「前次維護時間」) in `MAINTAIN_CONTENTS` to today.
|
Completion condition: the user has picked the methods to apply, and every picked method is either applied or reported with the reason it could not be.
|
||||||
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.
|
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.
|
||||||
|
|||||||
+9
-12
@@ -13,23 +13,20 @@ All wiki reads and writes go through `jsc-gitea:wiki`.
|
|||||||
|
|
||||||
## Steps
|
## Steps
|
||||||
|
|
||||||
1. **Model gate and stage lock** (capability tags, enforced in code — never self-assessed):
|
1. **Model gate and stage lock** — run `jsc-cli/tools/model-tags.sh sync`, then `jsc-hooks/hooks/sdlc-gate.sh lock plan`. This stage requires the `reasoning-max` capability tag. Rules: `references/model-gate.md`. Completion condition: the script exited 0, and you have reported the stage, the required tag, the actual model id it read from the transcript, and the verdict.
|
||||||
1. Run `jsc-cli/tools/model-tags.sh sync` to refresh `$JSC_HOME/model-tags.tsv` from `jsc-cli/references/model-tags.md`.
|
2. Read `PLAN_CONTENTS` via `jsc-gitea:wiki` and list the plans whose status is the literal 「未分析」 (not analyzed), with names and HASH. Completion condition: you have listed every 未分析 plan with its name and HASH, or reported that none exists.
|
||||||
2. Run `jsc-hooks/hooks/sdlc-gate.sh lock plan`. The script reads the **actual** model id from the transcript, compares it against this stage's required tags (`reasoning-max`), and locks the stage only when they match.
|
3. Let the user choose per `jsc-ask:ask` rules: **extend an existing plan** (list the not-analyzed plans as options) or **create a new plan**. State the impact scope on every option. Completion condition: the user has picked one option explicitly, and you have named the `PLAN_{HASH}` page this run writes to.
|
||||||
3. **Never claim a capability tag you have not verified with that script**, and never substitute your own judgement for its verdict. Non-zero exit = blocked: relay the script's message verbatim, stop the skill, do nothing else this turn. Do not run `unlock` to get past the gate; only the user may decide that.
|
|
||||||
4. **Report the gate result to the user every time — whether it passed or blocked.** State the stage, the required tags, the actual model id the script read from the transcript, and the verdict. A silent pass looks identical to a skipped check, and the whole point of moving this into code was that a claimed check cannot be trusted.
|
|
||||||
5. Exit 0 means the stage is locked. From now until the next stage's gate runs, the sdlc-gate hook blocks every prompt whose model stops satisfying the tags.
|
|
||||||
2. Read `PLAN_CONTENTS` via `jsc-gitea:wiki` and list the plans whose status is the literal 「未分析」 (not analyzed), with names and HASH.
|
|
||||||
3. Let the user choose per `jsc-ask:ask` rules: **extend an existing plan** (list the not-analyzed plans as options) or **create a new plan**. State the impact scope on every option.
|
|
||||||
4. **Keep questioning until consensus** — rules in `references/consensus.md`, which is the single authority for both planning and analysis. Cover all three items:
|
4. **Keep questioning until consensus** — rules in `references/consensus.md`, which is the single authority for both planning and analysis. Cover all three items:
|
||||||
- Goal: the problem to solve and the criteria for success.
|
- Goal: the problem to solve and the criteria for success.
|
||||||
- Scope: what is included, what is excluded, which repositories are involved.
|
- Scope: what is included, what is excluded, which repositories are involved.
|
||||||
- Feasibility: whether the system architecture and data sources support the goal.
|
- Feasibility: whether the system architecture and data sources support the goal.
|
||||||
|
|
||||||
**One round is never enough**: every answer must produce the next question, derived from what that answer just exposed. Consensus needs both conditions from `references/consensus.md` — no remaining unknown that would change the output, **and** the user's explicit confirmation of the summary you read back. Never fill a gap with your own assumption, and never move on to user stories while any item is still open.
|
**One round is never enough**: every answer must produce the next question, derived from what that answer just exposed. Never fill a gap with your own assumption, and never move on to user stories while any item is still open.
|
||||||
5. Turn the consensus into **user stories** (the zh-TW pattern 「身為⋯⋯我想要⋯⋯以便⋯⋯」), one per line.
|
|
||||||
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: all three items are settled under both conditions of `references/consensus.md` — no remaining unknown that would change the output, **and** the user's explicit confirmation of the summary you read back.
|
||||||
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 「未分析」.
|
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.
|
||||||
|
|
||||||
## Hard limits
|
## Hard limits
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
|
|
||||||
- 工作目錄:{關鍵檔案與結構摘要}
|
- 工作目錄:{關鍵檔案與結構摘要}
|
||||||
- 複用來源:[REPO_{HASH}]({REPO 頁絕對網址})(commit sha:`{sha}`)
|
- 複用來源:[REPO_{HASH}]({REPO 頁絕對網址})(commit sha:`{sha}`)
|
||||||
- 複用決策:{複用哪些方法/端點、為什麼;不複用的原因}
|
- 複用決策:{複用哪些方法、端點、為什麼;不複用的原因}
|
||||||
|
|
||||||
## 未決項
|
## 未決項
|
||||||
|
|
||||||
@@ -27,15 +27,15 @@
|
|||||||
|
|
||||||
## 工作分解結構(WBS)
|
## 工作分解結構(WBS)
|
||||||
|
|
||||||
`WP-01` 固定是交付/交接工作包,獨立成一包,不與實作合併;用到它規格的實作工作包相依於它。
|
`WP-01` 固定是交付、交接工作包,獨立成一包,不與實作合併;用到它規格的實作工作包相依於它。
|
||||||
交付型別於實作階段開工時確認(API 文件/由使用者輸入)。
|
交付型別於實作階段開工時確認(API 文件、由使用者輸入)。
|
||||||
資料來源欄記錄範例資料的出處:`真實:{來源}` 或 `推論:無來源`。
|
資料來源欄記錄範例資料的出處:`真實:{source}` 或 `推論:無來源`。
|
||||||
PR 欄記錄該工作包的 PR 連結與編號;PR 未合併前不得開始下一個工作包。
|
PR 欄記錄該工作包的 PR 連結與編號;PR 未合併前不得開始下一個工作包。
|
||||||
|
|
||||||
| 編號 | 工作包名稱 | 交付 | 交付型別 | 相依 | 工時(h) | 天數 | 資料來源 | 工作證 | PR | 狀態 |
|
| 編號 | 工作包名稱 | 交付 | 交付型別 | 相依 | 工時(h) | 天數 | 資料來源 | 工作證 | PR | 狀態 |
|
||||||
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
|
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
|
||||||
| WP-01 | {交付/交接:規格與介面定義} | 是 | API 文件 | - | {h} | {d} | 真實:{來源} | | | 未完成 |
|
| WP-01 | {交付、交接:規格與介面定義} | 是 | API 文件 | - | {h} | {d} | 真實:{source} | | | 未完成 |
|
||||||
| WP-02 | {實作類名稱} | 否 | - | WP-01 | {h} | {d} | 真實:{來源} | | | 未完成 |
|
| WP-02 | {實作類名稱} | 否 | - | WP-01 | {h} | {d} | 真實:{source} | | | 未完成 |
|
||||||
| WP-03 | {實作類名稱} | 否 | - | WP-01 | {h} | {d} | 推論:無來源 | | | 未完成 |
|
| WP-03 | {實作類名稱} | 否 | - | WP-01 | {h} | {d} | 推論:無來源 | | | 未完成 |
|
||||||
|
|
||||||
- 關鍵路徑:{WP-01 → WP-02 → ⋯⋯},總天數 {d}
|
- 關鍵路徑:{WP-01 → WP-02 → ⋯⋯},總天數 {d}
|
||||||
@@ -43,10 +43,10 @@ PR 欄記錄該工作包的 PR 連結與編號;PR 未合併前不得開始下
|
|||||||
|
|
||||||
## 待辦事項(TDD)
|
## 待辦事項(TDD)
|
||||||
|
|
||||||
### WP-01 {交付/交接工作包名稱}
|
### WP-01 {交付、交接工作包名稱}
|
||||||
|
|
||||||
- [ ] 盤點端點與參數:{對象}
|
- [ ] 盤點端點與參數:{對象}
|
||||||
- [ ] 產出規格與範例資料(標明真實/推論)
|
- [ ] 產出規格與範例資料(標明真實、推論)
|
||||||
- [ ] 與使用者確認交付內容並產出交付文件
|
- [ ] 與使用者確認交付內容並產出交付文件
|
||||||
|
|
||||||
### WP-02 {工作包名稱}
|
### WP-02 {工作包名稱}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# 交付目錄
|
# 交付目錄
|
||||||
|
|
||||||
<!-- 交付欄:是 = 交付/交接工作包(WP-01),接手者要據此動工;否 = 實作類工作包。 -->
|
<!-- 交付欄:是 = 交付、交接工作包(WP-01),接手者要據此動工;否 = 實作類工作包。 -->
|
||||||
|
|
||||||
| 計畫名稱 | 工作包 | 交付 | 交付型別 | 交付頁 | HASH | 存取庫 | 交付時間 |
|
| 計畫名稱 | 工作包 | 交付 | 交付型別 | 交付頁 | HASH | 存取庫 | 交付時間 |
|
||||||
| --- | --- | --- | --- | --- | --- | --- | --- |
|
| --- | --- | --- | --- | --- | --- | --- | --- |
|
||||||
|
|||||||
@@ -30,19 +30,19 @@
|
|||||||
- 說明:{這個端點做什麼}
|
- 說明:{這個端點做什麼}
|
||||||
|
|
||||||
標示規則:🆕 **新增**、⚠️ **變更**、❌ **移除**(含淘汰時程)、既有參數留空。
|
標示規則:🆕 **新增**、⚠️ **變更**、❌ **移除**(含淘汰時程)、既有參數留空。
|
||||||
資料來源:`真實:{來源}` 或 `推論:無來源`。範例只留格式,個資不留值。
|
資料來源:`真實:{source}` 或 `推論:無來源`。範例只留格式,個資不留值。
|
||||||
|
|
||||||
#### 輸入參數(全部)
|
#### 輸入參數(全部)
|
||||||
|
|
||||||
| 參數 | 位置 | 型別 | 必填 | 範例 | 資料來源 | 狀態 |
|
| 參數 | 位置 | 型別 | 必填 | 範例 | 資料來源 | 狀態 |
|
||||||
| --- | --- | --- | --- | --- | --- | --- |
|
| --- | --- | --- | --- | --- | --- | --- |
|
||||||
| {name} | path/query/header/body | {型別} | 是/否 | {值} | 真實:{來源} | 🆕 **新增** |
|
| {name} | path/query/header/body | {型別} | 是、否 | {值} | 真實:{source} | 🆕 **新增** |
|
||||||
|
|
||||||
#### 輸出參數(全部)
|
#### 輸出參數(全部)
|
||||||
|
|
||||||
| 參數 | 型別 | 說明 | 範例 | 資料來源 | 狀態 |
|
| 參數 | 型別 | 說明 | 範例 | 資料來源 | 狀態 |
|
||||||
| --- | --- | --- | --- | --- | --- |
|
| --- | --- | --- | --- | --- | --- |
|
||||||
| {name} | {型別} | {說明} | {值} | 真實:{來源} | |
|
| {name} | {型別} | {說明} | {值} | 真實:{source} | |
|
||||||
|
|
||||||
#### 錯誤回應
|
#### 錯誤回應
|
||||||
|
|
||||||
@@ -76,7 +76,7 @@
|
|||||||
|
|
||||||
| 項目 | 指令或步驟 | 結果 |
|
| 項目 | 指令或步驟 | 結果 |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| {測試} | `{指令}` | {通過/失敗} |
|
| {測試} | `{指令}` | {通過、失敗} |
|
||||||
|
|
||||||
## 已知限制
|
## 已知限制
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
# 異常目錄
|
|
||||||
|
|
||||||
> 由失敗回報流程維護。新異常附加在文末,查問題時先看最新一筆。
|
|
||||||
|
|
||||||
## 異常清單
|
|
||||||
|
|
||||||
| 時間 | 頁名 | 存取庫名稱 | 觸發流程 | 退出碼 | 摘要 |
|
|
||||||
| --- | --- | --- | --- | --- | --- |
|
|
||||||
| {yyyy-MM-dd HH:mm:ss} | [[{error title}|ERROR_{HASH}]] | {owner}/{repo} | {hook_name} | {exit_code} | {error_summary} |
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
# 異常紀錄:{error title}
|
|
||||||
|
|
||||||
> 由失敗回報流程建立。這是一筆單次異常紀錄,不覆寫舊內容;同類失敗再發生時,請另開新頁。
|
|
||||||
|
|
||||||
- 頁名:`ERROR_{HASH}`
|
|
||||||
- HASH:`{HASH}`
|
|
||||||
- 發生時間:{yyyy-MM-dd HH:mm:ss}
|
|
||||||
- 存取庫名稱:{owner}/{repo}
|
|
||||||
- CLI:{cli}
|
|
||||||
- Session:{session_id}
|
|
||||||
- 觸發流程:{hook_name}
|
|
||||||
- 退出碼:{exit_code}
|
|
||||||
- 錯誤摘要:{error_summary}
|
|
||||||
|
|
||||||
## 現象
|
|
||||||
|
|
||||||
- {現象描述}
|
|
||||||
|
|
||||||
## 可能原因
|
|
||||||
|
|
||||||
- {可能原因}
|
|
||||||
|
|
||||||
## 處理結果
|
|
||||||
|
|
||||||
- {處理方式}
|
|
||||||
|
|
||||||
## 相關資訊
|
|
||||||
|
|
||||||
| 欄位 | 內容 |
|
|
||||||
| --- | --- |
|
|
||||||
| 訊息來源 | {stdin / env / command} |
|
|
||||||
| 相關輸出 | {stdout / stderr 摘要} |
|
|
||||||
| 關聯 ticket | `TICKET_{yyyyMMdd}_{HHmmss}_{HASH}` |
|
|
||||||
@@ -7,6 +7,6 @@
|
|||||||
|
|
||||||
## 功能與端點
|
## 功能與端點
|
||||||
|
|
||||||
| 檔案路徑 | 方法/端點名稱 | 類型 | 邏輯摘要 |
|
| 檔案路徑 | 方法、端點名稱 | 類型 | 邏輯摘要 |
|
||||||
| --- | --- | --- | --- |
|
| --- | --- | --- | --- |
|
||||||
| {path} | {method 或 HTTP 動詞加路由} | 方法 \| 端點 | {做什麼、輸入輸出概要} |
|
| {path} | {method 或 HTTP 動詞加路由} | 方法 \| 端點 | {做什麼、輸入輸出概要} |
|
||||||
|
|||||||
Executable
+225
@@ -0,0 +1,225 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
# wp-gate.sh — 工作包 PR 閘門:一個工作包的 PR 沒合併,就不准開下一包(供 jsc-sdlc:implement 呼叫)。
|
||||||
|
#
|
||||||
|
# 為什麼要有這支腳本:這條規則原本只寫在技能內文裡,靠模型自律遵守。內文靠不住——換一個
|
||||||
|
# 工作階段、換一個模型,或只是上下文被截掉,規則就跟著消失,而且沒有任何徵兆看得出來。
|
||||||
|
# 所以判定搬到程式層:未結清的狀態由 jsc-hooks 的 sdlc-gate.sh 記在 $JSC_HOME/wp/ 下(刻意
|
||||||
|
# 不綁 session,開新對話照樣擋),PR 的真實合併狀態則由本檔向 Gitea 查,查到合併才結清。
|
||||||
|
#
|
||||||
|
# 分工:hook 那半(wp-check)只注入提醒並擋掉別的階段技能,它不打網路;本檔是唯一會去問
|
||||||
|
# Gitea 的一方,也是唯一有權解鎖的一方。兩邊都能改狀態的話,就沒有人說得清鎖為什麼不見了。
|
||||||
|
#
|
||||||
|
# 用法:
|
||||||
|
# wp-gate.sh check {owner}/{repo} {index} [--since {ISO 時間}]
|
||||||
|
# 查一支 PR。已合併就解鎖並放行;沒合併就把留言全部印出來並擋住。
|
||||||
|
# --since 只印比該時間更新的留言,值用上一輪印出的 latest=(UTC,形如 2026-08-25T10:19:59Z)。
|
||||||
|
# wp-gate.sh lock {owner}/{repo} {index}
|
||||||
|
# PR 開好之後上鎖,讓閘門跨工作階段有效。轉呼叫 sdlc-gate.sh wp-lock。
|
||||||
|
#
|
||||||
|
# 輸出: 第一行固定為 `status=...`(供程式判讀),其後為人類可讀的繁中說明。
|
||||||
|
# status=merged 已合併,鎖已解除,可以挑下一個工作包
|
||||||
|
# status=open PR 還開著,沒有合併
|
||||||
|
# status=closed-unmerged PR 被關掉但沒有合併——這不算完成
|
||||||
|
# status=locked 已記下這筆未結清的 PR
|
||||||
|
# status=usage 用法錯誤
|
||||||
|
# status=missing-dep 相依腳本找不到,或這支 PR 查不到
|
||||||
|
# check 未合併時,最後一行固定為 `latest={最新一筆留言的時間戳}`(一筆留言都沒有就是 `latest=`),
|
||||||
|
# 供呼叫端寫回分析頁,下一輪拿它當 --since,已處理過的留言就不會再處理一遍。
|
||||||
|
#
|
||||||
|
# 結束碼: 0=已合併或無阻擋 1=未合併(擋住,呼叫端必須停下來逐筆修留言)
|
||||||
|
# 2=用法錯誤 3=相依工具或 PR 查不到
|
||||||
|
#
|
||||||
|
# 陷阱:
|
||||||
|
# - 「state=closed 但 merged=false」最容易被當成完成:那是 PR 被關掉、程式碼沒進去,
|
||||||
|
# 所以單獨回報 closed-unmerged,結束碼一樣是 1。只看 state 會放行一包沒交出去的工作。
|
||||||
|
# - 查不到就擋,絕不安靜放行。相依腳本缺一支、或 PR 查不到,一律 exit 3 並講明缺哪一支:
|
||||||
|
# 閘門查不到卻放行,等於沒有閘門,而且比沒有更糟——大家以為有人在看。
|
||||||
|
# - 留言讀不到(例如網路中斷)時仍然擋(exit 1),不改判成通過。
|
||||||
|
# - --since 是字串比較,不做時區換算。Gitea 印的是 UTC 的 ...Z,字典順序等於時間順序;
|
||||||
|
# 餵進帶 +08:00 這類偏移的值會比錯,所以只餵上一輪的 latest=。
|
||||||
|
# - latest= 取的是「全部留言」裡最新的一筆,不是過濾後那幾筆。取過濾後的會在沒有新留言時
|
||||||
|
# 倒退回舊時間戳,下一輪又把處理過的留言全部翻出來。
|
||||||
|
# - 已合併時會呼叫 sdlc-gate.sh wp-unlock 解鎖;解鎖失敗照樣回報 merged 並 exit 0,但會多印
|
||||||
|
# 一行警示與手動指令——鎖沒清掉,hook 會一直提醒下去。
|
||||||
|
set -u
|
||||||
|
|
||||||
|
script_dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
|
||||||
|
plugin_root="${CLAUDE_PLUGIN_ROOT:-$script_dir/..}"
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
cat >&2 <<'EOF'
|
||||||
|
用法:
|
||||||
|
wp-gate.sh check {owner}/{repo} {index} [--since {ISO 時間}] 查 PR:合併就解鎖放行,沒合併就印出留言並擋住
|
||||||
|
wp-gate.sh lock {owner}/{repo} {index} 開完 PR 後上鎖,讓閘門跨工作階段有效
|
||||||
|
結束碼: 0=已合併或無阻擋 1=未合併(擋住) 2=用法錯誤 3=相依工具或 PR 查不到
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
# 找相依腳本。兩種版面都要顧到,否則腳本只在其中一種版面下會動:
|
||||||
|
# 並排存取庫(開發用):{workspace}/sdlc 旁邊就是 {workspace}/gitea、{workspace}/hooks
|
||||||
|
# 已安裝 plugin:每個 plugin 各有版本目錄,取排序最後的一份(通常即最新版)
|
||||||
|
# 找不到不回傳路徑,由呼叫端 exit 3;不預設一個猜的路徑,猜錯會變成安靜放行。
|
||||||
|
resolve_dep() { # $1=並排目錄名(gitea、hooks) $2=plugin 內的相對路徑(tools/gitea.sh)
|
||||||
|
_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; }
|
||||||
|
_c=$(command -v "$(basename "$_rel")" 2>/dev/null || true)
|
||||||
|
[ -n "$_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
|
||||||
|
}
|
||||||
|
|
||||||
|
missing_dep() { # $1=缺哪一支的說明
|
||||||
|
echo 'status=missing-dep'
|
||||||
|
{
|
||||||
|
echo "[jsc][工作包閘門][ERR]:$1"
|
||||||
|
echo '本次不判定,也不放行。閘門查不到卻放行等於沒有閘門,請先修好相依關係再重跑。'
|
||||||
|
} >&2
|
||||||
|
exit 3
|
||||||
|
}
|
||||||
|
|
||||||
|
# {owner}/{repo} 格式檢查:剛好一層斜線,前後都不得為空。
|
||||||
|
valid_repo() { # $1=參數
|
||||||
|
case "${1:-}" in
|
||||||
|
*/*/*|/*|*/) return 1 ;;
|
||||||
|
*/*) return 0 ;;
|
||||||
|
*) return 1 ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
valid_index() { # $1=參數
|
||||||
|
case "${1:-}" in
|
||||||
|
''|*[!0-9]*) return 1 ;;
|
||||||
|
*) return 0 ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
sub="${1:-}"
|
||||||
|
[ -n "$sub" ] || { echo 'status=usage'; usage; exit 2; }
|
||||||
|
shift
|
||||||
|
|
||||||
|
case "$sub" in
|
||||||
|
check)
|
||||||
|
repo="${1:-}"; index="${2:-}"
|
||||||
|
valid_repo "$repo" || { echo 'status=usage'; usage; exit 2; }
|
||||||
|
valid_index "$index" || { echo 'status=usage'; usage; exit 2; }
|
||||||
|
shift 2
|
||||||
|
since=''
|
||||||
|
while [ "$#" -gt 0 ]; do
|
||||||
|
case "$1" in
|
||||||
|
--since)
|
||||||
|
since="${2:-}"
|
||||||
|
[ -n "$since" ] || { echo 'status=usage'; usage; exit 2; }
|
||||||
|
shift 2 ;;
|
||||||
|
--since=*) since=${1#--since=}
|
||||||
|
[ -n "$since" ] || { echo 'status=usage'; usage; exit 2; }
|
||||||
|
shift ;;
|
||||||
|
*) echo 'status=usage'; usage; exit 2 ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
gitea=$(gitea_sh) || missing_dep "找不到 jsc-gitea 的 tools/gitea.sh。並排版面請確認 {workspace}/gitea 存在,已安裝版面請確認 jsc-gitea plugin 已安裝,或設定 JSC_GITEA_TOOLS 指向它的 tools 目錄。"
|
||||||
|
|
||||||
|
if ! st=$(sh "$gitea" pr-status "$repo" "$index" 2>/dev/null) || [ -z "$st" ]; then
|
||||||
|
missing_dep "查不到 PR $repo 第 $index 號的狀態。請確認存取庫、PR 編號、GITEA_HOST 與 GITEA_TOKEN 都對。"
|
||||||
|
fi
|
||||||
|
state=$(printf '%s\n' "$st" | awk '{print $1}')
|
||||||
|
merged=$(printf '%s\n' "$st" | awk '{print $2}')
|
||||||
|
# PR 不存在時 gitea.sh 印的是「? none none」並照樣 exit 0。認不得的欄位一律當成查不到,
|
||||||
|
# 不當成未合併也不當成合併:把打錯的 PR 編號當成「未合併」會讓呼叫端一直修不存在的留言。
|
||||||
|
case "$state:$merged" in
|
||||||
|
open:true|open:false|closed:true|closed:false) ;;
|
||||||
|
*) missing_dep "查不到 PR $repo 第 $index 號(pr-status 回「$st」)。請確認存取庫、PR 編號、GITEA_HOST 與 GITEA_TOKEN 都對。" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [ "$merged" = 'true' ]; then
|
||||||
|
echo 'status=merged'
|
||||||
|
echo "$repo 第 $index 號 PR 已合併,這一包結清了,可以挑下一個工作包。"
|
||||||
|
gate=$(sdlc_gate_sh) || missing_dep "找不到 jsc-hooks 的 hooks/sdlc-gate.sh,鎖解不掉。並排版面請確認 {workspace}/hooks 存在,或設定 JSC_HOOKS_DIR 指向它的 hooks 目錄。"
|
||||||
|
# stdin 一定要關掉:sdlc-gate.sh 的 hook 模式會讀標準輸入,管線沒人關閉時整支卡死。
|
||||||
|
if sh "$gate" wp-unlock "$repo" >/dev/null 2>&1 </dev/null; then
|
||||||
|
echo "鎖已解除($repo)。"
|
||||||
|
else
|
||||||
|
echo "[jsc][工作包閘門][WARN]:鎖解不掉($repo)。PR 確實已合併,但狀態檔還在,hook 會一直提醒。請手動執行:sdlc-gate.sh wp-unlock $repo" >&2
|
||||||
|
fi
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$state" = 'closed' ]; then
|
||||||
|
echo 'status=closed-unmerged'
|
||||||
|
echo "$repo 第 $index 號 PR 被關掉了,但沒有合併——程式碼沒進到來源分支,這不算完成。"
|
||||||
|
echo '要嘛重開這支 PR 並把留言修完,要嘛請使用者裁決;在那之前不得挑下一個工作包。'
|
||||||
|
else
|
||||||
|
echo 'status=open'
|
||||||
|
echo "$repo 第 $index 號 PR 還開著,沒有合併。這一包還沒結清,不得挑下一個工作包。"
|
||||||
|
fi
|
||||||
|
echo '下列每一筆留言都要有結果(已修、不需修、修不動)。修不動或純討論、讚美的留言直接忽略:'
|
||||||
|
echo '不在 PR 上回覆、也不因此停下流程,但要在最終回報裡逐筆列出被忽略的留言與理由。'
|
||||||
|
if [ -n "$since" ]; then
|
||||||
|
echo "留言(只列比 $since 更新的;更早的已經處理過):"
|
||||||
|
else
|
||||||
|
echo '留言(全部,沒有給 --since):'
|
||||||
|
fi
|
||||||
|
|
||||||
|
tmp=$(mktemp) || missing_dep '建不出暫存檔,無法讀留言。'
|
||||||
|
if ! sh "$gitea" pr-comments "$repo" "$index" >"$tmp" 2>/dev/null; then
|
||||||
|
rm -f "$tmp"
|
||||||
|
echo "[jsc][工作包閘門][WARN]:留言讀不到($repo 第 $index 號)。PR 沒合併這件事不變,照樣擋住;請自行到 PR 頁面確認留言。" >&2
|
||||||
|
echo 'latest='
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
awk -F'\t' -v since="$since" '
|
||||||
|
NF == 0 { next }
|
||||||
|
{ if ($1 > latest) latest = $1 }
|
||||||
|
since == "" || $1 > since { print }
|
||||||
|
END { print "latest=" latest }
|
||||||
|
' "$tmp"
|
||||||
|
rm -f "$tmp"
|
||||||
|
exit 1 ;;
|
||||||
|
|
||||||
|
lock)
|
||||||
|
repo="${1:-}"; index="${2:-}"
|
||||||
|
valid_repo "$repo" || { echo 'status=usage'; usage; exit 2; }
|
||||||
|
valid_index "$index" || { echo 'status=usage'; usage; exit 2; }
|
||||||
|
[ "$#" -le 2 ] || { echo 'status=usage'; usage; exit 2; }
|
||||||
|
|
||||||
|
gate=$(sdlc_gate_sh) || missing_dep "找不到 jsc-hooks 的 hooks/sdlc-gate.sh,鎖上不了。並排版面請確認 {workspace}/hooks 存在,已安裝版面請確認 jsc-hooks plugin 已安裝,或設定 JSC_HOOKS_DIR 指向它的 hooks 目錄。"
|
||||||
|
# stdin 一定要關掉,理由同 check 分支的 wp-unlock 呼叫。
|
||||||
|
if ! out=$(sh "$gate" wp-lock "$repo" "$index" 2>&1 </dev/null); then
|
||||||
|
echo 'status=missing-dep'
|
||||||
|
{
|
||||||
|
echo "[jsc][工作包閘門][ERR]:鎖上不了($repo 第 $index 號)。"
|
||||||
|
[ -n "$out" ] && printf '%s\n' "$out"
|
||||||
|
echo '沒記下等於沒鎖,閘門在下一個工作階段就會放行。請修好之後重跑。'
|
||||||
|
} >&2
|
||||||
|
exit 3
|
||||||
|
fi
|
||||||
|
echo 'status=locked'
|
||||||
|
echo "$repo 第 $index 號 PR 已記為未結清。合併之後跑 wp-gate.sh check 才會解鎖。"
|
||||||
|
exit 0 ;;
|
||||||
|
|
||||||
|
*)
|
||||||
|
echo 'status=usage'
|
||||||
|
echo "[jsc][工作包閘門][ERR]:不認得子命令「$sub」。" >&2
|
||||||
|
usage
|
||||||
|
exit 2 ;;
|
||||||
|
esac
|
||||||
Reference in New Issue
Block a user