このリポジトリで作業する AI エージェント向けの取り決め。人向けの手順は 開発参加ガイド が正本で、ここには写さない。ここに置くのは、 エージェントが最初に知っておくべきことと、実際に踏んだ落とし穴である。
devbase は Docker Compose で開発環境を立ち上げる CLI である。プロジェクトはプラグインとして外部の リポジトリから入れる。全体像は アーキテクチャ概要 にある。
| 置き場 | 中身 |
|---|---|
bin/devbase |
CLI の入口(Bash)。Python へ渡す |
lib/devbase/ |
Python の実装。主な開発対象 |
containers/ |
イメージの定義。base がすべての派生イメージの土台 |
tests/ |
pytest |
docs/specifications/ |
確定仕様。振る舞いの正本 |
docs/glossary.md |
用語集。docs/glossary/glossary.json から作る |
issues/ |
実装計画と設計の作業文書。確定したら docs/specifications/ へ移して消す |
projects/*・plugins/*・repos/ |
手元に入れたプラグイン。git の管理外(.gitignore) |
プロジェクト MVV に従う。次の 3 つは必ず人の承認を得てから行う。
| # | 操作 |
|---|---|
| P1 | base イメージに入れる道具や、その版を変える |
| P2 | タグ・GitHub Release を作る |
| P3 | 実際の利用者の環境で devbase up を実行する |
main は開発の起点であり、同時に本番の配布のチャネルでもある(install.sh が main を取得する)。
main へのマージがそのまま利用者への配布になる。 main へ直接 push しない。
push の前に、CI と同じ検査を手元で打つ。
env -u DEVBASE_ROOT uv run --locked pytest tests/ -q # 全体のテスト
uvx ruff check --select=E9,F63,F7,F82 lib # CI と同じ lint
python3 .github/scripts/proper_term_check.py # 固有の語の検査env -u DEVBASE_ROOTを外さない。 pytest は起動したシェルのDEVBASE_ROOTを継ぐ。up/scaleの テストは backend を隔離しないと、実のサーバーと実の docker に触る- 固有の語の検査が
語の一覧が無いため飛ばしたと出たら、検査はしていない。 終了コードは 0 でも当たりが無い という意味ではない。実機の記録は書き始めからproj-a・team-a・<user>のような中立の呼び名で書く - シェルスクリプトの ShellCheck は
devbase-base:latestの shellcheck で打てる(コマンドは開発参加ガイドの 「CI が実行するもの」)
- 利用者に見える変更は、同じ Pull Request で
CHANGELOG.mdの[Unreleased]を書く。開発者向けの文書だけ・ テストだけの変更は書かない mainは strict(ブランチが最新でないとマージできない)。続けてマージすると 2 本目以降が BEHIND になる。 1 本ずつmainを取り込み、CI を待ってからマージする- 並行する Pull Request は
CHANGELOG.mdの[Unreleased]とdocs/glossary/glossary.jsonでぶつかりやすい。 両側の記入を残して解く。docs/glossary.mdはglossary.jsonからglossary.py render(NDF の道具)で作り直し、手で直さない - 実装の Pull Request は、設計の決定と違えたところを、決定の番号と理由で本文に書く。違えたところが無ければ「無し」と書く。 書かないと、確定仕様化で初めて差に気づく(#435 は決定 1・4・5 が実装で変わり、本文に無かった)
- 作業は
.worktrees/<ブランチ名>の worktree で行う。メインディレクトリに差分を残さない
| 状況 | 起きること | どうするか |
|---|---|---|
containers/base/entrypoint.sh などイメージに入るファイルを変えた |
devbase up だけでは反映されない |
base を建て直してから確かめる |
| base を建てて比べる | base は 1 本で数 GB ある。並べて残すとディスクが埋まり Docker が止まる | 建てる前に docker system df を見る。作業用のタグは記録を書いたら消す |
| 確かめのためにボリュームを作る・写す | macOS の Docker Desktop では、VM の中の df はホストの空きを表さない。ボリュームを丸ごと写すとホストの Docker.raw が伸びてディスクが埋まり、Docker Desktop が止まって動いていたコンテナがすべて落ちる(m8a で #368 の受け入れ条件 9 のためにホームのボリュームを丸ごと写し、12 のコンテナと裏の検査が落ちた) |
写す前にホストの df -h /System/Volumes/Data を見る。写すのは確かめに要るファイルだけにする(CLI の設定なら数百 MB で足りた)。落ちたコンテナは devbase up で作り直さず、docker start と devbase project post-start <名前> で戻す |
| イメージの大きさを受け入れ条件に書く | docker images の .Size は、containerd のイメージストア(Docker Desktop)では圧縮後、classic のストアでは展開後に近い値を出す。同じ条件が端末で二通りに読める(#402 は 611MB と 1.29GB に分かれた) |
用語集の「展開後の大きさ」(du -sbx /)で書き、測った端末とストアを記録に残す |
| ビルドの前後でイメージが変わっていないことを確かめる | containerd のイメージストア(Docker Desktop)では、キャッシュだけで建っても docker images の ID が変わることがある(m7e のリリース後テストで devbase-base:latest が c6b3549b950d → 7c621a66378d、作成時刻は同じ)。「ID は変わらない」と先に書いて直した。前の ID は手元から消え、比べ直せなかった |
建てる前に docker image inspect の Id と Created を控える。変わっていないことは Created で示し、ID が変わったら変わったと書く |
通常の経路(build・up など)に docker compose の呼び出しを足す |
足した呼び出しが元の経路より多くの前提を持つと、これまで通っていた利用者の環境で止まる(#415 の docker compose config はサービスの env_file を読み、.env をまだ作っていない初回のビルドで失敗する形だった。直しに使った --no-env-resolution は Compose v2.35 より前では未知のオプション。どちらも設計を通り、スプリント m7e の cross-review で major になった) |
設計で、足す呼び出しが読むファイルと要る Compose の版を、元の経路と並べて書く。版で入ったオプションは、退けられたときの扱いを決める |
| base から道具を外す前に、使う者を探す | repos/*/ のプラグインだけを探すと、プロジェクトのコード(projects/*/repo)の .mcp.json・package.json が npx などで実行時に使う道具を見落とす(#401 で chrome-devtools-mcp・@playwright/mcp を承認ゲートで人が見つけた) |
projects/*/repo も探す範囲に入れ、探した場所と探していない場所を要求に書く |
非対話のセッション(claude -p の担当)で base を建てる |
macOS の Docker Desktop がキーチェーンを開けず、ubuntu などの取得が error getting credentials で止まる(#400 の担当は建てられないまま PR を出した) |
担当は建てられなかったことを「判断が要る」で返す。建てる確認は対話できるセッションか、取得済みのイメージを docker load してから行う |
「base の無い端末」での devbase build を確かめる |
手元の devbase-base:latest などを消すと利用者の環境を壊す。base を実際に建てると取得がキーチェーンで止まり得る(上の行) |
端末に無い作業用の名前で、最下段を FROM scratch にした同じ形の連なり(containers/<作業用>/・projects/<作業用>/)を git archive で展開した一時の root に組み、env -u DEVBASE_ROOT で建てる。たどり方は名前に依らない。実の名前での確認は base の無い端末で利用者に頼む(#404 のリリース後テスト、#421 のコメント) |
projects/* の compose.yml やフックを直したい |
projects/* はプラグインのリポジトリ(repos/ の下)への symlink で、このリポジトリの管理外 |
Pull Request はプラグインのリポジトリへ出す |
projects/* へ cd してから ../../ を書く |
symlink の先から解決され、devbase の外を指す | 絶対パスで書く |
| macOS で検査が通る | CI(Linux)と ln などの既定の振る舞いが違う。同時実行の競合が CI でだけ出た例がある |
振る舞いが分かれるオプションは明示する(例: ln -P) |
| tmux を使うテストを書く | $TMUX があると TMUX_TMPDIR は無視され、kill-server が実のサーバーを落とす |
env -u TMUX で起動するか、-S / -L で別のソケットを使う |
受け入れ条件や手動確認に「作業用のプロジェクトで devbase up」がある |
作業用のプロジェクトでも、共通のホーム(devbase_home_ubuntu)・グループのホーム・作業ボリュームの実物を付けて起動する。非対話の担当は P3 を聞けずにそのまま打つ(m8b で #470 の担当が打ち、利用者のボリュームを付けたコンテナが立った。痕跡は無かった) |
実装のプロンプトで担当に devbase up を禁じる。up の確認は conductor が P3 の承認を取ってから打つか、up を使わない確かめ方(構成の生成・SnapshotManager を一時のルートで組むなど)へ置き換える |
スナップショットの差分(tar の --listed-incremental)の失敗や中断の扱いを設計する |
中断した差分を「次の差分が重ねて含むので置いておく」とすると、ディレクトリの改名が A→B・B→C と続いたとき復元で A→C を求め、C の中身が欠ける。ファイルの追加・変更だけの例では正しく見える(#466 の決定 2 がレビューで major になり、f509cf5 で組の snar snapshot.snar.pending-NNN へ直した) |
設計で、中断の後の世代を復元する場面を、追加・変更に加えて削除とディレクトリの改名で並べる。snar と差分を同じ組として進めるか戻すかで決める(snapshot-series.md) |
| 社内向けの引き渡しや手順を書く | 社内のホスト名やアカウント ID が公開される | issues/internal/ か issues/*.internal.md・issues/*-handover.md に置く(git の管理外) |
- コード・文書・コミットメッセージ・Pull Request は日本語で書く。周りのコードの書き方に合わせる
- 設計文書の決定は
## 決定の記録の下に### 決定 Nの形で置く - 用語は 用語集 の語を使う。新しい語を足すときは
docs/glossary/glossary.jsonを直す