Skip to content

fix(compliance): release 3.0.16 storyboard fixes#5533

Merged
bokelley merged 4 commits into
3.0.xfrom
changeset-release/3.0.16-storyboard-fixes
Jun 14, 2026
Merged

fix(compliance): release 3.0.16 storyboard fixes#5533
bokelley merged 4 commits into
3.0.xfrom
changeset-release/3.0.16-storyboard-fixes

Conversation

@bokelley

Copy link
Copy Markdown
Contributor

Summary

Release 3.0.16 on the 3.0.x line with compliance storyboard fixes for the runner failures reported against the stable bundle.

This backports the fixed storyboard source for:

  • universal idempotency webhook placeholder behavior
  • universal schema-validation past-start rejection
  • universal security initialized-session/static credential probes
  • universal webhook-emission runner webhook URL requirements
  • media-buy state-machine setup before pause/resume/cancel transitions

3.0.15 already exists as released artifacts and a remote tag, so this ships the next patch release instead of mutating 3.0.15.

This replacement PR uses the changeset-release/ branch prefix so the changeset gate correctly treats it as a Version Packages-style release PR with consumed changesets.

Validation

  • npm run build:compliance -- --release
  • npm run build:protocol-tarball -- --release
  • npm run sign:protocol-tarball (skipped locally; SIGN_PROTOCOL_TARBALL=true not set)
  • npm run test:schemas
  • npm run test:storyboard-sample-request-schema
  • npm run test:storyboard-response-schema
  • npm run test:storyboard-context-entity
  • npm run test:storyboard-contradictions
  • npm run test:storyboard-test-kits
  • git diff --check && git diff --cached --check
  • pre-commit hook: npm run test:unit && npm run test:test-dynamic-imports && npm run typecheck

Notes

No existing dist/compliance/3.0.15, dist/schemas/3.0.15, or dist/protocol/3.0.15.* files are modified. The release adds new 3.0.16 artifacts.

@aao-release-bot aao-release-bot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Request changes on one phase. The past-start rewrite in schema-validation.yaml ships a breaking conformance MUST as a 3.0.x patch — and conformance.mdx is explicit that the storyboards are the spec, so this is a normative tightening, not a test fix. The other four storyboard fixes are the right shape and can ship as-is.

Things I checked

  • index.json delta is correct for a path-versioned patch. Only adcp_version 3.0.15 → 3.0.16 (static/schemas/source/index.json:7); the file's own versioning.note confirms path-based versioning with no per-schema version field. No wire-shape change in any source schema. ad-tech-protocol-expert: sound.
  • Webhook HMAC-loophole closure aligns the suite with an existing baseline. Removing skip_if: agent.webhook_auth_mode == 'hmac_legacy' from signature_validity and adding the signing_keys_published precheck (webhook-emission.yaml, diff L1704-1766) matches security.mdx:66 — 9421 signing is baseline-required in 3.0, and the HMAC fallback is a buyer-side opt-in, never a seller exemption. Legacy-HMAC-no-keys sellers were already non-conformant; this closes a suite gap, not a new bar. ad-tech-protocol-expert: sound.
  • New idempotency/webhook phases are additive and gated. concurrent_retry and rate_limit_replay_invariant sit behind requires_contract: parallel_dispatch_runner / rate_limit_trip_runner and grade not_applicable on runners without the contract; operation_id_echo and the sync-completion branch set are contract-gated or any_of. None newly-fail an existing agent on an existing runner.
  • Storyboard wiring is clean (code-reviewer): removed idempotency_key_a capture has zero downstream refs; all five expect_webhook trigger/filter operation_id literals match their paired triggers (op_webhook_presence_explicit, op_storyboard_echo_123, op_webhook_idempotency_replay, op_webhook_retry_explicit, op_webhook_signed_explicit); fixtures align with every test-product/test-pricing reference; no_duplicate_webhooks_on_replay is fully excised from idempotency.yaml and re-homed in webhook-emission.yaml with no dangling reference.
  • state-machine.yaml is the right shape. sync_creatives before create_media_buy + creative_assignments + status == active assertion + requires_capability: media_buy.creative_approval_mode == auto_approve is exactly what it takes to reach a pausable buy. Clean.

MUST FIX (blocking)

1. Past-start tightening is a breaking conformance change shipped on a patch — and it contradicts the wire spec. schema-validation.yaml deletes the any_of(past_start_reject_path | past_start_adjust_path) branch_set and the past_start_enforcement assertion, replacing them with a single mandatory past_start_rejection phase requiring INVALID_REQUEST. The deleted narrative stated the reason for the either-or directly: the spec "is silent on past start_time, which is why either-or is the current conformant stance." Confirmed at create_media_buy.mdx:981 — Flight date validation is SHOULD and covers only in-range dates and start_time < end_time; nothing about start_time vs. now.

What breaks for adopters: a seller that accepts-and-adjusts a past start_time is wire-conformant under that SHOULD, passed the 3.0.15 suite via the now-deleted adjust branch, and hard-fails the new 3.0.16 past_start_rejection MUST. conformance.mdx makes this normative, not cosmetic — "the storyboards ARE the conformance specification" — so a 3.0.x patch newly-fails a previously-conformant agent while the prose spec still says SHOULD. Breaking conformance change without a minor/major, plus a suite-vs-spec contradiction.

Pick one to unblock:

  • (a) Restore the any_of(reject | adjust) branch_set on the 3.0.x line and defer the tightening to a 3.1 minor with an RFC; or
  • (b) Re-rev this as a minor and update create_media_buy.mdx §Flight date validation to commit past-start to a hard INVALID_REQUEST MUST, so suite and spec agree.

The other four fixes are unaffected by either path.

Follow-ups (non-blocking — file as issues)

  • Orphan capture. idempotency.yaml's new idempotency_supported context_output (~L297, path adcp.idempotency.supported) is never consumed — the supported: false skip is handled by the permanent_advisory checks at the capability phase, not this capture. Drop it, or wire it into the #3919 precondition gate when that lands. (code-reviewer.)
  • CHANGELOG traceability. The conformance-affecting changes are rolled under one hashless "Patch Changes" bullet. Once the versioning in MUST-FIX #1 is settled, the bullet should name the past-start and HMAC-loophole changes explicitly so the conformance delta is auditable.
  • dist regeneration. Couldn't byte-diff dist/schemas/3.0.16/** against source from the PR diff. PR says build:compliance --release was run — confirm in CI that the tree is build-regenerated, not hand-edited.
  • Tarball signing. sign:protocol-tarball was skipped locally (SIGN_PROTOCOL_TARBALL unset). dist/protocol/3.0.16.tgz.sha256 is committed; CI must produce the signature.

Minor nits (non-blocking)

  1. Duplicate correlation_id. webhook-emission.yaml's synchronous_completion_success_path and _rejection_path reuse the same operation_id: op_sync_get_products_no_webhook and context.correlation_id: webhook_emission--sync_get_products_no_webhook across two distinct step ids. Not a wiring bug — both are any_of branches, neither is an expect_webhook, so no runtime collision — but rename the reject branch's correlation_id for log traceability.

Restore the branch_set on 3.0.x, or re-rev to minor and commit the spec to the MUST. Either flips me to approve — the rest is clean.

@aao-release-bot aao-release-bot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two new webhook-emission phases ship broken — both false-fail conformant agents against the bundle's own schemas. Block is on the release artifact, not the release mechanics, which are clean. The fixes are small.

MUST FIX (blocking)

  1. generate: uuid_v4 under context_outputs is an unsupported form — webhook_replay_key never populates. In static/compliance/source/universal/webhook-emission.yaml, capability_discovery adds:

    context_outputs:
      - name: webhook_replay_key
        generate: uuid_v4

    The ContextOutput contract in static/compliance/source/universal/storyboard-schema.yaml:391-419 is strictly {name, path} (plus the task_completion.<inner> path prefix). There is no generate: key. Every one of the ~40 $generate:uuid_v4 usages in static/compliance/source/** is an inline string placeholder on a request field — zero precedent for it under context_outputs. With no path, the capture never resolves, so the two consumers in idempotency_replay_side_effects (idempotency_key: \"\$context.webhook_replay_key\" on trigger_idempotent_webhook_initial and trigger_idempotent_webhook_replay) resolve to unresolved_substitution and grade the phase failed for every agent. What breaks for adopters: the new idempotency-replay-no-duplicate-webhook phase false-fails 100% of runs. Fix: use a shared inline $generate:uuid_v4#webhook_replay_key across both steps and add universal/webhook-emission.yaml to the alias-reuse exemption in scripts/build-compliance.cjs (the same exemption universal/idempotency.yaml already has), or extend ContextOutput + the runner to support generate:. Do not ship the silently-ignored key.

  2. status == completed on a get-products-response that has no top-level status. In webhook-emission.yaml, synchronous_completion_success_pathsync_get_products_with_webhook_config_success:

    - check: field_value
      path: \"status\"
      value: \"completed\"

    static/schemas/source/media-buy/get-products-response.json defines top-level products, proposals, errors, property_list_applied, catalog_applied, refinement_applied — no top-level status (the status fields are nested inside refinement_applied[].oneOf[] scope objects, lines 51/75/99). A conformant synchronous get_products returns {products: [...]}, so this check can never resolve. What breaks for adopters: a seller that correctly keeps wholesale get_products synchronous fails the success branch, and unless it also rejects the registration it fails synchronous_completion_assertion — the branch set penalizes the exact behavior it is meant to reward. The companion field_absent: task_id already captures the real intent. Fix: drop the status assertion (or replace with a products presence check).

Neither bug is caught by the storyboard schema/contradiction tests in the test plan — those validate request/response shape, not runner-time \$context resolution or field_value paths against live responses. That is the gap that put these in the bundle.

Things I checked

  • Version triple is coherent. package.json 3.0.16, static/schemas/source/index.json adcp_version 3.0.16, CHANGELOG 3.0.16 patch section all agree. index.json is the only static/schemas/source/** file touched and it is a version-string bump — no wire field, type, enum, or required-flag change. No spec drift, no oneOf regression.
  • Changeset gate is satisfied. This is a changeset-release/ PR: it consumes (deletes) backport-supports-proposals-gate.md and tmp-context-match-seller-agent-url.md and rolls them into CHANGELOG. Release PRs consume changesets — no missing-changeset block.
  • idempotency_key_a removal is clean — it was a context_outputs definition only, referenced nowhere as \$context.idempotency_key_a.
  • Date logic is right. Reversed-dates uses 2099-12-312099-01-01 (end-before-start, still exercises reversal); past-start uses 20202099 (past start, future end, exercises auto-adjust); asap/2099 windows keep buys non-terminal. The 2026/2027 hardcoded dates were the reported runner failure — this is the right fix shape.
  • Security probe change does not weaken the bar (security-reviewer: no regression, highest finding Low). The negative probes (probe_unauth, probe_invalid_api_key, probe_invalid_basic, probe_invalid_oauth_token) are untouched and still assert rejection; the positive-path change correctly models MCP session auth (initialize handshake before tools/call) and a non-conformant server cannot newly pass because enforcement is proven by the positive+negative pairing, not the positive step alone.
  • Closing the skip_if: webhook_auth_mode == 'hmac_legacy' loophole is a security improvement — it removed a self-exemption from signature grading, and signature_validity still grades not_applicable (not pass) when the client 9421 verifier isn't shipped, so no false-pass. Tests an already-normative 3.0 baseline (security.mdx:64,66) — patch-appropriate, not a new wire requirement.

Follow-ups (non-blocking — file as issues)

  • rule 9 (first-insert-wins) does not exist in security.mdx. ad-tech-protocol-expert confirmed the idempotency normative list runs 1-8; idempotency.yaml cites a "rule 9" in the summary line, narrative bullet 6, the concurrent_retry narrative, and the cross_response_count_distinct description. (The rate_limit_replay_invariant "rule 3" citation is accurate — security.mdx:289,300.) Re-cite to rule 2's (authenticated_agent, account_id, idempotency_key) canonical-insert model, or add the rule to security.mdx in its own change — not silently in a backport.
  • Dead doc link introduced by this PR. security.yaml repoints the authentication link from /docs/building/integration/authentication (which exists) to /docs/building/by-layer/L2/authentication (which exists nowhere in the repo on this branch). Point it back, or land the target doc.
  • Confirm the runner treats unrecognized check: verbs under a missing requires_contract as not_applicable, not a hard parse error. The new cross_response_count_distinct, cross_response_field_equal, and replay_not_cached_rate_limit checks are assertion verbs, not contract-gated tasks — if an older runner hard-errors on an unknown check, the concurrent-retry and rate-limit phases fail closed on every current runner.
  • Cross-tenant idempotency isolation is a reviewer_check, not an automated assertion (idempotency.yaml concurrent_retry, and the rate-limit budget-scoping check). Fine as human guidance — but don't describe it as automated cross-tenant testing in release notes. A real second-principal step would close the gap.
  • Positive auth probes no longer assert any success signal (security-reviewer Low). Consider asserting a success envelope / absence of error_code so a server that 5xx's on a valid credential is still caught on the positive path.

Minor nits (non-blocking)

  1. Copy-paste ids in the sync-completion branches. Both sync_get_products_with_webhook_config_success and ..._reject use operation_id: \"op_sync_get_products_no_webhook\" and correlation_id: \"webhook_emission--sync_get_products_no_webhook\" despite distinct step ids. Harmless — no expect_webhook keys off them — but the _no_webhook token reads as stale on the success branch. Give each its own ids for trace clarity.

Fix the two false-fails and I'll re-review. The dates/security/state-machine fixes are the right shape — it's the two new webhook-emission phases that aren't ready.

@aao-release-bot aao-release-bot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requesting changes. The release mechanics are right, but the storyboard fixes ship graded MUST-pass assertions that contradict the 3.0 wire spec — false negatives in the stable 3.0.16 grading bundle, which is the exact failure class this release says it fixes.

code-reviewer: clean. No dangling $context refs, the filter.body.operation_id shape change is consistent across all five expect_webhook* steps, the synchronous_completion branch_set wiring is valid, and the no_duplicate_webhooks_on_replay / idempotency_key_a removals left zero references. ad-tech-protocol-expert: unsound — multiple new graded assertions test fields, error codes, and capability paths that do not exist on the 3.0.x branch this bundle ships against.

MUST FIX (blocking)

  1. operation_id_echo grades against a config field that does not exist, and inverts the documented mechanism. static/compliance/source/universal/webhook-emission.yaml:225-289 sends push_notification_config.operation_id: "op_storyboard_echo_123" and asserts (L24, L231) the seller MUST treat the URL as opaque and MUST NOT parse it. But push-notification-config.json:7-46 has exactly three properties — url, token, authentication — no operation_id. And the spec mechanism is the opposite: mcp-webhook-payload.json:17 says operation_id "was embedded in the webhook URL by the buyer," and webhooks.mdx:118 shows it in the URL path (.../{operation_id}), echoed "from your URL" (webhooks.mdx:38). This is a MUST-pass phase (webhook-emission.yaml:728), and idempotency_key_presence now depends on the same echo. What breaks for adopters: a spec-conformant 3.0 seller reads operation_id from the URL per webhooks.mdx, is sent a non-schema push_notification_config.operation_id, and gets graded as failing the first two MUST-pass webhook phases — a false negative shipped in the stable bundle.

  2. State-machine gate keys on a capability that does not exist → fail-open dead test. static/compliance/source/protocols/media-buy/state-machine.yaml:14-16 gates the entire storyboard on media_buy.creative_approval_mode == auto_approve. There is no creative_approval_mode field anywhere in static/schemas/source/**. No agent can populate it, so requires_capability never matches and every agent skips the storyboard. The state-machine conformance area silently goes dark — the opposite of the "stop manual-review sellers from false-failing" intent. (The status: active assertion at the create step is correct — active is valid per enums/media-buy-status.json.)

  3. IDEMPOTENCY_IN_FLIGHT is not in the error enum, and "rule 9" does not exist. idempotency.yaml blesses IDEMPOTENCY_IN_FLIGHT as a conformant concurrent-retry response and cites security.mdx#idempotency "rule 9 (first-insert-wins)." enums/error-code.json has only IDEMPOTENCY_CONFLICT and IDEMPOTENCY_EXPIRED; security.mdx idempotency rules are numbered 1-8. Lower blast radius than 1-2 (the phase is gated on a parallel_dispatch_runner contract and the code is MAY, not MUST), but it tells sellers an out-of-enum error code is conformant. Either add the code to the enum on main first, or relabel this assertion advisory.

These flow from ad-tech-protocol-expert's unsound verdict with cited divergence. The fix is to land the wire additions (push_notification_config.operation_id, creative_approval_mode, IDEMPOTENCY_IN_FLIGHT) on main as a minor and backport the storyboard grading after the schema, or mark these assertions advisory so 3.0.16 stays a true patch.

Things I checked

  • Release mechanics are sound: changeset-release/ branch prefix, two consumed patch changesets (backport-supports-proposals-gate, tmp-context-match-seller-agent-url) deleted, package.json and static/schemas/source/index.json both 3.0.15→3.0.16. index.json is a clean adcp_version bump — no wire-shape change. Existing dist/.../3.0.15 artifacts untouched; 3.0.16 is additive.
  • ~778 of 781 files are generated dist/compliance/3.0.16/** — confirmed generated, reviewed the 8 hand-edited source files instead.
  • scripts/build-compliance.cjs:276-410: the duplicate-generated-key lint allowlist widening to universal/webhook-emission.yaml is correct (rel is path.relative(sourceDir, p), matching the Set entries) and harmless — code-reviewer notes it is inert because those steps carry a $test_kit schema_ref that never matches mutatingRefs.
  • security.yaml: moving static-credential positive probes onto the initialized MCP session (handshake before tools/call) is the correct MCP Streamable HTTP model — removing the raw-bearer http_status: 200 probe is right.
  • idempotency.yaml: the rate_limit_replay_invariant phase is spec-grounded (security.mdx:300RATE_LIMITED MUST NOT be cached as the canonical replay). Moving the webhook side-effect assertion out to webhook-emission.yaml to keep core idempotency runnable without a receiver is the right seam.

Follow-ups (non-blocking — file as issues)

  • HMAC-legacy exemption removal. Dropping skip_if: agent.webhook_auth_mode == 'hmac_legacy' on signature_validity and adding the signing_keys_published precheck is a real conformance tightening. push-notification-config.json:20 says the baseline 9421 key "is always discoverable" even when HMAC is selected, so requiring publication is defensible — but failing live-HMAC agents on signing flips a fallback the wire spec blesses through 4.0. If intentional, it wants a deprecation note, not a backport bullet. (ad-tech-protocol-expert flagged this; I'm downgrading it from the block because the "always discoverable" language gives it cover and adcp_use: webhook-signing does exist in push-notification-config.json / get-adcp-capabilities-response.json — the expert overreached calling that fabricated.)
  • Storyboard fixes ride in without a discrete changeset. The five storyboard edits are introduced in this release PR and documented only by a hand-authored, hash-less CHANGELOG bullet. Defensible for a manual backport cut, but the graded-conformance tightening above is exactly what a changeset is supposed to surface.

Minor nits (non-blocking)

  1. Broken backported doc link. security.yaml:18 repoints the auth link to /docs/building/by-layer/L2/authentication. That path does not exist on 3.0.x — the doc is docs/building/integration/authentication.mdx. The by-layer/L2 reorg never landed on this branch; the backport carried the main-line path.

Land the wire-schema additions first, or mark the three assertions advisory, and this is a clean patch. Holding until operation_id_echo and the state-machine gate grade against fields that exist on 3.0.x.

@bokelley bokelley merged commit 6b96e69 into 3.0.x Jun 14, 2026
10 checks passed
@bokelley bokelley deleted the changeset-release/3.0.16-storyboard-fixes branch June 14, 2026 16:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant