> ## 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.

# x-entity スキーマアノテーション

> エンティティアイデンティティを運ぶ AdCP スキーマフィールドをどうアノテーションするか、クロスストーリーボードの context-entity lint が #2627 brand_id advertiser 対 rights-holder ケースのような混同バグを捕捉できるように。

# `x-entity` スキーマアノテーション

## スキーマ作成者向け TL;DR

スキーマを編集していて、追加（またはレビュー）しているフィールドが id、slug、または安定した参照:

1. **値はストーリーボードステップを越えるか?**（`context_outputs` 経由でキャプチャ、`$context.<name>` として消費、またはリクエストとレスポンス間でエコー。）いいえなら、アノテーションしない。
2. 下のテーブルから **エンティティタイプを選ぶ**。どれも合わない場合、`static/schemas/source/core/x-entity-types.json` の完全なレジストリを読む — それでも何もない場合、追加するためレジストリに PR。
3. リーフプロパティの `type` の隣に **`x-entity: <value>` を追加**。`$ref` された共有タイプには、使用サイトではなく共有タイプにアノテーション。多くの既知の id フィールドを持つドメインスイープには、`node scripts/add-x-entity-annotations.mjs <files> [--overlay <map>]` を実行 — ベースマップは `scripts/x-entity-field-map.json`、ドメインごとのオーバーレイが曖昧な名前（`list_id`、`plan_id`、`pricing_option_id`）を解決。スクリプトは書き込み前にすべての値をレジストリに対して検証するので、タイポはハード失敗。
4. フィールドがリクエストからの値のパススルー **エコー** の場合、両側で **同じ** エンティティタイプでアノテーション。

lint は `x-entity` のないフィールドで沈黙するので、部分的ロールアウトは安全です。

## なぜこれが存在するか

一部の AdCP スキーマは単一のフィールド名 — `brand_id`、`list_id`、`plan_id` — を、異なるコンテキストで **異なる種類のエンティティ** を参照する値に使います。最も引用される例: `brand_id` は「アドバタイザーのブランド」（`get_brand_identity` から）または「権利保有者 / タレントブランド」（`get_rights` 内）を意味しうる。同じ JSON 形状、異なるエンティティ。両方ともローカルで有効。不一致は、ストーリーボードが 1 つの種類の値を `$context` にキャプチャし後のステップが他を期待して消費するときのみ表面化 — [issue #2627](https://github.com/adcontextprotocol/adcp/issues/2627) で追跡されるとおり。

`x-entity` は、各アイデンティティを運ぶフィールドを値が解決する *エンティティタイプ* でタグ付けする非検証 JSON Schema アノテーションです。context-entity lint（`scripts/lint-storyboard-context-entity.cjs`）は、ストーリーボードの `context_outputs` キャプチャサイトと `$context.<name>` 消費サイトを歩き、両端で `x-entity` を読み、不一致をフラグします。

## いつ追加するか

以下のとき、かつそのときのみフィールドに `x-entity` を追加:

1. フィールドの値がビジネスエンティティへの id、slug、または安定した参照、**かつ**
2. ストーリーボードがその値をステップ全体でキャプチャまたは消費する可能性がある（`context_outputs` または `$context.<name>` 経由）。

リクエストフィールドとレスポンスフィールドの両方がアノテーションを取ります。`$ref` で参照される共有タイプ（例: `core/brand-id.json`）はアノテーションを一度運びます。それはすべての使用サイトで適用されます。

**エコーフィールド**（クライアントがリクエストで送った値をパススルーするレスポンスフィールド）は、リクエスト側と同じエンティティタイプでアノテーションされる *べき* です。lint はキャプチャと消費を対称的に扱います — アノテーションされたエコーは、ストーリーボードがそれを `$context` に再キャプチャし誤解を招く名前の下で転送するときを捕捉します。

以下は **アノテーションしない**:

* 一時的なリクエストスコープの値（`idempotency_key`、`request_id`、`correlation_id`）。
* 純粋に記述的なフィールド（表示名、URL、フリーテキスト）。
* ストーリーボードステップ境界を越えないフィールド。
* Enum 値（`right_type`、`audience_type`） — それらはタグで、エンティティ参照ではない。

## 配置

リーフプロパティ定義の、`type` / `description` の隣:

```json theme={null}
{
  "properties": {
    "brand_id": {
      "type": "string",
      "description": "Brand identifier from the agent's roster",
      "x-entity": "rights_holder_brand"
    }
  }
}
```

エンティティの配列には、アイテムスキーマにアノテーション:

```json theme={null}
{
  "rights": {
    "type": "array",
    "items": {
      "properties": {
        "rights_id": {
          "type": "string",
          "x-entity": "rights_contract"
        }
      }
    }
  }
}
```

共有 `$ref` タイプ（例: `core/brand-id.json`）には、共有タイプにアノテーション。すべての使用サイトがエンティティタイプを継承:

```json theme={null}
{
  "$id": "/schemas/core/brand-id.json",
  "type": "string",
  "x-entity": "advertiser_brand"
}
```

**共有タイプ不変条件:** 共有タイプが `x-entity` を運ぶと、それへのすべての `$ref` がそのエンティティスコープを主張します。`core/brand-id.json` は `advertiser_brand` とタグ付けされているので、権利保有者 / タレントロスターブランド id はそのタイプを再利用できません — 文字列形状が同一でも別の共有タイプ（例: `core/rights-holder-brand-id.json`）を作成します。lint は共有タイプを真実の源泉として扱います。それをスコープ全体で黙って再利用することが、私たちが捕捉するバグです。

共有タイプがコンテキスト全体で曖昧に使われる場合、アノテーションを省略するのではなく *タイプを分割* します — 曖昧性が lint が捕捉するために存在する問題です。

### `oneOf` / `anyOf` / `allOf` バリアントを持つ共有タイプ

共有タイプのルートが複合（`oneOf` / `anyOf` / `allOf`）ですべての分岐が同じエンティティに解決する場合、ルートで一度アノテーション — lint はバリアントに降下する前にルートレベル `x-entity` を読むので、オブジェクト全体のキャプチャ（例: `core/signal-id.json` の `$context.signal_id`）は各バリアントに `x-entity` を重複させずにクリーンに解決します。`core/signal-id.json` はこのパターンに従います: ルートレベル `x-entity: signal`、バリアントローカルの `id` フィールドは、`id` がそのバリアントの名前空間（`data_provider_domain` または `agent_url`）内でのみ一意なので、意図的にアノテーションされないままです。内部 `id` をアノテーションすると、2 つの異なる名前空間の id が lint に交換可能に見えます。

バリアントが *異なる* エンティティに解決する場合、**タイプを分割** します。レジストリ lint は、ウォーカーのルートレベルチェックが空パスで勝ちバリアント値を黙って落とすため、root+variant の不一致（`composite_entity_disagreement` ルール）をフラグします。

## 登録されたエンティティタイプ

権威的なリストは `static/schemas/source/core/x-entity-types.json` に存在します。lint は未知の値を拒否します — レジストリの拡張は意図的で PR を要求します。

高レベルのグループ化（完全な説明についてはレジストリを参照）。*下のカテゴリーは方向付けのための編集グループ化のみ。`static/schemas/source/core/x-entity-types.json` のレジストリが権威的リスト。*

| Category           | Values                                                                                                                                     |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------ |
| Brand & rights     | `advertiser_brand`, `rights_holder_brand`, `rights_grant`                                                                                  |
| Account & party    | `account`, `operator`                                                                                                                      |
| Media buy          | `media_buy`, `package`, `product`, `product_pricing_option`                                                                                |
| Creative           | `creative`, `creative_format`                                                                                                              |
| Data & targeting   | `audience`, `signal`, `signal_activation_id`, `event_source`                                                                               |
| Lists & catalogs   | `collection_list`, `property_list`, `catalog`, `property`                                                                                  |
| Plans & governance | `media_plan`, `governance_plan`, `governance_registry_policy`, `governance_inline_policy`, `governance_check`, `content_standards`, `task` |
| Vendor services    | `vendor_pricing_option`, `vendor_metric`                                                                                                   |
| SI                 | `si_session`, `offering`                                                                                                                   |

**Plan 対 policy 対 check:** `governance_plan` はプランコンテナを識別（*「どのプラン?」* に答える）。`governance_registry_policy` / `governance_inline_policy` はプラン内またはプランが参照するルールを識別（*「どのルール?」*）。`governance_check` はポリシーに対するプランの特定の評価を識別（*「どのチェック?」* — `check_governance` と `report_plan_outcome` 間でラウンドトリップ）。キャプチャされた値が答える質問で選ぶ。

**Registry 対 inline ポリシー:** フィールドがグローバルに一意なレジストリ id（例: `uk_hfss`、`us_coppa`、`garm:brand_safety:violence`）を保持するとき `governance_registry_policy` を使う。フィールドが `policy-entry.json` 経由で作成されたプランスコープのカスタム id を保持するとき `governance_inline_policy` を使う。AdCP タスクスキーマの `policy-entry.json` へのすべての `$ref` は定義上インライン — レジストリエントリーは別の帯域外 API でサーブされる。フィールドがランタイムでいずれかを正当に保持できる場合（2 つの曖昧なサイト: `check-governance-response::findings[].policy_id`、`get-plan-audit-logs-response` 監査エントリー、プラス予約された `creative/creative-feature-result.json::policy_id` と `core/feature-requirement.json::policy_id`）、アノテーションされないままにし `"x-entity deliberately omitted"` で始まる `$comment` を追加 — ギャップリスターがそのフレーズを認識しリーフをスキップします。

レジストリファイルが真実の源泉です。リポジトリ全体のすべてのアノテーションされたフィールドを見るには: `git grep -l x-entity static/schemas/source`。

### 新しいエンティティタイプの追加

スキーマ変更が任意の登録された値に合わない id を導入するとき:

1. `static/schemas/source/core/x-entity-types.json` の `enum` 配列に新しい値を追加。
2. 同じファイルの `x-entity-definitions` の下に一段落の定義を追加。id が何を識別するか、それを使うスキーマ、既知の注意点（例: 名前空間スコープ）を記述。
3. 上のカテゴリーテーブルの最も適切な行に新しい値を追加。
4. 新しい値が既存のもの（例: plan 対 policy 対 check）に隣接する場合、テーブルの下に一文の曖昧性解消を追加。
5. 値が将来のドメインスイープでパッチスクリプトによって適用される場合、正準フィールド名 → エンティティ値マッピングとともに `scripts/x-entity-field-map.json` に追加。同じフィールド名がドメインで分割する場合（`plan_id` や `list_id` のように）、`__scope_specific__` / `__ambiguous__` センチネルを使い、ドメインごとの PR が供給すべきオーバーレイパターンを文書化。

## lint がアノテーションをどう読むか

クロスストーリーボードウォーク（`scripts/lint-storyboard-context-entity.cjs`）は `npm run build:compliance` と `npm run test:storyboard-context-entity` として実行:

1. 各ストーリーボードステップの `context_outputs[].path` について、ステップの `response_schema_ref` を参照場所まで歩き、そこで `x-entity` を読む。`(capture_name → entity_type)` を記録。
2. 値が `$context.<name>` の各ストーリーボードステップの `sample_request` フィールドについて、ステップの `schema_ref`（リクエストスキーマ）を参照フィールドまで歩き、そこで `x-entity` を読む。キャプチャテーブルで名前をルックアップ。
3. 両端が `x-entity` を持ち一致しない場合、違反をフラグ。

lint は **欠けているアノテーションで沈黙** します — 部分的ロールアウトは安全です。欠けているアノテーションは「これがどのエンティティか知らない」として扱われ、「これらは一致しなければならない」ではありません。これはアノテーションパスが偽陽性を生成せずにドメインごとに進むことを可能にします。どのドメインがアノテーションされたかチェックするには、`git grep -l x-entity static/schemas/source` を実行。

## 関連

* レジストリ: `static/schemas/source/core/x-entity-types.json`
* Lint: `scripts/lint-storyboard-context-entity.cjs`
* テスト: `tests/lint-storyboard-context-entity.test.cjs`
* 正準ケース: [#2627 brand\_rights storyboard conflates advertiser brand\_id with talent brand\_id](https://github.com/adcontextprotocol/adcp/issues/2627)
* 追跡 issue: [#2660 Storyboard field-entity-context lint](https://github.com/adcontextprotocol/adcp/issues/2660)
