概要
tests/unit/test-pr-creation-log.sh の 6 箇所(L53 / L54 / L73 / L74 / L93 / L94)が、#161 と同じ「jq -e は最後の出力で終了コードを決める」仕様に依存している。
現状は matcher: "Bash" を持つ PostToolUse エントリがキット内に 1 つしかないため誤判定しないが、2 つ目が追加された時点で発火する。否定形のアサーションは偽 PASS(退行を検出できない)になるため、テストが守っているつもりの性質が黙って守られなくなる。
該当箇所
# L53-54(肯定形)
jq -e '.hooks.PostToolUse[] | select(.matcher == "Bash") | .hooks[0].if == "Bash(gh pr create *)"'
jq -e '.hooks.PostToolUse[] | select(.matcher == "Bash") | .hooks[0].async == true'
# L93-94(否定形・legacy fallback の検査)
jq -e '.hooks.PostToolUse[] | select(.matcher == "Bash") | .hooks[0] | has("if") | not'
jq -e '.hooks.PostToolUse[] | select(.matcher == "Bash") | .hooks[0] | has("async") | not'
L73-74 は L53-54 と同型。
select は一致したエントリの数だけ出力を生成するため、matcher: "Bash" のエントリが 2 つ以上あると jq -e は最後の 1 件だけで判定する。
再現
matcher: "Bash" の PostToolUse が 2 つあり、かつ 1 件目(pr-creation-log)が legacy 形に退行している状態を模したフィクスチャ。
{"hooks":{"PostToolUse":[
{"matcher":"Bash","hooks":[{"type":"command","command":"log-pr.sh","if":"Bash(gh pr create *)","async":true}]},
{"matcher":"Bash","hooks":[{"type":"command","command":"future-hook.sh"}]}
]}}
実測(jq-1.7.1-apple):
| フィルタ |
rc |
意味 |
| 現行の否定形(L93) |
0 |
退行を見逃す偽 PASS |
[...] | any(has("if") | not) |
0 |
直っていない |
[...] | all(has("if") | not) |
1 |
正しく検出 |
| 現行の肯定形(L53) |
1 |
正しい状態なのに偽 FAIL |
否定形は any() では直らない。 #161 で採用した配列集約 + any() の型をそのまま当てると、L93/94 は偽 PASS のまま残る。
発火条件
_FEATURE_ORDER(lib/features.sh)に matcher: "Bash" の PostToolUse を持つ feature が追加されると成立する。CLAUDE.md の「Adding a New Feature」は新しい hook を既存の後ろに追記するよう案内しており、追記されたエントリは pr-creation-log より後ろに来る。これは L93/94 が偽 PASS になる並びそのもの。
現時点で PostToolUse を持つのは prettier-hooks / biome-hooks(Edit|Write)、doc-size-guard(Write)、pr-creation-log(Bash)で、Bash は 1 つだけ。modern / legacy いずれの経路でも _versioned_hooks_fragment が hooks.json か hooks.legacy.json のどちらか一方しか返さないため、二重にはならない。
提案する修正
意図は「pr-creation-log のエントリがどうなっているか」なので、matcher ではなくコマンドで対象を選ぶのが正確。
jq -e 'any(.hooks.PostToolUse[]?.hooks[]?;
(.command? // "") | contains("pr-creation-log/log-pr.sh"))'
そのうえで、
- 肯定形は対象エントリを 1 件に絞ったうえで属性を検査する
- 否定形は
all(...) で畳み込む(any() では直らない)
あわせて、退行を検出できることを担保する回帰テストを追加したい。合成した 2 件目の matcher: "Bash" エントリを足したフィクスチャに対し、否定形の検査が失敗することを確認する形。現行フィルタでは通ってしまい、修正後は落ちる。
補足
#161(lib/update.sh の _check_auto_update_health)と同じクラスだが、本番コード側の掃討では lib/update.sh:2101 と :2104 の 2 箇所のみが該当し、それらは PR #164 で修正済み。本 issue は tests/ 側に残っている同クラスの箇所。
環境
- starter kit: v0.76.0(
554e455)
- jq: jq-1.7.1-apple
概要
tests/unit/test-pr-creation-log.shの 6 箇所(L53 / L54 / L73 / L74 / L93 / L94)が、#161 と同じ「jq -eは最後の出力で終了コードを決める」仕様に依存している。現状は
matcher: "Bash"を持つPostToolUseエントリがキット内に 1 つしかないため誤判定しないが、2 つ目が追加された時点で発火する。否定形のアサーションは偽 PASS(退行を検出できない)になるため、テストが守っているつもりの性質が黙って守られなくなる。該当箇所
L73-74 は L53-54 と同型。
selectは一致したエントリの数だけ出力を生成するため、matcher: "Bash"のエントリが 2 つ以上あるとjq -eは最後の 1 件だけで判定する。再現
matcher: "Bash"のPostToolUseが 2 つあり、かつ 1 件目(pr-creation-log)が legacy 形に退行している状態を模したフィクスチャ。{"hooks":{"PostToolUse":[ {"matcher":"Bash","hooks":[{"type":"command","command":"log-pr.sh","if":"Bash(gh pr create *)","async":true}]}, {"matcher":"Bash","hooks":[{"type":"command","command":"future-hook.sh"}]} ]}}実測(jq-1.7.1-apple):
[...] | any(has("if") | not)[...] | all(has("if") | not)否定形は
any()では直らない。 #161 で採用した配列集約 +any()の型をそのまま当てると、L93/94 は偽 PASS のまま残る。発火条件
_FEATURE_ORDER(lib/features.sh)にmatcher: "Bash"のPostToolUseを持つ feature が追加されると成立する。CLAUDE.md の「Adding a New Feature」は新しい hook を既存の後ろに追記するよう案内しており、追記されたエントリはpr-creation-logより後ろに来る。これは L93/94 が偽 PASS になる並びそのもの。現時点で
PostToolUseを持つのはprettier-hooks/biome-hooks(Edit|Write)、doc-size-guard(Write)、pr-creation-log(Bash)で、Bashは 1 つだけ。modern / legacy いずれの経路でも_versioned_hooks_fragmentがhooks.jsonかhooks.legacy.jsonのどちらか一方しか返さないため、二重にはならない。提案する修正
意図は「pr-creation-log のエントリがどうなっているか」なので、
matcherではなくコマンドで対象を選ぶのが正確。そのうえで、
all(...)で畳み込む(any()では直らない)あわせて、退行を検出できることを担保する回帰テストを追加したい。合成した 2 件目の
matcher: "Bash"エントリを足したフィクスチャに対し、否定形の検査が失敗することを確認する形。現行フィルタでは通ってしまい、修正後は落ちる。補足
#161(
lib/update.shの_check_auto_update_health)と同じクラスだが、本番コード側の掃討ではlib/update.sh:2101と:2104の 2 箇所のみが該当し、それらは PR #164 で修正済み。本 issue は tests/ 側に残っている同クラスの箇所。環境
554e455)