---
title: "GroeneAI のご紹介: ClickHouse の自律型コントリビューター"
date: "2026-10-30T06:44:17.579Z"
author: "James Cunningham, Artem Brustovetskii and Alex Soffronow Pagonidis"
category: "Engineering"
excerpt: "7か月足らずで3,000件以上のマージ済みプルリクエストに貢献した、ClickHouse の自律型エンジニアリングエージェント「GroeneAI」をご紹介します。"
---

# GroeneAI のご紹介: ClickHouse の自律型コントリビューター

## 現場で見つかる CI 障害の調査

ソフトウェアを書くこと自体も時に困難ですが、それを一日のボトルネックだと感じたことはないはずです。自身のテストを実行し、機能を確認し、変更を提出し、同僚がコードについてどう考えるかを辛抱強く待った末に…… CI が失敗します。

しかし調べてみると、自分自身のバグではなく、変更とはまったく関係のない障害でした。パッチの作業を中断し、以前の CI 実行を検索し、障害を再現した上で、問題がデータベース側にあるのか、テスト側にあるのか、あるいは実行環境側にあるのかを判断しなければなりませんでした。ジョブを再実行すると、多くの場合パスしてしまいます。根本的な問題は放置されたままとなり、次のコントリビューターが再び直面することになります。

誰かがその作業を行わなければなりません。そして 2026年3月以降、ClickHouse のコードベースにおいて、その「誰か」は人間ではないケースが頻繁にあります。

私たちは、そうした調査作業を引き受けるために [GroeneAI](https://github.com/groeneai) を構築しました。24時間365日稼働する、完全自律型の AI エンジニアリングエージェントです。障害を再現し、修正コードの作成とテストを行い、自身のプルリクエストが CI とレビューを通過するまでフォローします。「Groene」はオランダ語で「緑」を意味し、「CI をグリーンに保つ」という当初の目標に由来しています。

GroeneAI は 3月25日に最初の [ClickHouse の issue](https://github.com/ClickHouse/ClickHouse/issues/100708) を起票し、その 72 分後に最初の [プルリクエスト](https://github.com/ClickHouse/ClickHouse/pull/100715) を作成して以来、活動を止めずに続けています。エージェントは開発プロセス全体を、人を介さずに自律して実行します。作業を選択し、原因を調査し、修正を設計・実装し、ClickHouse をビルドし、テストを実行して、プルリクエストを作成します。これらの手順に人が手を下すことはなく、ほとんどの場合、作業の選定すら人が行いません。GroeneAI は作業の大半を CI の結果、ファザーによる検出結果、ユーザーからの報告から自ら見つけ出します。メンテナーが単一のコメントでバグを指示することも可能です。それ以外の場合、人間の関与はレビューから始まります。メンテナーは変更を受け入れるか、あるいは好みに応じて GroeneAI に修正方針を指示するかを判断します。

その後のフォローアップ作業も自律して行われます。GroeneAI は CI を監視し、レビューコメントに回答し、実装を修正し、リポジトリの進行に合わせて変更をリベースします。1 つのタスクは複数のセッションにまたがってアクティブな状態を維持でき、調査内容と次のステップは全体を通して保持されます。

GroeneAI は障害を調査するにつれて、コードベース全体の構造を学習していきました。根本原因、失敗したアプローチ、レビュアーからのフィードバックを永続メモリに保存し、それらの教訓を以降のタスクで活用します。そのループにより、SQL エンジン、ストレージ、クエリプランニング、そしてそれらを取り巻くテストインフラにわたる正当性やパフォーマンスの問題へと対応範囲が広がっていきました。その活動範囲は、長くとどまることなく CI 以外の領域へと拡大していったのです。

2026年10月までに、GroeneAI は ClickHouse に 3,000 件以上のマージされたプルリクエストを貢献しました。10月19日までの 3 か月間において、自身が作成した非マージコミット数で、ClickHouse 共同創業者兼 CTO である [Alexey Milovidov](https://clickhouse.com/company/our-story) に次ぐ、ClickHouse コントリビューター中第 2 位にランクインしました。

![ClickHouse/ClickHouse における月別アクティビティ。作成、マージ、クローズの各イベントが発生した月でカウント。10月は 1 日から 25 日までのみを含む。](https://clickhouse.com/uploads/groeneai_monthly_activity_019fdadb4a.png)

ClickHouse/ClickHouse における月別アクティビティ。作成、マージ、クローズの各イベントが発生した月でカウント。10月は 1 日から 25 日までのみを含む。

このエージェントは、私たちのオープンソースのエージェントランタイムである [Nerve](https://github.com/ClickHouse/nerve) 上で動作します。Nerve は永続セッション、メモリ、ツール、スケジュール実行機能を提供します。GroeneAI は、そこに ClickHouse 向けに構築された開発ワークフローを追加しています。そのワークフローが現在の形に至った経緯を理解するには、ある 1 つのバグを取り上げるのが最適です。

私たちの小さなロボットが生み出したすべての変更を誇りに思っていますが、特に目覚ましい活躍を見せた特定の月をいくつかご紹介します。

## 6月: 誤った内容を記憶していたキャッシュ

6月3日、GroeneAI は[自身の DDL 変更に対する CI を監視](https://github.com/ClickHouse/ClickHouse/pull/106192#issuecomment-4612881667)していましたが、その最中、無関係なテストである `03047_on_fly_update_delete` が S3 ストレージ上の分散クエリプランで失敗しました。この障害はフレーキーなものに見えました。再実行すればパスしたはずです。しかし GroeneAI は再実行ではなく再現を試み、その再現作業の中でバグが姿を現しました。

[クエリ条件キャッシュ](https://clickhouse.com/docs/ja/concepts/features/performance/caches/query-condition-cache)は、データパートのどの範囲にフィルターに一致する行が含まれていないかを記憶し、同じ条件の以降のクエリがそれらの範囲をスキップできるようにします。このエントリは、フィルターが実際のデータを参照した場合にのみ信頼できます。別の操作によって先に行が削除または変更されていた場合、空の結果が意味するのは、格納されているデータではなく、その操作による影響です。

[調査](https://github.com/ClickHouse/ClickHouse/pull/107145#issuecomment-4677062740)により、フレーキーに見えていた原因はランダム化されたインデックスの粒度にあることが判明しました。グラニュールが大きい場合、一致する行が 1 行でも残っていれば範囲全体が読み取り対象となり、バグが隠れていました。グラニュールあたり 1 行の場合、誤ったスキップの判断が毎回再現しました。不足していたガードは古くから存在していたものでした。前年 3 月にマージされた[クエリ条件キャッシュの初期実装](https://github.com/ClickHouse/ClickHouse/pull/69236)にまで遡り、そこでは `PREWHERE` の前にミューテーションを適用していたものの、それらのミューテーションが行を変更したかどうかを確認せずに、結果として得られた空の範囲を記録していました。

[リグレッションテスト](https://github.com/ClickHouse/ClickHouse/blob/b7182f6c45469181b66158e293e1d5c4f6100755/tests/queries/0_stateless/03229_query_condition_cache_on_fly_mutations.sql)がその動作を具体的に示しています。このテストでは、`id` と `v` がいずれも 0 から 99 まで連続する 100 行を作成します。バックグラウンドマージを停止し、`id >= 50` の行に対して `v` を 0 に設定する非同期 UPDATE を発行します。元のパートは引き続き利用可能であるため、クエリは読み取り時に保留中のミューテーションを適用するかどうかを選択できます。キャッシュを有効化してクリアした後、テストは 2 回の読み取りを実行します。

```sql
SELECT count() FROM t_qcc_on_fly_update
PREWHERE v >= 50 SETTINGS apply_mutations_on_fly=1;

SELECT count() FROM t_qcc_on_fly_update
PREWHERE v >= 50 SETTINGS apply_mutations_on_fly=0;
```

1 つ目のクエリは、更新によって一致するすべての値が変更されるため、0 を返すべきです。2 つ目のクエリは、元の値を読み取るため、50 を返すべきです。修正前は、1 つ目のクエリがキャッシュエントリを書き込んでしまい、2 つ目のクエリが一致する行をスキップする原因となっていました。

| 読み取り | 修正前 | 期待値および修正後 |
| --- | --- | --- |
| 保留中の更新を適用 | 0 | 0 |
| 元の値を読み取り | 0 | 50 |

PR の単一レプリカのリグレッションフィクスチャにおける UPDATE ケースの記録結果(`index_granularity = 1` かつ `mutations_sync = 0`)。

読み取り側はすでに配慮されており、ミューテーションがアクティブな場合はキャッシュされた結果を避けていました。不足していたのは書き込み側の保護でした。[PR #107145](https://github.com/ClickHouse/ClickHouse/pull/107145) では、`PREWHERE` の前に[ミューテーションステップまたはパッチパートのチェック](https://github.com/ClickHouse/ClickHouse/blob/b7182f6c45469181b66158e293e1d5c4f6100755/src/Storages/MergeTree/MergeTreeReadTask.cpp#L464-L476)を行うことで、両方のキャッシュ書き込みパスを保護しました。これらの変換がアクティブな場合、[読み取りプロセッサ](https://github.com/ClickHouse/ClickHouse/blob/b7182f6c45469181b66158e293e1d5c4f6100755/src/Storages/MergeTree/MergeTreeSelectProcessor.cpp#L272-L325)は `PREWHERE` からのキャッシュ更新を抑制し、後続の `WHERE` フィルターがエントリを書き込む原因となるメタデータを差し止めます。行レベルセキュリティにも同様のガードが適用され、一方のユーザーには `v < 50` へのアクセスを許可し、もう一方には常に真となるポリシーを付与する [2 ユーザーのリグレッションテスト](https://github.com/ClickHouse/ClickHouse/blob/b7182f6c45469181b66158e293e1d5c4f6100755/tests/queries/0_stateless/03229_query_condition_cache_row_policy.sh) が追加されました。これらの変換を含まないクエリでは、最適化が引き続き維持されます。

プルリクエストのスレッドは、次節で述べるパイプラインが存在する前の 6 月時点において、GroeneAI がどのように機能していたかを示しています。GroeneAI は検証内容の表を添えて 6月11日に [PR #107145](https://github.com/ClickHouse/ClickHouse/pull/107145) をオープンしました。同日中に 4 件のレビュー指摘が届きました。`WHERE` パスがまだガードされていないこと、行ポリシーにも同様のガードが必要であること、パッチパートのケースにテストがないこと、そしてそのテストが非決定論的であることでした。GroeneAI はそれぞれを修正し、修正ごとに表を再投稿し、12 時間の間に 5 回更新しました。8 日後、[alesapin が](https://github.com/ClickHouse/ClickHouse/pull/107145#issuecomment-4752368866) Lightweight Mutation の下でこの変更が `PREWHERE` を無効化してしまうのではないかと質問しました。GroeneAI は [31 分で回答](https://github.com/ClickHouse/ClickHouse/pull/107145#issuecomment-4752601585)しました。抑制されるのはキャッシュへの書き込みのみであり、もしそちらを希望するならミューテーションの状態に基づいてキャッシュのキーを設定する代替案もあると伝えました。彼はその 33 分後に承認しました。

この変更は 6月19日にマージされました。そのテストは、更新、削除、パッチパート、行ポリシー、そして明示的に `WHERE` に残された述語をカバーしています。あらゆるケースにおいて 1 つのルールが適用されます。フィルターが「ある範囲に一致する行が含まれていない」と記録できるのは、そのキャッシュエントリが表す対象のデータを評価した場合のみである、というルールです。

よくやった、ロボット。

## 7月: 1 つの調査がプロセスへと進化

キャッシュの修正には、テストの失敗からマージまでに 16 日間を要しました。ビルド、夜間の CI 実行、数日後に戻ってくるレビュアーとのやり取りがすべてその期間内に含まれます。セッション間で記憶を失ってしまうエージェントはこの作業をまったく遂行できず、誤った内容を記憶するエージェントは粗悪な作業しかできません。6 月の修正を経た 7 月、現在のワークフローでも使用されているパイプラインを導入し、新たな障害によって未対応のケースが発覚するたびにチェックの改善を重ねてきました。

インテークはイベントから始まります。GitHub の通知を保存し、受信トレイ内における各コンシューマーの処理位置を追跡します。続いて GroeneAI の受信トレイプロセッサが、対応可能なメッセージをタスクに関連付けます。独立したスケジュールスキャンにより CI の障害を検査し、アカウントの参加通知に一度も表示されなかった障害も拾い上げます。

生成されたタスクは永続化されます。Nerve が Markdown コンテンツを保存し、SQLite でインデックスを作成するためです。GroeneAI は、フェーズ、ブランチ、コミット、ビルド識別子、完了したテスト、エビデンスの保存場所、次のアクションを記録するチェックポイントを追加します。以降のセッションでは、前回の会話を維持し続けなくても、その状態を復元できます。

![Nerve がタスクを保持し、実行をスケジュールする。GroeneAI のワークフローがフェーズ間のハンドオフを制御する。修正内容は実装へと差し戻され、CI やメンテナーからのフィードバックによって公開後の作業を再開できる。](https://clickhouse.com/uploads/groeneai_nerve_lifecycle_a688ba89ee.png)

Nerve がタスクを保持し、実行をスケジュールする。GroeneAI のワークフローがフェーズ間のハンドオフを制御する。修正内容は実装へと差し戻され、CI やメンテナーからのフィードバックによって公開後の作業を再開できる。

キャッシュの修正事例は、なぜその状態管理が重要であるかを示しています。再開されたワーカーは、どの読み取りパスがすでにガードされているか、どのテストが `WHERE` を検証しているか、パッチパートのテストが更新完了を待機しているかどうかを把握する必要があります。チェックポイントが関連する成果物を指し示すことで、完了した変更と未解決のテスト条件の違いが維持されます。

メモリは異なるタスクをまたいで情報を伝達します。短い運用ルールはセッションコンテキストに入ります。より長文の調査内容は関連時に読み込まれるファイルに保存され、セマンティックリコールによって過去の知見や手順を取得します。スキルは、ClickHouse のビルドや CI 障害の調査といった作業向けの再利用可能な指示を提供します。

保存された情報は古くなる可能性もあり、ワークフローはその前提で扱います。GroeneAI のビルド手順では、メモリ内にコンパイラバージョンが保持されている場合でも、現在の CI コンパイラ構成と CMake キャッシュの確認が求められます。記憶された教訓は、現在のチェックアウト環境で検証すべきエビデンスをワーカーに指示する役割を果たします。これらのランタイムプリミティブについては、[Nerve タスクのドキュメント](https://github.com/ClickHouse/nerve/blob/main/docs/tasks.md)、[ソース取り込みのドキュメント](https://github.com/ClickHouse/nerve/blob/main/docs/sources.md)、[メモリのドキュメント](https://github.com/ClickHouse/nerve/blob/main/docs/memory.md)で説明されています。

このランタイム上で、ワークフローは新しいコントリビューションを異なる役割を持つ 3 つのフェーズに分割します。

プランニングワーカーが報告内容を調査し、ソースコードや既存の修正を確認し、再現手順を含めたアプローチを作成します。別のモデルがその計画をレビューします。報告が重複である場合、再現できない場合、あるいはメンテナーの判断が必要な場合は、作業をここで停止できます。

続いて実装ワーカーが、タスクのチェックアウト環境で変更を加えます。その指示では、リグレッションテストがパッチなしで失敗し、パッチ適用でパスするというエビデンスが求められます。計画の前提が成り立たない場合、ワーカーは実行した内容を記録し、タスクをプランニングフェーズに差し戻します。

新しいレビューセッションが、実装者の検証内容を読む前にコードを検証します。変更された関数、その呼び出し元、関連する実装を検査した上で、テスト内容と提案されたプルリクエストの説明を突き合わせる必要があります。GroeneAI はメインのワーカーセッションに Claude を使用し、アプローチおよび結果として得られた変更の独立したレビューには Codex を使用します。レビュアーはパッチに対する独自の解説を構築し、それを実装のエビデンスと比較します。承認された実質的な指摘事項は、実装への修正指示となります。異議のある指摘事項には、その見解の不一致を説明するコードやテストのエビデンスが必要となります。

ワークフローは、どのリビジョンがレビューされたかもチェックします。通常のプッシュ前のパスでは、提案されたコミット、ツリー、または完全なパッチの識別情報に対して、第 2 モデルによるレビューが完了して記録されていることが求められます。指摘事項には引き続き判断が必要であり、古いバージョンに対するレビューによって、その後のコード変更が暗黙のうちに承認されてはなりません。

## 8月: プロセスの本格稼働

8 月の 2 つの変更は、このプロセスが実際に機能し、真に自律して目覚ましい成果を上げ始めた様子を示しています。1 つ目は、メンテナーがマージする前に GroeneAI が安全性を証明しなければならなかったプランナーの最適化であり、2 つ目は、エンジニアが他者の変更を判断する際に参照する CI レポートの修正です。

ロボットの働きぶりを見てみましょう。

### 1 つ目: UNION DISTINCT の並列処理化

[PR #115173](https://github.com/ClickHouse/ClickHouse/pull/115173) において、GroeneAI は 2 つの等価なクエリ形式が異なるパイプラインを生成し、人間が日常的に書く形式の方が低速であることに気づきました。変更内容自体は小さなものです。このプルリクエストが記録している価値は、GroeneAI がいかにしてその妥当性を立証したかにあります。マージに先立ち、重複密度とスレッド数を網羅した[逆のケースの測定](https://github.com/ClickHouse/ClickHouse/pull/115173#issuecomment-5399220630)を行い、16 スレッドの入力で 22% 低速化し、`max_threads = 1` でペナルティが最大になることを見出し、1 スレッド時には従来の計画を維持するガードを追加しました。その後、自身による[第 2 モデルのレビュー](https://github.com/ClickHouse/ClickHouse/pull/115173#issuecomment-5318654935)が最初のリグレッションテストを却下しました。事前ステップを無効化してもプランラベルが残っていれば、存在確認のみを行うアサーションを通過してしまうためです。現在のテストは、厳密に 2 つの予備プランステップと 3 つの `DistinctTransform` をアサートし、いずれかのプランナーでフラグを意図的に反転させるとテストが失敗するようになっています。

2 つの形式とは、すべての行を 1 つの最終的な重複排除ステップに送る `UNION DISTINCT` と、各ブランチ内で先に重複を並列に削除する `UNION ALL` をラップした外部 `SELECT DISTINCT` でした。このパッチは、有効スレッド数が 1 より大きい場合に `UNION DISTINCT` に対する[事前重複排除ステップ](https://github.com/ClickHouse/ClickHouse/blob/2848e3ee55e51800a4bf0b7a609de7f26c6e2d93/src/Planner/Planner.cpp#L2105-L2131)を追加し、それを各ブランチにプッシュダウンしつつ、異なるブランチが同一の値を保持する可能性があるため最終ステップも維持します。両方のプランナー実装が更新されました。`INTERSECT` と `EXCEPT` は既存の計画を維持します。

![ブランチローカルの重複排除により、最終的な distinct ステージに入る行数が削減される。最終ステージでは、異なるブランチ間で共有される重複が引き続き削除される。](https://clickhouse.com/uploads/groeneai_union_distinct_82a6c5bf02.png)

ブランチローカルの重複排除により、最終的な distinct ステージに入る行数が削減される。最終ステージでは、異なるブランチ間で共有される重複が引き続き削除される。

プルリクエストは、リリースビルドにおけるこの実験結果を報告しています。各ブランチ 800 万行、ユニーク値 2,000 件、7 回実行したうちの最速値です。

| `max_threads` | 変更前 | 変更後 |
| --- | --- | --- |
| 1 | 60 ms | 61 ms |
| 8 | 44 ms | 13 ms |
| 16 | 59 ms | 9 ms |

この表は、この変更が想定した重複の多いケースを示しています。この網羅的測定があったからこそ、`max_threads = 1` におけるガードが存在し、すべての入力に対して高速化を主張していない理由となっています。この変更は、クエリ結果と実行プランの構造をカバーする[リグレッションテスト](https://github.com/ClickHouse/ClickHouse/blob/2848e3ee55e51800a4bf0b7a609de7f26c6e2d93/tests/queries/0_stateless/04951_union_distinct_preliminary_distinct.sql)とともに、8月27日にマージされました。

### 2 つ目: CI 障害の判読性向上

2 つ目の変更は、GroeneAI 自身が依存しているツールに対するものです。[PR #114451](https://github.com/ClickHouse/ClickHouse/pull/114451) は、サマリーではパスしているにもかかわらず詳細に 22 件の赤い行が表示されていた性能比較レポートに対処しました。GroeneAI は判定自体には手を加えませんでした。11 件の不安定なクエリを含むパス状態のシャードと、赤色を維持すべき失敗状態のシャードという 2 つの実際のレポート成果物からリグレッションテストを作成し、この修正が表示のみを変更することを検証しました。

このレポートは、すべての詳細行にハードコードされた失敗ステータスを割り当てており、各比較のベースライン側と候補側の両方を表示していました。[結果ビルダーの変更](https://github.com/ClickHouse/ClickHouse/blob/883eb8e9f09c273eb6cfca435d388882064db0f0/ci/jobs/performance_tests.py#L649-L684)により、実際のクエリ判定がレポートに反映され、CI データベースに保存された行と履歴リンクを維持したまま、候補側を優先して各比較が 1 回ずつ表示されるようになりました。[リグレッションテスト](https://github.com/ClickHouse/ClickHouse/blob/883eb8e9f09c273eb6cfca435d388882064db0f0/ci/tests/test_perf_check_results_children.py)は、パス状態のシャードが 11 行の不安定な行を出力し、失敗状態のシャードがその失敗ステータスを維持することを検証しています。この変更は 8月17日にマージされました。

## 9月: 1 件のインジェクションから改善の連鎖へ

9 月には焦点がセキュリティへと移りました。その月にマージされたプルリクエストのうち 5 件が SQL インジェクションを解消するものであり、それぞれが次の修正へとつながっていきました。1 件目の [PR #108469](https://github.com/ClickHouse/ClickHouse/pull/108469) は、6月25日からオープンになっていたものです。GroeneAI は、PostgreSQL ワイヤプロトコルがプリペアドステートメントのパラメータを生の文字列として SQL テキストに直接挿入していることを突き止めました。そのため、`x' UNION ALL SELECT ... --` のような値がリテラルからエスケープできてしまっていました。9月2日、メンテナーの 1 人が COPY 識別子のクォートと CancelRequest の修正という、彼自身の 2 つのコミットを GroeneAI のブランチにプッシュしました。翌朝、彼は GroeneAI にそれらのレビューを依頼しました。

![PR #108469: 2026年9月3日のメンテナーからの依頼と、彼のコミットに対する GroeneAI のレビューの冒頭部分。](https://clickhouse.com/uploads/groeneai_pr_108469_handover_a91fc4ec87.png)

PR #108469: 2026年9月3日のメンテナーからの依頼と、彼のコミットに対する GroeneAI のレビューの冒頭部分。

GroeneAI は [1 時間で回答](https://github.com/ClickHouse/ClickHouse/pull/108469#issuecomment-5523227317)しました。デバッグビルドで両方の修正を確認した上で、ブロッキングではないとしつつ 3 点の不備を指摘しました。新しいキャンセルキーが非暗号論的なジェネレータから生成されていること、キーをクレデンシャルとして固定するテストがないこと、COPY FROM のテーブル名がインジェクションの対象経路の中で唯一検証されていないことでした。Antonio は 1 つ目の不備に自ら対応し、`RAND_bytes` から取得したキャンセルキーを含む 4 つのコミットを追加してプルリクエストを戻しました。数時間のうちに GroeneAI は[残る 2 つの不備を解消](https://github.com/ClickHouse/ClickHouse/pull/108469#issuecomment-5526760414)し、さらに 2 つの欠陥を独自に見つけ出しました。キャンセルのシークレットがクエリ ID に表示されていたことと、2 つのリスナーが同じ接続 ID を割り当てる可能性があったことです。この変更はオープンから 71 日後の翌朝にマージされました。翌日、もう 1 つの ClickHouse エージェントである ClickGap がマージされたコードにリグレッションを発見した際、GroeneAI は 2 時間以内に[修正コード](https://github.com/ClickHouse/ClickHouse/pull/118210)を提出しました。

このマージによって、同種の脆弱性の対応が終わったわけではありませんでした。4 日後、[PR #111516](https://github.com/ClickHouse/ClickHouse/pull/111516) が PostgreSQL メタデータクエリ内の文字列リテラルのシンク(注入先)を塞ぎました。その翌日、ClickGap は識別子のシンクがまだ開いたままであることを[報告](https://github.com/ClickHouse/ClickHouse/issues/118954)しました。コードが二重引用符をバックスラッシュでエスケープしており、PostgreSQL が `""` のみを有効とみなすため、`a"; CREATE TABLE t(x int); --` というリモートテーブル名が PostgreSQL サーバー上で実行されてしまう状態でした。GroeneAI は同日夕方に [PR #119103](https://github.com/ClickHouse/ClickHouse/pull/119103) をオープンし、最後の段落に読み取りパスの残課題を記録して、翌日その残課題に対する [PR #119216](https://github.com/ClickHouse/ClickHouse/pull/119216) をオープンしました。[PR #117421](https://github.com/ClickHouse/ClickHouse/pull/117421) では SQLite エンジンに対して同様の分析を適用しました。そこでのレビューでは組み込みライブラリ内にデータ競合が見つかり、修正にはまず別のリポジトリでの[1 行の変更](https://github.com/ClickHouse/sqlite-amalgamation/pull/3)が必要となりました。各プルリクエストは直前のものを明記し、それぞれの説明文で何が未解決のまま残されているかを提示しています。

このリストの中で最も古い項目はインジェクションではありませんでした。2022年3月、Alexey Milovidov は [issue #35504](https://github.com/ClickHouse/ClickHouse/issues/35504) を起票していました。`clickhouse-client` がサーバー証明書のドメイン名を検証していないという問題です。[PR #116724](https://github.com/ClickHouse/ClickHouse/pull/116724) は、それが `SSLManager.cpp` 内の 1 つのリテラル、すなわちすべてのアウトバウンド TLS 接続において任意のホストに対する任意の CA 署名証明書を受け入れてしまう `false` というデフォルト値に起因することを突き止めました。修正ではクライアントに対してこのチェックを有効にし、サーバーのデフォルトは維持し、ループバックアドレスを除外して、従来の挙動に戻すための 2 つの設定をドキュメント化しました。Alexey は起票から 4 年半後の 9月20日にこれを承認し、4 つのリリースブランチにバックポートされました。

## 10月:メンテナーからの 1 文

GroeneAI の仕事の多くは、テストの失敗、ファザーによる検出、Issue トラッカーへの報告といった、誰も手入力していないシグナルから始まります。ときには人間から始まることもあります。9月28日、ある外部ユーザーから、ローカルの ClickHouse をソースとする辞書(Dictionary)が、ロード時にユーザーが保持していたロールを維持し続けてしまうという報告がありました。後から付与されたロールが反映されず、更新のたびに `ACCESS_DENIED` で失敗し、誰かがリロードするまで辞書が古いデータを提供し続けていました。1 週間後、あるメンテナーがその報告を読み、対応を引き渡しました。

![Issue #122663: メンテナーのリクエストと GroeneAI による 2 件の返信(いずれも 2026年10月5日)](https://clickhouse.com/uploads/groeneai_issue_122663_9780945053.png)

Issue #122663: メンテナーのリクエストと GroeneAI による 2 件の返信(いずれも 2026年10月5日)

そのメンションは他の通知と同様にインボックスへ届き、タスク化されました。GroeneAI はリリース済みの 26.8.10.6 バイナリでバグを再現し、2023年の変更以降、ユーザーのデフォルトロールを固定リストとして保持していたコンテキストに原因を突き止め、7 時間以内に [PR #124051](https://github.com/ClickHouse/ClickHouse/pull/124051) をオープンしました。修正は 2 つのソースファイルにわたる 17 行です。テストは 108 行で、master 上では実行するたびに失敗するものでした。

このスレッドは、前節までに挙げた教訓が実際に活かされていることを示しています。GroeneAI はプルリクエストに 2 件のコメントを残しましたが、いずれもデフォルトで折りたたまれていました。ブロッキング判定がゼロであった第 2 モデルによるレビューと、検証ゲートのコメントです。パイプラインが公開前にそれらのチェックを実行したため、表の再投稿はありません。[メンテナーから](https://github.com/ClickHouse/ClickHouse/pull/124051#discussion_r4218996610)「他でもこれが必要か確認してほしい」と依頼されると、GroeneAI はリモート辞書ソースから Definer ビュー、HTTP セッションに至るまで、ClickHouse ユーザーとしてクエリを実行する他のすべての箇所を調査し、3 時間足らずで[表形式で回答](https://github.com/ClickHouse/ClickHouse/pull/124051#discussion_r4220613248)しました。メンテナーはその 21 分後に承認し、最初の 1 文から 3 日後の同日午後にマージされました。

## 数字で見る 6 か月間

2026年10月25日までに、GroeneAI は `ClickHouse/ClickHouse` で 4,400 件以上のプルリクエストをオープンしました。そのうち 3,100 件以上がマージされ、約 750 件がオープンのままで、500 件近くがマージされずにクローズされました。最終判断に至ったプルリクエストのうち、86.5% がマージされています。

月間マージ数は 4 月の 100 件弱から 9 月には 700 件近くまで増加し、10 月の最初の 25 日間だけで 800 件以上が追加されました。これには、バグ修正としてラベル付けされた 1,300 件以上のマージ済みプルリクエストが含まれており、CI およびテスト関連とほぼ同数で、パフォーマンス改善向けが約 150 件あります。23 件には重大なバグ修正(critical-bug-fix)ラベルが付いています。

![10月25日時点のマージ済みプルリクエストにおける主な Changelog ラベル。ラベルは重複する場合があるため、これらの数値を単純に合算することはできません。](https://clickhouse.com/uploads/groeneai_changelog_labels_7104d3b720.png)

10月25日時点のマージ済みプルリクエストにおける主な Changelog ラベル。ラベルは重複する場合があるため、これらの数値を単純に合算することはできません。

マージされたプルリクエストは、GitHub の Issue クローズリンクを通じて 1,000 件以上の個別の Issue を参照しています。また、Issue から始まっていない作業、たとえば新たなリグレッションのテストカバレッジ、より分かりやすい CI レポート、別の障害の調査中に見つかった改善なども含まれています。

マージされたパッチの中央値は、3 ファイルにわたる 67 行の変更です。前述の例もこのパターンに当てはまります。読み取りパスの前提の修正、欠落していたプランナーステップの追加、誤解を招く CI 結果の修復、あるいはパラメータのデータとしてのバインドなどです。小規模なパッチであっても、特に元の障害を再現可能にするテストを伴う場合、広く使用されている操作を確実に保護できます。

## 最も重要な点:レビュアーからの学習

プルリクエストをオープンしただけでは作業は完了しません。チェックが失敗したり、ベースブランチが進んだり、メンテナーから別のアプローチを求められたりすることがあります。GroeneAI はタスクを保持し、それらのイベントをメンテナンス用や人間対応用のワーカーにルーティングします。

そのフィードバックは、次の課題へのアプローチ方法も変えます。調査中、根本原因、再現手順、失敗したアプローチ、レビュアーからの指示を永続メモリに保存します。新しいタスクは関連する履歴の想起から開始されるため、現在の会話には含まれていない過去の知見を活用できます。

GroeneAI は毎週、変更要求、却下されたプルリクエスト、および一部のマージ済みコントリビューションに対する人間のフィードバックを精査します。それらの結果を `REVIEW-LESSONS.md` に集約し、元のプルリクエスト、レビュアーの判断、適用すべき教訓を記録します。実装、レビュー、実質的な返信に関する指示では、いずれも作業前にこのドキュメントを読むことが求められます。

保存された教訓の中には、明確なきっかけを持つものがあります。[PR #109738](https://github.com/ClickHouse/ClickHouse/pull/109738) では、レビュー中に追加のケースをカバーするよう修正が拡大したにもかかわらず、説明文には依然として元のバグのみが記載されていました。レビュアーの指摘は、問題を最も簡潔に言い表したものでした。

![PR #109738 における Avogar によるレビュー(2026年7月23日)](https://clickhouse.com/uploads/groeneai_pr_109738_review_18d53880f7.png)

PR #109738 における Avogar によるレビュー(2026年7月23日)

どちらの指摘も教訓となりました。1 つ目は、パッチが約束する内容の正確な記述とコードおよびテストを後続のレビューで照合できるよう、説明文には最終的な変更全体を反映しなければならないという指示になりました。2 つ目の教訓の習得にはより時間がかかりました。6 月、GroeneAI はあるプルリクエストで検証用の表を 5 回再投稿していました。9 月には、[PR #117727](https://github.com/ClickHouse/ClickHouse/pull/117727) でメンテナーが「多数のエージェントコメントの合間にこれを読んでいるなら」とコメントを始めました。GroeneAI の返答はその指摘を認めるものでした。

![PR #117727 における GroeneAI の返答(2026年9月2日)](https://clickhouse.com/uploads/groeneai_pr_117727_reply_ba2470a9c4.png)

PR #117727 における GroeneAI の返答(2026年9月2日)

その返答は恒久的な指示となりました。以降、GroeneAI のプルリクエストコメントには、生の Markdown を開かない限り読者には見えない注記が含まれるようになりました。

> REVIEW-LESSONS 恒久的指示:2 名のメンテナーから、当フリートのコメント量がレビュアーの負荷になっているとの指摘がありました(@rschu1ze #117727、@Algunenano #117619)。投稿するのは折りたたまれたこの 1 件のコメントのみです。a-i 検証表は意図的に投稿しません。
出典: [PR #120259](https://github.com/ClickHouse/ClickHouse/pull/120259#issuecomment-5686386824)、生の Markdown 内の非表示 HTML コメント。
> 

GroeneAI はまた、自身が却下した自動検出結果と、その後の人間のレビューを比較します。エージェントが一蹴した問題をメンテナーが提起した場合、以前の判断とその後の訂正の両方を記録します。メモリには過去の判断がなぜ失敗したかが保持されるため、その訂正は問題が発生した単一のプルリクエストを超えて活用できるようになります。

CI の修正においては、マージが終わりではありません。GroeneAI はマージ後も `verifying` のタスク状態を維持し、スケジュールされたワーカーが `master` 上の後続の失敗をクエリします。指示では、タスクをクローズする前に報告された失敗なしで少なくとも 2 日間の master 実行を求めており、失敗が続く場合は調査に戻されます。マージの決定と、元の失敗シグナルに対するその後の検証は、別個のステップとして維持されます。

## これによって何が可能になるのか？

GroeneAI のコントリビューションは、データベースエンジニアリングの複数の領域に及んでいます。正確性の修正はクエリ結果を保護します。実行の改善は並列処理の活用度を高めます。CI の変更はコントリビューターが失敗の原因を把握するのを助け、リグレッションテストはそもそもバグを露呈させた条件を恒久的に維持します。

長時間のビルド、遅延する CI 結果、数日間に及ぶレビューのやり取りを越えてこれを可能にしているのが、常時稼働です。Nerve が調査を保持し、新たな証拠が得られた際に GroeneAI がそこに戻ります。永続メモリによって、次のタスクはそれ以前の作業から得られた教訓を活かして開始できます。

その成果は、ClickHouse のコードとテストに明確に表れています。7 か月足らずの間に、自律型エージェントから 3,000 件以上のマージ済みコントリビューションがもたらされました。断続的な失敗を決定論的なリグレッションテストに変えることができます。プランナーステップの不足をクエリの高速化に変えることができます。レビューでの訂正を再利用可能なエンジニアリングの教訓に変えることができます。公開されている [Issue とプルリクエスト](https://github.com/ClickHouse/ClickHouse/pulls?q=is%3Apr+author%3AgroeneAI) は、そのプロセスが日々継続していることを示しています。

私たちが作成したコンポーネントから自律型の同僚を構築したいとお考えの場合、[Nerve](https://github.com/ClickHouse/nerve) というセルフホスト型ランタイムがオープンソースとして公開されています。新しい活用方法についてのフィードバックをお待ちしています。

あるいは、GroeneAI のようなロボットと一緒に働きたいとお考えでしたら、ぜひ[採用情報ページ](https://clickhouse.com/company/careers)をご覧ください。ClickHouse では、AI と自律性の力を活かして、これまで手の届きにくかった箇所のクリーンアップを軽々と成し遂げられる人材を常に求めています。