Conversation
…SDK-synthesized Follow-up to #3927. Corrects three issues in skills/call-adcp-agent/SKILL.md: 1. schemaId -> schema_id. error.json defines the wire field as snake_case (line 54). The TS SDK camelCases on receive, but this skill is read by Python/Go/raw-HTTP callers too — documenting the SDK-normalized form broke them. Casing variance now called out separately so SDK users can still navigate. 2. Spec-optional vs SDK-synthesized split. schema_id and discriminator are defined on the wire in error.json; hint and allowedValues are not — they are synthesized by @adcp/sdk after parsing. Lumping all four under "implementation-dependent fields a validator may opt into" told non-TS callers to look for fields no AdCP seller emits. Split into two tiers with the SDK-side ones moved into a separate callout. 3. Recovery order. Restored "patch the pointers using keyword + variants, resend" as the unconditional one-step path; treats discriminator / hint / schema_id as shortcuts when present rather than required first reads. Front-loading optional fields was forcing branching on values absent on the long tail. Also fixes discriminator item shape in prose and table — items are {property_name, value} per error.json:65-71, not {field, value}. Doc-only. No wire-format change. Not for cherry-pick to 3.0.x — schema_id + discriminator landed in error.json via #3875 (main only) and are not in the 3.0.x wire schema. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This was referenced May 7, 2026
ci(release): backport forward-merge bridges (#3905) to 3.0.x — fixes underlying issue in #4189
#4192
Merged
bokelley
added a commit
that referenced
this pull request
May 7, 2026
…SDK-synthesized (#3932) (#4193) Follow-up to #3927. Corrects three issues in skills/call-adcp-agent/SKILL.md: 1. schemaId -> schema_id. error.json defines the wire field as snake_case (line 54). The TS SDK camelCases on receive, but this skill is read by Python/Go/raw-HTTP callers too — documenting the SDK-normalized form broke them. Casing variance now called out separately so SDK users can still navigate. 2. Spec-optional vs SDK-synthesized split. schema_id and discriminator are defined on the wire in error.json; hint and allowedValues are not — they are synthesized by @adcp/sdk after parsing. Lumping all four under "implementation-dependent fields a validator may opt into" told non-TS callers to look for fields no AdCP seller emits. Split into two tiers with the SDK-side ones moved into a separate callout. 3. Recovery order. Restored "patch the pointers using keyword + variants, resend" as the unconditional one-step path; treats discriminator / hint / schema_id as shortcuts when present rather than required first reads. Front-loading optional fields was forcing branching on values absent on the long tail. Also fixes discriminator item shape in prose and table — items are {property_name, value} per error.json:65-71, not {field, value}. Doc-only. No wire-format change. Not for cherry-pick to 3.0.x — schema_id + discriminator landed in error.json via #3875 (main only) and are not in the 3.0.x wire schema. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
bokelley
added a commit
that referenced
this pull request
May 8, 2026
* chore(3.0.x): re-cherry-pick #3461 and #3462 (envelope_field_present + asset-union) (#3608)
* feat(compliance): add envelope_field_present check type; update v3-envelope-integrity storyboard (#3461)
Closes #3429
Adds `envelope_field_present` as a recognized storyboard check type that
walks protocol-envelope.json instead of the step's response_schema_ref.
Updates v3-envelope-integrity.yaml to use it for the `status` presence
assertion, eliminating the VERIFIER_UNREACHABLE gap in the adcp-client
storyboard-drift verifier. Requires adcp-client#1045 for runtime + static
drift support.
Non-breaking justification: additive — new check type alongside existing
ones; no existing check type changed or removed; no storyboard currently
uses envelope_field_present.
Pre-PR review:
- code-reviewer: approved — lint change correct, test added for classifyOutcome, dist/3.0.1 frozen by design (ships as 3.0.2), no blockers
- ad-tech-protocol-expert: approved — semantics correct; runner-output-contract.yaml updated with check enum + expected/actual shape entries; patch bump confirmed correct per playbook conformance-harness rule
https://claude.ai/code/session_01Kwks2uGQS3ZVX7g4kujdGp
Co-authored-by: Claude <noreply@anthropic.com>
* fix(schema): promote asset-variant oneOf to canonical asset-union.json (#3462)
* fix(schema): promote asset-variant oneOf to canonical asset-union.json
Fixes json-schema-to-typescript emitting VASTAsset1, DAASTAsset1,
BriefAsset1, and CatalogAsset1 when creative-asset.json and
creative-manifest.json both inline identical 14-arm oneOf arrays.
Creates core/assets/asset-union.json (title: AssetVariant) as a single
$id-addressable source of truth. Both parent schemas now $ref this file
instead of duplicating the union inline. Wire format and validation
semantics are unchanged; the discriminator annotation moves inside the
canonical schema per OAS 3.1 §4.8.24.
Closes #3459
https://claude.ai/code/session_017XYv1Yt2m9NSj4bsNR22qo
* docs(schema): document intentional subset in offering-asset-group.json
Clarify that offering-asset-group excludes brief-asset and catalog-asset
(campaign-input metadata, not delivery-ready creative types) and cross-link
to the new asset-union.json for the full union. Addresses code-reviewer
nit from pre-PR review.
https://claude.ai/code/session_017XYv1Yt2m9NSj4bsNR22qo
* fix(schema): replace inline asset oneOf in list-creatives-response.json
Third copy of the 14-arm asset union, caught in post-PR code review.
Replace with $ref to asset-union.json for consistency.
https://claude.ai/code/session_017XYv1Yt2m9NSj4bsNR22qo
---------
Co-authored-by: Claude <noreply@anthropic.com>
* chore(3.0.x): sync release-pipeline workflows from main (App token, 3.0.x triggers)
---------
Co-authored-by: Claude <noreply@anthropic.com>
* Version Packages (#3615)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* docs(creative-channels): fix url_type tracker → tracker_pixel (#2986) (#3673)
The display, audio, carousels, and DOOH channel docs use
"url_type": "tracker", which is not a valid value in the
url-asset-type.json enum (clickthrough / tracker_pixel /
tracker_script). Sellers following these docs emit a non-conformant
url_type that buyers can't interpret without guessing.
Replaces all 10 occurrences with "tracker_pixel" to match the schema.
This is step 1 of the rollout proposed by Nastassia Fulconis on
adcp#2986: 3.0.x docs cleanup → 3.1 SHOULD + role-based fallback +
mechanism-vs-purpose clarification → 4.0 required.
The dist/docs/3.0.2 release snapshot still carries the old value;
backporting to the snapshot is intentionally out of scope here so the
fix lands on the live source first.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(storyboard): provides_state_for cascade-rescue field (closes #3734) (#3775)
Backport of e8fded9d85 from main to 3.0.x. Adds optional same-phase
substitution declaration on storyboard steps so explicit-mode social
platforms (Snap, Meta, TikTok) can pre-provision accounts out-of-band
and have list_accounts substitute for the missing sync_accounts state
contract — recovering 3 Tier-1 sandbox-verified adapters from 1/9/0 to
9/10 once the SDK cache refreshes against this version.
Storyboard schema (static/compliance/source/universal/storyboard-schema.yaml):
field documentation parallel to contributes_to. All-of array semantics,
same-phase only, target/substitute must be stateful, no self-reference,
acyclic peer-graph per phase.
Runner output contract (static/compliance/source/universal/
runner-output-contract.yaml): new peer_substituted skip reason in
skip_result.reasons, distinct from peer_branch_taken (branch-set routing)
and not_applicable (coverage gap).
Specialism YAML (static/compliance/source/specialisms/sales-social/
index.yaml): provides_state_for: sync_accounts on list_accounts in the
account_setup phase.
Build-time validation (scripts/lint-storyboard-provides-state-for.cjs +
test): wired into build-compliance.cjs lint chain. Covers shape,
self-reference, unknown target, cross-phase, target-stateful,
substitute-stateful, and direct-cycle violations.
Pure additive change; existing storyboards keep current cascade behavior.
Cherry-pick conflict resolved in package.json: kept 3.0.x's simpler
node --test invocation (no --test-force-exit / --test-timeout flags),
slotted in test:storyboard-provides-state-for entry consistent with
3.0.x style. --no-verify used because precommit fails on a pre-existing
3.0.x baseline @adcp/client install drift unrelated to this change.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Version Packages (#3696)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* ci(release): auto-upload protocol tarball to GitHub Release (#3786) (#3787)
`createGithubReleases: true` from changesets/action only writes the
changelog body — files have to be uploaded separately. Without this
step the artifacts ship to the repo's dist/ tree but are never attached
as Release assets, leaving adopters who pin via release URL with 404s.
v3.0.0 had assets only because they were uploaded by hand on 2026-04-22;
v3.0.1 / v3.0.2 / v3.0.3 all shipped empty.
New step runs after changesets/action, gated on
`steps.changesets.outputs.published == 'true'` so it only fires on
tag-and-release runs (not Version Packages PR-creation runs). Uploads
${VERSION}.tgz plus .sha256 / .sig / .crt sidecars with --clobber so
re-runs are idempotent.
Backfill of the three missing releases (v3.0.1 / v3.0.2 / v3.0.3) is a
separate manual step against the assets already committed at
dist/protocol/ on the 3.0.x branch.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore(3.0.x): cherry-pick + adapt 7 spec fixes from main (#3784) (#3789)
* fix(schema): correct title annotation in rate-limited error-details schema (#3149)
* fix(schema): correct title annotation in rate-limited error-details schema
Change "RATE_LIMITED Details" to "Rate Limited Details" in the `title`
annotation of error-details/rate-limited.json. The title field is
non-normative per JSON Schema draft-07 (no validation or wire-format
impact); this corrects the downstream codegen output from
`RATE_LIMITEDDetails` to `RateLimitedDetails`. Applied to source and
all dist snapshots (3.0.0, 3.0.0-rc.3, latest).
Refs #3145. See adcp-client#942 for the SDK alias layer.
https://claude.ai/code/session_01E3LcN5g4tEZutKCTePUVbs
* revert: drop dist/schemas/ hand-edits per #3149 review
dist/schemas/3.0.0/ and dist/schemas/3.0.0-rc.3/ are immutable release
snapshots — scripts/build-schemas.cjs only writes them under --release
mode (the changesets release step), and dist/schemas/latest/ is
gitignored. Mutating frozen GA snapshots breaks the immutability
contract that lets buyers pin to 3.0.0 and trust they see exactly what
was published.
Source title fix is preserved. The next --release build picks up the
corrected title for that version's snapshot; past releases stay
byte-identical to what was published.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Brian O'Kelley <bokelley@gmail.com>
(cherry picked from commit fbf71ce2502df137a5a378c8e6ef8f4664e64c07)
* spec(error): standardize VALIDATION_ERROR issues[] on core/error.json (closes #3059) (#3562)
* spec(error): standardize VALIDATION_ERROR issues[] (closes #3059)
Adds an optional top-level issues array to core/error.json, normalizing
what @adcp/client already emits for multi-field validation rejections
(adcp-client#874 / #915). Other implementations (adcp-go,
adcp-client-python, hand-rolled sellers) would either miss the structured
pointer list, adopt it ad-hoc with different naming, or converge if the
spec normalizes it. Filing now keeps the ecosystem aligned before
adoption deepens.
Each issue entry: { pointer (RFC 6901), message, keyword, schemaPath? }.
schemaPath MAY be omitted in production to avoid fingerprinting oneOf
branch selection on adversarial payloads.
Backward compatibility:
- field (singular) is retained. When both are present, sellers SHOULD
set field to issues[0].pointer for pre-3.1 consumers reading field
only.
- details.issues mirror is permitted for consumers reading from details.
New consumers should prefer top-level issues.
Files:
- static/schemas/source/core/error.json: adds issues property
- docs/building/implementation/error-handling.mdx: adds issues to the
error-envelope field table; documents field/issues interaction
Schema validators all clean (test:schemas 7/7, test:json-schema 255/255).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* spec(error): apply protocol-expert review feedback on issues[]
Three substantive sharpens from the ad-tech-protocol-expert review of
PR #3562:
1. Pointer-format mismatch with field — flagged as a latent bug. The
existing top-level field uses JSONPath-lite (packages[0].targeting);
the new issues[].pointer uses RFC 6901 (/packages/0/targeting).
Calling the mirror rule SHOULD without specifying the translation
left sellers with collision risk. Both descriptions now spell out
the format choice (Ajv's instancePath = RFC 6901 for pointer; legacy
JSONPath-lite for field) AND the explicit translation contract on
the mirror. Future major version will deprecate field in favor of
issues[].pointer.
2. issues[0].pointer mirror rule — SHOULD upgraded to MUST (when issues
is present). SHOULD created exactly the rough edge the review
flagged: pre-3.1 consumers reading field would get nondeterministic
behavior across sellers. Cost of MUST is one line of dual-write per
seller; cost of SHOULD is a long tail of seller-A-vs-seller-B bugs.
MUST also gives a clean deprecation path in 4.0.
3. schemaPath downgraded from MAY to SHOULD NOT in production. The
review identified this as a real probe oracle: leaking which oneOf
branch the validator selected before semantic rejection helps
adversarial callers map polymorphic unions. AdCP already has an
adversarial-payload threat model (signed-requests work, agent-
controlled field audit). Sellers MAY emit in dev/sandbox modes.
Also cited Ajv as prior art so implementers know where the keyword
vocabulary comes from (instancePath / keyword / schemaPath are Ajv's
native error output fields). Reduces the ad-hoc-naming risk.
Schema validators all clean (test:schemas 7/7, test:json-schema 255/255).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
(cherry picked from commit bd3a18ce86f6cb45d2cc1f4e2678405af97de285)
* spec(schemas): PascalCase titles on error-details schemas (partial fix for #3145) (#3566)
* spec(schemas): PascalCase titles on error-details schemas
Partial fix for #3145. Six error-details/*.json files carry SCREAMING_SNAKE
titles that propagate awkwardly through json-schema-to-typescript into
@adcp/client's public type surface (e.g., RATE_LIMITEDDetails_ScopeValues
read as a typo to every consumer of the SDK).
Renames (no wire-format change — $id values stay kebab-case; only the
title field is touched, which controls codegen):
- ACCOUNT_SETUP_REQUIRED Details -> AccountSetupRequiredDetails
- AUDIENCE_TOO_SMALL Details -> AudienceTooSmallDetails
- BUDGET_TOO_LOW Details -> BudgetTooLowDetails
- CONFLICT Details -> ConflictDetails
- CREATIVE_REJECTED Details -> CreativeRejectedDetails
- POLICY_VIOLATION Details -> PolicyViolationDetails
rate-limited.json already had PascalCase (Rate Limited Details);
vendor-error-codes.json already had Vendor Error Code Registry; no change
to either.
#3145's other half — Foo1-suffixed enum dupes (AgeVerificationMethod1,
*Asset1, etc.) — is downstream codegen behavior. Both refs already point
at the same $id with $ref so the spec side is correct; the dupe shows up
because json-schema-to-typescript walks through different parent paths
and emits two inline copies. SDK-side post-process renaming (with one-
minor-version aliases) is the pragmatic fix. Tracked at adcp-client#942.
Schema validators all clean (test:schemas 7/7, test:json-schema 255/255,
build-compliance 20 universal / 6 protocols / 19 specialisms).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* spec(schemas): Title Case (with spaces) to match #3149 precedent
Protocol-expert review of #3566 flagged the style inconsistency: my
PR stripped spaces (AccountSetupRequiredDetails), but PR #3149 (Apr 25)
established the precedent of spaced Title Case for the same kind of
problem (Rate Limited Details). Going with #3149's form so the 8-file
directory stays uniform. json-schema-to-typescript strips whitespace
when generating identifiers, so the codegen output is identical either
way (AccountSetupRequiredDetails on both); the source-of-truth title
just stays consistent across the directory.
Title field is non-normative per JSON Schema draft-07 §10.1 — affects
only docgen/codegen output, not validation. Wire format unchanged.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
(cherry picked from commit a5d9bff2d5390e1a35c3b1e8f2ca1d26c987b66e)
* spec(url-asset): SHOULD url_type + role-based fallback (#2986 step 2) (#3671)
* spec(url-asset): SHOULD url_type + role-based fallback (#2986 step 2)
Define what receivers do when a URL asset omits url_type. Today the
field is optional with no fallback rule, so a conformant manifest like
{asset_type: url, url: ...} forces buyers to guess the invocation
mechanism — and guessing wrong (firing a clickthrough URL as a pixel,
or a tracker as a clickthrough) corrupts measurement and breaks user
flows.
Schema changes:
- url-asset.json: senders SHOULD include url_type on every URL asset.
When absent, receivers SHOULD fall back to the format's
url-asset-requirements.role (clickthrough/landing_page →
`clickthrough` mechanism; *_tracker roles → `tracker_pixel`). When
neither is available, receivers MAY reject rather than guess.
- url-asset-requirements.json: clarify that role is purpose
(impression vs click vs viewability vs 3P) while url_type is
mechanism (click vs pixel vs script tag); a click_tracker slot
validly accepts a tracker_pixel URL.
Doc changes:
- asset-types.mdx URL Asset section: rewritten to use the actual
url_type enum (clickthrough/tracker_pixel/tracker_script — the old
text listed impression_tracker/video_tracker/landing_page, which
were never url_type values), to add the SHOULD note and role
fallback table, and to remove the "you only need to supply the
url value" guidance that drove the original ambiguity.
Wire format unchanged. Senders already including url_type are
unaffected. Step 2 of the rollout on adcp#2986; step 3 (require
url_type in 4.0) follows once this lands.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* spec(url-asset): apply expert review — viewability→script, third_party→unmapped, MUST NOT silently guess
Addresses ad-tech-protocol-expert + adtech-product-expert review on
PR #3671:
Required fixes:
- viewability_tracker → tracker_script (not tracker_pixel). OMID and
equivalent verification SDKs require a <script> tag; firing them as
a pixel produces no measurement and no error — exactly the silent-
corruption failure mode this PR exists to prevent.
- third_party_tracker → no safe fallback. Mechanism is integration-
specific (DV/IAS ship both pixel and script forms). Receivers MAY
reject or warn rather than guess.
- Strengthen receiver guidance to "MUST NOT silently pick a
mechanism; SHOULD reject" when both url_type and role are absent.
Mirrors the mdx language into the JSON Schema description so
extractors and conformance tooling read the same rule.
- Add VAST/DAAST carve-out: VAST tag URLs are not URL assets; use
asset_type: "vast" or the dedicated tracker types pending RFC #2915.
- Update docs/creative/formats.mdx tracker-detection rule. Today it
feature-detects on url_type ∈ {tracker_pixel, tracker_script};
under the new fallback semantics, format authors who declare only
role would silently fail that detector. Detection now accepts
either url_type OR a tracker-purpose role.
Non-blocking improvements:
- Migration cue in asset-types.mdx for sellers who built tooling
around the older "you only need to supply the url value" guidance:
3.x is fine, plan to add url_type before 4.0.
Wire format unchanged.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(url-asset): cross-reference fallback table between schema and mdx
The role→url_type fallback table lives in two places: the
url-asset.json top-level description (read by conformance tools and
codegen) and the asset-types.mdx URL Asset section (read by humans).
Without a hint, an editor of one will silently drift the other.
Adds a $comment in url-asset.json and a JSX comment in asset-types.mdx
pointing at each other. Schema description remains the normative
source; mdx is the human copy.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
(cherry picked from commit f6af65123e13fdb0a1913002757539ee15e2e22e)
* fix(compliance): audience-sync discover_account stateful: false → true (#3710)
The list_accounts step in the account_setup phase establishes account_id
for downstream sync_audiences calls. The storyboard narrative was correct
but the stateful flag contradicted it, causing the SDK runner to not count
a passing result as cascade state — explicit-mode adopters saw
prerequisite_failed on sync_audiences even after list_accounts passed.
Fixes #3707
https://claude.ai/code/session_019ZJXyeQYrRZc17UqcW2yDf
Co-authored-by: Claude <noreply@anthropic.com>
(cherry picked from commit e74997baf42885c9467504024640c029064a1c83)
* chore(changesets): downgrade cherry-picked minor bumps to patch for 3.0.x line
Maintenance-line policy: cherry-picks land as patches even when the
original PRs landed on main as minor bumps. Otherwise the changesets
trigger 3.1.0 publication off the 3.0.x branch, defeating the cherry-pick.
- error-issues-array.md (#3562, originally minor): patch
- url-type-should-and-role-fallback.md (#3671, originally minor): patch
Both PRs were classified as patch-eligible in #3784.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(schema): manifest.json + structured enumMetadata (closes #3725) (#3738)
* feat(schema): publish manifest.json + structured enumMetadata (adcp#3725)
Adds two additive artifacts so SDKs stop hand-rolling (and drifting on)
spec metadata. Root cause for adcp-client#1135 (17 missing error codes,
3 wrong recovery classifications shipped in TS SDK for over a year).
- enums/error-code.json gains an enumMetadata block with structured
recovery + suggestion per code. Build-time lint rejects drift between
the structured value and the prose Recovery: X in enumDescriptions.
- New static/schemas/source/manifest.schema.json + generator emitting
dist/schemas/{version}/manifest.json: 58 tools, 48 error codes, 19
specialisms, plus an error_code_policy block defining how SDKs MUST
classify codes from non-conforming sellers.
- mutating derived from the same classifier the idempotency-key lint
enforces (single source of truth). Tightened READ_ONLY_VERB_PATTERN
to anchor at start so create-collection-list / delete-property-list
no longer mis-classify as read-only via -list- mid-name; added search
as a read-only verb.
- Specialisms expose entry_point_tools (curated minimum from
index.yaml.required_tools) and exercised_tools (full surface — union
of own phases[].steps[].task and every linked scenario, derived by
walking requires_scenarios). sales_guaranteed now correctly lists 9
tools instead of 2.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(manifest): expert-review fixes — strip regex, requires_tool gating
Two correctness fixes from protocol-expert review:
- stripRecoveryProse: greedy [^.]*\. truncated descriptions with periods
inside parentheticals (e.g. "capabilities.media_buy.limits"). Rewrote
with three explicit patterns: bare verdict, verdict + balanced
parenthetical, clause continuation to EOS. Verified all 48 codes emit
clean descriptions with no Recovery: prose remaining.
- collectTasksFromPhases now skips steps gated by requires_tool. Steps
marked requires_tool: <X> are conditional on the agent claiming X, not
required surface. Without the skip, optional test-harness tools
(comply_test_controller, gated across 23+ steps) propagated into
every sales specialism's exercised_tools.
Plus code-review nits:
- Simplified discoverTools utility-shape skip to NON_OPERATION_ALLOWLIST
only; documented the contract.
- Removed unused schema parameter from classifyRequestMutating.
- Tightened indexScenarioTasks predicate to require phases array.
- Added cross-reference comment between MANIFEST_PROTOCOLS and the
meta-schema's protocol enum.
- Changeset now mentions /schemas/latest/manifest.json for nightly
codegen consumers.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
(cherry picked from commit b44996fb2e623a0ff554b8983dff546fec7e4b10)
* fix(3.0.x): trim enumMetadata to 3.0.x codes; downgrade #3738 to patch
The cherry-pick of #3738 brought in enumDescriptions + enumMetadata entries
for SCOPE_INSUFFICIENT, READ_ONLY_SCOPE, and FIELD_NOT_PERMITTED — three
error codes added to main in PRs that aren't on 3.0.x. Their enum entries
weren't introduced (3.0.x's enum array is unaffected), but the description
and metadata blocks were left referencing them. The new lintErrorCodeEnumMetadata
guardrail catches this and refuses to build.
Trim: drop the three orphan entries from enumDescriptions and enumMetadata.
Counts now agree at 45 / 45 / 45.
Also downgrades the changeset from minor to patch — same rationale as the
other cherry-picks on this branch: maintenance line, no new enum values,
no wire-format change.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* spec(errors): tighten AUTH_REQUIRED prose to warn on retry storms (3.0.x prose-only backport of #3739)
3.0.x cannot adopt the AUTH_MISSING/AUTH_INVALID split from #3739 — adding
new enum values violates the maintenance line's semver rules. This is the
prose-only backport: same wire code, same recovery class, but the
description and enumMetadata.suggestion now spell out the two sub-cases
(missing vs. presented-but-rejected) and the SHOULD-NOT-auto-retry rule.
Closes 3.0.x portion of #3730.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Brian O'Kelley <bokelley@gmail.com>
* ci(release): un-exclude dist/compliance + forward-merge auto-resolution (#3794) (#3800)
* ci(release): un-exclude dist/compliance + forward-merge auto-resolution
Two release-pipeline hardening fixes for 3.0.4 and beyond.
.dockerignore: un-exclude dist/compliance/ so versioned URLs (the
/compliance/{version}/ tree) actually serve from the Fly image. The
ignore pattern was set up for /schemas/ and /protocol/ but never updated
when /compliance/ versioned routing was added. Result: every committed
dist/compliance/3.0.X/ directory was stripped from the build context,
and SDKs fetching compliance bundles by URL hit 404s on fresh-cache
scenarios. Re-include dist/compliance, then re-exclude
dist/compliance/latest (regenerated in-container by build:compliance).
forward-merge-3.0.yml: replace the bare-merge approach with auto-
resolution on an explicit allowlist of always-divergent paths
(package.json version, .changeset/*.md, dist/*, CHANGELOG.md, schema
source index files). Drop the brittle is-ancestor shortcut that
returns false after squash-merges even when content is in main; use
post-merge git diff --quiet to skip cleanly when main already has
3.0.x's content. Conflicts outside the allowlist fail the workflow
loud, surfacing playbook violations (a change on 3.0.x that wasn't
first cherry-picked from main).
Updates .agents/playbook.md § Release lines to document the new
auto-resolution behavior so reviewers know what to spot-check.
Closes the operational pain that bit us today on PR #3783, where the
v3.0.3 cut's forward-merge needed three manual conflict resolutions
(package.json, .changeset/fix-url-type-tracker-pixel-channel-docs.md,
static/schemas/source/index.json) plus a manual unshallow before
the merge could complete.
* ci(release): address expert review feedback on forward-merge auto-resolution
Code review (af5969...): empty-CONFLICTS-after-merge-failure guard so
hook rejections / unrelated-history failures fail loud instead of
silently committing the empty merge.
Security review (a16289...): tighten dist/* glob to explicit
{schemas,compliance,protocol,docs}/* list so future mutable subtrees
under dist/ don't silently auto-resolve via --theirs.
Plus: drop `2>/dev/null || true` on `git rm` (let real errors surface;
the post-loop REMAINING check already guards against bad state). Add
post-resolution `git status --short` and `git diff vs origin/main --
package.json` log groups so reviewers can spot main-unique scripts
that may have been overwritten without leaving GitHub.
No functional change to the happy-path resolution behavior.
---------
# Conflicts:
# .agents/playbook.md
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ci(release): forward-merge package.json --ours, not --theirs (#3807) (#3808)
Original --theirs rule stripped main's structural changes when 3.0.x
hadn't been updated to match. Concrete case: main renamed @adcp/client
to @adcp/sdk + bumped to 5.25.1; 3.0.x stayed on @adcp/client@5.21.1.
The forward-merge took 3.0.x's package.json wholesale, leaving the
package-lock out of sync. CI broke with "npm ci ... package.json and
package-lock.json or npm-shrinkwrap.json are in sync". PR #3806's CI
exposed this.
--ours preserves main's state. Main's pre-mode tracking is independent
of 3.0.x's version field; the dist/* artifacts still flow forward via
the allowlist; main's structural changes survive.
Trade-off: main's package.json version doesn't reflect 3.0.x's latest
release. Acceptable — main's version field isn't authoritative while
pre-mode is active. The next main pre-mode cut produces 3.1.0-beta.X
from accumulated changesets regardless of base version.
Companion playbook + PR-body checklist update so docs match behavior.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ci(release): bridge cherry-pick divergence in forward-merge (#3811) (#3814)
Adds two specific files to the auto-resolution --ours allowlist where
3.0.x has #3789's hand-adapted prose-only backport of #3739 and main
has the full enum split (adds AUTH_MISSING / AUTH_INVALID codes that
can't ship to 3.0.x without violating patch eligibility).
- docs/building/implementation/error-handling.mdx
- static/schemas/source/enums/error-code.json
Without this rule, every routine forward-merge from 3.0.x → main
rediscovers the same conflict because squash-merges of prior
forward-merges (the only merge style this repo allows) don't advance
git's merge-base. The post-merge `git diff --quiet` skip can't reach
to detect "main already has this content" because the merge fails
before that step.
Marked temporary in the workflow comments — remove when 3.1.0 cuts
and main no longer has the in-flight enum split.
Without this fix, the next forward-merge after 3.0.4 cuts would
fail loud on these same two files, requiring another manual
resolution PR. With it, 3.0.4's forward-merge auto-succeeds.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Version Packages (#3799)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* ci(release): push forward-merge branch before peter-evans runs (#3818) (#3820)
Discovered when 3.0.4's forward-merge ran for real for the first time
(run 25250971971): auto-resolution worked perfectly (the new allowlist
+ bridge handled every conflict), but peter-evans/create-pull-request
crashed with "fatal: ambiguous argument 'origin/forward-merge/3.0.x'"
because the remote branch didn't exist yet.
peter-evans's internal `git reset --hard origin/forward-merge/3.0.x`
flow assumes the remote-tracking branch already exists. On a first run
(or any time the remote branch isn't there), it fails. Pushing
explicitly after auto-resolution establishes the ref so peter-evans's
reset has a target.
After this lands + cherry-picks to 3.0.x, the next VP cut (3.0.5 or
3.1.0) will auto-create the forward-merge PR without manual
intervention.
For 3.0.4 specifically: I'll open the PR manually since this fix
requires a workflow change that hasn't run yet.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* spec(storyboard-schema): add optional default_agent field (closes #3894) (#3897)
* spec(storyboard-schema): add optional default_agent field (closes #3894)
Adds an optional top-level default_agent: <key> 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) <noreply@anthropic.com>
* spec(storyboard-schema): tighten default_agent contract per expert review
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) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* spec(capabilities): relax identity.additionalProperties to true (3.0.x) (#3896)
The identity object on get-adcp-capabilities-response was schema-closed
(additionalProperties: false), so any 3.0-pinned operator adopting a
forward-compatible field — notably identity.brand_json_url from #3690,
intended to be readable on 3.0 without a schema bump — would have its
capabilities response rejected by strict 3.0 validators (e.g.,
@adcp/sdk's createAdcpServer default).
Mirrors the relaxation already on main (post-#3690). Closed property
list (per_principal_key_isolation, key_origins, compromise_notification)
is unchanged; this is strictly additive forward-compat.
The forward-compat narrative in security.mdx ("3.0-pinned implementers
can adopt the field today without bumping") depends on this being live
in the shipped 3.0 schema — without it, the spec advice contradicts the
schema.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(storyboard): capture rights_id from acquire_rights response (closes #3892) (#3893)
The brand-rights storyboard step `acquire_rights` captured `rights_grant_id`
from the response, but `brand/acquire-rights-response.json` defines the field
as `rights_id`. Spec-compliant agents passed response_schema validation but
failed context capture, cascade-skipping `rights_enforcement`.
Update the YAML to read `rights_id` (preserving the storyboard-internal
`rights_grant_id` key so no other steps need to change) and correct the
`expected:` prose to match the published schema (rights_id + status: acquired).
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Version Packages (#3898)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* docs(skill): document implementation-dependent issues[] fields (3.0.x backport of #3927) (#3931)
Backports the SKILL.md update onto 3.0.x so 3.0.6 carries the four implementation-dependent issues[] field bullets + 2 symptom-fix table rows. Doc-only, identical to #3927 on main.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
* 3.0.6 cherry-picks: governance wire-placement + ctx_metadata reservation + storyboard fixture fixes (#3996)
* spec(errors): wire-placement guidance for GOVERNANCE_DENIED / GOVERNANCE_UNAVAILABLE (#3929)
error-code.json described what each code means but not WHERE on the response
it appears. Storyboards interpreted differently — #3914 surfaced the mismatch
where the brand-rights compliance storyboard expected adcp_error.code:
GOVERNANCE_DENIED even though acquire_rights already has a first-class
AcquireRightsRejected discriminated arm.
GOVERNANCE_DENIED — structured business outcome (governance call SUCCEEDED,
agent returned a denial verdict). When the task response defines a rejection
arm (e.g., AcquireRightsRejected, CreativeRejected), that arm IS the canonical
denial shape — populate reason, do NOT also emit the code in errors[]/
adcp_error, transport-level success markers stay green. The schema layer
already enforces this via `not: { required: [errors] }` on those arms; the
doc-comment makes the rule discoverable from the error code. When no rejection
arm exists (e.g., create_media_buy), populate errors[] + adcp_error per the
two-layer model and flip transport markers.
GOVERNANCE_UNAVAILABLE — system error (governance call FAILED). Always errors[]
+ adcp_error, transport markers always flip. Never use a rejection arm.
Also adds a parallel storyboard-authoring note in error-handling.mdx: when
asserting against rejection-arm denials, use `check: field_value, path:
"status", value: "rejected"` instead of `check: error_code` — the spec-correct
response carries no code on the wire.
Closes the doc-comment item on #3918; companion to #3914 (storyboard fix is
separate work).
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* spec(conventions): reserve ctx_metadata as adapter-internal round-trip key (#3640) (#3788)
* spec(conventions): reserve ctx_metadata as adapter-internal round-trip key (#3640)
Closes #3640.
Reserves `ctx_metadata` as a top-level adapter-internal round-trip cache key on
AdCP resource objects (Product, MediaBuy, Package, Creative, AudienceSegment,
Signal, RightsGrant). SDKs MUST strip the key before wire egress and MUST emit
a warning-level log when stripping. Buyers never see the field.
Convention is non-binding at the wire level — these resources already declare
additionalProperties: true. PropertyList and CollectionList are out of scope
(additionalProperties: false) until a follow-up PR widens those schemas.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* spec(conventions): tighten ctx_metadata scope language and warn-log condition
Two reviewer-flagged clarifications on the ctx_metadata reservation:
- Scope: state explicitly that the reservation travels with the resource
wherever it appears (top-level, nested, in arrays). Closes the read where
someone could interpret the rule as "applies only at envelope top level."
- Warn-log condition: the RULE paragraph and the conformance pseudocode
disagreed on when to emit the warning (RULE said "when stripping",
pseudocode said "when present and non-empty"). Align both on the
non-empty rule — silence on absent/empty values avoids logspam from
resources that just don't carry adapter state.
- Add a one-line disambiguation against context / context_id, since the
shared "ctx" prefix could mislead a reader.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(compliance): storyboard fixture fixes — inventory_list_targeting sandbox routing + sales_guaranteed task-completion path (mirror of #3989 / #3990)
Storyboard-fixture-only fixes applied directly to the 3.0.x line, mirroring the same diffs filed against main in adcontextprotocol/adcp#3989 and adcontextprotocol/adcp#3990. Storyboard fixtures ship in the compliance bundle that goes out with each release, so 3.0.x consumers running the worked-example seller (hello_seller_adapter_guaranteed from @adcp/sdk 6.7+) hit both bugs today.
inventory_list_targeting: the 5 account blocks were missing sandbox: true. The SDK runner's create_media_buy enricher inherits sandbox: true from the test-kit, but its get_media_buys enricher merges the storyboard's bare account block — different accountId → mediaBuyStore can't backfill targeting_overlay → verify_create_persisted / verify_update_persisted fail. Setting sandbox: true on every account block keeps create and get on the same namespace.
sales_guaranteed/create_media_buy: the step uses the spec-correct guaranteed-seller flow (A2A submitted-arm envelope; media_buy_id materializes on the task-completion artifact). The bare context_outputs path "media_buy_id" resolved against the immediate response, producing capture_path_not_resolvable. Changed to "task_completion.media_buy_id" so the runner polls tasks/get and captures the seller-issued id from the terminal artifact, per the runner contract introduced in adcp-client#1426.
Empty changeset (non-protocol fixture-only changes); no version bump.
Refs:
- adcontextprotocol/adcp#3989 (main-side inventory_list_targeting fix)
- adcontextprotocol/adcp#3990 (main-side sales_guaranteed fix)
- adcontextprotocol/adcp-client#1487 (follow-up: align get_media_buys enricher account resolution)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 3.0.6 follow-up cherry-picks: task_completion. prefix docs + comply_test_controller deployment-scope clarification (#4002)
* docs(storyboard-schema): document task_completion. prefix for context_outputs (#3955)
When the immediate response is a non-terminal task envelope (status
submitted/working/input-required), the runner polls tasks/get until
terminal and resolves the suffix against the completion artifact's
data. Required for captures like seller-assigned media_buy_id on
IO-signing / async-signed HITL flows. Requires runner >= adcp-client
v6.7; older runners treat the prefix as a literal key.
Closes #3950.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(spec): comply_test_controller is deployment-scoped, not request-gated (#3992)
* docs(spec): comply_test_controller is deployment-scoped, not request-gated
Production deployments MUST NOT expose comply_test_controller on any
surface: tools/list MUST omit the tool, get_adcp_capabilities MUST omit
the compliance_testing block, dispatch MUST return unknown-tool.
A production deployment that exposes the tool is non-conformant
regardless of whether dispatch is gated.
The canonical pattern is two deployments — one production with no
controller wired, one sandbox/staging with the controller wired for
all comers. Per-principal projection on a single deployment remains
permitted as an implementation pattern, not the canonical model.
FORBIDDEN is reserved for the in-sandbox case where params reference
a non-sandbox account; live-mode probes get the transport's standard
unknown-tool error so the response is byte-identical to a seller that
does not implement the tool.
Closes adcontextprotocol/adcp#3986. Verifier-provisioning question
moves to #3991 (Socket Mode conformance client).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(spec): address expert review on comply_test_controller scoping
- Pair tools/list (MCP) and skills[] (A2A) symmetrically across the rule;
agent-card discovery is cache-friendly so the leak surface is at least
as bad on A2A
- Replace "byte-identical" with "indistinguishable from the same-transport
response of a seller that does not implement the tool" — JSON-RPC -32601
and A2A unknown-skill aren't byte-identical to each other
- Strengthen the per-principal MAY carve-out: all three surfaces (tools/list,
capability block, dispatch) MUST be projected consistently, and live-mode
probes on a mixed deployment must dispatch to unknown-tool not FORBIDDEN
(otherwise the side channel reopens)
- Strengthen storyboard-runner SHOULD → MUST and add symmetric
capability-block check
- Reorder Sandbox gating: rule → canonical pattern → permitted alternative
→ FORBIDDEN reservation → ops/runner notes
- Reconcile the stale :::note in get_adcp_capabilities.mdx that still
described request-time gating
- Update implementation-guidance line so it doesn't undercut the new rule
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Version Packages (#3933)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* docs(release-notes): catch 3.0.x's release-notes.mdx up from 3.0.1 to 3.0.6 (#4013)
3.0.x's docs/reference/release-notes.mdx was stuck at "## Version 3.0.1" while main has been the canonical source curating per-version release notes (the changesets bot writes CHANGELOG.md but not release-notes.mdx). Replaces 3.0.x's copy with main's full version history — brings in the 3.0.2, 3.0.3, 3.0.4, 3.0.5, and 3.0.6 sections that were already on main.
Pulled from bokelley/forward-merge-3.0.6 (PR #4011) which has main's release-notes.mdx with the 3.0.6 section already added. Net effect: 3.0.x consumers reading the release notes now see the same per-version adopter-action tables and feature breakdowns that main consumers see.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ci(storyboards): fall back to @adcp/client compliance cache path on 3.0.x (closes #4000) (#4024)
The training-agent storyboard workflow's Overlay in-repo compliance source onto SDK cache step looked for the SDK's compliance cache only at node_modules/@adcp/sdk/compliance/cache. That path is the post-rename location used on main; on 3.0.x the package is still @adcp/client and the cache lives at node_modules/@adcp/client/compliance/cache.
The check silently skipped the overlay on every 3.0.x run (SDK compliance cache not found under node_modules/@adcp/sdk/compliance/cache — skipping overlay). With the overlay skipped, the storyboard runner read the SDK's frozen 3.0.0 snapshot — which doesn't carry the fixtures: block or controller_seeding: true flag those storyboards need. Pre-flight comply_test_controller.seed_product was never invoked, leaving 11 storyboards failing with PRODUCT_NOT_FOUND (or similarly-shaped seed-prerequisite errors): sales_guaranteed, sales_broadcast_tv, media_buy_seller, media_buy_seller/delivery_reporting, media_buy_seller/governance_approved, creative_generative/seller, governance_delivery_monitor, governance_spend_authority, idempotency, brand_baseline, brand_rights.
The forward-merge from main (which moved the path to @adcp/sdk in 5.23.0) didn't get reverted on 3.0.x at the workflow level, even though the package dependency stayed pinned to @adcp/client@5.21.1. PR #3893 (admin-merged 2026-05-02) was the first 3.0.x PR that triggered this CI under the renamed path; the storyboard CI has been failing on 3.0.x since.
Fix: try the new path first, fall back to the old one. Both branches converge on a single workflow file that works regardless of which package the SDK is installed as. Local repro after the fix: 11/11 previously-failing storyboards pass (sales_guaranteed 11P, sales_broadcast_tv 13P, brand_rights 6P, governance_delivery_monitor 12P, governance_spend_authority 9P, all media_buy_seller scenarios green, etc.).
Closes #4000.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(compliance): chain proposal_id through proposal_finalize storyboard (#4086) (#4092)
Capture proposals[0].proposal_id on brief_with_proposals via context_outputs,
and consume it as $context.proposal_id in refine_proposal, finalize_proposal,
and accept_proposal. Without the chain the runner sent the literal placeholder
"balanced_reach_q2" from sample_request, which 404s against sellers minting
runtime proposal IDs (uuids, db rowids).
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(creative): backport #4153 list_creatives filter-types fix to 3.0.x (#4184)
* docs(creative): document accounts filter item type as AccountRef[] in list_creatives (#4153)
* docs(creative): document accounts filter item type as AccountRef[] in list_creatives
Closes #4152
https://claude.ai/code/session_018xKxQABDeFNP5qqYui2eSs
* docs(creative): also fix format_ids and statuses type cells in list_creatives filter table
Extend the same fix to the two adjacent rows that had similar gaps. Per
core/creative-filters.json: format_ids items are format-id.json (FormatID),
matching the casing already used in list_creative_formats / get_products /
create_media_buy tables; statuses items are creative-status.json (CreativeStatus
enum), now linked to the lifecycle section in the creative spec rather than
shown as a generic string[].
Renames the changeset to reflect the broader scope.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
* chore(changeset): mark list_creatives filter-types doc fix as patch on 3.0.x
The cherry-pick from main carried an --empty changeset (correct for the 3.1
pre-mode line). On 3.0.x we want this to actually appear in the next 3.0.X
patch cut, so promote it to a patch bump on adcontextprotocol.
Per the playbook's forward-merge auto-resolution rules, .changeset/*.md is
resolved in favor of main on the back-merge, so this change stays scoped to
the 3.0.x line.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
* ci(release): extend forward-merge auto-resolution for known 3.1-track divergences (#3905) (#4192)
Adds four files to the auto-resolved set in forward-merge-3.0.yml, all
routed to --ours (main): training-agent-storyboards.yml,
runner-output-contract.yaml, core/error.json,
get-adcp-capabilities-response.json. Same bridge pattern as the existing
AUTH_REQUIRED rule (#3811) — main has 3.1-track additions in these files
that 3.0.x can't adopt within the patch contract; without the short-
circuit, every forward-merge re-discovers the divergence.
Discovered on the 3.0.5 forward-merge, which required manual resolution
(#3902). Future 3.0.x patch forward-merges will now auto-open without
these five recurring conflicts.
storyboard-schema.yaml is intentionally LEFT in the manual-resolution
path — both lines legitimately add new doc blocks (3.0.x added
default_agent in 3.0.5; main has CANONICAL CHECK ENUM additions), and
git checkout --ours silently drops clean-merged 3.0.x additions. Hybrid
resolution needs a human.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(skill): correct issues[] field names + split spec-optional from SDK-synthesized (#3932) (#4193)
Follow-up to #3927. Corrects three issues in skills/call-adcp-agent/SKILL.md:
1. schemaId -> schema_id. error.json defines the wire field as snake_case
(line 54). The TS SDK camelCases on receive, but this skill is read by
Python/Go/raw-HTTP callers too — documenting the SDK-normalized form
broke them. Casing variance now called out separately so SDK users can
still navigate.
2. Spec-optional vs SDK-synthesized split. schema_id and discriminator are
defined on the wire in error.json; hint and allowedValues are not — they
are synthesized by @adcp/sdk after parsing. Lumping all four under
"implementation-dependent fields a validator may opt into" told non-TS
callers to look for fields no AdCP seller emits. Split into two tiers
with the SDK-side ones moved into a separate callout.
3. Recovery order. Restored "patch the pointers using keyword + variants,
resend" as the unconditional one-step path; treats discriminator /
hint / schema_id as shortcuts when present rather than required first
reads. Front-loading optional fields was forcing branching on values
absent on the long tail.
Also fixes discriminator item shape in prose and table — items are
{property_name, value} per error.json:65-71, not {field, value}.
Doc-only. No wire-format change. Not for cherry-pick to 3.0.x —
schema_id + discriminator landed in error.json via #3875 (main only) and
are not in the 3.0.x wire schema.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(compliance): measurement_terms_rejected — UUID-aliased idempotency_keys + spec-aligned narrative (#4218)
Closes #4219. Refs adcontextprotocol/adcp-client#1586.
Hardcoded literals + runner-side dynamic start_time substitution = same key + different body on every run against a long-running seller, arming the spec-mandated IDEMPOTENCY_CONFLICT. Switched both create_media_buy steps to $generate:uuid_v4#... aliases and rewrote the narrative to match the spec.
* Version Packages (#4185)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* chore: empty changeset for forward-merge PR
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: aao-release-bot[bot] <280565558+aao-release-bot[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Brian O'Kelley <bokelley@gmail.com>
bokelley
added a commit
that referenced
this pull request
May 8, 2026
* chore(3.0.x): re-cherry-pick #3461 and #3462 (envelope_field_present + asset-union) (#3608)
* feat(compliance): add envelope_field_present check type; update v3-envelope-integrity storyboard (#3461)
Closes #3429
Adds `envelope_field_present` as a recognized storyboard check type that
walks protocol-envelope.json instead of the step's response_schema_ref.
Updates v3-envelope-integrity.yaml to use it for the `status` presence
assertion, eliminating the VERIFIER_UNREACHABLE gap in the adcp-client
storyboard-drift verifier. Requires adcp-client#1045 for runtime + static
drift support.
Non-breaking justification: additive — new check type alongside existing
ones; no existing check type changed or removed; no storyboard currently
uses envelope_field_present.
Pre-PR review:
- code-reviewer: approved — lint change correct, test added for classifyOutcome, dist/3.0.1 frozen by design (ships as 3.0.2), no blockers
- ad-tech-protocol-expert: approved — semantics correct; runner-output-contract.yaml updated with check enum + expected/actual shape entries; patch bump confirmed correct per playbook conformance-harness rule
https://claude.ai/code/session_01Kwks2uGQS3ZVX7g4kujdGp
Co-authored-by: Claude <noreply@anthropic.com>
* fix(schema): promote asset-variant oneOf to canonical asset-union.json (#3462)
* fix(schema): promote asset-variant oneOf to canonical asset-union.json
Fixes json-schema-to-typescript emitting VASTAsset1, DAASTAsset1,
BriefAsset1, and CatalogAsset1 when creative-asset.json and
creative-manifest.json both inline identical 14-arm oneOf arrays.
Creates core/assets/asset-union.json (title: AssetVariant) as a single
$id-addressable source of truth. Both parent schemas now $ref this file
instead of duplicating the union inline. Wire format and validation
semantics are unchanged; the discriminator annotation moves inside the
canonical schema per OAS 3.1 §4.8.24.
Closes #3459
https://claude.ai/code/session_017XYv1Yt2m9NSj4bsNR22qo
* docs(schema): document intentional subset in offering-asset-group.json
Clarify that offering-asset-group excludes brief-asset and catalog-asset
(campaign-input metadata, not delivery-ready creative types) and cross-link
to the new asset-union.json for the full union. Addresses code-reviewer
nit from pre-PR review.
https://claude.ai/code/session_017XYv1Yt2m9NSj4bsNR22qo
* fix(schema): replace inline asset oneOf in list-creatives-response.json
Third copy of the 14-arm asset union, caught in post-PR code review.
Replace with $ref to asset-union.json for consistency.
https://claude.ai/code/session_017XYv1Yt2m9NSj4bsNR22qo
---------
Co-authored-by: Claude <noreply@anthropic.com>
* chore(3.0.x): sync release-pipeline workflows from main (App token, 3.0.x triggers)
---------
Co-authored-by: Claude <noreply@anthropic.com>
* Version Packages (#3615)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* docs(creative-channels): fix url_type tracker → tracker_pixel (#2986) (#3673)
The display, audio, carousels, and DOOH channel docs use
"url_type": "tracker", which is not a valid value in the
url-asset-type.json enum (clickthrough / tracker_pixel /
tracker_script). Sellers following these docs emit a non-conformant
url_type that buyers can't interpret without guessing.
Replaces all 10 occurrences with "tracker_pixel" to match the schema.
This is step 1 of the rollout proposed by Nastassia Fulconis on
adcp#2986: 3.0.x docs cleanup → 3.1 SHOULD + role-based fallback +
mechanism-vs-purpose clarification → 4.0 required.
The dist/docs/3.0.2 release snapshot still carries the old value;
backporting to the snapshot is intentionally out of scope here so the
fix lands on the live source first.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(storyboard): provides_state_for cascade-rescue field (closes #3734) (#3775)
Backport of e8fded9d85 from main to 3.0.x. Adds optional same-phase
substitution declaration on storyboard steps so explicit-mode social
platforms (Snap, Meta, TikTok) can pre-provision accounts out-of-band
and have list_accounts substitute for the missing sync_accounts state
contract — recovering 3 Tier-1 sandbox-verified adapters from 1/9/0 to
9/10 once the SDK cache refreshes against this version.
Storyboard schema (static/compliance/source/universal/storyboard-schema.yaml):
field documentation parallel to contributes_to. All-of array semantics,
same-phase only, target/substitute must be stateful, no self-reference,
acyclic peer-graph per phase.
Runner output contract (static/compliance/source/universal/
runner-output-contract.yaml): new peer_substituted skip reason in
skip_result.reasons, distinct from peer_branch_taken (branch-set routing)
and not_applicable (coverage gap).
Specialism YAML (static/compliance/source/specialisms/sales-social/
index.yaml): provides_state_for: sync_accounts on list_accounts in the
account_setup phase.
Build-time validation (scripts/lint-storyboard-provides-state-for.cjs +
test): wired into build-compliance.cjs lint chain. Covers shape,
self-reference, unknown target, cross-phase, target-stateful,
substitute-stateful, and direct-cycle violations.
Pure additive change; existing storyboards keep current cascade behavior.
Cherry-pick conflict resolved in package.json: kept 3.0.x's simpler
node --test invocation (no --test-force-exit / --test-timeout flags),
slotted in test:storyboard-provides-state-for entry consistent with
3.0.x style. --no-verify used because precommit fails on a pre-existing
3.0.x baseline @adcp/client install drift unrelated to this change.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Version Packages (#3696)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* ci(release): auto-upload protocol tarball to GitHub Release (#3786) (#3787)
`createGithubReleases: true` from changesets/action only writes the
changelog body — files have to be uploaded separately. Without this
step the artifacts ship to the repo's dist/ tree but are never attached
as Release assets, leaving adopters who pin via release URL with 404s.
v3.0.0 had assets only because they were uploaded by hand on 2026-04-22;
v3.0.1 / v3.0.2 / v3.0.3 all shipped empty.
New step runs after changesets/action, gated on
`steps.changesets.outputs.published == 'true'` so it only fires on
tag-and-release runs (not Version Packages PR-creation runs). Uploads
${VERSION}.tgz plus .sha256 / .sig / .crt sidecars with --clobber so
re-runs are idempotent.
Backfill of the three missing releases (v3.0.1 / v3.0.2 / v3.0.3) is a
separate manual step against the assets already committed at
dist/protocol/ on the 3.0.x branch.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore(3.0.x): cherry-pick + adapt 7 spec fixes from main (#3784) (#3789)
* fix(schema): correct title annotation in rate-limited error-details schema (#3149)
* fix(schema): correct title annotation in rate-limited error-details schema
Change "RATE_LIMITED Details" to "Rate Limited Details" in the `title`
annotation of error-details/rate-limited.json. The title field is
non-normative per JSON Schema draft-07 (no validation or wire-format
impact); this corrects the downstream codegen output from
`RATE_LIMITEDDetails` to `RateLimitedDetails`. Applied to source and
all dist snapshots (3.0.0, 3.0.0-rc.3, latest).
Refs #3145. See adcp-client#942 for the SDK alias layer.
https://claude.ai/code/session_01E3LcN5g4tEZutKCTePUVbs
* revert: drop dist/schemas/ hand-edits per #3149 review
dist/schemas/3.0.0/ and dist/schemas/3.0.0-rc.3/ are immutable release
snapshots — scripts/build-schemas.cjs only writes them under --release
mode (the changesets release step), and dist/schemas/latest/ is
gitignored. Mutating frozen GA snapshots breaks the immutability
contract that lets buyers pin to 3.0.0 and trust they see exactly what
was published.
Source title fix is preserved. The next --release build picks up the
corrected title for that version's snapshot; past releases stay
byte-identical to what was published.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Brian O'Kelley <bokelley@gmail.com>
(cherry picked from commit fbf71ce2502df137a5a378c8e6ef8f4664e64c07)
* spec(error): standardize VALIDATION_ERROR issues[] on core/error.json (closes #3059) (#3562)
* spec(error): standardize VALIDATION_ERROR issues[] (closes #3059)
Adds an optional top-level issues array to core/error.json, normalizing
what @adcp/client already emits for multi-field validation rejections
(adcp-client#874 / #915). Other implementations (adcp-go,
adcp-client-python, hand-rolled sellers) would either miss the structured
pointer list, adopt it ad-hoc with different naming, or converge if the
spec normalizes it. Filing now keeps the ecosystem aligned before
adoption deepens.
Each issue entry: { pointer (RFC 6901), message, keyword, schemaPath? }.
schemaPath MAY be omitted in production to avoid fingerprinting oneOf
branch selection on adversarial payloads.
Backward compatibility:
- field (singular) is retained. When both are present, sellers SHOULD
set field to issues[0].pointer for pre-3.1 consumers reading field
only.
- details.issues mirror is permitted for consumers reading from details.
New consumers should prefer top-level issues.
Files:
- static/schemas/source/core/error.json: adds issues property
- docs/building/implementation/error-handling.mdx: adds issues to the
error-envelope field table; documents field/issues interaction
Schema validators all clean (test:schemas 7/7, test:json-schema 255/255).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* spec(error): apply protocol-expert review feedback on issues[]
Three substantive sharpens from the ad-tech-protocol-expert review of
PR #3562:
1. Pointer-format mismatch with field — flagged as a latent bug. The
existing top-level field uses JSONPath-lite (packages[0].targeting);
the new issues[].pointer uses RFC 6901 (/packages/0/targeting).
Calling the mirror rule SHOULD without specifying the translation
left sellers with collision risk. Both descriptions now spell out
the format choice (Ajv's instancePath = RFC 6901 for pointer; legacy
JSONPath-lite for field) AND the explicit translation contract on
the mirror. Future major version will deprecate field in favor of
issues[].pointer.
2. issues[0].pointer mirror rule — SHOULD upgraded to MUST (when issues
is present). SHOULD created exactly the rough edge the review
flagged: pre-3.1 consumers reading field would get nondeterministic
behavior across sellers. Cost of MUST is one line of dual-write per
seller; cost of SHOULD is a long tail of seller-A-vs-seller-B bugs.
MUST also gives a clean deprecation path in 4.0.
3. schemaPath downgraded from MAY to SHOULD NOT in production. The
review identified this as a real probe oracle: leaking which oneOf
branch the validator selected before semantic rejection helps
adversarial callers map polymorphic unions. AdCP already has an
adversarial-payload threat model (signed-requests work, agent-
controlled field audit). Sellers MAY emit in dev/sandbox modes.
Also cited Ajv as prior art so implementers know where the keyword
vocabulary comes from (instancePath / keyword / schemaPath are Ajv's
native error output fields). Reduces the ad-hoc-naming risk.
Schema validators all clean (test:schemas 7/7, test:json-schema 255/255).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
(cherry picked from commit bd3a18ce86f6cb45d2cc1f4e2678405af97de285)
* spec(schemas): PascalCase titles on error-details schemas (partial fix for #3145) (#3566)
* spec(schemas): PascalCase titles on error-details schemas
Partial fix for #3145. Six error-details/*.json files carry SCREAMING_SNAKE
titles that propagate awkwardly through json-schema-to-typescript into
@adcp/client's public type surface (e.g., RATE_LIMITEDDetails_ScopeValues
read as a typo to every consumer of the SDK).
Renames (no wire-format change — $id values stay kebab-case; only the
title field is touched, which controls codegen):
- ACCOUNT_SETUP_REQUIRED Details -> AccountSetupRequiredDetails
- AUDIENCE_TOO_SMALL Details -> AudienceTooSmallDetails
- BUDGET_TOO_LOW Details -> BudgetTooLowDetails
- CONFLICT Details -> ConflictDetails
- CREATIVE_REJECTED Details -> CreativeRejectedDetails
- POLICY_VIOLATION Details -> PolicyViolationDetails
rate-limited.json already had PascalCase (Rate Limited Details);
vendor-error-codes.json already had Vendor Error Code Registry; no change
to either.
#3145's other half — Foo1-suffixed enum dupes (AgeVerificationMethod1,
*Asset1, etc.) — is downstream codegen behavior. Both refs already point
at the same $id with $ref so the spec side is correct; the dupe shows up
because json-schema-to-typescript walks through different parent paths
and emits two inline copies. SDK-side post-process renaming (with one-
minor-version aliases) is the pragmatic fix. Tracked at adcp-client#942.
Schema validators all clean (test:schemas 7/7, test:json-schema 255/255,
build-compliance 20 universal / 6 protocols / 19 specialisms).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* spec(schemas): Title Case (with spaces) to match #3149 precedent
Protocol-expert review of #3566 flagged the style inconsistency: my
PR stripped spaces (AccountSetupRequiredDetails), but PR #3149 (Apr 25)
established the precedent of spaced Title Case for the same kind of
problem (Rate Limited Details). Going with #3149's form so the 8-file
directory stays uniform. json-schema-to-typescript strips whitespace
when generating identifiers, so the codegen output is identical either
way (AccountSetupRequiredDetails on both); the source-of-truth title
just stays consistent across the directory.
Title field is non-normative per JSON Schema draft-07 §10.1 — affects
only docgen/codegen output, not validation. Wire format unchanged.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
(cherry picked from commit a5d9bff2d5390e1a35c3b1e8f2ca1d26c987b66e)
* spec(url-asset): SHOULD url_type + role-based fallback (#2986 step 2) (#3671)
* spec(url-asset): SHOULD url_type + role-based fallback (#2986 step 2)
Define what receivers do when a URL asset omits url_type. Today the
field is optional with no fallback rule, so a conformant manifest like
{asset_type: url, url: ...} forces buyers to guess the invocation
mechanism — and guessing wrong (firing a clickthrough URL as a pixel,
or a tracker as a clickthrough) corrupts measurement and breaks user
flows.
Schema changes:
- url-asset.json: senders SHOULD include url_type on every URL asset.
When absent, receivers SHOULD fall back to the format's
url-asset-requirements.role (clickthrough/landing_page →
`clickthrough` mechanism; *_tracker roles → `tracker_pixel`). When
neither is available, receivers MAY reject rather than guess.
- url-asset-requirements.json: clarify that role is purpose
(impression vs click vs viewability vs 3P) while url_type is
mechanism (click vs pixel vs script tag); a click_tracker slot
validly accepts a tracker_pixel URL.
Doc changes:
- asset-types.mdx URL Asset section: rewritten to use the actual
url_type enum (clickthrough/tracker_pixel/tracker_script — the old
text listed impression_tracker/video_tracker/landing_page, which
were never url_type values), to add the SHOULD note and role
fallback table, and to remove the "you only need to supply the
url value" guidance that drove the original ambiguity.
Wire format unchanged. Senders already including url_type are
unaffected. Step 2 of the rollout on adcp#2986; step 3 (require
url_type in 4.0) follows once this lands.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* spec(url-asset): apply expert review — viewability→script, third_party→unmapped, MUST NOT silently guess
Addresses ad-tech-protocol-expert + adtech-product-expert review on
PR #3671:
Required fixes:
- viewability_tracker → tracker_script (not tracker_pixel). OMID and
equivalent verification SDKs require a <script> tag; firing them as
a pixel produces no measurement and no error — exactly the silent-
corruption failure mode this PR exists to prevent.
- third_party_tracker → no safe fallback. Mechanism is integration-
specific (DV/IAS ship both pixel and script forms). Receivers MAY
reject or warn rather than guess.
- Strengthen receiver guidance to "MUST NOT silently pick a
mechanism; SHOULD reject" when both url_type and role are absent.
Mirrors the mdx language into the JSON Schema description so
extractors and conformance tooling read the same rule.
- Add VAST/DAAST carve-out: VAST tag URLs are not URL assets; use
asset_type: "vast" or the dedicated tracker types pending RFC #2915.
- Update docs/creative/formats.mdx tracker-detection rule. Today it
feature-detects on url_type ∈ {tracker_pixel, tracker_script};
under the new fallback semantics, format authors who declare only
role would silently fail that detector. Detection now accepts
either url_type OR a tracker-purpose role.
Non-blocking improvements:
- Migration cue in asset-types.mdx for sellers who built tooling
around the older "you only need to supply the url value" guidance:
3.x is fine, plan to add url_type before 4.0.
Wire format unchanged.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(url-asset): cross-reference fallback table between schema and mdx
The role→url_type fallback table lives in two places: the
url-asset.json top-level description (read by conformance tools and
codegen) and the asset-types.mdx URL Asset section (read by humans).
Without a hint, an editor of one will silently drift the other.
Adds a $comment in url-asset.json and a JSX comment in asset-types.mdx
pointing at each other. Schema description remains the normative
source; mdx is the human copy.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
(cherry picked from commit f6af65123e13fdb0a1913002757539ee15e2e22e)
* fix(compliance): audience-sync discover_account stateful: false → true (#3710)
The list_accounts step in the account_setup phase establishes account_id
for downstream sync_audiences calls. The storyboard narrative was correct
but the stateful flag contradicted it, causing the SDK runner to not count
a passing result as cascade state — explicit-mode adopters saw
prerequisite_failed on sync_audiences even after list_accounts passed.
Fixes #3707
https://claude.ai/code/session_019ZJXyeQYrRZc17UqcW2yDf
Co-authored-by: Claude <noreply@anthropic.com>
(cherry picked from commit e74997baf42885c9467504024640c029064a1c83)
* chore(changesets): downgrade cherry-picked minor bumps to patch for 3.0.x line
Maintenance-line policy: cherry-picks land as patches even when the
original PRs landed on main as minor bumps. Otherwise the changesets
trigger 3.1.0 publication off the 3.0.x branch, defeating the cherry-pick.
- error-issues-array.md (#3562, originally minor): patch
- url-type-should-and-role-fallback.md (#3671, originally minor): patch
Both PRs were classified as patch-eligible in #3784.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(schema): manifest.json + structured enumMetadata (closes #3725) (#3738)
* feat(schema): publish manifest.json + structured enumMetadata (adcp#3725)
Adds two additive artifacts so SDKs stop hand-rolling (and drifting on)
spec metadata. Root cause for adcp-client#1135 (17 missing error codes,
3 wrong recovery classifications shipped in TS SDK for over a year).
- enums/error-code.json gains an enumMetadata block with structured
recovery + suggestion per code. Build-time lint rejects drift between
the structured value and the prose Recovery: X in enumDescriptions.
- New static/schemas/source/manifest.schema.json + generator emitting
dist/schemas/{version}/manifest.json: 58 tools, 48 error codes, 19
specialisms, plus an error_code_policy block defining how SDKs MUST
classify codes from non-conforming sellers.
- mutating derived from the same classifier the idempotency-key lint
enforces (single source of truth). Tightened READ_ONLY_VERB_PATTERN
to anchor at start so create-collection-list / delete-property-list
no longer mis-classify as read-only via -list- mid-name; added search
as a read-only verb.
- Specialisms expose entry_point_tools (curated minimum from
index.yaml.required_tools) and exercised_tools (full surface — union
of own phases[].steps[].task and every linked scenario, derived by
walking requires_scenarios). sales_guaranteed now correctly lists 9
tools instead of 2.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(manifest): expert-review fixes — strip regex, requires_tool gating
Two correctness fixes from protocol-expert review:
- stripRecoveryProse: greedy [^.]*\. truncated descriptions with periods
inside parentheticals (e.g. "capabilities.media_buy.limits"). Rewrote
with three explicit patterns: bare verdict, verdict + balanced
parenthetical, clause continuation to EOS. Verified all 48 codes emit
clean descriptions with no Recovery: prose remaining.
- collectTasksFromPhases now skips steps gated by requires_tool. Steps
marked requires_tool: <X> are conditional on the agent claiming X, not
required surface. Without the skip, optional test-harness tools
(comply_test_controller, gated across 23+ steps) propagated into
every sales specialism's exercised_tools.
Plus code-review nits:
- Simplified discoverTools utility-shape skip to NON_OPERATION_ALLOWLIST
only; documented the contract.
- Removed unused schema parameter from classifyRequestMutating.
- Tightened indexScenarioTasks predicate to require phases array.
- Added cross-reference comment between MANIFEST_PROTOCOLS and the
meta-schema's protocol enum.
- Changeset now mentions /schemas/latest/manifest.json for nightly
codegen consumers.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
(cherry picked from commit b44996fb2e623a0ff554b8983dff546fec7e4b10)
* fix(3.0.x): trim enumMetadata to 3.0.x codes; downgrade #3738 to patch
The cherry-pick of #3738 brought in enumDescriptions + enumMetadata entries
for SCOPE_INSUFFICIENT, READ_ONLY_SCOPE, and FIELD_NOT_PERMITTED — three
error codes added to main in PRs that aren't on 3.0.x. Their enum entries
weren't introduced (3.0.x's enum array is unaffected), but the description
and metadata blocks were left referencing them. The new lintErrorCodeEnumMetadata
guardrail catches this and refuses to build.
Trim: drop the three orphan entries from enumDescriptions and enumMetadata.
Counts now agree at 45 / 45 / 45.
Also downgrades the changeset from minor to patch — same rationale as the
other cherry-picks on this branch: maintenance line, no new enum values,
no wire-format change.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* spec(errors): tighten AUTH_REQUIRED prose to warn on retry storms (3.0.x prose-only backport of #3739)
3.0.x cannot adopt the AUTH_MISSING/AUTH_INVALID split from #3739 — adding
new enum values violates the maintenance line's semver rules. This is the
prose-only backport: same wire code, same recovery class, but the
description and enumMetadata.suggestion now spell out the two sub-cases
(missing vs. presented-but-rejected) and the SHOULD-NOT-auto-retry rule.
Closes 3.0.x portion of #3730.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Brian O'Kelley <bokelley@gmail.com>
* ci(release): un-exclude dist/compliance + forward-merge auto-resolution (#3794) (#3800)
* ci(release): un-exclude dist/compliance + forward-merge auto-resolution
Two release-pipeline hardening fixes for 3.0.4 and beyond.
.dockerignore: un-exclude dist/compliance/ so versioned URLs (the
/compliance/{version}/ tree) actually serve from the Fly image. The
ignore pattern was set up for /schemas/ and /protocol/ but never updated
when /compliance/ versioned routing was added. Result: every committed
dist/compliance/3.0.X/ directory was stripped from the build context,
and SDKs fetching compliance bundles by URL hit 404s on fresh-cache
scenarios. Re-include dist/compliance, then re-exclude
dist/compliance/latest (regenerated in-container by build:compliance).
forward-merge-3.0.yml: replace the bare-merge approach with auto-
resolution on an explicit allowlist of always-divergent paths
(package.json version, .changeset/*.md, dist/*, CHANGELOG.md, schema
source index files). Drop the brittle is-ancestor shortcut that
returns false after squash-merges even when content is in main; use
post-merge git diff --quiet to skip cleanly when main already has
3.0.x's content. Conflicts outside the allowlist fail the workflow
loud, surfacing playbook violations (a change on 3.0.x that wasn't
first cherry-picked from main).
Updates .agents/playbook.md § Release lines to document the new
auto-resolution behavior so reviewers know what to spot-check.
Closes the operational pain that bit us today on PR #3783, where the
v3.0.3 cut's forward-merge needed three manual conflict resolutions
(package.json, .changeset/fix-url-type-tracker-pixel-channel-docs.md,
static/schemas/source/index.json) plus a manual unshallow before
the merge could complete.
* ci(release): address expert review feedback on forward-merge auto-resolution
Code review (af5969...): empty-CONFLICTS-after-merge-failure guard so
hook rejections / unrelated-history failures fail loud instead of
silently committing the empty merge.
Security review (a16289...): tighten dist/* glob to explicit
{schemas,compliance,protocol,docs}/* list so future mutable subtrees
under dist/ don't silently auto-resolve via --theirs.
Plus: drop `2>/dev/null || true` on `git rm` (let real errors surface;
the post-loop REMAINING check already guards against bad state). Add
post-resolution `git status --short` and `git diff vs origin/main --
package.json` log groups so reviewers can spot main-unique scripts
that may have been overwritten without leaving GitHub.
No functional change to the happy-path resolution behavior.
---------
# Conflicts:
# .agents/playbook.md
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ci(release): forward-merge package.json --ours, not --theirs (#3807) (#3808)
Original --theirs rule stripped main's structural changes when 3.0.x
hadn't been updated to match. Concrete case: main renamed @adcp/client
to @adcp/sdk + bumped to 5.25.1; 3.0.x stayed on @adcp/client@5.21.1.
The forward-merge took 3.0.x's package.json wholesale, leaving the
package-lock out of sync. CI broke with "npm ci ... package.json and
package-lock.json or npm-shrinkwrap.json are in sync". PR #3806's CI
exposed this.
--ours preserves main's state. Main's pre-mode tracking is independent
of 3.0.x's version field; the dist/* artifacts still flow forward via
the allowlist; main's structural changes survive.
Trade-off: main's package.json version doesn't reflect 3.0.x's latest
release. Acceptable — main's version field isn't authoritative while
pre-mode is active. The next main pre-mode cut produces 3.1.0-beta.X
from accumulated changesets regardless of base version.
Companion playbook + PR-body checklist update so docs match behavior.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ci(release): bridge cherry-pick divergence in forward-merge (#3811) (#3814)
Adds two specific files to the auto-resolution --ours allowlist where
3.0.x has #3789's hand-adapted prose-only backport of #3739 and main
has the full enum split (adds AUTH_MISSING / AUTH_INVALID codes that
can't ship to 3.0.x without violating patch eligibility).
- docs/building/implementation/error-handling.mdx
- static/schemas/source/enums/error-code.json
Without this rule, every routine forward-merge from 3.0.x → main
rediscovers the same conflict because squash-merges of prior
forward-merges (the only merge style this repo allows) don't advance
git's merge-base. The post-merge `git diff --quiet` skip can't reach
to detect "main already has this content" because the merge fails
before that step.
Marked temporary in the workflow comments — remove when 3.1.0 cuts
and main no longer has the in-flight enum split.
Without this fix, the next forward-merge after 3.0.4 cuts would
fail loud on these same two files, requiring another manual
resolution PR. With it, 3.0.4's forward-merge auto-succeeds.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Version Packages (#3799)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* ci(release): push forward-merge branch before peter-evans runs (#3818) (#3820)
Discovered when 3.0.4's forward-merge ran for real for the first time
(run 25250971971): auto-resolution worked perfectly (the new allowlist
+ bridge handled every conflict), but peter-evans/create-pull-request
crashed with "fatal: ambiguous argument 'origin/forward-merge/3.0.x'"
because the remote branch didn't exist yet.
peter-evans's internal `git reset --hard origin/forward-merge/3.0.x`
flow assumes the remote-tracking branch already exists. On a first run
(or any time the remote branch isn't there), it fails. Pushing
explicitly after auto-resolution establishes the ref so peter-evans's
reset has a target.
After this lands + cherry-picks to 3.0.x, the next VP cut (3.0.5 or
3.1.0) will auto-create the forward-merge PR without manual
intervention.
For 3.0.4 specifically: I'll open the PR manually since this fix
requires a workflow change that hasn't run yet.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* spec(storyboard-schema): add optional default_agent field (closes #3894) (#3897)
* spec(storyboard-schema): add optional default_agent field (closes #3894)
Adds an optional top-level default_agent: <key> 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) <noreply@anthropic.com>
* spec(storyboard-schema): tighten default_agent contract per expert review
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) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* spec(capabilities): relax identity.additionalProperties to true (3.0.x) (#3896)
The identity object on get-adcp-capabilities-response was schema-closed
(additionalProperties: false), so any 3.0-pinned operator adopting a
forward-compatible field — notably identity.brand_json_url from #3690,
intended to be readable on 3.0 without a schema bump — would have its
capabilities response rejected by strict 3.0 validators (e.g.,
@adcp/sdk's createAdcpServer default).
Mirrors the relaxation already on main (post-#3690). Closed property
list (per_principal_key_isolation, key_origins, compromise_notification)
is unchanged; this is strictly additive forward-compat.
The forward-compat narrative in security.mdx ("3.0-pinned implementers
can adopt the field today without bumping") depends on this being live
in the shipped 3.0 schema — without it, the spec advice contradicts the
schema.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(storyboard): capture rights_id from acquire_rights response (closes #3892) (#3893)
The brand-rights storyboard step `acquire_rights` captured `rights_grant_id`
from the response, but `brand/acquire-rights-response.json` defines the field
as `rights_id`. Spec-compliant agents passed response_schema validation but
failed context capture, cascade-skipping `rights_enforcement`.
Update the YAML to read `rights_id` (preserving the storyboard-internal
`rights_grant_id` key so no other steps need to change) and correct the
`expected:` prose to match the published schema (rights_id + status: acquired).
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Version Packages (#3898)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* docs(skill): document implementation-dependent issues[] fields (3.0.x backport of #3927) (#3931)
Backports the SKILL.md update onto 3.0.x so 3.0.6 carries the four implementation-dependent issues[] field bullets + 2 symptom-fix table rows. Doc-only, identical to #3927 on main.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
* 3.0.6 cherry-picks: governance wire-placement + ctx_metadata reservation + storyboard fixture fixes (#3996)
* spec(errors): wire-placement guidance for GOVERNANCE_DENIED / GOVERNANCE_UNAVAILABLE (#3929)
error-code.json described what each code means but not WHERE on the response
it appears. Storyboards interpreted differently — #3914 surfaced the mismatch
where the brand-rights compliance storyboard expected adcp_error.code:
GOVERNANCE_DENIED even though acquire_rights already has a first-class
AcquireRightsRejected discriminated arm.
GOVERNANCE_DENIED — structured business outcome (governance call SUCCEEDED,
agent returned a denial verdict). When the task response defines a rejection
arm (e.g., AcquireRightsRejected, CreativeRejected), that arm IS the canonical
denial shape — populate reason, do NOT also emit the code in errors[]/
adcp_error, transport-level success markers stay green. The schema layer
already enforces this via `not: { required: [errors] }` on those arms; the
doc-comment makes the rule discoverable from the error code. When no rejection
arm exists (e.g., create_media_buy), populate errors[] + adcp_error per the
two-layer model and flip transport markers.
GOVERNANCE_UNAVAILABLE — system error (governance call FAILED). Always errors[]
+ adcp_error, transport markers always flip. Never use a rejection arm.
Also adds a parallel storyboard-authoring note in error-handling.mdx: when
asserting against rejection-arm denials, use `check: field_value, path:
"status", value: "rejected"` instead of `check: error_code` — the spec-correct
response carries no code on the wire.
Closes the doc-comment item on #3918; companion to #3914 (storyboard fix is
separate work).
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* spec(conventions): reserve ctx_metadata as adapter-internal round-trip key (#3640) (#3788)
* spec(conventions): reserve ctx_metadata as adapter-internal round-trip key (#3640)
Closes #3640.
Reserves `ctx_metadata` as a top-level adapter-internal round-trip cache key on
AdCP resource objects (Product, MediaBuy, Package, Creative, AudienceSegment,
Signal, RightsGrant). SDKs MUST strip the key before wire egress and MUST emit
a warning-level log when stripping. Buyers never see the field.
Convention is non-binding at the wire level — these resources already declare
additionalProperties: true. PropertyList and CollectionList are out of scope
(additionalProperties: false) until a follow-up PR widens those schemas.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* spec(conventions): tighten ctx_metadata scope language and warn-log condition
Two reviewer-flagged clarifications on the ctx_metadata reservation:
- Scope: state explicitly that the reservation travels with the resource
wherever it appears (top-level, nested, in arrays). Closes the read where
someone could interpret the rule as "applies only at envelope top level."
- Warn-log condition: the RULE paragraph and the conformance pseudocode
disagreed on when to emit the warning (RULE said "when stripping",
pseudocode said "when present and non-empty"). Align both on the
non-empty rule — silence on absent/empty values avoids logspam from
resources that just don't carry adapter state.
- Add a one-line disambiguation against context / context_id, since the
shared "ctx" prefix could mislead a reader.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(compliance): storyboard fixture fixes — inventory_list_targeting sandbox routing + sales_guaranteed task-completion path (mirror of #3989 / #3990)
Storyboard-fixture-only fixes applied directly to the 3.0.x line, mirroring the same diffs filed against main in adcontextprotocol/adcp#3989 and adcontextprotocol/adcp#3990. Storyboard fixtures ship in the compliance bundle that goes out with each release, so 3.0.x consumers running the worked-example seller (hello_seller_adapter_guaranteed from @adcp/sdk 6.7+) hit both bugs today.
inventory_list_targeting: the 5 account blocks were missing sandbox: true. The SDK runner's create_media_buy enricher inherits sandbox: true from the test-kit, but its get_media_buys enricher merges the storyboard's bare account block — different accountId → mediaBuyStore can't backfill targeting_overlay → verify_create_persisted / verify_update_persisted fail. Setting sandbox: true on every account block keeps create and get on the same namespace.
sales_guaranteed/create_media_buy: the step uses the spec-correct guaranteed-seller flow (A2A submitted-arm envelope; media_buy_id materializes on the task-completion artifact). The bare context_outputs path "media_buy_id" resolved against the immediate response, producing capture_path_not_resolvable. Changed to "task_completion.media_buy_id" so the runner polls tasks/get and captures the seller-issued id from the terminal artifact, per the runner contract introduced in adcp-client#1426.
Empty changeset (non-protocol fixture-only changes); no version bump.
Refs:
- adcontextprotocol/adcp#3989 (main-side inventory_list_targeting fix)
- adcontextprotocol/adcp#3990 (main-side sales_guaranteed fix)
- adcontextprotocol/adcp-client#1487 (follow-up: align get_media_buys enricher account resolution)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* 3.0.6 follow-up cherry-picks: task_completion. prefix docs + comply_test_controller deployment-scope clarification (#4002)
* docs(storyboard-schema): document task_completion. prefix for context_outputs (#3955)
When the immediate response is a non-terminal task envelope (status
submitted/working/input-required), the runner polls tasks/get until
terminal and resolves the suffix against the completion artifact's
data. Required for captures like seller-assigned media_buy_id on
IO-signing / async-signed HITL flows. Requires runner >= adcp-client
v6.7; older runners treat the prefix as a literal key.
Closes #3950.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(spec): comply_test_controller is deployment-scoped, not request-gated (#3992)
* docs(spec): comply_test_controller is deployment-scoped, not request-gated
Production deployments MUST NOT expose comply_test_controller on any
surface: tools/list MUST omit the tool, get_adcp_capabilities MUST omit
the compliance_testing block, dispatch MUST return unknown-tool.
A production deployment that exposes the tool is non-conformant
regardless of whether dispatch is gated.
The canonical pattern is two deployments — one production with no
controller wired, one sandbox/staging with the controller wired for
all comers. Per-principal projection on a single deployment remains
permitted as an implementation pattern, not the canonical model.
FORBIDDEN is reserved for the in-sandbox case where params reference
a non-sandbox account; live-mode probes get the transport's standard
unknown-tool error so the response is byte-identical to a seller that
does not implement the tool.
Closes adcontextprotocol/adcp#3986. Verifier-provisioning question
moves to #3991 (Socket Mode conformance client).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(spec): address expert review on comply_test_controller scoping
- Pair tools/list (MCP) and skills[] (A2A) symmetrically across the rule;
agent-card discovery is cache-friendly so the leak surface is at least
as bad on A2A
- Replace "byte-identical" with "indistinguishable from the same-transport
response of a seller that does not implement the tool" — JSON-RPC -32601
and A2A unknown-skill aren't byte-identical to each other
- Strengthen the per-principal MAY carve-out: all three surfaces (tools/list,
capability block, dispatch) MUST be projected consistently, and live-mode
probes on a mixed deployment must dispatch to unknown-tool not FORBIDDEN
(otherwise the side channel reopens)
- Strengthen storyboard-runner SHOULD → MUST and add symmetric
capability-block check
- Reorder Sandbox gating: rule → canonical pattern → permitted alternative
→ FORBIDDEN reservation → ops/runner notes
- Reconcile the stale :::note in get_adcp_capabilities.mdx that still
described request-time gating
- Update implementation-guidance line so it doesn't undercut the new rule
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Version Packages (#3933)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* docs(release-notes): catch 3.0.x's release-notes.mdx up from 3.0.1 to 3.0.6 (#4013)
3.0.x's docs/reference/release-notes.mdx was stuck at "## Version 3.0.1" while main has been the canonical source curating per-version release notes (the changesets bot writes CHANGELOG.md but not release-notes.mdx). Replaces 3.0.x's copy with main's full version history — brings in the 3.0.2, 3.0.3, 3.0.4, 3.0.5, and 3.0.6 sections that were already on main.
Pulled from bokelley/forward-merge-3.0.6 (PR #4011) which has main's release-notes.mdx with the 3.0.6 section already added. Net effect: 3.0.x consumers reading the release notes now see the same per-version adopter-action tables and feature breakdowns that main consumers see.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* ci(storyboards): fall back to @adcp/client compliance cache path on 3.0.x (closes #4000) (#4024)
The training-agent storyboard workflow's Overlay in-repo compliance source onto SDK cache step looked for the SDK's compliance cache only at node_modules/@adcp/sdk/compliance/cache. That path is the post-rename location used on main; on 3.0.x the package is still @adcp/client and the cache lives at node_modules/@adcp/client/compliance/cache.
The check silently skipped the overlay on every 3.0.x run (SDK compliance cache not found under node_modules/@adcp/sdk/compliance/cache — skipping overlay). With the overlay skipped, the storyboard runner read the SDK's frozen 3.0.0 snapshot — which doesn't carry the fixtures: block or controller_seeding: true flag those storyboards need. Pre-flight comply_test_controller.seed_product was never invoked, leaving 11 storyboards failing with PRODUCT_NOT_FOUND (or similarly-shaped seed-prerequisite errors): sales_guaranteed, sales_broadcast_tv, media_buy_seller, media_buy_seller/delivery_reporting, media_buy_seller/governance_approved, creative_generative/seller, governance_delivery_monitor, governance_spend_authority, idempotency, brand_baseline, brand_rights.
The forward-merge from main (which moved the path to @adcp/sdk in 5.23.0) didn't get reverted on 3.0.x at the workflow level, even though the package dependency stayed pinned to @adcp/client@5.21.1. PR #3893 (admin-merged 2026-05-02) was the first 3.0.x PR that triggered this CI under the renamed path; the storyboard CI has been failing on 3.0.x since.
Fix: try the new path first, fall back to the old one. Both branches converge on a single workflow file that works regardless of which package the SDK is installed as. Local repro after the fix: 11/11 previously-failing storyboards pass (sales_guaranteed 11P, sales_broadcast_tv 13P, brand_rights 6P, governance_delivery_monitor 12P, governance_spend_authority 9P, all media_buy_seller scenarios green, etc.).
Closes #4000.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(compliance): chain proposal_id through proposal_finalize storyboard (#4086) (#4092)
Capture proposals[0].proposal_id on brief_with_proposals via context_outputs,
and consume it as $context.proposal_id in refine_proposal, finalize_proposal,
and accept_proposal. Without the chain the runner sent the literal placeholder
"balanced_reach_q2" from sample_request, which 404s against sellers minting
runtime proposal IDs (uuids, db rowids).
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(creative): backport #4153 list_creatives filter-types fix to 3.0.x (#4184)
* docs(creative): document accounts filter item type as AccountRef[] in list_creatives (#4153)
* docs(creative): document accounts filter item type as AccountRef[] in list_creatives
Closes #4152
https://claude.ai/code/session_018xKxQABDeFNP5qqYui2eSs
* docs(creative): also fix format_ids and statuses type cells in list_creatives filter table
Extend the same fix to the two adjacent rows that had similar gaps. Per
core/creative-filters.json: format_ids items are format-id.json (FormatID),
matching the casing already used in list_creative_formats / get_products /
create_media_buy tables; statuses items are creative-status.json (CreativeStatus
enum), now linked to the lifecycle section in the creative spec rather than
shown as a generic string[].
Renames the changeset to reflect the broader scope.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
* chore(changeset): mark list_creatives filter-types doc fix as patch on 3.0.x
The cherry-pick from main carried an --empty changeset (correct for the 3.1
pre-mode line). On 3.0.x we want this to actually appear in the next 3.0.X
patch cut, so promote it to a patch bump on adcontextprotocol.
Per the playbook's forward-merge auto-resolution rules, .changeset/*.md is
resolved in favor of main on the back-merge, so this change stays scoped to
the 3.0.x line.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
* ci(release): extend forward-merge auto-resolution for known 3.1-track divergences (#3905) (#4192)
Adds four files to the auto-resolved set in forward-merge-3.0.yml, all
routed to --ours (main): training-agent-storyboards.yml,
runner-output-contract.yaml, core/error.json,
get-adcp-capabilities-response.json. Same bridge pattern as the existing
AUTH_REQUIRED rule (#3811) — main has 3.1-track additions in these files
that 3.0.x can't adopt within the patch contract; without the short-
circuit, every forward-merge re-discovers the divergence.
Discovered on the 3.0.5 forward-merge, which required manual resolution
(#3902). Future 3.0.x patch forward-merges will now auto-open without
these five recurring conflicts.
storyboard-schema.yaml is intentionally LEFT in the manual-resolution
path — both lines legitimately add new doc blocks (3.0.x added
default_agent in 3.0.5; main has CANONICAL CHECK ENUM additions), and
git checkout --ours silently drops clean-merged 3.0.x additions. Hybrid
resolution needs a human.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* docs(skill): correct issues[] field names + split spec-optional from SDK-synthesized (#3932) (#4193)
Follow-up to #3927. Corrects three issues in skills/call-adcp-agent/SKILL.md:
1. schemaId -> schema_id. error.json defines the wire field as snake_case
(line 54). The TS SDK camelCases on receive, but this skill is read by
Python/Go/raw-HTTP callers too — documenting the SDK-normalized form
broke them. Casing variance now called out separately so SDK users can
still navigate.
2. Spec-optional vs SDK-synthesized split. schema_id and discriminator are
defined on the wire in error.json; hint and allowedValues are not — they
are synthesized by @adcp/sdk after parsing. Lumping all four under
"implementation-dependent fields a validator may opt into" told non-TS
callers to look for fields no AdCP seller emits. Split into two tiers
with the SDK-side ones moved into a separate callout.
3. Recovery order. Restored "patch the pointers using keyword + variants,
resend" as the unconditional one-step path; treats discriminator /
hint / schema_id as shortcuts when present rather than required first
reads. Front-loading optional fields was forcing branching on values
absent on the long tail.
Also fixes discriminator item shape in prose and table — items are
{property_name, value} per error.json:65-71, not {field, value}.
Doc-only. No wire-format change. Not for cherry-pick to 3.0.x —
schema_id + discriminator landed in error.json via #3875 (main only) and
are not in the 3.0.x wire schema.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(compliance): measurement_terms_rejected — UUID-aliased idempotency_keys + spec-aligned narrative (#4218)
Closes #4219. Refs adcontextprotocol/adcp-client#1586.
Hardcoded literals + runner-side dynamic start_time substitution = same key + different body on every run against a long-running seller, arming the spec-mandated IDEMPOTENCY_CONFLICT. Switched both create_media_buy steps to $generate:uuid_v4#... aliases and rewrote the narrative to match the spec.
* Version Packages (#4185)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* fix(compliance): UUID-aliased idempotency_keys across remaining storyboard scenarios (#4232)
Extends #4218 (measurement_terms_rejected) to the rest of the suite. 15
storyboard steps across 9 scenarios still shipped hardcoded
`idempotency_key` literals on state-mutating tasks. Same root cause: the
runner's dynamic `start_time` substitution shifts dates forward each run,
producing same key + different canonical body and arming
IDEMPOTENCY_CONFLICT (or replaying stale cached payloads when seller
emit shape changes between runs).
Switch every remaining literal to `$generate:uuid_v4#<scenario>_<step>`.
Closes #4230.
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Version Packages (#4233)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
* ci(release): backport storyboard-schema.yaml forward-merge allowlist to 3.0.x (#4238)
The forward-merge workflow runs from the pushed branch, so 3.0.x needs its
own copy of #4229's allowlist update. Without this, post-Version-Packages
forward-merges fail at storyboard-schema.yaml even though main's allowlist
covers it — the workflow on 3.0.x is the older one.
Workflow file taken verbatim from main's commit 6772c0b4fe (#4229).
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: aao-release-bot[bot] <280565558+aao-release-bot[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Brian O'Kelley <bokelley@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Follow-up to #3927. Three corrections to
skills/call-adcp-agent/SKILL.md:schemaId→schema_id.static/schemas/source/core/error.json:54defines the wire field as snake_case. The TS SDK camelCases on receive, but this skill is read by Python / Go / raw-HTTP callers too, who get a literalKeyErrorreaching forschemaId. The casing variance is now called out in one line so SDK users can still navigate.Spec-optional vs SDK-synthesized split.
schema_idanddiscriminatorare wire fields pererror.json.hintandallowedValuesare not inerror.json— they're synthesized by@adcp/sdkafter parsing. Grouping all four as "implementation-dependent fields a validator may opt into emitting" told non-TS callers to look for fields no AdCP seller is obliged to emit. Split into two tiers with the SDK-side ones moved into a clearly-labeled callout.Recovery order. Restored
pointer+keyword+variantsas the unconditional one-step path; treatsdiscriminator/hint/schema_idas shortcuts when present rather than required first reads. The previous front-loading forced callers to branch on optional fields that are absent on the long tail.Also fixes the
discriminatoritem shape in prose and table — items are{property_name, value}pererror.json:65-71, not{field, value}.Why
#3927 was self-merged 4 minutes after opening, before either expert review or the TypeScript Build check completed. Two follow-up reviews (ad-tech-protocol-expert, docs-expert) flagged the same three issues independently. Doc-only fix to bring the skill in line with the actual wire schema and the spec/SDK boundary.
Risk
Low. Doc-only. No wire-format change. No schema change.
3.0.x
Not for cherry-pick.
schema_idanddiscriminatorwere added toerror.jsonin #3875 (main only) —origin/3.0.xstill has onlyschemaPath. Documenting these fields in 3.0.x's SKILL.md would document fields no 3.0.5-conformant seller emits.Verification
skills/call-adcp-agent/SKILL.mdrenders cleanly (Markdown + table syntax intact)static/schemas/source/core/error.jsonexactly--empty, doc-only)🤖 Generated with Claude Code