@@ -1506,16 +1506,57 @@ if [ "$action" = smoke ]; then
# 舊格式的單一狀態檔照樣擋得下來。用暫時目錄是為了不動到使用者真正的
# $JSC_HOME/restart-required.d/——冒煙測試不該把別人的閘門拆掉。
# 一律 </dev/null: hook 模式會讀標準輸入,管線沒人關閉時整支卡死。
smoke_rs_case( ) { # $1=情境 $2=技能名 $3=預期結束碼 $4=JSC_RESTART_GATE 值(可省略)
#
# 「擋下」的形態依 CLI 而定,事實來源是 hooks/deny.sh 的 case: claude、codex、copilot 與認不得
# 的代號走 stderr 加結束碼 2; antigravity 改印一行 stdout 的 deny JSON, kiro 只能注入警告,
# 這兩支的結束碼都固定 0。所以預期擋下不能寫死 2。
# 也不能一律放寬成 0:那兩支上放行也是 0,放寬之後「該擋沒擋」與「正確擋下」完全同形,
# 這道斷言等於作廢。解法是結束碼與擋人標記一起比,而且預期放行的案例要反過來確認標記沒出現,
# 兩個方向都守得住。既有的 smoke_dn_case 與 smoke_cx_case 已經是這個做法,這裡沿用同一套。
smoke_deny_rc( ) { # 這支 CLI 擋下時的結束碼
case " $cli " in antigravity| kiro) printf 0 ; ; *) printf 2 ; ; esac
}
smoke_deny_mark( ) { # 結束碼分不出擋下與放行時,輸出必定出現的擋人標記;空字串代表結束碼已經夠用
case " $cli " in
antigravity) printf '"decision":"deny"' ; ;
kiro) printf 'kiro 擋不下技能叫用' ; ;
*) printf '' ; ;
esac
}
smoke_deny_ok( ) { # $1=預期(數字,或字面值 deny)$2=實際結束碼 $3=實際輸出;回 0 代表通過
_d_want = $1 ; _d_rc = $2 ; _d_out = $3
_d_mark = $( smoke_deny_mark)
if [ " $_d_want " = deny ] ; then
[ " $_d_rc " -eq " $( smoke_deny_rc) " ] || return 1
[ -n " $_d_mark " ] || return 0
printf '%s' " $_d_out " | grep -qF " $_d_mark "
return $?
fi
[ " $_d_rc " -eq " $_d_want " ] || return 1
[ -n " $_d_mark " ] || return 0
! printf '%s' " $_d_out " | grep -qF " $_d_mark "
}
smoke_deny_desc( ) { # $1=預期;把預期講成失敗訊息看得懂的形態
_d_mark = $( smoke_deny_mark)
if [ " $1 " = deny ] ; then
if [ -n " $_d_mark " ] ; then printf 'exit %s 且輸出含「%s」' " $( smoke_deny_rc) " " $_d_mark "
else printf 'exit %s' " $( smoke_deny_rc) " ; fi
else
if [ -n " $_d_mark " ] ; then printf 'exit %s 且輸出不含「%s」' " $1 " " $_d_mark "
else printf 'exit %s' " $1 " ; fi
fi
}
smoke_rs_case( ) { # $1=情境 $2=技能名 $3=預期(結束碼,或 deny) $4=JSC_RESTART_GATE 值(可省略)
_out = $( JSC_HOME = " $rs_home " JSC_CLI = " $cli " JSC_SKILL = " $2 " SKILL = " $2 " \
JSC_RESTART_GATE = " ${ 4 :- } " sh " $HOOKS /restart-gate.sh " </dev/null 2>& 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 2>/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 2>/dev/null
smoke_rs_file "清除一併刪掉舊格式狀態檔" " $rs_home /restart-required " absent
@@ -1658,7 +1699,7 @@ if [ "$action" = smoke ]; then
# GITEA_HOST 一律清空:放行的案例會繼續往下走到遠端比對,站台推導得出來就會真的連網,
# 冒煙不該依賴網路,也不該讓遠端落後把預期放行的案例判成擋下。
# 一律 </dev/null: hook 模式會讀標準輸入,管線沒人關閉時整支卡死。
smoke_vg_case( ) { # $1=情境 $2=技能名 $3=預期結束碼 $4=額外環境設定(KEY=值,可省略)
smoke_vg_case( ) { # $1=情境 $2=技能名 $3=預期( 結束碼,或 deny) $4=額外環境設定(KEY=值,可省略)
# 第四個參數省略時仍要餵一個合法的 KEY=值 給 env,否則它會把空字串當成要執行的指令。
# 這個名字沒有任何 hook 讀它,只是佔位。
_extra = " ${ 4 :- JSC_VERSION_GUARD_UNUSED =1 } "
@@ -1668,12 +1709,13 @@ if [ "$action" = smoke ]; then
JSC_TOOL_NAME = Skill JSC_SKILL = " $2 " SKILL = " $2 " " $_extra " \
sh " $HOOKS /version-guard.sh " </dev/null 2>& 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