Skip to content

[codex] Fix available-actions active-buy storyboard#5379

Merged
bokelley merged 4 commits into
mainfrom
fix-storyboard-asap-pause
Jun 6, 2026
Merged

[codex] Fix available-actions active-buy storyboard#5379
bokelley merged 4 commits into
mainfrom
fix-storyboard-asap-pause

Conversation

@bokelley
Copy link
Copy Markdown
Contributor

@bokelley bokelley commented Jun 6, 2026

Summary

Fixes the available-actions storyboard so it first drives the buy active with start_time: "asap", a synced creative, and an assigned creative before running pause-shaped negative probes. Also fixes dev compliance latest metadata to use semver and adds a temporary ledgered SDK request-builder shim so local/CI storyboard runs preserve asap until the upstream SDK release lands.

Verification

Precommit passed unit, lint, and typecheck stages; pre-push passed version sync, current-source storyboard matrix, 3.0 compatibility storyboard matrix, and skipped docs checks because no docs changed.

@mintlify
Copy link
Copy Markdown

mintlify Bot commented Jun 6, 2026

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
adcp 🟢 Ready View Preview Jun 6, 2026, 10:57 AM

💡 Tip: Enable Workflows to automatically generate PRs for you.

…ause

# Conflicts:
#	static/compliance/source/protocols/media-buy/scenarios/available_actions.yaml
Comment thread scripts/patch-sdk-storyboard-request-builder.cjs Fixed
@bokelley bokelley marked this pull request as ready for review June 6, 2026 12:18
aao-release-bot[bot]
aao-release-bot Bot previously approved these changes Jun 6, 2026
Copy link
Copy Markdown
Contributor

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

Choose a reason for hiding this comment

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

Approving. The fix is the right shape: drive the buy to a real terminal state before probing its action surface, instead of asserting available_actions[] semantics against a buy still stuck in pending_creatives.

The core change is sound. The negative probe at direct_decrease_wrong_status was previously testing the wrong prerequisite — it fired wrong_status only because the buy never left pending_creatives, which is creative-review noise, not action-discovery enforcement. Inverting decrease_budget to allowed_statuses: ["paused"] and driving the buy active via start_time: "asap" + an assigned creative makes the probe test what the scenario name claims: a product-supported action gated out by the buy's current status.

Things I checked

  • Lifecycle wiring is complete. New sync_creatives step plumbs products[0].format_ids[0] into $context.available_actions_format_id, then create_media_buy assigns it via creative_assignments alongside start_time: "asap". ad-tech-protocol-expert confirmed asap is a valid start-timing.json sentinel and that asap + assigned creative under auto_approve lands active — matching the sibling pending_creatives_to_start.yaml pattern.
  • Capability gate is honest. requires_capability: media_buy.creative_approval_mode == auto_approve is a real path (get-adcp-capabilities-response.json:266, enum ["auto_approve","require_human"]) lifted verbatim from pending_creatives_to_start.yaml. Manual-review sellers grade not_applicable rather than false-failing on a prerequisite they can't satisfy. Scenario isn't in any required-clean floor, so not_applicable drops nothing.
  • Stale prose got fixed. The direct_decrease_wrong_status narrative now reads "The created buy is active" instead of "still pending_creatives." Self-consistent with the inverted assertion — no contradiction left on the wire.
  • build-compliance.cjs version is in scope. Both buildTo(latestDir, version, ...) sites sit inside main() after const version = getVersion(); stamping latest/ with real semver instead of the literal 'latest'. No consumer keys off the literal string value.
  • Shim is ledgered and fail-loud. patch-sdk-storyboard-request-builder.cjs is idempotent (sampleStart === 'asap' early-exit), backs up before patching, and hard-exits 1 if the expected window is gone. Ledger entry, lint-shim assertion, and restore loop over both backups are all updated in lockstep. field_value_or_absent on legacy status correctly handles the 3.1 status-rename deprecation window (#4908).

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

  • Request-builder patch now runs unconditionally (run-storyboards-matrix.sh:175). The pre-existing SDK reach-ins were OVERLAY=1-gated; this new patch is not, so the asap-preservation behavior also applies to --latest-3.0 released-bundle compat runs and to other asap scenarios (proposal_finalize_asap_timing.yaml, etc.). The trap/restore is symmetric so nothing leaks — this is blast-radius, not corruption. PR body says the 3.0 matrix passed pre-push; if the universal application is deliberate, state that in the ledger entry. Otherwise gate it behind OVERLAY=1 to match the other reach-ins.

Minor nits (non-blocking)

  1. Backup precedes the match guard. patch-sdk-storyboard-request-builder.cjs:23-25 writes .adcp-overlay-backup before checking that legacyWindow exists. A future SDK lacking both the legacy window and the asap marker leaves a spurious (identical) backup on the exit-1 path. run-storyboards-matrix.sh's EXIT trap cleans it, but overlay-compliance-cache.sh (set -euo pipefail) aborts first. Move the backup after both early-exit guards.

Two clean expert verdicts: code-reviewer — no blockers, one Medium noted above; ad-tech-protocol-expert — sound-with-caveats, and every caveat it raised is already closed by the diff it couldn't fetch.

LGTM. Follow-ups noted below.

Copy link
Copy Markdown
Contributor

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

Choose a reason for hiding this comment

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

Approving. Drives the buy to active before the negative probes so the storyboard tests available_actions enforcement, not a pending-creative lifecycle artifact — the prerequisite and the thing under test are finally separated.

Things I checked

  • The status flip is internally consistent. decrease_budget allowed_statuses goes ["active"]["paused"] in the fixture (yaml L88) and the get_products assertion (L170), and the negative-probe expected prose now reads "only for paused buys. The created buy is active" (L529-531). The probe fires decrease_budget against an active buy and correctly expects ACTION_NOT_ALLOWED. No stale assertion stranded — decrease_budget is never asserted into buy-level available_actions.
  • The active-buy path is wired end to end. requires_capability: media_buy.creative_approval_mode == auto_approve + accepts_creatives + the new sync_creatives step + context_outputs capturing available_actions_format_id + $context.available_actions_format_id in the sync request all line up. Mirrors the merged pending_creatives_to_start.yaml gate pattern. Manual-review sellers grade not_applicable rather than false-failing on the lifecycle prerequisite.
  • media_buy_status: active + field_value_or_absent on legacy status is the sanctioned dual-field handling for the 3.1 deprecation window. media_buy_status is the correct 3.1 wire field, active is a valid enum (enums/media-buy-status.json), start_time: "asap" is spec-valid (core/start-timing.json, create_media_buy.mdx L165).
  • build-compliance.cjs:765,782 ('latest'version) flows to generateIndex and stamps latest/index.json. No consumer compares those fields to the literal "latest"; the ^3\.0\.[0-9]+$ reader in run-storyboards-matrix.sh is unaffected. Aligns dev latest/ with the release build, which already stamped real semver.
  • run-storyboards-matrix.sh trap moved out of the OVERLAY==1 block. restore_sdk_generated_schema is idempotent (acts only if the backup dir exists). Always-on EXIT trap now also cleans up the --latest-3.0/--compliance-dir paths the old code leaked. Safe and an improvement.
  • Changeset present, empty frontmatter = no version bump — correct for a compliance-scenario + build-infra change with no published-package wire impact.

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

  • active-on-create is a hard pin against a spec MAY. specification.mdx L143 lets auto_approve sellers return active, pending_creatives, or pending_start from create_media_buy (seller's choice based on platform setup time). The asap + assigned-creative + auto_approve combination makes active a reasonable deterministic reading, but a conformant seller with non-trivial setup time could legitimately return pending_start and false-fail. Consider accepting active-or-pending_start and driving a flight-start transition before the negative probe.
  • confirmed_at is not pinned. create-media-buy-response.json makes active + confirmed_at: null a oneOf violation. The scenario asserts active but never asserts confirmed_at != null, so a provisional buy could pass the active check while silently violating the response oneOf. Worth a field_present on confirmed_at alongside the active assertion.

The third drift-cleanup in the lineage that ends with the SDK shim ledger swapping "SDK package" for "protocol bundle" in a single sentence — notable that the prose churn ships in the same PR as the fix, but it's accurate.

LGTM. Follow-ups noted below.

@bokelley bokelley merged commit 39a3a51 into main Jun 6, 2026
27 of 28 checks passed
@bokelley bokelley deleted the fix-storyboard-asap-pause branch June 6, 2026 18:09
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.

2 participants