RRetailAgentOS
All Specs
RAOS-0002Draft · RFCv1.0.0June 2026

Contextual Pricing (Member + Bulk)

com.os.retailagent.shopping.member_pricing

com.os.retailagent.shopping.bulk_pricing

Plane 3 · Price & Value · Tier 2 · Priced · Reference implementation in Playground

This is an open draft. The point is to be argued with. The member-vs-bulk precedence question (§9.1) is a genuine fork with real implications for grocery + wholesale. See the open questions →

Abstract

This spec defines how a merchant declares contextual pricing — prices that vary by buyer identity and order quantity — and how an agent computes the applicable price at catalog time, before a cart is built. It produces a ComputedPriceState contract: a single unit price with full provenance.

The defining feature is offer transparency. Every applied discount carries a structured AppliedOffer record; every evaluated-but-superseded discount appears in suppressedOffers[] with the reason it lost. An agent can explain why the price is $X, not just state it.

The problem this solves

  • A gold member is quoted the public price because the agent doesn't know member prices exist.
  • A wholesale buyer orders below MOQ and hits a wall at checkout with no explanation.
  • A guest sees an item they can't add but gets no teaser showing the member price or how to qualify.
  • An agent quotes a price without knowing which offers were applied — the RAOS-0007 quote token can't bind what it can't see.

What goes in

Merchant declares on the variant

Member price, teaser, bulk tiers, MOQ, quantity increments, purchase limits.

Agent supplies buyer context

Customer type and membership tier determine which price the buyer qualifies for.

Agent supplies quantity

Cart-line quantity determines which bulk tier applies and whether MOQ/increments are met.

What comes out

Applied offers

Which discounts applied, and what each one did to the price. RAOS-0007 quote tokens bind this array.

Suppressed offers

Discounts that were evaluated but didn't win — with the reason why. An agent can show buyers exactly why their expected discount didn't apply.

Reason code registry

Every pricing decision is machine-readable. Two namespaces, seven codes.

CodeMeaningSeverityResolvable?
MEMBER_PRICE_APPLIEDMember pricing applied to this buyer.INFO
Advisory
TEASER_LOCKEDGuest or unqualified buyer sees a teaser price but cannot add to cart. Addability is blocked by RAOS-0001 eligibility.CONDITION
Upgrade membership tier
BULK_TIER_APPLIEDA volume pricing tier was matched and applied.INFO
Advisory
BELOW_MOQQuantity is below the minimum order quantity. Cannot proceed.BLOCK
Increase quantity to MOQ
QUANTITY_INCREMENT_MISMATCHQuantity is not a multiple of the required increment.BLOCK
Adjust quantity to a valid increment
PURCHASE_LIMIT_EXCEEDEDQuantity exceeds the per-order purchase limit for this pricing configuration.BLOCK
Reduce quantity to within the limit
CALL_FOR_PRICEPrice is not published — must be requested via the merchant. Distinct from a zero ($0) price.CONDITION
Submit intent capture (RAOS-0013 forward-ref)

How the price is computed

Start with the base price. Apply member pricing first (priority 10), then bulk tier pricing (priority 20) — bulk overrides member when the quantity threshold is met. Promo pricing (priority 30) overrides all. Each override records the previous price as a suppressed offer so agents can always explain the price history.

Real merchant scenarios

Sara's Boutique — member teaser (DTC)

Guest

TEASER_LOCKED (CONDITION) — shows $28 teaser, cannot add

Member tier: gold

MEMBER_PRICE_APPLIED (INFO) — proceeds

Member tier: none

TEASER_LOCKED — tier not met

Atlas Wholesale — MOQ + tier boundary (B2B)

qty 5

BELOW_MOQ (BLOCK, min=10)

qty 7

BELOW_MOQ + QUANTITY_INCREMENT_MISMATCH (both BLOCK)

qty 10 (exact MOQ)

BULK_TIER_APPLIED — tier boundary exactly met (>=)

qty 50 (next boundary)

BULK_TIER_APPLIED — higher tier

qty 100

BULK_TIER_APPLIED — max volume tier

Fresh Corner Market — member price + rounding (Grocery)

Guest

Base price — no rounding needed

Member / gold

MEMBER_PRICE_APPLIED — $3.995 rounds to $4.00 (half-up)

Open questions

These are genuine forks. Tell me I'm wrong.

#1Member price higher than bulk tier: last-wins or best-for-buyer?

When a gold member's member price ($58) is lower than the applicable bulk tier ($60), bulk still wins today because it runs second (last-wins). Should bulk override member when its price is worse for the buyer?

Option C: defer to the RAOS-0006 stacking ladder, where member and bulk become named ladder entries with merchant-declared priorities. Until then, last-wins is documented behavior with the suppressed offer visible in the payload.

#2`callForPrice` and eligibility interaction

When callForPrice is true, the pricing stage emits CALL_FOR_PRICE (CONDITION). Should eligibility (RAOS-0001) also signal call-for-price, or is it strictly the pricing stage's concern?

Currently pricing-only. Open for comment.

#3`purchaseLimit` scope: per-order vs per-customer

This spec defines purchaseLimit as a per-order (cart) limit, not a cumulative per-customer limit across multiple orders. Should it be renamed perOrderLimit to avoid confusion with RAOS-0011's per-customer limits?

Renaming to `perOrderLimit` would be more precise but is a breaking change to the config shape. Open for comment — currently kept as `purchaseLimit` with the scope documented explicitly.

Have a view on any of these?

The member-vs-bulk precedence question (§9.1) matters most if you run a wholesale or grocery operation. Email me or reply on the build-log post.

rikbanerjee007@gmail.com

Why start here

Pricing is the most visible merchant-reasoning gap after eligibility. An agent that can't explain “why $X” is not useful for wholesale buyers negotiating or loyalty members expecting their discount. More importantly, RAOS-0007 (Quote Integrity) requires a stable AppliedOffer shape to bind in the quote token — shipping that shape here, before 0007, ensures those specs are additive evolutions rather than breaking changes.