mcpField
P
propagate

propagate

github.com/juxtテスト・品質5242.6k 回インストール4 週間前に更新
リポジトリ

propagate について

propagateは、Allium仕様から対象コードベース向けのテストを生成するスキルです。境界と仕様全体のテスト義務を扱い、アサーション、プロパティベース、ステートマシンの各方式に対応します。

主な機能

  1. 01仕様のテスト義務からテスト生成
  2. 02surface宣言に基づく境界テスト
  3. 03仕様全体のテストカバレッジ
  4. 04アサーション・プロパティベース・ステートマシンテスト
  5. 05仕様要素と実装コードの対応付け

活用シーン

  1. 01API・UI・統合境界のテスト
  2. 02ルール・エンティティ・状態遷移の検証
  3. 03プロパティベーステストで不変条件を確認
  4. 04エンティティの全ライフサイクルをテスト

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/juxt/allium --skill propagate

インストール後、Claude Code で/propagateを入力して読み込まれたか確認します。

詳細

Propagation

このスキルは、Allium 仕様からテストを生成します。Propagation とは、植物が親株の切り枝から増えることです。仕様が親株で、テストがそこから生まれる株です。

決定論的なツールによって完全性が保証されます(仕様のすべての構成要素がテストの要件に対応します)。実装との橋渡しはあなたが担います。仕様の構成要素とコードを関連付け、プロジェクトの規約に沿ってテストを生成します。

インタラクションモード

このスキルは 2 つのモードで実行されます。以下のユーザーへの依頼や提案はすべて、モードに応じて行ってください。

  • インタラクティブ — 会話内で直接実行します。ユーザーに直接質問し、回答を待ちます。
  • 非インタラクティブ — propagate サブエージェントとして実行します(たとえば Allium ループ内)。ユーザーと連絡が取れないため、人間による判断が必要な事項(伝播には粗すぎる仕様、曖昧な実装との橋渡し、未対応の要件など)を最終出力で報告し、それに依存しない作業を続けます。

前提条件

テストを生成する前に、以下が必要です。

  1. Allium 仕様 — システムの振る舞いを記述した .allium ファイル
  2. 対象コードベース — テスト対象の実装
  3. テスト要件 — allium plan <spec> による出力(必須のテストをすべて列挙した JSON)
  4. ドメインモデル — allium model <spec> による出力(エンティティの形状、制約、状態機械を記述した JSON)

CLI ツールを利用できない場合は、references/test-generation.md のテスト生成の分類を使い、仕様からテスト要件を手動で導き出してください。

モード

サーフェスモード

サーフェス宣言から境界テストを生成します。API、UI コントラクト、または統合境界をテストしたい場合に使用します。

仕様内の各サーフェスについて、次のテストを行います。

  1. 公開テスト — exposes の各項目にアクセスできることを、指定されたアクターについて検証します。コレクションに対する for 反復も含みます
  2. 提供テスト — when 条件が真の場合に操作が表示され、そうでない場合は非表示になることを検証します。対応するルールの requires 節が満たされない場合も含みます
  3. アクター制限テスト — 他のアクター型がサーフェスにアクセスできないことを検証します
  4. アクター識別テスト — アクターの identified_by 述語に一致するエンティティのみが操作できることを検証します。within を持つアクターについては、操作が宣言されたコンテキスト内に限定されることを検証します
  5. コンテキストのスコープ設定テスト — context 述語に一致するエンティティがない場合、サーフェスのインスタンスが存在しないことを検証します
  6. コントラクト要件テスト — demands が相手側によって満たされ、fulfils がこのサーフェスによって提供されることを、型付きシグネチャをすべて含めて検証します
  7. 保証テスト — @guarantee 注釈が境界を越えて成立することを検証します
  8. タイムアウトテスト — 参照される時間的ルールが、サーフェスのコンテキスト内で発動することを検証します
  9. 関連ナビゲーションテスト — 関連サーフェスへのナビゲーションが、正しいコンテキストエンティティに解決されることを検証します
仕様モード

テスト義務のドキュメント全体を確認します。仕様全体を網羅するテストを求めている場合に使用します。

テスト生成の分類体系におけるカテゴリ:

  • エンティティと値型のテスト — フィールド、型、オプショナル(?)の null 処理、when 節による状態依存の存在、関係、結合検索、等価性
  • 列挙型のテスト — 名前付き列挙型間の比較可能性、メンバーシップテスト、インライン列挙型の分離
  • 代数的データ型のテスト — バリアントのフィールド、型ガード、網羅性、バリアント名による作成、基底の .created トリガーによる絞り込み
  • 派生値とプロジェクションのテスト — 計算、フィルタリング、-> field による抽出、パラメーター化された派生値、now の揮発性、コレクション操作
  • デフォルトインスタンスのテスト — 無条件での存在、フィールド値、デフォルト間の相互参照
  • 設定のテスト — デフォルト、上書き、必須パラメーター、式形式のデフォルト、修飾参照、設定チェーン
  • 不変条件のテスト — ルール実行後の検証、エッジケース、含意ロジック、エンティティレベルの不変条件
  • ルールのテスト — 成功/失敗/エッジケース、条件式(if ガードが結果の状態を読み取ることの確認)、エンティティの作成、削除、一括更新、ルールレベルの for 反復、let バインディング、連鎖トリガー
  • 状態遷移のテスト — 有効/無効な遷移、終端状態、transitions_to と becomes の意味上の違い
  • 時間的テスト — 期限の境界、再発火の防止、オプショナルフィールドの null 動作
  • サーフェスのテスト — 公開、利用可能性、within スコープによるアクター識別、コンテキストスコープ、関連ナビゲーション
  • コントラクトのテスト — シグネチャの充足、@invariant の遵守、demands/fulfils の方向性
  • モジュール横断テスト — 修飾されたエンティティ参照、外部トリガーへの応答、型プレースホルダーの置換
  • ルール間相互作用のテスト — 重複作成ガード、provides の利用可能性
  • 遷移グラフのテスト — 宣言された各エッジが、それを実証するルールを通じて到達可能であること、宣言されていない遷移が拒否されること、終端状態に送出ルールがないこと、非終端状態に少なくとも 1 つの出口があること、列挙型の値とグラフのエッジが正確に対応していること
  • 状態依存フィールドのテスト — 条件を満たす状態で存在すること、条件外で存在しないこと、when セットに入る際の存在義務、セットから出る際の不在義務、セット内またはセット外での遷移には義務がないこと、収束するすべての遷移でフィールドが設定されること、when 修飾フィールドへのアクセスにガードが必要であること、入力の共通部分による派生値の when 推論
  • シナリオテスト — ハッピーパス、エッジケース、順序の非依存性
  • データフローチェーンのテスト — サーフェスでの入力取得からルール、さらに下流ルールの前提条件まで、完全なチェーンを実行します。各チェーン(サーフェスがトリガーを提供 → ルールがフィールドを保証 → 下流ルールがフィールドを要求)について、サーフェスからデータを送信し、それが下流の前提条件に到達することを確認する統合テストを生成します。
  • 到達可能性テスト — 各初期状態から(.created() を介して)各終端状態まで、遷移グラフ上の有効な経路をたどります。各テストではライフサイクル全体を実行します。
  • デッドロックシナリオテスト — allium analyse が潜在的なデッドロックを特定した状態について、エンティティを行き詰まった状態にして、そこから進行できるかを確認するテストを生成します。
  • エンティティ横断プロセステスト — 複数のエンティティにまたがるプロセスについて、関与するすべてのエンティティを対象に、開始から終端状態までのプロセス全体を実行する統合テストを生成します。

allium analyse が利用可能な場合は、その検出結果を使ってテスト生成の優先順位を決めます。missing_producer または dead_transition の検出結果は、テストで実行する価値のあるギャップを示します。deadlock の検出結果については、エンティティが行き詰まった状態から抜け出せないことを記録するテストを生成します。検出結果の種類の分類については、検出結果への対応を参照してください。

テスト出力の種類

1. アサーションベースのテスト

フィールドの有無、列挙型の値に含まれること、遷移の妥当性、サーフェスでの公開、状態に応じたフィールドの有無など、決定論的な要件に使用します。これらは標準的なユニットテスト/統合テストです。

2. プロパティベーステスト

不変条件とルールの特性に使用します。式を含む各不変条件を、PBT プロパティにします。

  • ジェネレーターの仕様を使用して、有効なエンティティ状態を生成する
  • ルールのシーケンスを適用する(遷移グラフが宣言されている場合はそれに従い、宣言されていない場合はルールのみから有効なシーケンスを導出する)
  • 各ステップで不変条件が成り立つことを確認する

プロジェクトの PBT フレームワークを使用します。

言語フレームワーク確認先
TypeScriptfast-checkpackage.json
PythonHypothesispyproject.toml
RustproptestCargo.toml
Gorapidgo.mod
ElixirStreamDatamix.exs

PBT フレームワークが存在しない場合は、アサーションベースのテストに切り替えます。

3. ステートマシンテスト

ステータスの列挙型を持つエンティティに使用します。遷移グラフが宣言されている場合は、そのグラフ内のすべての経路をたどります。グラフが宣言されていない場合は、ルールから有効な遷移を導出します。

  • 遷移を実現するルールを通じて、遷移が成功することを確認する
  • 拒否される遷移が失敗することを確認する
  • 各状態で、when 節に従って状態依存フィールドの有無を確認する
  • 各状態で不変条件が成り立つことを確認する

ステートマシンテストにはアクションマップが必要です。これは、各遷移エッジに対して、ソース状態のエンティティを受け取り、実際の実装コードを呼び出してターゲット状態のエンティティを生成する関数です。このマップがなければ、テストフレームワークはグラフ内の有効な経路を記述できますが、実行することはできません。

アクションマップを作成するには、次の手順に従います。

  1. 遷移グラフ内の各エッジについて、仕様内でその遷移を実現するルールを見つける
  2. そのルールを実装しているコード(実装ブリッジ)を見つける
  3. 事前条件(requires 節)を設定し、コードを呼び出して、ターゲット状態のエンティティを返すテストアクションを記述する
  4. (from_state, to_state) キーの下にアクションを登録する

マップを作成すると、PBT フレームワークはランダムな有効経路をたどることができます。任意の非終端状態から開始し、ランダムな出辺を選び、そのアクションを適用し、エンティティレベルのすべての不変条件を確認して、これを繰り返します。経路の長さと開始状態はランダムに生成されます。これは、仕様の遷移グラフをテストとして最も完全に表現する方法です。

実装ブリッジ

乖離の確認で weed スキルが行うのと同様に、仕様の構成要素と実装コードを対応付けます。

サーフェステストの場合

サーフェスを実装に対応付けます。

  • API サーフェスはエンドポイント(REST ルート、GraphQL リゾルバー、gRPC サービス)に対応付けます
  • UI サーフェスはコンポーネントまたはページに対応付けます
  • インテグレーションサーフェスはメッセージハンドラーまたは SDK メソッドに対応付けます

コードベースを読み、対応関係を特定します。命名パターン、ルート定義、ハンドラーの登録を探します。

内部テストの場合

仕様内の各ルールについて、次の手順に従います。

  1. ルールを実装しているコード(サービスメソッド、イベントハンドラー、ステートマシンの遷移)を見つける
  2. 関係するエンティティのインスタンス化方法(ファクトリー、ビルダー、フィクスチャ)を特定する
  3. ルールの呼び出し方法(API 呼び出し、メソッド呼び出し、イベントのディスパッチ)を特定する
  4. 事後条件のアサート方法(データベースクエリ、戻り値、イベントのアサーション)を特定する
時間依存テストについて

時間依存トリガー(期限ベースのルール)では、テスト内で制御可能な時刻の情報源が必要です。実装が壁時計の時刻(Instant.now()、System.currentTimeMillis())を使用している場合、テストで期限の前、ちょうど期限時刻、期限後を確実に設定することはできません。

時間依存テストを試みる前に、コンポーネントが注入されたクロックまたは時刻パラメーターを受け取れるか確認してください。一般的なパターンとしては、コンストラクターのClockパラメーター、メソッドのエポックミリ秒引数、TimeProviderインターフェースなどがあります。そのような接続点がある場合は、制御可能な時刻の情報源を注入してください。ない場合は、テスト基盤の不足として報告してください。コンポーネントが時刻の注入に対応するまで、時間依存テストは生成できません。スリープしたり、壁時計の時刻との競合に頼ったりして、時間依存の動作をテストしないでください。

モジュール間のトリガーチェーンについて

あるルールが出力したトリガーを、別の仕様のルールが受け取る場合(例:ArbiterがClerkReceivesEventを出力し、Clerkが処理する場合)、チェーンのテストには複数のコンポーネントを接続する必要があります。

モジュール間テストを生成する前に:

  1. 計画出力からトリガー出力のグラフをたどり、どのルールがトリガーを出力し、他の仕様のどのルールがそれを受け取るか確認します
  2. 参加するコンポーネントを接続する既存の統合テスト用フィクスチャ(パイプラインテスト、エンドツーエンドテスト用ヘルパー、テストハーネスクラス)がコードベースにあるか確認します
  3. フィクスチャがある場合は再利用します。モジュール間テストでは、既存の配線を再構築せずに組み合わせてください
  4. フィクスチャがなくても、コードベースの構造から配線(サービスのコンストラクター、依存性注入、イベントバスの設定)を十分に理解できる場合は、フィクスチャとテストを生成します
  5. 配線が複雑すぎる、または不明瞭で確信を持って生成できない場合は、コンポーネントの配線が必要な箇所をTODOで示したテストのスケルトンを生成します

モジュール間テストは本質的に統合テストです。仕様のトリガーチェーンがコンポーネント間で忠実に実装されていることを検証します。単一コンポーネントのテストが合格した後に優先して実施してください。

既存テストの再利用

コードベースを調査する際は、どの仕様上の義務が既存テストですでにカバーされているか確認します。イベントの送信から確認済みの出力までの正常系を実行する既存の統合テストであれば、複数のrule_successの義務とエンドツーエンドのシナリオをすでにカバーしています。

既存のテストが仕様上の義務をカバーしている場合は、重複テストを生成せず、そのテストを参照してください。統合レベルにおけるpropagateスキルの価値は、仕様の義務リストに対してテストの網羅性が完全かを確認し、不足を特定して、それを補うテストを生成することにあります。以下のプロセスにある照合ステップで、この確認を明示的に行います。正常に動作している手書きテストを、同等の生成テストに置き換えても価値はありません。

遅延された仕様について

遅延された仕様は、別ファイルで完全に定義されています。対象のコードベースに遅延された仕様のモジュールが含まれていない場合は、次のプレースホルダーを含むテストスタブを生成します。

// TODO: deferred spec — InterviewerMatching.suggest
// This behaviour is specified as deferred. Provide a mock or skip.

プロセス

  1. 仕様を読む — エンティティ、ルール、サーフェス、不変条件、遷移グラフ、状態依存フィールド、コントラクト、設定、デフォルト値を把握します。仕様の成熟度を評価するには、仕様の評価を読みます。粗い仕様(エンティティと遷移グラフはあるものの、ルールがないもの)では、テスト要件は限られたものになり、主に構造テストが対象になります。意味のあるテスト生成には仕様が粗すぎる場合は、テストを展開する前に、elicit または distill スキルを使って仕様をさらに作り込むことを提案します。ルールとサーフェスを含む仕様であれば、データフローチェーンテストや到達可能性テストを含む、完全なテスト分類を利用できます。
  2. テスト要件を読む — allium plan の出力、または手動で導出したものを確認します
  3. ドメインモデルを読む — allium model の出力、または手動で導出したものを確認します
  4. コードベースを調査する — 既存のテスト、テストフレームワーク、エンティティの実装、ルールの実装を探します
  5. 構成要素をコードに対応付ける — 仕様のエンティティ/ルール/サーフェスを、実装のクラス/関数/エンドポイントと対応付けます
  6. テストを生成する — プロジェクトの規約に沿ってテストファイルを作成します
  7. テストのコンパイル/実行を確認する — 生成されたテストが構文的に有効であることを確認します
  8. 要件を照合する — すべての要件に対応するテストがあることを確認し、不足があれば自動的に補います(要件の照合を参照)
調査チェックリスト

テストを生成する前に、以下を確認します。

  • テストフレームワークとランナー(Jest、pytest、cargo test など)
  • PBT フレームワークがある場合はその種類(fast-check、Hypothesis、proptest など)
  • テストファイルの配置規約(同じ場所への配置、__tests__/、tests/ など)
  • エンティティ/モデルの配置場所とパターン(クラス、インターフェース、構造体)
  • テストデータのファクトリー/フィクスチャのパターン
  • 状態遷移の実装方法(メソッド、イベント、ステートマシン)
  • サーフェスの実装方法(ルート、コントローラー、リゾルバー)
  • 既存のテストヘルパーやユーティリティの有無
  • 時間に関するテスト用に、コンポーネントが注入された時間ソースを受け付けるか
  • モジュール間のトリガーチェーン用の統合テストフィクスチャがあるか
  • 仕様の要件のうち、既存のテストでカバーされているもの
ジェネレーターへの対応

ジェネレータースペックが利用できる場合は、有効なテストデータの生成に使用します。

  • フィールドの型と制約を遵守します
  • 遷移グラフを持つエンティティでは、when 条件に応じて適切なフィールドを設定し、特定のライフサイクル状態のエンティティを生成します(例:shipped 状態の Order には tracking_number と shipped_at が設定され、pending 状態の Order には設定されません)
  • 不変条件については、境界条件を検証できる状態を生成します
  • 設定パラメーターについては、オーバーライドをテストする場合を除き、宣言されたデフォルト値を使用します
オブリゲーションの照合

テストを生成した後、allium plan の出力または手動で導き出したオブリゲーションの一覧全体を確認し、生成したテストか既存のテストかを問わず、各オブリゲーションをそれに対応するテストに結び付けます。照合は表に出しません。オブリゲーションは内部作業データなので、すべて順調な間は対応関係を説明したり、オブリゲーションの一覧を出力したり、カバレッジレポートを提示したりしないでください。

カバーするテストがないオブリゲーションについては、カバーを試みます。

  1. 不足しているテストを生成するか、既存のテストを拡張して部分的にカバーする
  2. 新しいテストがコンパイルされ、実行できることを確認する
  3. 結果に照らしてオブリゲーションを再確認する

これは小さなループであり、外側の Allium ループと同じガードが必要です(ループの進め方を参照)。

  • 試行回数の上限 — オブリゲーションごとに最大 2 回まで試行します。2 回目の試行でも、コンパイル可能でオブリゲーションをカバーするテストができなければ、そのオブリゲーションは保留します。
  • 進捗なしの上限 — 残っている未対応項目を一通り確認しても新たにカバーできるものがなければ、そこで終了します。それ以上続けても空回りします。

ガードに達した時点で未カバーのオブリゲーションだけをユーザーに提示します。それぞれに分類と簡潔な理由を 1 行で記載します。

  • インフラストラクチャの不足 — テストに必要な接続点がコードにありません。たとえば、時間に関するオブリゲーションに対応する注入可能なクロックがない場合や、モジュール間の処理フローに対応する統合テスト用フィクスチャがない場合です。
  • マッピング不能な構成要素 — コードを起点とするフローで、仕様の構成要素に対応する実装コードがなく、実装との橋渡しを構築できませんでした。

実装の不足は、未解決項目の分類には含まれません。仕様を起点とするフローでは、設計上、まだコードがありません。意図されたインターフェースに対してテストを作成し、失敗させてください。失敗するテストもオブリゲーションをカバーしており、ループの実装フェーズで成功する状態にします。オブリゲーションが未カバーとなるのは、それに対応するテストをまったく作成できない場合だけです。

最後に、N obligations, M covered, K uncovered の形式で要約を 1 行だけ記載します。すべてカバーされている場合、この 1 行が照合に関するユーザー向け出力のすべてです。個別のオブリゲーションに言及がなければカバーされています。項目別に記載された内容については、人による判断が必要です。propagate が Allium ループ内で実行される場合、この行はループの統合サマリーに含まれます。未カバーのオブリゲーションが報告理由なしに残っている間は、仕様を収束したものとして扱ってはなりません。

改ざんベースラインの記録(ループ内)

Allium ループ内で実行する場合、ループの独立した検証担当が後から改ざんされていないことを確認できるよう、照合のベースラインを記録します。台帳(.allium-loop/<goal-slug>.json。 ループの進め方を参照)には、次の内容を記録します。

  • 生成した各テストファイルのコンテンツハッシュ(例:sha256)を、パスをキーとして generated_test_hashes に書き込む
  • 照合の要約行を reconciliation に記録する

このハッシュは、witness スキルが再計算する正解の基準です。propagate が実行されていないのに生成済みテストのハッシュが変わっていれば、手作業で編集されたテストです。これは、ループが決して収束に至ってはならない不正行為です。ベースラインを記録することで、このチェックが可能になります。省略すると、witness はテストの合格は確認できても、テストが弱められていないことは確認できません。これは簡単な記録作業であり、レポートではありません。説明しないでください。

型付き結果(ループへの引き渡し)

Allium ループ内で propagate サブエージェントとして実行する場合は、結果を propagate-result.schema.json に準拠する単一の JSON オブジェクトとして返し、それ以外は何も出力しないでください。ループは構造化フィールドに基づいて処理を振り分けます。obligations と uncovered_obligations によって網羅性が完全かどうかが判断されます(uncovered_obligations が空でない間、ループは収束してはなりません)。また、generated_tests には witness が再導出するパスとハッシュのベースラインが含まれます。これは、上記で記録したものと同じハッシュを、引き渡しデータの正式なフィールドとして含めたものです。summary フィールドは N obligations, M covered, K uncovered の行にしてください。すべてのフィールドを出力し、空のリストには [] を使用してください。フィールドを省略してはいけません。対話形式で実行する場合は、これまでと同様に summary 行全体がユーザー向けの出力です。型付きレコードは会話ではなく、機械への引き渡しに使用します。

{
  "phase": "propagate",
  "obligations": { "total": 12, "covered": 12, "uncovered": 0 },
  "uncovered_obligations": [],
  "generated_tests": [{ "path": "order.test.js", "hash": "sha256:9f2c…" }],
  "test_paths": ["order.test.js"],
  "open_questions": [],
  "summary": "12 obligations, 12 covered, 0 uncovered"
}

その他のツールとの連携

  • distill はコードから仕様を生成します。その仕様を propagate に渡します。
  • weed は整合性を確認します。テストを生成した後、weed が仕様とコードの一致を検証します。
  • tend は仕様を進化させます。仕様を変更した後、propagate を再実行してテストを更新してください。
  • elicit は会話を通じて仕様を作成します。仕様の準備ができたら、propagate がテストを生成します。

制限事項

  • 生成されたテストは出発点です。プロジェクト固有のパターンに合わせて調整が必要な場合があります。
  • 実装との橋渡しは LLM を介して行われます。複雑なコードベースや特殊なコードベースでは、マッピングに手動での指示が必要になる場合があります。
  • モジュール間テストでは、サービス境界をまたぐコンポーネントの接続を理解する必要があります。コードベースの構造が明確な場合は、完全なテストを生成できます。接続関係が不明瞭な場合は、手動設定用の TODO を含むスケルトンとしてテストが生成されます。
  • ランタイムトレースの検証とモデル検査は、別個の作業分野です。

コメント