Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 96 additions & 0 deletions docs/development-history/02-2026-09-03.md
Original file line number Diff line number Diff line change
@@ -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 した
ものは無い。
Loading