演習中に止まったときに、画面に出ている文字から原因と直し方を引くページです。見出しを開くと、原因と直し方が出ます。
困ったときの引き方
画面に出ている文字から引いてください。左の列で症状を探し、右の列のカテゴリへ飛ぶと、その中に同じ見出しの項目があります。見出しをクリックすると原因と直し方が開きます。講師を呼ぶ前に直し方の1番だけ試していただけると、こちらで原因を絞る時間が減ります。
| 症状 | カテゴリ |
|---|---|
| Claude Code が起動しない、アプリの一覧に出てこない | 起動と実行環境 |
| アプリからプロジェクトのフォルダを開けない | 起動と実行環境 |
| フックが効いているのか画面で分からない | 起動と実行環境 |
| 確認が出ないまま編集が進んでしまう | 起動と実行環境 |
| パイプでつなぐ書き方が動かない | 起動と実行環境 |
| コマンドを打つ場所が見つからない | 起動と実行環境 |
| スクリプトの実行が無効と赤い文字で出る | 起動と実行環境 |
| フックの日本語が読めない並びになる | 起動と実行環境 |
| bash\r、bad interpreter と出る | 起動と実行環境 |
| Permission denied と出る、止まるはずが素通りする | 起動と実行環境 |
| 編集のたびに数秒待たされる | 起動と実行環境 |
| jq not found と出て判定が通ってしまう | 起動と実行環境 |
| 拒否の規則を足したのに実行される | 設定とフックの効き方 |
| CLAUDE.md に書いた禁止が守られない | 設定とフックの効き方 |
| フックも拒否も同時に効かなくなった | 設定とフックの効き方 |
| 隔離して起動するとフックが発火しない | 設定とフックの効き方 |
| effort やモデルが指定どおりにならない | 設定とフックの効き方 |
| AI の変更の一部だけを戻したい | 巻き戻しと隔離 |
| 自分の手直しと AI の変更が混ざった | 巻き戻しと隔離 |
| 巻き戻しても戻らないファイルがある | 巻き戻しと隔離 |
| Esc を2回押しても巻き戻しの一覧が開かない | 巻き戻しと隔離 |
| 隔離した作業の始め方が分からない | 巻き戻しと隔離 |
| 隔離すると .env が入っていない | 巻き戻しと隔離 |
| 隔離したあとブランチが使えない | 巻き戻しと隔離 |
| 対象バージョンで動かない構文が差し戻される | 対象バージョンの制約とテスト |
| lint のジョブだけが赤になる | 対象バージョンの制約とテスト |
| main に push できない | GitLab と CI |
| 見覚えのないブランチや MR が一覧に並ぶ | GitLab と CI |
| パイプラインが pending のまま動かない | GitLab と CI |
| ai_review が緑なのにコメントが付かない | GitLab と CI |
| ai_gate が止めたのにマージできる | GitLab と CI |
| 夜間ジョブの手動ボタンが押せない | 夜間ループと Stop hook |
| 夜間ジョブが MR を作らない、403 で弾かれる | 夜間ループと Stop hook |
| Stop hook が止まらない | 夜間ループと Stop hook |
| 進め方そのものに迷った、時間内に終わらない | 進行と画面共有 |
起動と実行環境
入り口で止まる症状です。Claude Desktop の起動、プロジェクトフォルダの選び方、フックの確かめ方、パーミッションモード、統合ターミナル、実行ポリシー、文字化け、改行コード、実行ビット、動く速さ、判定コマンドの不在を置いています。当日の朝に一番多く出ます。
プロジェクトのフォルダを開けない共通
Claude Desktop は起動しているのに、演習のファイルが見えません。ファイル名を伝えても「そのファイルが見つかりません」と返ってきます。プロジェクトの一覧に dl-training-app が並んでいません。
プロジェクトフォルダは、セッションを始めるときにプロンプト領域で設定する4項目の1つです。ここを選ばないまま書き始めると、会話はできてもファイルには手が届きません。ZIP を展開した場所と、選んだ場所が違う場合も同じ症状になります。
- 1まず事前セットアップで
dl-training-appを取り込んだ場所を確認してください。 - 2新しいセッションを作ってください。
Ctrl+Nです。 - 3プロンプト領域の4項目から、プロジェクトフォルダに
dl-training-appを選んでください。1つ上の階層を選ぶと、.claudeの設定が読まれません。 - 4「
app/libraries/util.phpの先頭10行を見せてください」と頼み、中身が返ることを確かめてください。ここまで通れば、以降の演習は進みます。 - 5返らない場合は、フォルダの位置がネットワークドライブや OneDrive の同期フォルダの下になっていないかを見てください。演習中は
C:\workのようなローカルのフォルダに置いてください。
Ctrl+N で並べて持てます。切り替えは Ctrl+Tab です。演習では、実装のセッションとレビューのセッションを分けて使います。同じことは CLI でもできます。研修では Claude Desktop で進めます。フックが効いているか画面で分からないプチ演習3
settings.json にフックを書いたものの、効いているかどうかが画面から読み取れません。何も起きていないのか、書き方が違うのか、判断が付きません。
フックは動いたときだけ画面に出ます。止めるものが何も来ていなければ、正しく登録されていても表示は変わりません。「出ない」と「効いていない」を、見た目で区別できないということです。なお、フックと permissions、プロジェクトの CLAUDE.md は Claude Desktop と CLI で共有されます。片方で書いたものが、もう片方で効かないということはありません。
- 1まず
settings.jsonを VSCode で開き、hooksのキーの中にイベント名が並んでいるかを見てください。Claude Desktop の入力欄に/hooksと打つと「isn't available in this environment」と返ります。登録の確認はいつもこのファイルで行います。 - 2拒否の規則も同じファイルの
permissions.denyで見ます。自分が足した行がBash(...)とPowerShell(...)の対で並んでいるかを確認してください。/permissionsも入力欄では使えません。 - 3効いていることを確かめるには、止まるはずの操作を1回頼んでください。止まった理由がフックの書いた文言で返れば、動いています。
- 4何も出ずに実行された場合は、フックが素通りしています。Windows では、まず
matcherがBash|PowerShellになっているかを見てください。Bashだけだと、PowerShell ツールから打たれたコマンドではフックが呼ばれません。次に実行ポリシーの項目を見ます。 - 5書き換えたあとは
Ctrl+Nで新しいセッションを開いてください。settings.jsonはセッションの開始時に読まれます。
/hooks を送ったときの画面です。この表示が出るのは故障ではなく、入力欄では使えないコマンドだからです
PreToolUse:Bash hook error で始まるフックの出力がそのまま出ます確認が出ないまま編集が進むプチ演習2
ファイルを変更してよいかの確認が出ません。頼んだ内容がそのまま適用され、気づいたときには複数のファイルが書き換わっています。拒否の規則に当たるものは止まるので、設定が壊れているわけではありません。
パーミッションモードが 自動 になっています。画面で選べるのは 自動、手動、編集を受け入れる、プラン の4つです。編集を受け入れる でも、ファイルの編集は確認なしで進みます。新しいセッションやワークツリーに移ったときに、前と違うモードで始まっていないかを見直してください。
- 1いまのモードを確認してください。演習中は 手動 にしておくと、どこで何を聞かれるかが見えます。
- 2止め金の演習をしているあいだは 自動 を選ばないでください。規則とフックが止めた場面を見ることが、演習の中身です。
- 3何を作るか先に見たいときは プラン を使ってください。ファイルは変わらず、手順だけが返ります。
- 4すでに書き換わってしまった場合は、巻き戻しの項目へ進んでください。ファイル単位で戻せます。
パイプでつなぐ書き方が動かないハンズオン1
差分をレビューへ渡す1行を、Claude Desktop の入力欄に貼っても動きません。-p や --print を使う形は、アプリの中では実行できません。
git diff main | claude --bare -p "..." --output-format json
Claude Desktop は対話で使うアプリで、非対話の実行(-p / --print)には対応していません。同じ理由で、エージェントチーム、インラインのコード補完、Bedrock などの第三者プロバイダも、アプリの側にはありません。CI の夜間ジョブが claude -p で書いてあるのは、あれが CLI で動いているためです。
- 1統合ターミナルを開いてください。この形はターミナルから CLI を呼んで実行します。
- 2コマンドを1行ずつ貼って流してください。出力先の
findings.jsonができることを確認します。 - 3ターミナルから
claudeが起動しない場合は、起動の項目の折りたたみを開いてください。 - 4対話で進める演習は、アプリの側へ戻してください。使い分けの線はここです。無人で回す形だけが CLI の仕事になります。
コマンドを打つ場所が見つからない共通
手順に git worktree list のようなコマンドが出てきます。Claude Desktop の入力欄にそのまま打っても、コマンドとしては実行されません。
入力欄は依頼を書くところで、シェルではありません。コマンドは統合ターミナルから実行します。この研修でコマンドを打つ場面は十数か所あり、どれも統合ターミナルにそのまま貼れる形で手順に書いてあります。
- 1統合ターミナルを開いてください。右上の
>_アイコンか、Ctrl+バッククォートです。 - 2開いた場所はセッションの作業ディレクトリです。
dl-training-appの中にいれば、手順のコマンドはそのまま通ります。pwd
- 32つ目のタブが要るときは、ターミナルペーンの + を押してください。
- 4コマンドを頼む形でも実行できます。「
git worktree listを実行して結果を見せてください」と書くと、確認のうえで実行されます。拒否の規則に当たるものは、ここで止まります。 - 5止まった場合は、止めているのが規則なのかフックなのかを読み分けてください。理由の1行に、どちらが止めたかが出ます。
>_)か、Ctrl+バッククォートで開きます。右のペインがターミナルで、開いた場所はセッションの作業フォルダです。画面は Mac で撮ったもので、Windows ではプロンプトが PS C:\...> の形になりますpwd で確かめてください。PowerShell が .ps1 の実行を拒むプチ演習3
Claude Desktop がフックを呼んだとき、または中のターミナルから手で試したときに、赤い文字でこう出ます。
.\block-destructive.ps1 : このシステムではスクリプトの実行が無効になっているため、ファイル
C:\Users\yamada\Desktop\dl-training-app\.claude\hooks\block-destructive.ps1 を読み込めません。
詳細については、「about_Execution_Policies」(https://go.microsoft.com/fwlink/?LinkID=135170)
を参照してください。
+ CategoryInfo : セキュリティ エラー: (: ) []、PSSecurityException
+ FullyQualifiedErrorId : UnauthorizedAccess
Windows の既定の実行ポリシーは Restricted で、署名の無い .ps1 を読み込めません。配布した settings.json の呼び出しは -ExecutionPolicy Bypass を付けていて、ふつうの端末ならこの形で読み込めます。エラーが出るのは、その引数を落として登録したとき、ターミナルから .\block-destructive.ps1 のように直接叩いたとき、そして会社のグループポリシーで実行ポリシーが固定されているときです。-ExecutionPolicy Bypass はその場のプロセスだけの指定で、グループポリシーの値(MachinePolicy と UserPolicy)は上書きできません。
- 1
.claude/settings.jsonのフック登録を開き、"command": "powershell.exe"のargsに"-NoProfile", "-ExecutionPolicy", "Bypass", "-File"が並んでいることを確認してください。 - 2中のターミナルから手で叩くときも同じ形にします。
powershell -NoProfile -ExecutionPolicy Bypass -File .claude\hooks\block-destructive.ps1
- 3それでも出る場合は、いまのポリシーを確認してください。
Get-ExecutionPolicy -List
- 4
MachinePolicyかUserPolicyの行がUndefined以外になっていたら、グループポリシーで固定された端末です。この端末では-ExecutionPolicy Bypassを付けても ps1 は動かないので、講師に知らせてください。Set-ExecutionPolicyは実行しないでください。
引数を落として叩いたときの表示です。1行目の このシステムではスクリプトの実行が無効になっているため がこの症状の目印です。
.\block-destructive.ps1 : このシステムではスクリプトの実行が無効になっているため、ファイル C:\Users\yamada\Desktop\dl-training-app\.claude\hooks\block-destructive.ps1 を読み込むことができません。詳細については、「about_Execution_Policies」(https://go.microsoft.com/fwlink/?LinkID=135170) を参照してください。
発生場所 行:1 文字:1
+ .\block-destructive.ps1
+ ~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : セキュリティ エラー: (: ) []、PSSecurityException
+ FullyQualifiedErrorId : UnauthorizedAccess
-NoProfile も外さないでください。個人のプロファイルが読み込まれると、そこに書いた文字コード設定や関数がフックの出力に混ざります。Claude Code が起動しない共通
Claude Desktop を開いても、演習で使う Claude Code の画面に入れません。サインインを求められたまま進まない、または起動はするものの、どのフォルダも開けない状態のままです。
多いのはサインインが済んでいない場合と、アプリの版が古い場合です。社内の配布で入っている版が古いまま固定されていることもあります。
- 1Claude Desktop をいったん終了し、もう一度起動してください。サインインの画面が出たら、案内したアカウントで入ってください。
- 2アプリの更新が来ていないかを確認してください。更新のあとは、もう一度起動し直します。
- 3Claude Desktop の設定画面で版を確認してください。
2.1.280以降であることが、モデルと effort の演習の前提です。 - 4ここまでで入れない場合は講師へお知らせください。端末の側の制限であることが多く、受講者側で直せる範囲を越えます。
フォルダを開いたセッションで「こんにちは」と送り、返事の中にフォルダ名 dl-training-app が出れば、この項目の問題ではありません。
2.1.280 より前の版では、model: opus のサブエージェントが Opus 5.5 ではなく Opus 5 で動き、タスクのペインの表示が他の方と食い違います。maxEffortLevel も 2.1.267 より前の版では無視されます。版が上がらない場合は講師へお知らせください。CLI 版が見つからない場合
研修は Claude Desktop で進めるので、ここは読まなくても完走できます。持ち帰って CLI でも使う方向けの補足です。
ターミナルで claude と打って次が出る場合は、導入先が PATH に入っていません。
claude : 用語 'claude' は、コマンドレット、関数、スクリプト ファイル、または操作可能な
プログラムの名前として認識されません。
+ FullyQualifiedErrorId : CommandNotFoundException
Git Bash や WSL では bash: claude: command not found の1行になります。導入の直後はターミナルを開き直してください。PATH の変更は、起動中のターミナルには届きません。それでも出る場合は、実体の場所と npm の導入先を見比べます。
where.exe claude npm config get prefix $env:PATH -split ';' | Select-String npm
1つ目で出たパスが3つ目の結果に含まれていなければ、アプリケーションを終了して起動し直してください。
フックの日本語が文字化けするプチ演習3
フックが返す理由が読めない並びになります。
[block-destructive] 繧ウ繝槭Φ繝峨r豁「繧√∪縺励◆
日本語版 Windows の PowerShell 5.1 は、指定しなければ CP932 で読み書きします。.ps1 を UTF-8 で保存して日本語のメッセージを書くと、出力がこの並びになります。読み込み側も同じで、Get-Content に -Encoding UTF8 を付けないと、UTF-8 の日本語が CP932 として読まれて化けます。
- 1自分で足したメッセージに日本語が入っていないかを確認し、英語に直してください。
.ps1はコメントも含めて ASCII だけで書きます。 - 2日本語で残したい説明は
.claude/hooks/README.mdへ移してください。フックの中に説明を置く必要はありません。 - 3PHP ファイルを読む処理を自分で足した場合は、
-Encoding UTF8が付いているかを見てください。$lines = @(Get-Content -LiteralPath $path -Encoding UTF8)
- 4セッションを開き直し、
sessionstart_verifyの報告に ASCII 以外のバイトの指摘が残っていないことを確認してください。
sessionstart_verify のような検証を1本足せば、機械が守る側へ移せます。改行コードでフックが不発になるプチ演習3
bash 版のフックを登録した端末で、Bash ツールを使うたびにこれが出ます。
/usr/bin/env: 'bash\r': No such file or directory
環境によっては次の形になります。
bash: .claude/hooks/block-destructive.sh: /usr/bin/env: bad interpreter: No such file or directory
フックが何も出さないまま素通りするだけのこともあります。画面には失敗の表示すら出ません。
行末の \r がシェバングの一部として読まれています。.sh を Windows のエディタで開いて保存し直すと、改行が CRLF に変わることがあります。WSL 上のプロジェクトを Windows 側のエディタから開いた場合も同じです。リポジトリ直下の .gitattributes で .ps1 は CRLF、.sh は LF に固定していますが、ZIP で受け取ったファイルを編集したときはこの固定が効きません。
- 1VSCode で該当の
.shを開き、右下のステータスバーの CRLF をクリックして LF を選び、保存してください。 - 2複数ある場合は PowerShell からまとめて直せます。
Get-ChildItem .claude\hooks\*.sh | ForEach-Object { $t = [IO.File]::ReadAllText($_.FullName) -replace "`r`n", "`n" [IO.File]::WriteAllText($_.FullName, $t) } - 3もう一度フックを手で流し、
2が返ることを確認してください。echo '{"tool_name":"Bash","tool_input":{"command":"git reset --hard"}}' | bash .claude/hooks/block-destructive.sh echo $? - 4
Permission deniedが出た場合は改行ではなく実行ビットの側です。次の項目へ進んでください。
.ps1 が LF になると、PowerShell 5.1 が途中でパースに失敗することがあります。自社ハーネスへ持ち帰るときは、置き先のリポジトリにも .gitattributes の2行を入れてください。フックの実行ビットが落ちているプチ演習3
フックを登録したのに、止まるはずのコマンドが素通りします。手で叩くとこう返ります。
bash: .claude/hooks/block-destructive.sh: Permission denied
WSL では、chmod +x を打っても ls -l の表示が変わらないことがあります。
ZIP は実行ビットを持たないので、展開した .sh は 644 になります。WSL から /mnt/c 配下(Windows 側のディスク)を触っている場合は、chmod を打っても元に戻ります。この位置のファイルシステムは、既定ではパーミッションを保持しません。素通りは失敗として表示されないので、気づくのが遅れます。
- 1いまの状態を見てください。左端が
-rwxr-xr-xなら実行ビットは立っています。ls -l .claude/hooks/*.sh
- 2Linux 側のディスク(
~/配下)に置いたリポジトリなら、これで直ります。chmod +x .claude/hooks/*.sh
- 3
/mnt/c配下で戻ってしまう場合は、フックの登録をbash .claude/hooks/block-destructive.shの形に変えてください。bash に渡す形なら実行ビットを見ません。 - 4Windows で進めている方は
.ps1側を登録してください。実行ビットの概念がありません。 - 5直したあと、止まるはずのコマンドを1回頼んで、止まることを目で確認してください。素通りは黙って起きます。
フックは動くが毎回もたつくプチ演習3
フックは効いています。ただ、Edit のたびに数秒から十数秒待たされます。ツリー全体を見る検査を足したあとに目立ちます。
WSL から /mnt/c 配下を読み書きすると、1ファイルごとに変換が挟まります。フックは編集のたびに走るので、この遅さが回数分積み上がります。AI は1つの依頼で何度も編集するため、体感は人が手で書くときの比ではありません。
- 1いまどちら側にいるかを確認してください。
/mnt/cで始まっていれば Windows 側のディスクです。pwd
- 2リポジトリを WSL 側のディスク(
~/配下)へ移してください。VSCode の WSL 接続から開けば、操作感は変わりません。 - 3移せない事情がある場合は、フックが見る範囲を編集されたファイル1本に絞ってください。標準入力の
tool_input.file_pathに対象が入っています。 - 4ツリー全体の検査は CI の側へ置いてください。手元で毎回やる必要のある検査は多くありません。
50_環境別のフック設計/環境別のフック設計.md にまとめてあります。WSL と Windows をまたぐ構成、対象バージョンが複数ある構成は、そちらを見てください。判定コマンドが無くて素通りするプチ演習3・5
1つ目は、レビュー結果を findings.json に落とす1行で止まります。
jq : 用語 'jq' は、コマンドレット、関数、スクリプト ファイル、または操作可能な プログラムの名前として認識されません。
2つ目は、bash 版のフックを登録したのに、止まるはずのコマンドが実行されます。標準エラーに1行だけ出ます。セッション開始時の報告にも同じ趣旨の行が並びます。
[block-destructive] jq not found, allowing the call
jq は CI の alpine イメージに入れている道具で、端末に入っているとは限りません。判定スクリプトそのものは jq を使っていません。ci/gate.ps1 は ConvertFrom-Json で読み、ci/gate.sh と ci/ai_gate.sh が jq を使います。bash 版のフックも jq でペイロードを読むので、jq が無いとガードを止めずに報告して素通りさせます。全部の Bash 呼び出しを止めるより、止まらないことを大きく知らせるほうが被害が小さいという判断です。
- 1Windows で進めている方は、フックは bash 版ではなく
.ps1側を登録してください。こちらは jq を使いません。 - 2JSON の取り出しは
ConvertFrom-Jsonに置き換えてください。自分で JSON を書き出すときは、BOM を付けない形にします。[IO.File]::WriteAllText("$PWD\findings.json", $out, (New-Object Text.UTF8Encoding $false))Set-Content -Encoding UTF8とOut-File -Encoding utf8は先頭に BOM を付けます。ci/gate.ps1は読めますが、bash ci/gate.shの jq はparse errorを返します。 - 3
findings.jsonを開き、summaryとfindingsの2つのキーがあることを確認してから判定にかけます。powershell -NoProfile -ExecutionPolicy Bypass -File ci\gate.ps1 $LASTEXITCODE
- 4bash を正として持ち帰る方は、jq を入れてからフックの単体テストを流してください。1件でも合わないとランナーが
1を返し、そのケースの出力を表示します。bash .claude/hooks/tests/run_cases.sh
- 5自分で書いたフックでも同じ形を作らないでください。判定に使うコマンドが無いときにエラーを握りつぶすと、毎回通ってしまいます。
command -v jqのような存在確認を先頭に置き、無ければ止めるか、標準エラーへ大きく出すかを決めてください。 - 6CI 側は触らないでください。ジョブのイメージには jq を入れてあります。
ci/gate.ps1、その bash 版の ci/gate.sh、CI が使う ci/ai_gate.sh は、どれも findings.json を読んで GATE_LEVEL 以上の件数を数えます。落ちる理由が揃っていないときは、まず文字コードを疑ってください。設定とフックの効き方
書いたのに効かないという症状です。置き場所、再起動、JSON の壊れ、隔離起動、effort とモデルの優先順位の6件を置いています。設定は書いた瞬間ではなく、セッションの開始時に読まれます。
deny に足したのに実行されるプチ演習2
拒否の規則を足したのに、頼むと実行されます。止まるはずの git reset --hard がそのまま走ります。
Windows でいちばん多いのは、Bash(...) の規則だけを書いている場合です。Git for Windows が入った Windows では、Claude は PowerShell ツールでコマンドを打つので、Bash(...) の規則は当たりません。次に多いのが、書き足した先が違う場合です。Claude Code が読むのは .claude/settings.json だけで、注釈付きの .claude/settings.example.jsonc は読みません。次に多いのが、VSCode で開いたフォルダが dl-training-app の親になっていて、プロジェクトルートが別の場所になっている場合です。
- 1セッションのプロジェクトフォルダが
dl-training-appになっていることを確認してください。1つ上の階層だと、別の場所の設定が読まれます。 - 2
.claude/settings.jsonを VSCode で開き、permissions.denyに自分が足した行がBash(git reset --hard *)とPowerShell(git reset --hard *)のような対で並んでいるかを見てください。.jsoncのほうに書いていたら、注釈行を落としてsettings.jsonへ写します。 - 3
Ctrl+Nで新しいセッションを開いてください。settings.jsonはセッション開始時に読まれます。 - 4止まるはずの操作を1回頼み、
permissions.denyによる拒否と返ることを確認してください。 - 5並んでいるのに実行される場合は、書き方の側です。
Bash(git reset --hard *)は文字列の照合なので、/bin/rmやbash -c "..."の形は当たりません。この穴を塞ぐのが PreToolUse フックです。
settings.local.json、プロジェクトの settings.json、~/.claude/settings.json の順です。deny はどこに書いたものも合わせて効きます。CLAUDE.md に書いた禁止が守られないプチ演習2
CLAUDE.md に禁止と書いた操作が実行されます。あるいは、数ターン前に直したはずの PHP 8.0 の match 式が、新しい編集でまた入ります。
$label = match ($status) { 1 => '見積中', 2 => '受注', default => '' };
CLAUDE.md は文脈であって設定ではありません。長い対話では優先度が下がり、会話の圧縮でも薄れます。公開されている不具合報告にも、禁止と書いた操作が実行された例と、指示どおりに戻せずコミットが消えた例が残っています。
- 1破ると実害が出る約束を1つ選び、
permissions.denyの1行に移してください。規則の形にします。 - 2文字列の照合ですり抜ける形が思いつくなら、PreToolUse フックの側にも足してください。
block-destructiveはコマンド全文を見ます。 - 3編集のたびに検査したい約束は PostToolUse に置いてください。対象バージョンの制約がこの位置です。
- 4
CLAUDE.mdに残すのは、禁止の列挙ではなく代替の書き方にしてください。matchを禁止と書くより「switchで書く」と書くほうが、次の編集に効きます。
--bare では読まれません。 claude --bare -p はフック、スキル、CLAUDE.md、MCP の自動読み込みを全部飛ばします。レビュー側に使うのは正しい用法ですが、実装側に使うと制約が1つも効きません。フックも拒否もまとめて効かないプチ演習3
フックも拒否の規則も、まとめて効かなくなります。止まるはずの操作が止まらず、差し戻されるはずの編集がそのまま通ります。さっきまで効いていたものが同時に消えるのが特徴です。
JSON として読めないファイルは、丸ごと無視されます。手で足していく演習なので、末尾のカンマ、閉じ括弧の過不足、// のコメントが原因になりやすい場所です。コメントを書けるのは .jsonc のほうだけです。
- 1VSCode で
.claude/settings.jsonを開き、赤い波線が付いている行を探してください。 - 2末尾のカンマを消し、
//で始まる行があれば削除してください。 - 3PowerShell で読めるかどうかを確かめます。エラーなく通れば JSON として正しい形です。
Get-Content .claude\settings.json -Raw | ConvertFrom-Json
- 4Claude Desktop のセッションを開き直し、止まるはずの操作を1回頼んで止まることを確認してください。
隔離起動でフックが発火しないハンズオン1
元のフォルダでは止まっていたコマンドが、ワークツリーで始めたセッションでは素通りします。あるいは、ワークツリーの .claude/settings.json を開くと中身が配布時点のままです。
{
"worktree": {
"baseRef": "head"
},
"permissions": {
"deny": [],
"ask": [],
"allow": []
},
"hooks": {}
}
ワークツリーは新しいチェックアウトです。配布した settings.json には worktree.baseRef = head が入っているので、ワークツリーは開始したときに乗っていたブランチの最後のコミットから切られます。午前に足した model、effort、拒否の規則、フックの登録は、ex/p3- にコミットしてあれば入ります。入っていないのは、開始したときに ex/p3- 以外のブランチに乗っていたか、設定をコミットしていなかったかのどちらかです。
- 1いったんセッションをアーカイブし、元のフォルダの VSCode で左下のブランチ名が
ex/p3-で始まっているかを見てください。違っていたらex/p3-に切り替えます。 - 2ソース管理ペインに
.claudeの下の変更が残っていたら、+ でステージし、メッセージを書いてコミットします。 - 3もう一度、ブランチ名の右の ワークツリー にチェックを入れて始めてください。開いたら
.claude/worktrees/の下のフォルダの.claude/settings.jsonに拒否の規則が入っていることを確認します。 - 4フックの登録を自分で書き換えた方は、
.claude/settings.example.jsoncの形と比べてください。フックは入力 JSON のcwdから作業フォルダを割り出すので、ワークツリーの中でも正しい場所を検査します。
effort を下げても元に戻るプチ演習1
settings.json の effortLevel を medium にしたのに、入力欄の右下の effort の表示が変わりません。下げても、次のプロンプトで元に戻ります。サブエージェントの effort はバックグラウンドタスクのペインに出ないので、この症状はセッション本体の話です。
解決の順は、環境変数 CLAUDE_CODE_EFFORT_LEVEL、--effort と /effort、settings.json の modelSettings と effortLevel、モデル既定です。同じファイルの中では、モデルごとの modelSettings がトップレベルの effortLevel より優先されます。/effort で段を確定すると modelSettings に書き戻されるので、そのあとトップレベルを書き換えても負けます。もう1つ、~/.claude/settings.json のトップレベルに書いた effortLevel は Opus 5.5 には効きません。プロジェクトの .claude/settings.json に書いた値は効きます。
- 1版を確認してください。Claude Desktop のバージョンは設定画面で見られます。中の Claude Code が
2.1.280以降であることが前提です。 - 2環境変数が残っていないかを見ます。値が返ったら、それが最優先で効いています。
$env:CLAUDE_CODE_EFFORT_LEVEL
- 3入力欄の右下のモデル名と effort の表示で、いま効いている値を見てください。
- 4
settings.jsonのmodelSettingsに今のモデルの行があれば、その行を書き換えるか、入力欄で/effortに続けて段を打って選び直してください。 - 5上限で抑える手もあります。
maxEffortLevelはどのスコープから指定しても、最も低い上限が効きます。
サブエージェントのモデルが違うプチ演習1
frontmatter に model: haiku と書いた grep-scout を呼んだのに、バックグラウンドタスクの行には別のモデル名が出ます。
サブエージェントのモデルは、呼び出し時の指定、定義の frontmatter の model、環境変数 CLAUDE_CODE_SUBAGENT_MODEL、親のモデルの順に決まります。frontmatter に書いてあれば環境変数より先に効くのがふつうです。ただし環境変数 CLAUDE_CODE_SUBAGENT_MODEL_FORCE が 1 のときは、CLAUDE_CODE_SUBAGENT_MODEL が最優先になります。社内の配布設定で入っていることがあります。版が 2.1.251 より前だと、FORCE が無くても環境変数が先に効きます。
- 1統合ターミナルで、2つの環境変数に値が入っていないかを確認してください。
$env:CLAUDE_CODE_SUBAGENT_MODEL_FORCE $env:CLAUDE_CODE_SUBAGENT_MODEL
- 21行目が
1を返した場合は、それが原因です。自分で設定したものなら外し、Claude Desktop を終了して起動し直してください。会社の配布設定で入っているなら講師に知らせてください。 - 3外せない設定であれば、そのまま進めて構いません。台帳には「この端末では
CLAUDE_CODE_SUBAGENT_MODEL_FORCEで環境変数が優先された」と1行書いてください。優先順位を自分の目で確かめたことが、この演習の中身です。
巻き戻しと隔離
AI が一度に広く書き換えたあとの戻し方です。チェックポイントの届く範囲、一部だけ戻す手順、自分の手直しと混ざったときの分け方、先に範囲を絞る方法、worktree での隔離を置いています。
巻き戻しても戻らない変更があるプチ演習2
復元で コードのみ を選んだのに、ファイルが元に戻りません。プチ演習2では、この形で残ります。
$ ls app/libraries sql_lib.php util.bak
ファイル名を変えたのがシェルのコマンド経由だからです。チェックポイントが持っているのはファイル編集ツールの変更だけで、シェルのコマンドで動かした結果は対象の外にあります。同じ理由で、サブエージェントの変更と、エディタで手で直した分も戻りません。AI に「全部やり直して」と頼んでも、この差は埋まりません。
- 1VSCode のソース管理ペインを開いてください。シェルのコマンドで動かした分も、ここには変更として出ます。チェックポイントより広い範囲が見えます。
- 2戻したいファイルの行にカーソルを合わせ、右端の 変更を破棄(戻る矢印)を押してください。そのファイルだけが直前のコミットの状態に戻ります。
- 3すでに push した変更を取り消す場合は、GitLab の Web 画面でマージ済みの MR を開き、右上の Revert を押してください。打ち消すコミットが新しく積まれ、履歴は残ります。
- 4次のタスクに入る前にコミットしてください。戻せる単位はコミットの単位です。
git reset --hard が走った事例が報告されています(issue #17190)。拒否の規則を Bash(...) と PowerShell(...) の対で入れ、フックも入れておけば、この頼み方でも止まります。AI の変更を一部だけ戻したいプチ演習2
1回の依頼で20ファイルが書き換わりました。当たっている修正と、外している修正が混ざっています。巻き戻しの コードのみ は選んだ点まで全部を戻すので、当たっている分も一緒に消えます。
チェックポイントの単位はセッションの時点で、ファイルや行では選べません。粒度を選べるのは git の側です。AI は1つの依頼で広く触るので、人が手で書くときより、この粒度の差が効いてきます。
- 1VSCode のソース管理ペインを開き、変更されたファイルの一覧を出してください。これが AI が触った範囲です。
- 2戻したいファイルの行で 変更を破棄 を押してください。そのファイルだけが直前のコミットの状態に戻ります。
- 31つのファイルの中の一部だけ戻す場合は、ファイル名をクリックして差分を開き、戻したい塊の上で右クリックして 選択した範囲を元に戻す を選びます。左側が元、右側が今の状態です。
- 4残した変更だけでテストを流してください。AI の変更どうしが依存していることがあり、片方だけ残すと通らない場合があります。手元に PHP は入っていないので、コミットして push し、CI の
testとtest-phpunitのジョブの結果で確かめます。 - 5どのファイルを残し、どれを捨てたかを台帳に1行書いてください。同じ依頼をもう一度出すときに、範囲を絞る材料になります。
自分の手直しと AI の変更が混ざったプチ演習2
AI に直させたあと、自分でも手を入れています。あとから AI の分だけを取り消したくなっても、どの行が誰の分か分かりません。ソース管理ペインはファイル単位でしか教えてくれません。
どちらも未コミットの変更として同じ場所に積まれています。チェックポイントはファイル編集ツールの変更しか持たないので、巻き戻しで コードのみ を選ぶと自分の手直しだけが残る形になり、意図した状態とずれます。
- 1すでに混ざっている場合は、ソース管理ペインでファイルをクリックして差分を開いてください。自分が書いた塊の上で右クリックし、選択した範囲をステージ を選びます。
- 2自分の分を全部ステージしたら、メッセージを書いてコミットしてください。「手直しの分」と1行で構いません。
- 3残った未ステージの変更が AI の分です。この状態なら、丸ごと捨てるか、ファイル単位で戻すかを選べます。
- 4どちらの分か判断が付かない塊は、捨てずに残してください。あとから履歴で追えるのは、コミットした分だけです。
- 5次からは混ざる前に切ってください。AI に渡す直前にコミットを1つ打ち、AI の出力をそのまま2つ目のコミットにし、自分の手直しを3つ目にします。この順にすると、上の作業がまるごと要らなくなります。
変更の範囲を先に絞りたいプチ演習3
止めたいコマンドは拒否の規則とフックで止まります。ただ、「30ファイルを一度に書き換える」「頼んでいないディレクトリまで触る」といった範囲の広さは止まりません。気づくのは終わったあとです。
拒否の規則もフックも、見ているのはコマンドの種類だけです。変更の量と場所は見ていません。範囲は依頼の文面に書いてあるだけで、機械が守っている決まりにはなっていません。
- 1依頼の時点で触ってよい場所を書いてください。「
app/libraries/util.phpの中だけ」のように、先に決めます。 - 2PreToolUse のフックで、Edit と Write の対象パスを見てください。範囲の外なら
2で返します。if ($payload.tool_input.file_path -notlike "*\app\libraries\*") { exit 2 } - 3件数で見る場合は PostToolUse の側です。変更されたファイルの本数を数え、決めた本数を超えたら標準エラーへ出してください。止めずに知らせるだけでも効きます。
(git status --short | Measure-Object).Count
- 4作業そのものを隔離すると、範囲はそのワークツリーの中に収まります。ブランチ名の右の ワークツリー にチェックを入れて始めてください。
- 5どの絞り方を選んだかを台帳に書いてください。パスで絞ったのか、件数で知らせたのか、隔離したのかで、戻す手間が変わります。
隔離した作業の始め方ハンズオン1
手順に「隔離して起動してください」と書いてあります。どこから作るのかが分かりません。
ワークツリーはアプリの画面から作ります。入力欄の上の行で、ブランチ名の右に ワークツリー のチェックがあり、チェックを入れて始めると、同じリポジトリの別のチェックアウトで作業が始まります。元のフォルダはそのまま残るので、手元の作業と並べて進められます。
- 1始める前に
.claudeの変更をコミットしておいてください。ワークツリーは開始したときのブランチの最後のコミットから切られ、コミットしていない設定は持ち込まれません。 - 2ブランチ名の右の ワークツリー にチェックを入れ、入力欄に1行送ってセッションを開始してください。名前を入れる欄はありません。
- 3入力欄の上に出た
worktree-で始まるブランチ名を控えてください。作業用のフォルダは<プロジェクトルート>/.claude/worktrees/の下に1つできます。 - 4作業が終わって push まで済んだら、サイドバーのアーカイブのアイコンで削除してください。
.claude/worktrees/ の下に作業用のコピーが作られます隔離すると .env が無いハンズオン1
worktree で作業を始めたら、動いていたはずのものが動きません。設定ファイルを探すと、.env のような無視対象のファイルが入っていません。
worktree は新しいチェックアウトなので、入るのは追跡されているファイルだけです。.gitignore に書いたファイルは持ち込まれません。鍵や接続先を .env に置いている場合、隔離した側からは見えなくなります。
- 1プロジェクトルートに
.worktreeincludeがあるかを見てください。演習リポジトリには配布時点で入っています。 - 2持ち込みたいファイルを1行ずつ書きます。
.env .env.local
- 3もう一度 worktree を作り直し、書いたファイルが入っていることを確認してください。
- 4鍵そのものを書かないでください。持ち込むのはファイルの名前で、中身は元の場所から写されます。
.worktreeincludeはコミットするファイルで、演習リポジトリには.envの行が入った状態で配布しています。
隔離のあとブランチが使えないハンズオン1
隔離して作業したあと、元のフォルダで作業を続けようとすると弾かれます。
fatal: 'worktree-issue-1' is already used by worktree at 'C:/work/dl-training-app/.claude/worktrees/...'
ブランチ名とフォルダ名は、ワークツリーを作ったときに自動で付いた名前になります。この資料の worktree-issue-1 は、控えた名前に読み替えてください。
あるいは、元のフォルダのソース管理ペインを見ても、隔離中に作ったコミットが出てきません。
同じブランチを2つの作業ツリーで同時にチェックアウトすることはできません。隔離中のコミットはワークツリーのブランチの側にあり、元のフォルダの ex/p3- には入っていません。元のフォルダは ex/p3- のままで正しい状態です。
- 1いまある作業ツリーの一覧を見てください。ここはコマンドを使います。
git worktree list
- 2隔離したフォルダを VSCode でもう1つ開いてください。ファイル から フォルダーを開く で、
.claude/worktrees/の下にできたフォルダを選びます。ここに隔離中のコミットが見えます。 - 3その窓のソース管理ペインから push してください。初回は ブランチの発行 の表示になります。
- 4MR は GitLab の Web 画面で作ります。push 後に画面上部へ出る Create merge request から、ソースに控えたブランチ(この資料の
worktree-issue-1)、ターゲットにmainを選んでください。 - 5push が済んでから片付けます。サイドバーの worktree の行にあるアーカイブのアイコンを押すと削除されます。
対象バージョンの制約とテスト
このプロジェクトの対象は PHP 7.4 です。検査は手元の check-phpver フックと、CI の lint-phpver の2か所です。フックは phpcs の PHPCompatibility があればそれを使い、無ければ正規表現で見ます。lint-phpver は php -l と php ci/undefined_functions.php の2段です。どこで捕まえたかによって直す場所が変わる5件を置いています。
対象バージョンの決め方プチ演習4
いま動いている環境の版と、これから移す先の版が違います。どちらを検査の対象に書けばよいか迷います。
検査の対象を現行の版に合わせると、移行のときに書き直す量が増えます。逆に新しすぎる版を書くと、いまの環境で動かないコードが通ってしまいます。この演習では移行先の 7.4 を対象に置いています。
- 1リプレイスの予定がある場合は、移行先の版を対象に書いてください。現行より新しい構文は止まりますが、移行のときにそのまま持っていけます。
- 2対象は設定ファイルの1か所に書き、フックと CI の両方がそこを読む形にしてください。2か所に書くと、片方だけ上げたときに食い違います。
- 3AI が素で書きたがるのは、もっと新しい版の構文です。
match式、enum、readonly、名前付き引数、コンストラクタプロモーション、str_contains、?->は、どれも 7.4 には入っていません。 - 4現行の環境でしか動かさない部分がある場合は、そこだけ別の対象を持たせてください。1つのリポジトリに複数の対象があること自体は、おかしなことではありません。
str_contains() は関数の呼び出しなので、php -l の構文検査は通ります。7.4 で実行すると Call to undefined function になります。CI の lint-phpver はこれを2段目の php ci/undefined_functions.php で落とします。構文だけを見る層では捕まえられないので、関数の有無を見る段を別に置いてください。phpcs が入っていないプチ演習4
手で phpcs を叩くと、コマンドが見つからない旨のメッセージになります。フックの側は phpcs が無くても何も表示せずに正規表現の検査へ進むので、差し戻しはそのまま起きます。phpcs は入っているのに規格が無い端末では、フックの標準エラーに次の1行が出て、同じく正規表現の検査へ進みます。
[check-phpver] PHPCompatibility standard not installed, using the regex fallback
端末に PHP と phpcs を入れていないためです。演習では入れなくても進みます。フックは phpcs が見つからないときに正規表現で代替し、対象外の構文と関数を並べた表の分だけ見ます。
- 1そのまま進めてください。正規表現版でも
match式、enum、readonly、?->などの構文と、str_contains()やstr_ends_with()などの関数の呼び出しは捕まります。 - 2誤検知が出た場合は、指摘された行を開いて確認してください。正規表現版はコメントと引用符の中身を外してから照合しますが、文字列の中のエスケープまでは追わないので、まれに当たります。
- 3最終の判定は CI の
lint-phpverです。1段目のphp -lが構文を、2段目のphp ci/undefined_functions.phpが関数の有無を見ます。 - 4自社の環境に入れる場合は、規格の配布元を見てください。
https://github.com/PHPCompatibility/PHPCompatibilityです。導入の手順は社内の PHP 環境に依存するため、この場では扱いません。
対象内の行が差し戻されるプチ演習4
該当する構文が無い行が差し戻されます。
[check-phpver] code that PHP 7.4 cannot run: C:\...\app\views\estimate_list.php:44 nullsafe operator (?->) (8.0) -> write isset($a) ? $a->b : null Rewrite these lines with the form shown after the arrow, then edit again. Arrow functions, typed properties, ?? and the spaceship operator are all fine on 7.4.
正規表現版はコメントを外し、引用符で囲まれた文字列の中身を空にしてから照合します。それでも、文字列の中にエスケープした引用符があると区切りを読み違え、画面に出す文言の一部が照合に残ることがあります。文字コードの読み違いでも起こります。日本語コメントを CP932 として読むと、コメントの区切りが崩れて同じ形に見えます。
- 1指摘された行を開き、実際に 7.4 で動かない構文や関数があるかを目で確かめてください。
- 2無ければ、文字列の書き方の側です。エスケープした引用符を含む文言を変えられるなら変え、変えられないなら phpcs を入れて正規表現版を使わない形にします。
- 3ほかの行でも同じ誤検知が出る場合は、読み込みの文字コードを疑ってください。
Get-Contentに-Encoding UTF8が付いているかを見ます。 - 4アロー関数
fn()と型付きプロパティは 7.4 で動きます。これが指摘されたら、フックを自分で書き換えたときの取りこぼしです。
lint ジョブだけが赤になるプチ演習4
push すると lint-phpver だけが赤になり、後ろの test と test-phpunit が動きません。ログには、1段目で落ちたときは PHP Parse error で始まる行と Errors parsing に続くファイル名の行が、2段目で落ちたときは次の行が出ます。
undefined_functions: PHP 7.4 に無い関数を呼んでいます。php -l は通りますが実行時に落ちます。
lint-phpver は対象バージョンの PHP 7.4 で2段の検査をします。1段目の php -l は構文を見るので、match 式のような 8.0 以降の構文はここで落ちます。2段目の php ci/undefined_functions.php は、呼んでいる関数が 7.4 にあるかを function_exists() で確かめるので、str_contains() のような 8.0 で入った関数はここで落ちます。手元のフックを登録していれば編集の直後に差し戻されるはずなので、落ちたということは、その端末でフックが効いていないか、CI 側だけに届いた変更です。
- 1ログに出た行番号を開き、対象バージョンで動く書き方に置き換えてください。
match式ならswitch、str_contains()ならstrpos()です。ハンズオン1 のように Issue が「直さずに記録」を求めている場合は、置き換えずに行番号とエラーの1行目をそのまま記録します。 - 2同じ構文や関数がほかにも無いかを確認してから push してください。
- 3フックが効いていなかった方は、
settings.jsonのhooksにPostToolUseが並んでいるかを確認してください。
検査用の見本ファイルで lint が落ちるプチ演習4
フックのテストケースを足したあと、lint-phpver のジョブが落ちます。ログの Errors parsing の行に、足した見本のファイル名(.claude/hooks/tests/fixtures/ の下の .php)が出ます。
CI の lint はツリーの中の *.php を全部 php -l に掛けます。対象バージョンで動かない見本を .php の名前で置くと、見本そのものが構文エラーとして拾われます。配布した見本の拡張子が .php.txt なのはこのためです。
- 1足した見本のファイル名を
.php.txtに戻してください。 - 2
.claude/hooks/tests/cases.jsonのfixtureの値も、同じ名前に直してください。 - 3単体テストを流し、全件が合格することを確認してください。
powershell -NoProfile -ExecutionPolicy Bypass -File .claude\hooks\tests\run_cases.ps1
- 4ランナーは実行のたびに一時ディレクトリへ
.phpとして写します。検査そのものは、拡張子を変えても同じように走ります。
GitLab と CI
push、パイプライン、AI レビューのジョブで止まる6件です。CI で止まったときは、ジョブのログの最初の10行と最後の10行で切り分けられます。
main に push できない共通
push すると VSCode の右下に赤い通知が出ます。出力ペインの Git を開くと、この文面が入っています。
remote: GitLab: You are not allowed to push code to protected branches on this project. ! [remote rejected] main -> main (pre-receive hook declined)
main にブランチ保護がかかっており、合流の経路は MR だけです。受講者の権限は Developer なので、直接 push は必ず弾かれます。ブランチを切らずに編集を始めたときに出ます。
- 1VSCode の左下、青いステータスバーにいまのブランチ名が出ています。
mainになっていないかを確認してください。 - 2
mainのままなら、そのブランチ名をクリックし、上に出る一覧から 新しいブランチの作成 を選び、ex/p1-yamadaのように、その演習のブランチ名を付けてください。コミット前の変更はそのまま移ります。 - 3ソース管理ペインでメッセージを書き、チェックマークでコミットしてください。続けて ブランチの発行 を押すと、そのブランチが GitLab 側にできます。
- 4MR は GitLab の Web 画面で作ります。push のあと、プロジェクトの画面上部に Create merge request のボタンが出ます。
- 5ボタンが出ていない場合は Code から Merge requests を開き、New merge request でソースに自分のブランチ、ターゲットに
mainを選んでください。
パイプラインが pending のまま動かない共通
MR を出してもジョブが pending のままです。ジョブを開くと、上部にこの文言が出ます。
This job is stuck because the project doesn't have any runners online assigned to it.
タグを付けた場合は次の形になります。
This job is stuck because you don't have any active runners online or available with any of these tags assigned to them: ...
ジョブを実行する runner が空いていないか、オフラインです。runner は1台です。全員が同じ時間に push すると、並列数の上限で順番待ちになります。ハンズオン3では nightly_implement が1本最長30分 runner を使うので、その間に出した MR のパイプラインも後ろに並びます。この場合の pending は異常ではありません。
- 12分ほど待ってから、パイプラインの画面を再読み込みしてください。順番待ちなら、ここで動き始めます。
- 25分以上動かない場合は、講師へお知らせください。runner の状態は受講者の権限では見られません。
- 3待っているあいだは、手元でできる作業を先に進めてください。レビューの記録と、MR の説明欄の記入は CI を待ちません。
ジョブ画面の上部に出る文言です。stuck の語が出ていれば順番待ちではなく、runner 側の問題です。
This job is stuck because the project doesn't have any runners online assigned to it.
ai_review が緑なのにコメントが付かないハンズオン2
ai_review ジョブは緑で終わるのに、MR にコメントが1本も付きません。ジョブのログにこの1行が出ています。
[ai_review] CI/CD変数 ANTHROPIC_API_KEY が未設定のため、レビューを実行せずに終了します
artifacts の findings.json を開くと、findings が空で "skipped":"no_api_key" が入っています。指摘が0件なので ai_gate も緑になり、ゲートが何も止めていない状態で MR がマージできる形になります。
トークン側が足りないときは、こちらは落ちます。ログの末尾はこの形です。
ci/ai_review.sh: line 20: GITLAB_BOT_TOKEN: CI/CD変数 GITLAB_BOT_TOKEN が未設定です
ハンズオン3の nightly_implement は、キーが無いと先頭の確認で止まります。「実装を開始します」の行は出ず、ci_logs/ も作られません。
ci/nightly_implement.sh: line 15: ANTHROPIC_API_KEY: CI/CD変数 ANTHROPIC_API_KEY が未設定です ERROR: Job failed: exit code 1
ジョブが使う変数が登録されていないか、登録はされていても Protect variable が付いています。保護付きの変数は、保護されたブランチのパイプラインにしか渡りません。受講者のブランチは保護対象ではないので、値が届かずこの形になります。ai_review が緑で終わるのは、読むだけのジョブを環境の不備で赤にしない作りにしているためです。落とす判断は ai_gate の側にあり、指摘が0件なら通ります。緑だから安全、とは読めません。
- 1これは講師側の設定で、受講者の権限では CI/CD の変数の画面は見られません。講師へお知らせください。
- 2講師から直ったと連絡があったら、MR の画面の Pipelines タブで該当の行の右端にある再実行のボタンを押してください。push をやり直す必要はありません。
nightly_implementの場合は、同じ手動ボタンをもう一度押します。 - 3キーが無いまま緑になった
ai_reviewの回は、計測表の「CI の ai_review」の行に「未実施」と書いてください。指摘0件として数えると、比較が壊れます。
ai_gate が赤にならないハンズオン2
ai_gate のログには止めた記録が出ているのに、パイプライン全体は緑で、MR はマージできる状態のままです。ジョブ名の横に Allowed to fail の表示が付いています。
しきい値 P1 以上、確信度 0.0 以上の指摘を数えます。 confidence の無い指摘は 1.0、P0〜P3 以外の severity は P0 として数えます。 P1: 2 件 / P2: 2 件 ゲートで止めた指摘 2 件 [P1] app/controllers/estimate_ctl.php:36 date_to が条件に使われず、範囲の上限が効きません 直すか、直さない理由をMRのコメントに書いてから再実行してください。
.gitlab-ci.yml の ai_gate に allow_failure: true が入っています。初期状態はこれです。列挙だけを見せて、落とす判断は受講者が自分で入れる順番にしてあります。なお、この行を外してパイプラインが赤になっても、演習のプロジェクトでは Merge ボタンは押せます。Pipelines must succeed を設定していないためです。押せますが押しません。
- 1
.gitlab-ci.ymlを開き、ai_gateの節のallow_failure: trueの1行を消してください。 - 2コミットして push し、パイプラインが赤くなることを確認してください。ここで1回落ちる記録が、この演習の合格の形です。
- 3指摘を直してもう一度 push し、緑に戻してください。
- 4落とす重大度を変えたい場合は、
allow_failureではなくGATE_LEVELを動かしてください。P1は P0 と P1 を止め、P2はそこに P2 を加えます。
REVIEW.md、後者は GATE_LEVEL です。2つのジョブに分けてあるのは、後から追えるようにするためです。指摘が差分の前半に偏るハンズオン2
ai_review は緑で、MR のコメントも投稿されているのに、指摘が差分の前半のファイルに偏ります。後半で入れた変更には1件も付きません。
差分が長いと、先頭の一定量で打ち切ってからモデルに渡しています。打ち切ったことはプロンプトには書いていますが、指摘そのものは前半に偏ります。
- 1MR を分けてください。1本を小さく保つほうが、しきい値を上げるより効きます。レビューの質は差分の長さで下がります。
- 2分けられない事情があるときだけ、CI/CD Variables に
AI_REVIEW_DIFF_CHAR_LIMITを足して上限を上げてください。 - 3上げた場合は、モデルの選択も見直してください。差分の行数で切り替えるしきい値は
AI_REVIEW_DIFF_THRESHOLDです。 - 4台帳には、どこまで読まれた状態の指摘なのかを1行残してください。件数の比較に効きます。
夜間ループと Stop hook
無人で回すときに止まる9件です。1回目は止まる前提で流します。どこで止まったかを計測表に書くところまでが演習なので、緑にならなくても先へ進んでください。
手動ボタンが押せないハンズオン3
パイプラインの一覧に nightly_implement の再生ボタンが出ていない、または灰色で押せません。ジョブを開くと、上部にこの文言が出ています。
This job requires a manual action
別の形では、ジョブの状態が created か skipped のまま動きません。
3つのどれかです。1つ目は、前の段(lint と test)がまだ終わっていない場合で、implement の段はその後に来ます。2つ目は、押しているパイプラインのブランチに権限が無い場合です。保護ブランチのパイプラインの手動ジョブは、そのブランチへのマージ権限を持つアカウントだけが押せます。演習の main は Developer にもマージを許しているので、受講者も main の手動ジョブを押せます。押せても押しません。ハンズオン3 の Step 3 は講師が main で流し、Step 5 で各自が自分のブランチで押します。3つ目は、pipeline schedule から起動されたパイプラインを見ている場合で、そこでは手動ボタンではなく自動で走る形になります。
- 1Build > Pipelines の行の Stages で、lint と test の丸が緑になるまで待ってください。
test-phpunitが2分ほど長くかかります。 - 2緑になったら画面を再読み込みし、いちばん右の丸に再生ボタンが出ていることを確認してください。
- 3押すのはハンズオン3 の Step 5 で、自分のブランチのパイプラインの行です。それより前に押してしまった場合は、そのジョブの Cancel を押してください。
schedule が有効のまま残っているハンズオン3
Pipeline schedules の一覧で、自分の行が Active になっています。Next Run に翌日の 2:00 が入っています。
Activated のチェックが既定で入っており、外さずに保存しています。このままだと翌日の 2:00 に受講者の人数ぶんの nightly_implement が同時に走り、runner と API の予算を使います。
- 1Build > Pipeline schedules を開き、自分の行の Active のトグルを切ってください。表示が Inactive に変わります。
- 2すでに走ってしまった場合は、Build > Pipelines で起動元が schedule の行を探し、Cancel を押してください。
- 3行の右端の再生ボタン(Run pipeline schedule)も押さないでください。schedule を手で即時起動するボタンで、同じことが起きます。
夜間ジョブが30分で打ち切られるハンズオン3
「実装を開始します」の行のあと、ログが動かないまま30分が過ぎ、末尾にこの行が出て終わります。
ERROR: Job failed: execution took longer than 30m0s seconds
artifacts の ci_logs/ は残りますが、claude-issue-4.json が空か、途中で切れた JSON になっていることがあります。
.gitlab-ci.yml の timeout: 30m が効いています。--max-turns 15 は回数の上限で、1ターンが長いと回数に届く前に時間のほうが先に来ます。tests/run_tests.php が DB の接続待ちで固まった場合と、影響範囲の調査で grep-scout がツリー全体を読みに行った場合に起こります。
- 1
timeoutと--max-turnsは上げないでください。1本あたりの所要時間と費用が読めなくなります。 - 2計測表には「時間の上限で打ち切られた」と書き、直す層は「Issue の粒度」を選んでください。Issue の「対象ファイル」を2本に絞った版を予備の Issue #5 の形で書くのが、研修後の課題です。
- 3ジョブ画面の上部の経過時間が動いているあいだは、打ち切りではなく実行中です。25分を過ぎたら講師へお知らせください。
夜間ジョブが許可待ちで終わるハンズオン3
nightly_implement は数十秒で終わり、ログの末尾はこうなります。ブランチもコミットもできていません。
turns: 1 / cost_usd: 0.01 / is_error: false 変更がありません。MRは作りません。どこで止まったかは ci_logs/claude-issue-4.json を見てください。
結果 JSON の result には、ツールの使用許可を求める文が入っています。
配布のスクリプトでは3つの引数がそろっているので、ここで終わるのは次の症状Bのほうが多いです。
結果 JSON(ci_logs/claude-issue-4.json)の permission_denials に curl を使おうとした記録が入り、result が「受入条件が特定できない」で終わります。拒否は .err には出ません。
症状Aは、非対話の -p では確認が要る操作がすべてその場で拒否されるためです。確認を出す相手がいないので、書き換えもコマンドも通らずに終わります。無人で動かすには --permission-mode dontAsk と --allowedTools と --max-turns の3つで、許す範囲と回数の上限を決めます。症状Bは、SKILL.md の手順1 が Issue 本文の取得に curl を使う一方、--allowedTools に渡しておらず、ci/settings.ci.json の deny にも Bash(curl *) が入っているためです。SKILL.md が意図した止め金として書いている箇所で、ハンズオン3 の1回目はここで終わります。
- 1
ci/nightly_implement.shの起動行を開き、3つの引数がそろっているかを確認してください。claude -p "$PROMPT" \ --permission-mode dontAsk \ --allowedTools "Read,Edit,Write,Grep,Glob,Bash(php *),Bash(git add *),Bash(git commit *)" \ --max-turns 15 \ --output-format json
- 2
Bash(git add *)の*の前の半角スペースを消さないでください。Bash(git add*)と書くと別のコマンドにも当たります。 - 3結果 JSON を開き、拒否されたツールの名前を確かめてください。
--allowedToolsに無いツールを使おうとして止まっている場合は、許すべきツールかどうかを先に判断します。 - 4ブランチ作成と push は、エージェントではなくスクリプトの仕事にしてあります。ここを許すと、止め金の外で push できてしまいます。渡すツールを増やす前に、その線を動かしてよいかを考えてください。
- 5症状Bの直し方は1通りです。
ci/nightly_implement.shのPROMPTに「Issue の本文は_issues/issue-4.mdにあります」の1行を足して、Readで読ませます。--allowedToolsにBash(curl *)を足しても、ci/settings.ci.jsonのdenyが先に効くので通りません。外へ出る経路を増やさない形が、夜間の実行に向いています。
夜間ジョブが MR を作らないハンズオン3
ジョブは緑で終わっているのに、MR の一覧に何も増えません。ログの末尾はこの1行です。
変更がありません。MRは作りません。どこで止まったかは ci_logs/claude-issue-4.json を見てください。
差分が1行も無いときは MR を作らない作りにしています。原因は3つのどれかです。許可待ちで止まった、Issue の本文を読んで「変更不要」と判断した、途中でターン数の上限に当たった。
- 1ジョブ画面の右側の Job artifacts で Browse を押し、
ci_logsの中のclaude-issue-4.jsonをクリックして開いてください。 - 2
num_turnsを見ます。15に張り付いていれば上限で打ち切られています。Issue の粒度が大きすぎるので、範囲を狭めてください。 - 3
1か2で終わっていれば許可待ちです。前の項目の手順へ進んでください。 - 4
resultに「変更は不要」という趣旨の文が入っていた場合は、Issue の期待する振る舞いと受入条件を読み直してください。人が読んで曖昧なら、エージェントも同じところで迷います。 - 5止まった箇所と、どの層の設定で直すかを台帳に1行書いてから再実行してください。
MR の作成が 403 で弾かれるハンズオン3
コミットと push は通っているのに、MR が立ちません。ci_logs/ とジョブログの末尾に GitLab の応答が出ます。
remote: GitLab: 403 Forbidden - You are not allowed to create merge request
夜間ジョブは、push に使ったトークンの権限で MR を作ります。CI_JOB_TOKEN では通りません。GITLAB_BOT_TOKEN のスコープが足りない場合も同じ形になります。
- 1これは講師側の設定です。まず講師へお知らせください。
- 2
GITLAB_BOT_TOKENをプロジェクトアクセストークン(ロール Developer、スコープapiとwrite_repository)に差し替えます。 - 3ブランチは push できているので、GitLab の画面から Create merge request を押せば先へ進めます。止まった箇所は「MR の作成で止まった」として記録してください。
流し直しで push が拒否されるハンズオン3
2回目の実行で、最後の push だけが落ちます。
! [rejected] HEAD -> nightly/issue-4 (non-fast-forward) error: failed to push some refs to 'https://gitlab-09291006aidev.give-app.net/...' hint: Updates were rejected because the tip of your current branch is behind its hint: remote counterpart.
1回目の nightly/issue-4 が残っており、今回作り直したブランチとは別の歴史になっています。強制 push は拒否の規則で禁じているので、CI にも例外を作っていません。
- 1GitLab の Code から Branches を開いてください。
- 2
nightly/issue-4を探し、右端のごみ箱で削除してください。前回の MR が開いていれば、先に閉じます。 - 3自分のブランチのパイプラインで、もう一度
nightly_implementの手動ボタンを押してください。 - 4前回の結果を残したい場合は、
ISSUE_IDを予備の5に変えて流します。New pipeline の Variables の欄に、キーISSUE_ID、値5を1行入れて起動します。ブランチ名がnightly/issue-5になるので衝突しません。
Stop hook が止まらないプチ演習6
応答が終わりません。同じ理由の継続が何度も繰り返され、テストの実行だけが延々と走ります。
php tests/run_tests.php failed with exit code 1. Run it, read the failing case, fix the code, then finish.
継続で呼び戻されたときに stop_hook_active が true になります。これを見ずに block を返し続けると、Claude Code が8回連続の block で打ち切るまで、同じ継続が繰り返されます。配布した stop-gate は、この判定を先頭に置いています。講師が先に見せる壊れた版は、その3行を外したものです。
- 1
Escでいったん止めてください。 - 2
.claude/hooks/stop-gate.ps1の先頭の判定が残っているかを確認してください。if ($payload.stop_hook_active -eq $true) { exit 0 } - 3継続の回数にも上限を置いてください。環境変数
STOP_GATE_MAXで変えられます。既定は同じセッションで 2 回までです。 - 4回数を数えている作業メモを消してから、もう一度試してください。
Remove-Item .claude\hooks\.stop-gate.state -ErrorAction SilentlyContinue
- 5正しい版では、1回の連鎖で継続が入るのは1回だけです(
stop_hook_activeが true になって2回目は素通りします)。同じセッションで依頼を繰り返すと累計が上限(既定2)に達し、次の行を出して終わります。[stop-gate] already continued 2 time(s), the cap is 2. Letting the session stop.
作業メモがコミットに混ざるプチ演習6
コミットの前にソース管理ペインを見ると、見覚えのないファイルが並びます。
.claude/hooks/stop-gate.ps1 M .claude/hooks/.stop-gate.state U
継続の回数をセッションごとに数えているファイルです。テストが通れば自動で消えますが、赤いまま終えると残ります。手元の作業メモなので、共有するものではありません。
- 1
.gitignoreに1行足してください。.claude/hooks/.stop-gate.state
- 2すでにコミットへ入れてしまった場合は、追跡だけを外してください。ここはコマンドを使います。
git rm --cached .claude/hooks/.stop-gate.state
- 3ソース管理ペインでは、コミットしたいファイルの行の + だけを押してステージしてください。まとめて上げる癖をやめると、この種の混入は起きません。
進行と画面共有
進め方そのもので止まったときの2件です。リモートの方も多く参加されます。手が止まったときに一番早いのは、画面を見せることです。
時間内に演習が終わらない共通
Step の [Nmin] を超えても終わりません。次の Step の説明が始まってしまいます。
[Nmin] は目安で、全員が同じ速さで進む前提には置いていません。CI の待ち時間と、レビューの読み込みで差が出ます。
- 1OK 基準の欄を開き、いくつ満たしているかを数えてください。すべて満たす必要はありません。
- 2満たしていない項目のうち、次の演習の土台になるものだけを先に片付けてください。プチ演習の成果物は、後の演習で使います。
- 3残りは「追加と考察」の扱いにして、先へ進んでください。止まったまま待つより、次の Step の前半を聞くほうが得るものが多くなります。
- 4どこで詰まったかを台帳に1行書いてください。Day2 の冒頭で、詰まりの多かった箇所から返します。



