fix(wix-manage): send each variant complete on a Catalog V3 update - #926
fix(wix-manage): send each variant complete on a Catalog V3 update#926danshw wants to merge 3 commits into
Conversation
`variantsInfo.variants` replaces the array *and* each variant object —
it does not merge. The recipe said to pass the entire existing array and
to include each existing variant `id`, which an agent can satisfy while
still rebuilding each variant from only its `id` plus the field being
changed. That drops every field it did not carry, and the update is
rejected on the first required one:
HTTP 400 {"message":"product is invalid:\n`-- variantsInfo is
invalid:\n `-- variants [at index 0] is invalid:\n `--
price must not be empty","details":{"validationError":
{"fieldViolations":[{"field":"product.variantsInfo.variants[0].price",
"description":"must not be empty","violatedRule":"REQUIRED_FIELD"}]}}}
Setting a SKU on an existing product hits this: the agent reads the
product, maps its variants to `{ id, sku }`, and loses the price. It
recovers by retrying with the full variant, so the user still gets the
right result — one rejected write later.
Say that each variant object is replaced whole, and correct the error
table, which attributed `price must not be empty` to new variants only
and so read as not applying to an existing-variant update.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Eval comparison — the fix works; the scenario still cannot certify itFourth run, and the first in which both arms read the recipe, so recipe content was genuinely the only variable.
Production reproduced the defect this change documents, verbatim from its path judge:
The PR arm did not hit it at all — it went straight to a working update in two calls. That is the change doing its job. It nonetheless scored 5, for an unrelated reason: it parsed the Search Products response with the Catalog V1/V2 shape ( So the change is supported by this run — the documented friction appears without it and not with it — but the scenario cannot award |
The single-variant seed let an agent satisfy the rule by accident: spreading the one variant it read back is the obvious move, so the run often passed without the replace-not-merge behaviour ever being tested. Seed the product with a Size option and two variants at different prices, and ask for the SKU of one of them. Now the agent has to send both variants complete — dropping the untouched variant, or rebuilding either one from just its `id` plus the changed field, is rejected. Judge bands updated to match: correctness checks the other variant survives with its price, and the path judge's 1-3 band covers both the REQUIRED_FIELD rejection and the dropped-variant case. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The recipe tells you to use `optionChoiceNames` in variants, so an agent reasonably expects to read them back. Get Product does not return them by default — each variant's `choices` carries `optionChoiceIds` only, and `optionChoiceNames` is populated only when the request's `fields` array includes `"VARIANT_OPTION_CHOICE_NAMES"`. Matching a variant on a choice name the response never carried does not raise; it selects nothing. In a recorded run the agent asked for one size of a two-variant product, matched the choice by name, got no variant back, and had to spend two more calls inspecting the product's options before it could resolve the choice GUID and retry. Give both ways out: request the field and match on names, or read the GUID from `options[].choicesSettings.choices[].choiceId` and match `variants[].choices[].optionChoiceIds.choiceId`. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Folded into #927. The two changes are coupled: the variant fixes here could not reach a clean path while the Search Products response envelope was still undocumented, so a single gate run over both is the only way to test them together. All commits from this branch were cherry-picked onto #927 unchanged. |
variantsInfo.variantsreplaces the array and each variant object — it does not merge them. The recipe already said to pass the entire existing array, and to include each existing variantid. An agent can satisfy both of those and still rebuild each variant from only itsidplus the field it wants to change, which drops every field it did not carry. The update is then rejected on the first required one:How it shows up
Setting a SKU on an existing product. From a recorded run, the agent read the recipe, located the product and re-read it with Get Product — then assembled the PATCH as:
The variant's price went with it, and the write was rejected. The agent recovered by retrying with the variant spread intact, so the user still got the right SKU — one rejected write and an extra round trip later.
The error table made this harder to diagnose than it needed to be: it attributed
price must not be emptyto a variant "created or replaced without a price" and prescribed the fix "on every new variant", so an agent updating an existing variant reads that row as not applying to it.Changes
Two lines in
update-product-with-options.md:price must not be emptyrow now names the existing-variant case and says to carrypriceon every variant sent, not just new ones.No request or response shapes are restated — both edits are about how to assemble a call from what Get Product already returned.
Coverage
New scenario
yaml/wix-manage-evals/stores/update-product-sku-full-variant.yml, three assertions with the path judge gating at 7, per this repo's eval-scenario guide. Its 1-3 band is specifically a rejected-then-retried Update Product caused by aREQUIRED_FIELDviolation onvariantsInfo.variants[...]from fields the agent did not carry over, so the scenario fails on the behaviour this PR is meant to remove rather than on the outcome, which was already correct.