> ## Documentation Index
> Fetch the complete documentation index at: https://adcp-docs-ja.pier1.co.jp/llms.txt
> Use this file to discover all available pages before exploring further.

# テスト準備を整える

> セールスエージェントオペレーターがストーリーボードを実行する前に整えておく必要があるもの — ケイパビリティ、サンドボックスアカウント、コンプライアンステストコントローラー。

ストーリーボードは、エージェントが **conformant** として公開されるかを決めるバージョン管理されたバイヤーシミュレーションスイートです。バイヤーエージェントはそのステータスでフィルターします — 過剰主張やストーリーボード失敗は、CI 警告ではなく公開で永続的なシグナルです。このページは「エージェントを構築した」と「`npx @adcp/sdk@latest storyboard run` を実行できる」の間のチェックリストです。

## ランナーが必要とする 3 つのサーフェス

ランナーは、バイヤーが呼ぶのと同じ公開ツール、加えてフィクスチャセットアップ用の 1 つのサンドボックス専用ツールを通じてエージェントを駆動します。3 つのサーフェスが整っていなければなりません:

| Surface                                                                         | What it tells the runner            | Where it lives      |
| ------------------------------------------------------------------------------- | ----------------------------------- | ------------------- |
| [`get_adcp_capabilities`](/docs/protocol/get_adcp_capabilities)                 | どのプロトコルと専門分野を主張するか、サンドボックスをサポートすること | エージェントのケイパビリティレスポンス |
| [`sync_accounts`](/docs/media-buy/advanced-topics/sandbox)（または `list_accounts`） | テストを実行するサンドボックスアカウントを取得する方法         | エージェントのアカウントツール     |
| [`comply_test_controller`](/docs/building/by-layer/L3/comply-test-controller)   | フィクスチャをシードしセラー側の遷移を決定的に強制する方法       | エージェント、サンドボックス専用    |

あなたはこれら 3 つのサーフェスを出荷します。ランナーはストーリーボード選択、フィクスチャ順序、レスポンス比較を所有します。

## Step 1 — ケイパビリティを正直に宣言

`get_adcp_capabilities` は、ランナーがどのストーリーボードがあなたに適用されるかを選ぶ方法です。それは [適合性](/docs/building/verification/conformance) コントラクトでもあります: あなたは宣言したものに一致するすべてのストーリーボードを通過することを約束しています。

下の例はフルサービスの保証セラー（プロポーザルライフサイクル有効）用です。直接購入の保証セラーは `media_buy.supports_proposals: false` を設定（または省略）します。放送 TV セラーは `sales-broadcast-tv` を主張します。クリエイティブのみのエージェントは `creative-ad-server` または `creative-generative` 専門分野で `creative` プロトコルを主張します。シグナルプロバイダーは `signals` を主張します。パターンは同じ: 実際に実装するもののみを宣言。

```json theme={null}
{
  "supported_protocols": ["media_buy", "creative"],
  "specialisms": ["sales-guaranteed"],
  "media_buy": {
    "supports_proposals": true
  },
  "account": {
    "sandbox": true,
    "require_operator_auth": false
  }
}
```

* **`supported_protocols`** — `/compliance/{version}/protocols/` から一致するプロトコルストーリーボードを引き込む。
* **`specialisms`** — オプトイン専門分野ストーリーボードを引き込む（完全な列挙については [Compliance Catalog](/docs/building/verification/compliance-catalog) を参照）。
* **`account.sandbox: true`** — サンドボックスセマンティクス（実際の支出なし、本番副作用なし）を尊重することをシグナル。
* **`account.require_operator_auth`** — サンドボックスブートストラップパス（ステップ 2）を決定。

<Warning>
  RTB のみを実行するのに `sales-guaranteed` を主張すると、記録に残るかたちで失敗するストーリーボードに送り込まれます。適合性ステータスは、バイヤーエージェントがセラーをフィルターするのに使う [Verified](/docs/building/verification/conformance) バッジの一部です — 一度過剰主張すると、どこでも包含を失います。
</Warning>

## Step 2 — サンドボックスブートストラップパスを選ぶ

ランナーは何かをする前にサンドボックスアカウントを取得しなければなりません。`require_operator_auth` フラグがパスを選びます:

**バイヤー宣言アカウント（`require_operator_auth: false`）。** エージェントは任意の認証されたバイヤーからの `sync_accounts` を受け入れます。ランナーは `sandbox: true` で `sync_accounts` を呼びオンデマンドでテストアカウントを鋳造します。ほとんどの新しいセールスエージェントはここから始めます。

**アカウント ID 名前空間（`require_operator_auth: true`）。** アカウントはあなた側の人間または上流プラットフォームによって事前プロビジョニングされなければなりません。ランナーはサンドボックスフィルターで `list_accounts` を呼び既存のテストアカウントを発見します。認証情報が正確に 1 つのサンドボックスアカウントにバインドされている場合、そのシングルトンを返します。オペレーターに 1 つを要求する方法を伝える短いノートを公開します — 連絡先、期待されるターンアラウンド、受け取る認証情報を含めます。

完全な詳細と例: [サンドボックスモード](/docs/media-buy/advanced-topics/sandbox)。

## Step 3 — コンプライアンステストコントローラーを実装

コンプライアンステストコントローラーなしでは、ランナーはバイヤー開始のフロー（**観察モード**）のみをテストします — スキーマ適合性、auth 拒否、ハッピーパスバイヤー呼び出し。それは初期統合作業と本番パスのサンドボックススモークテストに十分ですが、コントローラーシードまたはコントローラー強制のシナリオがスコープ内のときはいつでも **部分カバレッジ** を生成します。[適合性](/docs/building/verification/conformance) は、コントローラーによって可能になる完全なライフサイクルウォークである **決定的モード** を、完全な専門分野カバレッジのラインとして扱います。

実際には、セラーは通常両方を実行します:

| Run         | Endpoint                                                     | Expected result                                                                                                    |
| ----------- | ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------ |
| 観察サンドボックス実行 | サンドボックスアカウントを持つ本番エンドポイント、コントローラー未公開                          | `steps_failed = 0`。live-only プローブは未選択として現れ、選択されたコントローラー依存シナリオは `missing_test_controller` スキップでサマリーを `partial` にしうる |
| 決定的実行       | `comply_test_controller` を持つ dev/staging またはサンドボックス専用エンドポイント | `steps_failed = 0` でコントローラー依存カバレッジスキップなし                                                                           |

最初の実行は「本番バイヤー可視パスはサンドボックストラフィックを許容するか？」に答えます。2 番目は「ランナーは宣言された専門分野のすべてのライフサイクルパスを証明できるか？」に答えます。それらを 1 つのシグナルに折り畳まないでください。ゼロ失敗の部分実行は有用ですが、バイヤーはどのライフサイクルアサーションがグレードされなかったかを正確に知るためスキップリストが必要です。

`comply_test_controller` は、3 つのファミリーをカバーする `scenario` パラメーターを持つ単一のサンドボックス専用ツールです:

| Scenario family | What it does                                             | When you need it                              |
| --------------- | -------------------------------------------------------- | --------------------------------------------- |
| `seed_*`        | 呼び出し元供給の ID でフィクスチャ（プロダクト、価格オプション、クリエイティブ、プラン、メディアバイ）を作成 | ほぼすべてのストーリーボード — これがハードコード ID ディスカバリーを置き換える   |
| `force_*`       | 通常セラー開始のエンティティを状態遷移を通じて駆動                                | ステートマシン（クリエイティブ承認、アカウント停止など）をテストする任意のストーリーボード |
| `simulate_*`    | 配信データまたは予算支出を注入                                          | レポートと予算のストーリーボード                              |

シナリオごとのパラメーターとレスポンス形状については [コンプライアンステストコントローラーリファレンス](/docs/building/by-layer/L3/comply-test-controller) を、各専門分野がどのシナリオを要求するかについては [Compliance Catalog](/docs/building/verification/compliance-catalog) を参照。

### SDK スキャフォールドの配線

`@adcp/sdk`（6.x が AdCP 3.0 の本番 GA）は `createComplyController` を出荷し、ツール登録、パラメーター検証、エラーエンベロープ、再シード冪等性を再実装せずにデータ層をコントローラーに配線できます。

```bash theme={null}
npm install @adcp/sdk
```

<Note>
  スキャフォールドは TypeScript/JavaScript です。Python、Go、Java のセラーは [スキーマ](https://adcontextprotocol.org/schemas/v3/compliance/comply-test-controller-request.json) に対して直接ツールを実装します — 下のコントラクト（アダプター、エラーコード、冪等性セマンティクス）は同じように適用されます。他言語の SDK は [Choose your SDK](/docs/building/by-layer/L4/choose-your-sdk) で追跡されます。
</Note>

```ts theme={null}
import { createComplyController, TestControllerError } from '@adcp/sdk/testing';
// `server` is your AdcpServer or MCP server instance — see `createAdcpServer` in
// `@adcp/sdk/server` if you need a reference setup.

const controller = createComplyController({
  seed: {
    product: async ({ product_id, fixture }) => {
      await productRepo.upsert(product_id, fixture);
    },
    creative: async ({ creative_id, fixture }) => {
      await creativeRepo.upsert(creative_id, fixture);
    },
    // Add pricing_option, plan, media_buy as your claimed storyboards require.
  },

  force: {
    creative_status: async ({ creative_id, status, rejection_reason }) => {
      const previous = await creativeRepo.getStatus(creative_id);
      if (previous == null) {
        throw new TestControllerError('NOT_FOUND', `creative ${creative_id} not found`);
      }
      const result = await creativeRepo.transition(creative_id, status, rejection_reason);
      if (result.kind === 'invalid_transition') {
        throw new TestControllerError('INVALID_TRANSITION', result.message, previous);
      }
      return { success: true, previous_state: previous, current_state: result.status };
    },
    // Add account_status, media_buy_status, session_status as needed.
  },

  // simulate: { delivery, budget_spend } — add if you claim reporting/budget specialisms.
});

// Primary gate: register the tool only in sandbox deployments, so it never
// appears in production `tools/list`.
if (process.env.ADCP_SANDBOX === '1') {
  controller.register(server);
}
```

スキャフォールドがあなたのために扱うもの:

* **ツール登録とスキーマ。** `controller.toolDefinition` が公開された仕様バージョンと同期のまま。
* **ディスパッチと `UNKNOWN_SCENARIO`。** 登録しないシナリオは自動的に `UNKNOWN_SCENARIO` を返す — スキーマエラーは決してない。
* **パラメーター検証。** 無効なパラメーターは、アダプターに到達せずに読める `error_detail` を伴う `INVALID_PARAMS` を生成。
* **シード冪等性。** 同じ `product_id` と等価な `fixture` で `seed_product` を 2 回呼ぶと `previous_state: "existing"` を返す。分岐した `fixture` は `INVALID_PARAMS` を返す。アダプターは最初のシードでのみ呼ばれる。
* **型付きエラーエンベロープ。** コントローラーエラーコード表の `code` で `TestControllerError(code, message, currentState?)` を throw。一般的なアダプターコードは `'INVALID_TRANSITION' | 'NOT_FOUND' | 'FORBIDDEN' | 'INVALID_PARAMS'`。ダイジェストモードの `query_upstream_traffic` 実装は、RFC 8785/JCS 正準化が非有限数値に遭遇したとき `JCS_NON_FINITE_NUMBER` も返しうる。

スキャフォールドはステートマシンを **所有しません**。遷移ルールはあなたのアダプターに存在するため、コンプライアンステストと本番が 1 つの真実の源泉を共有します — [teach-to-test 回避セクション](#avoiding-the-teach-to-test-trap) が依存するメカニクス。

### サンドボックスゲーティングの 2 層

スキャフォールドは 2 つのゲートをサポートします。同じプロセスからサンドボックスと本番の両トラフィックを提供する任意のデプロイで両方を出荷してください:

1. **登録ゲート（主要）。** `controller.register(server)` を環境チェックでラップ。これが `comply_test_controller` を本番 `tools/list` から完全に外すものです。それなしでは、本番エンドポイントの漏洩したサンドボックス認証情報がセラー側の状態強制を露出します。
2. **リクエストごとのゲート（多層防御）。** `createComplyController` に `sandboxGate: (input) => boolean` を渡す。スキャフォールドはすべてのリクエストでそれを呼び、`false` を返すとき `FORBIDDEN` を返す。ツールが登録されているが一部のリクエストが依然本番アカウントを参照しうる共有プロセスデプロイでこれを使う。

`sandboxGate` は生のツール入力（`Record<string, unknown>`）を受け取ります。SDK は auth コンテキストをそれに配管しません — あなたが何を検査するかを決めます。典型的なパターンは、参照されたエンティティ ID を `params` から引き出し、それが自身のデータ層でサンドボックスアカウントに属することを検証することです:

```ts theme={null}
sandboxGate: async (input) => {
  const params = input.params as { account_id?: string; media_buy_id?: string } | undefined;
  const accountRef = params?.account_id
    ?? (params?.media_buy_id && await mediaBuyRepo.getAccountId(params.media_buy_id));
  return typeof accountRef === 'string' && await accountRepo.isSandbox(accountRef);
}
```

<Note>
  カスタム MCP ラッパー — リクエストごとの auth の AsyncLocalStorage、トランスポートレベルのサンドボックスゲーティング、セッション裏付けストア — については、`@adcp/sdk/server` から低レベルの `handleTestControllerRequest`、`toMcpResponse`、`TOOL_INPUT_SHAPE` を直接合成してください。
</Note>

## Step 4 — ストーリーボードランナーを実行

3 つのサーフェスが整ったら、ランナーが引き継ぎます:

```bash theme={null}
npx @adcp/sdk@latest --save-auth my-agent http://localhost:3001/mcp
npx @adcp/sdk@latest storyboard run my-agent
```

ランナーはケイパビリティを発見し、サンドボックスアカウントを取得し、コントローラー経由でフィクスチャをシードし、各一致するストーリーボードを歩きます。完全な CLI、デバッグフラグ、Addie ワークフローについては [エージェントを検証する](/docs/building/verification/validate-your-agent) を参照。

## Avoiding the teach-to-test trap

ストーリーボードはフィクスチャ ID をハードコードします — `"test-product"`、`"campaign_hero_video"`、`"acmeoutdoor.example"`。それらの文字列を特別扱いするコントローラーは、すべての実際のバイヤーで黙って失敗しながらスイートを通過します。それはまさに適合性が防ごうとしている業界コストです: すべての適合性後の統合失敗がセラーの評判を焼き、バイヤーエージェントの懐疑を膨らませ、プロトコル採用を遅らせます。

SDK スキャフォールドは既に正しい方向を指しています: アダプターは `product_id`、`creative_id` などを条件ではなく値として受け取ります。アダプターに `product_id === "test-product"` のスイッチが含まれていたら、後退しています。

2 つの経験則:

1. **シードシナリオを汎用に実装。** `seed_product` は任意の `product_id` を受け入れ、その ID でプロダクトをサンドボックスデータ層に永続化する。アダプターはサンドボックスストアに対する実際の upsert の薄いラッパー。
2. **`fixture` オブジェクトがコントラクトで、ID はそうでない。** ストーリーボード作者は `fixture` をテストが必要とする最小の形状に設定する。それを超えたすべて — ディスカバリー、フィルタリング、認可 — は、本番データで実行するのと同じ方法でフィクスチャシードされたデータで実行される、あなたの通常のコードパス。

確認するには: ストーリーボードのフィクスチャ ID をランダム UUID にスワップし再実行します。実行がまだ通過すれば、コントローラーは正しいです。壊れれば、修正するハードコードされた動作があります。

## 準備チェックリスト

最初の完全なストーリーボードスイープの前に:

* [ ] `get_adcp_capabilities` が実際に実装するプロトコルと専門分野のみを返す
* [ ] `account.sandbox: true` が宣言され尊重される — サンドボックスリクエストが実際の支出、本番プラットフォーム呼び出し、永続化された本番状態を生成しない
* [ ] `sync_accounts`（暗黙）または `list_accounts`（明示）がステップ 2 に従いサンドボックスリクエストを扱う
* [ ] `comply_test_controller` が任意の本番エンドポイントの `tools/list` から欠如している
* [ ] 非サンドボックスアカウントを参照するリクエストが `FORBIDDEN` で拒否される
* [ ] 主張するストーリーボードが依存するすべてのシードシナリオが、ID 特別扱いなしにフィクスチャを汎用に永続化する
* [ ] すべての force シナリオが本番と同じ状態遷移ルールを使い、無効な遷移で型付きエラーを返す
* [ ] フィクスチャ ID がランダム UUID にスワップされても完全なストーリーボードスイープが通過する

## 次は

* **[エージェントを検証する](/docs/building/verification/validate-your-agent)** — CLI、Addie ワークフロー、マルチインスタンス検証
* **[コンプライアンステストコントローラーリファレンス](/docs/building/by-layer/L3/comply-test-controller)** — 完全なシナリオごとの仕様
* **[サンドボックスモード](/docs/media-buy/advanced-topics/sandbox)** — 2 つのアカウントモデルパスを詳しく
* **[Conformance](/docs/building/verification/conformance)** — 実行が通過したら「conformant」と「verified」が何を意味するか
