コンテンツにスキップ

Dogfood シナリオフレームワーク

Reyn のリリースをまたいで継続的に使用する回帰テスト用シナリオスイート。 reply テキスト・P6 イベント・ワークスペース成果物の 3 つの観測面に対してアサーションを宣言し、 docs/feature-map.md のカバレッジと照合する。 一回限りのバッチ事前準備ではなく、YAML で記述された再利用可能な回帰スイートとして設計されている。

なぜ必要か

FP-0036 以前は 3 つの仕組みが統合されていなかった:

reyn eval Dogfood シナリオフレームワーク
起点 reyn run <skill> reyn chat(ルーターがスキルを選択)
検証面 フェーズ単位の rubric(LLM judge) reply + events + artifacts
スコープ 1 スキル単位 feature-map カバレッジ
結果スケール 二値(pass/fail) 4 バンド(verified / inconclusive / refuted / blocked)
用途 スキル単位の回帰 システム全体の e2e 回帰

このフレームワークは reyn eval直交 している。judge_output op バックエンドと ベースライン比較パターンは再利用するが、CLI とスキーマは独立している。 一回限りのバッチプレリュード(Markdown 散文)とも直交する。

4 つの設計制約:

  • 確率論的挙動 — アサーションは安定性バンドを使用する(二値 pass/fail ではない)
  • コスト — フルスイートの再実行は LLMReplay フィクスチャを使用(LLM コスト ゼロ)
  • 挙動ドリフトreyn dogfood compare <baseline> <candidate> で回帰とノイズを区別
  • カバレッジreyn dogfood coverage で feature-map の未カバー項目を一覧表示

スキーマ

シナリオセットは dogfood/scenarios/ 以下の YAML ファイル。 トップレベルの covers: はセット全体がカバーする機能を列挙する。 各シナリオの covers: はカバレッジマトリックスに反映される。

シングルターンシナリオ

type: dogfood_scenario_set
name: chat_router_smoke
description: チャットルーター意図振り分け + stdlib カタログディスパッチスモーク
covers:
  - chat-router/intent-routing
  - stdlib-skills/direct-llm

scenarios:
  - id: simple_greeting
    covers: [chat-router/intent-routing, stdlib-skills/direct-llm]
    input: "こんにちは、何ができますか?"
    expected:
      reply:
        kind: judge
        rubric:
          - 機能を高レベルで説明している
          - chat / スキル / エージェントに言及している
      events:
        must_emit:
          - { type: skill_run_spawned, count: ">=1" }
          - { type: skill_run_completed, status: success }
        must_not_emit:
          - { type: permission_denied }
      artifacts:
        - { skill: direct_llm, present: true }
    outcome_prediction:
      verified: 0.7
      inconclusive: 0.2
      refuted: 0.05
      blocked: 0.05

マルチターンシナリオ

マルチターンは input: の代わりに prompts: [...] を使用する。 expected ブロックは per_turn_expected が指定されていない限り最終ターンに適用される。

  - id: multi_turn_plan
    covers: [os-core/phase-engine/act-decide-loop]
    prompts:
      - "コードを改善してください"
      - "変更を適用してください"
    expected:
      events:
        sequence:
          - skill_run_spawned
          - skill_run_completed

inputprompts は相互排他。両方指定すると ScenarioLoadError が発生する。

検証面

3 つのベリファイアが独立して動作し、結果は最悪ケースで合成される: 1 つでも refuted になるとシナリオ全体が refuted となる。

Reply

kind でマッチング方式を指定:

kind アサーション
judge rubric の自然言語基準リストを judge_output op でスコアリング
substring value 文字列が reply のどこかに含まれる
exact value 文字列が reply に完全一致する(前後の空白は無視)
regex value パターンが re.search でマッチする

Events

must_emit はイベントの存在をアサート(カウント比較子 >=1==2<5 等、ペイロードのサブセットマッチ対応)。 must_not_emit は不在をアサート。sequence はイベントタイプの順序付き部分列をアサート。

Artifacts

ArtifactAssertion はワークスペース状態をテスト: skill / type による存在確認と、 オプションで fingerprint(正規化コンテンツの SHA256)によるピン留め回帰テスト。

4 バンド結果

各ベリファイアは 4 バンドのいずれかを返す:

  • verified — アサーション明確に通過
  • inconclusive — 判断に十分なシグナルなし
  • refuted — アサーション明確に失敗
  • blocked — インフラ障害(タイムアウト、フィクスチャ不在など)

Events / artifacts ベリファイアの結果が優先される。judgeinconclusive 時のタイブレーカー。 バンドのセマンティクスと Brier スコアリングの詳細は Dogfood discipline を参照。

outcome_prediction は期待する 4 バンド確率分布(合計 1.0 ± 0.001)を宣言する。 Brier スコアが複数回の実行にわたるキャリブレーション品質を測定する。

カバレッジ

各シナリオの covers: タグは docs/feature-map.md のフィーチャーパスにマッピングされる。 パス体系は小文字ケバブケース:

### OS Core          -> os-core
#### Phase Engine    -> os-core/phase-engine
| Act/Decide loop |  -> os-core/phase-engine/act-decide-loop

reyn dogfood coverage(機械可読出力は --json)は全シナリオセットを読み込んで報告する:

Total features:   187
Covered:           42  (22%)
Uncovered:        145

Uncovered (sample):
  os-core/llm-validation/artifact-schema-validation
  control-ir-ops/sandboxed-exec
  stdlib-skills/skill-builder
  ...

フィーチャーパスに一致しない未知のタグは警告として表示されるが、実行は失敗しない。

回帰ワークフロー

# ベースラインを記録
reyn dogfood run dogfood/scenarios/chat_router_smoke.yaml --n 5 --baseline smoke-v1

# Reyn 変更後にキャンディデートを実行して比較
reyn dogfood run dogfood/scenarios/chat_router_smoke.yaml --n 5
reyn dogfood compare smoke-v1 <run_id>

compare は回帰したシナリオ、新たに通過したシナリオ、Brier ドリフトを報告する。 いずれかのシナリオが --threshold(デフォルト 0.1)を超えて回帰した場合、終了コード 1 を返す。

リプレイモード

初回実行時のフィクスチャは LLMReplay 統合により dogfood/fixtures/<scenario_id>/ に記録される。 --replay <fixture_dir> 付きの後続実行では記録済みの LLM レスポンスを使用する (LLM コストゼロ、完全決定論的):

reyn dogfood run dogfood/scenarios/chat_router_smoke.yaml --replay dogfood/fixtures/

リプレイモードの実行は run_id にタグが付くため、レポートでライブ実行と区別できる。 フィクスチャはリリースタグごとに再記録される。スキーマ不一致は自動的に再記録を強制する。

リプレイは src/reyn/testing/replay.pyLLMReplay)上に構築されている。 フィクスチャ記録の詳細は リプレイテストガイド を参照。

相互参照