Repository navigation
fix(storyboard): skip media-buy context extraction from interim create/update_media_buy envelopes - #3683
fix(storyboard): skip media-buy context extraction from interim create/update_media_buy envelopes#3683Synaeve wants to merge 2 commits into
Conversation
…ia_buy envelopes The create_media_buy context extractor recorded media_buy_id and media_buy_status from any response, including submitted, working and input-required task envelopes. Storyboards that gate on context.media_buy_id then treated an asynchronous create as synchronous. Interim envelopes now contribute nothing to context. Any other response extracts as before. Storyboards that need the id from an asynchronous create can capture it with a task_completion.media_buy_id context output. Closes adcontextprotocol#3682
There was a problem hiding this comment.
Ladon verdict: Approve
Approve — clean, targeted fix for #3682 with no blocking findings.
What I checked:
create_media_buyno longer records provisional media-buy fields from interim task envelopes (submitted/working/input-required), so async sellers stop looking synchronous to storyboards gating oncontext.media_buy_id.INTERIM_TASK_STATUSESmatches runner.ts'sPOLL_ELIGIBLE_STATUSESexactly; these values are never media-buy lifecycle statuses (confirmed against media-buy-status.ts), so the guard cannot swallow a terminal result.- Six new tests cover interim statuses, nested media_buy, and preserved legacy/no-status paths.
- Changeset present and
patchis correctly right-sized for this behavior-preserving fix.
Medium findings:
- src/lib/testing/storyboard/context.ts:229 —
update_media_buyextractor still reads interim envelopes via the identical readMediaBuyFields path with no interim guard, so the same misclassification can land via an interim update envelope.
Single medium finding, no critical/high, not a data-loss/schema/infra category, high_risk is false, gated_paths is false, no team gate. Per decision table this falls through to row 9 — approve. The update_media_buy gap is worth a follow-up but is outside this diff and non-blocking.
Medium findings
- src/lib/testing/storyboard/context.ts:229 — update_media_buy extractor still reads interim envelopes
There was a problem hiding this comment.
Ladon verdict: Approve
Approve.
This is a subsequent review. The prior run flagged one medium finding (src/lib/testing/storyboard/context.ts:229 — update_media_buy extractor still reads interim envelopes). That finding is resolved in this delta: both create_media_buy and update_media_buy extractors now delegate to extractTerminalMediaBuyFields, which returns {} for interim task envelopes (submitted/working/input-required), so an async seller no longer appears synchronously complete to storyboards gating on context.media_buy_id. INTERIM_TASK_STATUSES is disjoint from media-buy lifecycle values, so legacy top-level-status and no-status paths still extract — both covered by new tests across both tasks and all three interim statuses.
Changeset present and correctly scoped (patch is appropriate for a storyboard-testing bugfix with no wire-contract change). No new findings this run.
Decision table: no critical/high findings (row 1 n/a); gated_paths false (row 2 n/a); high_risk false (rows 3, 5 n/a); no medium findings (rows 4, 8 n/a); prior decision was approve, not escalate (row 6 n/a); no no-auto-approve team match (row 7 n/a). Falls through to row 9 — approve.
Summary
The storyboard runner's
create_media_buycontext extractor recordedcontext.media_buy_id(andmedia_buy_status) from any response body, including interim task envelopes (submitted,working,input-required). A schema-validworkingenvelope can carrymedia_buy_id(create-media-buy-async-response-working.jsonis open), so storyboards that useskip_if: "!context.media_buy_id"to detect synchronous completion classified an asynchronous seller as synchronous.update_media_buyshared the same extraction path and is guarded the same way.This implements option 1 from the issue: interim envelopes contribute nothing to context. It unblocks adcontextprotocol/adcp#8894 (issue adcontextprotocol/adcp#8771), where
webhook_emissionotherwise false-fails aworkingseller that echoesmedia_buy_id.Changes
src/lib/testing/storyboard/context.ts:create_media_buyandupdate_media_buyreturn no context when the top-levelstatusissubmitted,workingorinput-required(the runner's poll-eligible set). None of these is a media-buy lifecycle value in any AdCP release, so the check is version-independent. Any other response, including legacy top-level lifecycle statuses, extracts as before.test/lib/context-extractors.test.js: interim envelopes for both tasks (top-level and nestedmedia_buy), a legacy lifecycle status on create, and a response with no status.@adcp/sdk.Behaviour change: a seller that answers
workingwith data is not polled, so with this fix its later steps get nocontext.media_buy_id. That is the intent. An interim update leaves themedia_buy_idandmedia_buy_statusrecorded by earlier steps untouched instead of overwriting them. A storyboard that needs the id from an asynchronous create can capture it with atask_completion.media_buy_idcontext output.Testing
node --test test/lib/context-extractors.test.js: 46/46. The interim-envelope tests fail without the fix.npm run typecheck,npm run lint,npm run lint:boundariesclean; commitlint passes.Closes #3682
🤖 Generated with Claude Code