feat(media-buy): add get_products rejection outcome - #6127
Conversation
| ) { | ||
| throw new Error('Pending submitted task cap reached (1000)'); | ||
| } | ||
| await registerSubmittedTask(directive.taskId, args, directivePrincipal, 'get_products'); |
There was a problem hiding this comment.
Medium: registerSubmittedTask is unguarded against a live deterministic ID. This PR newly writes the caller-supplied task_id into the task store on submit. DeterministicInMemoryTaskStore.createTaskWithId throws Task with ID … already exists (deterministic-task-store.ts:31), and a completed task lingers in the store for its full 15-min TTL before scheduleCleanup drops it. Sequence that bites: register force_get_products_arm submitted with task_id T → consume → force_task_completion T → within 15 min re-register + consume the same T for the same account+principal. The throw propagates out of handleGetProducts and is caught only by the generic dispatch try/catch, so the buyer gets an opaque tool error instead of a clean submitted envelope or a controller-level INVALID_PARAMS. Compliance storyboards use fixed task_ids, so a re-run against the same sandbox account inside the TTL window reproduces this. Same call site at task-handlers.ts:4541 for create_media_buy. Prior force_create_media_buy_arm used SDK-generated IDs and had no such collision surface.
There was a problem hiding this comment.
Ladon verdict: Escalate to human review
Escalate — gated schema paths require human/CODEOWNERS approval.
This PR adds the GetProductsRejected structured-decline arm to get_products: new schema, response-arm mutual exclusion (via not.anyOf across get-products-rejected/get-products-response/async-response-data), A2A/protocol-envelope/task-status semantics, a compliance scenario + controller directive, and a 3.2-beta.0 version-negotiated training-agent implementation with account+principal-scoped deterministic task polling. Reviewer confirms schema↔docs coherence holds, mutual exclusion is enforced, the changeset is present and correctly minor (additive, non-breaking), and no dist/** artifacts were touched.
No critical/high findings. One medium (non-blocking on its own).
Why escalate: this PR modifies multiple files under static/schemas/source/**, which are under the repo's deterministic hard approval gate (gated_paths: true). review_decision is not APPROVED, so per decision-table row 2 the gate is not satisfied and the outcome must be escalate. A human/CODEOWNERS approval on the gated schema files is required before this can proceed.
Medium findings
- server/src/training-agent/task-handlers.ts:3141 — registerSubmittedTask throws opaquely on deterministic task_id reuse within the 15-min TTL window.
Medium findings
- server/src/training-agent/task-handlers.ts:3141 — registerSubmittedTask throws opaquely on deterministic task_id reuse within TTL
Why human review
- Gated paths: PR modifies files under static/schemas/source/** (comply-test-controller-request.json, comply-test-controller-response.json, async-response-data.json, protocol-envelope.json, task-status.json, index.json, get-products-response.json) plus adds get-products-rejected.json — these are under a hard approval gate and review_decision is not APPROVED. Human/CODEOWNERS review required.
- Protocol wire-shape change: adds a new response arm (GetProductsRejected) to get_products; requires human sign-off on the schema surface even though the change is additive/minor.
- This PR touches a path under a hard, non-overridable approval gate (static/schemas/source/compliance/comply-test-controller-request.json (modified) matches
static/schemas/source/**; static/schemas/source/compliance/comply-test-controller-response.json (modified) matchesstatic/schemas/source/**; static/schemas/source/core/async-response-data.json (modified) matchesstatic/schemas/source/**; static/schemas/source/core/protocol-envelope.json (modified) matchesstatic/schemas/source/**; static/schemas/source/enums/task-status.json (modified) matchesstatic/schemas/source/**; static/schemas/source/index.json (modified) matchesstatic/schemas/source/**; static/schemas/source/media-buy/get-products-rejected.json (added) matchesstatic/schemas/source/**; static/schemas/source/media-buy/get-products-response.json (modified) matchesstatic/schemas/source/**) and the current GitHub review decision is 'unknown', not APPROVED. This is a hard gate enforced in code — Ladon cannot auto-approve until a human/CODEOWNERS approval is recorded, regardless of how clean the diff is.
There was a problem hiding this comment.
Ladon verdict: Escalate to human review
Escalate — protocol schema surface under a hard approval gate; human/CODEOWNERS review required.
Why escalate (row 2): gated_paths: true and review_decision is not APPROVED. This PR modifies eight files under static/schemas/source/** (a hard, deterministic approval gate), including a new response arm (GetProductsRejected) on get_products. Row 2 fires before any fall-through — outcome must be escalate until a human/CODEOWNERS approval is recorded.
Gated files requiring review:
- static/schemas/source/media-buy/get-products-rejected.json (added)
- static/schemas/source/media-buy/get-products-response.json (modified)
- static/schemas/source/core/async-response-data.json (modified)
- static/schemas/source/core/protocol-envelope.json (modified)
- static/schemas/source/enums/task-status.json (modified)
- static/schemas/source/index.json (modified)
- static/schemas/source/compliance/comply-test-controller-request.json (modified)
- static/schemas/source/compliance/comply-test-controller-response.json (modified)
Delta since prior pass: The prior medium finding (task-handlers.ts:3141) is out of scope this pass and does not carry forward. The two new delta items — a justified codeql[js/user-controlled-bypass] suppression and a test-only CURRENT_ADCP_VERSION bump — clear no severity bar. No current findings at medium-or-above.
Blocking findings: none.
Medium findings: none.
This remains an additive protocol wire-shape change (new response arm on get_products) that requires human sign-off on the schema surface. Once review_decision becomes APPROVED, a clean re-run can fall through to approve.
Why human review
- Gated paths: PR modifies files under static/schemas/source/** (get-products-response.json, async-response-data.json, protocol-envelope.json, task-status.json, index.json, comply-test-controller-request.json, comply-test-controller-response.json) and adds get-products-rejected.json — hard approval gate, and review_decision is not APPROVED. Human/CODEOWNERS review required.
- Protocol wire-shape change: adds a new response arm (GetProductsRejected) to get_products; requires human sign-off on the published schema surface even though the change is additive.
- This PR touches a path under a hard, non-overridable approval gate (static/schemas/source/compliance/comply-test-controller-request.json (modified) matches
static/schemas/source/**; static/schemas/source/compliance/comply-test-controller-response.json (modified) matchesstatic/schemas/source/**; static/schemas/source/core/async-response-data.json (modified) matchesstatic/schemas/source/**; static/schemas/source/core/protocol-envelope.json (modified) matchesstatic/schemas/source/**; static/schemas/source/enums/task-status.json (modified) matchesstatic/schemas/source/**; static/schemas/source/index.json (modified) matchesstatic/schemas/source/**; static/schemas/source/media-buy/get-products-rejected.json (added) matchesstatic/schemas/source/**; static/schemas/source/media-buy/get-products-response.json (modified) matchesstatic/schemas/source/**) and the current GitHub review decision is 'unknown', not APPROVED. This is a hard gate enforced in code — Ladon cannot auto-approve until a human/CODEOWNERS approval is recorded, regardless of how clean the diff is.
There was a problem hiding this comment.
Ladon verdict: Escalate to human review
Escalate — gated schema paths require human/CODEOWNERS approval.
This PR modifies and adds files under static/schemas/source/**, which is a hard, non-overridable approval gate (gated_paths: true). The current review_decision is not APPROVED (unknown/none), so decision-table row 2 fires: outcome must be escalate.
Blocking findings: none. The reviewer surfaced no critical/high/medium findings. The only delta since the prior clean pass is a behavior-preserving hunk in server/src/training-agent/comply-test-controller.ts (hoisting the get_products-rejected version check into a const and removing a CodeQL suppression comment) — confirmed to have no runtime security impact since the account gate still runs above the sandbox scenario selector.
Escalation reasons:
- Touches gated paths under
static/schemas/source/**(get-products-response.json, async-response-data.json, protocol-envelope.json, task-status.json, index.json, comply-test-controller-request.json, comply-test-controller-response.json modified; get-products-rejected.json added) whilereview_decisionis not APPROVED — human/CODEOWNERS review required. - Protocol wire-shape change: adds a new response arm (GetProductsRejected) to get_products. Even though additive, the published schema surface requires human sign-off per repo governance.
This is a clean diff (no blocking findings), so once a human/CODEOWNERS records an APPROVED review, the gate lifts and the PR can proceed.
Why human review
- Gated paths: PR modifies/adds files under static/schemas/source/** (get-products-response.json, async-response-data.json, protocol-envelope.json, task-status.json, index.json, comply-test-controller-request.json, comply-test-controller-response.json modified; get-products-rejected.json added) and review_decision is not APPROVED — hard approval gate, human/CODEOWNERS review required.
- Protocol wire-shape change: adds a new response arm (GetProductsRejected) to get_products; published schema surface requires human sign-off even though the change is additive.
- This PR touches a path under a hard, non-overridable approval gate (static/schemas/source/compliance/comply-test-controller-request.json (modified) matches
static/schemas/source/**; static/schemas/source/compliance/comply-test-controller-response.json (modified) matchesstatic/schemas/source/**; static/schemas/source/core/async-response-data.json (modified) matchesstatic/schemas/source/**; static/schemas/source/core/protocol-envelope.json (modified) matchesstatic/schemas/source/**; static/schemas/source/enums/task-status.json (modified) matchesstatic/schemas/source/**; static/schemas/source/index.json (modified) matchesstatic/schemas/source/**; static/schemas/source/media-buy/get-products-rejected.json (added) matchesstatic/schemas/source/**; static/schemas/source/media-buy/get-products-response.json (modified) matchesstatic/schemas/source/**) and the current GitHub review decision is 'unknown', not APPROVED. This is a hard gate enforced in code — Ladon cannot auto-approve until a human/CODEOWNERS approval is recorded, regardless of how clean the diff is.
There was a problem hiding this comment.
Ladon verdict: Escalate to human review
Escalate — gated-path approval gate is unsatisfied (and this is a breaking-class-eligible protocol wire-shape change).
Escalation reasons
- Gated paths: this PR modifies/adds files under
static/schemas/source/**(get-products-response.json,async-response-data.json,protocol-envelope.json,task-status.json,index.json,comply-test-controller-request.json,comply-test-controller-response.jsonmodified;get-products-rejected.jsonadded) andreview_decisionis notAPPROVED. Human/CODEOWNERS approval is required before this can merge. - Protocol wire-shape change: adds a new response arm (
GetProductsRejected) toget_products. Published-surface schema changes require human sign-off per the repo's breaking-class hard gate, even when additive.
Findings
- No critical/high/medium findings surfaced. The delta (duplicate task_id re-registration converted from a 500 into a clean conflict response, fail-closed race handling, fully-awaited async plumbing, four new tests) reads as clean.
Notes for reviewers
- The diff appears well-tested; the block here is purely the hard, deterministic gated-paths gate plus the breaking-class-change human-ratification requirement — not a code defect. Once a CODEOWNERS/human approval is recorded (
review_decision: APPROVED), a re-run with a clean finding set can fall through to approve. - Prior decision was also
escalatefor the same gated-paths reason; this remains the correct outcome under decision-table row 2.
Why human review
- Gated paths: PR modifies/adds files under static/schemas/source/** (get-products-response.json, async-response-data.json, protocol-envelope.json, task-status.json, index.json, comply-test-controller-request.json, comply-test-controller-response.json modified; get-products-rejected.json added) and review_decision is not APPROVED — hard approval gate, human/CODEOWNERS review required.
- Breaking-class-eligible protocol wire-shape change: adds a new response arm (GetProductsRejected) to get_products on the published schema surface; per the repo's hard gate, ratification is a human act and cannot be auto-approved until review_decision is APPROVED.
- This PR touches a path under a hard, non-overridable approval gate (static/schemas/source/compliance/comply-test-controller-request.json (modified) matches
static/schemas/source/**; static/schemas/source/compliance/comply-test-controller-response.json (modified) matchesstatic/schemas/source/**; static/schemas/source/core/async-response-data.json (modified) matchesstatic/schemas/source/**; static/schemas/source/core/protocol-envelope.json (modified) matchesstatic/schemas/source/**; static/schemas/source/enums/task-status.json (modified) matchesstatic/schemas/source/**; static/schemas/source/index.json (modified) matchesstatic/schemas/source/**; static/schemas/source/media-buy/get-products-rejected.json (added) matchesstatic/schemas/source/**; static/schemas/source/media-buy/get-products-response.json (modified) matchesstatic/schemas/source/**) and the current GitHub review decision is 'unknown', not APPROVED. This is a hard gate enforced in code — Ladon cannot auto-approve until a human/CODEOWNERS approval is recorded, regardless of how clean the diff is.
Adds a terminal
GetProductsRejectedresponse arm for well-formed commercial declines while keeping semantic no-match as completedproducts: [], partial usable responses inincomplete, and transient upstream failures inSERVICE_UNAVAILABLE.Updates schemas, protocol and task documentation, A2A semantics, and compliance coverage with strict response-arm mutual exclusion.
Extends the training agent with account-and-principal-scoped submitted-task polling, durable completion, webhook correlation, and downstream media-buy materialization.
Validation: current-version storyboards passed across all six tenants, focused schema/A2A/controller/MCP suites passed, while the unchanged SDK safe-fetch fixture, released 3.0 signals governance-denial storyboard, and baseline
otel-logs.tstype error remain failing.Closes #6035.