diff --git a/docs/glossary.md b/docs/glossary.md
index e6c51e4d..79c9119d 100644
--- a/docs/glossary.md
+++ b/docs/glossary.md
@@ -112,7 +112,7 @@ base イメージのフォントの解決(docs/specifications/base-image-rende
| 総称ファミリ | — | sans-serif / sans / serif / monospace。具体の書体名ではなく様式を指す指定 | — | — | `docs/specifications/base-image-rendering.md` |
| metric 互換 | — | 字幅・行送りが元の書体と一致する代替の書体 | — | — | `docs/specifications/base-image-rendering.md` |
| 受け皿 | — | イメージに実在しない書体名を指定されたときに末尾へ足すフェイス | — | — | `docs/specifications/base-image-rendering.md` |
-| 追加の太さのフォント | — | fonts-noto-cjk-extra が入れる Noto CJK の Thin・Light・Medium・Black などのフェイス。標準の太さ(Regular・Bold)は fonts-noto-cjk にある | — | — | — |
+| 追加の太さのフォント | — | fonts-noto-cjk-extra が入れる Noto CJK の Thin・Light・Medium・Black などのフェイス。標準の太さ(Regular・Bold)は fonts-noto-cjk にある | — | — | `docs/specifications/base-image-rendering.md` |
## base イメージの tmux のコマンド(`tmux`)
@@ -177,10 +177,10 @@ base の設定が派生イメージへ届く道筋(containers/*/Dockerfile)
| 語 | 識別子 | 意味 | 廃止した語 | 廃止した識別子 | 正本 |
| --- | --- | --- | --- | --- | --- |
-| 派生イメージ | — | FROM devbase-base:latest で base を継ぎ、道具を足すイメージ(containers/ の general・php など) | — | — | — |
-| DinD | — | コンテナの中で dockerd を起こし、そのコンテナの中だけの docker を使う形。#400 で廃止し、ENABLE_DIND は知らせを 1 行出すだけになった。ホストの docker.sock を mount して使う形と区別する | — | — | — |
-| 展開後の大きさ | — | 建てたイメージから作ったコンテナで du -sbx / を測ったバイト数。docker images の圧縮後の大きさとは違う | — | — | — |
-| 文書の除外 | — | dpkg の path-exclude の設定で、apt が入れるパッケージから文書などの置き場を展開しないこと。base は Ubuntu の excludes に加え、changelog・info・/usr/include/node を外す(copyright は残す)。base を FROM で継ぐ派生イメージにも効く | — | — | — |
+| 派生イメージ | — | FROM devbase-base:latest で base を継ぎ、道具を足すイメージ(containers/ の general・php など) | — | — | `docs/specifications/base-image-contents.md` |
+| DinD | — | コンテナの中で dockerd を起こし、そのコンテナの中だけの docker を使う形。#400 で廃止し、ENABLE_DIND は知らせを 1 行出すだけになった。ホストの docker.sock を mount して使う形と区別する | — | — | `docs/specifications/base-image-contents.md` |
+| 展開後の大きさ | — | 建てたイメージから作ったコンテナで du -sbx / を測ったバイト数。docker images の圧縮後の大きさとは違う | — | — | `docs/specifications/base-image-contents.md` |
+| 文書の除外 | — | dpkg の path-exclude の設定で、apt が入れるパッケージから文書などの置き場を展開しないこと。base は Ubuntu の excludes に加え、changelog・info・/usr/include/node を外す(copyright は残す)。base を FROM で継ぐ派生イメージにも効く | — | — | `docs/specifications/base-image-contents.md` |
## コンテナの起動(`container-start`)
@@ -230,4 +230,4 @@ devbase up / scale がコンテナを起動して待ち、その後にホスト
| システムの Chrome | — | apt で入る google-chrome-stable。/usr/bin/google-chrome にあり、Playwright を介さずに使う道具が呼ぶ。base にはどちらのアーキにも入れない(#401) | — | — | `docs/specifications/base-image-rendering.md` |
| snap スタブ | — | Ubuntu の chromium-browser パッケージ。中身は chromium の snap を入れる案内だけで、コンテナの中では Chromium として起動しない | — | — | `docs/specifications/base-image-rendering.md` |
| ブラウザの依存パッケージ | — | playwright install の --with-deps が apt で入れるパッケージ。共有ライブラリ(libnss3 など)とフォント(fonts-liberation・fonts-ipafont-gothic・fonts-wqy-zenhei など)を含む。base は、このうち fonts-liberation・fonts-ipafont-gothic・fonts-wqy-zenhei・libnss3 などを明示して持つ | — | — | `docs/specifications/base-image-rendering.md` |
-| ブラウザの派生イメージ | — | containers/browser(タグ devbase-browser:latest)。base を継ぎ、Playwright の Chromium・ブラウザの依存パッケージ・追加の太さのフォントを足す | — | — | — |
+| ブラウザの派生イメージ | — | containers/browser(タグ devbase-browser:latest)。base を継ぎ、Playwright の Chromium・ブラウザの依存パッケージ・追加の太さのフォントを足す | — | — | `docs/specifications/base-image-rendering.md` |
diff --git a/docs/glossary/glossary.json b/docs/glossary/glossary.json
index ec2a982f..4c66bfa6 100644
--- a/docs/glossary/glossary.json
+++ b/docs/glossary/glossary.json
@@ -365,8 +365,7 @@
"term": "追加の太さのフォント",
"context": "base-image",
"meaning": "fonts-noto-cjk-extra が入れる Noto CJK の Thin・Light・Medium・Black などのフェイス。標準の太さ(Regular・Bold)は fonts-noto-cjk にある",
- "source": "",
- "pending_source": "issues/issue-402-requirements.md"
+ "source": "docs/specifications/base-image-rendering.md"
},
{
"term": "検査ジョブ",
@@ -588,8 +587,7 @@
"term": "派生イメージ",
"context": "image-lineage",
"meaning": "FROM devbase-base:latest で base を継ぎ、道具を足すイメージ(containers/ の general・php など)",
- "source": "",
- "pending_source": "issues/issue-402-requirements.md"
+ "source": "docs/specifications/base-image-contents.md"
},
{
"term": "利用者に見える変更",
@@ -847,29 +845,25 @@
"term": "ブラウザの派生イメージ",
"context": "browser",
"meaning": "containers/browser(タグ devbase-browser:latest)。base を継ぎ、Playwright の Chromium・ブラウザの依存パッケージ・追加の太さのフォントを足す",
- "source": "",
- "pending_source": "issues/issue-402-design.md"
+ "source": "docs/specifications/base-image-rendering.md"
},
{
"term": "DinD",
"context": "image-lineage",
"meaning": "コンテナの中で dockerd を起こし、そのコンテナの中だけの docker を使う形。#400 で廃止し、ENABLE_DIND は知らせを 1 行出すだけになった。ホストの docker.sock を mount して使う形と区別する",
- "source": "",
- "pending_source": "issues/issue-400-requirements.md"
+ "source": "docs/specifications/base-image-contents.md"
},
{
"term": "展開後の大きさ",
"context": "image-lineage",
"meaning": "建てたイメージから作ったコンテナで du -sbx / を測ったバイト数。docker images の圧縮後の大きさとは違う",
- "source": "",
- "pending_source": "issues/issue-400-requirements.md"
+ "source": "docs/specifications/base-image-contents.md"
},
{
"term": "文書の除外",
"context": "image-lineage",
"meaning": "dpkg の path-exclude の設定で、apt が入れるパッケージから文書などの置き場を展開しないこと。base は Ubuntu の excludes に加え、changelog・info・/usr/include/node を外す(copyright は残す)。base を FROM で継ぐ派生イメージにも効く",
- "source": "",
- "pending_source": "issues/issue-400-requirements.md"
+ "source": "docs/specifications/base-image-contents.md"
}
]
}
diff --git a/docs/specifications/base-image-contents.md b/docs/specifications/base-image-contents.md
new file mode 100644
index 00000000..26c43ada
--- /dev/null
+++ b/docs/specifications/base-image-contents.md
@@ -0,0 +1,247 @@
+# base イメージの中身の絞り込み(文書の除外と DinD の廃止)
+
+## 概要
+
+base イメージは、すべての利用者が使う道具だけを持つ。使う者の見当たらない中身と、ビルドの片付けの
+漏れを持たない。重い道具は派生イメージに置く([プロジェクト MVV](../../.ndf/mvv.md) の Value 1)。
+
+この仕様は、base が**入れないもの**と、その規則を保つ仕組みを定める。
+
+- dockerd と containerd を入れず、DinD(コンテナの中で dockerd を起こす形)を持たない。
+ `ENABLE_DIND` は廃止を 1 行で知らせるだけで、起動は止めない
+- グローバルの `aws-cdk-lib` と root の uv を入れない。`PATH` に `/root/.local/bin` を持たない
+- apt の一覧(`/var/lib/apt/lists`)をイメージに残さない
+- 文書の除外: apt が入れるパッケージの文書(copyright のほか)・man・info・Node.js のヘッダを展開しない。
+ この設定は base を `FROM` で継ぐ派生イメージにも効く
+
+ブラウザ(Playwright の Chromium・システムの Chrome)・追加の太さのフォント・`terraform` を base に
+入れないことは [base イメージの文字の描画と、文書を扱う道具](base-image-rendering.md) が定める。
+
+## 用語
+
+DinD・文書の除外・展開後の大きさ・派生イメージの定義は
+[用語集: イメージの継承(`image-lineage`)](../glossary.md#イメージの継承image-lineage) にある。
+
+## 背景
+
+base には、使う者の見当たらない中身と片付けの漏れが arm64 で約 0.5GB あった。base とその派生を
+建てる・取得する・置くすべての利用者が、その分の時間とディスクを払っていた。利用者は 2026-10-03 に
+base の削減を承認し(MVV の P1)、次の 4 つの課題で絞った。
+
+| 課題 | 外したもの | 定める仕様 |
+| --- | --- | --- |
+| #403 | 派生イメージ lfm | この仕様(「派生イメージ」) |
+| #400 | dockerd・containerd・DinD・グローバルの `aws-cdk-lib`・root の uv・apt の一覧・文書 | この仕様 |
+| #401 | システムの Chrome(amd64 だけにあった) | [base-image-rendering.md](base-image-rendering.md) |
+| #402 | Playwright の Chromium とブラウザの依存パッケージ・追加の太さのフォント・`terraform` | [base-image-rendering.md](base-image-rendering.md) |
+
+**DinD は使う者の有無で廃止した(#400 の決定 1)。** `ENABLE_DIND` を設定する compose・env は、
+このリポジトリ・手元のプラグイン・手元のプロジェクトに 0 件だった。プロジェクトはどれもホストの
+docker.sock を mount して docker を使う。DinD を前提に作られた唯一の派生 lfm は使うプロジェクトが無く、
+#403 で先に廃止した。DinD を残しても動かす先が無く、誰も通らない経路を base とすべての派生に配り続ける
+ことになる。dockerd を入れた新しい派生へ DinD を移す形は、移す先を使う者がいないため採らない。
+
+**`ENABLE_DIND` は黙って無視も拒否もせず、1 行で知らせて起動を続ける(#400 の決定 2)。** 黙って
+無視すると、設定を残した利用者は DinD が起きない理由を知る手段が無い。起動を止めると、使っていない
+設定の残りのためにコンテナが使えなくなる。
+
+**lfm は使うプロジェクトが無く廃止した(#403)。** lfm は base を `COPY --from` で取り込み、base の
+約 3GB を重ねて持っていた。`devbase build lfm` など lfm を名指しした操作に特別な分岐は残さず、
+知らないイメージとして既存の扱い(`Image directory not found`)で止まる。
+
+## 対象範囲
+
+- `containers/base/Dockerfile` の apt の層・root の道具の層・`ENV PATH`・利用者の層の片付け
+- 文書の除外の設定 `containers/base/dpkg-excludes`
+- `containers/base/entrypoint.sh` の `ENABLE_DIND` の扱い
+- base から派生するイメージ(`browser` / `general` / `go` / `php` / `php85` / `bi-tools` / `latex` /
+ `trygroup`。いずれも `FROM devbase-base:latest`)への文書の除外の伝播
+- `containers/snapshot` は base を継がないため対象に含まない
+
+## 構成要素
+
+| 要素 | 置き場所 | 責務 |
+| --- | --- | --- |
+| 文書の除外の設定 | `containers/base/dpkg-excludes`(イメージの中では `/etc/dpkg/dpkg.cfg.d/excludes-devbase`) | Ubuntu の `excludes` の後に読まれ、copyright を残して文書・man・info・`/usr/include/node` を外す |
+| 設定の配置 | `containers/base/Dockerfile` の `FROM` の直後の `COPY --chmod=0644 dpkg-excludes /etc/dpkg/dpkg.cfg.d/excludes-devbase` | 最初の `RUN` より前に置き、base と派生のすべての `apt-get install` に効かせる |
+| apt の層 | `containers/base/Dockerfile` の 1 つ目の `RUN` | 2 回目の `apt-get install` で `docker-ce-cli`・`docker-buildx-plugin`・`docker-compose-plugin` を入れ、`docker-ce`・`containerd.io` を入れない。末尾で `/usr/share/doc` の copyright でない物と `/usr/share/info` の中身と apt の一覧を消す |
+| root の道具の層 | `containers/base/Dockerfile` の AWS CLI・gcloud・npm のグローバルを入れる `RUN` | `npm i -g` に `aws-cdk` を入れ、`aws-cdk-lib` を入れない。uv のインストーラを呼ばない |
+| 利用者の層 | `containers/base/Dockerfile` の `USER ${USERNAME}` の後の uv・claude・agy・kiro を入れる `RUN` | uv を利用者 `ubuntu` の `~/.local/bin` に入れる。片付けで `/usr/share/doc` の copyright でない物(`sudo find`)と `/var/lib/apt/lists/*` を消す |
+| DinD の知らせ | `containers/base/entrypoint.sh` の `devbase_notice_dind_removed` | `ENABLE_DIND` が `true` か `1` なら廃止を 1 行出す。どの値でも 0 を返す |
+| 形の検査 | `tests/containers/test_base_dockerfile_slim.py` | Docker を起動せずに、Dockerfile と `dpkg-excludes` の形を固定する |
+| 知らせの検査 | `tests/containers/test_entrypoint_dind_removed.py` | entrypoint を source して、知らせの出力と、触れてはならないコマンドを呼ばないことを固定する |
+
+型(クラス)は持たない。Dockerfile の命令・設定ファイル・entrypoint のシェル関数だけで構成する。
+Dockerfile を命令に分ける部品(`Instruction` / `parse`)は `tests/containers/dockerfile_parse.py` に
+あり、Dockerfile の形を固定する検査が共通に使う。
+
+## 仕様
+
+### 常に成り立つ条件
+
+| # | 条件 | 破れたときの扱い |
+| --- | --- | --- |
+| I1 | apt で `docker-ce`・`containerd.io` を入れず、`docker-ce-cli`・`docker-buildx-plugin`・`docker-compose-plugin` を入れる | 形の検査がパッケージの名前を挙げて落ちる |
+| I2 | npm のグローバルに `aws-cdk-lib` を入れず、`aws-cdk` を入れる | 同上 |
+| I3 | uv のインストーラを呼ぶのは `USER ubuntu` の後の `RUN` だけで、`ENV PATH` に `/root/.local/bin` が無い | 同上 |
+| I4 | apt の一覧を取得する `RUN`(`apt-get update` か `--with-deps` を含み、`/var/lib/apt` を cache mount しないもの)は、同じ `RUN` で `/var/lib/apt/lists` を消す | 同上。どの `RUN` かを挙げて落ちる |
+| I5 | 文書の除外の設定を、最初の `RUN` より前に、Ubuntu の `excludes` より後に読まれる名前で置く。最初の `RUN` と利用者の層の `RUN` は、`/usr/share/doc` の下の copyright でないファイルと、ディレクトリを指さない symlink を消す | 同上 |
+| I6 | `containers/base/dind` が無く、Dockerfile は `/usr/local/bin/dind` を置かない | 同上 |
+| I7 | `ENABLE_DIND` が `true` か `1` のとき、DinD の廃止を 1 行出して起動を続ける。dockerd を起こさず、`/var/run/docker.sock` を消さず、`docker info` を待たない | 知らせの検査が落ちる |
+| I8 | `ENABLE_DIND` が無い・`true` と `1` 以外のとき、何も出さず、起動の手順と順序は変わらない | 同上。既存の entrypoint の検査も落ちる |
+
+I4 は「利用者の層の片付けに `/var/lib/apt/lists` がある」ではなく規則で縛る(#400 の決定 7)。
+apt の一覧を取得しない `RUN` には当たらないため、`RUN` から `--with-deps` を外しても検査は意味を保つ。
+
+### docker
+
+base は docker の**クライアント**だけを持つ。docker CLI・`docker buildx`・`docker compose` は使える。
+デーモン(dockerd)と containerd は持たず、コンテナの中で docker を使うときはホストの docker.sock を
+mount する。利用者 `ubuntu` は `docker` グループに属する。
+
+### `ENABLE_DIND`
+
+entrypoint は起動の手順 5(AWS の設定の後、AI の設定の symlink の前)で
+`devbase_notice_dind_removed` を呼ぶ。
+
+| `ENABLE_DIND` | 振る舞い |
+| --- | --- |
+| `true` / `1` | `NOTICE: ENABLE_DIND は廃止しました (DinD は起動しません)。ホストの docker.sock を mount して docker を使ってください` を 1 行出し、0 を返す |
+| 未設定・空・それ以外の値 | 何も出さずに 0 を返す |
+
+どちらの場合も `sudo`・`dockerd`・`docker`・`rm` を呼ばない。関数は `DEVBASE_ENTRYPOINT_LIB_ONLY` で
+entrypoint を source した検査から呼べる。
+
+### 文書の除外
+
+`ubuntu:26.04` は `/etc/dpkg/dpkg.cfg.d/excludes` で man・翻訳の `.mo`・doc を既に外し、copyright と
+changelog だけを `path-include` で残している。dpkg は後に読んだ規則を優先するため、`excludes` より後に
+並ぶ名前(`excludes-devbase`)のファイルで doc を外し直し、copyright だけを入れ直す。これで changelog も
+外れる(#400 の決定 4)。
+
+**dpkg の規則だけでは消えない物を、`RUN` の中で消す。** 規則は設定を置いた後に展開するパッケージに
+しか効かず、`FROM` の層に既にある物(`NEWS.Debian.gz`・`README`・`examples/*` などのファイルと、
+別のパッケージの置き場を指す symlink)は残る。そのため 1 つ目の `RUN` の末尾と利用者の層の片付けの
+両方で次を打つ。
+
+```sh
+find /usr/share/doc -mindepth 1 ! -type d ! -xtype d ! -name copyright -delete
+```
+
+- `! -type d` は symlink を含み、`! -xtype d` は指す先がディレクトリの symlink を外す。
+ `libgcc-s1` などのディレクトリを指す symlink は `/usr/share/doc/<パッケージ>/copyright` への経路なので残す
+- 利用者の層は `USER ubuntu` の下で動き、`/usr/share/doc` は apt が root 所有で作るため `sudo find` で消す。
+ `sudo` が無いと `find` が Permission denied で 0 でなく終わり、`set -eux` でビルドが止まる
+- 1 つ目の `RUN` は `/usr/share/info` の中身も消す
+
+| 採らない形 | 理由 |
+| --- | --- |
+| Ubuntu の `excludes` を書き換える | Ubuntu の持ち物を混ぜる |
+| `RUN` の中で `printf` で設定を書く | 検査がファイルとして読めない |
+| 入れた後に `rm` するだけにする | 前の層に大きさが残る |
+
+翻訳(`/usr/share/locale`)は外さない。`.mo` は Ubuntu が既に外しており、残る `locale.alias` は glibc が読む。
+
+### 入れないもの
+
+| 対象 | 理由 |
+| --- | --- |
+| `docker-ce`(dockerd)・`containerd.io` | DinD でだけ使い、DinD を廃止した。arm64 で 106MB と 81MB |
+| `containers/base/dind`(`/usr/local/bin/dind`) | 同上 |
+| グローバルの `aws-cdk-lib` | Node はグローバルのモジュールを `require` で探さず、CDK のアプリは自分の `package.json` に持つ。CLI の `aws-cdk` は残す。181MB |
+| root の uv | 利用者 `ubuntu` の uv と重複し、root で uv を使う処理が無い。`sudo uv` は「コマンドが無い」で失敗する。40MB |
+| `PATH` の `/root/.local/bin` | root の uv を外すと入る物が無い。空の置き場を配り続けない(#400 の決定 6) |
+| `/var/lib/apt/lists` | 実行時に読まない。42MB |
+| `/usr/share/doc`(copyright のほか)・`/usr/share/man`・`/usr/share/info` | 実行時に読まない |
+| `/usr/include/node` | ネイティブアドオンのビルドでは node-gyp がヘッダを取得する。67MB |
+
+### 派生イメージ
+
+base を継ぐ派生イメージは `browser` / `general` / `go` / `php` / `php85` / `bi-tools` / `latex` / `trygroup` で、
+いずれも `FROM devbase-base:latest` である。派生イメージは dpkg の設定(`/etc/dpkg/dpkg.cfg.d/`)を継ぐ
+ため、派生が自分で `apt-get install` するパッケージの文書も展開されない。
+
+npm グループの GID は `ARG NPM_GID="2000"` で固定する。建て直しても、npm のグローバル領域とブラウザの
+置き場の数値の所有者が変わらないようにするためである。
+
+## データ・設定
+
+`containers/base/dpkg-excludes` の規則:
+
+| 規則 | 対象 |
+| --- | --- |
+| `path-exclude=/usr/share/doc/*` | 文書をすべて外す |
+| `path-include=/usr/share/doc/*/copyright` | copyright だけを入れ直す(`path-exclude` の後に置く) |
+| `path-exclude=/usr/share/man/*` | man |
+| `path-exclude=/usr/share/info/*` | info |
+| `path-exclude=/usr/include/node/*` | Node.js のヘッダ |
+
+展開後の大きさ(arm64・2026-10-03。コンテナで `du -sxb /` を測ったバイト数):
+
+| base | 展開後の大きさ |
+| --- | ---: |
+| 絞り込みの前 | 4,711,162,948 |
+| #400 まで | 4,241,009,996(−470,152,952) |
+| #402 まで(今の base) | 2,948,038,543(−1,763,124,405) |
+
+`docker image inspect` の `.Size` は、containerd のイメージストアでは圧縮後の大きさを返す。大きさは
+展開後の大きさで比べる(#402 の決定 10)。
+
+## 運用
+
+- 変更は**イメージを建て直すまで反映されない**。`devbase build base --no-cache` で base を建て直し、
+ 使っている派生イメージも建て直し、稼働中のコンテナは `devbase down` → `devbase up` で作り直す。
+ `devbase up` だけでは反映されない
+- base を前後で建てて比べるときは、建てる前に `docker system df` で空きを見る。base は 1 本で数 GB あり、
+ 作業用のタグは記録を書いたら消す
+- 大きさは同じ端末・同じ日に建てた前後の base から作ったコンテナで `du -sxb /` を測って比べる
+ (apt と npm が取得する版の差を小さくする)
+- uv は利用者 `ubuntu` の `~/.local/bin` にあり、`PATH` へは `~/.profile` が足す。`docker run ... bash -c` の
+ ログインでないシェルでは `uv` が見つからないため、ログインシェル(`bash -lc`)で確かめる
+- 派生イメージ(`latex`・`php`・`php85` など)で実行時に `/usr/share/doc` の中身を読む道具は、文書が
+ 展開されないため動かない。見つかったときは、その派生の Dockerfile で `path-include` を足す
+
+## テスト観点
+
+`tests/containers/test_base_dockerfile_slim.py`(Docker を要さない):
+
+- I1: apt の一覧に `docker-ce`・`containerd.io` が無く、`docker-ce-cli`・`docker-buildx-plugin`・
+ `docker-compose-plugin` があること。
+- I2: `npm i -g` の一覧に `aws-cdk-lib` が無く、`aws-cdk` があること。
+- I3: uv のインストーラ(`astral.sh/uv/install.sh`)を呼ぶ `RUN` が `USER` の後にだけあり、`ENV PATH` に
+ `/root/.local/bin` が無いこと。
+- I4: cache mount の無い apt の一覧の取得を持つ `RUN` は、同じ `RUN` で `/var/lib/apt/lists` を消すこと。
+- I5: 文書の除外の設定の `COPY` が最初の `RUN` より前にあり、宛先の名前が `excludes` より後に並ぶこと。
+ doc の除外の後に copyright の取り込みがあり、man・info・`/usr/include/node` を外すこと。最初の `RUN` と
+ 利用者の層の `RUN` が `find /usr/share/doc` に `! -type d`・`! -xtype d`・`! -name copyright`・`-delete` を
+ 持ち、利用者の層ではそれが `sudo find` であること。
+- I6: `containers/base/dind` が無く、Dockerfile に `/usr/local/bin/dind` が無いこと。
+
+`tests/containers/test_entrypoint_dind_removed.py`(Docker を要さない):
+
+- I7: `ENABLE_DIND` が `true`・`1` のとき、知らせが 1 行だけ出て 0 で返り、`sudo`・`dockerd`・`docker`・
+ `rm` などを呼ばないこと。
+- I8: `ENABLE_DIND` が無い・`false`・空のとき、何も出さずに 0 で返ること。
+- 本体の手順で、知らせの呼び出しが AWS の設定と AI の設定の間にあり、`dockerd`・`/usr/local/bin/dind`・
+ `docker info`・`/var/run/docker.sock` が残っていないこと。
+
+建てた base で確かめること(手動。CI はイメージを建てない):
+
+- arm64 と amd64 で、`command -v dockerd`・`command -v containerd` が失敗し、利用者 `ubuntu` のログイン
+ シェルで `docker --version`・`docker buildx version`・`docker compose version`・`cdk --version`・
+ `uv --version` が 0 で終わること。
+- `npm ls -g aws-cdk-lib` に無く、`/root/.local/bin/uv` が無いこと。`/var/lib/apt/lists` に `lock` と
+ `partial` のほかのファイルが無いこと。
+- `/usr/share/man` と `/usr/include/node` にファイルが無く、`find /usr/share/doc ! -type d ! -xtype d
+ ! -name copyright` が 0 件で、apt で入れたパッケージの `/usr/share/doc/<パッケージ>/copyright` が読めること。
+- 変更後の base から派生イメージがすべて建つこと。
+- `ENABLE_DIND=true` で起こしたコンテナの起動の出力に知らせが 1 行あり、起動が完了すること。
+- 中身を外す変更では、前後の展開後の大きさの差を記録すること。
+
+## 関連リンク
+
+- [base イメージの文字の描画と、文書を扱う道具](base-image-rendering.md)(ブラウザ・追加の太さのフォント・`terraform`)
+- [base イメージの Bash の静的検査(shellcheck)](base-image-shellcheck.md)
+- [CI の検査](ci-checks.md)
+- [用語集: イメージの継承(`image-lineage`)](../glossary.md#イメージの継承image-lineage)
+- [コンテナ操作ガイド](../user/container-operations.md)
diff --git a/docs/specifications/base-image-rendering.md b/docs/specifications/base-image-rendering.md
index 9e374df1..3b14af94 100644
--- a/docs/specifications/base-image-rendering.md
+++ b/docs/specifications/base-image-rendering.md
@@ -26,6 +26,34 @@ PDF を作れる。システムの Chrome(`google-chrome-stable`)と Ubuntu
[コンテナ操作ガイド: 文字の描画と、文書を扱う道具](../user/container-operations.md#文字の描画と文書を扱う道具base-以降)
にある。
+## 背景
+
+**base にブラウザを置かないことは、以前の「base に Chromium を残す」判断を覆したものである(#402 の決定 1)。**
+#220 は、ブラウザをネットワーク無しで使えるよう Playwright の Chromium を `--with-deps` のまま base に
+残すと決めた。その時点では、ブラウザを使わないプロジェクトの数と依存パッケージの大きさ(arm64 で 296MB)を
+測っていなかった。その後、設定の上でブラウザを求めるプロジェクトが無く、コードの見えるプロジェクトで
+使うのは 1 つだけと分かり、利用者が 2026-10-03 に base からの削除を承認した(MVV の P1)。ネットワーク
+無しで使えるという #220 の目的は、ブラウザの派生イメージが引き継ぐ。#220 の置き場の作り方と、検査する
+イメージを環境変数で差し替える形は覆さず、置き場は base に、検査の形はブラウザの派生イメージに引き継いだ。
+
+base に Chromium を残して依存パッケージだけを外す形は、依存パッケージが無いと Chromium が起動できない
+ため採らない。Chromium だけを外して依存パッケージを残す形は、外せる量が足りないため採らない。
+
+**システムの Chrome は両アーキとも base に入れない(#401)。** amd64 にだけあり(依存と合わせて約 540MB)、
+呼ぶ処理がリポジトリとプラグインに無く、アーキで道具がそろっていなかった。
+
+ブラウザまわりのほかの判断:
+
+| 判断 | 理由 |
+| --- | --- |
+| ブラウザと追加の太さのフォントを 1 つの派生イメージ `devbase-browser` にまとめる(#402 の決定 2) | 追加の太さが効くのは、太さを指定したページをブラウザで描くときである。分けると、両方が要るプロジェクトが 1 つしか選べない。名前は役割の中心が Chromium であるため `browser` にする |
+| base に `ENV PLAYWRIGHT_BROWSERS_PATH` と空の置き場を残す(#402 の決定 3) | 派生イメージが宣言し直さずに継げ、base 系のコンテナで道具が実行時に取得する先も変わらない。大きさは増えない |
+| base でブラウザが無いことは Playwright 自身の失敗の出力に任せる(#402 の決定 4) | 案内の包みを足すと base へ道具を足すことになる。実行時に `--with-deps` で取得させると、作り直すたびに数百 MB を取り直し、ネットワークの無い所で動かない |
+| HashiCorp の apt の取得元を base から外す(#402 の決定 5) | 残すと、base を継ぐイメージの `apt-get update` が、使う者の見当たらない道具のために外部の配布元に頼り続ける |
+| `fonts-liberation`・`fonts-ipafont-gothic`・`fonts-wqy-zenhei` を 1 回目の一覧で明示する(#402 の決定 6) | 以前は `--with-deps` の副作用で入っていた。「解決先」の表を変えないため、表に現れる 3 つだけを明示する。`--with-deps` が入れていたほかのフォント(絵文字など)は残さない |
+| 派生イメージとそのほかの道具を組み合わせる派生(例 `php-browser`)は devbase に足さない(#402 の決定 7) | 組み合わせの数だけ派生イメージが増える。プロジェクトの道具立てに合う道筋は、利用者向けの文書の移り方から選ぶ |
+| ブラウザの派生イメージの単体ビルド(`devbase build browser`)は base を先に建てない(#402 の決定 8) | 既存の派生イメージと同じ扱いにする。base を先に建てるのは、プロジェクトの `devbase build` が dev の Dockerfile の `FROM devbase-*` を見る経路である |
+
## 対象範囲
- base イメージの fontconfig の設定の置き場所と内容、フォントキャッシュの作り直し
@@ -447,9 +475,27 @@ skip し、明示したときは skip せず落とす。
無くても skip せず落とす。** 片付けで置き場を消して建てたイメージは古いイメージと見分けられず、
skip にすると壊れ方が見えなくなるためである。
+### 大きさ
+
+ブラウザ・追加の太さのフォント・`terraform` を base から外した効果は、変更の前後の base の**展開後の
+ファイルの大きさ**(コンテナで `du -sxb /`)で判定する(手動。CI はイメージを建てない)。判定の物差しは
+展開後の大きさで、`docker image inspect` の `.Size` ではない(#402 の決定 10)。containerd のイメージ
+ストアでは `.Size` が圧縮後の大きさを返し、取得する量の目安にはなるが展開後の減りとは別の値になる。
+`.Size` の値も並べて記録する。
+
+| イメージ(arm64・2026-10-03) | `.Size`(圧縮後) | `du -sxb /`(展開後) |
+| --- | ---: | ---: |
+| 外す前の base | 1,709,531,737 | 4,239,699,252 |
+| 外した後の base | 1,098,398,245 | 2,948,038,519 |
+| 差 | 611,133,492 | 1,291,660,733 |
+| `devbase-browser`(外した後の base の上) | 1,675,443,221 | 4,125,424,157 |
+
+base の展開後の大きさの全体の推移は [base イメージの中身の絞り込み](base-image-contents.md) にある。
+
## 関連リンク
- [用語集: ブラウザ(`browser`)](../glossary.md#ブラウザbrowser)
+- [base イメージの中身の絞り込み(文書の除外と DinD の廃止)](base-image-contents.md)
- [コンテナ操作ガイド: 文字の描画と、文書を扱う道具](../user/container-operations.md#文字の描画と文書を扱う道具base-以降)
- [AI CLI alias の読み込み](ai-cli-alias-loading.md)
- [Kiro CLI 認証永続化と tmux コピー操作](kiro-auth-persistence-and-tmux-copy.md)
diff --git a/docs/specifications/base-image-shellcheck.md b/docs/specifications/base-image-shellcheck.md
index 9b087101..348bd2d8 100644
--- a/docs/specifications/base-image-shellcheck.md
+++ b/docs/specifications/base-image-shellcheck.md
@@ -16,7 +16,7 @@ bash-language-server などの言語サーバは Bash の診断を `shellcheck`
## 対象範囲
- base イメージへの `shellcheck` の導入と、入れ損ないをビルドで止める仕組み
-- base から派生するイメージ(`general` / `go` / `php` / `php85` / `bi-tools` / `latex` /
+- base から派生するイメージ(`browser` / `general` / `go` / `php` / `php85` / `bi-tools` / `latex` /
`trygroup`)への伝播の規則
- `containers/snapshot` は base を継がないため対象に含まない
- bash-language-server 自体は同梱しない
@@ -39,7 +39,7 @@ bash-language-server などの言語サーバは Bash の診断を `shellcheck`
`shellcheck` は Ubuntu の標準のアーカイブ(`universe`)にあり、外部のリポジトリを要さない。
そのため 1 つ目の `RUN` の**1 回目**の `apt-get install` の一覧に置く。2 回目の一覧は後から
-足したリポジトリのパッケージ(`docker-ce-cli` / `terraform` / `gh` / `nodejs`)
+足したリポジトリのパッケージ(`docker-ce-cli` / `docker-buildx-plugin` / `docker-compose-plugin` / `gh` / `nodejs`)
のためにあり、標準のアーカイブのパッケージを混ぜない。
shellcheck のための `RUN` は立てない。立てると派生イメージ 7 つが積む層が 1 つ増え、
diff --git a/docs/specifications/shellrc-dir.md b/docs/specifications/shellrc-dir.md
index 058e50d8..1cad4e41 100644
--- a/docs/specifications/shellrc-dir.md
+++ b/docs/specifications/shellrc-dir.md
@@ -19,7 +19,7 @@ ai-plugins の中継(devbasex/ai-plugins#928)である。
- 置き場所の永続化(`entrypoint.sh` の分類 B のエントリ)と、置き場所を読む読み込み器
- 環境変数 `DEVBASE_SHELLRC_DIR`
-- base から派生するイメージ(`general` / `go` / `php` / `php85` / `bi-tools` / `latex` /
+- base から派生するイメージ(`browser` / `general` / `go` / `php` / `php85` / `bi-tools` / `latex` /
`trygroup`)への伝播の規則
- 対象に含まないもの:
- zsh。base に zsh は入っておらず、`~/.zshrc` を読むシェルがいない。zsh を入れる変更の
diff --git a/issues/issue-400-design.md b/issues/issue-400-design.md
deleted file mode 100644
index a0260ce9..00000000
--- a/issues/issue-400-design.md
+++ /dev/null
@@ -1,406 +0,0 @@
-# base イメージ: 誰も使わない dockerd・aws-cdk-lib・root の uv・node のヘッダと片付けの漏れで展開後の大きさが約 0.5GB 余分にある → 外して arm64 で 450MB 以上小さくし、使う者のいない DinD を廃止する(#400)
-
-## 目的
-
-- **何が壊れているか**: base に、使う者が見当たらない中身と片付けの漏れが合わせて約 525MB ある。dockerd・containerd・グローバルの `aws-cdk-lib`・root の uv・node のヘッダと、apt の一覧である
-- **誰が困るか**: base とその派生を建てる・取得する・置くすべての利用者。建てる時間とディスクを、誰も使わない中身の分だけ払っている
-- **直すと何が成り立つか**: base の展開後の大きさが arm64 で 450MB 以上減る。docker CLI・buildx・compose・`cdk`・uv は今と同じに使える。`ENABLE_DIND` は廃止を 1 行で知らせて起動を続ける
-
-## 適用範囲
-
-- **働く範囲**: このリポジトリの `containers/base` と、それを `FROM` で継ぐ派生 7 つ。利用者が base を建て直した時点で届く。lfm は #403 で #400 より先に無くなるため含まない(決定 3)
-- **プロジェクトごとに違うもの**: 無し(イメージの中身だけを変え、プロジェクトの設定・引数を読まない)
-- **当たるモード**: `standard`
-
-## あるべき姿の根拠
-
-| 根拠 | 種類 | 何を示すか |
-| --- | --- | --- |
-| `docker run --rm --entrypoint bash devbase-base:latest` で測った値(2026-10-03・arm64): `/usr/include/node` 67MB・`/usr/share/doc` 8.0MB・`/usr/share/info` 940KB・`/var/lib/apt/lists` 42MB・グローバルの `aws-cdk-lib` 181MB | 実測 | 外す中身の大きさ。docker-ce 106MB・containerd 81MB・root の uv 40MB は課題の本文の実測 |
-| 同じコンテナの `/etc/dpkg/dpkg.cfg.d/excludes` が既に `man`・翻訳の `.mo`・`doc`(copyright と changelog を除く)を外している。`/usr/share/man` の下のファイルは 0。`/usr/share/doc` には copyright と `changelog*` のほかに 40 個のファイルと symlink が残る(`apt/NEWS.Debian.gz`・`gnupg/TODO`・`gnupg/examples/*`・`base-files/FAQ`・`libavahi-common3/README`・`python3/README.Debian`・`fonts-dejavu-core/*` などのファイルと、`libgcc-s1`・`libpython3.14` などの symlink)(2026-10-03・arm64 で `find /usr/share/doc ! -type d ! -name copyright ! -name 'changelog*'` を数えた) | 実測 | 文書の除外で足すのは changelog・info・`/usr/include/node` の 3 つ。dpkg の規則だけでは既に残る物が消えないため、最初の `RUN` と利用者の層で `/usr/share/doc` の下の copyright でないファイルと、ディレクトリを指さない symlink を消す(ディレクトリを指す symlink は copyright への経路なので残す。決定 4) |
-| `grep -rIl ENABLE_DIND` を `repos/*/`・`projects/*/` の compose.yml・env・.env にかけて 0 件。当たった compose の注記 1 件はホストの dockerd を指す | 実測 | `ENABLE_DIND` を設定する者がいない |
-| #403 の本文: lfm を使うプロジェクトは無く、利用者が 2026-10-03 に lfm の廃止を決めた。Dockerfile を読む試験の部品(`Instruction` / `parse`)をテストの共通の置き場へ移す | 利用者の指示 | DinD を前提に作られた唯一の派生が無くなる。#400 の試験は移した先の部品を使える |
-| #400 への利用者のコメント(2026-10-03): lfm は #403 で先に廃止する。lfm への影響は考えなくてよい | 利用者の指示 | #400 は lfm のファイル・試験・仕様が無い状態から始める |
-| 設計の起動の指示「DinD を廃止するかは …… grep して使う者の有無で決める」 | 利用者の指示の原文 | DinD の扱いを使う者の有無で決める |
-
-要求と受け入れ条件は #400 の本文にある(コピーは `issues/issue-400-requirements.md`)。この文書は「どう作るか」だけを扱う。
-設計の中で要求の前提 1 を「DinD を lfm に残す」から「DinD を廃止する」へ改め、本文の「変更の記録」に残した(決定 1)。
-
-## ドメインモデル
-
-### コンテキスト
-
-| コンテキスト | 何の語が 1 つの意味に決まるか |
-| --- | --- |
-| イメージの継承(`image-lineage`) | base の中身・文書の除外・展開後の大きさ・DinD。base が入れる物と、派生へ届く道筋 |
-| コンテナの起動(`container-start`) | entrypoint が起動のたびに行う用意。`ENABLE_DIND` の読み方 |
-
-イメージの継承が供給者、コンテナの起動が顧客の関係(顧客 / 供給者)。entrypoint は base が入れた物だけを前提に動く。
-base が dockerd を入れなくなるため、entrypoint は dockerd を起こす手順を持たない。
-
-### 集約
-
-| 集約 | 持ち主(書き換えてよいもの) | 根 | エンティティ | 値オブジェクト |
-| --- | --- | --- | --- | --- |
-| base の中身 | `containers/base/Dockerfile` | base イメージ | apt のパッケージ・npm のグローバルのモジュール・root と ubuntu の道具 | 文書の除外の設定・片付けの対象のパス・`PATH` |
-| 起動の用意 | `containers/base/entrypoint.sh` | 起動の手順 | DinD の知らせ | `ENABLE_DIND` の値 |
-
-### 不変条件
-
-| # | 集約 | 条件 | 破れたときの扱い |
-| --- | --- | --- | --- |
-| I1 | base の中身 | apt で `docker-ce`・`containerd.io` を入れず、`docker-ce-cli`・`docker-buildx-plugin`・`docker-compose-plugin` を入れる | 実装の誤り。試験がパッケージの名前を挙げて落ちる |
-| I2 | base の中身 | npm のグローバルに `aws-cdk-lib` を入れず、`aws-cdk` を入れる | 同上 |
-| I3 | base の中身 | uv のインストーラを呼ぶのは `USER ubuntu` の後の `RUN` だけで、`ENV PATH` に `/root/.local/bin` が無い | 同上 |
-| I4 | base の中身 | apt の一覧を取得する `RUN`(`apt-get update` か `--with-deps` を含み、`/var/lib/apt` を cache mount しないもの)は、同じ `RUN` で `/var/lib/apt/lists` を消す | 同上。どの `RUN` かを挙げて落ちる |
-| I5 | base の中身 | 文書の除外の設定が、最初の `RUN` より前に Ubuntu の `excludes` より後に読まれる名前で置かれ、copyright を残して changelog・info・man・`/usr/include/node` を外す。最初の `RUN` と利用者の層の `RUN` は、`/usr/share/doc` の下の copyright でないファイルと、ディレクトリを指さない symlink を消す(利用者の層は `sudo` で消す)。ディレクトリを指す symlink は残し、`/usr/share/doc/<パッケージ>/copyright` の経路を保つ | 同上 |
-| I6 | base の中身 | `containers/base/dind` が無く、Dockerfile は `/usr/local/bin/dind` を置かない | 同上 |
-| I7 | 起動の用意 | `ENABLE_DIND` が `true` か `1` のとき、DinD の廃止を 1 行出して起動を続ける。dockerd を起こさず、`/var/run/docker.sock` を消さず、`docker info` を待たない | 実装の誤り。entrypoint の試験で落とす |
-| I8 | 起動の用意 | `ENABLE_DIND` が無い・`true` と `1` 以外のとき、何も出さず、起動の手順と順序は変わらない | 同上 |
-
-### ドメインイベント
-
-| # | イベント | 発生元 | 受け手 |
-| --- | --- | --- | --- |
-| E1 | 変更前の base を建て、展開後の大きさを測った | 実装の担当(main の Dockerfile) | E5 の比べる元 |
-| E2 | 変更後の base を arm64 で建てた | 実装の担当(この変更の Dockerfile) | E3・E4・E6・E8 |
-| E3 | 変更後の base を amd64 で建てた | 実装の担当 | E4 |
-| E4 | 建てた base の中身を確かめた | 実装の担当 | PR の記録 |
-| E5 | 展開後の大きさを測り、減った量を PR に記録した | 実装の担当 | PR の記録・承認する人 |
-| E6 | 変更後の base から派生イメージを建てた | 実装の担当 | PR の記録 |
-| E7 | (#403 で対象が無くなったため削除) | — | — |
-| E8 | base で `ENABLE_DIND=true` のコンテナを起こし、廃止の知らせが出て起動が続いた | entrypoint | PR の記録 |
-| E9 | 利用者が base を建て直した | 利用者(main へのマージの後) | リリース後テスト |
-
-### 用語
-
-| 用語 | 意味 | 用語集への反映 |
-| --- | --- | --- |
-| DinD | コンテナの中で dockerd を起こし、そのコンテナの中だけの docker を使う形。#400 で廃止し、`ENABLE_DIND` は知らせを 1 行出すだけになった。ホストの docker.sock を mount して使う形と区別する | 意味の変更(`image-lineage`) |
-| 文書の除外 | dpkg の `path-exclude` の設定で、apt が入れるパッケージから文書などの置き場を展開しないこと。base は Ubuntu の `excludes` に加え、changelog・info・`/usr/include/node` を外す(copyright は残す)。base を `FROM` で継ぐ派生イメージにも効く | 意味の変更(`image-lineage`) |
-| 展開後の大きさ | 建てたイメージから作ったコンテナで `du -sbx /` を測ったバイト数。`docker images` の圧縮後の大きさとは違う | 新規(`image-lineage`) |
-
-意味の文はこの表が正本で、`docs/glossary/glossary.json` の `meaning` はこの表の文と同じ文にする(コードの印の backtick だけを除く)。
-
-## 機能一覧
-
-| # | 機能 | 誰が使うか |
-| --- | --- | --- |
-| F1 | base を建てると、dockerd・containerd・グローバルの `aws-cdk-lib`・root の uv が入らない | base と派生を建てる利用者 |
-| F2 | base と派生で、apt が入れるパッケージの changelog・info・man・node のヘッダが展開されない | 同上 |
-| F3 | base の最後の利用者の層に apt の一覧が残らない | 同上 |
-| F4 | docker CLI・buildx・compose・`cdk`・ubuntu の uv が今と同じに使える | コンテナの中で作業する利用者 |
-| F5 | `ENABLE_DIND` を設定したままの利用者に、DinD の廃止を 1 行で知らせ、起動は止めない | `ENABLE_DIND` を設定していた利用者 |
-
-## 構成要素
-
-型(クラス)は持たない。Dockerfile の命令と、entrypoint のシェル関数だけで構成する。
-
-| 要素 | 責務 | 変更 |
-| --- | --- | --- |
-| 文書の除外の設定(`containers/base/dpkg-excludes`、新規) | `/etc/dpkg/dpkg.cfg.d/excludes-devbase` として置かれ、Ubuntu の `excludes` の後に読まれて changelog・info・man・`/usr/include/node` を外し、copyright を残す(決定 4) | 作る |
-| base の Dockerfile の apt の層(最初の `RUN`) | 文書の除外の設定を `COPY` で先に置き、`docker-ce`・`containerd.io` を一覧から外す。同じ `RUN` の最後で `/usr/share/doc` の下の copyright でないファイルとディレクトリを指さない symlink(changelog を含む)と `/usr/share/info` の中身を消す。ディレクトリを指す symlink は残す(決定 4) | 変える |
-| base の Dockerfile の root の道具の層(2 つ目の大きな `RUN`) | root の uv のインストーラの呼び出しと、npm の `aws-cdk-lib` を外す | 変える |
-| base の Dockerfile の `ENV PATH` | `/root/.local/bin` を外す(決定 6) | 変える |
-| base の Dockerfile の利用者の層 | 片付けに `/var/lib/apt/lists/*` を足す(決定 7)。同じ片付けで `/usr/share/doc` の下の copyright でないファイルとディレクトリを指さない symlink を `sudo find` で消す。`USER ubuntu` の下で動き、apt が root 所有で作る物は `sudo` なしでは消せず `set -e` でビルドが止まるため(決定 4) | 変える |
-| base の Dockerfile の最後の `COPY` | `dind` の `COPY` を外し、`entrypoint.sh` だけを置く | 変える |
-| `containers/base/dind` | — | 消す |
-| entrypoint の DinD の知らせ(`devbase_notice_dind_removed`、新規) | `ENABLE_DIND` が `true` か `1` なら廃止を 1 行出して 0 で返る。それ以外は何も出さずに 0 で返る。`DEVBASE_ENTRYPOINT_LIB_ONLY` で source した試験から呼べる(決定 2) | 作る |
-| entrypoint の本体の手順 5 | dockerd を起こす分岐を消し、DinD の知らせを呼ぶ 1 行にする。手順 4 と AI の設定の間という位置は変えない | 変える |
-| base の形の試験(`tests/containers/test_base_dockerfile_slim.py`、新規) | I1〜I6 を Docker を起こさずに固定する。Dockerfile の読み方は #403 が共通の置き場へ移した `Instruction` / `parse` を import する(決定 5) | 作る |
-| DinD の知らせの試験(`tests/containers/test_entrypoint_dind_removed.py`、新規) | I7・I8 を固定する | 作る |
-| base のシェルスクリプトの数え上げ(`tests/containers/test_base_shellcheck_ci.py`・`.github/workflows/ci.yml`・`docs/developer/contributing.md`) | ShellCheck の対象の並びから `dind` を外す | 変える |
-| 確定仕様(`docs/specifications/base-image-shellcheck.md`・`ci-checks.md`) | 2 回目の apt の一覧・`dind` の ShellCheck・`SC2009` の抑止の記述を今の形へ直す | 変える |
-| 用語集(`docs/glossary/glossary.json`) | DinD・文書の除外の意味を直し、展開後の大きさを `image-lineage` に新しく載せ(意味は用語の表の文のまま)、`glossary.py render` で `docs/glossary.md` を作り直す | 変える |
-| `CHANGELOG.md` の `[Unreleased]` | 外した中身と、DinD の廃止(破壊的変更・ホストの docker.sock を使う)を書く | 変える |
-
-```mermaid
-graph TD
- subgraph base["containers/base"]
- X[文書の除外の設定] --> A[apt の層]
- A --> R[root の道具の層]
- R --> P[ENV PATH]
- P --> U[利用者の層]
- U --> E[entrypoint の COPY]
- N[DinD の知らせ] -.属する.-> E
- end
- subgraph 派生["FROM で継ぐ派生 7 つ"]
- D[自分の apt-get install]
- end
- subgraph 試験["tests/containers"]
- T1[base の形の試験]
- T2[DinD の知らせの試験]
- end
- X -->|dpkg の設定を継ぐ| D
- T1 -.読む.-> base
- T2 -.source.-> N
-```
-
-## 構造
-
-```text
-containers/
-├── base/
-│ ├── Dockerfile 変える
-│ ├── dpkg-excludes 新規
-│ ├── dind 消す
-│ └── entrypoint.sh 変える
-tests/containers/
-├── test_base_dockerfile_slim.py 新規
-├── test_entrypoint_dind_removed.py 新規
-└── test_base_shellcheck_ci.py 変える
-```
-
-## 処理の流れ
-
-### base を建てる順(変更後)
-
-```mermaid
-graph TD
- F[FROM ubuntu:26.04] --> C1["COPY dpkg-excludes
/etc/dpkg/dpkg.cfg.d/excludes-devbase"]
- C1 --> R1["RUN apt の層
docker-ce-cli・buildx・compose を入れる
doc の copyright でない物と info を消す"]
- R1 --> R2[RUN 利用者とグループ]
- R2 --> R3["RUN root の道具
aws・gcloud・npm(aws-cdk-lib なし)
root の uv なし"]
- R3 --> P["ENV PATH
/opt/google-cloud-sdk/bin を足す"]
- P --> R4[RUN bao・確認]
- R4 --> R5["USER ubuntu の RUN
uv・claude・agy・kiro・playwright
片付けで apt の一覧と doc の copyright でない物も消す"]
- R5 --> R6[設定の COPY と fc-cache]
- R6 --> C2[COPY entrypoint.sh]
-```
-
-建てる流れと起動の流れに乗らない要素(試験・ShellCheck の対象の並び・確定仕様・用語集・`CHANGELOG.md`)は図に含めない。
-
-`playwright install --with-deps` の `apt-get` も文書の除外の設定に従うため、その層でも文書は展開されない。
-
-### コンテナの起動(`ENABLE_DIND` の扱い)
-
-```mermaid
-graph TD
- S[手順 4 AWS の設定] --> Q{ENABLE_DIND は true か 1 か}
- Q -->|はい| M["廃止の知らせを 1 行出す
(docker.sock・dockerd・docker info に触れない)"]
- Q -->|いいえ| Z[何も出さない]
- M --> AI[AI の設定の symlink]
- Z --> AI
- AI --> RP[リポジトリの用意] --> RD[完了の印] --> X[exec]
-```
-
-知らせの文は 1 行で、`ENABLE_DIND` が廃止されたことと、ホストの docker.sock を使うこと(`docker` はそのまま使える)を含める。
-文言は実装で決める。
-
-## 非機能の実現方式
-
-| 大項目 | 要求の条件 | 実現方式 | 確かめ方 |
-| --- | --- | --- | --- |
-| 性能・拡張性 | arm64 で展開後の大きさが 450MB 以上減る | 下の内訳の 7 つを外す。見込みは合わせて約 525MB で、450MB に約 75MB の余裕がある | 前提 8 の形で前後の base から作ったコンテナで `du -sbx /` を測り、差と内訳を PR に記録する |
-| 移行性 | 利用者は base を建て直すだけで移る。戻すときは前の Dockerfile で建て直す | 変えるのはイメージの中身と entrypoint だけで、ボリューム・設定・機密の形を変えない。`ENABLE_DIND` を残した利用者は起動が止まらず、知らせで気づく | 建て直した base で既存の entrypoint の試験が通ること・E8 の起動の出力を PR に記録する |
-| システム環境 | arm64 と amd64 の両方で建ち、受け入れ条件 1〜5 を満たす | アーキの `case` を足さない。外すのはアーキに依らない行だけで、文書の除外もアーキに依らない | arm64 で建てて 1〜6・8・9 を、amd64 で建てて 1〜5 を確かめ、amd64 の端末と方法を PR に書く |
-
-見込みの内訳(arm64):
-
-| 中身 | 大きさ | 出所 |
-| --- | --- | --- |
-| `docker-ce`(dockerd) | 106MB | 課題の本文 |
-| `containerd.io` | 81MB | 課題の本文 |
-| グローバルの `aws-cdk-lib` | 181MB | 実測(2026-10-03) |
-| root の uv | 40MB | 課題の本文 |
-| `/var/lib/apt/lists` | 42MB | 実測 |
-| `/usr/include/node` | 67MB | 実測 |
-| `/usr/share/doc` の changelog と `/usr/share/info` | 約 9MB | 実測(8.0MB と 940KB) |
-| 合計 | 約 525MB | — |
-
-`docker-ce` が依存で引き込んでいたパッケージ(`iptables` など)も、他に要る物が無ければ入らなくなる。この分は見込みに含めない。
-
-## 同じスプリントの課題との順と境目
-
-### 実装の順
-
-- **#403 → #400 → #401 → #402 の順に 1 本ずつマージする。** 後の課題は、前の課題がマージされた main を取り込んでから建てて測る。
- 大きさの比べる元は、その時点の main で建てた base である
-- **#400 の 450MB は arm64 で測る。** #401 は amd64 だけを変えるため、#400 の測りに #401 は入らない
-- **#400 は #403 の後の main から始める。** lfm・`test_lfm_base_settings.py`・`lfm-base-settings.md` は既に無く、#400 は lfm に触れない。前提 8 の「前」の base も #403 の後の main で建てる
-
-```mermaid
-graph LR
- D["#403
lfm の廃止"] --> A["#400 段階 1
使う者のいない中身"]
- A --> B["#401 段階 2
amd64 の Chrome"]
- B --> C["#402 段階 3
ブラウザ・フォント・terraform"]
-```
-
-### Dockerfile の行の持ち主
-
-#401・#402 は base の Dockerfile の同じ層を触る。#403 は #400 より先に入り、lfm を前提にした行を片付ける。行ごとに持ち主を 1 つに決め、他の課題の行には触れない(決定 8)。
-行番号は main の `9f3e5c5` の `containers/base/Dockerfile` で数えた。#403 の後の main では行がずれるため、実装の担当は中身で読み替える。
-
-| 場所(行) | 中身 | 持ち主 |
-| --- | --- | --- |
-| 最初の `RUN` の前(新しい `COPY`) | 文書の除外の設定 | #400 |
-| 22 | `fonts-noto-cjk-extra` | #402 |
-| 23〜25 の後(最初の apt の一覧) | `fonts-liberation` などの明示 | #402 |
-| 37〜43 | Docker の apt の取得元 | 変えない(CLI・buildx・compose に要る) |
-| 44〜48 | HashiCorp の apt の取得元 | #402 |
-| 56〜67 | amd64 の Chrome の取得元と `BROWSER_PKG` | #401 |
-| 71 | `docker-ce`・`containerd.io` | #400(`docker-ce-cli` は残す) |
-| 73 | `terraform` | #402 |
-| 73 | `$BROWSER_PKG` | #401 |
-| 75〜76 の後 | `/usr/share/doc` の下の copyright でないファイルとディレクトリを指さない symlink・`/usr/share/info` の中身の削除 | #400 |
-| 84〜89・111〜113・247〜251・259 | lfm を前提にした注記と `NPM_GID` | #403(先に入る。#400 は触らない) |
-| 155〜156 | root の uv | #400 |
-| 159 | `aws-cdk-lib` | #400 |
-| 177 | `/root/.local/bin` | #400 |
-| 234〜235 | Playwright の Chromium と `--with-deps` | #402 |
-| 236〜245 | 片付け(`/var/lib/apt/lists/*` と `/usr/share/doc` の下の copyright でない物の削除を足す。どちらも `sudo` で消す) | #400。注記の `--with-deps` の語は #402 |
-| 304〜308 | `fc-cache` の注記 | #402 |
-| 310〜312 | `dind` の `COPY` | #400 |
-
-### Dockerfile の外で重なるもの
-
-| 場所 | 重なり | 扱い |
-| --- | --- | --- |
-| `docs/specifications/base-image-shellcheck.md` の 2 回目の apt の一覧の行 | #400(`docker-ce` → `docker-ce-cli`)・#401(Chrome)・#402(`terraform`) | 後から入る側が main を取り込み、前の課題の直しを残して自分の語を直す |
-| `tests/containers/test_base_dockerfile_playwright.py` | #401・#402 | #400 は触らない。#400 の試験は新しいファイルに置く |
-| Dockerfile の読み方の部品(`Instruction` / `parse`) | #403(`test_lfm_base_settings.py` から共通の置き場へ移す)・#400(import する) | #400 の試験は #403 が移した先を import する。部品は直さない |
-| `CHANGELOG.md`・`docs/glossary/glossary.json` | 4 つすべて | 両側の記入を残して解く。`docs/glossary.md` は `glossary.py render` で作り直す |
-
-## 決定の記録
-
-### 決定 1: 使う者の有無で決めるため、DinD を廃止し、`dind` と dockerd を起こす分岐を消す
-
-`ENABLE_DIND` を設定する者は、リポジトリ・手元のプラグイン・手元のプロジェクトに 0 件である。DinD を前提に作られた
-lfm は使う者が無く、#403 で #400 より先に廃止する。lfm は自分で dockerd を入れ、NVIDIA の runtime を DinD 向けに書いていた。
-DinD を残しても動かす先が無く、`dind` と分岐は誰も通らない経路として base とすべての派生に配られ続ける。要求の前提 1 は
-lfm が残る前提で書かれていたため、課題の本文を改め「変更の記録」に残した。
-
-dockerd を入れた新しい派生へ DinD を移す形は採らない。移す先を使う者がいない。lfm に残す形は、lfm ごと消えるため採らない。
-
-根拠: Value 1 / P1(MVV 版 1)
-
-### 決定 2: 設定を残した利用者が原因に気づけるよう、`ENABLE_DIND` は 1 行で知らせて起動を続ける
-
-分岐を消すだけだと、`ENABLE_DIND` を残した利用者は DinD が起きない理由を知る手段が無い。起動を止めると、使っていない
-設定の残りのためにコンテナが使えなくなる。知らせは関数に切り出し、既存の entrypoint の試験と同じく
-`DEVBASE_ENTRYPOINT_LIB_ONLY` で source して呼べるようにする。
-
-`ENABLE_DIND` を黙って無視する形と、起動を止める形は採らない。前者は原因が見えず、後者は作業を止める。
-
-根拠: Mission(MVV 版 1)
-
-### 決定 3: #403 の後に入れるため、lfm には触らない
-
-利用者は m7c を #403 → #400 → #401 → #402 の順に入れると決め、#400 に「lfm への影響は考えなくてよい」とコメントした。
-#400 の起点の main には lfm の 3 つが無い。`containers/lfm`・`tests/containers/test_lfm_base_settings.py`・
-`docs/specifications/lfm-base-settings.md` である。base から `dind` を消しても、取り込む側が無いため壊れる物は無い。
-
-#400 が先に入る場合に備えて、lfm から `dind` の取り込みの 2 行を消す形は採らない。順が決まっているため、
-誰も通らない分岐を設計と PR に抱えるだけになる。
-
-根拠: Value 1(MVV 版 1)
-
-### 決定 4: 派生にも効かせて層の大きさから外すため、文書の除外を Ubuntu の `excludes` の後に読まれる別のファイルで置く
-
-`ubuntu:26.04` は `/etc/dpkg/dpkg.cfg.d/excludes` で man・`.mo`・doc を既に外している。copyright と changelog だけは
-`path-include` で残している。dpkg は後に読んだ規則を優先する。そのため `excludes` より後に並ぶ名前(`excludes-devbase`)の
-ファイルで doc を外し直し、copyright だけを入れ直せば changelog が外れる。
-
-同じファイルで `/usr/share/info/*`・`/usr/share/man/*`・`/usr/include/node/*` を外す。ファイルは
-`containers/base/dpkg-excludes` に置き、最初の `RUN` の前に `COPY` する。
-
-dpkg の規則だけでは受け入れ条件 5 の「copyright のほかのファイルが無い」を満たせない。今の base には changelog のほかに
-`NEWS.Debian.gz`・`README`・`examples/*` などのファイルと、別のパッケージの置き場を指す symlink(`libgcc-s1` など)が
-40 個残る(根拠の表の実測)。そのため最初の `RUN` の最後と利用者の層の片付けの両方で、
-`find /usr/share/doc -mindepth 1 ! -type d ! -xtype d ! -name copyright -delete` を実行し、`/usr/share/doc` の下の
-copyright でないファイルと、ディレクトリを指さない symlink(指す先の無い物を含む)を消す。`! -type d` は symlink を
-含み、`! -xtype d` は指す先がディレクトリの symlink を外す。`libgcc-s1` などのディレクトリを指す symlink は、
-条件 5 が残すと定める `/usr/share/doc/<パッケージ>/copyright` への経路なので消さない(指す先のディレクトリの copyright は
-同じ削除で残る)。利用者の層は `USER ubuntu` の下の `set -eux` の `RUN` で動き、`/usr/share/doc` は apt が root 所有で
-作るため、`sudo find /usr/share/doc ...` で消す(同じ層の `rm` が `sudo rm -rf` なのと同じ理由。`sudo` が無いと
-Permission denied で `find` が 0 でなく終わり、ビルドが止まる)。最初の `RUN` は root で動くので `sudo` を付けない。
-最初の `RUN` では `/usr/share/info` の中身も消す。利用者の層で消すのは、`playwright install --with-deps` の `apt-get` が入れる物を
-同じ規則で片付けるためである。`FROM` の層にある分は層の大きさが減らないが、条件 5 を満たす。
-
-翻訳(`/usr/share/locale`)は外さない。`.mo` は Ubuntu が既に外しており、残る `locale.alias` は glibc が読む。
-
-| 採らない形 | 理由 |
-| --- | --- |
-| Ubuntu の `excludes` を書き換える | Ubuntu の持ち物を混ぜる |
-| `RUN` の中で `printf` で書く | 試験がファイルとして読めない |
-| 入れた後に `rm` する | 前の層に大きさが残る |
-
-根拠: Value 1(MVV 版 1)
-
-### 決定 5: 試験が外した物を名前で挙げるよう、base の形の試験を新しいファイルに置く
-
-外した物ごとに 1 つの試験にし、Dockerfile へ戻すと名前を挙げて落ちる(受け入れ条件 11)。既存の
-`test_base_dockerfile_playwright.py` などは #401・#402 が直すため、同じファイルに書くと並行する変更がぶつかる。
-Dockerfile の読み方(命令への分け方)は新しく書かない。#403 が `test_lfm_base_settings.py` からテストの共通の置き場へ
-移した部品(`Instruction` / `parse`)を import する。
-
-根拠: Value 1(MVV 版 1)
-
-### 決定 6: 空の置き場を指さないよう、`PATH` から `/root/.local/bin` を外す
-
-root の uv を外すと `/root/.local/bin` に入る物が無くなる。`PATH` に残すと、何も入らない置き場を base とすべての派生に
-配り続ける。この要素を名前で除外していた lfm の試験は #403 で消えているため、外しても他の試験は変わらない。
-
-`PATH` を残す形は、理由の無い要素を配り続けるため採らない。
-
-根拠: Value 1(MVV 版 1)
-
-### 決定 7: #402 が Playwright を外しても壊れないよう、apt の一覧の片付けを規則で縛る
-
-試験は I4 の規則を見る。「`/var/lib/apt` を cache mount せずに apt の一覧を取得する `RUN` は、同じ `RUN` で一覧を消す」
-である。「利用者の層の片付けに `/var/lib/apt/lists` がある」は見ない。#402 が `--with-deps` を外すと、利用者の層は一覧を取得しなく
-なり、規則は当たる `RUN` が無いまま通る。片付けの 1 語が残っても害は無く、#402 が消してもよい。
-
-`--with-deps` の有無に関わらず利用者の層に片付けの語を求める形は、#402 の後に意味の無い要求になるため採らない。
-
-根拠: Value 1(MVV 版 1)
-
-### 決定 8: 並行する削減がぶつからないよう、#403 → #400 → #401 → #402 の順に 1 本ずつ入れ、Dockerfile の行の持ち主を分ける
-
-#403 は lfm と、lfm を前提にした base の行を先に片付け、後の 3 つの比べる元から lfm を外す。#400 以下の 3 つは段階の
-番号の順で、外す量の小さい順でもある。#400 は arm64 の測りが #401 に依らず、#401 は amd64 だけを触り、
-#402 は Playwright と apt の一覧の両方を変える。後の課題ほど前の結果を前提にできる。`main` は strict で、続けて
-マージすると後の 1 本が BEHIND になるため、1 本ずつ main を取り込み、建てて測り直してからマージする。行の持ち主は
-「同じスプリントの課題との境目」の表に従う。
-
-削減の 3 つを 1 本の PR にまとめる形は、測りが混ざり、どの削減が何を減らしたかを PR に記録できないため採らない。
-
-根拠: Value 1 / P1(MVV 版 1)
-
-## テスト設計
-
-| 受け入れ条件・不変条件 | どの振る舞いで縛るか | どう壊したら落ちるべきか |
-| --- | --- | --- |
-| I1(受け入れ条件 1・2) | base の apt の一覧に `docker-ce`・`containerd.io` が無く、`docker-ce-cli`・`docker-buildx-plugin`・`docker-compose-plugin` がある | `docker-ce` か `containerd.io` を一覧へ戻す・`docker-buildx-plugin` を消すと、そのパッケージの名前を挙げて落ちる |
-| I2(受け入れ条件 2・3) | npm のグローバルの一覧に `aws-cdk-lib` が無く、`aws-cdk` がある | `aws-cdk-lib` を戻す・`aws-cdk` を消すと落ちる |
-| I3(受け入れ条件 2・3) | uv のインストーラを呼ぶ `RUN` が `USER ubuntu` の後にだけあり、`ENV PATH` に `/root/.local/bin` が無い | root の `RUN` に uv のインストーラを戻す・利用者の `RUN` から消す・`PATH` に戻すと落ちる |
-| I4(受け入れ条件 4) | cache mount の無い apt の一覧の取得を持つ `RUN` は、同じ `RUN` で `/var/lib/apt/lists` を消す | 利用者の層の片付けから一覧を外すと、その `RUN` を挙げて落ちる。`--with-deps` を消した Dockerfile では当たる `RUN` が無く通る |
-| I5(受け入れ条件 5) | 文書の除外の設定が `excludes` より後に並ぶ名前で、最初の `RUN` より前に置かれ、doc の除外の後に copyright の取り込みがあり、changelog・info・man・`/usr/include/node` を外す | 名前を `devbase-excludes`(`excludes` より前)にする・`COPY` を最初の `RUN` の後へ動かす・copyright の取り込みを消す・`/usr/include/node` の行を消すと落ちる |
-| I5(受け入れ条件 5・残る文書の削除) | 最初の `RUN` と利用者の層の `RUN` は、どちらも `/usr/share/doc` の下の copyright でないファイルとディレクトリを指さない symlink を消す(`find /usr/share/doc` に `! -type d` と `! -xtype d` と `! -name copyright` と `-delete` がある)。利用者の層の `RUN` ではその `find` が `sudo find` である | どちらかの `RUN` から削除を消す・`! -name copyright` を外す・`! -type d` を `-type f` に変える(symlink が残る)・`! -xtype d` を外す(ディレクトリを指す symlink が消え copyright への経路が失われる)・利用者の層の `sudo` を外すと、その `RUN` を挙げて落ちる |
-| I6(受け入れ条件 11) | `containers/base/dind` が無く、Dockerfile に `/usr/local/bin/dind` が無い | `dind` を戻す・`COPY` を戻すと落ちる |
-| I7(受け入れ条件 9) | `ENABLE_DIND` が `true`・`1` のとき、DinD の知らせが 1 行だけ出て 0 で返り、`sudo`・`dockerd`・`docker`・`rm` を呼ばない | 知らせの後に `docker info` を待つ・docker.sock を消す・dockerd を起こす・0 以外で返るように壊すと落ちる |
-| I8(受け入れ条件 10) | `ENABLE_DIND` が無い・`false`・空のとき、何も出さずに 0 で返る。既存の entrypoint の試験が変わらず通る | 未設定でも知らせを出すように壊すと落ちる |
-| 受け入れ条件 1〜5(建てた base) | arm64 と amd64 で建てた base で、ubuntu の利用者のログインシェル(`docker run --rm --entrypoint bash devbase-base:latest -lc '<コマンド>'`)で確かめる。`/home/ubuntu/.local/bin` は `ENV PATH` に無く `~/.profile` が足すため、`-lc` でない `bash -c` では変更の前後どちらでも `uv` が見つからない。`ENV PATH` に足して通すと受け入れ条件 14 を破る。`command -v dockerd`・`command -v containerd` が失敗し、`docker --version` などの 5 つが 0 で終わり、`npm ls -g aws-cdk-lib` が空で、`/var/lib/apt/lists`・`/usr/share/man`・`/usr/include/node`・`/usr/share/doc` が条件どおりである(`find /usr/share/doc ! -type d ! -xtype d ! -name copyright` が 0 件で、`dpkg-query -W -f '${Package}\n'` の各パッケージのうち元の base で `/usr/share/doc/<パッケージ>/copyright` を読めた物が、変更後も読める。`libgcc-s1` などの symlink の先を含む) | 手動確認(マージ前)。出力を PR に記録する |
-| 受け入れ条件 6 | 前提 8 の形で前後の展開後の大きさの差が 450,000,000 バイト以上 | 手動確認。届かなければ内訳を PR に書いて人へ戻す |
-| 受け入れ条件 8 | 変更後の base から派生 7 つが建つ | 手動確認。建たなければ外した物を前提にした手順を直す |
-| 受け入れ条件 9(建てた base) | `ENABLE_DIND=true` で起こしたコンテナの起動の出力に知らせが 1 行あり、完了の印ができる | 手動確認。出力を PR に記録する |
-| 受け入れ条件 12 | `env -u DEVBASE_ROOT uv run --locked pytest tests/ -q` が通る | ShellCheck の対象に `dind` が残ると、既存の試験が落ちる |
-| 受け入れ条件 13 | `CHANGELOG.md` の `[Unreleased]` に、外した 5 つと DinD の廃止(破壊的変更・ホストの docker.sock を使う)がある | 実装のレビューで見る |
-| 受け入れ条件 14 | base の Dockerfile の差分が、次の 6 つの変更(除外の `COPY`・apt の層・root の道具の層・`PATH`・片付け・`dind` の `COPY`)だけである | 実装のレビューで `git diff main -- containers/base/Dockerfile` を見る |
-
-## 設計の結果
-
-| 課題 | 扱い | 取り込み先 | 触るファイル |
-| --- | --- | --- | --- |
-| #400 | 実装する | — | `containers/base/Dockerfile`、`containers/base/dpkg-excludes`、`containers/base/dind`、`containers/base/entrypoint.sh`、`tests/containers/`、`.github/workflows/ci.yml`、`docs/developer/contributing.md`、`docs/specifications/`、`docs/glossary/glossary.json`、`docs/glossary.md`、`CHANGELOG.md` |
-
-## 未確認のまま残ること
-
-| 項目 | 内容 |
-| --- | --- |
-| 大きさの見込み | docker-ce・containerd・root の uv の値は課題の本文の実測で、この設計では測り直していない。450MB に届くかは E5 で決まる。届かなければ人へ戻す |
-| amd64 の確かめ方 | WSL2 の端末で建てるか `docker buildx build --platform linux/amd64` で建てるかは実装の担当が決め、PR に書く(要求の前提 7) |
-| node のヘッダの無い環境 | ネイティブアドオンを入れるプロジェクトで node-gyp がヘッダを取得できるか(要求の前提 6)は、リリース後テストで利用者のプロジェクトで分かる |
-| 派生の apt の文書 | latex・php・php85 が自分で入れるパッケージの文書も外れる。実行時に `/usr/share/doc` の中身を読む道具が派生にあるかは、派生を建てて使うまで分からない |
-| #402 の要求との食い違い | #402 の前提 5 は amd64 の Chrome を base に残すとしているが、#401 がそれを外す。#402 の設計で前提を直す必要がある(#402 へ書き残した) |
-| 共通の部品の置き場 | #400 の試験が import する `Instruction` / `parse` の置き場の名前は #403 の実装で決まる。#400 の実装の担当は #403 の後の main で確かめる |
diff --git a/issues/issue-400-requirements.md b/issues/issue-400-requirements.md
deleted file mode 100644
index 6112a807..00000000
--- a/issues/issue-400-requirements.md
+++ /dev/null
@@ -1,169 +0,0 @@
-# #400: base から使う者が見当たらない中身を外す(dockerd・aws-cdk-lib・root の uv・apt の片付けの漏れ)
-
-正は課題の本文(#400)で、この文書はその写しである。`spec-copy.py write` で作り直す。手で直さない。
-
-## 依頼(原文)
-
-> base イメージから、使う者が見当たらない中身と片付けの漏れを外す。base の削減の段階 1(段階 2・3 は別の課題)。利用者は 2026-10-03 にこの削減を承認した(MVV の P1)。
->
-> | 中身 | 場所 | 大きさ | 外す根拠 |
-> | --- | --- | --- | --- |
-> | `docker-ce` と `containerd.io`(dockerd と containerd) | `containers/base/Dockerfile:71` | 106+81MB | DinD でだけ使う。`ENABLE_DIND` を設定している箇所はリポジトリとプラグインに見当たらない(`grep -rn ENABLE_DIND`)。プロジェクトはどれもホストの docker.sock を mount する。CLI・buildx・compose は残す |
-> | グローバルの `aws-cdk-lib` | `Dockerfile:159` | 181MB | Node はグローバルのモジュールを `require` で探さず、CDK のアプリは自分の package.json に持つ。CLI の `aws-cdk` は残す |
-> | root の uv | `Dockerfile:156` | 40MB | ubuntu の uv(`Dockerfile:211`)と重複し、root で uv を使う処理が見当たらない |
-> | `/var/lib/apt/lists` | `Dockerfile:235-245` | 42MB | `playwright install --with-deps` の `apt-get update` が残し、片付けが消していない |
-> | `/usr/share/doc`・`man`・`/usr/include/node` など | apt の層 | 約 75MB | 実行時に読まない。dpkg の path-exclude で入れない |
->
-> 決めること:
-> - DinD の扱い: `containers/base/dind` と entrypoint の `ENABLE_DIND` の分岐を消すか、dockerd を入れたうえで使う派生へ移すか
-> - path-exclude で外す置き場の範囲(copyright は残す)
->
-> 受け入れ条件(起票時):
-> 1. 建てた base(arm64・amd64)に上の中身が無い。docker CLI・`docker buildx`・`docker compose`・`aws-cdk`・ubuntu の uv は動く
-> 2. 展開後の大きさが arm64 で 450MB 以上減る(`du -sbx /` の前後で測り、PR に記録する)
-> 3. lfm が建ち、`tests/containers/` が通る(lfm は自分で docker-ce を入れるため影響しない見込み)
-> 4. DinD を廃止するなら、利用者向けの文書と CHANGELOG に書く
->
-> 由来: スプリント m7b の振り返りの後、利用者が「base が機能盛りすぎで肥大化している」として削減のプランを求めた(2026-10-03)。
-
-## 目的
-
-- base を建てる時間・取得する量・ディスクの消費を、誰も使っていない中身の分だけ減らす(MVV の Value 1「base を重くしすぎず、重い道具は派生イメージに置く」)
-- 利用者がコンテナの中で使う道具(docker CLI・buildx・compose・`cdk`・uv)は、削減の前と同じに使える
-
-## 調べて分かったこと(2026-10-03)
-
-| 事実 | 出所 |
-| --- | --- |
-| entrypoint の DinD の分岐は `ENABLE_DIND` が `true` / `1` のときだけ動き、最初に `/var/run/docker.sock` を消してから `dind dockerd` を起こし、`docker info` を最大 30 秒待つ | `containers/base/entrypoint.sh:862-` |
-| `ENABLE_DIND` を設定している compose・env は、このリポジトリと手元のプラグイン(`repos/` の下)に見当たらない。利用者向けの文書(`docs/`・`README`・`CHANGELOG.md`)にも DinD の案内は無い | `grep -rn "ENABLE_DIND\|DinD" docs README* CHANGELOG.md lib` と `grep -rln "ENABLE_DIND\|dockerd" repos/ projects/`(当たりはプラグインの 1 つ(proj-a)の compose の注記だけで、ホストの dockerd を指す) |
-| base を `FROM` で継ぐ派生イメージは bi-tools・general・go・latex・php・php85・trygroup。dpkg の設定(`/etc/dpkg/dpkg.cfg.d/`)は継がれ、latex・php・php85 は自分で `apt-get install` する | `containers/*/Dockerfile` |
-| 派生イメージに root で uv を使う手順は無い(bi-tools の `uv tool install` は `USER ubuntu` の下) | `containers/bi-tools/Dockerfile:29-47` |
-| CI はイメージを建てない。イメージの確かめは手元で行う | `.github/workflows/ci.yml` |
-| DinD を前提に作られた唯一の派生 lfm(自分で dockerd を入れ、base から `dind` を `COPY --from` で取り込む)は、使うプロジェクトが無く、同じスプリント m7c の #403 で #400 より先に廃止する | #403 の本文・#400 への利用者のコメント(2026-10-03) |
-| `ENABLE_DIND` を設定している compose・env は、手元のプラグイン(`repos/*/`)と `projects/*/` に 0 件である(設計で再び確かめた) | `grep -rIl ENABLE_DIND repos/*/ projects/*/ --include=compose.yml --include=env --include=.env` |
-
-## 前提
-
-- 前提 1(DinD の扱い): DinD を廃止する。base から dockerd と containerd を外し、`containers/base/dind` と entrypoint の `ENABLE_DIND` の分岐(dockerd を起こす手順)を消す。根拠: `ENABLE_DIND` を設定する者がリポジトリ・手元のプラグイン・手元のプロジェクトに無く、DinD を前提に作られた lfm は使う者が無く、#400 より先に #403 で廃止する。起票時の選択肢「消す」「使う派生へ移す」のうち、移す先を使う者がいないため前者を採る(設計で前提を改めた。「変更の記録」)
-- 前提 2: base とその派生で `ENABLE_DIND=true` を設定している利用者はいない(上の調べ)。設定していた利用者は、base を建て直した後に DinD が起きなくなる。entrypoint はこの設定を見つけたら廃止を 1 行で知らせて起動を続け、CHANGELOG でホストの docker.sock を使うよう案内する
-- 前提 3: root で uv を使う利用者・処理は無い。base の root の uv を外した後、`sudo uv` は「コマンドが無い」で失敗してよい
-- 前提 4: グローバルの `aws-cdk-lib` を `require` する利用者は無い。CDK のアプリは自分の `package.json` に `aws-cdk-lib` を持ち、`cdk synth` はそれを使う
-- 前提 5: dpkg の除外設定は base を `FROM` で継ぐ派生イメージにも効き、派生が後から apt で入れるパッケージの文書も入らなくてよい(copyright は残る)
-- 前提 6: `/usr/include/node` を外すと、npm のネイティブアドオンのビルドでは node-gyp がヘッダを取得する(ネットワークが要る)。これを許す
-- 前提 7: amd64 の確かめは、amd64 の Docker が動く端末(WSL2 の端末など)か、`docker buildx build --platform linux/amd64` で行う。どちらで行ったかを PR に書く
-- 前提 8: 大きさは、建てた base から作ったコンテナで `du -sbx /` を測った値で比べる。前は main の Dockerfile で建てた base、後はこの変更の Dockerfile で建てた base とし、同じ端末・同じ日に建てる(apt と npm の取得する版の差を小さくする)
-- 前提 9: #403(lfm の廃止)が #400 より先にスプリントの統合先へ入る。#400 の起点には `containers/lfm`・`tests/containers/test_lfm_base_settings.py`・`docs/specifications/lfm-base-settings.md` が無く、Dockerfile を読む試験の部品(`Instruction` / `parse`)は #403 が移した共通の置き場にある。#400 は lfm への影響を考えない(利用者のコメント、2026-10-03)
-
-## 対象範囲
-
-含む:
-- base の Dockerfile から `docker-ce`・`containerd.io`・グローバルの `aws-cdk-lib`・root の uv を外す
-- base の最後の利用者の層で残る `/var/lib/apt/lists` を消す
-- dpkg の除外設定で、`/usr/share/doc`(copyright を除く)・`/usr/share/man` などの文書と `/usr/include/node` を入れない
-- `containers/base/dind` と entrypoint の DinD の分岐を消し、`ENABLE_DIND` を設定したときは廃止の知らせを 1 行出す
-- 外した中身に触れる試験(`tests/containers/`)と仕様(`docs/specifications/`)の追従
-- `CHANGELOG.md` の `[Unreleased]` への記入
-
-含まない:
-- 段階 2・3 の削減(重い道具を派生イメージへ移す・版の固定の見直し)。別の課題
-- lfm の廃止(#403。#400 より先に入るため、#400 は lfm に触れない)
-- base の他の道具の追加・版の変更
-- イメージを建てる CI の追加
-- 利用者の環境で `devbase up` を実行して確かめること(P3。リリース後テストで利用者が行う)
-
-## ドメインイベント
-
-| # | イベント | 引き金 | 失敗したとき | 順序の前提 |
-| --- | --- | --- | --- | --- |
-| E1 | 変更前の base を建て、展開後の大きさを測った | 実装の着手 | 建たなければ main の不具合として別に起票し、測りを止める | — |
-| E2 | 変更後の base を arm64 で建てた | Dockerfile の変更 | 建たなければ変更を直す | E1 |
-| E3 | 変更後の base を amd64 で建てた | E2 の成功 | 建たなければ変更を直す(アーキの差は `case` の分岐で起きやすい) | E2 |
-| E4 | 建てた base の中身を確かめた(外した物が無く、残す道具が動く) | E2・E3 の成功 | 残す道具が動かなければ変更を直す | E2・E3 |
-| E5 | 展開後の大きさを測り、減った量を PR に記録した | E4 の成功 | 450MB に届かなければ、届かない理由と内訳を PR に書いて人へ戻す | E1・E4 |
-| E6 | 変更後の base から、`FROM` で継ぐ派生イメージを建てた | E2 の成功 | 建たなければ、外した物を前提にした取り込みや手順を直す | E2 |
-| E7 | (#403 で対象が無くなったため削除) | — | — | — |
-| E8 | base で `ENABLE_DIND=true` のコンテナを起こし、entrypoint が DinD の廃止を知らせて起動を続けた | E2 の成功 | 起動が止まる・dockerd を起こそうとする・ホストの docker.sock が消えるなら entrypoint を直す | E2 |
-| E9 | 利用者が base を建て直した | main へのマージ(配布) | 建たなければ利用者は前のイメージで動き続ける。リリース後テストで見つける | E2〜E8・マージ |
-
-## 用語
-
-| 用語 | 意味 |
-| --- | --- |
-| DinD | コンテナの中で dockerd を起こし、そのコンテナの中だけの docker を使う形。`ENABLE_DIND` で有効にする。ホストの docker.sock を mount して使う形と区別する。この課題で廃止する |
-| 展開後の大きさ | 建てたイメージから作ったコンテナで `du -sbx /` を測ったバイト数。`docker images` の圧縮後の大きさとは違う |
-| 文書の除外 | dpkg の `path-exclude` の設定で、apt が入れるパッケージから文書などの置き場を展開しないこと |
-
-## 受け入れ条件
-
-- [ ] 1. 変更後の base(arm64・amd64)で、`dockerd`・`containerd` がコマンドとして見つからない(`command -v` が失敗する)
-- [ ] 2. 変更後の base(arm64・amd64)で、ubuntu の利用者として `docker --version`・`docker buildx version`・`docker compose version`・`cdk --version`・`uv --version` がどれも終了コード 0 で版を出す
-- [ ] 3. 変更後の base で、`npm ls -g aws-cdk-lib` に `aws-cdk-lib` が無く、`/root/.local/bin/uv` が無い
-- [ ] 4. 変更後の base で、`/var/lib/apt/lists` の下に `lock` と `partial` のほかのファイルが無い
-- [ ] 5. 変更後の base で、`/usr/share/man` の下と `/usr/include/node` にファイルが無く、`/usr/share/doc` の下には `copyright` のほかのファイルが無い。`/usr/share/doc/<パッケージ>/copyright` は apt で入れたパッケージについて残る
-- [ ] 6. arm64 で、変更前の base と変更後の base の展開後の大きさの差が 450MB(450,000,000 バイト)以上ある。前後の値と差を PR に記録する
-- 7. (#403 で lfm が #400 より先に無くなるため削除。番号は参照を保つため詰めない)
-- [ ] 8. 変更後の base から、`FROM devbase-base:latest` で継ぐ派生イメージ(bi-tools・general・go・latex・php・php85・trygroup)が建つ
-- [ ] 9. `ENABLE_DIND` に `true` か `1` を設定して base のコンテナを起こすと、entrypoint は DinD を廃止したことを 1 行出して起動を続ける。`/var/run/docker.sock` を消さず、dockerd を起こさず、`docker info` を待たない
-- [ ] 10. `ENABLE_DIND` を設定しない base のコンテナの起動は、変更の前と同じ順で同じ用意を行う(entrypoint の既存の試験が通る)
-- [ ] 11. base の Dockerfile が 1〜5 を満たす形であり、`containers/base/dind` が無いことを、Docker を起こさない試験(`tests/containers/` の Dockerfile を読む試験)が固定する。外した物を Dockerfile へ戻すと、その試験が名前を挙げて落ちる
-- [ ] 12. `env -u DEVBASE_ROOT uv run --locked pytest tests/ -q` が通る
-- [ ] 13. `CHANGELOG.md` の `[Unreleased]` に、base から dockerd・containerd・グローバルの `aws-cdk-lib`・root の uv・文書を外したことと、DinD(`ENABLE_DIND`)を廃止したこと(破壊的変更。ホストの docker.sock を使う)を書く
-- [ ] 14. base の Dockerfile の差分は「対象範囲」の含むに挙げた物を外す変更だけで、ほかの道具の追加・削除・版の変更を含まない
-
-## 非機能の条件
-
-| 大項目 | 条件 |
-| --- | --- |
-| 性能・拡張性 | arm64 で展開後の大きさが 450MB 以上減る(受け入れ条件 6) |
-| 移行性 | 利用者は base を建て直すだけで移る。ボリューム・設定・機密は触らない。戻すときは前の Dockerfile で建て直す |
-| システム環境 | arm64 と amd64 の両方で建ち、受け入れ条件 1〜5 を満たす |
-
-## 影響
-
-| 対象 | 影響 |
-| --- | --- |
-| 公開インタフェース | devbase の CLI は変わらない。base のイメージから `dockerd`・`containerd`・グローバルの `aws-cdk-lib`・root の uv・文書が消える。base とその派生での `ENABLE_DIND=true` は DinD を起こさなくなり、廃止の知らせを 1 行出す |
-| データ | 変わらない |
-| 既存の振る舞い | entrypoint の DinD の分岐が無くなる。派生イメージで apt が入れるパッケージの文書が入らなくなる |
-
-## 検証手段
-
-| 項目 | 手段 |
-| --- | --- |
-| 起動 | `devbase build` で base と派生を建てる(建て方は設計で決める) |
-| テスト | `env -u DEVBASE_ROOT uv run --locked pytest tests/ -q` |
-| 静的解析・型検査 | `uvx ruff check --select=E9,F63,F7,F82 lib`・`python3 .github/scripts/proper_term_check.py`・base のシェルスクリプトの ShellCheck(開発参加ガイドの「CI が実行するもの」) |
-| 手動確認(マージ前) | arm64 で建てた base と派生で受け入れ条件 1〜6・8・9 を確かめ、出力と大きさを PR に記録する。amd64 で建てた base で受け入れ条件 1〜5 を確かめ、どの端末・どの方法で建てたかを PR に記録する |
-| 手動確認 | 利用者が自分の環境で base を建て直し、普段のプロジェクトで docker・`cdk`・uv が使えることを確かめる(リリース後テスト。P3 のため利用者が行う) |
-
-## 前提とする取り決め
-
-| 項目 | 参照先 / 決めたこと |
-| --- | --- |
-| プロジェクト構造 | `AGENTS.md` の「このリポジトリ」。イメージの定義は `containers/`、試験は `tests/containers/`、確定仕様は `docs/specifications/` |
-| コーディング規約 | `AGENTS.md` の「書き方」と開発参加ガイド。シェルは ShellCheck を通す |
-| テスト戦略 | Dockerfile の中身は Docker を起こさない試験で固定し(既存の `test_base_dockerfile_*.py` と同じ形)、entrypoint の分岐は既存の entrypoint の試験の形で確かめる。イメージを建てて確かめる条件は手動確認(マージ前)で PR に記録する |
-
-## 境界
-
-| 区分 | 内容 |
-| --- | --- |
-| 常に行う | 全体のテスト・lint・固有の語の検査を push の前に打つ。base を建てる前に `docker system df` で空きを見て、作業用のタグは記録の後に消す(`AGENTS.md` の落とし穴) |
-| 確認してから行う | 対象範囲の外の道具を base から外す・足す・版を変える(P1) |
-| 行わない | 利用者の環境で `devbase up` を実行する(P3)。タグ・Release を作る(P2)。lfm に触れる(#403 で #400 より先に廃止される)。プラグインのリポジトリの compose を直す |
-
-## 未決
-
-| 項目 | 誰が決めるか | 期限 |
-| --- | --- | --- |
-| dpkg の除外設定で外す置き場の一覧(`/usr/share/doc`・`man`・`info`・`locale`・`/usr/include/node` のどこまでか)と、除外設定を置く位置(最初の `apt-get install` より前) | 設計(`design`) | 設計 PR |
-| 外した後の `PATH` の `/root/.local/bin` を残すか消すか | 設計(`design`) | 設計 PR |
-| amd64 を建てる端末と方法(前提 7) | 実装の担当 | 手動確認(マージ前) |
-
-## 変更の記録
-
-| 日 | 工程 | 変えたこと | 理由 |
-| --- | --- | --- | --- |
-| 2026-10-03 | 設計 | 前提 1 を「DinD を lfm に残す」から「DinD を廃止する」へ改め、前提 2・対象範囲・E6〜E8・受け入れ条件 7・9・11・13・影響・境界・未決を合わせて直した | 要求を書いた後に #403 で lfm の廃止が決まり、DinD を使える唯一の派生が無くなる。`ENABLE_DIND` を設定する者も 0 件である |
-| 2026-10-03 | 設計 | #403 が #400 より先に入る前提へ改めた。前提 9 を足し、lfm の事実の 2 行・前提 2・5 の lfm・対象範囲の lfm の取り込み・E6・E7・受け入れ条件 7・影響・検証手段・境界・未決の lfm を消すか直した。E7 と受け入れ条件 7 は番号を詰めずに削除と書いた | 利用者が #403 → #400 → #401 → #402 の順に入れると決め、#400 で lfm への影響を考えなくてよいとコメントした |
diff --git a/issues/issue-402-design.md b/issues/issue-402-design.md
deleted file mode 100644
index d14fbf3b..00000000
--- a/issues/issue-402-design.md
+++ /dev/null
@@ -1,467 +0,0 @@
-# base: ブラウザ・追加の太さのフォント・terraform を使わない利用者も約 1.2GB を取得・展開・保存している → base から外し、要るプロジェクトはブラウザの派生イメージ devbase-browser を選ぶ(#402)
-
-## 目的
-
-- **何が壊れているか**: base が、Playwright の Chromium とその依存パッケージ・追加の太さのフォント・`terraform`(arm64 で計約 1.28GB)を全員に配っている
-- **誰が困るか**: これらを使わない大半のプロジェクトの利用者。base とその派生イメージを建てるたびに取得・展開・保存の費用を払う
-- **直すと何が成り立つか**: base はすべての利用者が使う道具だけを持つ。ブラウザと文書のフォントが要るプロジェクトは、ブラウザの派生イメージ `devbase-browser` を選べば、今の base と同じ描画とブラウザを建て直した直後からネットワーク無しで使える
-
-## 適用範囲
-
-- **働く範囲**: このリポジトリの `containers/`(base・新設の `browser`・bi-tools のコメント)と、その検査・仕様・利用者向けの文書。プラグインのリポジトリは、ブラウザを使うプロジェクト `proj-a` の移行を別の Pull Request で扱う(移り方はその Pull Request で決める。決定 7)
-- **前提とする順**: #403(派生イメージ lfm の廃止)・#400・#401(システムの Chrome を両アーキとも base から外す)が先に入った `main` の上に作る(決定 9)。この変更は lfm とシステムの Chrome を扱わない
-- **プロジェクトごとに違うもの**: どのイメージを使うかは、プロジェクトの `compose.yml` の dev サービスの `build.context`(または自前の Dockerfile の `FROM`)で受ける。引数や環境変数は足さない
-- **当たるモード**: `standard`
-
-## あるべき姿の根拠
-
-| 根拠 | 種類 | 何を示すか |
-| --- | --- | --- |
-| 依頼の表の大きさ(Chromium 662MB・依存の apt 296MB・`fonts-noto-cjk-extra` 215MB・`terraform` 108MB。arm64) | 実測(2026-10-03、課題の本文) | 外す中身が 1.2GB を超える |
-| プラグインのリポジトリ 3 つの `compose.yml`・`env`・`project.yml`(各 131)とフック 3 つに、`playwright`・`chromium`・`chrome`・`puppeteer`・`selenium`・`headless`・`noto-cjk`・`terraform` の当たりが 0 件 | 実測(この設計の `grep -rIilE`、2026-10-03) | 設定の上でブラウザや `terraform` を求めるプロジェクトは無い |
-| 手元にコードがあるプロジェクトのうち、ブラウザを使うのは `proj-a` の 1 つだけ(`.mcp.json` で `npx chrome-devtools-mcp@latest` と `npx -y @playwright/mcp@latest` を起動) | 実測(`projects/*/repo` の `grep`) | 移す Pull Request が要るのは 1 つ |
-| 「base を重くしすぎず、重い道具は派生イメージに置く」 | 利用者の指示の原文(MVV 版 1 の Value 1) | 重い道具の置き場は派生イメージ |
-| 利用者が 2026-10-03 に、依頼の 4 つの中身を base から外すことを承認した | 利用者の指示の原文(課題の本文。P1) | base の道具を変える承認がある |
-| #402 の課題のコメント(2026-10-03): lfm は #403 で廃止し、#400〜#402 より先に入れる。この課題の設計では lfm への影響を考えなくてよい | 利用者の指示 | この変更は lfm を扱わない |
-| #401 の本文と、スプリント m7c の順(#403 → #400 → #401 → #402) | 利用者の指示 | この変更の時点で、システムの Chrome は両アーキとも base に無い |
-
-要求と受け入れ条件は #402 の本文にある(コピーは `issues/issue-402-requirements.md`)。この文書は「どう作るか」だけを扱う。
-前提・受け入れ条件・ドメインイベント・未決の番号は、要求の番号をそのまま使う。
-設計の中で要求の前提 5(システムの Chrome)と前提 6(lfm)を、#401 と #403 が先に入る形へ改め、本文の「変更の記録」に残した(決定 9)。lfm を扱う E5・受け入れ条件 11 と、この文書の I14・I15・F7 は欠番にした。
-
-## ドメインモデル
-
-### コンテキスト
-
-| コンテキスト | 何の語が 1 つの意味に決まるか |
-| --- | --- |
-| ブラウザ(`browser`) | ブラウザの置き場・Playwright の Chromium・ブラウザの依存パッケージ・ブラウザの派生イメージ・システムの Chrome |
-| base イメージ(`base-image`) | 追加の太さのフォント。base が持つフォントの一覧 |
-| イメージの継承(`image-lineage`) | 派生イメージ。派生イメージが base から継ぐもの(ブラウザの置き場と `ENV PLAYWRIGHT_BROWSERS_PATH`) |
-
-`image-lineage` が `browser` に**順応者**として従う。ブラウザの派生イメージは、base が宣言するブラウザの置き場(`/opt/ms-playwright`)をそのまま使い、別の置き場へ訳し直す層を持たない(#220 と同じ関係)。`base-image` と `browser` の間も同じで、ブラウザの派生イメージは base のフォントの規則(`/etc/fonts/local.conf`)を書き換えずに継ぐ。
-
-### 集約
-
-この変更の集約は Dockerfile の命令の組である。持ち主は、その命令を書き換えてよい Dockerfile を指す。
-
-| 集約 | 持ち主(書き換えてよいもの) | 根 | エンティティ | 値オブジェクト |
-| --- | --- | --- | --- | --- |
-| base の apt の一覧 | `containers/base/Dockerfile` | 1 つ目の `RUN` の 2 回の `apt-get install` | — | パッケージ名・apt の取得元 |
-| base のブラウザの置き場 | `containers/base/Dockerfile` | `ENV PLAYWRIGHT_BROWSERS_PATH` | — | 置き場の値・所有者と権限(中身は空) |
-| ブラウザの派生イメージ | `containers/browser/Dockerfile`(新設) | `FROM devbase-base:latest` | Playwright の Chromium(版ごとのディレクトリ) | ブラウザの依存パッケージ・追加の太さのフォント |
-| プロジェクトのイメージの選択 | プラグインのリポジトリの `compose.yml`(このリポジトリの外) | dev サービスの `build` | — | `build.context`・自前の Dockerfile の `FROM` |
-
-ブラウザの派生イメージは base のブラウザの置き場の値を書き換えず、置き場の中身(Chromium)だけを足す。
-
-### 不変条件
-
-| # | 集約 | 条件 | 破れたときの扱い |
-| --- | --- | --- | --- |
-| I1 | base の apt の一覧 | base の Dockerfile に `npx playwright install` が無く、`apt-get install` の一覧に `fonts-noto-cjk-extra` と `terraform` が無く、HashiCorp の apt の取得元が無い | Dockerfile の形の検査が、名前を挙げて落ちる |
-| I2 | base の apt の一覧 | 1 回目の `apt-get install` の一覧に `fonts-liberation`・`fonts-ipafont-gothic`・`fonts-wqy-zenhei` がある | Dockerfile の形の検査が落ちる。建てた base の `fc-match` の解決先が変わる |
-| I3 | base の apt の一覧 | `npm i -g` の一覧に `@playwright/test` が残る | Dockerfile の形の検査が落ちる |
-| I4 | base のブラウザの置き場 | `ENV PLAYWRIGHT_BROWSERS_PATH=/opt/ms-playwright` が 1 つあり、置き場を `ubuntu:npm`・`2775` で作る root の `RUN` より前にある | Dockerfile の形の検査が落ちる |
-| I5 | base の apt の一覧 | `fc-cache -f` は 1 度だけで、`/etc/fonts/local.conf` の `COPY` より後にある | フォントの形の検査が落ちる |
-| I6 | base の apt の一覧 | 建てた base の `fc-match` の解決先が、確定仕様の「解決先」の表のまま変わらない | 建てた base の解決先の検査が落ちる |
-| I7 | base のブラウザの置き場 | 建てた base の置き場に Chromium の版のディレクトリが無く、`fonts-noto-cjk-extra` と `terraform` が無い | 建てた base の検査が落ちる |
-| I8 | base のブラウザの置き場 | 建てた base で Playwright の Chromium を起動すると非 0 で終わり、出力に `playwright install` が含まれる | 建てた base の検査が落ちる |
-| I9 | ブラウザの派生イメージ | Dockerfile の最初の `FROM` が `devbase-base:latest` である | Dockerfile の形の検査と、`devbase build` の経路の検査が落ちる |
-| I10 | ブラウザの派生イメージ | `npx playwright install --with-deps chromium` を利用者の `RUN` で 1 回打ち、その片付けの `rm -rf` が `/opt` と置き場を対象にせず、`/var/lib/apt/lists` を消す | Dockerfile の形の検査が落ちる |
-| I11 | ブラウザの派生イメージ | `fonts-noto-cjk-extra` を apt で入れ、`fc-cache -f` が 1 度だけ、Playwright と apt の `RUN` より後にある | Dockerfile の形の検査が落ちる |
-| I12 | ブラウザの派生イメージ | 建てたイメージで、#220 の受け入れ条件 5〜9 が成り立つ(ネットワーク無しの PDF・置き場への書き込み・`NotoSansCJKjp`・ブラウザの依存パッケージの存在と `chromium-browser` の不在・環境変数) | 建てたブラウザの派生イメージの検査が落ちる |
-| I13 | ブラウザの派生イメージ | 建てたイメージに `fonts-noto-cjk-extra` があり、`Noto Sans CJK JP` の `Thin` と `Black` のフェイスがある | 建てたブラウザの派生イメージの検査が落ちる |
-| I16 | base の apt の一覧 | `containers/bi-tools/Dockerfile` の先頭のコメントが、base に含まれるものとして `terraform` を挙げない | Dockerfile の形の検査が落ちる |
-
-I14・I15(lfm のブラウザ)は欠番にした。lfm は #403 で先に廃止される(決定 9)。
-
-### ドメインイベント
-
-| # | イベント | 発生元 | 受け手 |
-| --- | --- | --- | --- |
-| E1 | base のビルドが、`fonts-noto-cjk-extra`・`terraform` を除き、3 つのフォントを明示した apt の一覧で入れた | base の 1 つ目の `RUN` | E3 の `fc-cache` |
-| E2 | base のビルドが、Chromium を取得せず `--with-deps` の apt も打たずに終わった | base の利用者の `RUN` | E4(空の置き場を渡す) |
-| E3 | base のビルドがフォントのキャッシュを作り直した | base の `RUN sudo fc-cache -f` | 建てた base の解決先の検査(I6) |
-| E4 | ブラウザの派生イメージのビルドが、base の上に Chromium・ブラウザの依存パッケージ・追加の太さのフォントを入れた | ブラウザの派生イメージの `RUN` | 派生イメージの `fc-cache`・建てた派生イメージの検査(I12・I13) |
-| E6 | 利用者が base 系のコンテナで Playwright の Chromium を起動しようとし、失敗した | 利用者・道具 | 利用者(Playwright の出力の `npx playwright install` と、利用者向けの文書の移り方) |
-| E7 | 利用者が base 系のコンテナで、実行時にブラウザとその依存パッケージを取得した | `npx playwright install --with-deps chromium` | ブラウザの置き場(I4 の権限で書き込める)。コンテナを作り直すと消える |
-| E8 | ブラウザを使うプロジェクトが、`compose.yml` で使うイメージを替えた | プラグインのリポジトリへの Pull Request のマージ | そのプロジェクトの `devbase build`(E4 か、プロジェクトの Dockerfile のビルド) |
-
-E5(lfm のビルド)は欠番にした(要求の E5 と同じ)。
-
-### 用語
-
-| 用語 | 意味 | 用語集への反映 |
-| --- | --- | --- |
-| ブラウザの派生イメージ | `containers/browser`(タグ `devbase-browser:latest`)。base を継ぎ、Playwright の Chromium・ブラウザの依存パッケージ・追加の太さのフォントを足す | 追加(`browser`) |
-| ブラウザの置き場 | Playwright がブラウザを取得して置くディレクトリ。`PLAYWRIGHT_BROWSERS_PATH` が指す `/opt/ms-playwright`。base は空のまま作り、ブラウザの派生イメージが中身を持つ。コンテナの利用者が書き込める | 意味の変更(`browser`) |
-| Playwright の Chromium | `playwright install chromium` がブラウザの置き場へ取得する Chromium(版ごとのディレクトリ)。ブラウザの派生イメージに入る | 意味の変更(`browser`) |
-| ブラウザの依存パッケージ | `playwright install` の `--with-deps` が apt で入れるパッケージ。base は、このうち `fonts-liberation`・`fonts-ipafont-gothic`・`fonts-wqy-zenhei`・`libnss3` などを明示して持つ | 意味の変更(`browser`) |
-| 派生イメージ | 要求の段で足した定義から、lfm を除く一文を外したもの(lfm は #403 で廃止) | 反映済み(出所を確定仕様へ直すのは確定仕様化の工程) |
-| 追加の太さのフォント | 要求の段で足した定義のまま | 反映済み(同上) |
-
-コンテキスト `browser` の名前と意味も「base イメージのブラウザ」から「ブラウザ」(ブラウザの派生イメージに入るブラウザと、base が作る置き場)へ直す。
-
-## 機能一覧
-
-| # | 機能 | 誰が使うか |
-| --- | --- | --- |
-| F1 | base を建てると、ブラウザ・追加の太さのフォント・`terraform` の無い、小さい base ができる | すべての利用者 |
-| F2 | base で日本語・中国語・韓国語の標準の太さと欧文の metric 互換が、今と同じ書体で描ける | すべての利用者 |
-| F3 | ブラウザの派生イメージを選ぶと、ネットワーク無しで Chromium を起動し、日本語のページを PDF やスクリーンショットにできる | 文書を描く・ブラウザを動かすプロジェクト |
-| F4 | ブラウザの派生イメージで、Noto CJK の追加の太さ(Thin〜Black)で描ける | 文書を描くプロジェクト |
-| F5 | base 系のコンテナで Chromium を起動すると、取得のコマンドを示して止まる | base 系のコンテナでブラウザを使おうとした利用者・道具 |
-| F6 | base 以外の派生イメージ(`php` など)を使うプロジェクトが、自分の Dockerfile でブラウザの依存パッケージを足せる | `proj-a` など |
-| F8 | 利用者向けの文書と CHANGELOG で、外れたものと移り方が分かる | すべての利用者 |
-
-F7(lfm がブラウザ・追加の太さのフォント・`terraform` を持ち続けること)は欠番にした(lfm は #403 で廃止)。
-
-## 構成要素
-
-型(クラス)は持たない。Dockerfile の命令・設定ファイル・検査だけで構成する。
-
-| 要素 | 置き場所 | 責務 |
-| --- | --- | --- |
-| base の apt の一覧 | `containers/base/Dockerfile` の 1 つ目の `RUN` | 1 回目の一覧から `fonts-noto-cjk-extra` を外し、3 つのフォントを明示する。HashiCorp の取得元を外す。2 回目の一覧から `terraform` を外す |
-| base の利用者の `RUN` | `containers/base/Dockerfile` の uv・claude・agy・kiro を入れる `RUN` | `npx playwright install --with-deps chromium` の行を外す。片付けは今のまま残す |
-| base のブラウザの置き場 | `containers/base/Dockerfile` の `ENV PLAYWRIGHT_BROWSERS_PATH` と root の `RUN` の `install -d` | 今のまま残す。コメントを「中身はブラウザの派生イメージが持つ」に直す |
-| base のフォントのキャッシュ | `containers/base/Dockerfile` の `RUN sudo fc-cache -f` | 位置は今のまま。コメントの「Playwright の RUN の後」の理由を外す |
-| ブラウザの派生イメージ | `containers/browser/Dockerfile`・`containers/browser/compose.yml`(新設) | base を継ぎ、`fonts-noto-cjk-extra` を apt で入れ、Chromium と依存パッケージを置き場へ取得し、フォントのキャッシュを作り直す |
-| bi-tools のコメント | `containers/bi-tools/Dockerfile` の先頭 | 「base に既に含まれるもの」から `terraform` を外す |
-| base の形の検査 | `tests/containers/test_base_dockerfile_playwright.py`・`test_base_dockerfile_fonts.py` | I1〜I5・I16(bi-tools のコメント)を Docker 無しで縛る |
-| ブラウザの派生イメージの形の検査 | `tests/containers/test_browser_dockerfile.py`(新設) | I9〜I11 を Docker 無しで縛る |
-| `devbase build` の経路の検査 | `tests/cli/test_wrapper_shellcheck_fixes.py` と同じ起動の形のテスト | ブラウザの派生イメージを選んだプロジェクトの `devbase build` が base を先に建てる(受け入れ条件 10) |
-| 建てた base の検査 | `tests/containers/test_base_image_font_matching.py`(変えない)・`tests/containers/test_base_image_without_browser.py`(新設) | I6〜I8 |
-| 建てたブラウザの派生イメージの検査 | `tests/containers/test_browser_image.py`(`test_base_image_browser.py` を移す) | I12・I13。検査するイメージを `DEVBASE_TEST_BROWSER_IMAGE` で差し替える |
-| 確定仕様 | `docs/specifications/base-image-rendering.md` | base にブラウザが無いこと、ブラウザの派生イメージの形 |
-| 利用者向けの文書 | `docs/user/container-operations.md` | イメージの表に `browser` を足し、移り方(イメージの選び方・プロジェクトの Dockerfile・`terraform` の入れ方)を書く |
-| 用語集 | `docs/glossary/glossary.json`・`docs/glossary.md` | 用語の表の反映 |
-| CHANGELOG | `CHANGELOG.md` の `[Unreleased]` | 外れたもの・移り方の文書へのリンク・建て直しの順 |
-| `proj-a` の移行 | プラグインのリポジトリの `proj-a` の `compose.yml`(選ぶ道筋によってはプロジェクトの Dockerfile か `.mcp.json`) | 2 つの MCP が動くブラウザを用意する。道筋はその Pull Request で決める(決定 7) |
-
-### 構成要素図
-
-```mermaid
-graph TD
- subgraph このリポジトリ
- subgraph 定義
- BaseApt[base の apt の一覧]
- BaseRun[base の利用者の RUN]
- BasePath[base のブラウザの置き場]
- BaseCache[base のフォントのキャッシュ]
- Browser[ブラウザの派生イメージ]
- BiTools[bi-tools のコメント]
- end
- subgraph 検査
- BaseShape[base の形の検査]
- BrowserShape[派生イメージの形の検査]
- BuildPath[devbase build の経路の検査]
- BaseImg[建てた base の検査]
- BrowserImg[建てた派生イメージの検査]
- end
- subgraph 文書
- Spec[確定仕様]
- UserDoc[利用者向けの文書]
- Glossary[用語集]
- Changelog[CHANGELOG]
- end
- end
- subgraph プラグインのリポジトリ
- ProjA[proj-a の移行]
- end
- BaseApt --> BaseCache
- BasePath --> Browser
- BaseCache --> Browser
- BaseShape --> BaseApt
- BaseShape --> BaseRun
- BaseShape --> BasePath
- BaseShape --> BiTools
- BrowserShape --> Browser
- BuildPath --> Browser
- BaseImg --> BaseCache
- BrowserImg --> Browser
- UserDoc --> Browser
- UserDoc --> ProjA
- Changelog --> UserDoc
- Spec --> Glossary
- ProjA --> BaseApt
-```
-
-`proj-a の移行` から `base の apt の一覧` への辺は、`proj-a` が使う `devbase-php`(またはそれを継ぐプロジェクトの Dockerfile)が base を継ぐことを表す。
-
-### システムの文脈と配置
-
-```mermaid
-graph LR
- User[利用者] -->|devbase build / up| Devbase[devbase の配布物]
- Plugin[プラグインのリポジトリ] -->|compose.yml でイメージを選ぶ| Devbase
- Devbase -->|apt| Ubuntu[Ubuntu のアーカイブ]
- Devbase -->|ブラウザの派生イメージのビルドで取得| CDN[Playwright のブラウザの配布元]
- Tools[実行時の道具 @playwright/mcp・playwright-kit] -->|実行時に自分の版を取得| CDN
-```
-
-| 実行の単位 | 何が動くか | 境界をまたぐもの |
-| --- | --- | --- |
-| base のビルド(`docker buildx build`) | apt・npm・各 CLI の取得 | Ubuntu のアーカイブと各配布元。Playwright の配布元へは出なくなる。HashiCorp の apt へも出なくなる |
-| ブラウザの派生イメージのビルド | apt(追加の太さのフォント・依存パッケージ)と Chromium の取得 | Ubuntu のアーカイブと Playwright の配布元 |
-| コンテナ(base 系) | 道具が実行時にブラウザを取得するなら、そのたびに配布元へ出る | 置き場はイメージの外の変更で、作り直すと消える |
-
-### 置き場所
-
-```text
-containers/
-├── base/Dockerfile # 変える
-├── browser/ # 新設
-│ ├── Dockerfile
-│ └── compose.yml
-└── bi-tools/Dockerfile # コメントだけ変える
-tests/
-├── cli/ # devbase build の経路の検査を足す
-└── containers/
- ├── test_base_dockerfile_playwright.py # 変える
- ├── test_base_dockerfile_fonts.py # 変える
- ├── test_base_image_without_browser.py # 新設
- ├── test_browser_dockerfile.py # 新設
- └── test_browser_image.py # test_base_image_browser.py を移す
-docs/
-├── specifications/base-image-rendering.md # 変える
-├── user/container-operations.md # 変える
-└── glossary/glossary.json # 変える(glossary.md は作り直す)
-```
-
-### ブラウザの派生イメージの中身
-
-| 順 | 命令 | 理由 |
-| --- | --- | --- |
-| 1 | `FROM devbase-base:latest` | `devbase build` が base を先に建てる経路に乗る(I9) |
-| 2 | root で `apt-get install -y --no-install-recommends fonts-noto-cjk-extra`、同じ `RUN` で `/var/lib/apt/lists` を消す | 標準のアーカイブのパッケージで、外部の取得元が要らない |
-| 3 | 利用者 `ubuntu` で `npx playwright install --with-deps chromium`、同じ `RUN` の末尾で `sudo rm -rf /tmp/* ~/.cache ~/.npm /var/lib/apt/lists/*` | 今の base の利用者の `RUN` と同じ取得で、同じ版・同じ依存パッケージになる。`npx` は base の `@playwright/test` の CLI を使う。片付けは置き場(`/opt`)を対象にしない(I10) |
-| 4 | `RUN sudo fc-cache -f` | 2 と 3 が入れたフォントをキャッシュへ載せる(I11) |
-
-`compose.yml` は `containers/latex/compose.yml` と同じ形(dev サービス・`image: devbase-browser:latest`・`build.context: .`)にする。`ENV PLAYWRIGHT_BROWSERS_PATH` と置き場は base から継ぐため、この Dockerfile では宣言しない(決定 3)。
-
-### 移るプロジェクト
-
-| 対象 | 今 | 移る先 | 出す Pull Request |
-| --- | --- | --- | --- |
-| `proj-a`(dev サービスは `devbase-php`。`.mcp.json` で `npx chrome-devtools-mcp@latest` と `npx -y @playwright/mcp@latest` を起動) | base のシステムの Chrome か Playwright の Chromium で動いていると見込む(どちらを使っているかは確かめていない。「未確認のまま残ること」) | 3 つの道筋(`devbase-browser` を選ぶ・プロジェクトの Dockerfile で足す・MCP の起動引数でブラウザの場所を渡す)から、プラグインのリポジトリの Pull Request で決める(決定 7) | プラグインのリポジトリへ 1 本(C5。出す前に人の確認を取る) |
-| `proj-a` のアプリの `docker-compose.dev.yml` の `seleniarm/standalone-chromium` | アプリ自身のテスト用の別イメージ | 変えない(base を使わない) | 無し |
-| 設定の上でブラウザの当たりが無いプロジェクト(プラグインのリポジトリ 3 つの全プロジェクト) | — | 変えない。コードがボリュームの中で見えない範囲は未確認(「未確認のまま残ること」) | 無し |
-| 手元に無いプロジェクト | — | CHANGELOG と利用者向けの文書の移り方で知らせる(前提 7) | 無し |
-
-#401 とこの変更の後は、`devbase-php` を含む base 系のイメージに、システムの Chrome も Playwright の Chromium とその依存パッケージも無い。`proj-a` の 2 つの MCP は、どちらもそのままでは動くブラウザを持たない。`@playwright/mcp` は既定でシステムの Chrome(channel `chrome`)を探すとされ、`chrome-devtools-mcp` もシステムの Chrome を探すと見込まれる。どちらも、依存パッケージを足すだけで動くとは言えない。起動引数でブラウザ(`--browser chromium` など)や実行ファイルの場所を渡して動くかは確かめていない(「未確認のまま残ること」)。
-
-### 同じ層を触る課題との境目
-
-#403・#400・#401 は同じスプリントで、この変更より先に入る。#400・#401 は同じ `containers/base/Dockerfile` の 1 つ目の `RUN` と利用者の `RUN` を触り、#403 は lfm とその検査・仕様を消す。
-
-| 課題 | base の Dockerfile で触る所 | この変更と重なる所 |
-| --- | --- | --- |
-| #403(lfm の廃止。最初に入る) | `containers/lfm/`・lfm の検査・`lfm-base-settings.md`・base の lfm を前提にしたコメント | base の置き場と `fc-cache` の上のコメント。#403 が直した後の文言の上で、この変更の分だけを直す。lfm の部分はこの変更に無い |
-| #400(dockerd・`aws-cdk-lib`・root の uv・片付けの漏れ。2 番目に入る) | 2 回目の `apt-get install` の `docker-ce`・`containerd.io`。1 回目の前の dpkg の除外設定。root の uv。`npm i -g` の `aws-cdk-lib`。利用者の `RUN` の片付けへ `/var/lib/apt/lists` を足す | 2 回目の一覧の 1 行(`terraform` と同じ行)。利用者の `RUN` の片付けとそのコメント |
-| #401(システムの Chrome を両アーキとも base から外す。3 番目に入る) | Google の取得元と `BROWSER_PKG` の分岐。2 回目の一覧の `$BROWSER_PKG`。`test_system_chrome_stays_amd64_only` | 2 回目の一覧の 1 行。#401 が残したブラウザのコメント(「Chromium は Playwright のものを使う」の類)を、この変更でブラウザの派生イメージを指す形へ直す |
-| この変更 | 1 回目の一覧(`fonts-noto-cjk-extra`・3 つのフォント)。HashiCorp の取得元。2 回目の一覧の `terraform`。利用者の `RUN` の `npx playwright install`。`ENV` と `install -d` と `fc-cache` のコメント | — |
-
-実装の順は #403 → #400 → #401 → #402 にする(決定 9)。この変更は 3 つが入った `main` の上に作り、重なる行は先に入った課題の変更を残したうえで自分の分だけを直す。2 回目の一覧は #400・#401・この変更がそれぞれ別の語を消すため、先に入った削除を残して自分の語だけを消す。
-
-## 処理の流れ
-
-### base のビルド
-
-```mermaid
-graph TD
- A[1 つ目の RUN: 1 回目の apt に 3 つのフォントを明示し、fonts-noto-cjk-extra を入れない] --> B[取得元を足す: Docker・GitHub CLI・NodeSource。HashiCorp は足さない]
- B --> C[2 回目の apt: terraform を入れない]
- C --> D[ENV PLAYWRIGHT_BROWSERS_PATH]
- D --> E[root の RUN: 置き場を ubuntu:npm 2775 で作る。npm i -g に @playwright/test]
- E --> F[利用者の RUN: Chromium を取得しない。片付け]
- F --> G[fonts-local.conf を COPY]
- G --> H[fc-cache -f]
-```
-
-`fc-cache -f` の位置は変わらない。今の位置は「Playwright がフォントを入れた後」を理由にしていたが、Playwright の `RUN` が base から消えると、残る理由は「`COPY` の後」と「`~/.cache` を消した後にキャッシュを作り直す」の 2 つになる(I5)。E3 の順序の前提(キャッシュが 3 つのフォントを知っている)は、3 つが 1 つ目の `RUN` で入ることで満たされる。
-
-### ブラウザの派生イメージのビルド
-
-```mermaid
-sequenceDiagram
- participant U as 利用者
- participant W as devbase build
- participant B as base のビルド
- participant D as 派生イメージのビルド
- participant C as Playwright の配布元
- U->>W: devbase build(dev が containers/browser を選ぶプロジェクト)
- W->>W: Dockerfile の FROM devbase-base を見つける
- W->>B: devbase-base を建てる(キャッシュあり)
- B-->>W: 成功
- W->>D: docker compose build dev
- D->>D: fonts-noto-cjk-extra を apt で入れる
- D->>C: npx playwright install --with-deps chromium
- C-->>D: Chromium を置き場へ
- D->>D: 依存パッケージを apt で入れ、片付ける
- D->>D: fc-cache -f
- D-->>U: devbase-browser の dev イメージ
-```
-
-`devbase build browser`(単体ビルド)は base を先に建てない。既存の派生イメージと同じ扱いで、この変更では変えない(決定 8)。
-
-### 実行時
-
-| 場面 | base 系(base・`general`・`php` など) | ブラウザの派生イメージ |
-| --- | --- | --- |
-| イメージの Playwright で Chromium を起動する | 失敗する。Playwright が「`npx playwright install` を打て」と出す(E6・I8) | 置き場の Chromium を起動する。取得は起きない |
-| 利用者が `npx playwright install chromium` を打つ | 置き場へ取得できるが、依存パッケージが無いため起動で失敗する。Playwright が `install-deps` を出す | 同じ版なら何も起きない |
-| 利用者が `sudo npx playwright install --with-deps chromium` 相当を打つ(E7) | 起動できるようになる。コンテナを作り直すと消える | — |
-| 道具が自分の版で取得する(`@playwright/mcp`・`playwright-kit`) | 取得は済むが、依存パッケージが無いため起動で失敗する | 置き場へ自分の版を足して起動する |
-
-## 非機能の実現方式
-
-| 大項目 | 要求の条件 | 実現方式 | 確かめ方 |
-| --- | --- | --- | --- |
-| 性能・拡張性 | base が arm64 で 1.2GB 以上減る。ブラウザの派生イメージの大きさを変更前の base と比べて記録する | 外す 4 つは base の層から命令ごと消す。3 つのフォントだけを残す。ブラウザの派生イメージは今の base の取得と同じ命令を足すだけにし、層を増やしすぎない | 同じ arm64 の端末で、変更前の base・変更後の base・ブラウザの派生イメージを作業用のタグで建て、展開後のファイルの大きさ(`du -sxb /`)と `.Size`(圧縮後)の値と差を PR に残す。建てる前に `docker system df` で空きを見る。1.2GB に届かなければフォントを外さず人へ戻す(前提 3) |
-| 移行性 | 移る作業は `compose.yml` の 1 か所か Dockerfile の追記で済む。ボリュームとデータに触れない | イメージの選択だけで移れるよう、ブラウザの派生イメージを `containers/` に置き、`build.context: ${DEVBASE_ROOT}/containers/browser/` で選べるようにする。置き場の値は base と同じにする | 利用者向けの文書の移り方の節を、`proj-a` の Pull Request の差分と突き合わせる |
-| 運用・保守性 | 建て直しの順(base → 派生イメージ → `devbase down` / `up`)を文書と CHANGELOG に書く | 確定仕様の「運用」と利用者向けの文書の注記に、3 段とも省けないことを書く。`devbase rebuild` では建て直らないことも書く | PR で文書の記述を `grep` した出力を残す(#220 の決定 4 と同じ) |
-| システム環境 | 受け入れ条件は arm64 で確かめ、amd64 はリリース後テスト | アーキで分ける命令を新しく足さない。Playwright の CLI がアーキに合う Chromium を選ぶ | arm64 は手元で建てて検査を通す。amd64 は #242 と同じくリリース後テストで同じ検査を通す |
-
-## 決定の記録
-
-### 決定 1: 全員に配る重さを減らすため、#220 で base に残すと決めた Playwright の Chromium と依存パッケージを base から外す
-
-#220 は「ブラウザはイメージに残す」と決め(#220 の要求の結論)、その帰結として Playwright の取得を `--with-deps` のまま base に残した(#220 の前提 2・決定 2)。この変更はこの 2 つを覆す。#220 の時点では、ブラウザを使わないプロジェクトの数と、依存パッケージの大きさ(296MB)を測っていなかった。今回、設定の上でブラウザを求めるプロジェクトが無く、コードの見えるプロジェクトで使うのは 1 つだけと分かり、利用者が 2026-10-03 に base からの削除を承認した。ブラウザをネットワーク無しで使えるという #220 の目的は、ブラウザの派生イメージがそのまま引き継ぐ(I12)。
-
-#220 の決定 1(置き場の作り方)と決定 3(検査するイメージを環境変数で差し替える)は覆さず、置き場は base に、検査の形はブラウザの派生イメージに引き継ぐ。#220 の決定 5(システムの Chrome は amd64 だけに残す)は、先に入る #401 が覆し、システムの Chrome は両アーキとも base から外れている。この変更はシステムの Chrome を扱わない(前提 5)。
-
-base に Chromium を残して依存パッケージだけを外す形は採らない。依存パッケージが無いと Chromium は起動できず、残す意味が無い。Chromium だけを外して依存パッケージを残す形は、外せる量が 662MB に留まり 1.2GB に届かない。
-
-根拠: Value 1 / P1(MVV 版 1)
-
-### 決定 2: 文書を描く使い方を 1 つの選択で揃えるため、ブラウザと追加の太さのフォントを 1 つの派生イメージ `containers/browser`(`devbase-browser`)にまとめる
-
-追加の太さのフォント(Thin〜Black)が効くのは、CSS の `font-weight` で太さを指定したページをブラウザで描くときである。描く道具とフォントを別のイメージに分けると、文書を描くプロジェクトは両方が要るのに 1 つしか選べない。まとめると、今の base で描けていたものが、ブラウザの派生イメージを選ぶだけで同じに描ける。名前は役割の中心が Chromium であるため `browser` にする。
-
-ブラウザとフォントを別のイメージ(例 `browser` と `docs`)に分ける案は、両方を要るプロジェクトが選べなくなるため採らない。名前を `docs` にする案は、ブラウザの操作(E2E のテストや MCP)に使うプロジェクトが選びにくくなるため採らない。`containers/latex` に足す案は、LaTeX を要らないプロジェクトにも TeX Live を配ることになるため採らない。
-
-根拠: Value 1(MVV 版 1)
-
-### 決定 3: ブラウザの派生イメージと実行時の取得が同じ置き場を使うため、base に `ENV PLAYWRIGHT_BROWSERS_PATH` と空の置き場を残す
-
-置き場を base に残すと、ブラウザの派生イメージは `ENV` と置き場を宣言せずに継げる。base 系のコンテナで道具が実行時に取得したときも、今と同じ置き場に入る。空のディレクトリを足すだけで、大きさは増えない。
-
-`ENV` と置き場を base から外し、ブラウザの派生イメージで宣言する案は採らない。base 系のコンテナでの実行時の取得先が `~/.cache/ms-playwright` に変わって、今の置き場の説明(用語集)と食い違う。
-
-根拠: Value 1(MVV 版 1)
-
-### 決定 4: base へ道具を足さずに伝えるため、ブラウザが無いことは Playwright 自身の失敗の出力に任せる
-
-base で Playwright の Chromium を起動すると、Playwright は置き場に実行ファイルが無いことと `npx playwright install` を出して止まる。取得した後に依存パッケージが無ければ、`install-deps` を打てと出す。これで受け入れ条件 5 の出力の条件を満たし、次の手は利用者向けの文書の移り方が補う。base の大きさも振る舞いも増えない。
-
-案内のための包み(`playwright` のラッパーや起動時の知らせ)を base に足す案は、base へ新しい道具を足すことになり P1 の承認の外にあるため採らない。実行時に `--with-deps` で取得させる案は、コンテナを作り直すたびに数百 MB を取り直し、ネットワークの無い所で動かないため採らない。
-
-根拠: Value 1 / P1(MVV 版 1)
-
-### 決定 5: base が配らない道具の取得元を持たないため、HashiCorp の apt の取得元を base から外す
-
-取得元を残すと、base を継ぐ派生イメージで `apt-get update` を打つたびに HashiCorp の配布元へ出て、配布元が止まると派生イメージのビルドが止まる。`terraform` を要るプロジェクトは、利用者向けの文書に置く 3 行(鍵・取得元・`apt-get install`)を自分の Dockerfile かフックで打てば入る。
-
-取得元を残して `sudo apt-get install terraform` だけで入るようにする案は、使う者の見当たらない道具のために全員の `apt-get update` に外部の取得元を足し続けるため採らない。
-
-根拠: Value 1 / Value 3(MVV 版 1)
-
-### 決定 6: 3 つのフォントの出どころを Dockerfile から読めるようにするため、1 つ目の `RUN` の 1 回目の一覧へ、既にあるフォントの行の隣に置く
-
-3 つ(`fonts-liberation`・`fonts-ipafont-gothic`・`fonts-wqy-zenhei`)は標準のアーカイブのパッケージで、文書を扱う道具と metric 互換のフォントと同じ 1 回目の一覧が置き場になる(確定仕様の「データ・設定」と同じ規則)。今までは `--with-deps` の副作用で入っていたため、確定仕様の「入れないもの」の `fonts-wqy-zenhei` の行は「Dockerfile に導入の行は無く、Playwright が依存として入れる」と書いていた。これを「1 回目の一覧で明示して入れる」に直す。
-
-`--with-deps` が入れていたほかのフォント(絵文字・`fonts-unifont`・`fonts-freefont-ttf`・`fonts-tlwg-loma-otf`・`xfonts-*`)は明示しない。前提 2 が残すのは `fc-match` の解決先の表に現れる 3 つだけで、ほかを残すことは承認の外にある。
-
-根拠: Value 1 / P1(MVV 版 1)
-
-### 決定 7: プロジェクトの道具立てに合う道筋を選べるよう、`proj-a` の移り方はプラグインのリポジトリの Pull Request で決め、この変更は選べる道筋を文書に書く
-
-`proj-a` の dev サービスは `devbase-php` を使い、PHP の道具をそのまま要する。`.mcp.json` は `npx chrome-devtools-mcp@latest` と `npx -y @playwright/mcp@latest` を起動する。#401 の後は base 系のイメージにシステムの Chrome が無く、この変更の後は Playwright の Chromium とその依存パッケージも無い。2 つの MCP は、どちらもそのままでは動くブラウザを持たない。
-
-道筋は 3 つある。
-
-| 道筋 | 何をするか | 気をつけること |
-| --- | --- | --- |
-| `devbase-browser` を選ぶ | `compose.yml` の dev の `build.context` を `containers/browser` へ替える | PHP の道具が無くなる。`proj-a` はそのままでは選べない |
-| プロジェクトの Dockerfile で足す | `FROM devbase-php:latest` の上に、ブラウザの依存パッケージ(`npx playwright install-deps chromium`)や Chromium を足す | 2 段の継承になる(下の段落)。依存パッケージだけでは足りず、2 つの MCP が探すブラウザも用意する |
-| MCP の起動引数でブラウザの場所を渡す | `.mcp.json` の起動引数で、2 つの MCP に同じブラウザの実行ファイルを指させる | ブラウザと依存パッケージは、ほかの 2 つの道筋のどちらかで用意する |
-
-どれを採るかは、`proj-a` の Pull Request の中で 2 つの MCP が動くことを確かめて決める(C5。出す前に人の確認を取る)。この変更が受け持つのは、3 つの道筋を利用者向けの文書の移り方に書くことと、その Pull Request を出すこと(受け入れ条件 13)である。
-
-設計の初版は「プロジェクトの Dockerfile で依存パッケージだけを足す」に決めていた。しかし `@playwright/mcp` は既定でシステムの Chrome(channel `chrome`)を探すとされ、`chrome-devtools-mcp` もシステムの Chrome を探すと見込まれる。#401 で Chrome が両アーキとも外れると、どちらも依存パッケージだけで動くとは言えない(確かめていない)。このため道筋を 1 つに決めず、`proj-a` の側で確かめて選ぶ形に改めた。
-
-ブラウザの派生イメージを `FROM` に取る `php` 用の派生(例 `php-browser`)を devbase に足す案は、組み合わせの数だけ派生イメージが増えるため採らない。`./deploy` フックで起動のたびに入れる案は、作り直すたびに約 300MB を取り直すため採らない。`containers/php` に足す案は、`php` を使う 47 のプロジェクトの大半が使わない重さを配るため採らない。
-
-プロジェクトの Dockerfile の道筋を採ると 2 段の継承(`proj-a` の Dockerfile → `devbase-php` → base)になり、`devbase build` は `devbase-php` を建てるが、その下の base が無いときに先に建てない(「未確認のまま残ること」)。`proj-a` の利用者の端末には base があるため、移行は止まらない。
-
-根拠: Value 1 / Value 2 / C5(MVV 版 1)
-
-### 決定 8: 受け入れ条件 10 を既存の仕組みで満たすため、プロジェクトの `devbase build` の経路で確かめ、単体ビルドは変えない
-
-base を先に建てるのは、プロジェクトの dev サービスの Dockerfile の `FROM devbase-*` を見る経路(`bin/devbase` の `check_base_image_dependency`)である。単体ビルド `devbase build browser`(`_build_single_image`)は、既存の派生イメージと同じく base を先に建てない。ブラウザの派生イメージの最初の `FROM` を `devbase-base:latest` にすれば、プロジェクトの経路にそのまま乗る(I9)。確かめは `bin/devbase` を実プロセスで起動する既存の検査の形で、Docker 無しで行う。
-
-単体ビルドに base の先行ビルドを足す案は、すべての派生イメージの振る舞いを変え、この課題の範囲を超えるため採らない。
-
-根拠: Value 1(MVV 版 1)
-
-### 決定 9: 先に入る課題の結果の上に作るため、実装の順を #403 → #400 → #401 → #402 にする
-
-スプリント m7c の 4 つの課題は、この順に 1 本ずつ `main` へ入る(#402 の課題のコメント、#400 の設計)。#403 が lfm を消すため、この変更は lfm を扱わない(前提 6。要求の E5・受け入れ条件 11 と、この文書の I14・I15・F7 は欠番)。#400 は利用者の `RUN` の片付けに `/var/lib/apt/lists` を足し、この変更はその `RUN` から Playwright を外すため、#400 を先に入れると片付けの変更の意図が履歴に残る。#401 の受け入れ条件 4 は、base の `test_base_image_browser.py` を amd64 で通すことを求める。この変更がこのファイルをブラウザの派生イメージへ移すため、#401 を先に入れると #401 の条件を書いたまま確かめられる。#401 がシステムの Chrome を両アーキとも base から外すため、この変更の後の base 系のイメージにはブラウザが 1 つも無い(前提 5・決定 7)。
-
-大きさの差(受け入れ条件 7)は、3 つが入った後の `main` の base を「変更前」として測る。#400 の文書の除外(dpkg の `path-exclude`)が先に入ると、依存パッケージの文書の分だけこの変更で減る量は小さくなる。#401 が外すのは amd64 の Chrome だけで、arm64 で測るこの差には効かない。1.2GB に届かないときは前提 3 のとおり人へ戻す。
-
-この変更を #403 より先に入れる案(設計の初版の順)は採らない。スプリントの順は #403 を先に決めており、先に入れると lfm の取得の振る舞いを変えた直後に lfm ごと消すことになり、受け入れ条件 11 と lfm の仕様の直しが無駄になる。順を入れ替えるときは、後に入る課題の設計を、先に入った変更に合わせて読み替える。
-
-根拠: Value 1 / P1(MVV 版 1)
-
-### 決定 10: 依頼の 1.2GB と同じ物差しで測るため、受け入れ条件 7 は展開後のファイルの大きさで判定する
-
-実装で測ると、containerd のイメージストアの `docker image inspect` の `.Size` は圧縮後の大きさを返した。差は 611,133,492 バイトで、展開後のファイル(`du -sxb /`)の差は 1,291,660,733 バイトだった。依頼の表の大きさ(Chromium 662MB など)と課題の本文の「展開後の大きさが 1.2GB 以上減る」は展開後の物差しであり、受け入れ条件 7 が `.Size` を指したのは測り方の取り違えだった。前提 3 のとおり人へ戻し、利用者が展開後の大きさで判定すると決めた(2026-10-03)。`.Size` の値も PR に並べて残す。取得の量(圧縮後)の減りは約 611MB である。
-
-根拠: 課題の本文の受け入れ条件 1 / 前提 3
-
-## テスト設計
-
-| 受け入れ条件・不変条件 | どの振る舞いで縛るか | どう壊したら落ちるべきか |
-| --- | --- | --- |
-| 受け入れ条件 1・I1 | base の Dockerfile の命令に `npx playwright install`・`fonts-noto-cjk-extra`・`terraform`・`apt.releases.hashicorp.com` が無い | どれか 1 つを戻すと、その名前を挙げて落ちる |
-| 受け入れ条件 2・I2 | 3 つのフォントが 1 つ目の `RUN` の 1 回目の `apt-get install` の一覧にある | 1 つ消す、または 2 回目の一覧へ移すと落ちる |
-| 受け入れ条件 3・I3 | `npm i -g` の一覧に `@playwright/test` がある | 一覧から外すと落ちる |
-| I4 | base に `ENV PLAYWRIGHT_BROWSERS_PATH=/opt/ms-playwright` が 1 つで、置き場を作る `install -d` の `RUN` より前にある | `ENV` を消す・2 つにする・後ろへ動かすと落ちる |
-| I5 | base の `fc-cache -f` が 1 度だけで、`fonts-local.conf` の `COPY` より後にある(「Playwright の `RUN` より後」の検査はブラウザの派生イメージへ移す) | `fc-cache` を `COPY` の前へ動かすと落ちる |
-| 受け入れ条件 4・I6 | 建てた変更後の base で `test_base_image_font_matching.py` が `EXPECTED_MATCHES` を書き換えずに通る | 3 つのフォントの 1 つを外して建てると、`Arial` / `WenQuanYi Zen Hei` / `IPAPGothic` のどれかの行が落ちる |
-| 受け入れ条件 5・I8 | `--network none` の変更後の base で、利用者 `ubuntu` の非対話の `bash -c` から Chromium を起動すると非 0 で、出力に `playwright install` か `devbase-browser` が含まれる | base に Chromium を戻して建てると、起動が 0 で終わって落ちる |
-| 受け入れ条件 6・I7 | 変更後の base で `dpkg -s fonts-noto-cjk-extra` と `command -v terraform` が非 0、置き場に `chromium-*`・`chromium_headless_shell-*` が無い | どれかを戻して建てると、その項目が落ちる |
-| 受け入れ条件 7 | 変更前と変更後の base の展開後のファイルの大きさ(`du -sxb /`)の差が 1,200,000,000 以上(手動。`.Size` の値とともに PR に残す) | — |
-| 受け入れ条件 8・I12 | 移したテストが、`DEVBASE_TEST_BROWSER_IMAGE`(既定 `devbase-browser:latest`)のイメージで #220 の受け入れ条件 5〜9 を確かめる。明示したイメージに置き場の中身が無ければ skip せず落とす | ブラウザの派生イメージの片付けに置き場を足して建てると、PDF の作成と置き場の項目が落ちる |
-| 受け入れ条件 9・I13 | 建てたブラウザの派生イメージで `dpkg -s fonts-noto-cjk-extra` が 0、`fc-list` に `Noto Sans CJK JP` の `Thin` と `Black` がある | 派生イメージの apt から `fonts-noto-cjk-extra` を外して建てると落ちる |
-| 受け入れ条件 10・I9 | ブラウザの派生イメージの最初の `FROM` が `devbase-base:latest`。dev の `build.context` が `containers/browser` のプロジェクトで `bin/devbase build` を起動すると、`devbase-base` を建ててから `docker compose build dev` を打つ | `FROM` を別の名前にすると、形の検査と経路の検査の両方が落ちる |
-| I10 | ブラウザの派生イメージの利用者の `RUN` に `npx playwright install --with-deps chromium` が 1 つあり、片付けが `/opt`・置き場を対象にせず `/var/lib/apt/lists` を消す | 片付けに `/opt` を足す、または `/var/lib/apt/lists` を外すと落ちる |
-| I11 | ブラウザの派生イメージの apt に `fonts-noto-cjk-extra` があり、`fc-cache -f` が 1 度だけで apt と Playwright の `RUN` より後にある | `fc-cache` を Playwright の前へ動かすと落ちる |
-| 受け入れ条件 12 | 利用者向けの文書と CHANGELOG の記述(PR に `grep` の出力を残す。#220 の決定 4 と同じく pytest で縛らない) | — |
-| 受け入れ条件 13 | `proj-a` の移り方の 3 つの道筋が利用者向けの文書にあり、プラグインのリポジトリの Pull Request の URL を PR に残す(手動) | — |
-| 受け入れ条件 14 | 確定仕様の記述(PR に `grep` の出力を残す)。#220 を覆したことは決定 1 にある | — |
-| 受け入れ条件 15・I16 | `containers/bi-tools/Dockerfile` の先頭のコメントに `terraform` が無い | コメントへ `terraform` を戻すと落ちる |
-| 受け入れ条件 16 | 全体のテスト・lint・固有の語の検査(push の前に打つ) | — |
-
-受け入れ条件 11・I14・I15(lfm)は欠番である(決定 9)。
-
-## 設計の結果
-
-| 課題 | 扱い | 取り込み先 | 触るファイル |
-| --- | --- | --- | --- |
-| #402 | 実装する | — | `containers/base/Dockerfile`、`containers/browser/`、`containers/bi-tools/Dockerfile`、`tests/containers/`、`tests/cli/`、`docs/specifications/base-image-rendering.md`、`docs/user/container-operations.md`、`docs/glossary/glossary.json`、`docs/glossary.md`、`CHANGELOG.md` |
-
-## 未確認のまま残ること
-
-| 項目 | 内容 |
-| --- | --- |
-| ボリュームの中のコード | プラグインのリポジトリの設定と、コードの見える `proj-a` 以外のプロジェクトがブラウザ・追加の太さのフォント・`terraform` を使うかは、コードがボリュームの中にあり見えない。使っていたプロジェクトは、base を建て直した後に E6 の失敗か `command not found` で気づく。CHANGELOG と利用者向けの文書の移り方で知らせる |
-| `latex` の文書の書体 | `containers/latex` を使う 1 つのプロジェクトが、文書で Noto CJK の追加の太さを名指ししているかは見えない。名指ししていれば、base を建て直した後に別の太さへ置き換わる。`latex` は変えない |
-| base から消えるほかのフォント | 絵文字(`fonts-noto-color-emoji`)・`fonts-unifont` などは base から消える(決定 6)。base でブラウザ以外の道具がこれらに頼って描いているかは確かめていない |
-| Playwright の失敗の文言 | 決定 4 は、置き場に実行ファイルが無いときの Playwright の出力に `npx playwright install` が含まれることを前提にする。変更後の base を建てて受け入れ条件 5 の検査で確かめる |
-| `proj-a` の 2 つの MCP | `chrome-devtools-mcp` は Playwright ではなくシステムの Chrome を探すと見込み、`@playwright/mcp` も既定でシステムの Chrome(channel `chrome`)を探すとされる(どちらも確かめていない。起動引数で `--browser chromium` などを渡して動くかも確かめていない)。#401 とこの変更の後は、`devbase-php` を含む base 系のイメージにシステムの Chrome も Playwright の Chromium も無く、`npx chrome-devtools-mcp@latest` と `npx -y @playwright/mcp@latest` はどちらもブラウザを見つけられない。移り方(`devbase-browser` を選ぶ・プロジェクトの Dockerfile で足す・MCP の起動引数でブラウザの場所を渡す)はプラグインのリポジトリの Pull Request で決め、そこで 2 つの MCP が動くことを確かめる(決定 7)。その Pull Request がマージされるまで、`proj-a` で base を建て直すと 2 つの MCP は動かない |
-| 2 段の継承の先行ビルド | `proj-a` がプロジェクトの Dockerfile の道筋を選ぶなど、派生イメージを `FROM` に取る Dockerfile では、`devbase build` は base が無いときに先に建てない(決定 7・決定 8)。今の利用者の端末には base があるため移行は止まらない。新しい端末での扱いは #404 で扱う |
-| 減る量 | 3 つのフォントを残し、#400 の文書の除外が先に入るため、減る量は依頼の表の合計より小さい。1.2GB に届くかは建てて測るまで分からない(前提 3) |
-| amd64 | amd64 で受け入れ条件 4〜11 が成り立つかは、リリース後テストで確かめる |
diff --git a/issues/issue-402-requirements.md b/issues/issue-402-requirements.md
deleted file mode 100644
index 91bbb64d..00000000
--- a/issues/issue-402-requirements.md
+++ /dev/null
@@ -1,222 +0,0 @@
-# #402: Playwright のブラウザ・追加の CJK フォント・terraform を base から派生イメージへ移す
-
-正は課題の本文(#402)で、この文書はその写しである。`spec-copy.py write` で作り直す。手で直さない。
-
-## 依頼(原文)
-
-起票時の本文をそのまま写す。
-
-> ## 何をするか
->
-> すべての利用者には要らない重い中身を、base から派生イメージへ移す。base の削減の段階 3。利用者は 2026-10-03 にこの削減を承認した(MVV の P1)。
->
-> ## 対象(arm64 の実測)
->
-> | 中身 | 大きさ | 移す先の案 |
-> | --- | --- | --- |
-> | Playwright の Chromium(`/opt/ms-playwright`。chromium 393MB・headless_shell 266MB) | 662MB | ブラウザを使うプロジェクト向けの派生イメージ |
-> | `playwright install --with-deps` の apt(libllvm21・mesa・フォント・xvfb など 74 個) | 296MB | 同上 |
-> | `fonts-noto-cjk-extra`(Thin・Light・Medium・Black などの追加の太さだけ。標準の太さは `fonts-noto-cjk` にある) | 215MB | 文書を作る派生イメージ |
-> | `terraform` | 108MB | 外す。使う者は見当たらない(`grep -rIlE terraform repos/*/`)。要るプロジェクトは派生か `post-start` で入れる |
->
-> base の `@playwright/test` は残す。利用時にブラウザを取りに行けるようにするかは設計で決める。
->
-> ## 決めること(設計)
->
-> - 派生イメージの切り方: ブラウザと文書のフォントを 1 つ(例 `containers/browser`)にまとめるか分けるか。名前と、`devbase build` の依存の扱い
-> - ブラウザを使っているプロジェクトの洗い出しと移り方(プラグインのリポジトリの `compose.yml` の image を替える。PR はプラグインのリポジトリへ)
-> - base で Playwright を使ったときに、ブラウザが無いことをどう伝えるか(案内か、置き場へ取得させるか)
-> - #220 で「base に Chromium を残す」と決めた判断を覆すことの記録。lfm は自分で Playwright を入れるため影響しない見込み
-> - #220 の受け入れ条件 5〜9 の検査(`tests/containers/test_base_image_browser.py`)を派生イメージへ移す
->
-> ## 受け入れ条件
->
-> 1. base に Playwright のブラウザ・その依存の apt・`fonts-noto-cjk-extra`・`terraform` が無い。base の展開後の大きさが arm64 で 1.2GB 以上減る(前後で測り、PR に記録する)
-> 2. 派生イメージで #220 の受け入れ条件 5〜9(PDF の作成と置き場の形)が通る
-> 3. 標準の太さの日本語の描画は base のまま変わらない(`tests/containers/test_base_image_font_matching.py` が通る)
-> 4. ブラウザを使うプロジェクトは移る先が文書に書かれ、移すための PR がプラグインのリポジトリに出ている
-> 5. 確定仕様(`base-image-rendering.md` など)、利用者向けの文書、CHANGELOG を直す
->
-> ## 由来
->
-> base の削減のプラン(2026-10-03)。段階 1 と段階 2 は別の課題。
-
-## 目的
-
-- base を、すべての利用者が使う道具だけを持つ大きさへ戻す。ブラウザ・追加の太さのフォント・`terraform` を使わないプロジェクトは、それらを取得・展開・保存しなくて済む(MVV の Value 1「base を重くしすぎず、重い道具は派生イメージに置く」)
-- ブラウザと文書のフォントを要るプロジェクトは、派生イメージを選べば今と同じ描画とブラウザを、建て直した直後からネットワーク無しで使える
-
-## 調べたこと
-
-要求を決めるために手元で確かめた事実。設計はここから始める。
-
-| # | 事実 | 確かめた場所 |
-| --- | --- | --- |
-| F1 | `--with-deps` の apt は、ブラウザの共有ライブラリのほかに `fonts-liberation`・`fonts-ipafont-gothic`・`fonts-wqy-zenhei` を入れている。base の Dockerfile にこの 3 つを入れる行は無い | `docs/specifications/base-image-rendering.md` の「キャッシュの作り直し」・`containers/base/Dockerfile` |
-| F2 | `tests/containers/test_base_image_font_matching.py` は `Arial` → `Liberation Sans`、`WenQuanYi Zen Hei` の名指し → `WenQuanYi Zen Hei`、`IPAPGothic` の名指し → `IPAPGothic` を期待する。F1 の 3 つが base から消えると、この検査は落ちる | 同テストの `EXPECTED_MATCHES` |
-| F3 | 中国語・韓国語を明示した指定の解決先(Noto Sans CJK SC / KR など)は標準の太さのフェイスで、`fonts-noto-cjk` にある | `base-image-rendering.md` の「中国語・韓国語を明示した指定」 |
-| F4 | 派生イメージ lfm は #403 で廃止され、#403 はこの課題より先に入る(スプリント m7c の順は #403 → #400 → #401 → #402)。この課題の時点で lfm は無い | #402 の課題のコメント・#403 の本文 |
-| F5 | プラグインのリポジトリの dev サービスは、`build.context: ${DEVBASE_ROOT}/containers/<名前>/` で派生イメージを選ぶ。手元の 3 リポジトリでの数は `general` 79・`php` 47・`bi-tools`・`go`・`latex`・`php85`・`trygroup` が 1 ずつ。`image:` で選ぶものは `devbase-php:latest` が 1 つ | `repos/*` の `compose.yml` |
-| F6 | 手元にコードがあるプロジェクト(`projects/*/repo`)でブラウザを使うのは 1 つ(以下 `proj-a`)だけで、dev サービスは `devbase-php` を使い、`.mcp.json` で `npx chrome-devtools-mcp@latest` と `npx -y @playwright/mcp@latest` を起動する。`@playwright/mcp` は自分の版の Playwright を持ち、base の Chromium と版が合わなければ実行時に取得する。`chrome-devtools-mcp` はシステムの Chrome を探すと見込まれ、システムの Chrome は #401 で両アーキとも base から外れる | `proj-a` の `.mcp.json`・`compose.yml` |
-| F7 | NDF の `layout-review` は HTML のスクリーンショットを `playwright-kit` で撮る。`playwright-kit` も実行時に自分の版の Chromium をブラウザの置き場へ取得する | NDF の `skills/layout-review/SKILL.md`・`base-image-rendering.md` の「ブラウザ」 |
-| F8 | 実行時に取得したブラウザが起動できるのは、`--with-deps` の共有ライブラリが base にあるためである。依存パッケージが無いと、取得は済んでも起動で落ちる。利用者 `ubuntu` は `sudo` をパスワード無しで使えるため、実行時に `--with-deps` で入れ直すことはできる(コンテナを作り直すと消える) | `containers/base/Dockerfile` の sudoers・`base-image-rendering.md` の「運用」 |
-| F9 | `terraform` の名前は、プラグインのリポジトリの設定と手元のプロジェクトのコードに現れない。`containers/bi-tools/Dockerfile` の先頭のコメントが「base に含まれるもの」として `terraform` を挙げている | `grep -rIlE terraform repos/*/ projects/*/repo`・`containers/bi-tools/Dockerfile` |
-| F10 | base を `FROM` で継ぐ派生イメージは `devbase build <名前>` が base の有無と鮮度を見て先に建てる。新しい派生イメージも `FROM devbase-base:latest` で書けばこの仕組みに乗る | `bin/devbase` の `check_base_image_dependency`・`lib/devbase/commands/container.py` の `_get_base_image_ref` |
-
-## 前提
-
-- 前提 1: P1(base の道具の変更)の承認は、依頼の 4 つの中身を base から外すことに対して 2026-10-03 に得ている。base へ新しく道具を足すこと(前提 2 のフォントの明示を除く)は、この承認に含まれない
-- 前提 2: F1 の 3 つのフォント(`fonts-liberation`・`fonts-ipafont-gothic`・`fonts-wqy-zenhei`)は base に残し、`--with-deps` に頼らず base の apt の一覧で明示して入れる。base の今の中身を保つための書き換えで、道具を足すことではない。これにより `fc-match` の解決先の表(`base-image-rendering.md` の「解決先」)は 1 行も変わらない(受け入れ条件 3)
-- 前提 3: 前提 2 の 3 つのフォントを残すため、減る量は依頼の表の合計(約 1.28GB)より数十 MB 小さい。それでも 1.2GB を下回るなら、フォントを外して 1.2GB を満たすのではなく、測った値を記録して人へ戻す
-- 前提 4: base の共有ライブラリのうち、Dockerfile が明示して入れている `libnss3`・`libxrandr2`・`libxss1` は残す。外すのは `--with-deps` だけが入れていたパッケージである
-- 前提 5: システムの Chrome(`google-chrome-stable`)は #401 で両アーキとも base から外れている(#401 が先に入る)。この変更はシステムの Chrome を扱わない。#401 とこの変更の後は base 系のイメージにブラウザが無くなり、F6 の 2 つの MCP はどちらもそのままでは動かない。`proj-a` の移り方(ブラウザの派生イメージを選ぶか、プロジェクトの Dockerfile で足すか、MCP の起動引数でブラウザの場所を渡すか)はプラグインのリポジトリ側の Pull Request で決める
-- 前提 6: 派生イメージ lfm は #403 で先に廃止されている(F4)。この変更は lfm を扱わない
-- 前提 7: ブラウザを使うプロジェクトの洗い出しは、手元にある 3 つのプラグインのリポジトリと `projects/*/repo` に限る。手元に無いプロジェクトは、CHANGELOG と利用者向けの文書の移り方で知らせる
-- 前提 8: 派生イメージの名前・数(ブラウザと文書のフォントを 1 つにまとめるか分けるか)・base に `ENV PLAYWRIGHT_BROWSERS_PATH` とブラウザの置き場を残すか・HashiCorp の apt の取得元を残すかは設計で決める。受け入れ条件はどの切り方でも判定できる形で書く
-- 前提 9: 大きさは同じ arm64 の端末で、変更前の base と変更後の base を、展開後のファイルの大きさ(コンテナで `du -sxb /`)で測り、差を PR に記録する。`docker image inspect --format '{{.Size}}'`(containerd のイメージストアでは圧縮後の大きさ)の値も並べて残す。base を 2 本並べるため、建てる前に `docker system df` で空きを見て、作業用のタグは記録の後に消す(`AGENTS.md` の落とし穴)
-
-## 対象範囲
-
-含む:
-
-- base の Dockerfile から、Playwright の Chromium の取得・`--with-deps` の apt・`fonts-noto-cjk-extra`・`terraform` を外す
-- 前提 2 の 3 つのフォントを base の apt の一覧で明示する
-- ブラウザと追加の太さのフォントを持つ派生イメージを `containers/` に足す(数と名前は設計)
-- base で Playwright の Chromium が無いときの伝え方(設計で決める形)
-- #220 の受け入れ条件 5〜9 の検査(`tests/containers/test_base_image_browser.py`)と、Dockerfile の形の検査(`tests/containers/test_base_dockerfile_playwright.py`・`test_base_dockerfile_fonts.py`)を、新しい形に合わせて移す・直す
-- `containers/bi-tools/Dockerfile` の先頭のコメントの `terraform` を直す
-- 確定仕様(`base-image-rendering.md`)・利用者向けの文書(`docs/user/container-operations.md` など)・用語集・`CHANGELOG.md` の `[Unreleased]`
-- ブラウザを使うプロジェクト(F6 のプロジェクト `proj-a`)の移り方を文書に書き、プラグインのリポジトリへ移すための Pull Request を出す(境界の「確認してから行う」)
-- #220 で「base に Chromium を残す」と決めた判断を覆すことの記録(設計文書の決定の記録)
-
-含まない:
-
-- 段階 1・段階 2 の削減(別の課題)
-- システムの Chrome の扱い(#401。前提 5)
-- lfm(#403 で先に廃止。前提 6)
-- `proj-a` の移り方の選択(プラグインのリポジトリの Pull Request で決める。前提 5)
-- base の `@playwright/test` を外すこと(依頼が残すと決めている)
-- 手元に無いプロジェクトへの個別の Pull Request(前提 7)
-- amd64 での実測(#242 と同じく、amd64 の端末で建てるまで分からない。リリース後テストへ回す)
-- 実際の利用者の環境での `devbase up`(P3)
-
-## ドメインイベント
-
-| # | イベント | 引き金 | 失敗したとき | 順序の前提 |
-| --- | --- | --- | --- | --- |
-| E1 | base のビルドが、`fonts-noto-cjk-extra`・`terraform` を除き、前提 2 の 3 つのフォントを明示した apt の一覧で入れた | `devbase build base` | apt が失敗すればビルドが非 0 で止まる | — |
-| E2 | base のビルドが、Chromium を取得せず `--with-deps` の apt も打たずに終わった | E1 の後の利用者の `RUN` | — | E1 → 受け入れ条件 1・2 |
-| E3 | base のビルドがフォントのキャッシュを作り直した | フォントの規則の `COPY` | — | E1 の後。**Playwright の `RUN` がフォントを入れなくなっても、キャッシュが前提 2 の 3 つを知っていると仮定している** → 受け入れ条件 4 |
-| E4 | 派生イメージのビルドが、base の上に Chromium・ブラウザの依存パッケージ・追加の太さのフォントを入れた | `devbase build <派生イメージ>`。base が無いか古ければ先に E1〜E3 が起きる(F10) | 取得か apt が失敗すればビルドが非 0 で止まる | E1〜E3 → 受け入れ条件 6〜9 |
-| E6 | 利用者が base(または base を継ぐ派生イメージのうちブラウザを持たないもの)のコンテナで Playwright の Chromium を起動しようとし、失敗した | 利用者・道具の起動 | 失敗の出力か文書から、次にすることが辿れなければ利用者が止まる → 受け入れ条件 5 | E2 |
-| E7 | 利用者が base のコンテナで、実行時にブラウザとその依存パッケージを取得した | `npx playwright install --with-deps chromium` など | ネットワークが無ければ失敗する。コンテナを作り直すと消える(F8) | E6 と独立 → 未決 U2 |
-| E8 | ブラウザを使うプロジェクトが、`compose.yml` で派生イメージを選ぶように替わった | プラグインのリポジトリへの Pull Request のマージ | Pull Request が出ていなければ、そのプロジェクトは base 系のまま E6 に当たる | E4 が `main` に入った後 → 受け入れ条件 13 |
-
-E5(lfm のビルド)は欠番にした(前提 6)。
-
-## 用語
-
-| 用語 | 意味 |
-| --- | --- |
-| 派生イメージ | `FROM devbase-base:latest` で base を継ぎ、道具を足すイメージ(`containers/` の `general`・`php` など) |
-| 追加の太さのフォント | `fonts-noto-cjk-extra` が入れる Noto CJK の Thin・Light・Medium・Black などのフェイス。標準の太さ(Regular・Bold)は `fonts-noto-cjk` にある |
-| ブラウザの置き場 | 用語集の定義のまま |
-| Playwright の Chromium | 用語集の定義のまま |
-| ブラウザの依存パッケージ | 用語集の定義のまま |
-| システムの Chrome | 用語集の定義のまま |
-
-## 受け入れ条件
-
-「変更後の base」は、変更後の `containers/base` から作業用のタグ(前提 9)で建てた arm64 のイメージを指す。「ブラウザの派生イメージ」は、設計が決める、ブラウザを持つ派生イメージを指す(文書のフォントと同じイメージか別かは前提 8)。
-
-Dockerfile の形(Docker を起動しないテストで確かめる):
-
-- [ ] 1. `containers/base/Dockerfile` に `npx playwright install` が無く、`apt-get install` の一覧に `fonts-noto-cjk-extra` と `terraform` が無い
-- [ ] 2. `containers/base/Dockerfile` の 1 回目の `apt-get install` の一覧に `fonts-liberation`・`fonts-ipafont-gothic`・`fonts-wqy-zenhei` がある(前提 2)
-- [ ] 3. `containers/base/Dockerfile` の `npm i -g` の一覧に `@playwright/test` が今のまま残る
-
-変更後の base(arm64):
-
-- [ ] 4. `tests/containers/test_base_image_font_matching.py` が、`EXPECTED_MATCHES` を書き換えずに変更後の base で通る(標準の太さの日本語・中国語・韓国語と欧文の metric 互換の解決先が変わらない)
-- [ ] 5. 変更後の base を `--network none` で起動し、利用者 `ubuntu` の非対話の `bash -c` で Playwright の Chromium を headless で起動しようとすると、終了コードが非 0 で、出力にブラウザの派生イメージの名前か取得のコマンド(`playwright install`)が含まれる
-- [ ] 6. 変更後の base で `dpkg -s fonts-noto-cjk-extra` が非 0、`command -v terraform` が非 0 で終わり、`/opt/ms-playwright` の下に Chromium の版のディレクトリ(`chromium-*`・`chromium_headless_shell-*`)が無い
-- [ ] 7. 変更前の base と変更後の base の展開後のファイルの大きさ(`du -sxb /`)の差が、arm64 で 1.2GB(1,200,000,000 バイト)以上ある。両方の値と差を、`.Size`(圧縮後)の値と差とともに PR に記録する(前提 3・前提 9)
-
-ブラウザの派生イメージ(arm64):
-
-- [ ] 8. ブラウザの派生イメージで、#220 の受け入れ条件 5〜9(ネットワーク無しで日本語のページを PDF にできる・利用者がブラウザの置き場に書ける・PDF の書体が `NotoSansCJKjp` で `WenQuanYi` を含まない・ブラウザの依存パッケージがあり `chromium-browser` が無い・`PLAYWRIGHT_BROWSERS_PATH` が置き場を指し `~/.cache/ms-playwright` が無い)が通る。検査は `tests/containers/test_base_image_browser.py` を移したテストが、検査するイメージを環境変数で差し替えて行う
-- [ ] 9. 追加の太さのフォントを持つ派生イメージで `dpkg -s fonts-noto-cjk-extra` が 0 で終わり、`fc-list` に `Noto Sans CJK JP` の `Thin` と `Black` のフェイスがある
-- [ ] 10. `devbase build <ブラウザの派生イメージ>` が、base が無いときに base を先に建ててから派生イメージを建てる(F10 の仕組みに乗る)
-
-受け入れ条件 11(lfm)は欠番にした(前提 6)。
-
-移り方と文書:
-
-- [ ] 12. 利用者向けの文書に、ブラウザ・追加の太さのフォント・`terraform` が要るプロジェクトの移り方(派生イメージを選ぶ `compose.yml` の書き方、または `post-start` で入れる手順)が書かれている。`CHANGELOG.md` の `[Unreleased]` に、base から外れたものと移り方の文書へのリンクがある
-- [ ] 13. F6 のプロジェクト `proj-a` について、移る先(ブラウザの派生イメージを選ぶか、プロジェクトの Dockerfile で足すか、MCP の起動引数でブラウザの場所を渡すか)が文書に書かれ、移すための Pull Request がプラグインのリポジトリに出ている(境界の「確認してから行う」)
-- [ ] 14. `docs/specifications/base-image-rendering.md` が、base に Chromium・追加の太さのフォント・`terraform` が無いことと、ブラウザの派生イメージの置き場を書く。#220 の「base に Chromium を残す」を覆したことが、設計文書の決定の記録に理由とともにある
-- [ ] 15. `containers/bi-tools/Dockerfile` の先頭のコメントが、base に含まれるものとして `terraform` を挙げていない
-
-退行しない:
-
-- [ ] 16. `env -u DEVBASE_ROOT uv run --locked pytest tests/ -q`・`uvx ruff check --select=E9,F63,F7,F82 lib`・`python3 .github/scripts/proper_term_check.py` が通る
-
-## 非機能の条件
-
-| 大項目 | 条件 |
-| --- | --- |
-| 性能・拡張性 | base の大きさが arm64 で 1.2GB 以上減る(受け入れ条件 7)。ブラウザの派生イメージの大きさは、変更前の base と比べて記録する(上限は置かない) |
-| 移行性 | 移る作業は、プロジェクトの `compose.yml` の 1 か所(派生イメージの選択)か `post-start` の追記で済む。利用者のボリュームとデータには触れない |
-| 運用・保守性 | 変更は base と派生イメージを建て直すまで効かない。建て直しの順(base → 派生イメージ → `devbase down` / `up`)を利用者向けの文書と CHANGELOG に書く |
-| システム環境 | 受け入れ条件は arm64 で確かめる。amd64 で同じ条件が成り立つかはリリース後テストで確かめる |
-
-## 影響
-
-| 対象 | 影響 |
-| --- | --- |
-| 公開インタフェース | 変わる。base(と base を継ぐ `general`・`php` など)から Chromium・ブラウザの依存パッケージ(前提 2 の 3 つのフォントを除く)・追加の太さのフォント・`terraform` が消える。互換は保たない。移り方を文書と CHANGELOG で示す |
-| データ | 変わらない。利用者のボリュームには触れない |
-| 既存の振る舞い | base 系のコンテナで Playwright の Chromium・`@playwright/mcp`・`playwright-kit` を使う道具は、ブラウザの派生イメージへ移るか実行時に依存パッケージごと取得するまで動かない(F6〜F8)。#401 でシステムの Chrome も外れているため、`chrome-devtools-mcp` も `proj-a` の移行の Pull Request が入るまで動かない(前提 5) |
-
-## 検証手段
-
-| 項目 | 手段 |
-| --- | --- |
-| 起動(変更後の base) | `docker system df` で空きを見てから `docker build -t devbase-base:issue-402 containers/base` を建て、`DEVBASE_TEST_BASE_IMAGE=devbase-base:issue-402` で受け入れ条件 4〜6 の検査を打つ |
-| 起動(派生イメージ) | 変更後の base を `devbase-base:latest` として派生イメージを建て、受け入れ条件 8〜10 の検査を打つ |
-| テスト | `env -u DEVBASE_ROOT uv run --locked pytest tests/ -q` |
-| 静的解析 | `uvx ruff check --select=E9,F63,F7,F82 lib`・`python3 .github/scripts/proper_term_check.py` |
-| 手動確認(マージ前) | 受け入れ条件 4〜10 を手元の arm64 で確かめ、受け入れ条件 7 の大きさの値とともに出力を PR に残す。作業用のタグは記録の後に消す |
-| 手動確認 | amd64 で受け入れ条件 4〜10 が成り立つこと。`proj-a` が移った先で `@playwright/mcp` と `chrome-devtools-mcp` を使えること(プラグインのリポジトリの Pull Request で確かめる)。リリース後テストで確かめる |
-
-## 前提とする取り決め
-
-| 項目 | 参照先 / 決めたこと |
-| --- | --- |
-| プロジェクト構造 | `AGENTS.md` の「このリポジトリ」。イメージの定義は `containers/` に置く。`projects/*` の `compose.yml` はプラグインのリポジトリの管理で、このリポジトリでは直さない |
-| コーディング規約 | `AGENTS.md` の「書き方」と `docs/developer/contributing.md`。Dockerfile の変更理由は周りと同じく行の上のコメントに課題の番号つきで書く |
-| テスト戦略 | Dockerfile の形は `tests/containers/` の Docker を要さないテストで固定する(受け入れ条件 1〜3・15)。ブラウザ・書体・大きさは建てたイメージで確かめる(受け入れ条件 4〜10)。Docker を要するテストは検査するイメージを環境変数で差し替えられる形を保つ |
-
-## 境界
-
-| 区分 | 内容 |
-| --- | --- |
-| 常に行う | 全体のテスト・lint・固有の語の検査。base を建てる前の `docker system df`。作業用のタグを記録の後に消す |
-| 確認してから行う | プラグインのリポジトリへの Pull Request(C5。`proj-a` は他の組織のリポジトリ)。base へ前提 2 の 3 つ以外の道具を足すこと(P1)。base の削減が 1.2GB に届かないときの扱い(前提 3) |
-| 行わない | タグ・GitHub Release(P2)。実際の利用者の環境での `devbase up`(P3)。システムの Chrome の扱い(#401)と lfm(#403)。段階 1・2 の範囲の変更 |
-
-## 未決
-
-| 項目 | 誰が決めるか | 期限 |
-| --- | --- | --- |
-| U1: 派生イメージの切り方(ブラウザと追加の太さのフォントを 1 つにまとめるか分けるか)と名前。`php` など base 以外の派生イメージを使うプロジェクト(F6)がブラウザを使うときの道筋(ブラウザの派生イメージを `FROM` に取るか、プロジェクト側で入れるか) | 設計 | 設計 PR の承認まで |
-| U2: base でブラウザが無いときの伝え方。Playwright 自身の失敗の出力に任せるか、案内を足すか、実行時に `--with-deps` で取得させるか(E7。取得させるとコンテナを作り直すたびに数百 MB を取り直す) | 設計 | 設計 PR の承認まで。受け入れ条件 5 はどれでも判定できる |
-| U3: base に `ENV PLAYWRIGHT_BROWSERS_PATH` とブラウザの置き場(`/opt/ms-playwright`)を残すか。残さないと、ブラウザの派生イメージが置き場を宣言し直し、base 系のコンテナで実行時に取得する先が `~/.cache/ms-playwright` に変わる | 設計 | 設計 PR の承認まで |
-| U4: HashiCorp の apt の取得元を base に残すか(残すと `post-start` で `sudo apt-get install terraform` だけで入る) | 設計 | 設計 PR の承認まで |
-| U5: 前提 2 で残す `fonts-ipafont-gothic`・`fonts-wqy-zenhei` を、将来 base から外すか。外すと `fc-match` の解決先の表と受け入れ条件 4 の検査が変わる | 利用者(P1 に当たるため) | この変更では外さない。外すなら別の課題 |
-
-## 変更の記録
-
-| 日 | 工程 | 変えたこと | 理由 |
-| --- | --- | --- | --- |
-| 2026-10-03 | 設計 | 前提 5 を「システムの Chrome は #401 で両アーキとも base から外れている。この変更は扱わない」へ、前提 6 を「lfm は #403 で先に廃止されている」へ改め、F4・F6・対象範囲・E5・用語・受け入れ条件 11・13・14・影響・検証手段・前提とする取り決め・境界・U3 を合わせて直した。E5 と受け入れ条件 11 は欠番 | スプリント m7c の順が #403 → #400 → #401 → #402 に決まり、この課題の時点で lfm とシステムの Chrome が base 系に無い |