From 3c69983b528e9f84b8bfe6b52689288798b1c475 Mon Sep 17 00:00:00 2001 From: Brian O'Kelley Date: Sat, 2 May 2026 17:32:21 -0400 Subject: [PATCH 1/2] spec(storyboard-schema): add optional default_agent field (closes #3894) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds an optional top-level default_agent: field to the storyboard authoring schema. The multi-agent runner resolves the logical key (sales, governance, creative, …) against the runtime agents map passed to runStoryboard({ agents: {…} }) — see adcp-client#1066 / #1355. The runner already accepts default_agent via run-options. This change lets storyboard authors encode the topology intent in YAML once instead of re-asserting it on every CI invocation. Cross-domain tools (sync_creatives, list_creative_formats, comply_test_controller) route deterministically without per-step agent: overrides. Strictly additive — single-agent runs ignore it, existing 3.0.x storyboards keep working, pre-existing run-options default_agent keeps its lower-precedence slot. Mirrors the provides_state_for precedent (#3775) for additive storyboard-schema affordances on 3.0.x. Co-Authored-By: Claude Opus 4.7 (1M context) --- .changeset/storyboard-default-agent-3.0.x.md | 25 +++++++++++++++ .../source/universal/storyboard-schema.yaml | 32 +++++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 .changeset/storyboard-default-agent-3.0.x.md diff --git a/.changeset/storyboard-default-agent-3.0.x.md b/.changeset/storyboard-default-agent-3.0.x.md new file mode 100644 index 0000000000..08d9334b94 --- /dev/null +++ b/.changeset/storyboard-default-agent-3.0.x.md @@ -0,0 +1,25 @@ +--- +"adcontextprotocol": patch +--- + +spec(storyboard-schema): add optional storyboard-level `default_agent` field + +Closes #3894. Adds an optional top-level `default_agent: ` field to the storyboard authoring schema (`static/compliance/source/universal/storyboard-schema.yaml`). + +`default_agent` is the logical name (`sales`, `governance`, `creative`, etc.) the multi-agent runner falls back to when a step has no `step.agent` override and the tool has no unique specialism claimant in the runtime agents map. Resolved against the `agents` option passed to `runStoryboard({ agents: {…} })` — see adcp-client#1066 and adcp-client#1355. + +The runner already accepts `default_agent` via run-options. This change lets storyboard authors encode the topology intent in YAML once, rather than re-asserting `--default-agent sales` on every CI invocation. Cross-domain tools (`sync_creatives`, `list_creative_formats`, `comply_test_controller`) become deterministic without per-step `agent:` overrides. + +Strictly additive and backward-compatible: +- Single-agent runs ignore the field (precedent: `requires_scenarios`, `controller_seeding`). +- Existing 3.0.x storyboards keep working unchanged. +- Pre-existing run-options `default_agent` keeps the lower-precedence fallback slot. + +Resolution order (runner contract): +1. Step-level `agent:` override. +2. Unique specialism claimant in the runtime agents map. +3. Storyboard-level `default_agent` (this field). +4. Run-options `default_agent`. +5. Fail-fast (`unrouted_step`). + +Mirrors the `provides_state_for` precedent (#3775) for adding optional storyboard-schema fields on 3.0.x — small, additive authoring affordances that adopters need today and that don't bind 3.0 wire shape. diff --git a/static/compliance/source/universal/storyboard-schema.yaml b/static/compliance/source/universal/storyboard-schema.yaml index 5b7a0aacac..15142052b0 100644 --- a/static/compliance/source/universal/storyboard-schema.yaml +++ b/static/compliance/source/universal/storyboard-schema.yaml @@ -53,6 +53,38 @@ # MUST exist in the source tree at build time. Duplicate IDs # across files are a build-time error.) # +# default_agent: string (optional — logical agent key used by multi-agent +# storyboard runners to route steps that do not have a unique specialism +# claimant in the runtime agents map. Resolved by the runner against the +# `agents` option passed to `runStoryboard({ agents: { sales: …, governance: …, … } })`. +# +# When to set it. Storyboards that exercise cross-domain tools — e.g., +# `sync_creatives`, `list_creative_formats`, `comply_test_controller` — do +# not name a single specialism the runner can match. The author has the +# most context for which logical tenant should receive these calls (usually +# "wherever the seller is": `sales`). Encoding `default_agent: sales` once +# in the YAML beats re-asserting it on every CI invocation. +# +# Resolution order (runner contract — see adcp-client#1066, #1355): +# 1. Step-level `agent:` override (if declared on the step). +# 2. Unique specialism claimant in the runtime agents map (matched via +# each agent's `get_adcp_capabilities.supported_protocols`). +# 3. Storyboard-level `default_agent` (THIS field) resolved against the +# runtime agents map. +# 4. Run-options `default_agent` passed to `runStoryboard({ default_agent })`. +# 5. Fail-fast — runner raises `unrouted_step` and grades the step failed. +# +# Single-agent runs ignore this field entirely — there is no map to resolve +# against, every step routes to the only configured agent. Authors SHOULD +# still declare it for clarity; multi-agent runners treat it as advisory and +# single-agent runners as a no-op. +# +# Validation. The `` MUST be a non-empty string. Validation that the +# key resolves to a configured tenant is a runtime concern (the runner +# raises `default_agent_unresolved` when the key is absent from the runtime +# agents map AND no run-options fallback is supplied), not a schema concern — +# the same storyboard runs against different topologies.) +# # agent: # interaction_model: enum (stateless_transform | stateful_preloaded | stateful_push | stateless_generate | media_buy_seller | marketplace_catalog | owned_signals | si_platform | brand_rights_holder | governance_agent) # capabilities: string[] (AdCP capability flags: supports_transformation, has_creative_library, supports_generation, sells_media, accepts_briefs, supports_guaranteed, supports_non_guaranteed, catalog_signals) From e0f88980c18922e917de62e246cf67d141e19e35 Mon Sep 17 00:00:00 2001 From: Brian O'Kelley Date: Sat, 2 May 2026 17:38:34 -0400 Subject: [PATCH 2/2] spec(storyboard-schema): tighten default_agent contract per expert review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Address protocol- and product-expert review on PR #3897: - Slot 2: state explicitly what zero/one/multi specialism claimants do. Multi-claim grades unrouted_step (operator-config error); slots 3/4 do NOT rescue. Zero falls through to slot 3. - Slot 3: when the storyboard declares default_agent and the key is absent from the runtime map, grade default_agent_unresolved — do NOT silently fall to slot 4. Silent fallback would invisibly override the storyboard author's encoded intent. Slot 4 fires only when the field is unset. - Slot 4: same set-but-unmatched rule applied symmetrically. - Key shape: free-form non-empty string keyed by the runtime agents map. Spec does NOT constrain to the specialism enum — production topologies legitimately fan out per-property / per-region / per-rights-holder. Cross-operator portability is the author's concern, not the spec's. - Drop comply_test_controller from the cross-domain example — it's routed via prerequisites.controller_seeding, not default_agent. - Disambiguate adcp-client#1355 reference (was bare "#1355"). No wire-protocol surface change; doc-only edit to the storyboard authoring schema (already a comment-block YAML). Co-Authored-By: Claude Opus 4.7 (1M context) --- .../source/universal/storyboard-schema.yaml | 50 +++++++++++++------ 1 file changed, 36 insertions(+), 14 deletions(-) diff --git a/static/compliance/source/universal/storyboard-schema.yaml b/static/compliance/source/universal/storyboard-schema.yaml index 15142052b0..9329b017b4 100644 --- a/static/compliance/source/universal/storyboard-schema.yaml +++ b/static/compliance/source/universal/storyboard-schema.yaml @@ -58,20 +58,43 @@ # claimant in the runtime agents map. Resolved by the runner against the # `agents` option passed to `runStoryboard({ agents: { sales: …, governance: …, … } })`. # -# When to set it. Storyboards that exercise cross-domain tools — e.g., -# `sync_creatives`, `list_creative_formats`, `comply_test_controller` — do -# not name a single specialism the runner can match. The author has the -# most context for which logical tenant should receive these calls (usually -# "wherever the seller is": `sales`). Encoding `default_agent: sales` once -# in the YAML beats re-asserting it on every CI invocation. +# Key shape: free-form non-empty string, matched verbatim against the +# runtime `agents` map's keys. The spec does NOT constrain the key to the +# specialism enum (`sales`, `signals`, `governance`, `creative`, `brand`) +# because production multi-agent topologies legitimately fan out per-property +# (`nyt_sales`, `wsj_sales`), per-region (`sales_eu`, `sales_us`), or +# per-brand-rights-holder. Authors choose the convention that matches their +# operator's CI invocation; portability across operators is the author's +# concern, not the spec's. # -# Resolution order (runner contract — see adcp-client#1066, #1355): +# When to set it. Storyboards that exercise cross-domain tools — e.g., +# `sync_creatives`, `list_creative_formats` — do not name a single specialism +# the runner can match. The author has the most context for which logical +# tenant should receive these calls (usually "wherever the seller is": +# `sales`). Encoding `default_agent: sales` once in the YAML beats +# re-asserting it on every CI invocation. (Note: `comply_test_controller` +# is routed via `prerequisites.controller_seeding`, not this field — the +# controller is a back-channel, not a specialism claimant.) +# +# Resolution order (runner contract — see adcp-client#1066, adcp-client#1355): # 1. Step-level `agent:` override (if declared on the step). -# 2. Unique specialism claimant in the runtime agents map (matched via -# each agent's `get_adcp_capabilities.supported_protocols`). +# 2. Specialism-claimant match against the runtime agents map (matched +# via each agent's `get_adcp_capabilities.supported_protocols`): +# - Exactly one agent claims the step's task's specialism → route there. +# - Zero claimants → fall through to slot 3. +# - Two or more claimants → runner MUST grade the step `unrouted_step` +# rather than picking arbitrarily. Multi-claim is an operator-config +# error; the storyboard cannot disambiguate it. Slot 3/4 do NOT +# rescue this case — silently picking one would mask the misconfig. # 3. Storyboard-level `default_agent` (THIS field) resolved against the -# runtime agents map. +# runtime agents map. When the field is set but the key is absent from +# the map, the runner MUST grade the step `default_agent_unresolved` +# and MUST NOT fall through to slot 4 — silent fallback would invisibly +# override the storyboard author's encoded intent. Slot 4 fires only +# when the storyboard does NOT declare this field. # 4. Run-options `default_agent` passed to `runStoryboard({ default_agent })`. +# Same key-resolution rule as slot 3: set-but-unmatched grades +# `default_agent_unresolved`; unset falls through to slot 5. # 5. Fail-fast — runner raises `unrouted_step` and grades the step failed. # # Single-agent runs ignore this field entirely — there is no map to resolve @@ -80,10 +103,9 @@ # single-agent runners as a no-op. # # Validation. The `` MUST be a non-empty string. Validation that the -# key resolves to a configured tenant is a runtime concern (the runner -# raises `default_agent_unresolved` when the key is absent from the runtime -# agents map AND no run-options fallback is supplied), not a schema concern — -# the same storyboard runs against different topologies.) +# key resolves to a configured tenant is a runtime concern (per the slot 3 +# rule above), not a schema concern — the same storyboard runs against +# different topologies.) # # agent: # interaction_model: enum (stateless_transform | stateful_preloaded | stateful_push | stateless_generate | media_buy_seller | marketplace_catalog | owned_signals | si_platform | brand_rights_holder | governance_agent)