{
  "reference_version": "1.0.0",
  "ucp_version": "v2026-08-25",
  "scenario": "synthetic-shopify-merchant-fixed-cart",
  "data_classification": "stabile_operational",
  "generated_at": "2026-09-23T00:00:00Z",
  "contains_synthetic_data": true,
  "ucp_sources": {
    "loyalty_spec": "https://ucp.dev/2026-08-25/specification/common/extensions/loyalty",
    "loyalty_schema": "https://ucp.dev/2026-08-25/schemas/common/loyalty.json",
    "release": "https://github.com/Universal-Commerce-Protocol/ucp/releases/tag/v2026-08-25",
    "loyalty_pr": "https://github.com/Universal-Commerce-Protocol/ucp/pull/340"
  },
  "title": "Stabile reward lifecycle (not standardized by the UCP loyalty extension)",
  "ucp_status": "not standardized",
  "statement": "Reward codes, point holds, and settlement are StabileRewards and Shopify operational behavior. They are published as implementation evidence and standards feedback, not as a UCP operation.",
  "events": [
    {
      "step": "quote_options",
      "surface": "MCP get_reward_options (buyer-authorized)",
      "source": "loyalty.js buildRewardOptions()",
      "result": {
        "option_id": "burn_500",
        "value_amount": 5,
        "points_required": 500,
        "points_earned_if_redeemed": 335,
        "points_earned_if_not_redeemed": 360,
        "net_value": 4.75,
        "explanation": "Saves $5.00 now; this order then earns 335 points instead of 360 (-25 points, about $0.25 in future point value) — a net gain of $4.75."
      },
      "points_held_delta": 0,
      "points_settled_delta": 0
    },
    {
      "step": "issue",
      "surface": "MCP redeem_reward (idempotency_key: \"k-synthetic-1\")",
      "result": {
        "status": "pending_settlement",
        "reward_code": "<redacted:reward_code>",
        "replayed": false
      },
      "points_held_delta": 500,
      "points_settled_delta": 0,
      "note": "A real Shopify discount code is usable immediately. Points are held, not spent."
    },
    {
      "step": "idempotent_retry",
      "surface": "MCP redeem_reward (same idempotency_key)",
      "result": {
        "status": "pending_settlement",
        "reward_code": "<redacted:reward_code>",
        "replayed": true
      },
      "points_held_delta": 0,
      "points_settled_delta": 0,
      "note": "The original code is returned; no second hold is created."
    },
    {
      "step": "existing_code_selection",
      "surface": "MCP redeem_reward (new key, unused equal-or-better code exists)",
      "result": {
        "status": "existing_reward",
        "reward_code": "<redacted:reward_code>",
        "reused_existing_reward": true
      },
      "points_held_delta": 0,
      "points_settled_delta": 0,
      "note": "Shopify allows one discount code per order by default, so minting a second, unstackable code would spend points the shopper cannot use."
    },
    {
      "step": "settlement",
      "trigger": "Shopify orders/create webhook carrying the code",
      "source": "rewards.js processRewardRedemptionFromOrder()",
      "result": {
        "status": "redeemed",
        "discount_code_deactivated": true
      },
      "points_held_delta": -500,
      "points_settled_delta": -500,
      "note": "Settlement is keyed on the reward code, so a retried webhook does not deduct twice."
    },
    {
      "step": "expiry_release",
      "trigger": "Code unused for 180 days (alternative path)",
      "source": "loyalty.js computeAgentHolds()",
      "result": {
        "status": "expired"
      },
      "points_held_delta": -500,
      "points_settled_delta": 0,
      "note": "An expired hold stops counting against available points; no sweep job is needed."
    },
    {
      "step": "balance_unavailable",
      "surface": "MCP get_reward_options when the balance lookup fails",
      "result": {
        "balance_known": false,
        "options": [],
        "points_available": null
      },
      "points_held_delta": 0,
      "points_settled_delta": 0,
      "note": "Unknown is reported as unknown, never as zero points."
    }
  ],
  "known_gaps": [
    {
      "id": "GAP_CANCELLED_ORDER_RELEASE",
      "detail": "Points settle on orders/create. There is no orders/cancelled handling today, so points spent on an order that is later cancelled or refunded are not automatically returned."
    },
    {
      "id": "GAP_DISCOUNT_COMBINATION",
      "detail": "Stacking with other discount codes follows the Shopify discount's combination settings; the reference does not model merchant-specific combination rules."
    }
  ]
}
