x-entity スキーマアノテーション
スキーマ作成者向け TL;DR
スキーマを編集していて、追加(またはレビュー)しているフィールドが id、slug、または安定した参照:- 値はストーリーボードステップを越えるか?(
context_outputs経由でキャプチャ、$context.<name>として消費、またはリクエストとレスポンス間でエコー。)いいえなら、アノテーションしない。 - 下のテーブルから エンティティタイプを選ぶ。どれも合わない場合、
static/schemas/source/core/x-entity-types.jsonの完全なレジストリを読む — それでも何もない場合、追加するためレジストリに PR。 - リーフプロパティの
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)を解決。スクリプトは書き込み前にすべての値をレジストリに対して検証するので、タイポはハード失敗。 - フィールドがリクエストからの値のパススルー エコー の場合、両側で 同じ エンティティタイプでアノテーション。
x-entity のないフィールドで沈黙するので、部分的ロールアウトは安全です。
なぜこれが存在するか
一部の AdCP スキーマは単一のフィールド名 —brand_id、list_id、plan_id — を、異なるコンテキストで 異なる種類のエンティティ を参照する値に使います。最も引用される例: brand_id は「アドバタイザーのブランド」(get_brand_identity から)または「権利保有者 / タレントブランド」(get_rights 内)を意味しうる。同じ JSON 形状、異なるエンティティ。両方ともローカルで有効。不一致は、ストーリーボードが 1 つの種類の値を $context にキャプチャし後のステップが他を期待して消費するときのみ表面化 — issue #2627 で追跡されるとおり。
x-entity は、各アイデンティティを運ぶフィールドを値が解決する エンティティタイプ でタグ付けする非検証 JSON Schema アノテーションです。context-entity lint(scripts/lint-storyboard-context-entity.cjs)は、ストーリーボードの context_outputs キャプチャサイトと $context.<name> 消費サイトを歩き、両端で x-entity を読み、不一致をフラグします。
いつ追加するか
以下のとき、かつそのときのみフィールドにx-entity を追加:
- フィールドの値がビジネスエンティティへの id、slug、または安定した参照、かつ
- ストーリーボードがその値をステップ全体でキャプチャまたは消費する可能性がある(
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 の隣:
$ref タイプ(例: core/brand-id.json)には、共有タイプにアノテーション。すべての使用サイトがエンティティタイプを継承:
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 のレジストリが権威的リスト。
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 を導入するとき:static/schemas/source/core/x-entity-types.jsonのenum配列に新しい値を追加。- 同じファイルの
x-entity-definitionsの下に一段落の定義を追加。id が何を識別するか、それを使うスキーマ、既知の注意点(例: 名前空間スコープ)を記述。 - 上のカテゴリーテーブルの最も適切な行に新しい値を追加。
- 新しい値が既存のもの(例: plan 対 policy 対 check)に隣接する場合、テーブルの下に一文の曖昧性解消を追加。
- 値が将来のドメインスイープでパッチスクリプトによって適用される場合、正準フィールド名 → エンティティ値マッピングとともに
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 として実行:
- 各ストーリーボードステップの
context_outputs[].pathについて、ステップのresponse_schema_refを参照場所まで歩き、そこでx-entityを読む。(capture_name → entity_type)を記録。 - 値が
$context.<name>の各ストーリーボードステップのsample_requestフィールドについて、ステップのschema_ref(リクエストスキーマ)を参照フィールドまで歩き、そこでx-entityを読む。キャプチャテーブルで名前をルックアップ。 - 両端が
x-entityを持ち一致しない場合、違反をフラグ。
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
- 追跡 issue: #2660 Storyboard field-entity-context lint