diff --git a/docs/development-history/02-2026-09-03.md b/docs/development-history/02-2026-09-03.md new file mode 100644 index 00000000..791984cb --- /dev/null +++ b/docs/development-history/02-2026-09-03.md @@ -0,0 +1,96 @@ +# 開発履歴と知見 (2026-09-03) + +**期間**: 2026-09-03 +**対象**: [PR #148](https://github.com/devbasex/devbase/pull/148)(設計)/ [PR #149](https://github.com/devbasex/devbase/pull/149)(実装)/ [PR #151](https://github.com/devbasex/devbase/pull/151)(v3.2.0)/ [devbase-ext#20](https://github.com/takemi-ohama/devbase-ext/pull/20) + +「with-ai コンテナで gemini の Vertex AI 経由が使えない」という報告から入った。原因は +`containers/base/Dockerfile` が `.bashrc` へ書き込む alias が、全コンテナで +`GOOGLE_GENAI_USE_VERTEXAI=true` を無条件に前置していたことだった。Vertex AI を使わない +プロジェクトでも Vertex 経路へ倒れる。 + +利用者は Vertex ではなく OAuth で使う方針を選んだため、起動定義から認証方式の決定を外し、 +環境変数で選ぶ形にした。あわせて起動定義を Dockerfile のインライン `echo` から +`containers/base/ai-cli-aliases.sh` へ出し、Docker を起動せずに振る舞いを固定できるようにした。 + +## 何が起きたか + +### 調査中に 2 度、自分の環境の値を対象の値と取り違えた + +最初に「`GOOGLE_GENAI_USE_VERTEXAI=true` はプロジェクトの `env` にある」と読んだが、実際には +検証用に走らせた `bash -c` が**自分のシェル(`~/.bash_profile`)の値を引き継いでいた**もの +だった。次に「ホストシェルから漏れている」と読んだが、コンテナへ渡っていた +`GOOGLE_CLOUD_PROJECT` の出所は devbase の暗号化機密ストア(`secrets/global.env.age`)だった。 + +どちらも、値の**出所を確かめずに存在だけを見た**ことが原因である。環境変数は同名のものが +複数の層(ホストのシェル / 共通機密 / プロジェクトの `env` / 生成 compose)に同時に存在する。 + +### 設計レビューが 4 ラウンドかかった + +| round | 指摘 | 種類 | +| --- | --- | --- | +| 1 | `GOOGLE_CLOUD_PROJECT` の有無で認証方式を推論するのは category error | 設計の誤り | +| 2 | `devbase env set -g` は存在しない / 手動確認の欄が旧設計のまま | 事実誤認・更新漏れ | +| 3 | PR 本文が旧設計のまま残っている | 更新漏れ | +| 4 | — | 収束 | + +**round 1 は設計として拾うべき指摘だった。** Vertex AI にはプロジェクトが要る、という +前提条件から「プロジェクトがあれば Vertex」と短絡した。`GOOGLE_CLOUD_PROJECT` は gcloud も +BigQuery も使う変数で、gemini の認証方式の opt-in ではない。**必要条件を十分条件として +扱った**形になる。 + +round 2・3 は性質が違う。設計を書き換えたあと、同じ文書の他の節(手動確認の欄)と +**PR 本文**が古いまま残った。設計を差し替えるときに、その設計を参照している場所を数えて +いなかった。とくに PR 本文は文書の外にあるため、ファイルを直すだけでは追従しない。 + +### 実装のレビューは 1 ラウンドで通った + +設計が固まっていたためと読める。実装で足したのはテスト 37 件と、範囲を広げた `claudb` の +修正だけだった。 + +### テストが空振りで通っていた + +最初に書いたテストのうち 2 種類は、`ai-cli-aliases.sh` が存在せず source に失敗した状態でも +通っていた。`alias name` が空文字を返し、「`$@` を含まない」が自明に成り立つためである。 +実装前に気づいて、定義が読めていることを先に確かめる形へ直した。 + +**失敗するテストを確認する工程が、この空振りを捕まえた。** 全件が落ちることを期待して +実行し、19 件通っていたことで気づいた。 + +### 範囲を広げた判断が 1 件 + +`claudb` が `claude` の alias まで展開し、`--dangerously-skip-permissions` を 2 度渡していた。 +実コンテナで再現を確認した。書き換えている行そのものが原因で、放置するとテストが重複を +「正」として固定するため範囲に入れ、`command` を挟んで解消した。 + +### 起票を 1 件取りこぼしていた + +`GOOGLE_CLOUD_PROJECT` がプロジェクトの空上書きを無視してコンテナへ入る件を、仕様の +「対象範囲(含まない)」へ「別途切り分ける」と書いたまま**起票していなかった**。 +この振り返りで拾い、[#152](https://github.com/devbasex/devbase/issues/152) として起票した。 + +「含まない」に書いた時点では issue にしたつもりになっていた。**文書に書くことと、拾われる +場所に置くことは別である。** + +## 次に変えること + +| 変えること | 落とし先 | 状態 | +| --- | --- | --- | +| 環境変数の値を根拠にするときは、出所(どの層から来たか)まで確かめてから書く。同名の変数が複数の層に同時に存在する | 次の変更で試すこと | この記録に残す | +| 前提条件(必要条件)を判定条件(十分条件)へ流用しない。「A が無いと B できない」から「A があれば B」は導けない | 次の変更で試すこと | この記録に残す | +| 設計を差し替えたら、その設計を参照している場所を数える。同じ文書の他の節と、**文書の外にある PR 本文**を含む | 次の変更で試すこと | この記録に残す | +| 仕様の「対象範囲(含まない)」へ書いた項目は、書いた時点で `out-of-scope` を通す。文書への記載は起票の代わりにならない | プロジェクトの取り決め | この記録に残す | + +失敗するテストを目で確認する工程は変えない。空振りで通るテストを、実装前に捕まえられた。 + +## 途中で起票した課題 + +| 番号 | 何を見つけたか | 見つけた場面 | +| --- | --- | --- | +| [#152](https://github.com/devbasex/devbase/issues/152) | プロジェクトの `env` による共通機密の打ち消しが効かないことがある。`_project_env_overrides()` が `os.environ` から値を取るため、ラッパーが `env` を載せていない経路では 1 件も効かない | 調査中に発見し、**振り返りで起票の取りこぼしとして拾った** | + +`gh issue list --state all --search "issue #139"` で引ける #141 / #142 / #146 は前回の変更の +ぶんで、この変更からは #152 の 1 件である。 + +`quality-gates` の完了報告に挙げた「範囲外と判断したもの」は `GOOGLE_CLOUD_PROJECT` の件のみで、 +上記のとおり起票した。レビューの指摘 4 件はすべて修正しており、範囲外として resolve した +ものは無い。