From ec23f6ee12dbca49810f781373c8440d28b1ccbe Mon Sep 17 00:00:00 2001 From: Jeffery Date: Tue, 25 Aug 2026 18:59:37 +0800 Subject: [PATCH 1/3] =?UTF-8?q?feat(implement):=20=E6=9C=AA=E5=90=88?= =?UTF-8?q?=E4=BD=B5=20PR=20=E6=94=B9=E7=82=BA=E7=A8=8B=E5=BC=8F=E5=B1=A4?= =?UTF-8?q?=E7=A1=AC=E9=96=98=E9=96=80=EF=BC=8C=E7=95=99=E8=A8=80=E8=87=AA?= =?UTF-8?q?=E5=8B=95=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- skills/implement/SKILL.md | 16 ++- tools/wp-gate.sh | 225 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 237 insertions(+), 4 deletions(-) create mode 100755 tools/wp-gate.sh diff --git a/skills/implement/SKILL.md b/skills/implement/SKILL.md index 0643df3..ef53f68 100644 --- a/skills/implement/SKILL.md +++ b/skills/implement/SKILL.md @@ -1,6 +1,6 @@ --- name: implement -description: SDLC implementation stage. Gate on capability tags enforced in code by sdlc-gate (implement requires coding), then confirm the analysis page's source branch - it is both the worktree base and the PR target. 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 and closing with jsc-review code-review. One package, one PR back to the source branch, then stop until it merges, and finish with the chosen delivery document plus an optional MAINTAIN_CONTENTS entry. Use when analysis is done and code must be written; not for planning or analysis. +description: SDLC implementation stage. Gate on capability tags enforced in code by sdlc-gate (implement requires coding), then confirm the analysis page's source branch - it is both the worktree base and the PR target. An unmerged work package PR is a hard gate enforced in code by tools/wp-gate.sh: it reads every PR comment, sub agents fix them in the original worktree, and no next package starts until that PR merges. Claim a ready work package from ANALYZE_CONTENTS with a work ticket, confirm a delivery package's content type, then complete its TDD todos one at a time inside a worktree built from origin/{source-branch}, updating the wiki after every item, closing with jsc-review code-review, one PR back to the source branch, the chosen delivery document and an optional MAINTAIN_CONTENTS entry. Use when analysis is done and code must be written; not for planning or analysis. --- # implement @@ -17,9 +17,16 @@ All wiki reads and writes go through `jsc-gitea:wiki`. 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. Completion condition: the user has confirmed the source branch explicitly, and it is recorded on the analysis page next to the work ticket. 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. -4. **Settle the previous work package's PR before picking anything.** Read the analysis page's PR column; for every work package holding a PR that is not marked merged, run `jsc-gitea/tools/gitea.sh pr-status {owner}/{repo} {index}`, and when it is not merged run `jsc-gitea/tools/gitea.sh pr-comments {owner}/{repo} {index}` in the same breath, relay the comments verbatim, and ask per `jsc-ask:ask` rules whether to fix or wait. Fixes go back into the original worktree and push to the same work branch; never open a second PR for the same package. A PR that is closed but not merged does not count as done: report it and ask the same way. A merged PR clears the block: remove that package's worktree (`references/branch.md`) and mark the package done on the analysis page. Completion condition: no work package holds an unmerged PR; stop here otherwise. +4. **Settle the previous work package's PR before picking anything — the gate lives in code, not in this text**: + 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. + 2. Exit 0 (`status=merged`) clears that package: remove its worktree (`references/branch.md`) and mark the package done on the analysis page. + 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. + 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. + 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`. + 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. + 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**. 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. Completion condition: the ticket is saved on the wiki; only then 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. **A delivery/handover package confirms its content before its first todo**: 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. Required fields, sample-data order and the new-versus-existing parameter marking: `references/deliver-formats.md`. @@ -33,7 +40,8 @@ All wiki reads and writes go through `jsc-gitea:wiki`. 11. **One work package finished → commit, push, PR back to the source branch. Then stop and wait for that PR**: 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. 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. **Do not start another work package.** Completion condition: the PR exists, its URL is saved on the analysis page and reported to the user, and the skill has stopped. + 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. **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. 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). 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`. diff --git a/tools/wp-gate.sh b/tools/wp-gate.sh new file mode 100755 index 0000000..26db6cf --- /dev/null +++ b/tools/wp-gate.sh @@ -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 &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 &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 -- 2.53.0 From f299d1706a2b36ef5408b07d1dc488855584397d Mon Sep 17 00:00:00 2001 From: Jeffery Date: Tue, 25 Aug 2026 18:59:37 +0800 Subject: [PATCH 2/3] =?UTF-8?q?docs(sdlc):=20=E8=A3=9C=E4=B8=8A=20wp-gate.?= =?UTF-8?q?sh=20=E5=B7=A5=E5=85=B7=E8=AA=AA=E6=98=8E=E8=88=87=E5=85=A9?= =?UTF-8?q?=E5=B1=A4=E9=96=98=E9=96=80=E5=88=86=E5=B7=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 10 ++++++++-- references/branch.md | 11 ++++++++++- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index fd684fd..55409c8 100644 --- a/README.md +++ b/README.md @@ -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。 +## 工具 + +| 腳本 | 用途 | +| --- | --- | +| `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 目錄 呼叫方式:Claude / Antigravity `/jsc-sdlc:{name}`;Codex `${name}`;Copilot / Kiro 描述需求自動觸發。 @@ -34,7 +40,7 @@ Marketplace 統一為 `jsc`(https://gitea.jsc.idv.tw/plugins/meta.git),安 ### `implement` -實作:先確認來源分支(同時是 PR 目標)→ 產生工作證鎖定「未完成、無相依、無工作證」的工作包(交付工作包優先)→ 交付工作包開工前先確認交付內容(API 文件、由使用者輸入,見 `references/deliver-formats.md`)→ **動程式碼前先從 `origin/{source-branch}` 建立 worktree**(`.worktree/{analysis-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` @@ -53,7 +59,7 @@ Marketplace 統一為 `jsc`(https://gitea.jsc.idv.tw/plugins/meta.git),安 | `templates/maintain-contents.md` | 維護目錄(截止日 NULL = 永久維護) | | `references/model-gate.md` | 模型閘門:執行順序、各階段必要標籤、阻擋與回報的鐵則 | | `references/tdd.md` | 接縫、紅綠循環規則、反模式 | -| `references/branch.md` | 分支規則:**一律以遠端 `origin/{branch}` 為準、動作前先 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/deliver-formats.md` | 交付內容型別:API 文件必備欄位、範例資料優先序、既有端點的新舊參數標示 | diff --git a/references/branch.md b/references/branch.md index 2bade1f..84d6112 100644 --- a/references/branch.md +++ b/references/branch.md @@ -118,7 +118,16 @@ SDLC 各階段引用的參考分支與來源分支,**一律指遠端的 `origi 一個工作包的 PR 還沒合併,就不得開始下一包。修正一律回原 worktree、推同一條工作分支,**不為同一包開第二個 PR**。 -查驗程序(查狀態、讀留言、詢問修正或等待、已關閉但未合併怎麼辦)由 `skills/implement/SKILL.md` 步驟 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 擁有,本檔不重複。 ## 不破壞既有工作 -- 2.53.0 From a52e47be80c71e25a3ed7ab5bab37d1eac1991ac Mon Sep 17 00:00:00 2001 From: Jeffery Date: Tue, 25 Aug 2026 18:59:37 +0800 Subject: [PATCH 3/3] =?UTF-8?q?chore(sdlc):=20=E4=B8=89=E4=BB=BD=20manifes?= =?UTF-8?q?t=20=E5=90=8C=E6=AD=A5=E5=8D=87=E7=89=88=E5=88=B0=200.1.5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .claude-plugin/plugin.json | 2 +- .codex-plugin/plugin.json | 2 +- plugin.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index 4a81976..69a4358 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "jsc-sdlc", - "version": "0.1.4", + "version": "0.1.5", "description": "開發生命週期:規劃、分析、實作、維護(wiki 追蹤)", "skills": "./skills", "author": { diff --git a/.codex-plugin/plugin.json b/.codex-plugin/plugin.json index b343ffa..9bc5f6f 100644 --- a/.codex-plugin/plugin.json +++ b/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "jsc-sdlc", - "version": "0.1.4", + "version": "0.1.5", "description": "開發生命週期:規劃、分析、實作、維護(wiki 追蹤)", "skills": "./skills" } diff --git a/plugin.json b/plugin.json index 98db40c..527067f 100644 --- a/plugin.json +++ b/plugin.json @@ -1,6 +1,6 @@ { "name": "jsc-sdlc", - "version": "0.1.4", + "version": "0.1.5", "description": "開發生命週期:規劃、分析、實作、維護(wiki 追蹤)", "skills": "./skills/" } -- 2.53.0