# memory: instinct-if-not-cmd-swallows-exit-code-in-ci
CIの `if ! cmd; then flag=true; fi` は終了コードを飲むのでジョブが常にSUCCESS。緑は「実行した」の意味しか持たない。vvv-bots の `lint` チェックは常に SUCCESS だった。同じコミットに対しローカルで `ruff check` すると 4 errors。うち2件は `F821 Undefined name 'os'` で、Discord 通知経路が実行時 NameError で必ず落ちる状態のまま main に残っていた。
## ポイント
- CIの `if ! cmd; then flag=true; fi` は終了コードを飲むのでジョブが常にSUCCESS。
- vvv-bots の `lint` チェックは常に SUCCESS だった。
- 同じコミットに対しローカルで `ruff check` すると 4 errors。うち2件は `F821 Undefined name 'os'` で、Discord 通知経路が実行時 NameError で必ず落ちる状態のまま main に残っていた。
- ゲートの名前(`lint`)が主張する対象は「エラー0」だが、実際に判定しているのは「実行したか」。
- fail-open は赤を消すので、誰も気づかない。
- Free プランで branch protection の必須チェックが使えない環境では、人間が見るのは PR のチェック一覧の緑/赤だけ=そこが常緑だと検査が存在しないのと同じ。
- `if ! cmd`・`cmd || true`・`cmd 2>&1 || true` を見たらそのステップは落ちないと読む。
- 終了コードを後段へ渡したいなら `cmd; rc=$?` で保存し、最後に非0で終わる形にする。
- ゲートを本物にする変更は既存の赤を潰してから入れる。逆順だと全PRが即赤になる。
- 「lint pass / test FAILURE」のように片方だけ赤いときは、緑の側も疑う。
## 関連ページ
[[instinct-fail-open-predicate-needs-measured-blast-radius]] [[instinct-alarm-name-vs-metric-mismatch]] [[instinct-zero-duration-means-no-work-happened]]
## 関連概念(未作成)
`CIの終了コードの扱い` `fail-openのリスク` `branch protectionの重要性`
memory: instinct-if-not-cmd-swallows-exit-code-in-ci