こうすると、こう動く

10の場面で見る仕組み

誰が何をすると、どの仕組みが動き、AIがどこまでやり、人が何をするのか。研修のGitLabで起きる10の場面を、1段ずつ再生します。

場面ごとに、実機の記録か想定の場面かを書いています。想定の場面も、ログの文面とラベルの動きは研修のGitLabと同じ形です。

午後に出す1本

想定の場面。受講者のAさんと講師が登場し、午後のスケジュールが10分おきに動いています。

手元のPC
Claude Desktop、フック、pre-commit
人
書く、承認する、読んでマージする
Issueとボード
型、受け入れ条件、ラベル
起動
スケジュールかNew pipeline
ai_pick
承認済みのIssueを拾う
Runner
EC2の上でジョブを動かす
main
保護。マージはMaintainerだけ
MRのパイプライン
lint、test、AIレビュー、ai_scope
MR
ai-cycle-botか人が作る
AIの実装
Claudeが実装してコミット
時刻なしIssueのラベルなしMRなしAIの費用なし
  1. AさんがWork itemsのNew itemでIssueを作り、型ai-taskを選びます。レベル2のメモ「02期限切れ」をもとに、受け入れ条件を5行で書きました。Aさん期限の切れた見積に、一覧で印を付けたい。
  2. Aさんがラベルai::approvedとlevel::2を付けます。この時点では、ボードのOpenの列に並んだままです。Aさん受け入れ条件が書けたので、承認します。
  3. 午後のスケジュールが、毎時3分から10分おきにサイクル用のパイプラインを動かします。Aさんが何もしなくても、次の便で拾われる作りです。GitLabスケジュール「AIサイクル(午後の研修)」を実行しました。
  4. ai_pickがAさんのIssueを拾い、ai::runningを付けました。同じ便で、ほかの受講者のIssueもまとめて拾われます。ai_pickのログ承認済みで未着手の Issue: 6 本
  5. 実装ジョブは、まずラベルを確かめ直します。外れていなければ、Issueの本文をファイルに書き出し、Claudeに渡します。implement-issue-41のログAI-GATE: Issue #41 に ai::approved があります。実装に進みます。
  6. Claudeがコードを読み、一覧の画面とテストを直してコミットしました。レベル1と同じ大きさなら、6〜7ターン、0.3ドル前後が目安です。Claude受け入れ条件の5行を満たしたので、コミットしました。
  7. スクリプトがpushしてMRを作り、Issueにコメントを付けます。コメントの頭でAさんを@で呼ぶので、AさんのTo-Doに1件届きます。ai-cycle-bot@AさんAIの実装サイクルがMRを出しました。
  8. MRのパイプラインが走りました。ai_scopeは、テストを足さずに画面を変えたことを注意で知らせます。注意はマージを止めません。ai_scopeのログ注意 app/ を変えたのに tests/ を変えていません。テストで確かめていない変更です
  9. AさんがTo-Doから開き、差分を受け入れ条件と突き合わせます。注意の行を読み、画面で見た目を確かめました。Aさん表示を変えただけなので、画面で確かめてからマージを頼みます。
  10. 講師がマージします。Issueは自動で閉じ、ボードのClosedの列に移りました。講師差分と注意の行を見て、マージしました。
この場面で分かること受講者がするのは、書く、承認する、読む、の3つです。パイプラインを自分で動かす必要はありません。

受け入れ条件が曖昧なIssue

実機の記録(10月2日、Issue #31)。Bさんが書いた1行の受け入れ条件に、AIが質問を返しました。

手元のPC
Claude Desktop、フック、pre-commit
人
書く、承認する、読んでマージする
Issueとボード
型、受け入れ条件、ラベル
起動
スケジュールかNew pipeline
ai_pick
承認済みのIssueを拾う
Runner
EC2の上でジョブを動かす
main
保護。マージはMaintainerだけ
MRのパイプライン
lint、test、AIレビュー、ai_scope
MR
ai-cycle-botか人が作る
AIの実装
Claudeが実装してコミット
時刻なしIssueのラベルなしMRなしAIの費用なし
  1. Bさんが「見積一覧を見やすく」という題でIssueを書きました。受け入れ条件は「見やすくなっている」の1行だけです。Bさん営業から、見積一覧が見にくいと言われている。
  2. Bさんがラベルai::approvedを付けます。仕組みの側は、受け入れ条件の中身までは見ません。BさんAIに任せます。
  3. 次の起動でai_pickが拾い、ai::runningを付けました。「決まっていないこと」の節は空なので、ジョブはClaudeを呼びます。ai_pickのログ#31 見積一覧を見やすく
  4. Claudeは画面とコントローラを読みましたが、何を変えれば済むのかが決められません。そこで実装せずに、質問をファイルに書いて終えました。implement-issue-31のログターン 5 / 費用 0.0673516 ドル / 終わり方 success
  5. スクリプトがai::stoppedを付け、Claudeの質問をコメントに書きました。質問は4つで、その1つ目が下の文です。ai-cycle-bot「見やすくなっている」の具体的な基準が書かれていない。何を変えれば受け入れ条件を満たすと判断できるか決まっていない。
  6. BさんのTo-Doに通知が届きます。Bさんは営業に聞き直し、何を変えるかを決めました。Bさん状態の列を左端へ移し、金額は3桁で区切る。対象は一覧の画面だけ。
  7. Bさんが受け入れ条件を書き直し、ラベルai::stoppedを外しました。ai::approvedは付いたままなので、承認し直す手間はかかりません。Bさん受け入れ条件を3行に直しました。
  8. 次の便のai_pickが、同じIssueをもう一度拾いました。ai_pickのログ承認済みで未着手の Issue: 1 本
  9. 今度は受け入れ条件がはっきりしているので、Claudeが実装し、MRが出ました。このあとは場面01と同じ流れです。ai-cycle-bot@BさんAIの実装サイクルがMRを出しました。
この場面で分かることAIは、決められないことを自分で決めずに質問で返します。受け入れ条件を人が書く理由は、ここにあります。

決まっていないことが残るIssue

実機の記録(10月2日、Issue #32)。Cさんが「決まっていないこと」に1行を残したまま承認しました。

手元のPC
Claude Desktop、フック、pre-commit
人
書く、承認する、読んでマージする
Issueとボード
型、受け入れ条件、ラベル
起動
スケジュールかNew pipeline
ai_pick
承認済みのIssueを拾う
Runner
EC2の上でジョブを動かす
main
保護。マージはMaintainerだけ
MRのパイプライン
lint、test、AIレビュー、ai_scope
MR
ai-cycle-botか人が作る
AIの実装
Claudeが実装してコミット
時刻なしIssueのラベルなしMRなしAIの費用なし
  1. Cさんが、倉庫コード2の表示名を確かめるテストのIssueを書きます。受け入れ条件は具体的ですが、「決まっていないこと」に1行が残っています。Cさんテストを新しいファイルにするか、既存のファイルに足すかは、まだ決めていない。
  2. Cさんがラベルai::approvedを付けました。CさんあとはAIに任せます。
  3. ai_pickが拾い、実装ジョブが始まります。ai_pickのログ#32 テスト: warehouse_name(2) が「東日本」を返す
  4. ジョブのスクリプトが、Claudeを呼ぶ前に「決まっていないこと」の節を読みます。1行でも書いてあれば、その場で止める作りです。implement-issue-32のログIssue の「決まっていないこと」に書いてあることがあるので、実装しませんでした。
  5. スクリプトがai::stoppedを付け、残っていた1行をコメントに書き写しました。Claudeを呼んでいないので、費用は0ドルで、48秒で終わっています。ai-cycle-bot決めて受け入れ条件に移し、この節を空にしてください。
  6. Cさんが決めます。既存のテストのファイルに1本足すことにしました。CさんWarehouseNameTest.phpに足す、と受け入れ条件に書きます。
  7. Cさんが受け入れ条件に書き足し、「決まっていないこと」を空にしてからai::stoppedを外しました。Cさん節を空にして、ラベルを外しました。
  8. 次の便で拾い直され、MRが出ました。ai-cycle-bot@CさんAIの実装サイクルがMRを出しました。
この場面で分かること「決まっていないこと」の確かめは、AIに頼まずスクリプトが機械的に行います。止まるのが速く、費用もかかりません。

夜に貯めた3本

10月5日の夜に予定している流れに、9月30日に手元で試した結果を重ねた場面です。講師が夕方に3本を承認し、翌朝にMRを読みます。

手元のPC
Claude Desktop、フック、pre-commit
人
書く、承認する、読んでマージする
Issueとボード
型、受け入れ条件、ラベル
起動
スケジュールかNew pipeline
ai_pick
承認済みのIssueを拾う
Runner
EC2の上でジョブを動かす
main
保護。マージはMaintainerだけ
MRのパイプライン
lint、test、AIレビュー、ai_scope
MR
ai-cycle-botか人が作る
AIの実装
Claudeが実装してコミット
時刻なしIssueのラベルなしMRなしAIの費用なし
  1. 講師がdemo.csvを取り込みます。状態コードの一本化、広めの改修、受け入れ条件が粗い1本、の3本ができました。講師オリエンテーションで見せる3本を用意します。
  2. 講師が3本すべてにai::approvedを付けました。講師3本とも承認しました。
  3. 講師が、夜のスケジュール「AIサイクル(夜間)」のActivatedにチェックを入れます。これで今夜の便が動きます。講師夜の便を有効にした。あとは朝に見る。
  4. 夜のスケジュールが動きます。設定は22時ですが、GitLabの決まりで10分おきの枠に合わせるため、実際は22時3分です。GitLabスケジュール「AIサイクル(夜間)」を実行しました。
  5. ai_pickが3本を拾い、3つの実装ジョブが同時に動き始めました。人は誰もいません。ai_pickのログ承認済みで未着手の Issue: 3 本
  6. 1本目の状態コードの一本化は、9月30日の試走では18〜19ターン、約0.55ドルでコミットまで進みました。implement-issueのログターン 19 / 費用 0.55 ドル / 終わり方 success
  7. 広い2本目は、25分の上限に届くと打ち切られます。打ち切ったときは、理由と次の一手がIssueのコメントに残ります。打ち切ったときのコメント25分で打ち切りました。Issue を小さく分けるか、受け入れ条件を絞ってください。
  8. 粗い3本目は、9月30日の試走で質問を6つ返して止まりました。夜のジョブは人に確認を求めず、決められないことは質問で残します。ai-cycle-bot受け入れ条件が決まっていないため、実装しませんでした。
  9. 翌朝、講師がMRを読み、止まった2本のコメントを読みます。オリエンテーションで見せるMRを1本決めました。講師MRは読んでマージ、止まった2本は書き直してから、次の便に載せます。
  10. 10月7日の朝、夜のスケジュールのActivatedを外します。外し忘れると、毎晩22時3分に承認済みのIssueを拾い続けます。講師夜の便を止めました。
この場面で分かること昼と夜で違うのはスケジュールだけです。承認のラベル、ターンと時間の上限、pushをスクリプトだけが持つこと、の止め金は同じです。

人が自分でpushするMR

実機の記録(10月1日、Issue #29とMR !10)。午前の1周で、受講者のDさんが手でMRを通します。

手元のPC
Claude Desktop、フック、pre-commit
人
書く、承認する、読んでマージする
Issueとボード
型、受け入れ条件、ラベル
起動
スケジュールかNew pipeline
ai_pick
承認済みのIssueを拾う
Runner
EC2の上でジョブを動かす
main
保護。マージはMaintainerだけ
MRのパイプライン
lint、test、AIレビュー、ai_scope
MR
ai-cycle-botか人が作る
AIの実装
Claudeが実装してコミット
時刻なしIssueのラベルなしMRなしAIの費用なし
  1. DさんがIssue #29を開き、assign yourselfで自分の担当にします。DさんこのIssueは私がやります。
  2. Issueの画面のCreate merge requestで、ブランチとDraftのMRを一度に作りました。ブランチ名はIssue番号から始まり、日本語の題は落ちます。GitLab29-fmt_money-8
  3. Claude Desktopでブランチに切り替え、テストを書かせてコミットします。コミットの前に、手元のpre-commitがCLAUDE.mdの行数を数えました。pre-commitOK CLAUDE.md: 55 lines (max 80)
  4. 「pushしてください」と送ると、確認のダイアログにgit pushのコマンドが出ます。ブランチ名が自分のものかを見て、許可しました。Claude DesktopPush the branch to origin の run を許可しますか
  5. pushでGitLabに届き、MRのパイプラインが1本走りました。7つのジョブが、1分ほどで緑になっています。GitLablintの3本、testの2本、ai_review、ai_gateが通りました。
  6. AIレビューが、MRにコメントを1本置きました。指摘は0件で、合否はai_gateに任せています。AIレビュー指摘はありません。判定はai_gateジョブが行います。
  7. DさんがMark as readyを押し、Draftを外しました。受講者の画面には、Mergeのボタンは出ません。MRのマージの欄Ready to merge by members who can write to the target branch.
  8. Maintainerの講師がマージしました。説明のCloses #29によって、Issueは自動で閉じます。講師差分を読んで、マージしました。
この場面で分かることコミットは手元の履歴に区切りを残すだけで、GitLabには届きません。pushで初めて届き、パイプラインが動きます。

手元の検査を飛ばしたコミット

実機の記録(10月1日、MR !11)。Eさんが、手元のpre-commitを--no-verifyで飛ばしました。

手元のPC
Claude Desktop、フック、pre-commit
人
書く、承認する、読んでマージする
Issueとボード
型、受け入れ条件、ラベル
起動
スケジュールかNew pipeline
ai_pick
承認済みのIssueを拾う
Runner
EC2の上でジョブを動かす
main
保護。マージはMaintainerだけ
MRのパイプライン
lint、test、AIレビュー、ai_scope
MR
ai-cycle-botか人が作る
AIの実装
Claudeが実装してコミット
時刻なしIssueのラベルなしMRなしAIの費用なし
  1. EさんがClaude Desktopに頼み、CLAUDE.mdの末尾に30行を足しました。85行になり、上限の80行を超えています。Eさん検査の動きを見る練習なので、あとで戻します。
  2. コミットを頼むと、手元のpre-commitが止めました。pre-commitNG CLAUDE.md: 85 lines (max 80) The commit was stopped.
  3. Eさんが統合ターミナルでgit commit --no-verifyを打つと、手元の検査は素通りしました。サーバーからは、飛ばしたことが見えません。ターミナルgit commit --no-verify -am "p7: 手元の検査を飛ばしたコミット"
  4. Eさんがpushし、このブランチからMRを作りました。ターミナルgit push -u origin HEAD
  5. MRのパイプラインで、GitLabのcheck-rulesが赤になりました。lintが赤なので、後ろのtestの段は動きません。check-rulesCLAUDE.mdが80行を超えています。
  6. マージの欄には、マージのボタンが出ません。パイプラインが通らないMRはマージできない設定だからです。MRのマージの欄Merge blocked: 1 check failed Pipeline must succeed.
この場面で分かること手元のフックは速報です。止め金はGitLabの側に置き、check-rulesのジョブ、保護されたmain、マージの条件の3つで止めます。

止め金を書き換えたAIのMR

想定の場面。Fさんの書いたIssueを読んだAIが、レビューの基準のファイルまで書き換えました。判定はci/tests/ai_scope/run.shの15通りで確かめています。

手元のPC
Claude Desktop、フック、pre-commit
人
書く、承認する、読んでマージする
Issueとボード
型、受け入れ条件、ラベル
起動
スケジュールかNew pipeline
ai_pick
承認済みのIssueを拾う
Runner
EC2の上でジョブを動かす
main
保護。マージはMaintainerだけ
MRのパイプライン
lint、test、AIレビュー、ai_scope
MR
ai-cycle-botか人が作る
AIの実装
Claudeが実装してコミット
時刻なしIssueのラベルなしMRなしAIの費用なし
  1. Fさんが「AIレビューの指摘が細かすぎるので減らしたい」というIssueを書き、承認しました。FさんNitの指摘が多くて、MRが読みにくい。
  2. ai_pickが拾い、実装ジョブが始まります。ai_pickのログ#45 AIレビューの指摘を減らす
  3. ClaudeがREVIEW.mdのNitの上限を書き換え、コミットしました。REVIEW.mdは手元のフックが守る場所ではないので、書く前には止まりません。ClaudeREVIEW.mdのNitの上限を変えました。
  4. ジョブはMRを出す前に、ci/や.claude/を変えていないかを見ます。REVIEW.mdはこの一覧に無いので、MRが出ました。implement-issue-45触らない場所の変更はありません。MRを出します。
  5. MRのパイプラインでai_scopeが赤になりました。一覧はmainの側から読むので、MRの中で書き換えても判定は変わりません。ai_scopeのログNG REVIEW.md AI を止める仕組みか約束の文書です(ci/ai_scope.paths)
  6. 赤のジョブがあるので、このMRはマージできません。GitLabパイプラインが通っていないため、マージできません。
  7. 講師がMRを閉じます。レビューの基準を変えたいときは、人がMRで変え、差分を読んで決めます。講師止め金の変更は、AIに任せず人が書く。
この場面で分かること止め金そのものをAIが書き換えると、何が止まるのかを人が説明できなくなります。そこで、止める場所を2段に分けました。ci/や.claude/は実装ジョブがMRの前に止めます。CLAUDE.mdやREVIEW.mdの変更を止めるのは、ai_scopeの役目です。

途中で外した承認

想定の場面。Gさんが承認したあとで考え直し、ラベルを外しました。

手元のPC
Claude Desktop、フック、pre-commit
人
書く、承認する、読んでマージする
Issueとボード
型、受け入れ条件、ラベル
起動
スケジュールかNew pipeline
ai_pick
承認済みのIssueを拾う
Runner
EC2の上でジョブを動かす
main
保護。マージはMaintainerだけ
MRのパイプライン
lint、test、AIレビュー、ai_scope
MR
ai-cycle-botか人が作る
AIの実装
Claudeが実装してコミット
時刻なしIssueのラベルなしMRなしAIの費用なし
  1. GさんがIssueを書き、ai::approvedを付けました。Gさん承認しました。
  2. ai_pickが拾い、子パイプラインのジョブを作ります。Runnerの枠が埋まっていて、ジョブは順番を待っています。Runner8本が動いているので、空くまで待つ。
  3. Gさんが受け入れ条件の誤りに気づき、ai::approvedを外しました。Gさんやっぱり待って。条件が1つ違っていた。
  4. 順番が来たジョブは、Claudeを呼ぶ前にIssueを読み直します。承認のラベルが無いので、実装しません。implement-issue-46のログ承認のラベル ai::approved が外れていたので、実装しませんでした。
  5. スクリプトがai::stoppedと理由のコメントを付けました。Gさんは本文を直してから、もう一度承認します。ai-cycle-bot@GさんAIの実装サイクルはMRを出さずに止まりました。
この場面で分かること承認は、拾うときと実装の直前の2回確かめます。ただし、Claudeが実装を始めたあとで外しても、その回は止まりません。

続けてpushしたブランチ

想定の場面。Hさんがpushした直後に誤りに気づき、直してもう一度pushしました。

手元のPC
Claude Desktop、フック、pre-commit
人
書く、承認する、読んでマージする
Issueとボード
型、受け入れ条件、ラベル
起動
スケジュールかNew pipeline
ai_pick
承認済みのIssueを拾う
Runner
EC2の上でジョブを動かす
main
保護。マージはMaintainerだけ
MRのパイプライン
lint、test、AIレビュー、ai_scope
MR
ai-cycle-botか人が作る
AIの実装
Claudeが実装してコミット
時刻なしIssueのラベルなしMRなしAIの費用なし
  1. Hさんがpushすると、MRのパイプライン1本目が走り始めました。Hさんpushしました。
  2. 1本目のlintが動いている間に、Hさんがテストの期待値の誤りに気づきました。Hさん期待値を1つ間違えていた。
  3. Claude Desktopで直してコミットし、もう一度pushします。2本目のパイプラインができました。Hさん直して、pushし直しました。
  4. 1本目は途中で止まり、取り消し(canceled)と表示されます。ジョブにinterruptible: trueを付け、プロジェクトの自動キャンセルを有効にしているためです。GitLab同じブランチの新しいパイプラインが始まったので、古いほうを取り消しました。
  5. 2本目だけが最後まで走り、緑になりました。MRの画面には、新しいほうの結果が出ます。GitLab2本目のパイプラインが通りました。
この場面で分かること18人が赤を直してpushし直す時間帯でも、古いパイプラインがRunnerの枠とAIレビューの費用を使い続けません。

鍵が届かなかったAIレビュー

実機の記録(10月2日、MR !12と!13)。講師がトークンを差し替えたとき、変数の保護の印が付いたままでした。

手元のPC
Claude Desktop、フック、pre-commit
人
書く、承認する、読んでマージする
Issueとボード
型、受け入れ条件、ラベル
起動
スケジュールかNew pipeline
ai_pick
承認済みのIssueを拾う
Runner
EC2の上でジョブを動かす
main
保護。マージはMaintainerだけ
MRのパイプライン
lint、test、AIレビュー、ai_scope
MR
ai-cycle-botか人が作る
AIの実装
Claudeが実装してコミット
時刻なしIssueのラベルなしMRなしAIの費用なし
  1. 講師がAI用のトークンをDeveloperのai-cycle-botに差し替え、変数GITLAB_BOT_TOKENを書き換えました。このときProtect variableにチェックが入っていました。講師トークンを差し替えた。
  2. AIのサイクルはmainで走ります。mainは保護されたブランチなので、保護の印が付いた変数も届き、MR !13まで出ました。ai-cycle-botMRを出しました。
  3. MRのパイプラインは、保護されていないai/のブランチで走ります。保護の印が付いた変数は届かず、AIレビューが落ちました。ai_reviewのログGITLAB_BOT_TOKEN: CI/CD変数 GITLAB_BOT_TOKEN が未設定です
  4. AIレビューは失敗を許す設定なので、パイプラインは止まりません。ai_gateも注意で終わり、MRにはレビューのコメントが付きませんでした。ai_gatefindings.jsonが無いので、レビューが走らなかったと判定します。
  5. 講師がSettingsのCI/CDのVariablesを開き、GITLAB_BOT_TOKENのProtect variableのチェックを外しました。講師保護の印を外しました。
  6. AIレビューのジョブをRetryすると、変数が届いて先へ進みました。当日、受講者18人のMRでAIレビューが動くのは、この直しのおかげです。ai_reviewのログ差分なし。レビューを省略します。
この場面で分かること保護の印が付いた変数は、保護されたブランチのパイプラインにしか渡りません。受講者のブランチでも使う鍵は、Protectを外したうえで、研修の後に作り直します。