# メンテナンスチェックリスト

コード変更時に連動して修正が必要な箇所のリスト。
過去に実際に発生した不整合をもとに作成。

---

## 1. ツールモジュールを新規追加した場合

**対象ファイル:**
- `src/engine/tools/<module>.ts` — 新モジュール本体
- `src/engine/tools/index.ts` — `tryLoadModule` で動的ロード追加
- `src/bridge/tools-api.ts` — `/api/tools` のモジュール一覧に追加
- `CLAUDE.md` — 「ツールモジュール構成」テーブルに行を追加

**なぜ必要か:**
`index.ts` はエージェント実行時のツール dispatch に使われ、`tools-api.ts` は Settings UI のピース編集画面でツール候補を返す API。
`tools-api.ts` への追加を忘れると、エンジンでは使えるのに UI のドロップダウンに表示されないという不整合が起きる（実際に amazon, speech, checklist, knowledge の4モジュールで発生した）。

**確認方法:**
```bash
# index.ts でロードしているモジュール数と tools-api.ts のモジュール数を比較
grep -c 'tryLoadModule' src/engine/tools/index.ts
grep -c "engine/tools/" src/bridge/tools-api.ts
# 数が一致していなければ漏れがある
```

---

## 2. 既存モジュールにツールを追加した場合

**対象ファイル:**
- `pieces/*.yaml` — 必要な piece の `allowed_tools` にツール名を追加
- `CLAUDE.md` — 「ツールモジュール構成」テーブルの該当モジュール行に新ツール名を追加
- `docs/tools/{name}.md` — 新ツールの詳細ドキュメント（推奨）
- ツール `description` — 1 文 + 「詳細は ReadToolDoc({ name: "XXX" })」を末尾に記述

**なぜ必要か:**
`allowed_tools` に載っていないツールは LLM に提示されない。ツールを実装しても piece に追加しなければエージェントが使えない。
CLAUDE.md のテーブルが古いと、Claude Code 自身が既存ツールを認識せずに新規実装してしまうリスクがある。
ツール description は毎 LLM 呼び出しに乗るため 1 文に絞り、詳細は ReadToolDoc 経由で必要時のみ読み込む（agent-loop が movement 開始時に description サマリを自動カタログ化する）。

**確認方法:**
新ツールが使われるべき piece を特定し、`allowed_tools` に含まれているか確認する。
CLAUDE.md のモジュールテーブルに新ツール名が含まれているか確認する。

---

## 3. ツールをリネーム・削除した場合

**対象ファイル:**
- `pieces/*.yaml` — 全 piece の `allowed_tools` から旧名を削除/リネーム
- `src/engine/tools/raw-save.ts` — `RAW_SAVE_TOOLS` に旧名が残っていないか
- `ui/src/components/settings/ToolsForm.tsx` — ヘルプテキスト等にツール名の言及がないか
- `CLAUDE.md` — ツールモジュール構成テーブル

**なぜ必要か:**
BrowserAction を BrowseWeb に統合した際、5つの piece と UI ヘルプテキストに旧名の参照が残っていた。

**確認方法:**
```bash
grep -r '旧ツール名' pieces/ src/ ui/src/ CLAUDE.md
```

---

## 4. config.yaml に新しい設定キーを追加した場合

**対象ファイル:**
- `src/config.ts` — `ToolsConfig` / `AppConfig` / `LlmConfig` / `StorageConfig` 等のインターフェースにフィールド追加
- `src/config-normalize.ts` — v1 → v2 normalizer に新フィールドの mirror が要るか確認 (storage / llm 配下に置く場合)
- `config.yaml.example` — YAML キー（スネークケース）でサンプル・コメント追加。`config_version: 2` を必ず先頭に保つ
- `ui/src/components/settings/` — 該当セクションの Form コンポーネントに UI フィールド追加
- `CLAUDE.md` — 設定セクションに説明追加（必要に応じて）

**なぜ必要か:**
config.ts と config.yaml.example の不一致は「設定が効かない」「ドキュメントと実動作が違う」を招く。
Settings UI への追加を忘れると、YAML を直接編集しないと設定できなくなる。
v2 layout (`llm.*` / `storage.*`) では normalizer 側の backfill / migrate path も触らないと、v1 ファイルでの読み取り互換が壊れる。

**確認方法:**
```bash
# config.ts のフィールド数と config.yaml.example のキー数を目視比較
# Settings UI の各 Form ファイルで未対応フィールドがないか確認
# normalize の v1 fixture が落ちていないか
npx vitest run src/config-normalize.test.ts
```

---

## 4-B. 新しい Settings Form コンポーネントを追加した場合


**対象ファイル (両方必須):**
- `ui/src/components/settings/<Name>Form.tsx` — Form 本体
- `ui/src/components/settings/ConfigForm.tsx` — `renderActiveForm()` の `switch(section)` に新 `case 'new-id': return <NewForm {...formProps} />;` を追加
- `ui/src/components/settings/SettingsSidebar.tsx` — `CONFIG_GROUPS[].sections[]` に `{ id: 'new-id', label: '...' }` を追加。admin 限定なら親グループに `adminOnly: true`
- `ui/src/lib/urlState.ts` — section id の許可リストにも追加 (URL state からの復元)
- 旧 id をリネームした場合は `LEGACY_SECTION_REDIRECT` (SettingsSidebar.tsx) にエントリ追加してブックマーク互換を維持

**なぜ必要か:**
ConfigForm の switch と SettingsSidebar の section リストは独立に管理されており、片方だけ追加すると「サイドバーには項目があるが何も描画されない」「描画はされるがサイドバーから飛べない」状態になる。Step 3 (sidebar 再編) / Step 7-9 (form 分割) で両方を毎回触る前提で設計されている。

**禁止事項:**
- 削除した旧 Form (`ProviderForm.tsx`, `WorkersForm.tsx`, `WorkspaceForm.tsx` のような単一巨大 form) を復活させない。LLM Workers / Paths & Storage / Execution / Tools-* の分割は v2 design の前提
- カンマ区切り入力で配列フィールドを作らない。chip エディタ / textarea / multiselect のいずれかを使うこと (値に `,` を含めるケースを壊さないため)
- Secret 入力で `'********'` の magic string 判定を再導入しない。`SecretInput` (4-state: `unchanged` / `literal` / `env_ref` / `cleared`) を再利用すること

**確認方法:**
```bash
# section id が両方のファイルで揃っているか
grep -E "'[a-z-]+'" ui/src/components/settings/SettingsSidebar.tsx | grep "id:"
grep "case '" ui/src/components/settings/ConfigForm.tsx
# legacy redirect が漏れていないか
grep -A 20 'LEGACY_SECTION_REDIRECT' ui/src/components/settings/SettingsSidebar.tsx
```

---

## 5. SSRF 保護に関わる変更をした場合

**対象ファイル:**
- `src/engine/tools/shared/ssrf.ts` — SSRF チェックロジック本体
- `src/engine/tools/browser.ts` — BrowseWeb（`setupRouteInterception` + `ssrfCheck`）
- `src/engine/tools/web.ts` — WebFetch / DownloadFile
- `ui/src/components/settings/ToolsForm.tsx` — SSRF Allowed Hosts のヘルプテキスト

**なぜ必要か:**
`webfetchAllowedHosts` は WebFetch と BrowseWeb の両方で使われている。片方だけ修正すると保護に穴が開くか、意図しないブロックが発生する。

---

## 6. ログ出力に関わる変更をした場合

**対象ファイル:**
- `src/engine/tools/raw-save.ts` — `RAW_SAVE_TOOLS` / `RAW_LOG_ONLY_TOOLS` の更新
- 各ツールモジュール内の `appendXxxHistory()` 関数

**なぜ必要か:**
新ツールを追加したとき、`RAW_SAVE_TOOLS` への追加を忘れると `logs/raw/` に出力が保存されない。
knowledge ツールのように独自で raw 保存する場合は `RAW_SAVE_TOOLS` に含めない（二重保存回避）。

**保存先の整理:**

| ログ種別 | パス | 内容 |
|---------|------|------|
| 生データ | `logs/raw/{tool}-{timestamp}.txt/.json` | ツール実行結果の生出力 |
| WebFetch 履歴 | `logs/webfetch-history.jsonl` | URL, ステータス, サイズ |
| ダウンロード履歴 | `logs/downloads.jsonl` | 保存パス, サイズ |
| ナレッジ履歴 | `logs/knowledge-history.jsonl` | クエリ, ヒット数, 所要時間 |
| チェックリスト | `logs/checklists/{name}.json` | チェックリスト状態 |
| rawdata インデックス | `logs/rawdata-history.jsonl` | raw 保存のメタデータ |

---

## 7. ツールの詳細ドキュメントを書く場合

**対象ファイル:**
- `docs/tools/{toolname-lowercase}.md` — ツールの詳細な使い方ガイド
- 該当ツールの `description` — 概要 + `ReadToolDoc({ name: "XXX" })` で参照可能と明記
- `src/engine/tools/docs.ts` の `TOOL_DOC_ALIASES` — 関連ツール名を同じ doc にマップ（例: `checkitem: 'checklist'`）
- 既存 piece に同じガイダンスが書かれていれば削除する（重複解消）

**なぜ必要か:**
Tool description は毎回 LLM のコンテキストに乗るため肥大化させたくない。詳細手順・ワークフロー例は `docs/tools/{name}.md` に置き、`ReadToolDoc` ツールで必要時に取得する設計。
ReadToolDoc は META_TOOLS として常時利用可能なので、piece の `allowed_tools` に追加する必要はない。
関連ツール（CheckItem / CreateChecklist / GetChecklist 等）は1つの doc にまとめてエイリアス経由で引けるようにする。

**確認方法:**
- ファイル名は小文字（例: `BrowseWeb` → `browseweb.md`）
- description の末尾に「詳細は ReadToolDoc(...) で取得可能」を明記
- 全 piece から対応する重複ガイダンスを削除

---

## 8. piece の movement 構造を変更した場合

**対象ファイル:**
- 対象 `pieces/*.yaml` — `rules` に遷移先を明示追加

**なぜ必要か:**
`default_next` は機械的フォールバック専用。LLM が選べる遷移先は `rules[].next` に明示されたものだけ。
`rules` に追加せず `default_next` だけに頼ると、LLM が遷移先を選択できない。

---

## 9. Reflection に新しい memory `type` を追加した場合

**対象ファイル:**
- `src/engine/reflection/types.ts` — `ReflectionMemoryType` の union を拡張
- `src/engine/reflection/reflection-schema.ts` — LLM tool schema の `type` enum
- `src/engine/reflection/reflection-prompt.ts` — システムプロンプトの type 説明
- `src/engine/reflection/semantic-validator.ts` — `ALLOWED_TYPES` セット
- `src/bridge/memory-api.ts` — PUT のバリデーション
- `ui/src/components/settings/MemoryLearningForm.tsx` — type ドロップダウン

**なぜ必要か:**
4 種類 (user / feedback / project / reference) はメモリ意味論の単位。LLM は schema で型強制されるので、schema・prompt・validator のいずれかを忘れると LLM が「未知の type」を吐いて全 reflection が `rejected_unknown_type` で落ちる。UI ドロップダウンの更新を忘れるとユーザーが手動編集できなくなる。

---

## 10. Reflection に新しい rejection code を追加した場合

**対象ファイル:**
- `src/engine/reflection/types.ts` — `ReflectionRejectionCode` の union を拡張
- `src/engine/reflection/semantic-validator.ts` — 新しいケースで返す
- `src/engine/reflection/applier.ts` — 必要に応じて applier 側の rejection 経路も追加
- `src/bridge/memory-api.ts` — 同じコードを返す PUT バリデーションを更新
- `ui/src/components/settings/MemoryLearningForm.tsx` — UI 側でコード → 日本語メッセージ変換
- `src/engine/reflection/applier.fuzz.test.ts` — fuzz の "known code list" に追加

**なぜ必要か:**
rejection code は metrics・UI 表示・fuzz invariant の3箇所で参照される。fuzz テストは「decision の code は known list のいずれか」と assertion しているので、追加し忘れると fuzz が落ちる。UI 側で未対応コードはユーザーに「不明なエラー」と表示されてしまう。

---

## 11. MCP サーバー追加 / authKind 仕様変更時

**対象ファイル:**
- `src/mcp/registry.ts` — `auth_kind` の受け入れ・バリデーション
- `src/mcp/token-manager.ts` — `hasToken` / `getValidToken` の authKind 分岐
- `src/bridge/mcp-api.ts` — admin (global server) ルート、`/api/mcp/connections`
- `src/bridge/user-servers-api.ts` — user-owned server CRUD
- `ui/src/components/userfolder/McpServersPanel.tsx` — authKind ラジオ + 条件付きフォーム
- `ui/src/components/userfolder/McpConnectionsPanel.tsx` — authKind=api_key は Authorize ボタン非表示
- `docs/mcp.md` — 運用者向け手順 (OAuth と api_key 両方)

**なぜ必要か:**
authKind は server 行に保存され、token-manager・aggregator・UI のそれぞれで分岐する。Phase 8 で OAuth 専用設計から拡張したため、新しい authKind を入れる場合は schema migration + UI フォーム + docs の 3 点を必ず触る。OAuth start ルートは authKind=oauth でしか動かない (api_key は 400 を返す) ので、route 側のガードも忘れない。

**確認方法:**
- `grep -rn "authKind" src/mcp/ src/bridge/mcp-api.ts src/bridge/user-servers-api.ts ui/src/components/userfolder/Mcp*.tsx` で参照漏れを確認
- 統合テスト: `npx vitest run src/mcp/integration.test.ts` (OAuth + api_key 両 path をカバー)

---

## UI フォントサイズスケール (Tailwind class 規約)

UI に新しいパネルやコンポーネントを追加するときは、以下の **6 段階スケール** から選ぶ。それ以外の `text-[Npx]` を導入すると全体の字組が崩れる。

| サイズ | クラス | 用途 |
|---|---|---|
| 10px | `text-[10px]` | セクションラベル（uppercase）、極小バッジ |
| 11px | `text-2xs` ⭐ (Tailwind 拡張、`tailwind.config.js` で定義済み) | ヘルプテキスト、キャプション、メタ情報 |
| 12px | `text-xs` | 本文小、ボタンラベル、フォーム入力、テーブルセル |
| 13px | `text-[13px]` (Tailwind 標準に該当なし) | パネル本文、メイン段落 |
| 14px | `text-sm` | フォームラベル、ナビゲーション項目、見出し小 |
| 16px+ | `text-base` / `text-lg` / `text-xl` / `text-2xl` | h2 セクション見出し、ページタイトル、ヒーロー |

### 禁止リスト

以下は **絶対に書かないこと**。書いた場合は `2026-05 commit 993ef2f` の正規化と同様の一括置換でやり直しになる。

| 書いてしまった | 置き換え先 |
|---|---|
| `text-[11px]` | `text-2xs` |
| `text-[12px]` | `text-xs` |
| `text-[14px]` | `text-sm` |
| `text-[15px]` | `text-sm` |
| `text-[16px]` | `text-base` |
| `text-[18px]` | `text-lg` |
| `text-[9px]` | `text-[10px]` (極小に丸め) |
| `text-[12.5px]` | `text-[13px]` |
| 任意の非標準 px | 上記スケールに最も近いものに丸める |

### なぜ必要か

- 過去に新規パネル (Help, MCP, AGENTS.md 等) を追加するたびに implementer が独自に `text-[Npx]` を書き、累積で **13 種類のサイズ** が混在した
- 統一したフォントサイズは UI の一貫性 + 認知負荷の低下に直結する
- Tailwind utility (`text-xs/sm/base/lg/xl/2xl`) は font-size と line-height がセット定義されているので、行間も自然に揃う
- 不要な arbitrary value (`text-[Npx]`) は CSS の出力サイズも増やす

### 確認方法

```bash
cd ui
# 禁止サイズが残っていないか確認 — 何も出なければ OK
grep -rohE 'text-\[(9|11|12|12\.5|14|15|16|17|18)px\]' src --include='*.tsx'

# 全体のサイズ分布を確認
grep -rohE 'text-\[[0-9]+(\.[0-9]+)?px\]|text-(xs|sm|base|lg|xl|2xl|3xl|4xl|2xs)' src --include='*.tsx' | sort | uniq -c | sort -rn
```

期待される分布 (Tailwind utility が主、arbitrary は 10px / 13px のみ):

```
text-xs    (~250 ↑)
text-2xs   (~180 ↑)
text-sm    (~100 ↑)
text-[13px] (~100)
text-[10px] (~100)
text-base, text-lg, text-xl, text-2xl, text-4xl ... (少量)
```

### 触る場所

- 新規 UI コンポーネント全般 (`ui/src/**/*.tsx`)
- 既存パネルへの追加要素
- 設定フォーム (`ui/src/components/settings/`)
- ダイアログ・モーダル

---

## 12. SSH 関連変更時 (Phase 0-8)


### 12-A. SSH ツール (`SshExec` / `SshUpload` / `SshDownload`) を変更したとき

**対象ファイル:**
- `src/engine/tools/ssh.ts` — TOOL_DEFS + executeTool + 12-step orchestration
- `src/engine/tools/ssh.test.ts` — unit tests (23 件)
- `src/engine/tools/ssh.e2e.test.ts` — in-process ssh2 server e2e (4 件、SKIP_SSH_E2E=1 で skip)
- `src/engine/tools/index.ts` — `tryLoadModule('./ssh.js')` 経由のロード + dispatch
- `src/engine/tools/core.ts` — `ToolContext.pieceName` (piece grants check に必要)
- `src/engine/piece-runner.ts` — `ToolContext` 構築時に `pieceName: piece.name` を thread
- `src/engine/tools/docs.ts` — `TOOL_DOC_ALIASES` で 3 ツール名を `'ssh-tools'` にマップ
- `docs/tools/ssh-tools.md` — LLM 向け詳細ドキュメント
- `src/bridge/server.ts` — `setSshSubsystem({...})` で DI

**新ツールを SSH 系で追加する場合、または既存ツールに引数を増やす場合:**
- ssh.ts の `TOOL_DEFS` description は **1 文 + 「詳細は ReadToolDoc」** に絞る (毎呼び出しのトークン節約)
- preflight (steps 1-7) を流用するため `runExec`/`runUpload`/`runDownload` の skeleton を踏襲
- `finally` で `clearBuffer(pem, passphrase)` を必ず実行 — ssh2 の internal retention は防げないが我々の copy は zeroize する
- 監査 action 名は `ssh.<verb>` 形式 (例: `ssh.exec`, `ssh.upload`)。command 全文は **記録せず SHA-256 16-char hex** を `detail.command_hash` に
- piece grants check は `subsystem.accessResolver.resolve()` を必ず通す (piece membership → access decision → state checks → policy の順)

**新しい SshSessionError code を追加する場合:**
- `src/ssh/session.ts` の `SshSessionErrorCode` union を拡張
- `src/engine/tools/ssh.ts` の `formatSessionError` switch に LLM-actionable message を追加
- `docs/tools/ssh-tools.md` の「共通エラーコード一覧」表に追記
- `docs/ssh.md` の「Troubleshooting → Symptom → cause table」に追記

**確認方法:**
```bash
# tool が grants check を通っているか
grep -n "accessResolver\.resolve\|preflight" src/engine/tools/ssh.ts
# 監査 action 名が既存と整合
grep -n "action:" src/engine/tools/ssh.ts src/bridge/ssh-api.ts | sort -u
# tests
npx vitest run src/engine/tools/ssh.test.ts
SKIP_SSH_E2E=  npx vitest run src/engine/tools/ssh.e2e.test.ts  # 明示的に run
```

### 12-B. SSH HTTP API (`src/bridge/ssh-api.ts`) を変更したとき

**対象ファイル:**
- `src/bridge/ssh-api.ts` — User router + Admin router (25 endpoints)
- `src/bridge/ssh-api.test.ts` — auth / maintenance / reason / CRUD / TOFU / grants / rotation
- `src/ssh/maintenance.ts` — in-memory maintenance flag
- `src/ssh/admin-rate-limit.ts` — token bucket for force-unlock (10/hr)
- `src/bridge/server.ts` — `ssh.enabled` ゲートで両 router をマウント
- `src/ssh/session.ts` — `sshTest()` (HTTP test endpoint で使用)
- `docs/ssh.md` §"HTTP API Reference" — endpoint table

**新しい admin write エンドポイントを追加するとき:**
- `body.reason` 必須 (≥ 8 chars)。`validateReason()` を必ず呼ぶ
- 操作直前に `maintenance503()` ガードを呼ぶ (read 系は不要)
- 成功・失敗どちらも `auditRepo.beginAndComplete()` で `ssh_audit_log` に記録
- 監査 action は `src/ssh/audit-repo.ts` の `SshAuditOutcome` と Phase 5 設計書の enum を踏襲
- admin-only フラグ (`allow_remote_unrestricted`, `allow_private_addresses` per-connection) は user 経路で常に reject

**新しい user write エンドポイントを追加するとき:**
- `getUserId()` で取得した userId をオーナーチェックに使う
- 他人の所有物に対しては `404` を返す (存在を漏らさない)

**確認方法:**
```bash
# admin endpoint で reason check / maintenance ガードが漏れていないか
grep -n "deps.requireAdmin\|maintenance503\|validateReason" src/bridge/ssh-api.ts
# audit log の action 名が既存と整合しているか
grep -n "action:" src/bridge/ssh-api.ts
```

### 12-C. SSH repo / schema を変更したとき

**対象ファイル:**
- `src/db/schema.sql` — `ssh_connections`, `ssh_connection_grants`, `ssh_audit_log`, `ssh_abuse_counters`, `system_deks`
- `src/db/migrate.ts` — `PRAGMA table_info` → 列存在チェック → `ALTER TABLE ADD COLUMN` パターン (バージョン管理テーブル不使用)
- `src/ssh/connection-repo.ts` / `grants-repo.ts` / `audit-repo.ts` / `abuse-repo.ts` / `access.ts`
- 各 repo に対応する `*.test.ts`
- `src/ssh/recovery.ts` — boot 時 pending → aborted sweep
- `src/ssh/crypto.ts` — DEK ラップ / マスター鍵検証
- `docs/ssh.md` §"Connection Model" / §"Access Grants" / §"Audit Log"

**スキーマ列を追加するとき:**
- `schema.sql` の `CREATE TABLE` に列追加 (初期スキーマ)
- `migrate.ts` に冪等 ALTER TABLE 追加 (既存 DB 用)
- 既存テストが影響を受けないか `npm test` で検証
- 列をエンドポイントから書く場合、`POST/PATCH` ハンドラのバリデーションに追加し、admin-only フラグなら user 経路で reject

**新しい監査 action を追加するとき:**
- `src/ssh/audit-repo.ts` の `SshAuditOutcome` は触らない (固定 5 値: `pending|success|failed|denied|aborted`)
- action 名は `ssh.<entity>.<verb>` 形式 (例: `ssh.connection.host_key.replace`)
- `docs/ssh.md` §"Audit Log → Actions" 表に追加
- `docs/tools/ssh-tools.md` の「監査ログ」セクションは tool 経由 action のみなので、tool 起因なら追記

**確認方法:**
```bash
# 監査 action 一覧 (重複/typo 検出)
grep -rn "action: 'ssh\." src/ | awk -F"'" '{print $2}' | sort -u
# repo CAS / transaction が崩れていないか
grep -n "BEGIN\|COMMIT\|prepare(" src/ssh/*.ts | head -30
```

### 12-D. SSH UI を変更したとき

**対象ファイル:**
- ユーザー向け:
  - `ui/src/components/userfolder/SshConnectionsPanel.tsx` — 一覧 + actions
  - `ui/src/components/userfolder/SshConnectionForm.tsx` — 作成・編集フォーム
  - `ui/src/components/userfolder/SshHostKeyDialog.tsx` — TOFU verify ダイアログ
- 管理者向け:
  - `ui/src/components/settings/SshForm.tsx` — Settings → SSH のルート
  - `ui/src/components/settings/SshGlobalConnectionsForm.tsx` — グローバル接続 CRUD
  - `ui/src/components/settings/SshGrantsForm.tsx` — grants CRUD
  - `ui/src/components/settings/SshAuditLog.tsx` — 全テナント監査ログ
  - `ui/src/components/settings/SshMasterKeyRotationForm.tsx` — 鍵 rotation
- 型定義: `ui/src/lib/ssh-types.ts`
- `ui/src/components/settings/ConfigForm.tsx` / `SettingsSidebar.tsx` — `SshForm` の登録 (新規 Form 追加時)

**確認方法:**
- 新 form を追加した場合 `ConfigForm.tsx` の section リストに登録、`SettingsSidebar.tsx` のナビ項目に追加
- ssh-types.ts と API レスポンス shape (`SshConnection`, `SshGrant`, `SshAuditRow`) が一致しているか
- 禁止フォントサイズ (`text-[11px]` 等) を導入していないか — 既存セクションの「UI フォントサイズスケール」参照

### 12-E. piece schema (`allowed_ssh_connections`) を変更したとき

**対象ファイル:**
- `src/engine/piece-runner.ts` — `allowed_ssh_connections` の lint (validateMovement)
- `src/engine/types.ts` (or piece schema 定義箇所) — `allowed_ssh_connections?: string[]`
- `pieces/*.yaml` — SSH ツールを `allowed_tools` に含む movement は **必ず** `allowed_ssh_connections` 宣言が必要 (空配列 `[]` でも可)
- `docs/ssh.md` §"Per-piece `allowed_ssh_connections`"

**lint 規約:**
- `allowed_tools` に SSH ツール名が含まれる場合、`allowed_ssh_connections` の宣言が必須 (`undefined` は reject)
- 値は配列、各要素は `*` または lowercase hex + ハイフン UUID (≥ 8 chars)
- 空配列 `[]` は "deny all" として明示扱い

**確認方法:**
```bash
# 既存 piece に SSH 使用宣言があるか
grep -l 'Ssh\(Exec\|Upload\|Download\)' pieces/*.yaml | xargs -I {} grep -l 'allowed_ssh_connections' {}
```

### 12-F. config.yaml の SSH セクション (`SshRuntimeConfig`) を変更したとき

**対象ファイル:**
- `src/ssh/config.ts` — `SshRuntimeConfig` interface + `SSH_DEFAULTS` + `mergeSshConfig`
- `src/config.ts` — `transformKeys` でスネーク→キャメル変換、`AppConfig` に統合
- `config.yaml.example` — コメント付きデフォルト値
- `ui/src/components/settings/SshForm.tsx` — UI で編集可能にする場合
- `docs/ssh.md` §"`config.yaml` Reference"

**確認方法:**
```bash
# config.ts と config.yaml.example の SSH キー数が一致するか
grep -E '^\s+[a-z_]+:' config.yaml.example | grep -A 30 '^# ssh:' | head -40
```

### 12-G. SSH Console (interactive PTY) を変更したとき

SSH Console は SshExec/Upload/Download とは別系統の対話的 PTY 経路。Phase 8 で新規追加。

- 新ツール SshConsoleEnsure / SshConsoleSend / SshConsoleSnapshot を追加した時:
  - [ ] `src/engine/tools/ssh-console.ts` の `TOOL_DEFS` に追加
  - [ ] `src/engine/tools/index.ts` の dispatch + aggregation に追加
  - [ ] `src/engine/tools/docs.ts` の `TOOL_DOC_ALIASES` にエイリアス
  - [ ] `docs/tools/ssh-console-tools.md` を更新
- 新 WS endpoint を追加した時:
  - [ ] `src/bridge/server.ts` に upgrade handler 配線
  - [ ] auth / visibility / access の 4 段階チェックを記述
- 新 `SessionCloseReason` を追加した時:
  - [ ] `src/ssh/console-protocol.ts` (server) と `ui/src/lib/ssh-console-types.ts` (UI) の両方に追加
  - [ ] `ConsoleHeader.tsx` の disconnected reason 表示に文言を追加

## 13. Scheduler から呼ばない手動オペレーション endpoint

以下の endpoint は **UI からの手動操作専用** で、scheduler / Routine / 自動化経路から起動できない設計になっている。Routine 側の payload schema にこれらの override を追加してはいけない (scheduled task の `allowed_tools` 境界が想定外に拡大するリスクのため)。

- `POST /api/local/tasks/:id/continue` — 別 piece で task を続ける (handoff)
  - 実装: `src/bridge/local-tasks-api.ts` の `/continue` ハンドラ
  - terminal 状態 (`succeeded` / `failed` / `waiting_human` / `cancelled`) のときのみ有効
  - 新カテゴリを追加する場合: `scheduled-tasks-api.ts` / `scheduler.ts` 側に「実行する piece を override する」機能を持たせない。実行する piece は task 作成時に固定し、人間が UI から明示的に切り替える経路だけ残す

**確認方法:**
```bash
# scheduler 経路から /continue を叩いている箇所が無いか
grep -rn "/continue\b\|continueTaskWithPiece" src/scheduler.ts src/bridge/scheduled-tasks-api.ts
# 無いことが期待値
```

---

## 14. Knowledge Notes 追加・変更時

Knowledge Notes は `data/users/{userId}/notes/` 以下のマークダウンファイルで管理される共有ナレッジ機能。
FM (frontmatter) の `visibility` / `scope_org_id` で公開範囲を制御し、他ユーザーが検索・購読・inject できる。

**対象ファイル:**
- `src/db/schema.sql` と `src/db/migrate.ts` — dual path で両方更新 (知識テーブル追加時)
- `src/engine/tools/index.ts` — 新ツールの dynamic import
- `pieces/*.yaml` — `allowed_tools` に必要なツール名を追加
- `src/engine/tools/docs.ts` の `TOOL_DOC_ALIASES` — 関連ツールを同じ doc にまップ
- `ui/src/components/userfolder/FileTree.tsx` の `FILE_SUBDIRS` — サイドバーに新ディレクトリを追加
- `config.yaml.example` — `notes.inject` セクションのサンプル設定を更新

**セキュリティ・可視性チェックリスト:**

- [ ] `src/db/schema.sql` と `src/db/migrate.ts` の両方を更新 (dual path)
- [ ] 新ツールを `src/engine/tools/index.ts` の dynamic import に追加
- [ ] `pieces/*.yaml` の `allowed_tools` に必要なツール名を追加
- [ ] `src/engine/tools/docs.ts` の `TOOL_DOC_ALIASES` にエイリアスを追加
- [ ] `ui/src/components/userfolder/FileTree.tsx` の `FILE_SUBDIRS` を更新
- [ ] `config.yaml.example` の `notes.inject` セクションを更新
- [ ] FM の `visibility=org` チェックは publisher の所属 org で行う (`getUserOrgIds`)
- [ ] inject 注入経路 (`agent-loop.ts` `buildSystemPrompt`) は visibility WHERE を必ず通る
- [ ] cross-user read は `audit_log` に記録される

**なぜ必要か:**
`visibility=org` の org チェックを publisher 側の org で行わないと、異なる org のユーザーが他 org のノートを閲覧できる。inject 経路で visibility チェックを省略すると、private なノートがシステムプロンプト経由で漏洩する。cross-user read の監査記録が欠けると、後からアクセス追跡ができなくなる。

**確認方法:**
```bash
# visibility WHERE が inject 経路にあるか
grep -n "visibility\|getUserOrgIds" src/engine/agent-loop.ts

# audit_log への記録が行われているか
grep -n "audit_log\|read_note" src/engine/tools/knowledge.ts

# SearchNotes / ListNotes ツールが tools-api.ts に登録されているか
grep -n "knowledge\|notes" src/bridge/tools-api.ts
```

---

## Bash Unrestricted モード (`safety.bash_unrestricted`) の変更時

**対象ファイル:**
- `src/engine/tools/sandbox.ts` — bwrap サンドボックスのマウント構成・実行ロジック
- `src/engine/tools/core.ts` — `executeBash` の分岐 (`ctx.bashUnrestricted`)、`ToolContext` インターフェース
- `src/engine/piece-runner.ts` — `safetyConfig.bashUnrestricted` → `ToolContext` への伝播
- `src/worker-bootstrap.ts` — 起動時 bwrap 可用性チェック
- `src/config.ts` — `SafetyConfig.bashUnrestricted` 定義・バリデーション

**なぜ必要か:**
bwrap のマウント構成を変えた場合、セキュリティ境界が変わる。テスト (`sandbox.test.ts`, `core.test.ts` の `bashUnrestricted mode` セクション) を必ず更新すること。

---

## 自動検知の可能性

- **ツールモジュール登録漏れ**: `index.ts` と `tools-api.ts` のモジュール一覧を比較するスクリプトで CI チェック可能
- **piece の allowed_tools 不整合**: 全 piece の `allowed_tools` に含まれるツール名が実際の `TOOL_DEFS` に存在するか検証するスクリプトで CI チェック可能
- **code-review-graph**: `importers_of` で各ツールモジュールの参照元を列挙できるが、「tools-api.ts にも登録すべき」というルールの自動適用は困難。変更時の `detect_changes` + `get_impact_radius` で影響範囲の見落としを防ぐ用途が現実的
