From fbf0aec9f7fe11ad7de2c5ce6fe6260fd4407bed Mon Sep 17 00:00:00 2001 From: Jeffery Date: Tue, 1 Sep 2026 11:48:13 +0800 Subject: [PATCH 1/2] =?UTF-8?q?fix(smoke):=20=E6=93=8B=E4=BA=BA=E6=96=B7?= =?UTF-8?q?=E8=A8=80=E6=94=B9=E4=BE=9D=E5=90=84=20CLI=20=E7=9A=84=E5=BD=A2?= =?UTF-8?q?=E6=85=8B=E5=88=A4=E5=AE=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit What: - wire-cli.sh 的 smoke 區段新增四個共用小函式:smoke_deny_rc 給結束碼、smoke_deny_mark 給擋人標記、smoke_deny_ok 做判定、smoke_deny_desc 產生失敗訊息。 - smoke_rs_case 與 smoke_vg_case 的判定改走 smoke_deny_ok,五個呼叫點的預期值由結束碼 2 改成字面值 deny。 - 三份 manifest 的版本一起提升,由 sync-skill-manifest.sh 同步。 Why: - 兩個函式把「擋下」寫死成結束碼 2,但擋下的形態是由 deny.sh 依 CLI 決定的。claude、codex、copilot 與認不得的代號走 stderr 加結束碼 2;antigravity 改印一行 stdout 的 deny JSON,kiro 只能注入警告,這兩支的結束碼都固定 0。 - 結果是這兩支的 smoke 各有五條判定失敗,回報成執行期錯誤。但擋人訊息其實都正確印出來了,配套的訊息斷言也全部通過,壞的只有結束碼那一項比對——是斷言認錯形態,不是 hook 失效。 - 不能改成一律放寬到 0。那兩支上放行也是 0,放寬之後「該擋沒擋」與「正確擋下」完全同形,這道斷言等於作廢。 How: - 形態表在 smoke 這側鏡射一份,事實來源仍是 deny.sh 的 case。表只有一份,改一支不會忘了另一支。 - 判定同時比結束碼與擋人標記;預期放行的案例反過來要求標記不得出現,所以「該擋沒擋」與「不該擋卻擋了」兩個方向都守得住。 - 走 stderr 的三支標記為空字串,判定行為與原本完全相同,不產生回歸。 - 斷言條數不增不減,兩個預期條數常數都不必動。 - 反向測試確認斷言仍然有效:拿掉 deny.sh 裡 antigravity 的 deny JSON 輸出,做出該擋卻靜靜放行的情境,smoke 正確判失敗。結束碼相同,靠擋人標記才分得出來。 Who: 接線後的冒煙測試在 antigravity 與 kiro 上判定失敗,追出來的是斷言本身的缺陷。 --- .claude-plugin/plugin.json | 2 +- .codex-plugin/plugin.json | 2 +- plugin.json | 2 +- tools/wire-cli.sh | 64 +++++++++++++++++++++++++++++++------- 4 files changed, 56 insertions(+), 14 deletions(-) diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index 44a13b2..24cdf6d 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "jsc-hooks", - "version": "0.3.4", + "version": "0.3.5", "description": "跨 CLI hooks:STE100 語言強制、工時計時、技能用量記錄、SDLC 模型鎖、版本前置檢查、註解範圍守門、繁中編碼守門、部署後強制重啟、寫入與提交閘門", "skills": "./skills", "author": { diff --git a/.codex-plugin/plugin.json b/.codex-plugin/plugin.json index 20b68ee..90a90b0 100644 --- a/.codex-plugin/plugin.json +++ b/.codex-plugin/plugin.json @@ -1,7 +1,7 @@ { "hooks": "./hooks/codex-hooks.json", "name": "jsc-hooks", - "version": "0.3.4", + "version": "0.3.5", "description": "跨 CLI hooks:STE100 語言強制、工時計時、技能用量記錄、SDLC 模型鎖、版本前置檢查、註解範圍守門、繁中編碼守門、部署後強制重啟、寫入與提交閘門", "skills": "./skills", "jsc": { diff --git a/plugin.json b/plugin.json index cebe865..d5b67ff 100644 --- a/plugin.json +++ b/plugin.json @@ -1,6 +1,6 @@ { "name": "jsc-hooks", - "version": "0.3.4", + "version": "0.3.5", "description": "跨 CLI hooks:STE100 語言強制、工時計時、技能用量記錄、SDLC 模型鎖、版本前置檢查、註解範圍守門、繁中編碼守門、部署後強制重啟、寫入與提交閘門", "skills": "./skills/", "jsc": { diff --git a/tools/wire-cli.sh b/tools/wire-cli.sh index 0fd2dde..14544cb 100755 --- a/tools/wire-cli.sh +++ b/tools/wire-cli.sh @@ -1506,16 +1506,57 @@ if [ "$action" = smoke ]; then # 舊格式的單一狀態檔照樣擋得下來。用暫時目錄是為了不動到使用者真正的 # $JSC_HOME/restart-required.d/——冒煙測試不該把別人的閘門拆掉。 # 一律 &1); _rc=$? smoke_n_rs=$((smoke_n_rs + 1)) - if [ "$_rc" -eq "$3" ]; then + if smoke_deny_ok "$3" "$_rc" "$_out"; then printf '[jsc] restart-gate.sh(%s):exit %s,與預期相同。\n' "$1" "$_rc" >> "$smoke_out" else smoke_fails=$((smoke_fails + 1)) printf '[jsc] restart-gate.sh(%s):exit %s,預期 %s,重啟閘門判定壞了:%s\n' \ - "$1" "$_rc" "$3" "$(printf '%s' "$_out" | tr '\n' ' ' | cut -c1-200)" >> "$smoke_out" + "$1" "$_rc" "$(smoke_deny_desc "$3")" \ + "$(printf '%s' "$_out" | tr '\n' ' ' | cut -c1-200)" >> "$smoke_out" fi } # 狀態檔在不在也要比:一支 CLI 一份的重點就在「該留的留、該刪的刪」,只看結束碼看不出來。 @@ -1540,7 +1581,7 @@ if [ "$action" = smoke ]; then JSC_HOME="$rs_home" JSC_CLI="$cli" \ sh "$HOOKS/restart-gate.sh" require update hooks cli /dev/null smoke_rs_file "require 寫出當前 CLI 那一份" "$rs_dir/$cli" exist - smoke_rs_case "當前 CLI 那份存在,技能 jsc-sdlc:implement" jsc-sdlc:implement 2 + smoke_rs_case "當前 CLI 那份存在,技能 jsc-sdlc:implement" jsc-sdlc:implement deny smoke_rs_case "當前 CLI 那份存在,豁免技能 jsc-cli:deploy" jsc-cli:deploy 0 smoke_rs_case "當前 CLI 那份存在,豁免技能 jsc-gitea:wiki" jsc-gitea:wiki 0 smoke_rs_case "當前 CLI 那份存在,豁免技能 jsc-log:worklog" jsc-log:worklog 0 @@ -1557,7 +1598,7 @@ if [ "$action" = smoke ]; then # 舊格式的單一狀態檔(過渡相容):沒有 per-CLI 資訊,所以一律擋,clear 一併刪掉。 printf 'at=%s\nmode=update\ndomains=hooks\ncli=%s\n' "$(now_iso)" "$rs_other" \ > "$rs_home/restart-required" 2>/dev/null - smoke_rs_case "舊格式單一狀態檔存在" jsc-sdlc:implement 2 + smoke_rs_case "舊格式單一狀態檔存在" jsc-sdlc:implement deny JSC_HOME="$rs_home" JSC_CLI="$cli" JSC_SESSION_ID=smoke-restart-legacy \ sh "$HOOKS/session-timer.sh" restart /dev/null smoke_rs_file "清除一併刪掉舊格式狀態檔" "$rs_home/restart-required" absent @@ -1658,7 +1699,7 @@ if [ "$action" = smoke ]; then # GITEA_HOST 一律清空:放行的案例會繼續往下走到遠端比對,站台推導得出來就會真的連網, # 冒煙不該依賴網路,也不該讓遠端落後把預期放行的案例判成擋下。 # 一律 &1); _rc=$? smoke_n_vg=$((smoke_n_vg + 1)) - if [ "$_rc" -eq "$3" ]; then + if smoke_deny_ok "$3" "$_rc" "$vg_out"; then printf '[jsc] version-guard.sh(%s):exit %s,與預期相同。\n' "$1" "$_rc" >> "$smoke_out" else smoke_fails=$((smoke_fails + 1)) printf '[jsc] version-guard.sh(%s):exit %s,預期 %s,相依版本判定壞了:%s\n' \ - "$1" "$_rc" "$3" "$(printf '%s' "$vg_out" | tr '\n' ' ' | cut -c1-200)" >> "$smoke_out" + "$1" "$_rc" "$(smoke_deny_desc "$3")" \ + "$(printf '%s' "$vg_out" | tr '\n' ' ' | cut -c1-200)" >> "$smoke_out" fi } # 訊息內容也要比:擋下來卻沒指名是哪一個相依落後,使用者看不出要更新哪一個 plugin, @@ -1710,7 +1752,7 @@ if [ "$action" = smoke ]; then # 一、相依確定落後:擋下,而且訊息要指名是哪一項、需要哪一版、目前哪一版。 printf '{"name": "jsc-sdlc", "version": "0.1.0", "jsc": {"requires": {"jsc-cli": ">=9.9.9"}}}\n' \ > "$vg_manifest" - smoke_vg_case "相依落後" jsc-sdlc:implement 2 + smoke_vg_case "相依落後" jsc-sdlc:implement deny smoke_vg_msg "相依落後時訊息指名那一項" "jsc-cli 需要 >=9.9.9,目前 0.1.0" # 二、相依相等或超前都放行:只擋落後,本機超前是開發技能組時的常態,擋了維護者自己動不了。 @@ -1724,7 +1766,7 @@ if [ "$action" = smoke ]; then # 三、豁免技能:這一條最要緊,它證明閘門不會把更新與修復的唯一路徑鎖死。同一個 jsc-cli # plugin 底下,非豁免的技能在相依落後時被擋、豁免的那一支照樣放行,差別只在豁免清單。 smoke_vg_case "豁免技能 jsc-cli:deploy,相依落後照樣放行" jsc-cli:deploy 0 - smoke_vg_case "同 plugin 的非豁免技能 jsc-cli:doctor" jsc-cli:doctor 2 + smoke_vg_case "同 plugin 的非豁免技能 jsc-cli:doctor" jsc-cli:doctor deny # 四、fail-open 四條:沒有證據不等於落後。五支 CLI 只有 claude 讀得到本機載入版本, # 這四條任何一條改成擋人,另外四支就會被整批鎖死。 @@ -1746,7 +1788,7 @@ if [ "$action" = smoke ]; then # 前面那一項則是滿足的:漏掉最後一筆就會變成放行,當場被這一條抓出來。 printf '{\n "name": "jsc-sdlc",\n "version": "0.1.0",\n "jsc": {\n "requires": {\n "jsc-hooks": ">=0.0.1",\n "jsc-cli": ">=9.9.9"\n }\n }\n}\n' \ > "$vg_manifest" - smoke_vg_case "多行縮排 manifest,落後的相依擺在最後一個鍵" jsc-sdlc:implement 2 + smoke_vg_case "多行縮排 manifest,落後的相依擺在最後一個鍵" jsc-sdlc:implement deny smoke_vg_msg "最後一個鍵的落後項也列進訊息" "jsc-cli 需要 >=9.9.9,目前 0.1.0" rm -rf "$vg_home" else -- 2.53.0 From 113df378083721162eda409e7ec2fee366cd6e46 Mon Sep 17 00:00:00 2001 From: Jeffery Date: Tue, 1 Sep 2026 12:11:10 +0800 Subject: [PATCH 2/2] =?UTF-8?q?fix(comment-scope):=20=E9=A0=81=E9=9D=A2?= =?UTF-8?q?=E7=B7=A8=E8=99=9F=E5=81=B5=E6=B8=AC=E8=A3=9C=E9=BD=8A=E4=B8=89?= =?UTF-8?q?=E7=A8=AE=E7=BC=BA=E6=BC=8F=E7=9A=84=E5=9E=8B=E5=88=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit What: - wiki 頁面編號的偵測樣式補上 SKILLSET、TOOLING 與 MONITOR 三種型別。 - 三份 manifest 的版本一起提升。 Why: - 這支 hook 的職責是擋住把文件追蹤資訊寫進程式碼註解,wiki 頁面編號正是禁止項之一。 - 樣式只列到 REPORT,但實際的頁型清單早就有 SKILLSET 與 TOOLING,現在再加 MONITOR。清單漏掉的那幾種,頁面編號寫進註解就攔不到,等於這條規則對它們不存在。 - SKILLSET 與 TOOLING 的缺漏是既有落差,不是這次新增型別才產生的,一併補齊比較省事,也不會留下第二個要記得的地方。 How: - 型別的排列順序照 gitea.sh 的 resolve_wiki_repo 走,兩邊一致才看得出有沒有漏。 - 規則正文的唯一來源仍是 jsc-review 的註解範圍文件,這裡只補偵測樣式,不重述規則清單。 - 實測過三種型別各自都攔得下來,命中的說明都是「jsc wiki 頁面編號」,不是旁邊那條前綴加流水號的樣式誤撿。 Who: 技能助理落地帶出來的頁型別需求,四個存放庫同一批改。 --- .claude-plugin/plugin.json | 2 +- .codex-plugin/plugin.json | 2 +- hooks/comment-scope.sh | 2 +- plugin.json | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index 24cdf6d..0b0e8e3 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "jsc-hooks", - "version": "0.3.5", + "version": "0.3.6", "description": "跨 CLI hooks:STE100 語言強制、工時計時、技能用量記錄、SDLC 模型鎖、版本前置檢查、註解範圍守門、繁中編碼守門、部署後強制重啟、寫入與提交閘門", "skills": "./skills", "author": { diff --git a/.codex-plugin/plugin.json b/.codex-plugin/plugin.json index 90a90b0..f7beb60 100644 --- a/.codex-plugin/plugin.json +++ b/.codex-plugin/plugin.json @@ -1,7 +1,7 @@ { "hooks": "./hooks/codex-hooks.json", "name": "jsc-hooks", - "version": "0.3.5", + "version": "0.3.6", "description": "跨 CLI hooks:STE100 語言強制、工時計時、技能用量記錄、SDLC 模型鎖、版本前置檢查、註解範圍守門、繁中編碼守門、部署後強制重啟、寫入與提交閘門", "skills": "./skills", "jsc": { diff --git a/hooks/comment-scope.sh b/hooks/comment-scope.sh index b0bce81..b426d56 100755 --- a/hooks/comment-scope.sh +++ b/hooks/comment-scope.sh @@ -81,7 +81,7 @@ scan_file() { # $1=檔案路徑;命中就把報告印到 stdout 並回傳 1, hit '(^|[^[:alnum:]_/])#[0-9]+' '議題編號(#123)' hit '(^|[^[:alnum:]_])![0-9]+' 'PR、MR 編號(!45)' hit '[A-Z]{2,6}-[0-9]{1,6}' '工作包、故事、驗收、測試案例、變更單、議題編號(前綴加流水號)' - hit '(QUESTION|PLAN|ANALYZE|DELIVER|MAINTAIN|REPO|LOG|LEARN|ERROR|CHECK|REPORT)_([A-Z0-9]{8}|CONTENTS)' 'jsc wiki 頁面編號' + hit '(QUESTION|PLAN|ANALYZE|DELIVER|MAINTAIN|REPO|LOG|LEARN|ERROR|CHECK|REPORT|SKILLSET|TOOLING|MONITOR)_([A-Z0-9]{8}|CONTENTS)' 'jsc wiki 頁面編號' hit '(todo|TODO|待辦)[[:space:]]*#?[0-9]+' 'TDD 待辦編號' hit '([Ss]print|里程碑|[Mm]ilestone)[[:space:]]*[0-9]+' 'Sprint、里程碑編號' hit '(^|[^[:alnum:].])v[0-9]+\.[0-9]+|版本[[:space:]]*v?[0-9]+\.[0-9]+' '版本號' diff --git a/plugin.json b/plugin.json index d5b67ff..7c40fa4 100644 --- a/plugin.json +++ b/plugin.json @@ -1,6 +1,6 @@ { "name": "jsc-hooks", - "version": "0.3.5", + "version": "0.3.6", "description": "跨 CLI hooks:STE100 語言強制、工時計時、技能用量記錄、SDLC 模型鎖、版本前置檢查、註解範圍守門、繁中編碼守門、部署後強制重啟、寫入與提交閘門", "skills": "./skills/", "jsc": { -- 2.53.0