maestro/docs/maintenance-checklist.md
2026-06-03 05:08:00 +00:00

555 lines
31 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# メンテナンスチェックリスト
コード変更時に連動して修正が必要な箇所のリスト。
過去に実際に発生した不整合をもとに作成。
---
## 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` で影響範囲の見落としを防ぐ用途が現実的