Skip to content

fix(wix-manage): correct the Create Discount Rule discounts wire shape - #842

Open
danshw wants to merge 3 commits into
mainfrom
fix/discount-rule-discounts-wire-shape
Open

fix(wix-manage): correct the Create Discount Rule discounts wire shape#842
danshw wants to merge 3 commits into
mainfrom
fix/discount-rule-discounts-wire-shape

Conversation

@danshw

@danshw danshw commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Defect

The pricing-create-discount-rule recipe documented a Create Discount Rule body that the API
rejects, in two independent ways. Both were in the recipe's own Critical: discounts structure
section, so an agent following the page verbatim could not succeed on its first call.

1. discounts was documented as a bare array. It is an object wrapping a values array.

"discounts": [ { … } ]
→ 400 {"message":"Expected an object"}

2. Discount entries nested the type and value in a discount object. discountType and the
value field sit at the entry root.

"discounts": { "values": [ { …, "discount": { "discountType": "PERCENTAGE", "percentage": 10 } } ] }
→ 400 Validation failed: discount_rule.customer_gets.specific_items_discount
      Discount value type does not match discount value
      (violatedRule: OTHER, ruleName: DISCOUNT_TYPE_AND_DISCOUNT_VALUE_MISMATCH)

The shape the API accepts:

"discounts": {
  "values": [
    {
      "targetType": "SPECIFIC_ITEMS",
      "specificItemsInfo": { "scopes": [ ] },
      "discountType": "PERCENTAGE",
      "percentage": 10
    }
  ]
}

Evidence

From a recorded agent run on the request "Set up an automatic discount: 10% off any order over
$50."
The agent read this recipe, then spent four rejected POST /ecom/v1/discount-rules
attempts brute-forcing the payload before succeeding on the fifth:

Attempt Body Result
1 discounts array, nested discount (exactly as documented) 400 Expected an object
2 discounts.values, nested discount 400 DISCOUNT_TYPE_AND_DISCOUNT_VALUE_MISMATCH
3 discounts array, discountType at root 400 Expected an object
4 discounts.values, discountType at root, percentage still nested 400 DISCOUNT_TYPE_AND_DISCOUNT_VALUE_MISMATCH
5 discounts.values, discountType and percentage at root 2xx

Between attempts it issued six further schema-probing calls purely to work out the shape. The task
still completed — the outcome judge scored it 10/10 — so this defect is invisible to a
correctness-only assertion and shows up only as cost: the path judge scored 4/10 on two
consecutive daily runs, and the run took ~97 s against a ~50 s median for its batch.

Both corrections were verified against the Discount Rules API schema, which declares discounts
as a message wrapping repeated Discount values, and declares the value field as a oneof
(percentage / fixed_amount / fixed_price) with discount_type as its sibling — a oneof is
flattened in JSON, so its name is not a wire field. That is why the documented discount wrapper
could never have worked.

Why this surface

The recipe is the file the failing agent actually loaded (its first action after the README was
ReadFullDocsArticle on this recipe's URL), and the recipe is where the wrong shape was written.
The API schema and reference are already correct — the agent's own schema lookups returned the
right structure, which is how it eventually recovered. So this is not a rendering or publishing
gap upstream; it is a recipe contradicting the API it documents, and correcting the recipe is the
whole fix.

Change

Deliberately scoped to the wrong shape and nothing else — every changed line is one of the two
defects above. No reformatting, and the wrapper is added as "discounts": { "values": [ on one line
so the entries keep their existing indentation and the diff stays reviewable.

  • skills/wix-manage/references/ecommerce/pricing-promotions/ecom-pricing-create-discount-rule.md
    — corrects the six request/response examples, the Critical: discounts structure entry example,
    the key-field table, the find-by-name update pattern, and the recommendation mapping table. The
    two 400s go in the existing Error Handling table as two rows, mapping each message to its cause,
    rather than as new prose: the corrected examples already carry the shape, so the only thing worth
    adding is what an example cannot say. In the recorded run the agent received both 400s and still
    needed four attempts, because nothing connected the message to the cause.
  • yaml/wix-manage-evals/ecommerce/pricing-promotions/ecom-pricing-create-discount-rule-discounts-shape.yml
    — new covering scenario. It keeps the original request wording, so nothing in the prompt reveals
    the payload shape; the discrimination is in the rubrics. Three assertions per
    docs/eval-scenarios.md: coverage on this recipe's URL, a correctness judge on the created rule,
    and a path judge that fails a run which takes either 400 on the way.

On "Orchestration, not API shape"

CONTRIBUTING.md says request/response shapes belong in the Wix docs, because restating them
"creates a second source of truth that drifts as the API evolves." This recipe already restates seven
full request bodies, and the defect is exactly the drift that rule warns about — so this PR corrects
the restated shapes rather than adding new ones, and adds no new prose restating field names or enum
values. That rule is also why the two 400s became Error Handling rows instead of a shape
description at the top of the file.

Removing the restated bodies altogether, in favour of pointing at the Create Discount Rule reference,
would follow the rule more closely and would delete the drift risk at the source. That is a larger
change to a recipe several other recipes and scenarios lean on, so I have kept it out of this fix and
am flagging it as worth doing separately.

Verification

  • ruby -e 'require "yaml"; YAML.load_file(…)' on the new scenario — parses.
  • Every json block that parsed before still parses. Two do not, and both are pre-existing and
    untouched: a "<scopes unchanged from query response>" placeholder, and a trigger example with an
    intentional /* same scope */ comment.
  • git diff --check — clean.

The eval gate has not run on this PR

No workflow has run on this PR, so nothing here is gate-verified. GitHub Actions appears to be
disabled on the repository right now: the repo's Actions permissions report enabled: false, and no
workflow of any kind has started repo-wide since 16:09 UTC, while this PR was opened at 16:52 UTC.
Every PR opened earlier today has runs; this one has zero, for every workflow, not just the gate.

That is a repository-level condition rather than anything about this diff, and I have not changed the
setting. Once Actions is re-enabled, a push or a reopen will trigger EvalForge YAML Gate and the
new scenario will run. Please do not read the absence of a red check as a pass.

Please note before merging

This PR edits the same recipe as #666, which is open. The two changes do not overlap in
content — #666 adds Stores collection-ID lookup guidance and touches a different section — and
they do not conflict textually. But both PRs edit the recipe, so both pull in the pre-existing
ecommerce/pricing-promotions/create-discount-rule scenario, and the eval gate treats a scenario
claimed by two open PRs as held. If this gate reports scenarios held by another PR, that is the
cause rather than anything in this diff, and landing or closing #666 first clears it. I have not
described this PR's gate as passing.

@danshw
danshw force-pushed the fix/discount-rule-discounts-wire-shape branch 2 times, most recently from 50fa20d to ba90f44 Compare August 4, 2026 17:06
The discount-rule recipe documented `discounts` as a bare array whose entries
nest the value in a `discount` object. The Discount Rules API accepts neither,
and each mistake returns a 400 that does not name the real problem:

  discounts: [ ... ]
    -> 400 {"message":"Expected an object"}

  discounts: { values: [ { ..., discount: { discountType, percentage } } ] }
    -> 400 Validation failed: discount_rule.customer_gets.specific_items_discount
       Discount value type does not match discount value
       (DISCOUNT_TYPE_AND_DISCOUNT_VALUE_MISMATCH)

`discounts` is an object wrapping a `values` array, and `discountType` sits at
the discount entry root alongside exactly one of `percentage`, `fixedAmount` or
`fixedPrice`:

  discounts: { values: [ { targetType, specificItemsInfo, discountType,
                           percentage } ] }

Corrects the six request/response examples, the key-field table, the
find-by-name update pattern and the recommendation mapping table, maps both 400s
in the existing Error Handling table, and adds a covering eval scenario.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@danshw
danshw force-pushed the fix/discount-rule-discounts-wire-shape branch from ba90f44 to f73c2ef Compare August 4, 2026 17:09
@danshw

danshw commented Aug 5, 2026

Copy link
Copy Markdown
Contributor Author

❌ EvalForge YAML Gate: Scenario Locked by Another PR

These scenarios are draft-tagged for other PRs. Wait for those PRs to merge/close, or coordinate with their authors:

danshw added 2 commits August 5, 2026 21:17
…t responses

The Critical section described only what the API returns, and closed with "do
not reconstruct from scratch unless creating a new rule" — which leaves a create
request unconstrained. A paired eval run showed the agent still sending
discounts as a bare array with this recipe loaded, taking the same
400 "Expected an object" as production.

State the send-side requirement first, and name both rejections.
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