# ADR-0027b: AuditSeal の config_hash スコープ

**Status**: Proposed
**Date**: 2026-05-13
**Depends on**: ADR-0027 (AuditSeal 分離)

---

## Context

ADR-0027 は skill 実行開始時に書き出す `AuditContext` に `config_hash`
フィールドを定義している:

```json
{
  "run_id": "abc123",
  "config_hash": "sha256:..."
}
```

親 ADR では **ハッシュの対象として何を使うか** を defer した。これが重要な理由:

1. このハッシュは verifier が実行時と監査時の設定ドリフトを検出するために使う。
2. ハッシュのスコープが **検出可能** (ハッシュに含む) vs. **不可視**
   (除外) な変更を決定する。
3. スコープが広すぎると、影響のない設定変更でも seal が無効化される (ノイズ)。
   スコープが狭すぎると、LLM 動作に実質的な影響を与える変更が見逃される (盲点)。

競合の Hermes #487 は決定論的再現性 — モデルの重み、ツール定義、正確な
プロンプトテキストのピンを要求するより厳格な目標 — を追っている。
ここでの Reyn の設計選択は、`config_hash` が **compliance 監査可能性**
(システムは宣言通りに設定されていたか?) に向かうのか、**決定論的再現性**
(この実行をバイト完全に再現できるか?) に向かうのかを示す。

---

## Decision drivers

- **エンタープライズ compliance 要求**: 規制環境 (SOX、HIPAA) が主に問うのは
  「エージェントはポリシー文書に記述された通りに設定されていたか?」 — 再現性
  ではなく設定 integrity の問い。
- **OSS ライトユーザーの規律**: ライトユーザーは運用オーバーヘッドなしで監査を
  望む; 些細な設定変更による頻繁な seal 無効化は seal システムへの信頼を損う。
- **Hermes #487 ポジショニング**: Reyn が完全な決定論的再現性を追うなら、
  スコープにモデル設定 (プロバイダー、モデルバージョン、temperature) を含める
  必要がある。compliance のみを対象とするなら、モデル設定はハッシュに含めず
  AuditContext のメタデータとして別途記録できる。
- **ハッシュの安定性**: スキルの動作に影響しない reyn.yaml の構造変更 (コメント、
  空白、無関係なセクション) は seal を無効化すべきでない。
- **スキルレベルの粒度**: スキル固有の設定変更は reyn.yaml 全体を再ハッシュ
  せずに検出可能であることが望ましい。

---

## 検討した Options

### Option A: reyn.yaml 全体のハッシュ

reyn.yaml ファイル全体 (正規化形式、空白正規化済み) をハッシュする。

**Pros:**
- 実装がシンプル: ファイル 1 つ、ハッシュ 1 つ。
- 設計時に予期しなかったものも含め、すべての設定変更を捕捉。

**Cons:**
- `logging.level` や `retention.events_days` など無関係なセクションへの変更でも
  seal が無効化されてノイズが多い。
- コメントや書式変更 (空白正規化で一部は軽減) でドリフトが起きる可能性。
- 異なる実効設定で並行実行する複数のスキル (例: 1 スキルがモデルを上書き)
  が同じ `config_hash` を持ち、実効設定の違いが隠れる。

### Option B: スキル定義ハッシュのみ

実行するスキルに関連するスキル定義ファイルのみをハッシュする:
`skill.md` と参照するすべてのフェーズファイル。

**Pros:**
- 「このスキルの定義は実行間で変更されたか?」という問いに直接答える。
- モデル設定変更 (プロバイダー切替、temperature チューニング) を除外 —
  「スキル integrity」の関心事の外として扱う。
- スキル粒度: 各スキル実行のハッシュはそのスキルに固有。
- 無関係な reyn.yaml 変更に対して安定。

**Cons:**
- 同じスキル定義でも LLM 出力に実質的な影響を与えうるモデルプロバイダーの
  切替やバージョン変更を検出しない。
- スキル実行に影響する OS レベルの設定変更 (例: `audit.seal_unit`、
  パーミッションデフォルト) を検出しない。

### Option C: モデル設定ハッシュのみ

実行の実効モデル設定のみをハッシュする: プロバイダー、モデル名、モデルバージョン
(利用可能な場合)、推論パラメーター (temperature など)。

**Pros:**
- 「宣言された LLM が使われたか?」という問いに直接答える。
- Hermes #487 の決定論的再現性目標に沿う。

**Cons:**
- スキル定義の変更を検出しない。
- モデルバージョン文字列が安定しない場合がある (プロバイダー側のエイリアス
  "gemini-2.5-flash-latest" は時間とともに異なるバージョンに解決される)。
- スキル定義だけが変わってモデルが同じ場合に「同じ設定」という誤った印象を与える。

### Option D: 階層化 — AuditContext と AuditSeal に複数のハッシュフィールド

seal が複数の独立したハッシュフィールドを持つ:

```json
{
  "config_hash": {
    "skill_def": "sha256:...",
    "model_cfg": "sha256:...",
    "os_cfg": "sha256:..."
  }
}
```

Verifier はコンプライアンス要件に応じていずれかまたはすべてを独立して確認できる。

**Pros:**
- 最大の柔軟性: 異なる compliance 要件が異なるサブハッシュを確認。
- 無関係な変更によるノイズなし: モデル設定だけが変わっても `skill_def` ハッシュは安定。
- compliance 監査可能性と再現性の両目標に同時対応。
- 前方互換: 既存 verifier を壊さずに新しいハッシュフィールドを追加可能。

**Cons:**
- 実装の複雑度が高い: 3 つのハッシュ入力を定義、計算、維持する必要。
- `os_cfg` のスコープはそれ自身のサブ決定が必要 (reyn.yaml のどの部分が
  「OS 設定」vs「スキル設定」vs「モデル設定」か?)。
- Verifier の UI がどのサブハッシュが不一致かを伝える必要があり、エラー
  レポートが複雑化。

---

## Recommendation (proposed direction)

**Option D (階層化)** を推奨する。

理由:
- Option A は実用上ノイズが多すぎる。
- Option B と C はそれぞれ関連する懸念の半分しか捕捉しない。
- Option D は B と C の自然な合成。`os_cfg` スコープは初期段階では狭く
  (reyn.yaml の `audit.*` と `skill.permissions.*` セクションのみ) し、
  後のリリースで拡張できる。

**Option D の最小初期スコープ:**

| サブハッシュフィールド | ハッシュ対象 | 実装メモ |
|---|---|---|
| `skill_def` | スキルの `skill.md` と参照するフェーズファイルの正規化コンテンツ | スキル解決順序を走査して連結 |
| `model_cfg` | 実効プロバイダー + モデル名 + 主要推論パラメーター (temperature、max_tokens) | 解決済み reyn.yaml の `models.*` セクションから導出 |
| `os_cfg` | reyn.yaml の `audit.*` セクション (seal ポリシーパラメーター) | 初期スコープは狭く; 需要に応じて拡張 |

**実装着手時の再判断**: 階層化アプローチが最初の AuditSeal 実装には複雑すぎる
場合は Option B (スキル定義ハッシュのみ) にフォールバックし、`model_cfg`
ハッシュはフォローアップで追加するという設計メモを残す。これが最小限で
compliance に有用なハッシュ。

本 recommendation は実装着手時に再判断すること。

---

## Open questions

1. `skill_def` ハッシュについて: `skill.md` とフェーズファイルのみを含めるか、
   実行時に解決される stdlib スキルのソース (`@sub_skill` 参照など) も含めるか?
2. `model_cfg` ハッシュについて: プロバイダー管理のモデルエイリアス
   (例: "gemini-2.5-flash-latest") はエイリアス文字列をハッシュするか、
   実行時にピンされたバージョンに解決するか?
3. `os_cfg` ハッシュ用の reyn.yaml `audit.*` セクションの正規シリアライズ形式は?
   (JSON ソート? YAML? Pydantic モデル dict?)
4. `AuditContext` の `config_hash` はフラット文字列 (Option A/B/C) か構造化
   オブジェクト (Option D) か? 将来の進化を可能にするためスキーマバージョンを
   含める必要がある。

---

## Related

- ADR-0027: AuditSeal 分離 (親 ADR)
- ADR-0027a: ハッシュチェーントポロジー
- ADR-0027c: seal_unit と plan-mode 統合
- ADR-0027d: writer 失敗時のセマンティクス
- Hermes #487 競合コンテキスト: `docs/deep-dives/research/competitive/hermes-agent.md`
