{
 "_about": "spck.dev UCP conformance coverage \u2014 every normative MUST accounted as CHECK (kill-rate-validated), EXEMPT (documented), or GAP. Generated by conformance/coverage/matrix.py --json; the `coverage` CI gate fails if this file is stale or coverage regresses.",
 "spec_repo": "Universal-Commerce-Protocol/ucp",
 "spec_pins": {
  "2026-01-11": "806a5b74d3a5a918206844ea980450233b38664e",
  "2026-01-23": "dcf7eac71fc370dcc8768fcdbc5aa737037cca05",
  "2026-04-08": "a2d8bf0b8f5a6fc790f677899c2c7da0684fe33d"
 },
 "versions": {
  "2026-01-11": {
   "musts": 178,
   "check": 117,
   "exempt": 38,
   "gap": 23,
   "accounted_pct": 87,
   "gap_by_testability": {
    "manual": 2,
    "needs-receiver": 21
   },
   "areas": [
    {
     "area": "checkout-lifecycle",
     "check": 22,
     "exempt": 3,
     "gap": 3,
     "gap_needs-receiver": 3,
     "musts": 28
    },
    {
     "area": "discount-consent-identity",
     "check": 25,
     "exempt": 4,
     "musts": 29
    },
    {
     "area": "discovery-negotiation",
     "check": 16,
     "exempt": 11,
     "gap": 3,
     "gap_needs-receiver": 3,
     "musts": 30
    },
    {
     "area": "errors-validation-security",
     "check": 20,
     "gap": 1,
     "gap_manual": 1,
     "musts": 21
    },
    {
     "area": "fulfillment",
     "check": 7,
     "exempt": 5,
     "gap": 1,
     "gap_manual": 1,
     "musts": 13
    },
    {
     "area": "order",
     "check": 14,
     "exempt": 2,
     "gap": 2,
     "gap_needs-receiver": 2,
     "musts": 18
    },
    {
     "area": "overview",
     "exempt": 1,
     "musts": 1
    },
    {
     "area": "payment",
     "check": 13,
     "exempt": 11,
     "gap": 11,
     "gap_needs-receiver": 11,
     "musts": 35
    },
    {
     "area": "transports",
     "exempt": 1,
     "gap": 2,
     "gap_needs-receiver": 2,
     "musts": 3
    }
   ],
   "rows": [
    {
     "id": "A2A-001",
     "area": "transports",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "gap",
     "requirement": "The A2A business agent MUST return the checkout object inside an A2A Message DataPart keyed `a2a.ucp.checkout`",
     "source": "ucp:docs/specification/checkout-a2a.md#L141",
     "covered_by": []
    },
    {
     "id": "CHK-001",
     "area": "checkout-lifecycle",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Create Checkout initiates a new checkout session via POST /checkout-sessions",
     "source": "ucp:docs/specification/checkout-rest.md#L48",
     "covered_by": [
      "merchant_checks.py"
     ]
    },
    {
     "id": "CHK-002",
     "area": "checkout-lifecycle",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Get Checkout retrieves current state via GET /checkout-sessions/{id}",
     "source": "ucp:docs/specification/checkout-rest.md#L49",
     "covered_by": [
      "merchant_checks.py"
     ]
    },
    {
     "id": "CHK-003",
     "area": "checkout-lifecycle",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Update Checkout updates a session via PUT /checkout-sessions/{id}",
     "source": "ucp:docs/specification/checkout-rest.md#L50",
     "covered_by": [
      "area_lifecycle2.py",
      "merchant_checks.py"
     ]
    },
    {
     "id": "CHK-004",
     "area": "checkout-lifecycle",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Complete Checkout places the order via POST /checkout-sessions/{id}/complete",
     "source": "ucp:docs/specification/checkout-rest.md#L51",
     "covered_by": [
      "merchant_checks.py"
     ]
    },
    {
     "id": "CHK-005",
     "area": "checkout-lifecycle",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Cancel Checkout cancels a session via POST /checkout-sessions/{id}/cancel",
     "source": "ucp:docs/specification/checkout-rest.md#L52",
     "covered_by": [
      "merchant_checks.py"
     ]
    },
    {
     "id": "CHK-006",
     "area": "checkout-lifecycle",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "Update Checkout performs a full replacement of the checkout resource; the platform is REQUIRED to send the entire checkout resource",
     "source": "ucp:docs/specification/checkout.md#L318",
     "covered_by": [
      "area_lifecycle2.py"
     ]
    },
    {
     "id": "CHK-007",
     "area": "checkout-lifecycle",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "Each PUT replaces the entire session, so clients must include all previously set fields they wish to retain",
     "source": "ucp:docs/specification/checkout-rest.md#L188",
     "covered_by": [
      "area_lifecycle2.py"
     ]
    },
    {
     "id": "CHK-008",
     "area": "checkout-lifecycle",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Complete Checkout response is the checkout object with the order field populated (order provides id and permalink_url)",
     "source": "ucp:docs/specification/checkout.md#L329",
     "covered_by": [
      "merchant_checks.py"
     ]
    },
    {
     "id": "CHK-012",
     "area": "checkout-lifecycle",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "After a checkout session reaches the state completed it is considered immutable",
     "source": "ucp:docs/specification/checkout.md#L273",
     "covered_by": [
      "area_lifecycle2.py",
      "merchant_checks.py"
     ]
    },
    {
     "id": "CHK-013",
     "area": "checkout-lifecycle",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Status field enum is constrained to the six lifecycle values",
     "source": "ucp:source/schemas/shopping/checkout.json#L65",
     "covered_by": [
      "area_lifecycle2.py"
     ]
    },
    {
     "id": "CHK-014",
     "area": "checkout-lifecycle",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Checkout response MUST include required top-level fields: ucp, id, line_items, status, currency, totals, links",
     "source": "ucp:source/schemas/shopping/checkout.json#L9",
     "covered_by": [
      "merchant_checks.py"
     ]
    },
    {
     "id": "CHK-015",
     "area": "checkout-lifecycle",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "gap",
     "requirement": "Checkout status indicates the current phase and required action; the business sets the status",
     "source": "ucp:docs/specification/checkout.md#L57",
     "covered_by": []
    },
    {
     "id": "CHK-017",
     "area": "checkout-lifecycle",
     "keyword": "MUST NOT",
     "testability": "testable",
     "status": "check",
     "requirement": "ucp envelope field is omitted on request bodies (ucp_request: omit)",
     "source": "ucp:source/schemas/shopping/checkout.json#L22",
     "covered_by": [
      "schema_check_01_11_01_23.py"
     ]
    },
    {
     "id": "CHK-020",
     "area": "checkout-lifecycle",
     "keyword": "MUST NOT",
     "testability": "testable",
     "status": "check",
     "requirement": "status, totals, messages, links, expires_at, continue_url, and order are response-only (ucp_request: omit) and omitted on requests",
     "source": "ucp:source/schemas/shopping/checkout.json#L54",
     "covered_by": [
      "schema_check_01_11_01_23.py"
     ]
    },
    {
     "id": "CHK-025",
     "area": "checkout-lifecycle",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "Businesses MUST provide continue_url when returning status = requires_escalation (Availability)",
     "source": "ucp:docs/specification/checkout.md#L207",
     "covered_by": [
      "merchant_checks_01_11_01_23_receiver.py"
     ]
    },
    {
     "id": "CHK-028",
     "area": "checkout-lifecycle",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "continue_url MUST be an absolute HTTPS URL",
     "source": "ucp:docs/specification/checkout.md#L215",
     "covered_by": [
      "merchant_checks_01_11_01_23_receiver.py"
     ]
    },
    {
     "id": "CHK-032",
     "area": "checkout-lifecycle",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Platform MUST use continue_url when checkout status is requires_escalation (Platform guideline)",
     "source": "ucp:docs/specification/checkout.md#L258",
     "covered_by": [],
     "exempt_reason": "Platform-side behavior MUST: on canceled the platform starts a new session if needed; the business duty (canceled is terminal/immutable) is checked by the cancel/immutability checks."
    },
    {
     "id": "CHK-035",
     "area": "checkout-lifecycle",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Business MUST send a confirmation email after the checkout has been completed",
     "source": "ucp:docs/specification/checkout.md#L265",
     "covered_by": [],
     "exempt_reason": "'Business MUST send a confirmation email after the checkout has been completed' (checkout.md Business responsibilities, L261@01-11 / L265@01-23) is dispatched on a channel the protocol does not define; no checkout response field evidences it, and a conformance run has no principled inbox oracle \u2014 non-receipt is indistinguishable from spam-filtering or asynchronous delay. [Scoped: at 2026-04-08 this id is the testable ucp_request:omit rule on checkout id, already covered by schema_check_04_08_lifecycle; the 04-08 twin of the email requirement is CHK-011.]"
    },
    {
     "id": "CHK-037",
     "area": "checkout-lifecycle",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "gap",
     "requirement": "Logic handling the checkout sessions MUST be deterministic",
     "source": "ucp:docs/specification/checkout.md#L267",
     "covered_by": []
    },
    {
     "id": "CHK-038",
     "area": "checkout-lifecycle",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "Business MUST provide continue_url when returning status = requires_escalation (Business guideline restatement)",
     "source": "ucp:docs/specification/checkout.md#L268",
     "covered_by": [
      "merchant_checks_01_11_01_23_receiver.py"
     ]
    },
    {
     "id": "CHK-039",
     "area": "checkout-lifecycle",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "gap",
     "requirement": "Business MUST include at least one message with severity: escalation when returning status = requires_escalation",
     "source": "ucp:docs/specification/checkout.md#L270",
     "covered_by": []
    },
    {
     "id": "CHK-050",
     "area": "checkout-lifecycle",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "All REST request and response bodies MUST be valid JSON per RFC 8259",
     "source": "ucp:docs/specification/checkout-rest.md#L36",
     "covered_by": [
      "merchant_checks_01_11_01_23.py"
     ]
    },
    {
     "id": "CHK-051",
     "area": "checkout-lifecycle",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "All REST endpoints MUST be served over HTTPS with minimum TLS version 1.3",
     "source": "ucp:docs/specification/checkout-rest.md#L41",
     "covered_by": [
      "tls_check_01_11_01_23.py"
     ]
    },
    {
     "id": "CHK-052",
     "area": "checkout-lifecycle",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "All requests MUST include the UCP-Agent header containing the platform profile URI (RFC 8941 dictionary syntax)",
     "source": "ucp:docs/specification/checkout-rest.md#L1211",
     "covered_by": [],
     "exempt_reason": "'All requests MUST include the UCP-Agent header containing the platform profile URI' (checkout-rest.md L1211-1213) binds the REQUEST AUTHOR \u2014 the platform \u2014 per the suite-wide F2 policy (adversarial review 2026-07-03, register note applied): the spec mandates no business-side rejection of header-less requests, so no merchant response can prove or refute the platform's compliance; the tester, being the platform, simply sends the header. [Scoped: at 2026-04-08 this id is an unrelated SHOULD (severity unrecoverable when no resource exists); the 04-08 twin of this header rule is CHK-046.]"
    },
    {
     "id": "DISC-001",
     "area": "discovery-negotiation",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "All capability and service names MUST use the format {reverse-domain}.{service}.{capability}",
     "source": "ucp:docs/specification/overview.md#L51",
     "covered_by": [
      "area_negotiation.py",
      "merchant_checks.py"
     ]
    },
    {
     "id": "DISC-002",
     "area": "discovery-negotiation",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "The spec and schema fields are REQUIRED for all capabilities",
     "source": "ucp:docs/specification/overview.md#L74",
     "covered_by": [
      "merchant_checks_01_11_01_23.py"
     ]
    },
    {
     "id": "DISC-003",
     "area": "discovery-negotiation",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "The origin of capability spec/schema URLs MUST match the namespace authority",
     "source": "ucp:docs/specification/overview.md#L75",
     "covered_by": [
      "merchant_checks_01_11_01_23.py"
     ]
    },
    {
     "id": "DISC-004",
     "area": "discovery-negotiation",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Platform MUST validate the spec-origin/namespace binding and SHOULD reject capabilities where the spec origin does not match the namespace authority",
     "source": "ucp:docs/specification/overview.md#L82",
     "covered_by": [],
     "exempt_reason": "'Platform MUST validate this binding and SHOULD reject capabilities where the spec origin does not match the namespace authority' (overview.md L82-83) is a platform-internal validation step over discovery data it already holds; a validating and a non-validating platform emit byte-identical traffic toward the business, so nothing the suite can elicit from the party under test reflects it \u2014 same subject-binding as the already-exempt NEG-007. [Scoped: at 2026-04-08 this id is 'reject profile URLs not served over HTTPS', testable and covered by merchant_checks_04_08_discovery.]"
    },
    {
     "id": "DISC-005",
     "area": "discovery-negotiation",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Vendors MUST use their own reverse-domain namespace for custom capabilities",
     "source": "ucp:docs/specification/overview.md#L94",
     "covered_by": [],
     "exempt_reason": "'Vendors MUST use their own reverse-domain namespace for custom capabilities' (overview.md Governance Model, L94-95) turns on OWNERSHIP: whether com.example.* is really 'their own' is a domain-registration/governance fact established by registrars and the UCP governing body's reservation of dev.ucp.*, not by any protocol message \u2014 a payload shows the namespace string but never who owns it. [Scoped: at 2026-04-08 this id is 'service endpoint MUST be a valid HTTPS URL', testable and covered by merchant_checks_04_08_discovery.]"
    },
    {
     "id": "DISC-006",
     "area": "discovery-negotiation",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Transport definitions MUST be thin: they declare method names and reference base schemas only",
     "source": "ucp:docs/specification/overview.md#L125",
     "covered_by": [],
     "exempt_reason": "'Transport definitions MUST be thin: they declare method names and reference base schemas only' (overview.md L125-126) binds the AUTHORS of capability transport-definition documents (OpenAPI/OpenRPC) \u2014 the namespace authority publishing the capability, a party distinct from the business or platform under test; conformance is established by document review, same as the already-exempt DISC-009 (its 04-08 twin). [Scoped: at 2026-04-08 this id is the platform's profile-URI-in-every-request duty \u2014 a different, requester-bound requirement left to its own accounting.]"
    },
    {
     "id": "DISC-007",
     "area": "discovery-negotiation",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "A service endpoint MUST be a valid URL with scheme (https)",
     "source": "ucp:docs/specification/overview.md#L152",
     "covered_by": [
      "merchant_checks.py"
     ]
    },
    {
     "id": "DISC-009",
     "area": "discovery-negotiation",
     "keyword": "MUST NOT",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Transport definitions MUST NOT enumerate fields or define payload shapes inline",
     "source": "ucp:docs/specification/overview.md#L191",
     "covered_by": [],
     "exempt_reason": "Binds authors of capability transport-definition documents (OpenAPI/OpenRPC, overview.md Requirements L191-192) \u2014 the namespace authority publishing the capability, a party distinct from the business or platform under test; no runtime message reflects how those documents were authored."
    },
    {
     "id": "DISC-010",
     "area": "discovery-negotiation",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Each extension schema MUST declare the types it introduces and how it modifies base types using allOf composition",
     "source": "ucp:docs/specification/overview.md#L192",
     "covered_by": [],
     "exempt_reason": "Binds extension-schema AUTHORS (overview.md L193-195): being 'self-describing' via allOf composition is a property of ecosystem schema documents published by the capability authority, not of any implementation behavior the suite can elicit from a business."
    },
    {
     "id": "DISC-011",
     "area": "discovery-negotiation",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Platforms MUST resolve schemas client-side by fetching and composing base schemas with active extension schemas",
     "source": "ucp:docs/specification/overview.md#L195",
     "covered_by": [],
     "exempt_reason": "Binds the platform (overview.md L196-197): client-side schema resolution (fetch + compose before requests) is unobservable from the business side \u2014 wire traffic from a resolving and a non-resolving client can be byte-identical."
    },
    {
     "id": "DISC-013",
     "area": "discovery-negotiation",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Discovery profile at /.well-known/ucp MUST return 200 OK with the expected version, capabilities, and payment handlers",
     "source": "ucp:source/schemas/ucp.json#L24",
     "covered_by": [
      "merchant_checks.py"
     ]
    },
    {
     "id": "DISC-014",
     "area": "discovery-negotiation",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "All spec and schema URLs advertised in the discovery profile MUST be valid and resolvable (return 200 with valid HTML/JSON)",
     "source": "ucp:docs/specification/overview.md#L74",
     "covered_by": [
      "merchant_checks_01_11_01_23_disc.py"
     ]
    },
    {
     "id": "DSC-001",
     "area": "discount-consent-identity",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Submitting discounts.codes replaces any previously submitted codes (replacement semantics).",
     "source": "ucp:docs/specification/discount.md#L125",
     "covered_by": [
      "merchant_checks_01_11_01_23.py"
     ]
    },
    {
     "id": "DSC-002",
     "area": "discount-consent-identity",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Sending an empty codes array clears all discount codes.",
     "source": "ucp:docs/specification/discount.md#L126",
     "covered_by": [
      "merchant_checks.py"
     ]
    },
    {
     "id": "DSC-003",
     "area": "discount-consent-identity",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Discount codes are matched case-insensitively by the business.",
     "source": "ucp:docs/specification/discount.md#L127",
     "covered_by": [
      "merchant_checks_01_23.py"
     ]
    },
    {
     "id": "DSC-004",
     "area": "discount-consent-identity",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "discounts.applied contains all active discounts (code-based and automatic) in the response.",
     "source": "ucp:docs/specification/discount.md#L131",
     "covered_by": [
      "area_discount.py",
      "merchant_checks.py"
     ]
    },
    {
     "id": "DSC-005",
     "area": "discount-consent-identity",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "When multiple valid discount codes are submitted, all are applied (accept-both) and appear in discounts.applied.",
     "source": "ucp:docs/specification/discount.md#L131",
     "covered_by": [
      "area_discount.py",
      "merchant_checks.py"
     ]
    },
    {
     "id": "DSC-006",
     "area": "discount-consent-identity",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "When a submitted discount code cannot be applied, the business communicates this via the messages[] array.",
     "source": "ucp:docs/specification/discount.md#L137",
     "covered_by": [
      "area_discount.py",
      "merchant_checks.py"
     ]
    },
    {
     "id": "DSC-007",
     "area": "discount-consent-identity",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "A rejected discount code still appears in discounts.codes (echoed back) but not in discounts.applied.",
     "source": "ucp:docs/specification/discount.md#L314",
     "covered_by": [
      "area_discount.py",
      "merchant_checks.py"
     ]
    },
    {
     "id": "DSC-010",
     "area": "discount-consent-identity",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Automatic discounts appear in discounts.applied with automatic: true and no code field; they are applied without platform action and cannot be removed by the platform.",
     "source": "ucp:docs/specification/discount.md#L175",
     "covered_by": [
      "merchant_checks_01_23.py"
     ]
    },
    {
     "id": "DSC-011",
     "area": "discount-consent-identity",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "A fixed-amount discount code decreases the total correctly and surfaces applied discount details (code and amount).",
     "source": "ucp:docs/specification/discount.md#L131",
     "covered_by": [
      "area_discount.py",
      "merchant_checks.py"
     ]
    },
    {
     "id": "DSC-012",
     "area": "discount-consent-identity",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "An applied_discount must include title and amount; amount is a non-negative integer in minor currency units.",
     "source": "ucp:source/schemas/shopping/discount.json#L29",
     "covered_by": [
      "merchant_checks.py"
     ]
    },
    {
     "id": "DSC-013",
     "area": "discount-consent-identity",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "applied_discount.method, when present, must be one of 'each' or 'across'.",
     "source": "ucp:source/schemas/shopping/discount.json#L51",
     "covered_by": [
      "schema_check_01_11_01_23.py"
     ]
    },
    {
     "id": "DSC-014",
     "area": "discount-consent-identity",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "An allocation must include path and amount; allocation amount is a non-negative integer in minor currency units.",
     "source": "ucp:source/schemas/shopping/discount.json#L12",
     "covered_by": [
      "schema_check_01_11_01_23.py"
     ]
    },
    {
     "id": "DSC-016",
     "area": "discount-consent-identity",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "The sum of allocations[].amount must equal the applied_discount.amount.",
     "source": "ucp:docs/specification/discount.md#L117",
     "covered_by": [
      "merchant_checks_01_11_01_23.py"
     ]
    },
    {
     "id": "DSC-017",
     "area": "discount-consent-identity",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "All discount amounts are positive integers in minor currency units; when presenting totals, discount types are displayed as subtractive.",
     "source": "ucp:docs/specification/discount.md#L205",
     "covered_by": [
      "merchant_checks_01_11_01_23.py"
     ]
    },
    {
     "id": "DSC-018",
     "area": "discount-consent-identity",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "totals[type=items_discount].amount must equal sum(line_items[].discount).",
     "source": "ucp:docs/specification/discount.md#L199",
     "covered_by": [
      "merchant_checks_01_23.py"
     ]
    },
    {
     "id": "DSC-019",
     "area": "discount-consent-identity",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Buyer consent is communicated as boolean states within the buyer.consent object (analytics, preferences, marketing, sale_of_data) on create_checkout and update_checkout.",
     "source": "ucp:docs/specification/buyer-consent.md#L29",
     "covered_by": [
      "merchant_checks_01_11_01_23.py"
     ]
    },
    {
     "id": "DSC-020",
     "area": "discount-consent-identity",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Each buyer consent state (analytics, preferences, marketing, sale_of_data), when present, must be a boolean.",
     "source": "ucp:source/schemas/shopping/buyer_consent.json#L14",
     "covered_by": [
      "schema_check_01_11_01_23.py"
     ]
    },
    {
     "id": "DSC-022",
     "area": "discount-consent-identity",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Consent is declarative: the protocol communicates consent but does not enforce it, and legal compliance remains the business's responsibility.",
     "source": "ucp:docs/specification/buyer-consent.md#L132",
     "covered_by": [],
     "exempt_reason": "'Consent is declarative - the protocol communicates consent, it does not enforce it' and 'legal compliance remains the business's responsibility' (buyer-consent.md Security & Privacy Considerations, L131-133) assert a non-enforcement property of the protocol plus a LEGAL allocation of responsibility; whether a business actually meets its consent-law obligations is established by audit/regulation entirely outside any protocol message. [Scoped: at 2026-04-08 this id is the testable allocations-sum invariant, already covered by merchant_checks_04_08.]"
    },
    {
     "id": "ERR-001",
     "area": "errors-validation-security",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "An error message MUST include the fields type, code, content, and severity (all four are required on a Message Error).",
     "source": "ucp:source/schemas/shopping/types/message_error.json#L6-L11",
     "covered_by": [
      "schema_check_01_11_01_23.py"
     ]
    },
    {
     "id": "ERR-002",
     "area": "errors-validation-security",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "The error message type discriminator MUST be the constant string \"error\".",
     "source": "ucp:source/schemas/shopping/types/message_error.json#L13-L16",
     "covered_by": [
      "schema_check_01_11_01_23.py"
     ]
    },
    {
     "id": "ERR-003",
     "area": "errors-validation-security",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "The error code field MUST be a string; defined values include missing, invalid, out_of_stock, payment_declined, requires_sign_in, requires_3ds, requires_identity_linking, with freeform codes also allowed.",
     "source": "ucp:source/schemas/shopping/types/message_error.json#L18-L21",
     "covered_by": [
      "schema_check_01_11_01_23.py"
     ]
    },
    {
     "id": "ERR-004",
     "area": "errors-validation-security",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "The error severity field MUST be one of exactly three enum values: recoverable, requires_buyer_input, requires_buyer_review.",
     "source": "ucp:source/schemas/shopping/types/message_error.json#L39-L46",
     "covered_by": [
      "schema_check_01_11_01_23.py"
     ]
    },
    {
     "id": "ERR-005",
     "area": "errors-validation-security",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Errors with requires_* severity contribute to status requires_escalation.",
     "source": "ucp:source/schemas/shopping/types/message_error.json#L46",
     "covered_by": [
      "merchant_checks_01_11_01_23.py"
     ]
    },
    {
     "id": "ERR-006",
     "area": "errors-validation-security",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "REST implementations MUST use standard HTTP status codes (e.g., 200, 201, 400, 401, 500) for responses including errors.",
     "source": "ucp:docs/specification/overview.md#L951",
     "covered_by": [
      "merchant_checks_01_11_01_23.py"
     ]
    },
    {
     "id": "ERR-007",
     "area": "errors-validation-security",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "If negotiation fails, businesses MUST return an error response (status requires_escalation with a type:error message, e.g. version_unsupported, severity requires_buyer_input).",
     "source": "ucp:docs/specification/overview.md#L489",
     "covered_by": [
      "merchant_checks_01_11_01_23.py"
     ]
    },
    {
     "id": "ERR-008",
     "area": "errors-validation-security",
     "keyword": "MUST",
     "testability": "manual",
     "status": "gap",
     "requirement": "Businesses MUST include at least one message with severity escalation when returning status requires_escalation.",
     "source": "ucp:docs/specification/checkout.md#L270-L271",
     "covered_by": []
    },
    {
     "id": "FUL-003",
     "area": "fulfillment",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "A fulfillment_method MUST include id, type, and line_item_ids",
     "source": "ucp:source/schemas/shopping/types/fulfillment_method.json#L7",
     "covered_by": [
      "area_fulfillment.py",
      "merchant_checks.py"
     ]
    },
    {
     "id": "FUL-004",
     "area": "fulfillment",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "fulfillment_method.type MUST be one of shipping or pickup",
     "source": "ucp:source/schemas/shopping/types/fulfillment_method.json#L17",
     "covered_by": [
      "area_fulfillment.py"
     ]
    },
    {
     "id": "FUL-007",
     "area": "fulfillment",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "A fulfillment_group MUST include id and line_item_ids",
     "source": "ucp:source/schemas/shopping/types/fulfillment_group.json#L7",
     "covered_by": [
      "area_fulfillment.py"
     ]
    },
    {
     "id": "FUL-008",
     "area": "fulfillment",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "A fulfillment_option MUST include id, title, and totals",
     "source": "ucp:source/schemas/shopping/types/fulfillment_option.json#L8",
     "covered_by": [
      "area_fulfillment.py",
      "merchant_checks.py"
     ]
    },
    {
     "id": "FUL-009",
     "area": "fulfillment",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "options[].title is the required primary label that distinguishes the option from its siblings",
     "source": "ucp:docs/specification/fulfillment.md#L170",
     "covered_by": [
      "schema_check_01_11_01_23.py"
     ]
    },
    {
     "id": "FUL-010",
     "area": "fulfillment",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "options[].title MUST distinguish this option from its siblings",
     "source": "ucp:docs/specification/fulfillment.md#L179",
     "covered_by": [],
     "exempt_reason": "Whether a title lets a human buyer tell one option from its siblings (fulfillment.md Business Responsibilities) is a comprehension judgment: string uniqueness neither proves it (trivially-differing strings do not distinguish) nor is literally required by it."
    },
    {
     "id": "FUL-012",
     "area": "fulfillment",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "options[].title MUST be sufficient for buyer decision if description is absent",
     "source": "ucp:docs/specification/fulfillment.md#L181",
     "covered_by": [],
     "exempt_reason": "'Sufficient for buyer decision if description is absent' is a human-adequacy judgment about label content; no mechanical predicate separates sufficient titles from insufficient ones."
    },
    {
     "id": "FUL-013",
     "area": "fulfillment",
     "keyword": "MUST NOT",
     "testability": "manual",
     "status": "exempt",
     "requirement": "options[].description MUST NOT repeat title or total; provides supplementary context only",
     "source": "ucp:docs/specification/fulfillment.md#L185",
     "covered_by": [],
     "exempt_reason": "'Repeat' covers paraphrase, not just string equality: deciding whether a description merely restates title/total versus supplying supplementary context is linguistic judgment beyond any exact-match test."
    },
    {
     "id": "FUL-017",
     "area": "fulfillment",
     "keyword": "MUST",
     "testability": "manual",
     "status": "gap",
     "requirement": "options[].total is the required price in minor units and may be null if not yet available",
     "source": "ucp:docs/specification/fulfillment.md#L172",
     "covered_by": []
    },
    {
     "id": "FUL-018",
     "area": "fulfillment",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "available_methods[].description MUST be a standalone sentence explaining what, when, and where",
     "source": "ucp:docs/specification/fulfillment.md#L192",
     "covered_by": [],
     "exempt_reason": "Whether a description is 'a standalone sentence explaining what, when, and where' is a natural-language completeness judgment (does it actually explain all three?) that a parser cannot arbitrate."
    },
    {
     "id": "FUL-026",
     "area": "fulfillment",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "When supports_multi_group is false (default), the business MUST consolidate all items into a single group per method",
     "source": "ucp:docs/specification/fulfillment.md#L326",
     "covered_by": [
      "merchant_checks.py"
     ]
    },
    {
     "id": "FUL-029",
     "area": "fulfillment",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Extensions that add new fulfillment method types MUST add an extension schema that adds the method to the fulfillment_method type enum and adds corresponding business config options",
     "source": "ucp:docs/specification/fulfillment.md#L339",
     "covered_by": [],
     "exempt_reason": "Binds authors of FUTURE fulfillment extensions (fulfillment.md 'Adding New Methods'): the obligation attaches to a hypothetical extension schema document, not to any behavior of the business or platform under test."
    },
    {
     "id": "FUL-030",
     "area": "fulfillment",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "A shipping_destination MUST include an id (it is a postal_address plus a required id)",
     "source": "ucp:source/schemas/shopping/types/shipping_destination.json#L21",
     "covered_by": [
      "area_fulfillment.py"
     ]
    },
    {
     "id": "IDL-001",
     "area": "discount-consent-identity",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Platforms must authenticate using their client_id and client_secret through HTTP Basic Authentication when exchanging codes for tokens.",
     "source": "ucp:docs/specification/identity-linking.md#L42",
     "covered_by": [],
     "exempt_reason": "'Platforms must authenticate using their client_id and client_secret through HTTP Basic Authentication when exchanging codes for tokens' (identity-linking.md L42, RFC 6749 2.3.1 / RFC 7617) binds the PLATFORM as the token-request author, not the business under test; HTTP Basic client authentication is the platform's request authoring and no merchant response can prove or refute it. The business-side enforcement duty is IDL-007 (checked). Requester-bound per suite policy F2; precedent CAT-034. [Scoped: at 2026-04-08 this id is a different, already-covered requirement.]"
    },
    {
     "id": "IDL-002",
     "area": "discount-consent-identity",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Platforms must implement the OAuth 2.0 Authorization Code flow (RFC 6749 4.1) as the primary linking mechanism.",
     "source": "ucp:docs/specification/identity-linking.md#L52",
     "covered_by": [],
     "exempt_reason": "'Platforms must implement the OAuth 2.0 Authorization Code flow (RFC 6749 4.1) as the primary linking mechanism' (identity-linking.md L52) binds the PLATFORM's client implementation \u2014 a flow choice authored by the platform, not the business; no merchant response can prove or refute it. Requester-bound per policy F2; precedent CAT-034."
    },
    {
     "id": "IDL-005",
     "area": "discount-consent-identity",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Businesses must implement OAuth 2.0 (RFC 6749).",
     "source": "ucp:docs/specification/identity-linking.md#L72",
     "covered_by": [
      "merchant_checks_01_11_01_23_oauth.py"
     ]
    },
    {
     "id": "IDL-006",
     "area": "discount-consent-identity",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Businesses must adhere to RFC 8414 to declare the location of their OAuth 2.0 endpoints at /.well-known/oauth-authorization-server.",
     "source": "ucp:docs/specification/identity-linking.md#L74",
     "covered_by": [
      "merchant_checks_01_11_01_23_oauth.py"
     ]
    },
    {
     "id": "IDL-007",
     "area": "discount-consent-identity",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Businesses must enforce Client Authentication at the Token Endpoint.",
     "source": "ucp:docs/specification/identity-linking.md#L83",
     "covered_by": [
      "merchant_checks_01_11_01_23_oauth.py"
     ]
    },
    {
     "id": "IDL-008",
     "area": "discount-consent-identity",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Businesses must provide an account creation flow if the user does not already have an account.",
     "source": "ucp:docs/specification/identity-linking.md#L84",
     "covered_by": [],
     "exempt_reason": "'MUST provide an account creation flow if the user does not already have an account' (identity-linking.md L84-85) requires a human-facing enrollment journey on the business's own web property, outside every protocol message: a conformance run cannot enact a real new user signing up, and no identity-linking response field evidences that such a flow exists or works. [Scoped: at 2026-04-08 this id is the platform's WWW-Authenticate challenge-processing duty \u2014 an unrelated needs-oauth requirement left to its own accounting.]"
    },
    {
     "id": "IDL-009",
     "area": "discount-consent-identity",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Businesses must support standard UCP scopes, granting tokens permission to all associated Operations for a given resource.",
     "source": "ucp:docs/specification/identity-linking.md#L86",
     "covered_by": [
      "merchant_checks_01_11_01_23_oauth.py"
     ]
    },
    {
     "id": "IDL-010",
     "area": "discount-consent-identity",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Businesses must implement standard Token Revocation as defined in RFC 7009.",
     "source": "ucp:docs/specification/identity-linking.md#L94",
     "covered_by": [
      "merchant_checks_01_11_01_23_oauth.py"
     ]
    },
    {
     "id": "IDL-011",
     "area": "discount-consent-identity",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Businesses must revoke the specified token and should recursively revoke all associated tokens (revoking a refresh_token must also immediately revoke all access_tokens issued from it).",
     "source": "ucp:docs/specification/identity-linking.md#L96",
     "covered_by": [
      "merchant_checks_01_11_01_23_oauth.py"
     ]
    },
    {
     "id": "IDL-012",
     "area": "discount-consent-identity",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Businesses must support revocation requests authenticated with the same client credentials used for the token endpoint.",
     "source": "ucp:docs/specification/identity-linking.md#L99",
     "covered_by": [
      "merchant_checks_01_11_01_23_oauth.py"
     ]
    },
    {
     "id": "IDL-013",
     "area": "discount-consent-identity",
     "keyword": "MUST",
     "testability": "needs-oauth",
     "status": "check",
     "requirement": "A scope covering a capability must grant access to all operations associated with that capability (e.g., ucp:scopes:checkout_session must grant Get, Create, Update, Delete, Cancel, Complete).",
     "source": "ucp:docs/specification/identity-linking.md#L129",
     "covered_by": [
      "merchant_checks_01_11_01_23_oauth.py"
     ]
    },
    {
     "id": "IDM-002",
     "area": "checkout-lifecycle",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "When an Idempotency-Key is provided, the server MUST store the key with the operation result for at least 24 hours",
     "source": "ucp:docs/specification/checkout-rest.md#L1292",
     "covered_by": [
      "merchant_checks_01_11_01_23_receiver.py"
     ]
    },
    {
     "id": "IDM-003",
     "area": "checkout-lifecycle",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "When an Idempotency-Key is provided, the server MUST return the cached result for duplicate keys",
     "source": "ucp:docs/specification/checkout-rest.md#L1293",
     "covered_by": [
      "area_lifecycle2.py"
     ]
    },
    {
     "id": "IDM-004",
     "area": "checkout-lifecycle",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "When an Idempotency-Key is provided, the server MUST return 409 Conflict if the key is reused with different parameters",
     "source": "ucp:docs/specification/checkout-rest.md#L1294",
     "covered_by": [
      "merchant_checks.py"
     ]
    },
    {
     "id": "IDM-005",
     "area": "checkout-lifecycle",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "409 Conflict indicates the request could not be completed due to a conflict (e.g., idempotent key reuse)",
     "source": "ucp:docs/specification/checkout-rest.md#L1311",
     "covered_by": [
      "merchant_checks_01_11_01_23.py"
     ]
    },
    {
     "id": "MCP-001",
     "area": "transports",
     "keyword": "MUST NOT",
     "testability": "manual",
     "status": "exempt",
     "requirement": "MCP request payloads MUST NOT carry an `id` inside the `checkout` object; the target resource is identified by a top-level `id` parameter",
     "source": "ucp:docs/specification/checkout-mcp.md#L95",
     "covered_by": [],
     "exempt_reason": "Client/platform-bound obligation surfaced by the register-completeness pass: MCP request payloads MUST NOT carry an `id` inside the `checkout` object; the target resource is identified by a top-level `id` pa \u2014 binds the platform/agent (or is a spec-authoring rule), not the merchant server under test; a merchant check would be fake coverage. Same treatment as the existing client-bound PAY exemptions. Version-scoped so it never collides with a version where this id names a checked requirement."
    },
    {
     "id": "MCP-002",
     "area": "transports",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "gap",
     "requirement": "A conforming MCP checkout endpoint MUST implement JSON-RPC 2.0, provide all core checkout tools, handle errors with UCP-specific codes embedded in the JSON-RPC error object, validate tool inputs against schemas, and support HTTP streaming",
     "source": "ucp:docs/specification/checkout-mcp.md#L597",
     "covered_by": []
    },
    {
     "id": "NEG-001",
     "area": "discovery-negotiation",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Platforms MUST communicate their profile URI with each request to enable capability negotiation",
     "source": "ucp:docs/specification/overview.md#L408",
     "covered_by": [],
     "exempt_reason": "'Platforms MUST communicate their profile URI with each request to enable capability negotiation' (overview.md L438-439@01-23 / L408@01-11) binds the PLATFORM's request authoring (the UCP-Agent header), not the business; the spec places no duty on the business to reject header-less requests, so no merchant response reflects it and the tester itself sends the header. Requester-bound per policy F2; precedent CHK-046/DISC-006@04-08. [Scoped: at 2026-04-08 this id is a different, already-covered requirement.]"
    },
    {
     "id": "NEG-002",
     "area": "discovery-negotiation",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "For HTTP transport, platforms MUST use Dictionary Structured Field syntax (RFC 8941) in the UCP-Agent header",
     "source": "ucp:docs/specification/overview.md#L411",
     "covered_by": [],
     "exempt_reason": "'For HTTP transport, platforms MUST use Dictionary Structured Field syntax (RFC 8941) in the UCP-Agent header' (overview.md L441@01-23 / L411@01-11) binds the PLATFORM: RFC 8941 header syntax is authored by the platform and is not observable as a business obligation. Requester-bound per policy F2; precedent CHK-046@04-08. [Scoped: at 2026-04-08 this id is a different, already-covered requirement.]"
    },
    {
     "id": "NEG-004",
     "area": "discovery-negotiation",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Platforms MUST include their profile URI in every request using the transport-appropriate mechanism",
     "source": "ucp:docs/specification/overview.md#L446",
     "covered_by": [],
     "exempt_reason": "'Platforms MUST include their profile URI in every request using the transport-appropriate mechanism' (overview.md L479@01-23 / L446@01-11) binds the PLATFORM's request authoring; profile-URI advertisement is the platform's duty and the tester satisfies it on every request. Requester-bound per policy F2; precedent DISC-006@04-08. [Scoped: at 2026-04-08 this id is a different, already-covered requirement.]"
    },
    {
     "id": "NEG-007",
     "area": "discovery-negotiation",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Platforms MUST validate that capability spec URI origins match namespace authorities",
     "source": "ucp:docs/specification/overview.md#L451",
     "covered_by": [],
     "exempt_reason": "Binds the platform, not the business under test (overview.md Platform Requirements): validating spec-URI origins against namespace authorities is an internal client step that surfaces in no message a conformance run can elicit from the merchant."
    },
    {
     "id": "NEG-008",
     "area": "discovery-negotiation",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Platforms MUST fetch and compose schemas for negotiated capabilities before making requests",
     "source": "ucp:docs/specification/overview.md#L453",
     "covered_by": [],
     "exempt_reason": "Binds the platform: fetching and composing schemas happens BEFORE requests are made (overview.md Platform Requirements); the business only sees the resulting traffic, which is identical whether or not the platform resolved schemas."
    },
    {
     "id": "NEG-009",
     "area": "discovery-negotiation",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "gap",
     "requirement": "Upon receiving a request with a platform profile URI, businesses MUST fetch and validate the platform profile unless already cached",
     "source": "ucp:docs/specification/overview.md#L459",
     "covered_by": []
    },
    {
     "id": "NEG-010",
     "area": "discovery-negotiation",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "gap",
     "requirement": "Businesses MUST compute the intersection of platform and business capabilities",
     "source": "ucp:docs/specification/overview.md#L461",
     "covered_by": []
    },
    {
     "id": "NEG-011",
     "area": "discovery-negotiation",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "gap",
     "requirement": "Extensions without their parent capability in the intersection MUST be excluded",
     "source": "ucp:docs/specification/overview.md#L464",
     "covered_by": []
    },
    {
     "id": "NEG-012",
     "area": "discovery-negotiation",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Businesses MUST include the ucp field in every response containing version and active capabilities",
     "source": "ucp:docs/specification/overview.md#L465",
     "covered_by": [
      "area_negotiation.py"
     ]
    },
    {
     "id": "NEG-013",
     "area": "discovery-negotiation",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "If negotiation fails, businesses MUST return an error response",
     "source": "ucp:docs/specification/overview.md#L489",
     "covered_by": [
      "merchant_checks_01_11_01_23.py"
     ]
    },
    {
     "id": "NEG-014",
     "area": "discovery-negotiation",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Businesses MUST validate the platform's version and determine compatibility",
     "source": "ucp:docs/specification/overview.md#L1068",
     "covered_by": [
      "merchant_checks_01_11_01_23.py"
     ]
    },
    {
     "id": "NEG-015",
     "area": "discovery-negotiation",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "If platform version <= business version, the business MUST process the request",
     "source": "ucp:docs/specification/overview.md#L1072",
     "covered_by": [
      "merchant_checks.py"
     ]
    },
    {
     "id": "NEG-016",
     "area": "discovery-negotiation",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "If platform version > business version, the business MUST return version_unsupported error",
     "source": "ucp:docs/specification/overview.md#L1074",
     "covered_by": [
      "area_negotiation.py"
     ]
    },
    {
     "id": "NEG-017",
     "area": "discovery-negotiation",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Businesses MUST include the version used for processing in every response",
     "source": "ucp:docs/specification/overview.md#L1076",
     "covered_by": [
      "area_negotiation.py"
     ]
    },
    {
     "id": "NEG-018",
     "area": "discovery-negotiation",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "All UCP communication MUST occur over HTTPS",
     "source": "ucp:docs/specification/overview.md#L992",
     "covered_by": [
      "merchant_checks_01_11_01_23.py"
     ]
    },
    {
     "id": "NEG-019",
     "area": "discovery-negotiation",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "REST requests and responses MUST use Content-Type application/json",
     "source": "ucp:docs/specification/overview.md#L947",
     "covered_by": [
      "area_negotiation.py"
     ]
    },
    {
     "id": "NEG-020",
     "area": "discovery-negotiation",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "REST implementations MUST use standard HTTP verbs (e.g., POST for creation, GET for retrieval)",
     "source": "ucp:docs/specification/overview.md#L948",
     "covered_by": [
      "merchant_checks_01_11_01_23.py"
     ]
    },
    {
     "id": "NEG-021",
     "area": "discovery-negotiation",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "REST implementations MUST use standard HTTP status codes (e.g., 200, 201, 400, 401, 500)",
     "source": "ucp:docs/specification/overview.md#L950",
     "covered_by": [
      "merchant_checks_01_11_01_23.py"
     ]
    },
    {
     "id": "ORD-001",
     "area": "order",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Get Order MUST return the full order entity as a current-state snapshot (complete record of what was purchased, how it will be delivered, and what has happened since placement), reflecting line items, fulfillment, and event logs",
     "source": "ucp:docs/specification/order.md#L24",
     "covered_by": [
      "area_order.py",
      "merchant_checks.py"
     ]
    },
    {
     "id": "ORD-002",
     "area": "order",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "The order entity MUST include the required top-level fields ucp, id, checkout_id, permalink_url, line_items, fulfillment, and totals",
     "source": "ucp:source/schemas/shopping/order.json#L26",
     "covered_by": [
      "area_order.py",
      "merchant_checks.py"
     ]
    },
    {
     "id": "ORD-003",
     "area": "order",
     "keyword": "MUST NOT",
     "testability": "testable",
     "status": "check",
     "requirement": "currency MUST NOT be treated as a required top-level order field in spec version 2026-01-23 (currency is absent from order.json required[])",
     "source": "ucp:source/schemas/shopping/order.json#L26",
     "covered_by": [
      "area_order.py"
     ]
    },
    {
     "id": "ORD-004",
     "area": "order",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Each order line item MUST include id, item, quantity, totals, and status",
     "source": "ucp:source/schemas/shopping/types/order_line_item.json#L6",
     "covered_by": [
      "area_order.py",
      "merchant_checks.py"
     ]
    },
    {
     "id": "ORD-005",
     "area": "order",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Line item quantity MUST include both total and fulfilled counts",
     "source": "ucp:source/schemas/shopping/types/order_line_item.json#L24",
     "covered_by": [
      "area_order.py"
     ]
    },
    {
     "id": "ORD-006",
     "area": "order",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Line items reflect what was purchased at checkout and MUST be retained as the immutable source of truth for what was ordered",
     "source": "ucp:source/schemas/shopping/order.json#L57",
     "covered_by": [
      "merchant_checks_01_11_01_23.py"
     ]
    },
    {
     "id": "ORD-007",
     "area": "order",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Adjustments are an append-only log; each adjustment MUST include id, type, occurred_at, and status",
     "source": "ucp:source/schemas/shopping/types/adjustment.json#L7",
     "covered_by": [
      "merchant_checks_01_11_01_23.py"
     ]
    },
    {
     "id": "ORD-009",
     "area": "order",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Each fulfillment event (append-only log of actual shipments) MUST include id, occurred_at, type, and line_items",
     "source": "ucp:source/schemas/shopping/types/fulfillment_event.json#L7",
     "covered_by": [
      "merchant_checks_01_11_01_23.py"
     ]
    },
    {
     "id": "ORD-010",
     "area": "order",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Each fulfillment expectation (buyer-facing grouping) MUST include id, line_items, method_type, and destination",
     "source": "ucp:source/schemas/shopping/types/expectation.json#L7",
     "covered_by": [
      "area_order.py"
     ]
    },
    {
     "id": "ORD-011",
     "area": "order",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "gap",
     "requirement": "The platform MUST provide its webhook URL (webhook_url) in the order capability's config field during negotiation; the platform order capability config requires webhook_url",
     "source": "ucp:source/schemas/shopping/order.json#L14",
     "covered_by": []
    },
    {
     "id": "ORD-012",
     "area": "order",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "Businesses POST order lifecycle events to the platform-provided webhook URL, sending the full order entity (not incremental deltas)",
     "source": "ucp:docs/specification/order.md#L339",
     "covered_by": [
      "area_webhook.py"
     ]
    },
    {
     "id": "ORD-013",
     "area": "order",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "Businesses MUST send an 'Order created' event with a fully populated order entity",
     "source": "ucp:docs/specification/order.md#L338",
     "covered_by": [
      "area_webhook.py"
     ]
    },
    {
     "id": "ORD-014",
     "area": "order",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "Webhook payloads MUST be signed by the business and verified by the platform to ensure authenticity and integrity",
     "source": "ucp:docs/specification/order.md#L296",
     "covered_by": [
      "merchant_checks_01_11_01_23_events.py"
     ]
    },
    {
     "id": "ORD-015",
     "area": "order",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "Businesses MUST sign all webhook payloads using a key from their signing_keys array (published in /.well-known/ucp), and the signature MUST be included in the Request-Signature header as a detached JWT (RFC 7797)",
     "source": "ucp:docs/specification/order.md#L335",
     "covered_by": [
      "merchant_checks_01_11_01_23_events.py"
     ]
    },
    {
     "id": "ORD-016",
     "area": "order",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "Businesses MUST retry failed webhook deliveries",
     "source": "ucp:docs/specification/order.md#L340",
     "covered_by": [
      "merchant_checks_01_11_01_23_events.py"
     ]
    },
    {
     "id": "ORD-017",
     "area": "order",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "gap",
     "requirement": "Businesses MUST include a business identifier in the webhook path or headers",
     "source": "ucp:docs/specification/order.md#L341",
     "covered_by": []
    },
    {
     "id": "ORD-018",
     "area": "order",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "The platform MUST respond quickly with a 2xx HTTP status code to acknowledge receipt of a webhook, then process events asynchronously after responding",
     "source": "ucp:docs/specification/order.md#L330",
     "covered_by": [],
     "exempt_reason": "'The platform MUST respond quickly with a 2xx HTTP status code to acknowledge receipt of a webhook, then process events asynchronously after responding' (order.md L330, Platform Guidelines) binds the webhook RECEIVER (the platform), not the business under test; the receiver's acknowledgement/async behavior leaves no wire trace on the merchant. The 04-08 twin is ORD-032. [Scoped: at 2026-04-08 this id is a different, already-covered requirement.]"
    },
    {
     "id": "ORD-019",
     "area": "order",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "On webhook verification: the platform extracts Request-Signature, parses the JWT header for kid, fetches the business UCP profile, locates the matching key in signing_keys, and verifies the signature against the request body; if verification fails, it MUST reject the webhook with an appropriate error response",
     "source": "ucp:docs/specification/order.md#L314",
     "covered_by": [],
     "exempt_reason": "'On webhook verification ... if verification fails, it MUST reject the webhook with an appropriate error response' (order.md L314, Verification (Platform)) is the PLATFORM receiver's verification/rejection procedure \u2014 receiver-side conduct, not a business obligation observable on the merchant. The business-side signing-key publication is checked elsewhere; the 04-08 twin is ORD-033. [Scoped: at 2026-04-08 this id is a different, already-covered requirement.]"
    },
    {
     "id": "OVR-001",
     "area": "overview",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Sensitive data such as payment credentials or PII MUST be handled according to PCI-DSS and GDPR guidelines",
     "source": "ucp:docs/specification/overview.md#L1004",
     "covered_by": [],
     "exempt_reason": "Client/platform-bound obligation surfaced by the register-completeness pass: Sensitive data such as payment credentials or PII MUST be handled according to PCI-DSS and GDPR guidelines \u2014 binds the platform/agent (or is a spec-authoring rule), not the merchant server under test; a merchant check would be fake coverage. Same treatment as the existing client-bound PAY exemptions. Version-scoped so it never collides with a version where this id names a checked requirement."
    },
    {
     "id": "PAY-001",
     "area": "payment",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Every payment handler specification must define the core required elements.",
     "source": "ucp:docs/specification/payment-handler-guide.md#L52",
     "covered_by": [],
     "exempt_reason": "'Every payment handler specification MUST define the core elements below' (payment-handler-guide.md L52) binds AUTHORS of payment-handler specification documents \u2014 a party distinct from the business or platform under test; whether a third-party handler spec document defines identity/participants/credentials etc. is established by document review, and no runtime message reflects it. [Scoped: at 2026-04-08 this id is the testable payment_handlers-registry schema requirement, covered by the 04-08 payment checks; the 04-08 twin of this requirement is PAY-028.]"
    },
    {
     "id": "PAY-002",
     "area": "payment",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "A payment handler base/declaration must include an id field identifying the handler instance.",
     "source": "ucp:source/schemas/shopping/types/payment_handler.json#L6",
     "covered_by": [
      "area_payment.py"
     ]
    },
    {
     "id": "PAY-004",
     "area": "payment",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Payment handler specifications must explicitly document the participant field mappings.",
     "source": "ucp:docs/specification/payment-handler-guide.md#L94",
     "covered_by": [],
     "exempt_reason": "'Specifications MUST explicitly document these field mappings' (payment-handler-guide.md L94, participant mappings) binds handler-spec AUTHORS: documentation completeness of a third-party specification is a document-review property with no runtime counterpart in any message the suite can elicit. [Scoped: at 2026-04-08 this id is the testable platform-declaration spec+schema-URL schema requirement, covered by schema_check_04_08_payment; at 04-08 the mapping duty folds into PAY-028's core-elements list.]"
    },
    {
     "id": "PAY-005",
     "area": "payment",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "A payment handler specification must define which credential types are accepted by the handler.",
     "source": "ucp:docs/specification/payment-handler-guide.md#L265",
     "covered_by": [],
     "exempt_reason": "'The specification MUST define which credential types are accepted' (payment-handler-guide.md L265@01-11 / L525@01-23) binds handler-spec AUTHORS \u2014 whether a third-party handler document declares its accepted credential types is decided by reading that document, not by any protocol exchange with the business under test. [Scoped: at 2026-04-08 this id is the testable available_instruments minItems-1 schema requirement, covered by schema_check_04_08_payment; the 04-08 twin of this requirement is PAY-029.]"
    },
    {
     "id": "PAY-006",
     "area": "payment",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "If using token credentials, the handler schema must include an expiry/ttl field (or mechanism to refresh credentials).",
     "source": "ucp:docs/specification/payment-handler-guide.md#L267",
     "covered_by": [],
     "exempt_reason": "Binds authors of payment-handler credential schemas (payment-handler-guide.md): whether a token schema includes 'expiry, ttl, or similar' is a design requirement on a third-party specification document, with an open-ended naming clause ('or similar', 'or mechanism to refresh') that no fixed predicate can arbitrate without false-flagging conforming handlers. [Scoped: at 2026-04-08 this id is an unrelated MAY (available_instruments optionality); the 04-08 twin of this requirement is PAY-025.]"
    },
    {
     "id": "PAY-007",
     "area": "payment",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "A handler specification must define a mapping for common payment failures.",
     "source": "ucp:docs/specification/payment-handler-guide.md#L342",
     "covered_by": [],
     "exempt_reason": "'The specification MUST define a mapping for common failures' (payment-handler-guide.md L343@01-11 / L648@01-23) binds handler-spec AUTHORS: the failure-mapping table lives in a third-party specification document and is verified by reading it \u2014 no message from the business under test evidences whether its handler's spec defined one. [Scoped: at 2026-04-08 this id is the testable available_payment_instrument type-required schema rule, covered by schema_check_04_08_payment; at 04-08 the error-mapping duty folds into PAY-028's core-elements list.]"
    },
    {
     "id": "PAY-008",
     "area": "payment",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "A payment instrument must include id, handler_id, and type, where handler_id corresponds to the id of the payment handler definition.",
     "source": "ucp:source/schemas/shopping/types/payment_instrument_base.json#L7",
     "covered_by": [
      "schema_check_01_11_01_23.py"
     ]
    },
    {
     "id": "PAY-009",
     "area": "payment",
     "keyword": "MUST NOT",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "Credentials flow Platform to Business only; businesses must not echo credentials back in responses (unidirectional flow).",
     "source": "ucp:docs/specification/overview.md#L558",
     "covered_by": [
      "area_payment.py",
      "merchant_checks.py"
     ]
    },
    {
     "id": "PAY-010",
     "area": "payment",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "The handler_id in the payload ensures the business knows which payment credential provider key to use, preventing key confusion attacks (Handler ID Routing).",
     "source": "ucp:docs/specification/overview.md#L560",
     "covered_by": [
      "schema_check_01_11_01_23.py"
     ]
    },
    {
     "id": "PAY-012",
     "area": "payment",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "gap",
     "requirement": "Businesses must filter the handlers list based on the context of the cart (dynamic filtering).",
     "source": "ucp:docs/specification/overview.md#L601",
     "covered_by": []
    },
    {
     "id": "PAY-013",
     "area": "payment",
     "keyword": "MUST NOT",
     "testability": "manual",
     "status": "exempt",
     "requirement": "The card credential type must not be used for checkout, only with payment handlers that tokenize or encrypt credentials.",
     "source": "ucp:source/schemas/shopping/types/card_credential.json#L5",
     "covered_by": [],
     "exempt_reason": "Prohibits a USAGE by the credential sender \u2014 the platform submitting raw PANs at checkout (card_credential.json description). The spec places no rejection duty on the receiving business (the official sample suite itself accepts a raw card credential at checkout with 200), so no merchant response can prove or refute the prohibition. [Scoped: at 2026-04-08 this id is an unrelated SHOULD NOT (intermediary touching raw credentials); the 04-08 twin of this requirement is PAY-022.]"
    },
    {
     "id": "PAY-014",
     "area": "payment",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Both parties handling a CardCredential (sender and receiver) must be PCI DSS compliant, and transmission must use HTTPS/TLS with strong cipher suites.",
     "source": "ucp:source/schemas/shopping/types/card_credential.json#L5",
     "covered_by": [],
     "exempt_reason": "'Both parties handling CardCredential (sender and receiver) MUST be PCI DSS compliant. Transmission MUST use HTTPS/TLS with strong cipher suites' (card_credential.json description): PCI DSS compliance is an organizational certification established by out-of-band audit \u2014 no protocol message carries it \u2014 and the transmission clause binds whichever channel actually carries a raw PAN (which conformant checkout traffic never does, per PAY-013); generic endpoint transport is separately accounted by the HTTPS/TLS rows. [Scoped: at 2026-04-08 this id is the handler_id-validation business obligation, a needs-receiver row left to its own accounting.]"
    },
    {
     "id": "PAY-015",
     "area": "payment",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "A card credential must include type (const 'card') and card_number_type.",
     "source": "ucp:source/schemas/shopping/types/card_credential.json#L12",
     "covered_by": [
      "schema_check_01_11_01_23.py"
     ]
    },
    {
     "id": "PAY-016",
     "area": "payment",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "A token credential must include type and token.",
     "source": "ucp:source/schemas/shopping/types/token_credential.json#L12",
     "covered_by": [
      "schema_check_01_11_01_23.py"
     ]
    },
    {
     "id": "PAY-017",
     "area": "payment",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "A binding must include checkout_id identifying the checkout session the token is bound to (prevents token reuse across checkouts/participants).",
     "source": "ucp:source/schemas/shopping/types/binding.json#L7",
     "covered_by": [
      "schema_check_01_11_01_23.py"
     ]
    },
    {
     "id": "PAY-018",
     "area": "payment",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "gap",
     "requirement": "The binding identity is required when acting on behalf of another participant (e.g., agent tokenizing for merchant).",
     "source": "ucp:source/schemas/shopping/types/binding.json#L15",
     "covered_by": []
    },
    {
     "id": "PAY-019",
     "area": "payment",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "Businesses must embed a cryptographic signature (merchant_authorization) in checkout responses when AP2 is negotiated.",
     "source": "ucp:docs/specification/ap2-mandates.md#L31",
     "covered_by": [
      "merchant_checks_01_11_01_23_receiver.py"
     ]
    },
    {
     "id": "PAY-020",
     "area": "payment",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Platforms must provide cryptographically signed proofs (Mandates) for autonomous/non-repudiable authorization.",
     "source": "ucp:docs/specification/ap2-mandates.md#L33",
     "covered_by": [],
     "exempt_reason": "Platform-bound MUST (@01-era only; covered at 04-08): platforms provide cryptographically signed AP2 Mandates. The business AP2 duties are checked by PAY-019/021/026/027/035."
    },
    {
     "id": "PAY-021",
     "area": "payment",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "When ap2_mandate is negotiated, the business must include ap2.merchant_authorization in all relevant checkout responses.",
     "source": "ucp:docs/specification/ap2-mandates.md#L108",
     "covered_by": [
      "merchant_checks_01_11_01_23_receiver.py"
     ]
    },
    {
     "id": "PAY-022",
     "area": "payment",
     "keyword": "MUST NOT",
     "testability": "needs-receiver",
     "status": "gap",
     "requirement": "When ap2_mandate is negotiated, the business must not accept a complete_checkout request that lacks ap2.checkout_mandate.",
     "source": "ucp:docs/specification/ap2-mandates.md#L110",
     "covered_by": []
    },
    {
     "id": "PAY-023",
     "area": "payment",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "The platform must verify the business's merchant signature before presenting checkout terms to the user.",
     "source": "ucp:docs/specification/ap2-mandates.md#L112",
     "covered_by": [],
     "exempt_reason": "Platform-bound MUST (@01-era only; covered at 04-08): the PLATFORM verifies the business's merchant signature before presenting terms. The business duty to embed a valid ES256 merchant signature is checked by PAY-035."
    },
    {
     "id": "PAY-024",
     "area": "payment",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "gap",
     "requirement": "A public signing key must be available for the business to verify the mandate's signature.",
     "source": "ucp:docs/specification/ap2-mandates.md#L117",
     "covered_by": []
    },
    {
     "id": "PAY-025",
     "area": "payment",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "gap",
     "requirement": "If a public key cannot be resolved, or if the signature is invalid, the business must return an error.",
     "source": "ucp:docs/specification/ap2-mandates.md#L123",
     "covered_by": []
    },
    {
     "id": "PAY-026",
     "area": "payment",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "All AP2 signatures must use one of the approved algorithms (ES256/ES384/ES512).",
     "source": "ucp:docs/specification/ap2-mandates.md#L130",
     "covered_by": [
      "merchant_checks_01_11_01_23_receiver.py"
     ]
    },
    {
     "id": "PAY-027",
     "area": "payment",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "Businesses must embed their signature in the checkout response body under ap2.merchant_authorization.",
     "source": "ucp:docs/specification/ap2-mandates.md#L140",
     "covered_by": [
      "merchant_checks_01_11_01_23_receiver.py"
     ]
    },
    {
     "id": "PAY-028",
     "area": "payment",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "gap",
     "requirement": "The AP2 signature must cover both the JWS header and the checkout payload.",
     "source": "ucp:docs/specification/ap2-mandates.md#L172",
     "covered_by": []
    },
    {
     "id": "PAY-029",
     "area": "payment",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Completion of an autonomous AP2 flow must produce two distinct mandate artifacts: a checkout_mandate and a payment_mandate.",
     "source": "ucp:docs/specification/ap2-mandates.md#L200",
     "covered_by": [
      "schema_check_01_11_01_23.py"
     ]
    },
    {
     "id": "PAY-030",
     "area": "payment",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "gap",
     "requirement": "The checkout mandate must contain the full checkout response including the business's signature (nested signature model).",
     "source": "ucp:docs/specification/ap2-mandates.md#L207",
     "covered_by": []
    },
    {
     "id": "PAY-031",
     "area": "payment",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "gap",
     "requirement": "Both parties must follow the locked AP2 completion flow once the ap2_mandate capability is negotiated; a completion request without mandates must result in a session failure.",
     "source": "ucp:docs/specification/ap2-mandates.md#L233",
     "covered_by": []
    },
    {
     "id": "PAY-032",
     "area": "payment",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "gap",
     "requirement": "A completion request without mandates must result in a session failure.",
     "source": "ucp:docs/specification/ap2-mandates.md#L235",
     "covered_by": []
    },
    {
     "id": "PAY-033",
     "area": "payment",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "gap",
     "requirement": "For signature computation over JSON payloads, implementations must use JSON canonicalization (JCS).",
     "source": "ucp:docs/specification/ap2-mandates.md#L218",
     "covered_by": []
    },
    {
     "id": "PAY-034",
     "area": "payment",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "gap",
     "requirement": "On receiving the complete request, the business must check the mandate is present and return a mandate_required error if ap2.checkout_mandate is missing.",
     "source": "ucp:docs/specification/ap2-mandates.md#L358",
     "covered_by": []
    },
    {
     "id": "PAY-035",
     "area": "payment",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "The merchant_authorization is a JWS Detached Content signature; its header must contain alg (ES256/ES384/ES512) and kid claims.",
     "source": "ucp:source/schemas/shopping/ap2_mandate.json#L12",
     "covered_by": [
      "merchant_checks_01_23.py"
     ]
    },
    {
     "id": "PAY-036",
     "area": "payment",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "A platform in the trusted-platform-provider model must publish at least one key in the top-level signing_keys array of its profile.",
     "source": "ucp:docs/specification/ap2-mandates.md#L97",
     "covered_by": [],
     "exempt_reason": "Client/platform-bound obligation surfaced by the register-completeness pass: The platform must verify the business's signature before presenting the checkout to the user. \u2014 binds the platform/agent (or is a spec-authoring rule), not the merchant server under test; a merchant check would be fake coverage. Same treatment as the existing client-bound PAY exemptions. Version-scoped so it never collides with a version where this id names a checked requirement."
    },
    {
     "id": "PAY-037",
     "area": "payment",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "The trusted platform provider must ensure that mandates are not created without explicit user consent from trusted, deterministic channels.",
     "source": "ucp:docs/specification/ap2-mandates.md#L300",
     "covered_by": [],
     "exempt_reason": "Client/platform-bound obligation surfaced by the register-completeness pass: A platform in the trusted-platform-provider model must publish at least one key in the top-level signing_keys array of its profile \u2014 binds the platform/agent (or is a spec-authoring rule), not the merchant server under test; a merchant check would be fake coverage. Same treatment as the existing client-bound PAY exemptions. Version-scoped so it never collides with a version where this id names a checked requirement."
    },
    {
     "id": "SEC-001",
     "area": "errors-validation-security",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "A request to the simulation endpoint without the Simulation-Secret header MUST be rejected with 403 Forbidden.",
     "source": "ucp:docs/specification/overview.md#L951",
     "covered_by": [
      "area_validation.py"
     ]
    },
    {
     "id": "SEC-002",
     "area": "errors-validation-security",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "A request to the simulation endpoint with an incorrect Simulation-Secret MUST be rejected with 403 Forbidden.",
     "source": "ucp:docs/specification/overview.md#L951",
     "covered_by": [
      "area_validation.py"
     ]
    },
    {
     "id": "SEC-003",
     "area": "errors-validation-security",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "A request to the simulation endpoint with the correct Simulation-Secret MUST be accepted with 200 OK.",
     "source": "ucp:docs/specification/overview.md#L951",
     "covered_by": [
      "area_validation.py"
     ]
    },
    {
     "id": "SEC-004",
     "area": "errors-validation-security",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "All UCP communication MUST occur over HTTPS.",
     "source": "ucp:docs/specification/overview.md#L992",
     "covered_by": [
      "merchant_checks_01_11_01_23.py"
     ]
    },
    {
     "id": "VAL-001",
     "area": "errors-validation-security",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "When an out-of-stock item is added to a checkout session, the server MUST return a 400 Bad Request error indicating insufficient stock.",
     "source": "ucp:docs/specification/overview.md#L951",
     "covered_by": [
      "merchant_checks.py"
     ]
    },
    {
     "id": "VAL-002",
     "area": "errors-validation-security",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "An inventory/quantity validation failure on checkout creation MUST return a 400 Bad Request error.",
     "source": "ucp:docs/specification/overview.md#L951",
     "covered_by": [
      "merchant_checks.py"
     ]
    },
    {
     "id": "VAL-003",
     "area": "errors-validation-security",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "A request referencing a non-existent product MUST return a 400 Bad Request error indicating the product was not found.",
     "source": "ucp:docs/specification/overview.md#L951",
     "covered_by": [
      "merchant_checks.py"
     ]
    },
    {
     "id": "VAL-004",
     "area": "errors-validation-security",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Submitting a payment instrument with a known failing token at checkout completion MUST return a 402 Payment Required error.",
     "source": "ucp:docs/specification/overview.md#L951",
     "covered_by": [
      "merchant_checks.py"
     ]
    },
    {
     "id": "VAL-005",
     "area": "errors-validation-security",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Attempting to complete a checkout without required fulfillment MUST return a 400 Bad Request error.",
     "source": "ucp:docs/specification/overview.md#L951",
     "covered_by": [
      "area_validation.py"
     ]
    },
    {
     "id": "VAL-006",
     "area": "errors-validation-security",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Error responses MUST be structured: a 400 error body MUST contain a populated 'detail' field describing the error.",
     "source": "ucp:source/schemas/shopping/types/message_error.json#L35-L38",
     "covered_by": [
      "area_validation.py",
      "merchant_checks.py"
     ]
    },
    {
     "id": "VAL-007",
     "area": "errors-validation-security",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "An update specifying an invalid adjustment status (non-conforming literal) MUST be rejected with a 422 Unprocessable Entity error due to validation failure.",
     "source": "ucp:docs/specification/overview.md#L951",
     "covered_by": [
      "area_validation.py"
     ]
    },
    {
     "id": "VAL-008",
     "area": "errors-validation-security",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "An update with a malformed adjustments field (e.g., a dict instead of a list) MUST return a 422 Unprocessable Entity error.",
     "source": "ucp:docs/specification/overview.md#L951",
     "covered_by": [
      "area_validation.py"
     ]
    },
    {
     "id": "VAL-009",
     "area": "errors-validation-security",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "A request using an unknown discount code MUST succeed with 200 OK, with no discount applied (graceful handling, not an error).",
     "source": "ucp:docs/specification/overview.md#L951",
     "covered_by": [
      "area_validation.py"
     ]
    }
   ]
  },
  "2026-01-23": {
   "musts": 188,
   "check": 125,
   "exempt": 39,
   "gap": 24,
   "accounted_pct": 87,
   "gap_by_testability": {
    "manual": 2,
    "needs-receiver": 22
   },
   "areas": [
    {
     "area": "checkout-lifecycle",
     "check": 26,
     "exempt": 3,
     "gap": 3,
     "gap_needs-receiver": 3,
     "musts": 32
    },
    {
     "area": "discount-consent-identity",
     "check": 27,
     "exempt": 4,
     "musts": 31
    },
    {
     "area": "discovery-negotiation",
     "check": 16,
     "exempt": 12,
     "gap": 3,
     "gap_needs-receiver": 3,
     "musts": 31
    },
    {
     "area": "errors-validation-security",
     "check": 20,
     "gap": 1,
     "gap_manual": 1,
     "musts": 21
    },
    {
     "area": "fulfillment",
     "check": 7,
     "exempt": 5,
     "gap": 1,
     "gap_manual": 1,
     "musts": 13
    },
    {
     "area": "order",
     "check": 14,
     "exempt": 2,
     "gap": 2,
     "gap_needs-receiver": 2,
     "musts": 18
    },
    {
     "area": "overview",
     "exempt": 1,
     "musts": 1
    },
    {
     "area": "payment",
     "check": 15,
     "exempt": 11,
     "gap": 11,
     "gap_needs-receiver": 11,
     "musts": 37
    },
    {
     "area": "transports",
     "exempt": 1,
     "gap": 3,
     "gap_needs-receiver": 3,
     "musts": 4
    }
   ],
   "rows": [
    {
     "id": "A2A-001",
     "area": "transports",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "gap",
     "requirement": "The A2A business agent MUST return the checkout object inside an A2A Message DataPart keyed `a2a.ucp.checkout`",
     "source": "ucp:docs/specification/checkout-a2a.md#L143",
     "covered_by": []
    },
    {
     "id": "CHK-001",
     "area": "checkout-lifecycle",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Create Checkout initiates a new checkout session via POST /checkout-sessions",
     "source": "ucp:docs/specification/checkout-rest.md#L48",
     "covered_by": [
      "merchant_checks.py",
      "v2026_01_23.py"
     ]
    },
    {
     "id": "CHK-002",
     "area": "checkout-lifecycle",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Get Checkout retrieves current state via GET /checkout-sessions/{id}",
     "source": "ucp:docs/specification/checkout-rest.md#L49",
     "covered_by": [
      "merchant_checks.py",
      "v2026_01_23.py"
     ]
    },
    {
     "id": "CHK-003",
     "area": "checkout-lifecycle",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Update Checkout updates a session via PUT /checkout-sessions/{id}",
     "source": "ucp:docs/specification/checkout-rest.md#L50",
     "covered_by": [
      "area_lifecycle2.py",
      "merchant_checks.py"
     ]
    },
    {
     "id": "CHK-004",
     "area": "checkout-lifecycle",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Complete Checkout places the order via POST /checkout-sessions/{id}/complete",
     "source": "ucp:docs/specification/checkout-rest.md#L51",
     "covered_by": [
      "merchant_checks.py",
      "v2026_01_23.py"
     ]
    },
    {
     "id": "CHK-005",
     "area": "checkout-lifecycle",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Cancel Checkout cancels a session via POST /checkout-sessions/{id}/cancel",
     "source": "ucp:docs/specification/checkout-rest.md#L52",
     "covered_by": [
      "merchant_checks.py",
      "v2026_01_23.py"
     ]
    },
    {
     "id": "CHK-006",
     "area": "checkout-lifecycle",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "Update Checkout performs a full replacement of the checkout resource; the platform is REQUIRED to send the entire checkout resource",
     "source": "ucp:docs/specification/checkout.md#L318",
     "covered_by": [
      "area_lifecycle2.py"
     ]
    },
    {
     "id": "CHK-007",
     "area": "checkout-lifecycle",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "Each PUT replaces the entire session, so clients must include all previously set fields they wish to retain",
     "source": "ucp:docs/specification/checkout-rest.md#L172",
     "covered_by": [
      "area_lifecycle2.py"
     ]
    },
    {
     "id": "CHK-008",
     "area": "checkout-lifecycle",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Complete Checkout response is the checkout object with the order field populated (order provides id and permalink_url)",
     "source": "ucp:docs/specification/checkout.md#L329",
     "covered_by": [
      "merchant_checks.py",
      "v2026_01_23.py"
     ]
    },
    {
     "id": "CHK-012",
     "area": "checkout-lifecycle",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "After a checkout session reaches the state completed it is considered immutable",
     "source": "ucp:docs/specification/checkout.md#L273",
     "covered_by": [
      "area_lifecycle2.py",
      "merchant_checks.py",
      "v2026_01_23.py"
     ]
    },
    {
     "id": "CHK-013",
     "area": "checkout-lifecycle",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Status field enum is constrained to the six lifecycle values",
     "source": "ucp:source/schemas/shopping/checkout.json#L65",
     "covered_by": [
      "area_lifecycle2.py"
     ]
    },
    {
     "id": "CHK-014",
     "area": "checkout-lifecycle",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Checkout response MUST include required top-level fields: ucp, id, line_items, status, currency, totals, links",
     "source": "ucp:source/schemas/shopping/checkout.json#L9",
     "covered_by": [
      "merchant_checks.py"
     ]
    },
    {
     "id": "CHK-015",
     "area": "checkout-lifecycle",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "gap",
     "requirement": "Checkout status indicates the current phase and required action; the business sets the status",
     "source": "ucp:docs/specification/checkout.md#L57",
     "covered_by": []
    },
    {
     "id": "CHK-016",
     "area": "checkout-lifecycle",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Top-level checkout id is omitted on create and complete requests and required on update requests (ucp_request)",
     "source": "ucp:source/schemas/shopping/checkout.json#L27",
     "covered_by": [
      "merchant_checks.py"
     ]
    },
    {
     "id": "CHK-017",
     "area": "checkout-lifecycle",
     "keyword": "MUST NOT",
     "testability": "testable",
     "status": "check",
     "requirement": "ucp envelope field is omitted on request bodies (ucp_request: omit)",
     "source": "ucp:source/schemas/shopping/checkout.json#L22",
     "covered_by": [
      "schema_check_01_11_01_23.py"
     ]
    },
    {
     "id": "CHK-018",
     "area": "checkout-lifecycle",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "line_items is required on create and update requests and omitted on complete requests",
     "source": "ucp:source/schemas/shopping/checkout.json#L39",
     "covered_by": [
      "merchant_checks.py"
     ]
    },
    {
     "id": "CHK-019",
     "area": "checkout-lifecycle",
     "keyword": "MUST NOT",
     "testability": "testable",
     "status": "check",
     "requirement": "currency is omitted on create, update, and complete requests (merchant-determined, response-only)",
     "source": "ucp:source/schemas/shopping/checkout.json#L79",
     "covered_by": [
      "schema_check_01_11_01_23.py"
     ]
    },
    {
     "id": "CHK-020",
     "area": "checkout-lifecycle",
     "keyword": "MUST NOT",
     "testability": "testable",
     "status": "check",
     "requirement": "status, totals, messages, links, expires_at, continue_url, and order are response-only (ucp_request: omit) and omitted on requests",
     "source": "ucp:source/schemas/shopping/checkout.json#L74",
     "covered_by": [
      "schema_check_01_11_01_23.py"
     ]
    },
    {
     "id": "CHK-021",
     "area": "checkout-lifecycle",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "payment is optional on create and update, and required on complete requests",
     "source": "ucp:source/schemas/shopping/checkout.json#L123",
     "covered_by": [
      "schema_check_01_11_01_23.py"
     ]
    },
    {
     "id": "CHK-025",
     "area": "checkout-lifecycle",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "Businesses MUST provide continue_url when returning status = requires_escalation (Availability)",
     "source": "ucp:docs/specification/checkout.md#L207",
     "covered_by": [
      "merchant_checks_01_11_01_23_receiver.py"
     ]
    },
    {
     "id": "CHK-028",
     "area": "checkout-lifecycle",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "continue_url MUST be an absolute HTTPS URL",
     "source": "ucp:docs/specification/checkout.md#L215",
     "covered_by": [
      "merchant_checks_01_11_01_23_receiver.py"
     ]
    },
    {
     "id": "CHK-032",
     "area": "checkout-lifecycle",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Platform MUST use continue_url when checkout status is requires_escalation (Platform guideline)",
     "source": "ucp:docs/specification/checkout.md#L258",
     "covered_by": [],
     "exempt_reason": "Platform-side behavior MUST: on canceled the platform starts a new session if needed; the business duty (canceled is terminal/immutable) is checked by the cancel/immutability checks."
    },
    {
     "id": "CHK-035",
     "area": "checkout-lifecycle",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Business MUST send a confirmation email after the checkout has been completed",
     "source": "ucp:docs/specification/checkout.md#L265",
     "covered_by": [],
     "exempt_reason": "'Business MUST send a confirmation email after the checkout has been completed' (checkout.md Business responsibilities, L261@01-11 / L265@01-23) is dispatched on a channel the protocol does not define; no checkout response field evidences it, and a conformance run has no principled inbox oracle \u2014 non-receipt is indistinguishable from spam-filtering or asynchronous delay. [Scoped: at 2026-04-08 this id is the testable ucp_request:omit rule on checkout id, already covered by schema_check_04_08_lifecycle; the 04-08 twin of the email requirement is CHK-011.]"
    },
    {
     "id": "CHK-037",
     "area": "checkout-lifecycle",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "gap",
     "requirement": "Logic handling the checkout sessions MUST be deterministic",
     "source": "ucp:docs/specification/checkout.md#L267",
     "covered_by": []
    },
    {
     "id": "CHK-038",
     "area": "checkout-lifecycle",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "Business MUST provide continue_url when returning status = requires_escalation (Business guideline restatement)",
     "source": "ucp:docs/specification/checkout.md#L268",
     "covered_by": [
      "merchant_checks_01_11_01_23_receiver.py"
     ]
    },
    {
     "id": "CHK-039",
     "area": "checkout-lifecycle",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "gap",
     "requirement": "Business MUST include at least one message with severity: escalation when returning status = requires_escalation",
     "source": "ucp:docs/specification/checkout.md#L270",
     "covered_by": []
    },
    {
     "id": "CHK-050",
     "area": "checkout-lifecycle",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "All REST request and response bodies MUST be valid JSON per RFC 8259",
     "source": "ucp:docs/specification/checkout-rest.md#L36",
     "covered_by": [
      "merchant_checks_01_11_01_23.py"
     ]
    },
    {
     "id": "CHK-051",
     "area": "checkout-lifecycle",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "All REST endpoints MUST be served over HTTPS with minimum TLS version 1.3",
     "source": "ucp:docs/specification/checkout-rest.md#L41",
     "covered_by": [
      "tls_check_01_11_01_23.py"
     ]
    },
    {
     "id": "CHK-052",
     "area": "checkout-lifecycle",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "All requests MUST include the UCP-Agent header containing the platform profile URI (RFC 8941 dictionary syntax)",
     "source": "ucp:docs/specification/checkout-rest.md#L1211",
     "covered_by": [],
     "exempt_reason": "'All requests MUST include the UCP-Agent header containing the platform profile URI' (checkout-rest.md L1211-1213) binds the REQUEST AUTHOR \u2014 the platform \u2014 per the suite-wide F2 policy (adversarial review 2026-07-03, register note applied): the spec mandates no business-side rejection of header-less requests, so no merchant response can prove or refute the platform's compliance; the tester, being the platform, simply sends the header. [Scoped: at 2026-04-08 this id is an unrelated SHOULD (severity unrecoverable when no resource exists); the 04-08 twin of this header rule is CHK-046.]"
    },
    {
     "id": "DISC-001",
     "area": "discovery-negotiation",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "All capability and service names MUST use the format {reverse-domain}.{service}.{capability}",
     "source": "ucp:docs/specification/overview.md#L51",
     "covered_by": [
      "area_negotiation.py",
      "merchant_checks.py"
     ]
    },
    {
     "id": "DISC-002",
     "area": "discovery-negotiation",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "The spec and schema fields are REQUIRED for all capabilities",
     "source": "ucp:docs/specification/overview.md#L74",
     "covered_by": [
      "merchant_checks_01_11_01_23.py"
     ]
    },
    {
     "id": "DISC-003",
     "area": "discovery-negotiation",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "The origin of capability spec/schema URLs MUST match the namespace authority",
     "source": "ucp:docs/specification/overview.md#L75",
     "covered_by": [
      "merchant_checks_01_11_01_23.py"
     ]
    },
    {
     "id": "DISC-004",
     "area": "discovery-negotiation",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Platform MUST validate the spec-origin/namespace binding and SHOULD reject capabilities where the spec origin does not match the namespace authority",
     "source": "ucp:docs/specification/overview.md#L82",
     "covered_by": [],
     "exempt_reason": "'Platform MUST validate this binding and SHOULD reject capabilities where the spec origin does not match the namespace authority' (overview.md L82-83) is a platform-internal validation step over discovery data it already holds; a validating and a non-validating platform emit byte-identical traffic toward the business, so nothing the suite can elicit from the party under test reflects it \u2014 same subject-binding as the already-exempt NEG-007. [Scoped: at 2026-04-08 this id is 'reject profile URLs not served over HTTPS', testable and covered by merchant_checks_04_08_discovery.]"
    },
    {
     "id": "DISC-005",
     "area": "discovery-negotiation",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Vendors MUST use their own reverse-domain namespace for custom capabilities",
     "source": "ucp:docs/specification/overview.md#L94",
     "covered_by": [],
     "exempt_reason": "'Vendors MUST use their own reverse-domain namespace for custom capabilities' (overview.md Governance Model, L94-95) turns on OWNERSHIP: whether com.example.* is really 'their own' is a domain-registration/governance fact established by registrars and the UCP governing body's reservation of dev.ucp.*, not by any protocol message \u2014 a payload shows the namespace string but never who owns it. [Scoped: at 2026-04-08 this id is 'service endpoint MUST be a valid HTTPS URL', testable and covered by merchant_checks_04_08_discovery.]"
    },
    {
     "id": "DISC-006",
     "area": "discovery-negotiation",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Transport definitions MUST be thin: they declare method names and reference base schemas only",
     "source": "ucp:docs/specification/overview.md#L125",
     "covered_by": [],
     "exempt_reason": "'Transport definitions MUST be thin: they declare method names and reference base schemas only' (overview.md L125-126) binds the AUTHORS of capability transport-definition documents (OpenAPI/OpenRPC) \u2014 the namespace authority publishing the capability, a party distinct from the business or platform under test; conformance is established by document review, same as the already-exempt DISC-009 (its 04-08 twin). [Scoped: at 2026-04-08 this id is the platform's profile-URI-in-every-request duty \u2014 a different, requester-bound requirement left to its own accounting.]"
    },
    {
     "id": "DISC-007",
     "area": "discovery-negotiation",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "A service endpoint MUST be a valid URL with scheme (https)",
     "source": "ucp:docs/specification/overview.md#L152",
     "covered_by": [
      "merchant_checks.py",
      "v2026_01_23.py"
     ]
    },
    {
     "id": "DISC-009",
     "area": "discovery-negotiation",
     "keyword": "MUST NOT",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Transport definitions MUST NOT enumerate fields or define payload shapes inline",
     "source": "ucp:docs/specification/overview.md#L198",
     "covered_by": [],
     "exempt_reason": "Binds authors of capability transport-definition documents (OpenAPI/OpenRPC, overview.md Requirements L191-192) \u2014 the namespace authority publishing the capability, a party distinct from the business or platform under test; no runtime message reflects how those documents were authored."
    },
    {
     "id": "DISC-010",
     "area": "discovery-negotiation",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Each extension schema MUST declare the types it introduces and how it modifies base types using allOf composition",
     "source": "ucp:docs/specification/overview.md#L199",
     "covered_by": [],
     "exempt_reason": "Binds extension-schema AUTHORS (overview.md L193-195): being 'self-describing' via allOf composition is a property of ecosystem schema documents published by the capability authority, not of any implementation behavior the suite can elicit from a business."
    },
    {
     "id": "DISC-011",
     "area": "discovery-negotiation",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Platforms MUST resolve schemas client-side by fetching and composing base schemas with active extension schemas",
     "source": "ucp:docs/specification/overview.md#L202",
     "covered_by": [],
     "exempt_reason": "Binds the platform (overview.md L196-197): client-side schema resolution (fetch + compose before requests) is unobservable from the business side \u2014 wire traffic from a resolving and a non-resolving client can be byte-identical."
    },
    {
     "id": "DISC-013",
     "area": "discovery-negotiation",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Discovery profile at /.well-known/ucp MUST return 200 OK with the expected version, capabilities, and payment handlers",
     "source": "ucp:source/schemas/ucp.json#L23",
     "covered_by": [
      "merchant_checks.py",
      "v2026_01_23.py"
     ]
    },
    {
     "id": "DISC-014",
     "area": "discovery-negotiation",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "All spec and schema URLs advertised in the discovery profile MUST be valid and resolvable (return 200 with valid HTML/JSON)",
     "source": "ucp:docs/specification/overview.md#L74",
     "covered_by": [
      "merchant_checks_01_11_01_23_disc.py"
     ]
    },
    {
     "id": "DSC-001",
     "area": "discount-consent-identity",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Submitting discounts.codes replaces any previously submitted codes (replacement semantics).",
     "source": "ucp:docs/specification/discount.md#L125",
     "covered_by": [
      "merchant_checks_01_11_01_23.py"
     ]
    },
    {
     "id": "DSC-002",
     "area": "discount-consent-identity",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Sending an empty codes array clears all discount codes.",
     "source": "ucp:docs/specification/discount.md#L126",
     "covered_by": [
      "merchant_checks.py"
     ]
    },
    {
     "id": "DSC-003",
     "area": "discount-consent-identity",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Discount codes are matched case-insensitively by the business.",
     "source": "ucp:docs/specification/discount.md#L127",
     "covered_by": [
      "merchant_checks_01_23.py"
     ]
    },
    {
     "id": "DSC-004",
     "area": "discount-consent-identity",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "discounts.applied contains all active discounts (code-based and automatic) in the response.",
     "source": "ucp:docs/specification/discount.md#L131",
     "covered_by": [
      "area_discount.py",
      "merchant_checks.py"
     ]
    },
    {
     "id": "DSC-005",
     "area": "discount-consent-identity",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "When multiple valid discount codes are submitted, all are applied (accept-both) and appear in discounts.applied.",
     "source": "ucp:docs/specification/discount.md#L131",
     "covered_by": [
      "area_discount.py",
      "merchant_checks.py"
     ]
    },
    {
     "id": "DSC-006",
     "area": "discount-consent-identity",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "When a submitted discount code cannot be applied, the business communicates this via the messages[] array.",
     "source": "ucp:docs/specification/discount.md#L137",
     "covered_by": [
      "area_discount.py",
      "merchant_checks.py"
     ]
    },
    {
     "id": "DSC-007",
     "area": "discount-consent-identity",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "A rejected discount code still appears in discounts.codes (echoed back) but not in discounts.applied.",
     "source": "ucp:docs/specification/discount.md#L314",
     "covered_by": [
      "area_discount.py",
      "merchant_checks.py"
     ]
    },
    {
     "id": "DSC-010",
     "area": "discount-consent-identity",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Automatic discounts appear in discounts.applied with automatic: true and no code field; they are applied without platform action and cannot be removed by the platform.",
     "source": "ucp:docs/specification/discount.md#L175",
     "covered_by": [
      "merchant_checks_01_23.py"
     ]
    },
    {
     "id": "DSC-011",
     "area": "discount-consent-identity",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "A fixed-amount discount code decreases the total correctly and surfaces applied discount details (code and amount).",
     "source": "ucp:docs/specification/discount.md#L131",
     "covered_by": [
      "area_discount.py",
      "merchant_checks.py"
     ]
    },
    {
     "id": "DSC-012",
     "area": "discount-consent-identity",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "An applied_discount must include title and amount; amount is a non-negative integer in minor currency units.",
     "source": "ucp:source/schemas/shopping/discount.json#L29",
     "covered_by": [
      "merchant_checks.py"
     ]
    },
    {
     "id": "DSC-013",
     "area": "discount-consent-identity",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "applied_discount.method, when present, must be one of 'each' or 'across'.",
     "source": "ucp:source/schemas/shopping/discount.json#L51",
     "covered_by": [
      "schema_check_01_23.py"
     ]
    },
    {
     "id": "DSC-014",
     "area": "discount-consent-identity",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "An allocation must include path and amount; allocation amount is a non-negative integer in minor currency units.",
     "source": "ucp:source/schemas/shopping/discount.json#L12",
     "covered_by": [
      "schema_check_01_23.py"
     ]
    },
    {
     "id": "DSC-015",
     "area": "discount-consent-identity",
     "keyword": "MUST NOT",
     "testability": "testable",
     "status": "check",
     "requirement": "The discounts object is optional on create and update operations and must be omitted on complete.",
     "source": "ucp:source/schemas/shopping/discount.json#L98",
     "covered_by": [
      "schema_check_01_11_01_23.py"
     ]
    },
    {
     "id": "DSC-016",
     "area": "discount-consent-identity",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "The sum of allocations[].amount must equal the applied_discount.amount.",
     "source": "ucp:docs/specification/discount.md#L117",
     "covered_by": [
      "merchant_checks_01_11_01_23.py"
     ]
    },
    {
     "id": "DSC-017",
     "area": "discount-consent-identity",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "All discount amounts are positive integers in minor currency units; when presenting totals, discount types are displayed as subtractive.",
     "source": "ucp:docs/specification/discount.md#L205",
     "covered_by": [
      "merchant_checks_01_11_01_23.py"
     ]
    },
    {
     "id": "DSC-018",
     "area": "discount-consent-identity",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "totals[type=items_discount].amount must equal sum(line_items[].discount).",
     "source": "ucp:docs/specification/discount.md#L199",
     "covered_by": [
      "merchant_checks_01_23.py"
     ]
    },
    {
     "id": "DSC-019",
     "area": "discount-consent-identity",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Buyer consent is communicated as boolean states within the buyer.consent object (analytics, preferences, marketing, sale_of_data) on create_checkout and update_checkout.",
     "source": "ucp:docs/specification/buyer-consent.md#L29",
     "covered_by": [
      "merchant_checks_01_11_01_23.py"
     ]
    },
    {
     "id": "DSC-020",
     "area": "discount-consent-identity",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Each buyer consent state (analytics, preferences, marketing, sale_of_data), when present, must be a boolean.",
     "source": "ucp:source/schemas/shopping/buyer_consent.json#L14",
     "covered_by": [
      "schema_check_01_23.py"
     ]
    },
    {
     "id": "DSC-021",
     "area": "discount-consent-identity",
     "keyword": "MUST NOT",
     "testability": "testable",
     "status": "check",
     "requirement": "The buyer consent object is optional on create and update operations and must be omitted on complete (consent is not required to gate completion).",
     "source": "ucp:source/schemas/shopping/buyer_consent.json#L63",
     "covered_by": [
      "schema_check_01_11_01_23.py"
     ]
    },
    {
     "id": "DSC-022",
     "area": "discount-consent-identity",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Consent is declarative: the protocol communicates consent but does not enforce it, and legal compliance remains the business's responsibility.",
     "source": "ucp:docs/specification/buyer-consent.md#L132",
     "covered_by": [],
     "exempt_reason": "'Consent is declarative - the protocol communicates consent, it does not enforce it' and 'legal compliance remains the business's responsibility' (buyer-consent.md Security & Privacy Considerations, L131-133) assert a non-enforcement property of the protocol plus a LEGAL allocation of responsibility; whether a business actually meets its consent-law obligations is established by audit/regulation entirely outside any protocol message. [Scoped: at 2026-04-08 this id is the testable allocations-sum invariant, already covered by merchant_checks_04_08.]"
    },
    {
     "id": "ERR-001",
     "area": "errors-validation-security",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "An error message MUST include the fields type, code, content, and severity (all four are required on a Message Error).",
     "source": "ucp:source/schemas/shopping/types/message_error.json#L6-L11",
     "covered_by": [
      "schema_check_01_11_01_23.py"
     ]
    },
    {
     "id": "ERR-002",
     "area": "errors-validation-security",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "The error message type discriminator MUST be the constant string \"error\".",
     "source": "ucp:source/schemas/shopping/types/message_error.json#L13-L16",
     "covered_by": [
      "schema_check_01_23.py"
     ]
    },
    {
     "id": "ERR-003",
     "area": "errors-validation-security",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "The error code field MUST be a string; defined values include missing, invalid, out_of_stock, payment_declined, requires_sign_in, requires_3ds, requires_identity_linking, with freeform codes also allowed.",
     "source": "ucp:source/schemas/shopping/types/message_error.json#L18-L21",
     "covered_by": [
      "schema_check_01_23.py"
     ]
    },
    {
     "id": "ERR-004",
     "area": "errors-validation-security",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "The error severity field MUST be one of exactly three enum values: recoverable, requires_buyer_input, requires_buyer_review.",
     "source": "ucp:source/schemas/shopping/types/message_error.json#L39-L46",
     "covered_by": [
      "schema_check_01_23.py"
     ]
    },
    {
     "id": "ERR-005",
     "area": "errors-validation-security",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Errors with requires_* severity contribute to status requires_escalation.",
     "source": "ucp:source/schemas/shopping/types/message_error.json#L46",
     "covered_by": [
      "merchant_checks_01_11_01_23.py"
     ]
    },
    {
     "id": "ERR-006",
     "area": "errors-validation-security",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "REST implementations MUST use standard HTTP status codes (e.g., 200, 201, 400, 401, 500) for responses including errors.",
     "source": "ucp:docs/specification/overview.md#L1032-L1033",
     "covered_by": [
      "merchant_checks_01_11_01_23.py"
     ]
    },
    {
     "id": "ERR-007",
     "area": "errors-validation-security",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "If negotiation fails, businesses MUST return an error response (status requires_escalation with a type:error message, e.g. version_unsupported, severity requires_buyer_input).",
     "source": "ucp:docs/specification/overview.md#L522",
     "covered_by": [
      "merchant_checks_01_11_01_23.py"
     ]
    },
    {
     "id": "ERR-008",
     "area": "errors-validation-security",
     "keyword": "MUST",
     "testability": "manual",
     "status": "gap",
     "requirement": "Businesses MUST include at least one message with severity escalation when returning status requires_escalation.",
     "source": "ucp:docs/specification/checkout.md#L270-L271",
     "covered_by": []
    },
    {
     "id": "FUL-003",
     "area": "fulfillment",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "A fulfillment_method MUST include id, type, and line_item_ids",
     "source": "ucp:source/schemas/shopping/types/fulfillment_method.json#L7",
     "covered_by": [
      "area_fulfillment.py",
      "merchant_checks.py"
     ]
    },
    {
     "id": "FUL-004",
     "area": "fulfillment",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "fulfillment_method.type MUST be one of shipping or pickup",
     "source": "ucp:source/schemas/shopping/types/fulfillment_method.json#L17",
     "covered_by": [
      "area_fulfillment.py"
     ]
    },
    {
     "id": "FUL-007",
     "area": "fulfillment",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "A fulfillment_group MUST include id and line_item_ids",
     "source": "ucp:source/schemas/shopping/types/fulfillment_group.json#L7",
     "covered_by": [
      "area_fulfillment.py"
     ]
    },
    {
     "id": "FUL-008",
     "area": "fulfillment",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "A fulfillment_option MUST include id, title, and totals",
     "source": "ucp:source/schemas/shopping/types/fulfillment_option.json#L8",
     "covered_by": [
      "area_fulfillment.py",
      "merchant_checks.py",
      "v2026_01_23.py"
     ]
    },
    {
     "id": "FUL-009",
     "area": "fulfillment",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "options[].title is the required primary label that distinguishes the option from its siblings",
     "source": "ucp:docs/specification/fulfillment.md#L170",
     "covered_by": [
      "schema_check_01_11_01_23.py"
     ]
    },
    {
     "id": "FUL-010",
     "area": "fulfillment",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "options[].title MUST distinguish this option from its siblings",
     "source": "ucp:docs/specification/fulfillment.md#L179",
     "covered_by": [],
     "exempt_reason": "Whether a title lets a human buyer tell one option from its siblings (fulfillment.md Business Responsibilities) is a comprehension judgment: string uniqueness neither proves it (trivially-differing strings do not distinguish) nor is literally required by it."
    },
    {
     "id": "FUL-012",
     "area": "fulfillment",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "options[].title MUST be sufficient for buyer decision if description is absent",
     "source": "ucp:docs/specification/fulfillment.md#L181",
     "covered_by": [],
     "exempt_reason": "'Sufficient for buyer decision if description is absent' is a human-adequacy judgment about label content; no mechanical predicate separates sufficient titles from insufficient ones."
    },
    {
     "id": "FUL-013",
     "area": "fulfillment",
     "keyword": "MUST NOT",
     "testability": "manual",
     "status": "exempt",
     "requirement": "options[].description MUST NOT repeat title or total; provides supplementary context only",
     "source": "ucp:docs/specification/fulfillment.md#L185",
     "covered_by": [],
     "exempt_reason": "'Repeat' covers paraphrase, not just string equality: deciding whether a description merely restates title/total versus supplying supplementary context is linguistic judgment beyond any exact-match test."
    },
    {
     "id": "FUL-017",
     "area": "fulfillment",
     "keyword": "MUST",
     "testability": "manual",
     "status": "gap",
     "requirement": "options[].total is the required price in minor units and may be null if not yet available",
     "source": "ucp:docs/specification/fulfillment.md#L172",
     "covered_by": []
    },
    {
     "id": "FUL-018",
     "area": "fulfillment",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "available_methods[].description MUST be a standalone sentence explaining what, when, and where",
     "source": "ucp:docs/specification/fulfillment.md#L192",
     "covered_by": [],
     "exempt_reason": "Whether a description is 'a standalone sentence explaining what, when, and where' is a natural-language completeness judgment (does it actually explain all three?) that a parser cannot arbitrate."
    },
    {
     "id": "FUL-026",
     "area": "fulfillment",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "When supports_multi_group is false (default), the business MUST consolidate all items into a single group per method",
     "source": "ucp:docs/specification/fulfillment.md#L326",
     "covered_by": [
      "merchant_checks.py"
     ]
    },
    {
     "id": "FUL-029",
     "area": "fulfillment",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Extensions that add new fulfillment method types MUST add an extension schema that adds the method to the fulfillment_method type enum and adds corresponding business config options",
     "source": "ucp:docs/specification/fulfillment.md#L339",
     "covered_by": [],
     "exempt_reason": "Binds authors of FUTURE fulfillment extensions (fulfillment.md 'Adding New Methods'): the obligation attaches to a hypothetical extension schema document, not to any behavior of the business or platform under test."
    },
    {
     "id": "FUL-030",
     "area": "fulfillment",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "A shipping_destination MUST include an id (it is a postal_address plus a required id)",
     "source": "ucp:source/schemas/shopping/types/shipping_destination.json#L21",
     "covered_by": [
      "area_fulfillment.py"
     ]
    },
    {
     "id": "IDL-001",
     "area": "discount-consent-identity",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Platforms must authenticate using their client_id and client_secret through HTTP Basic Authentication when exchanging codes for tokens.",
     "source": "ucp:docs/specification/identity-linking.md#L42",
     "covered_by": [],
     "exempt_reason": "'Platforms must authenticate using their client_id and client_secret through HTTP Basic Authentication when exchanging codes for tokens' (identity-linking.md L42, RFC 6749 2.3.1 / RFC 7617) binds the PLATFORM as the token-request author, not the business under test; HTTP Basic client authentication is the platform's request authoring and no merchant response can prove or refute it. The business-side enforcement duty is IDL-007 (checked). Requester-bound per suite policy F2; precedent CAT-034. [Scoped: at 2026-04-08 this id is a different, already-covered requirement.]"
    },
    {
     "id": "IDL-002",
     "area": "discount-consent-identity",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Platforms must implement the OAuth 2.0 Authorization Code flow (RFC 6749 4.1) as the primary linking mechanism.",
     "source": "ucp:docs/specification/identity-linking.md#L52",
     "covered_by": [],
     "exempt_reason": "'Platforms must implement the OAuth 2.0 Authorization Code flow (RFC 6749 4.1) as the primary linking mechanism' (identity-linking.md L52) binds the PLATFORM's client implementation \u2014 a flow choice authored by the platform, not the business; no merchant response can prove or refute it. Requester-bound per policy F2; precedent CAT-034."
    },
    {
     "id": "IDL-005",
     "area": "discount-consent-identity",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Businesses must implement OAuth 2.0 (RFC 6749).",
     "source": "ucp:docs/specification/identity-linking.md#L72",
     "covered_by": [
      "merchant_checks_01_11_01_23_oauth.py"
     ]
    },
    {
     "id": "IDL-006",
     "area": "discount-consent-identity",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Businesses must adhere to RFC 8414 to declare the location of their OAuth 2.0 endpoints at /.well-known/oauth-authorization-server.",
     "source": "ucp:docs/specification/identity-linking.md#L74",
     "covered_by": [
      "merchant_checks_01_11_01_23_oauth.py"
     ]
    },
    {
     "id": "IDL-007",
     "area": "discount-consent-identity",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Businesses must enforce Client Authentication at the Token Endpoint.",
     "source": "ucp:docs/specification/identity-linking.md#L83",
     "covered_by": [
      "merchant_checks_01_11_01_23_oauth.py"
     ]
    },
    {
     "id": "IDL-008",
     "area": "discount-consent-identity",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Businesses must provide an account creation flow if the user does not already have an account.",
     "source": "ucp:docs/specification/identity-linking.md#L84",
     "covered_by": [],
     "exempt_reason": "'MUST provide an account creation flow if the user does not already have an account' (identity-linking.md L84-85) requires a human-facing enrollment journey on the business's own web property, outside every protocol message: a conformance run cannot enact a real new user signing up, and no identity-linking response field evidences that such a flow exists or works. [Scoped: at 2026-04-08 this id is the platform's WWW-Authenticate challenge-processing duty \u2014 an unrelated needs-oauth requirement left to its own accounting.]"
    },
    {
     "id": "IDL-009",
     "area": "discount-consent-identity",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Businesses must support standard UCP scopes, granting tokens permission to all associated Operations for a given resource.",
     "source": "ucp:docs/specification/identity-linking.md#L86",
     "covered_by": [
      "merchant_checks_01_11_01_23_oauth.py"
     ]
    },
    {
     "id": "IDL-010",
     "area": "discount-consent-identity",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Businesses must implement standard Token Revocation as defined in RFC 7009.",
     "source": "ucp:docs/specification/identity-linking.md#L94",
     "covered_by": [
      "merchant_checks_01_11_01_23_oauth.py"
     ]
    },
    {
     "id": "IDL-011",
     "area": "discount-consent-identity",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Businesses must revoke the specified token and should recursively revoke all associated tokens (revoking a refresh_token must also immediately revoke all access_tokens issued from it).",
     "source": "ucp:docs/specification/identity-linking.md#L96",
     "covered_by": [
      "merchant_checks_01_11_01_23_oauth.py"
     ]
    },
    {
     "id": "IDL-012",
     "area": "discount-consent-identity",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Businesses must support revocation requests authenticated with the same client credentials used for the token endpoint.",
     "source": "ucp:docs/specification/identity-linking.md#L99",
     "covered_by": [
      "merchant_checks_01_11_01_23_oauth.py"
     ]
    },
    {
     "id": "IDL-013",
     "area": "discount-consent-identity",
     "keyword": "MUST",
     "testability": "needs-oauth",
     "status": "check",
     "requirement": "A scope covering a capability must grant access to all operations associated with that capability (e.g., ucp:scopes:checkout_session must grant Get, Create, Update, Delete, Cancel, Complete).",
     "source": "ucp:docs/specification/identity-linking.md#L129",
     "covered_by": [
      "merchant_checks_01_11_01_23_oauth.py"
     ]
    },
    {
     "id": "IDM-002",
     "area": "checkout-lifecycle",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "When an Idempotency-Key is provided, the server MUST store the key with the operation result for at least 24 hours",
     "source": "ucp:docs/specification/checkout-rest.md#L1217",
     "covered_by": [
      "merchant_checks_01_11_01_23_receiver.py"
     ]
    },
    {
     "id": "IDM-003",
     "area": "checkout-lifecycle",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "When an Idempotency-Key is provided, the server MUST return the cached result for duplicate keys",
     "source": "ucp:docs/specification/checkout-rest.md#L1218",
     "covered_by": [
      "area_lifecycle2.py"
     ]
    },
    {
     "id": "IDM-004",
     "area": "checkout-lifecycle",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "When an Idempotency-Key is provided, the server MUST return 409 Conflict if the key is reused with different parameters",
     "source": "ucp:docs/specification/checkout-rest.md#L1219",
     "covered_by": [
      "merchant_checks.py",
      "v2026_01_23.py"
     ]
    },
    {
     "id": "IDM-005",
     "area": "checkout-lifecycle",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "409 Conflict indicates the request could not be completed due to a conflict (e.g., idempotent key reuse)",
     "source": "ucp:docs/specification/checkout-rest.md#L1236",
     "covered_by": [
      "merchant_checks_01_11_01_23.py"
     ]
    },
    {
     "id": "MCP-001",
     "area": "transports",
     "keyword": "MUST NOT",
     "testability": "manual",
     "status": "exempt",
     "requirement": "MCP request payloads MUST NOT carry an `id` inside the `checkout` object; the target resource is identified by a top-level `id` parameter",
     "source": "ucp:docs/specification/checkout-mcp.md#L117",
     "covered_by": [],
     "exempt_reason": "Client/platform-bound obligation surfaced by the register-completeness pass: MCP request payloads MUST NOT carry an `id` inside the `checkout` object; the target resource is identified by a top-level `id` pa \u2014 binds the platform/agent (or is a spec-authoring rule), not the merchant server under test; a merchant check would be fake coverage. Same treatment as the existing client-bound PAY exemptions. Version-scoped so it never collides with a version where this id names a checked requirement."
    },
    {
     "id": "MCP-002",
     "area": "transports",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "gap",
     "requirement": "A conforming MCP checkout endpoint MUST implement JSON-RPC 2.0, provide all core checkout tools, handle errors with UCP-specific codes embedded in the JSON-RPC error object, validate tool inputs against schemas, and support HTTP streaming",
     "source": "ucp:docs/specification/checkout-mcp.md#L601",
     "covered_by": []
    },
    {
     "id": "MCP-003",
     "area": "transports",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "gap",
     "requirement": "MCP transport MUST expose UCP OpenRPC operations via JSON-RPC tools/call, mapping method->params.name and params->params.arguments",
     "source": "ucp:docs/specification/checkout-mcp.md#L615",
     "covered_by": []
    },
    {
     "id": "NEG-001",
     "area": "discovery-negotiation",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Platforms MUST communicate their profile URI with each request to enable capability negotiation",
     "source": "ucp:docs/specification/overview.md#L438",
     "covered_by": [],
     "exempt_reason": "'Platforms MUST communicate their profile URI with each request to enable capability negotiation' (overview.md L438-439@01-23 / L408@01-11) binds the PLATFORM's request authoring (the UCP-Agent header), not the business; the spec places no duty on the business to reject header-less requests, so no merchant response reflects it and the tester itself sends the header. Requester-bound per policy F2; precedent CHK-046/DISC-006@04-08. [Scoped: at 2026-04-08 this id is a different, already-covered requirement.]"
    },
    {
     "id": "NEG-002",
     "area": "discovery-negotiation",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "For HTTP transport, platforms MUST use Dictionary Structured Field syntax (RFC 8941) in the UCP-Agent header",
     "source": "ucp:docs/specification/overview.md#L441",
     "covered_by": [],
     "exempt_reason": "'For HTTP transport, platforms MUST use Dictionary Structured Field syntax (RFC 8941) in the UCP-Agent header' (overview.md L441@01-23 / L411@01-11) binds the PLATFORM: RFC 8941 header syntax is authored by the platform and is not observable as a business obligation. Requester-bound per policy F2; precedent CHK-046@04-08. [Scoped: at 2026-04-08 this id is a different, already-covered requirement.]"
    },
    {
     "id": "NEG-003",
     "area": "discovery-negotiation",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "For MCP transport, platforms MUST include a meta object containing request metadata",
     "source": "ucp:docs/specification/overview.md#L453",
     "covered_by": [],
     "exempt_reason": "'MCP Transport: Platforms MUST include a meta object containing request metadata' (overview.md L453-454) binds the REQUEST AUTHOR on the MCP binding: the business only receives whatever the platform sent, the spec mandates no business-side rejection of meta-less calls, and the tester \u2014 being the platform \u2014 simply includes meta. No merchant response can prove or refute a third-party platform's compliance. [Scoped: at 2026-04-08 this id is the profile_unreachable/HTTP 424 mapping, testable and covered by merchant_checks_04_08_discovery; not a row at 2026-01-11.]"
    },
    {
     "id": "NEG-004",
     "area": "discovery-negotiation",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Platforms MUST include their profile URI in every request using the transport-appropriate mechanism",
     "source": "ucp:docs/specification/overview.md#L479",
     "covered_by": [],
     "exempt_reason": "'Platforms MUST include their profile URI in every request using the transport-appropriate mechanism' (overview.md L479@01-23 / L446@01-11) binds the PLATFORM's request authoring; profile-URI advertisement is the platform's duty and the tester satisfies it on every request. Requester-bound per policy F2; precedent DISC-006@04-08. [Scoped: at 2026-04-08 this id is a different, already-covered requirement.]"
    },
    {
     "id": "NEG-007",
     "area": "discovery-negotiation",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Platforms MUST validate that capability spec URI origins match namespace authorities",
     "source": "ucp:docs/specification/overview.md#L484",
     "covered_by": [],
     "exempt_reason": "Binds the platform, not the business under test (overview.md Platform Requirements): validating spec-URI origins against namespace authorities is an internal client step that surfaces in no message a conformance run can elicit from the merchant."
    },
    {
     "id": "NEG-008",
     "area": "discovery-negotiation",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Platforms MUST fetch and compose schemas for negotiated capabilities before making requests",
     "source": "ucp:docs/specification/overview.md#L486",
     "covered_by": [],
     "exempt_reason": "Binds the platform: fetching and composing schemas happens BEFORE requests are made (overview.md Platform Requirements); the business only sees the resulting traffic, which is identical whether or not the platform resolved schemas."
    },
    {
     "id": "NEG-009",
     "area": "discovery-negotiation",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "gap",
     "requirement": "Upon receiving a request with a platform profile URI, businesses MUST fetch and validate the platform profile unless already cached",
     "source": "ucp:docs/specification/overview.md#L492",
     "covered_by": []
    },
    {
     "id": "NEG-010",
     "area": "discovery-negotiation",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "gap",
     "requirement": "Businesses MUST compute the intersection of platform and business capabilities",
     "source": "ucp:docs/specification/overview.md#L494",
     "covered_by": []
    },
    {
     "id": "NEG-011",
     "area": "discovery-negotiation",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "gap",
     "requirement": "Extensions without their parent capability in the intersection MUST be excluded",
     "source": "ucp:docs/specification/overview.md#L497",
     "covered_by": []
    },
    {
     "id": "NEG-012",
     "area": "discovery-negotiation",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Businesses MUST include the ucp field in every response containing version and active capabilities",
     "source": "ucp:docs/specification/overview.md#L498",
     "covered_by": [
      "area_negotiation.py"
     ]
    },
    {
     "id": "NEG-013",
     "area": "discovery-negotiation",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "If negotiation fails, businesses MUST return an error response",
     "source": "ucp:docs/specification/overview.md#L522",
     "covered_by": [
      "merchant_checks_01_11_01_23.py"
     ]
    },
    {
     "id": "NEG-014",
     "area": "discovery-negotiation",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Businesses MUST validate the platform's version and determine compatibility",
     "source": "ucp:docs/specification/overview.md#L1151",
     "covered_by": [
      "merchant_checks_01_11_01_23.py"
     ]
    },
    {
     "id": "NEG-015",
     "area": "discovery-negotiation",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "If platform version <= business version, the business MUST process the request",
     "source": "ucp:docs/specification/overview.md#L1155",
     "covered_by": [
      "merchant_checks.py"
     ]
    },
    {
     "id": "NEG-016",
     "area": "discovery-negotiation",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "If platform version > business version, the business MUST return version_unsupported error",
     "source": "ucp:docs/specification/overview.md#L1157",
     "covered_by": [
      "area_negotiation.py"
     ]
    },
    {
     "id": "NEG-017",
     "area": "discovery-negotiation",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Businesses MUST include the version used for processing in every response",
     "source": "ucp:docs/specification/overview.md#L1159",
     "covered_by": [
      "area_negotiation.py"
     ]
    },
    {
     "id": "NEG-018",
     "area": "discovery-negotiation",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "All UCP communication MUST occur over HTTPS",
     "source": "ucp:docs/specification/overview.md#L1074",
     "covered_by": [
      "merchant_checks_01_11_01_23.py"
     ]
    },
    {
     "id": "NEG-019",
     "area": "discovery-negotiation",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "REST requests and responses MUST use Content-Type application/json",
     "source": "ucp:docs/specification/overview.md#L1029",
     "covered_by": [
      "area_negotiation.py"
     ]
    },
    {
     "id": "NEG-020",
     "area": "discovery-negotiation",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "REST implementations MUST use standard HTTP verbs (e.g., POST for creation, GET for retrieval)",
     "source": "ucp:docs/specification/overview.md#L1030",
     "covered_by": [
      "merchant_checks_01_11_01_23.py"
     ]
    },
    {
     "id": "NEG-021",
     "area": "discovery-negotiation",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "REST implementations MUST use standard HTTP status codes (e.g., 200, 201, 400, 401, 500)",
     "source": "ucp:docs/specification/overview.md#L1032",
     "covered_by": [
      "merchant_checks_01_11_01_23.py"
     ]
    },
    {
     "id": "ORD-001",
     "area": "order",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Get Order MUST return the full order entity as a current-state snapshot (complete record of what was purchased, how it will be delivered, and what has happened since placement), reflecting line items, fulfillment, and event logs",
     "source": "ucp:docs/specification/order.md#L24",
     "covered_by": [
      "area_order.py",
      "merchant_checks.py"
     ]
    },
    {
     "id": "ORD-002",
     "area": "order",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "The order entity MUST include the required top-level fields ucp, id, checkout_id, permalink_url, line_items, fulfillment, and totals",
     "source": "ucp:source/schemas/shopping/order.json#L26",
     "covered_by": [
      "area_order.py",
      "merchant_checks.py"
     ]
    },
    {
     "id": "ORD-003",
     "area": "order",
     "keyword": "MUST NOT",
     "testability": "testable",
     "status": "check",
     "requirement": "currency MUST NOT be treated as a required top-level order field in spec version 2026-01-23 (currency is absent from order.json required[])",
     "source": "ucp:source/schemas/shopping/order.json#L26",
     "covered_by": [
      "area_order.py"
     ]
    },
    {
     "id": "ORD-004",
     "area": "order",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Each order line item MUST include id, item, quantity, totals, and status",
     "source": "ucp:source/schemas/shopping/types/order_line_item.json#L6",
     "covered_by": [
      "area_order.py",
      "merchant_checks.py"
     ]
    },
    {
     "id": "ORD-005",
     "area": "order",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Line item quantity MUST include both total and fulfilled counts",
     "source": "ucp:source/schemas/shopping/types/order_line_item.json#L24",
     "covered_by": [
      "area_order.py"
     ]
    },
    {
     "id": "ORD-006",
     "area": "order",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Line items reflect what was purchased at checkout and MUST be retained as the immutable source of truth for what was ordered",
     "source": "ucp:source/schemas/shopping/order.json#L57",
     "covered_by": [
      "merchant_checks_01_11_01_23.py"
     ]
    },
    {
     "id": "ORD-007",
     "area": "order",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Adjustments are an append-only log; each adjustment MUST include id, type, occurred_at, and status",
     "source": "ucp:source/schemas/shopping/types/adjustment.json#L7",
     "covered_by": [
      "merchant_checks_01_11_01_23.py"
     ]
    },
    {
     "id": "ORD-009",
     "area": "order",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Each fulfillment event (append-only log of actual shipments) MUST include id, occurred_at, type, and line_items",
     "source": "ucp:source/schemas/shopping/types/fulfillment_event.json#L7",
     "covered_by": [
      "merchant_checks_01_11_01_23.py"
     ]
    },
    {
     "id": "ORD-010",
     "area": "order",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Each fulfillment expectation (buyer-facing grouping) MUST include id, line_items, method_type, and destination",
     "source": "ucp:source/schemas/shopping/types/expectation.json#L7",
     "covered_by": [
      "area_order.py"
     ]
    },
    {
     "id": "ORD-011",
     "area": "order",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "gap",
     "requirement": "The platform MUST provide its webhook URL (webhook_url) in the order capability's config field during negotiation; the platform order capability config requires webhook_url",
     "source": "ucp:source/schemas/shopping/order.json#L14",
     "covered_by": []
    },
    {
     "id": "ORD-012",
     "area": "order",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "Businesses POST order lifecycle events to the platform-provided webhook URL, sending the full order entity (not incremental deltas)",
     "source": "ucp:docs/specification/order.md#L339",
     "covered_by": [
      "area_webhook.py"
     ]
    },
    {
     "id": "ORD-013",
     "area": "order",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "Businesses MUST send an 'Order created' event with a fully populated order entity",
     "source": "ucp:docs/specification/order.md#L338",
     "covered_by": [
      "area_webhook.py"
     ]
    },
    {
     "id": "ORD-014",
     "area": "order",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "Webhook payloads MUST be signed by the business and verified by the platform to ensure authenticity and integrity",
     "source": "ucp:docs/specification/order.md#L296",
     "covered_by": [
      "merchant_checks_01_11_01_23_events.py"
     ]
    },
    {
     "id": "ORD-015",
     "area": "order",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "Businesses MUST sign all webhook payloads using a key from their signing_keys array (published in /.well-known/ucp), and the signature MUST be included in the Request-Signature header as a detached JWT (RFC 7797)",
     "source": "ucp:docs/specification/order.md#L335",
     "covered_by": [
      "merchant_checks_01_11_01_23_events.py"
     ]
    },
    {
     "id": "ORD-016",
     "area": "order",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "Businesses MUST retry failed webhook deliveries",
     "source": "ucp:docs/specification/order.md#L340",
     "covered_by": [
      "merchant_checks_01_11_01_23_events.py"
     ]
    },
    {
     "id": "ORD-017",
     "area": "order",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "gap",
     "requirement": "Businesses MUST include a business identifier in the webhook path or headers",
     "source": "ucp:docs/specification/order.md#L341",
     "covered_by": []
    },
    {
     "id": "ORD-018",
     "area": "order",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "The platform MUST respond quickly with a 2xx HTTP status code to acknowledge receipt of a webhook, then process events asynchronously after responding",
     "source": "ucp:docs/specification/order.md#L330",
     "covered_by": [],
     "exempt_reason": "'The platform MUST respond quickly with a 2xx HTTP status code to acknowledge receipt of a webhook, then process events asynchronously after responding' (order.md L330, Platform Guidelines) binds the webhook RECEIVER (the platform), not the business under test; the receiver's acknowledgement/async behavior leaves no wire trace on the merchant. The 04-08 twin is ORD-032. [Scoped: at 2026-04-08 this id is a different, already-covered requirement.]"
    },
    {
     "id": "ORD-019",
     "area": "order",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "On webhook verification: the platform extracts Request-Signature, parses the JWT header for kid, fetches the business UCP profile, locates the matching key in signing_keys, and verifies the signature against the request body; if verification fails, it MUST reject the webhook with an appropriate error response",
     "source": "ucp:docs/specification/order.md#L314",
     "covered_by": [],
     "exempt_reason": "'On webhook verification ... if verification fails, it MUST reject the webhook with an appropriate error response' (order.md L314, Verification (Platform)) is the PLATFORM receiver's verification/rejection procedure \u2014 receiver-side conduct, not a business obligation observable on the merchant. The business-side signing-key publication is checked elsewhere; the 04-08 twin is ORD-033. [Scoped: at 2026-04-08 this id is a different, already-covered requirement.]"
    },
    {
     "id": "OVR-001",
     "area": "overview",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Sensitive data such as payment credentials or PII MUST be handled according to PCI-DSS and GDPR guidelines",
     "source": "ucp:docs/specification/overview.md#L1085",
     "covered_by": [],
     "exempt_reason": "Client/platform-bound obligation surfaced by the register-completeness pass: Sensitive data such as payment credentials or PII MUST be handled according to PCI-DSS and GDPR guidelines \u2014 binds the platform/agent (or is a spec-authoring rule), not the merchant server under test; a merchant check would be fake coverage. Same treatment as the existing client-bound PAY exemptions. Version-scoped so it never collides with a version where this id names a checked requirement."
    },
    {
     "id": "PAY-001",
     "area": "payment",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Every payment handler specification must define the core required elements.",
     "source": "ucp:docs/specification/payment-handler-guide.md#L52",
     "covered_by": [],
     "exempt_reason": "'Every payment handler specification MUST define the core elements below' (payment-handler-guide.md L52) binds AUTHORS of payment-handler specification documents \u2014 a party distinct from the business or platform under test; whether a third-party handler spec document defines identity/participants/credentials etc. is established by document review, and no runtime message reflects it. [Scoped: at 2026-04-08 this id is the testable payment_handlers-registry schema requirement, covered by the 04-08 payment checks; the 04-08 twin of this requirement is PAY-028.]"
    },
    {
     "id": "PAY-002",
     "area": "payment",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "A payment handler base/declaration must include an id field identifying the handler instance.",
     "source": "ucp:source/schemas/payment_handler.json#L13",
     "covered_by": [
      "area_payment.py"
     ]
    },
    {
     "id": "PAY-003",
     "area": "payment",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "A platform-schema payment handler declaration must include spec and schema fields.",
     "source": "ucp:source/schemas/payment_handler.json#L21",
     "covered_by": [
      "schema_check_01_11_01_23.py"
     ]
    },
    {
     "id": "PAY-004",
     "area": "payment",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Payment handler specifications must explicitly document the participant field mappings.",
     "source": "ucp:docs/specification/payment-handler-guide.md#L94",
     "covered_by": [],
     "exempt_reason": "'Specifications MUST explicitly document these field mappings' (payment-handler-guide.md L94, participant mappings) binds handler-spec AUTHORS: documentation completeness of a third-party specification is a document-review property with no runtime counterpart in any message the suite can elicit. [Scoped: at 2026-04-08 this id is the testable platform-declaration spec+schema-URL schema requirement, covered by schema_check_04_08_payment; at 04-08 the mapping duty folds into PAY-028's core-elements list.]"
    },
    {
     "id": "PAY-005",
     "area": "payment",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "A payment handler specification must define which credential types are accepted by the handler.",
     "source": "ucp:docs/specification/payment-handler-guide.md#L525",
     "covered_by": [],
     "exempt_reason": "'The specification MUST define which credential types are accepted' (payment-handler-guide.md L265@01-11 / L525@01-23) binds handler-spec AUTHORS \u2014 whether a third-party handler document declares its accepted credential types is decided by reading that document, not by any protocol exchange with the business under test. [Scoped: at 2026-04-08 this id is the testable available_instruments minItems-1 schema requirement, covered by schema_check_04_08_payment; the 04-08 twin of this requirement is PAY-029.]"
    },
    {
     "id": "PAY-006",
     "area": "payment",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "If using token credentials, the handler schema must include an expiry/ttl field (or mechanism to refresh credentials).",
     "source": "ucp:docs/specification/payment-handler-guide.md#L528",
     "covered_by": [],
     "exempt_reason": "Binds authors of payment-handler credential schemas (payment-handler-guide.md): whether a token schema includes 'expiry, ttl, or similar' is a design requirement on a third-party specification document, with an open-ended naming clause ('or similar', 'or mechanism to refresh') that no fixed predicate can arbitrate without false-flagging conforming handlers. [Scoped: at 2026-04-08 this id is an unrelated MAY (available_instruments optionality); the 04-08 twin of this requirement is PAY-025.]"
    },
    {
     "id": "PAY-007",
     "area": "payment",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "A handler specification must define a mapping for common payment failures.",
     "source": "ucp:docs/specification/payment-handler-guide.md#L648",
     "covered_by": [],
     "exempt_reason": "'The specification MUST define a mapping for common failures' (payment-handler-guide.md L343@01-11 / L648@01-23) binds handler-spec AUTHORS: the failure-mapping table lives in a third-party specification document and is verified by reading it \u2014 no message from the business under test evidences whether its handler's spec defined one. [Scoped: at 2026-04-08 this id is the testable available_payment_instrument type-required schema rule, covered by schema_check_04_08_payment; at 04-08 the error-mapping duty folds into PAY-028's core-elements list.]"
    },
    {
     "id": "PAY-008",
     "area": "payment",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "A payment instrument must include id, handler_id, and type, where handler_id corresponds to the id of the payment handler definition.",
     "source": "ucp:source/schemas/shopping/types/payment_instrument.json#L7",
     "covered_by": [
      "schema_check_01_11_01_23.py"
     ]
    },
    {
     "id": "PAY-009",
     "area": "payment",
     "keyword": "MUST NOT",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "Credentials flow Platform to Business only; businesses must not echo credentials back in responses (unidirectional flow).",
     "source": "ucp:docs/specification/overview.md#L600",
     "covered_by": [
      "area_payment.py",
      "merchant_checks.py"
     ]
    },
    {
     "id": "PAY-010",
     "area": "payment",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "The handler_id in the payload ensures the business knows which payment credential provider key to use, preventing key confusion attacks (Handler ID Routing).",
     "source": "ucp:docs/specification/overview.md#L602",
     "covered_by": [
      "schema_check_01_11_01_23.py"
     ]
    },
    {
     "id": "PAY-012",
     "area": "payment",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "gap",
     "requirement": "Businesses must filter the handlers list based on the context of the cart (dynamic filtering).",
     "source": "ucp:docs/specification/overview.md#L642",
     "covered_by": []
    },
    {
     "id": "PAY-013",
     "area": "payment",
     "keyword": "MUST NOT",
     "testability": "manual",
     "status": "exempt",
     "requirement": "The card credential type must not be used for checkout, only with payment handlers that tokenize or encrypt credentials.",
     "source": "ucp:source/schemas/shopping/types/card_credential.json#L5",
     "covered_by": [],
     "exempt_reason": "Prohibits a USAGE by the credential sender \u2014 the platform submitting raw PANs at checkout (card_credential.json description). The spec places no rejection duty on the receiving business (the official sample suite itself accepts a raw card credential at checkout with 200), so no merchant response can prove or refute the prohibition. [Scoped: at 2026-04-08 this id is an unrelated SHOULD NOT (intermediary touching raw credentials); the 04-08 twin of this requirement is PAY-022.]"
    },
    {
     "id": "PAY-014",
     "area": "payment",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Both parties handling a CardCredential (sender and receiver) must be PCI DSS compliant, and transmission must use HTTPS/TLS with strong cipher suites.",
     "source": "ucp:source/schemas/shopping/types/card_credential.json#L5",
     "covered_by": [],
     "exempt_reason": "'Both parties handling CardCredential (sender and receiver) MUST be PCI DSS compliant. Transmission MUST use HTTPS/TLS with strong cipher suites' (card_credential.json description): PCI DSS compliance is an organizational certification established by out-of-band audit \u2014 no protocol message carries it \u2014 and the transmission clause binds whichever channel actually carries a raw PAN (which conformant checkout traffic never does, per PAY-013); generic endpoint transport is separately accounted by the HTTPS/TLS rows. [Scoped: at 2026-04-08 this id is the handler_id-validation business obligation, a needs-receiver row left to its own accounting.]"
    },
    {
     "id": "PAY-015",
     "area": "payment",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "A card credential must include type (const 'card') and card_number_type.",
     "source": "ucp:source/schemas/shopping/types/card_credential.json#L12",
     "covered_by": [
      "schema_check_01_11_01_23.py"
     ]
    },
    {
     "id": "PAY-016",
     "area": "payment",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "A token credential must include type and token.",
     "source": "ucp:source/schemas/shopping/types/token_credential.json#L12",
     "covered_by": [
      "schema_check_01_11_01_23.py"
     ]
    },
    {
     "id": "PAY-017",
     "area": "payment",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "A binding must include checkout_id identifying the checkout session the token is bound to (prevents token reuse across checkouts/participants).",
     "source": "ucp:source/schemas/shopping/types/binding.json#L7",
     "covered_by": [
      "schema_check_01_11_01_23.py"
     ]
    },
    {
     "id": "PAY-018",
     "area": "payment",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "gap",
     "requirement": "The binding identity is required when acting on behalf of another participant (e.g., agent tokenizing for merchant).",
     "source": "ucp:source/schemas/shopping/types/binding.json#L15",
     "covered_by": []
    },
    {
     "id": "PAY-019",
     "area": "payment",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "Businesses must embed a cryptographic signature (merchant_authorization) in checkout responses when AP2 is negotiated.",
     "source": "ucp:docs/specification/ap2-mandates.md#L31",
     "covered_by": [
      "merchant_checks_01_11_01_23_receiver.py"
     ]
    },
    {
     "id": "PAY-020",
     "area": "payment",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Platforms must provide cryptographically signed proofs (Mandates) for autonomous/non-repudiable authorization.",
     "source": "ucp:docs/specification/ap2-mandates.md#L33",
     "covered_by": [],
     "exempt_reason": "Platform-bound MUST (@01-era only; covered at 04-08): platforms provide cryptographically signed AP2 Mandates. The business AP2 duties are checked by PAY-019/021/026/027/035."
    },
    {
     "id": "PAY-021",
     "area": "payment",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "When ap2_mandate is negotiated, the business must include ap2.merchant_authorization in all relevant checkout responses.",
     "source": "ucp:docs/specification/ap2-mandates.md#L108",
     "covered_by": [
      "merchant_checks_01_11_01_23_receiver.py"
     ]
    },
    {
     "id": "PAY-022",
     "area": "payment",
     "keyword": "MUST NOT",
     "testability": "needs-receiver",
     "status": "gap",
     "requirement": "When ap2_mandate is negotiated, the business must not accept a complete_checkout request that lacks ap2.checkout_mandate.",
     "source": "ucp:docs/specification/ap2-mandates.md#L110",
     "covered_by": []
    },
    {
     "id": "PAY-023",
     "area": "payment",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "The platform must verify the business's merchant signature before presenting checkout terms to the user.",
     "source": "ucp:docs/specification/ap2-mandates.md#L112",
     "covered_by": [],
     "exempt_reason": "Platform-bound MUST (@01-era only; covered at 04-08): the PLATFORM verifies the business's merchant signature before presenting terms. The business duty to embed a valid ES256 merchant signature is checked by PAY-035."
    },
    {
     "id": "PAY-024",
     "area": "payment",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "gap",
     "requirement": "A public signing key must be available for the business to verify the mandate's signature.",
     "source": "ucp:docs/specification/ap2-mandates.md#L117",
     "covered_by": []
    },
    {
     "id": "PAY-025",
     "area": "payment",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "gap",
     "requirement": "If a public key cannot be resolved, or if the signature is invalid, the business must return an error.",
     "source": "ucp:docs/specification/ap2-mandates.md#L123",
     "covered_by": []
    },
    {
     "id": "PAY-026",
     "area": "payment",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "All AP2 signatures must use one of the approved algorithms (ES256/ES384/ES512).",
     "source": "ucp:docs/specification/ap2-mandates.md#L130",
     "covered_by": [
      "merchant_checks_01_11_01_23_receiver.py"
     ]
    },
    {
     "id": "PAY-027",
     "area": "payment",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "Businesses must embed their signature in the checkout response body under ap2.merchant_authorization.",
     "source": "ucp:docs/specification/ap2-mandates.md#L140",
     "covered_by": [
      "merchant_checks_01_11_01_23_receiver.py"
     ]
    },
    {
     "id": "PAY-028",
     "area": "payment",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "gap",
     "requirement": "The AP2 signature must cover both the JWS header and the checkout payload.",
     "source": "ucp:docs/specification/ap2-mandates.md#L172",
     "covered_by": []
    },
    {
     "id": "PAY-029",
     "area": "payment",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Completion of an autonomous AP2 flow must produce two distinct mandate artifacts: a checkout_mandate and a payment_mandate.",
     "source": "ucp:docs/specification/ap2-mandates.md#L200",
     "covered_by": [
      "schema_check_01_11_01_23.py"
     ]
    },
    {
     "id": "PAY-030",
     "area": "payment",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "gap",
     "requirement": "The checkout mandate must contain the full checkout response including the business's signature (nested signature model).",
     "source": "ucp:docs/specification/ap2-mandates.md#L207",
     "covered_by": []
    },
    {
     "id": "PAY-031",
     "area": "payment",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "gap",
     "requirement": "Both parties must follow the locked AP2 completion flow once the ap2_mandate capability is negotiated; a completion request without mandates must result in a session failure.",
     "source": "ucp:docs/specification/ap2-mandates.md#L233",
     "covered_by": []
    },
    {
     "id": "PAY-032",
     "area": "payment",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "gap",
     "requirement": "A completion request without mandates must result in a session failure.",
     "source": "ucp:docs/specification/ap2-mandates.md#L235",
     "covered_by": []
    },
    {
     "id": "PAY-033",
     "area": "payment",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "gap",
     "requirement": "For signature computation over JSON payloads, implementations must use JSON canonicalization (JCS).",
     "source": "ucp:docs/specification/ap2-mandates.md#L218",
     "covered_by": []
    },
    {
     "id": "PAY-034",
     "area": "payment",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "gap",
     "requirement": "On receiving the complete request, the business must check the mandate is present and return a mandate_required error if ap2.checkout_mandate is missing.",
     "source": "ucp:docs/specification/ap2-mandates.md#L371",
     "covered_by": []
    },
    {
     "id": "PAY-035",
     "area": "payment",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "The merchant_authorization is a JWS Detached Content signature; its header must contain alg (ES256/ES384/ES512) and kid claims.",
     "source": "ucp:source/schemas/shopping/ap2_mandate.json#L12",
     "covered_by": [
      "merchant_checks_01_23.py"
     ]
    },
    {
     "id": "PAY-036",
     "area": "payment",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "On AP2 complete requests the checkout_mandate (and ap2 object) is required.",
     "source": "ucp:source/schemas/shopping/ap2_mandate.json#L43",
     "covered_by": [
      "schema_check_01_23.py"
     ]
    },
    {
     "id": "PAY-037",
     "area": "payment",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "A platform in the trusted-platform-provider model must publish at least one key in the top-level signing_keys array of its profile.",
     "source": "ucp:docs/specification/ap2-mandates.md#L100",
     "covered_by": [],
     "exempt_reason": "Client/platform-bound obligation surfaced by the register-completeness pass: A platform in the trusted-platform-provider model must publish at least one key in the top-level signing_keys array of its profile \u2014 binds the platform/agent (or is a spec-authoring rule), not the merchant server under test; a merchant check would be fake coverage. Same treatment as the existing client-bound PAY exemptions. Version-scoped so it never collides with a version where this id names a checked requirement."
    },
    {
     "id": "PAY-038",
     "area": "payment",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "The trusted platform provider must ensure that mandates are not created without explicit user consent from trusted, deterministic channels.",
     "source": "ucp:docs/specification/ap2-mandates.md#L305",
     "covered_by": [],
     "exempt_reason": "Client/platform-bound obligation surfaced by the register-completeness pass: The trusted platform provider must ensure that mandates are not created without explicit user consent from trusted, deterministic  \u2014 binds the platform/agent (or is a spec-authoring rule), not the merchant server under test; a merchant check would be fake coverage. Same treatment as the existing client-bound PAY exemptions. Version-scoped so it never collides with a version where this id names a checked requirement."
    },
    {
     "id": "SEC-001",
     "area": "errors-validation-security",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "A request to the simulation endpoint without the Simulation-Secret header MUST be rejected with 403 Forbidden.",
     "source": "ucp:docs/specification/overview.md#L1032-L1033",
     "covered_by": [
      "area_validation.py"
     ]
    },
    {
     "id": "SEC-002",
     "area": "errors-validation-security",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "A request to the simulation endpoint with an incorrect Simulation-Secret MUST be rejected with 403 Forbidden.",
     "source": "ucp:docs/specification/overview.md#L1032-L1033",
     "covered_by": [
      "area_validation.py"
     ]
    },
    {
     "id": "SEC-003",
     "area": "errors-validation-security",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "A request to the simulation endpoint with the correct Simulation-Secret MUST be accepted with 200 OK.",
     "source": "ucp:docs/specification/overview.md#L1032-L1033",
     "covered_by": [
      "area_validation.py"
     ]
    },
    {
     "id": "SEC-004",
     "area": "errors-validation-security",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "All UCP communication MUST occur over HTTPS.",
     "source": "ucp:docs/specification/overview.md#L1074",
     "covered_by": [
      "merchant_checks_01_11_01_23.py"
     ]
    },
    {
     "id": "VAL-001",
     "area": "errors-validation-security",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "When an out-of-stock item is added to a checkout session, the server MUST return a 400 Bad Request error indicating insufficient stock.",
     "source": "ucp:docs/specification/overview.md#L1032-L1033",
     "covered_by": [
      "merchant_checks.py",
      "v2026_01_23.py"
     ]
    },
    {
     "id": "VAL-002",
     "area": "errors-validation-security",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "An inventory/quantity validation failure on checkout creation MUST return a 400 Bad Request error.",
     "source": "ucp:docs/specification/overview.md#L1032-L1033",
     "covered_by": [
      "merchant_checks.py"
     ]
    },
    {
     "id": "VAL-003",
     "area": "errors-validation-security",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "A request referencing a non-existent product MUST return a 400 Bad Request error indicating the product was not found.",
     "source": "ucp:docs/specification/overview.md#L1032-L1033",
     "covered_by": [
      "merchant_checks.py",
      "v2026_01_23.py"
     ]
    },
    {
     "id": "VAL-004",
     "area": "errors-validation-security",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Submitting a payment instrument with a known failing token at checkout completion MUST return a 402 Payment Required error.",
     "source": "ucp:docs/specification/overview.md#L1032-L1033",
     "covered_by": [
      "merchant_checks.py",
      "v2026_01_23.py"
     ]
    },
    {
     "id": "VAL-005",
     "area": "errors-validation-security",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Attempting to complete a checkout without required fulfillment MUST return a 400 Bad Request error.",
     "source": "ucp:docs/specification/overview.md#L1032-L1033",
     "covered_by": [
      "area_validation.py"
     ]
    },
    {
     "id": "VAL-006",
     "area": "errors-validation-security",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Error responses MUST be structured: a 400 error body MUST contain a populated 'detail' field describing the error.",
     "source": "ucp:source/schemas/shopping/types/message_error.json#L35-L38",
     "covered_by": [
      "area_validation.py",
      "merchant_checks.py"
     ]
    },
    {
     "id": "VAL-007",
     "area": "errors-validation-security",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "An update specifying an invalid adjustment status (non-conforming literal) MUST be rejected with a 422 Unprocessable Entity error due to validation failure.",
     "source": "ucp:docs/specification/overview.md#L1032-L1033",
     "covered_by": [
      "area_validation.py"
     ]
    },
    {
     "id": "VAL-008",
     "area": "errors-validation-security",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "An update with a malformed adjustments field (e.g., a dict instead of a list) MUST return a 422 Unprocessable Entity error.",
     "source": "ucp:docs/specification/overview.md#L1032-L1033",
     "covered_by": [
      "area_validation.py"
     ]
    },
    {
     "id": "VAL-009",
     "area": "errors-validation-security",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "A request using an unknown discount code MUST succeed with 200 OK, with no discount applied (graceful handling, not an error).",
     "source": "ucp:docs/specification/overview.md#L1032-L1033",
     "covered_by": [
      "area_validation.py"
     ]
    }
   ]
  },
  "2026-04-08": {
   "musts": 364,
   "check": 246,
   "exempt": 81,
   "gap": 37,
   "accounted_pct": 90,
   "gap_by_testability": {
    "manual": 7,
    "needs-oauth": 3,
    "needs-receiver": 27
   },
   "areas": [
    {
     "area": "cart",
     "check": 11,
     "exempt": 2,
     "gap": 1,
     "gap_needs-receiver": 1,
     "musts": 14
    },
    {
     "area": "catalog",
     "check": 27,
     "exempt": 4,
     "gap": 1,
     "gap_manual": 1,
     "musts": 32
    },
    {
     "area": "checkout-lifecycle",
     "check": 21,
     "exempt": 8,
     "gap": 4,
     "gap_needs-receiver": 4,
     "musts": 33
    },
    {
     "area": "discounts-consent",
     "check": 27,
     "exempt": 1,
     "gap": 1,
     "gap_needs-receiver": 1,
     "musts": 29
    },
    {
     "area": "discovery-and-profile",
     "check": 5,
     "exempt": 1,
     "musts": 6
    },
    {
     "area": "error-envelope",
     "check": 13,
     "exempt": 10,
     "gap": 2,
     "gap_manual": 2,
     "musts": 25
    },
    {
     "area": "fulfillment",
     "check": 6,
     "exempt": 6,
     "gap": 1,
     "gap_manual": 1,
     "musts": 13
    },
    {
     "area": "identity-linking",
     "check": 33,
     "exempt": 21,
     "gap": 3,
     "gap_manual": 1,
     "gap_needs-oauth": 2,
     "musts": 57
    },
    {
     "area": "order",
     "check": 21,
     "exempt": 4,
     "musts": 25
    },
    {
     "area": "overview",
     "exempt": 2,
     "gap": 11,
     "gap_manual": 2,
     "gap_needs-oauth": 1,
     "gap_needs-receiver": 8,
     "musts": 13
    },
    {
     "area": "payment",
     "check": 28,
     "exempt": 13,
     "gap": 3,
     "gap_needs-receiver": 3,
     "musts": 44
    },
    {
     "area": "signals-attribution-eligibility",
     "check": 11,
     "exempt": 1,
     "gap": 2,
     "gap_needs-receiver": 2,
     "musts": 14
    },
    {
     "area": "signatures",
     "check": 23,
     "exempt": 2,
     "gap": 8,
     "gap_needs-receiver": 8,
     "musts": 33
    },
    {
     "area": "totals",
     "check": 9,
     "exempt": 5,
     "musts": 14
    },
    {
     "area": "transports",
     "check": 6,
     "exempt": 1,
     "musts": 7
    },
    {
     "area": "version-negotiation-and-protocol-errors",
     "check": 5,
     "musts": 5
    }
   ],
   "rows": [
    {
     "id": "A2A-001",
     "area": "transports",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "The A2A business agent MUST return the checkout object inside an A2A Message DataPart keyed `a2a.ucp.checkout`",
     "source": "ucp:docs/specification/checkout-a2a.md#L144",
     "covered_by": [
      "merchant_checks.py"
     ]
    },
    {
     "id": "CART-001",
     "area": "cart",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "On cart-to-checkout conversion, business MUST use cart contents and MUST ignore overlapping fields in the checkout payload",
     "source": "ucp:docs/specification/cart.md#L66",
     "covered_by": [
      "merchant_checks_04_08_receiver.py"
     ]
    },
    {
     "id": "CART-002",
     "area": "cart",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "If an incomplete checkout already exists for the given cart_id, business MUST return the existing checkout session rather than creating a new one (idempotent conversion)",
     "source": "ucp:docs/specification/cart.md#L73",
     "covered_by": [
      "merchant_checks_04_08_receiver.py"
     ]
    },
    {
     "id": "CART-017",
     "area": "cart",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "For Update Cart (full replacement), the platform MUST send the entire cart resource",
     "source": "ucp:docs/specification/cart.md#L176",
     "covered_by": [
      "merchant_checks_04_08_cartupdate.py"
     ]
    },
    {
     "id": "CART-018",
     "area": "cart",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "On Cancel Cart, business MUST return the cart state before deletion",
     "source": "ucp:docs/specification/cart.md#L185",
     "covered_by": [
      "merchant_checks_04_08_receiver.py"
     ]
    },
    {
     "id": "CART-020",
     "area": "cart",
     "keyword": "MUST NOT",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Signal values MUST NOT be buyer-asserted claims",
     "source": "ucp:docs/specification/cart.md#L229",
     "covered_by": [],
     "exempt_reason": "Platform-bound MUST NOT: platforms provide signal values from direct observation; a business cannot be tested for the provenance of platform-supplied signals (overview.md #signals). No business-observable dual."
    },
    {
     "id": "CART-022",
     "area": "cart",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "All cart REST request and response bodies MUST be valid JSON as specified in RFC 8259",
     "source": "ucp:docs/specification/cart-rest.md#L74",
     "covered_by": [
      "merchant_checks_04_08_lifecycle.py"
     ]
    },
    {
     "id": "CART-023",
     "area": "cart",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "All cart REST endpoints MUST be served over HTTPS with minimum TLS version 1.3",
     "source": "ucp:docs/specification/cart-rest.md#L79",
     "covered_by": [
      "merchant_checks_04_08_lifecycle.py"
     ]
    },
    {
     "id": "CART-024",
     "area": "cart",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "All cart requests MUST include the UCP-Agent header containing the platform profile URI",
     "source": "ucp:docs/specification/cart-rest.md#L468",
     "covered_by": [],
     "exempt_reason": "Binds the REQUEST AUTHOR: 'All requests MUST include the UCP-Agent header' places the include-duty on the platform, and the suite's own probe traffic satisfies it. The business-side ENFORCEMENT duty exists SEPARATELY \u2014 overview.md L696 maps a malformed/MISSING/unresolvable profile URL to a 400 invalid_profile_url rejection \u2014 and is covered by register row NEG-005 with its own check, so exempting this row hides no observable obligation (rewritten 2026-07-03, wave-2 review F4: the earlier reason wrongly claimed no business-side duty exists)."
    },
    {
     "id": "CART-026",
     "area": "cart",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "When an Idempotency-Key is provided, the server MUST store the key with the operation result for at least 24 hours, return the cached result for duplicate keys, and return 409 Conflict if the key is reused with different parameters",
     "source": "ucp:docs/specification/cart-rest.md#L473",
     "covered_by": [
      "merchant_checks_04_08_receiver.py"
     ]
    },
    {
     "id": "CART-029",
     "area": "cart",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "A cart response object MUST include ucp, id, line_items, currency, and totals",
     "source": "ucp:source/schemas/shopping/cart.json#L32",
     "covered_by": [
      "area_04_08_cart.py",
      "merchant_checks.py"
     ]
    },
    {
     "id": "CART-030",
     "area": "cart",
     "keyword": "MUST NOT",
     "testability": "needs-receiver",
     "status": "gap",
     "requirement": "cart_id is only available on create_checkout (create:optional) and MUST be omitted on update",
     "source": "ucp:source/schemas/shopping/cart.json#L21",
     "covered_by": []
    },
    {
     "id": "CART-031",
     "area": "cart",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Each cart line item MUST include id, item, quantity, and totals",
     "source": "ucp:source/schemas/shopping/types/line_item.json#L7",
     "covered_by": [
      "area_04_08_cart.py",
      "merchant_checks.py"
     ]
    },
    {
     "id": "CART-032",
     "area": "cart",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Line item quantity MUST be an integer of at least 1",
     "source": "ucp:source/schemas/shopping/types/line_item.json#L27",
     "covered_by": [
      "area_04_08_cart.py"
     ]
    },
    {
     "id": "CART-033",
     "area": "cart",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Each line item's item MUST include id, title, and price",
     "source": "ucp:source/schemas/shopping/types/item.json#L6",
     "covered_by": [
      "area_04_08_cart.py"
     ]
    },
    {
     "id": "CAT-001",
     "area": "catalog",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Pagination response MUST include has_next_page",
     "source": "ucp:source/schemas/shopping/types/pagination.json#L42",
     "covered_by": [
      "area_04_08_catalog.py"
     ]
    },
    {
     "id": "CAT-002",
     "area": "catalog",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "When has_next_page is true, cursor MUST be present (if/then schema constraint)",
     "source": "ucp:source/schemas/shopping/types/pagination.json#L43",
     "covered_by": [
      "area_04_08_catalog.py"
     ]
    },
    {
     "id": "CAT-003",
     "area": "catalog",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Cursor to fetch next page MUST be present when has_next_page is true",
     "source": "ucp:source/schemas/shopping/types/pagination.json#L30",
     "covered_by": [
      "area_04_08_catalog.py"
     ]
    },
    {
     "id": "CAT-005",
     "area": "catalog",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "limit request parameter has minimum 1 and default 10",
     "source": "ucp:source/schemas/shopping/types/pagination.json#L16",
     "covered_by": [
      "schema_check_04_08.py"
     ]
    },
    {
     "id": "CAT-008",
     "area": "catalog",
     "keyword": "MUST NOT",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Clients MUST NOT assume the response size equals the requested limit",
     "source": "ucp:docs/specification/catalog/search.md#L75",
     "covered_by": [],
     "exempt_reason": "Prohibits a client-side ASSUMPTION (search.md L75): what a platform assumes about response size vs requested limit is internal reasoning state that appears in no protocol message in either direction."
    },
    {
     "id": "CAT-009",
     "area": "catalog",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "A valid search request MUST include at least one of: a query string, one or more filters, or an extension-defined input",
     "source": "ucp:docs/specification/catalog/search.md#L40",
     "covered_by": [
      "merchant_checks.py"
     ]
    },
    {
     "id": "CAT-010",
     "area": "catalog",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Implementations MUST validate that incoming requests contain at least one recognized input",
     "source": "ucp:docs/specification/catalog/search.md#L47",
     "covered_by": [
      "merchant_checks.py"
     ]
    },
    {
     "id": "CAT-012",
     "area": "catalog",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Empty search returns products:[] with no messages (not an error)",
     "source": "ucp:docs/specification/catalog/index.md#L191",
     "covered_by": [
      "merchant_checks.py"
     ]
    },
    {
     "id": "CAT-013",
     "area": "catalog",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Lookup MUST support lookup by product ID and variant ID",
     "source": "ucp:docs/specification/catalog/lookup.md#L51",
     "covered_by": [
      "merchant_checks.py"
     ]
    },
    {
     "id": "CAT-014",
     "area": "catalog",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "get_product MUST support product ID and variant ID for the single id parameter",
     "source": "ucp:source/schemas/shopping/catalog_lookup.json#L94",
     "covered_by": [
      "merchant_checks.py"
     ]
    },
    {
     "id": "CAT-015",
     "area": "catalog",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Duplicate identifiers in the request MUST be deduplicated",
     "source": "ucp:docs/specification/catalog/lookup.md#L56",
     "covered_by": [
      "merchant_checks.py"
     ]
    },
    {
     "id": "CAT-016",
     "area": "catalog",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "When multiple identifiers resolve to the same product, it MUST be returned once",
     "source": "ucp:docs/specification/catalog/lookup.md#L58",
     "covered_by": [
      "merchant_checks.py"
     ]
    },
    {
     "id": "CAT-017",
     "area": "catalog",
     "keyword": "MUST NOT",
     "testability": "testable",
     "status": "check",
     "requirement": "Variants without an inputs entry MUST NOT appear in lookup responses",
     "source": "ucp:docs/specification/catalog/lookup.md#L71",
     "covered_by": [
      "area_04_08_catalog.py",
      "merchant_checks.py"
     ]
    },
    {
     "id": "CAT-018",
     "area": "catalog",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "lookup response variants MUST include a non-empty inputs array (correlation metadata)",
     "source": "ucp:source/schemas/shopping/catalog_lookup.json#L13",
     "covered_by": [
      "area_04_08_catalog.py",
      "merchant_checks.py"
     ]
    },
    {
     "id": "CAT-019",
     "area": "catalog",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "For get_product / catalog/product, an unknown identifier (NOT_FOUND) returns HTTP 200 with ucp.status:\"error\" and a descriptive message",
     "source": "ucp:docs/specification/catalog/rest.md#L464",
     "covered_by": [
      "merchant_checks.py"
     ]
    },
    {
     "id": "CAT-020",
     "area": "catalog",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "All application-level outcomes (incl. not-found) return HTTP 200 with the UCP envelope and optional messages array",
     "source": "ucp:docs/specification/catalog/rest.md#L513",
     "covered_by": [
      "merchant_checks.py"
     ]
    },
    {
     "id": "CAT-022",
     "area": "catalog",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "When the Lookup capability is advertised, both /catalog/lookup and /catalog/product MUST be available",
     "source": "ucp:docs/specification/catalog/rest.md#L572",
     "covered_by": [
      "merchant_checks.py"
     ]
    },
    {
     "id": "CAT-023",
     "area": "catalog",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "REST transport MUST implement endpoints for each advertised catalog capability per its requirements",
     "source": "ucp:docs/specification/catalog/rest.md#L570",
     "covered_by": [
      "merchant_checks.py"
     ]
    },
    {
     "id": "CAT-024",
     "area": "catalog",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "REST transport MUST support cursor-based pagination with default limit of 10",
     "source": "ucp:docs/specification/catalog/rest.md#L574",
     "covered_by": [
      "merchant_checks.py"
     ]
    },
    {
     "id": "CAT-025",
     "area": "catalog",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "REST transport MUST return HTTP 200 for lookup requests; unknown identifiers result in fewer products returned",
     "source": "ucp:docs/specification/catalog/rest.md#L575",
     "covered_by": [
      "merchant_checks.py"
     ]
    },
    {
     "id": "CAT-026",
     "area": "catalog",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "REST transport MUST return HTTP 400 with request_too_large error for requests exceeding batch size limits",
     "source": "ucp:docs/specification/catalog/rest.md#L576",
     "covered_by": [
      "merchant_checks.py"
     ]
    },
    {
     "id": "CAT-027",
     "area": "catalog",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Lookup implementations MAY enforce a maximum batch size and MUST reject requests exceeding their limit with an appropriate error",
     "source": "ucp:docs/specification/catalog/lookup.md#L76",
     "covered_by": [
      "merchant_checks.py"
     ]
    },
    {
     "id": "CAT-028",
     "area": "catalog",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Lookup ids array is required with minItems 1",
     "source": "ucp:source/schemas/shopping/catalog_lookup.json#L29",
     "covered_by": [
      "schema_check_04_08.py"
     ]
    },
    {
     "id": "CAT-029",
     "area": "catalog",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "search_response MUST include ucp and products",
     "source": "ucp:source/schemas/shopping/catalog_search.json#L35",
     "covered_by": [
      "v2026_04_08.py"
     ]
    },
    {
     "id": "CAT-030",
     "area": "catalog",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "lookup_response MUST include ucp and products",
     "source": "ucp:source/schemas/shopping/catalog_lookup.json#L53",
     "covered_by": [
      "area_04_08_catalog.py"
     ]
    },
    {
     "id": "CAT-031",
     "area": "catalog",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "get_product_response MUST include ucp and a singular product object",
     "source": "ucp:source/schemas/shopping/catalog_lookup.json#L155",
     "covered_by": [
      "area_04_08_catalog.py"
     ]
    },
    {
     "id": "CAT-032",
     "area": "catalog",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "For Variant ID lookups via get_product, the requested variant MUST be the first element (featured)",
     "source": "ucp:docs/specification/catalog/lookup.md#L131",
     "covered_by": [
      "merchant_checks.py"
     ]
    },
    {
     "id": "CAT-033",
     "area": "catalog",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "get_product response MUST include product.selected when the product has configurable options",
     "source": "ucp:docs/specification/catalog/lookup.md#L164",
     "covered_by": [
      "merchant_checks.py"
     ]
    },
    {
     "id": "CAT-034",
     "area": "catalog",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "In get_product selected input, each option name MUST appear at most once",
     "source": "ucp:docs/specification/catalog/lookup.md#L156",
     "covered_by": [],
     "exempt_reason": "Binds the client's get_product `selected` input (lookup.md '#### Input', L156); the spec places no rejection duty on the merchant, so a merchant check would be fake coverage \u2014 same subject-binding as CAT-008 (see the register note from the 2026-07-02 reclassification)."
    },
    {
     "id": "CAT-035",
     "area": "catalog",
     "keyword": "MUST",
     "testability": "manual",
     "status": "gap",
     "requirement": "Eligibility and policy enforcement MUST occur at checkout time using binding transaction data (catalog context signals are provisional)",
     "source": "ucp:docs/specification/catalog/index.md#L68",
     "covered_by": []
    },
    {
     "id": "CAT-036",
     "area": "catalog",
     "keyword": "MUST NOT",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Signal values MUST NOT be buyer-asserted claims",
     "source": "ucp:docs/specification/catalog/index.md#L87",
     "covered_by": [],
     "exempt_reason": "Binds the PROVENANCE of platform-supplied signal values (catalog/index.md L87): a buyer-asserted value and a platform-measured value are byte-identical on the wire, so no protocol message can establish where a signal value came from."
    },
    {
     "id": "CAT-038",
     "area": "catalog",
     "keyword": "MUST NOT",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Disclosure warnings (presentation:\"disclosure\") carry notices that platforms must not hide or dismiss",
     "source": "ucp:docs/specification/catalog/index.md#L168",
     "covered_by": [],
     "exempt_reason": "Binds platform rendering of disclosure-presentation catalog warnings (index.md L168): hiding or dismissing a notice happens in the platform's UI after the protocol exchange ends, leaving no wire trace the suite could observe."
    },
    {
     "id": "CHK-001",
     "area": "checkout-lifecycle",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "continue_url MUST be provided when status is requires_escalation (Availability section)",
     "source": "ucp:docs/specification/checkout.md#L461",
     "covered_by": [
      "merchant_checks_04_08_receiver.py"
     ]
    },
    {
     "id": "CHK-004",
     "area": "checkout-lifecycle",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "continue_url MUST be an absolute HTTPS URL",
     "source": "ucp:docs/specification/checkout.md#L469",
     "covered_by": [
      "merchant_checks_04_08_receiver.py"
     ]
    },
    {
     "id": "CHK-008",
     "area": "checkout-lifecycle",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Platform MUST use continue_url when checkout status is requires_escalation (Platform guideline)",
     "source": "ucp:docs/specification/checkout.md#L524",
     "covered_by": [],
     "exempt_reason": "Platform-bound MUST (@04-08 only; a business response MUST at 01-era, covered there): the PLATFORM uses continue_url on requires_escalation. The business duty to PROVIDE it is checked by CHK-001/014/043."
    },
    {
     "id": "CHK-011",
     "area": "checkout-lifecycle",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Business MUST send a confirmation email after the checkout has been completed",
     "source": "ucp:docs/specification/checkout.md#L531",
     "covered_by": [],
     "exempt_reason": "The confirmation email (checkout.md L531) is dispatched on a channel the protocol does not define; no checkout response field evidences it, and a conformance run has no principled inbox oracle \u2014 non-receipt is indistinguishable from spam-filtering or asynchronous delay. [Scoped: at 2026-01-11/2026-01-23 this id is an unrelated SHOULD (cancelable sessions); the same email requirement there is CHK-035.]"
    },
    {
     "id": "CHK-013",
     "area": "checkout-lifecycle",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "gap",
     "requirement": "Logic handling the checkout sessions MUST be deterministic",
     "source": "ucp:docs/specification/checkout.md#L533",
     "covered_by": []
    },
    {
     "id": "CHK-014",
     "area": "checkout-lifecycle",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "Business MUST provide continue_url when returning status = requires_escalation (Business guideline restatement)",
     "source": "ucp:docs/specification/checkout.md#L534",
     "covered_by": [
      "merchant_checks_04_08_receiver.py"
     ]
    },
    {
     "id": "CHK-015",
     "area": "checkout-lifecycle",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "Business MUST include at least one message with severity requires_buyer_input or requires_buyer_review when returning status = requires_escalation",
     "source": "ucp:docs/specification/checkout.md#L536",
     "covered_by": [
      "merchant_checks_04_08_receiver.py"
     ]
    },
    {
     "id": "CHK-017",
     "area": "checkout-lifecycle",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "After a checkout session reaches state completed it is considered immutable",
     "source": "ucp:docs/specification/checkout.md#L540",
     "covered_by": [
      "merchant_checks.py"
     ]
    },
    {
     "id": "CHK-018",
     "area": "checkout-lifecycle",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Create Checkout initiates a new checkout session via POST /checkout-sessions",
     "source": "ucp:docs/specification/checkout-rest.md#L48",
     "covered_by": [
      "merchant_checks.py"
     ]
    },
    {
     "id": "CHK-019",
     "area": "checkout-lifecycle",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Get Checkout retrieves current state via GET /checkout-sessions/{id}",
     "source": "ucp:docs/specification/checkout-rest.md#L49",
     "covered_by": [
      "merchant_checks.py"
     ]
    },
    {
     "id": "CHK-020",
     "area": "checkout-lifecycle",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Update Checkout updates a session via PUT /checkout-sessions/{id}",
     "source": "ucp:docs/specification/checkout-rest.md#L50",
     "covered_by": [
      "merchant_checks.py"
     ]
    },
    {
     "id": "CHK-021",
     "area": "checkout-lifecycle",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Complete Checkout places the order via POST /checkout-sessions/{id}/complete",
     "source": "ucp:docs/specification/checkout-rest.md#L51",
     "covered_by": [
      "merchant_checks.py"
     ]
    },
    {
     "id": "CHK-022",
     "area": "checkout-lifecycle",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Cancel Checkout cancels a session via POST /checkout-sessions/{id}/cancel",
     "source": "ucp:docs/specification/checkout-rest.md#L52",
     "covered_by": [
      "merchant_checks.py"
     ]
    },
    {
     "id": "CHK-023",
     "area": "checkout-lifecycle",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Update Checkout performs a full replacement of the checkout resource; platform is REQUIRED to send the entire checkout resource",
     "source": "ucp:docs/specification/checkout.md#L585",
     "covered_by": [],
     "exempt_reason": "Request-author (platform) obligation to send the entire checkout resource on PUT; the business full-replacement semantics are checked by the update checks. Not observable on a business response."
    },
    {
     "id": "CHK-024",
     "area": "checkout-lifecycle",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Each PUT replaces the entire session; clients must include all previously set fields they wish to retain",
     "source": "ucp:docs/specification/checkout-rest.md#L197",
     "covered_by": [],
     "exempt_reason": "Client obligation: clients MUST include all previously set fields on PUT. Not observable on a business response."
    },
    {
     "id": "CHK-025",
     "area": "checkout-lifecycle",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Complete Checkout response is the checkout object with the order field populated",
     "source": "ucp:docs/specification/checkout.md#L595",
     "covered_by": [
      "merchant_checks.py"
     ]
    },
    {
     "id": "CHK-033",
     "area": "checkout-lifecycle",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "status field enum is constrained to the six lifecycle values",
     "source": "ucp:source/schemas/shopping/checkout.json#L67",
     "covered_by": [
      "area_04_08_checkout.py"
     ]
    },
    {
     "id": "CHK-034",
     "area": "checkout-lifecycle",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Checkout response MUST include required top-level fields: ucp, id, line_items, status, currency, totals, links",
     "source": "ucp:source/schemas/shopping/checkout.json#L8",
     "covered_by": [
      "area_04_08_checkout.py",
      "merchant_checks.py"
     ]
    },
    {
     "id": "CHK-035",
     "area": "checkout-lifecycle",
     "keyword": "MUST NOT",
     "testability": "testable",
     "status": "check",
     "requirement": "Top-level checkout id is omitted on request bodies (ucp_request: omit)",
     "source": "ucp:source/schemas/shopping/checkout.json#L23",
     "covered_by": [
      "schema_check_04_08_lifecycle.py"
     ]
    },
    {
     "id": "CHK-036",
     "area": "checkout-lifecycle",
     "keyword": "MUST NOT",
     "testability": "manual",
     "status": "exempt",
     "requirement": "ucp envelope field is omitted on request bodies (ucp_request: omit)",
     "source": "ucp:source/schemas/shopping/checkout.json#L19",
     "covered_by": [],
     "exempt_reason": "'The ucp envelope field is omitted on request bodies (ucp_request: omit)' (checkout.json L19) constrains the REQUEST author \u2014 the platform that sends checkout requests \u2014 to omit a response-only field; the spec places no duty on the business to reject requests that include it, so no merchant response reflects it and the tester itself omits it. Request-author-bound; same subject-binding as DISC-006/CART-024."
    },
    {
     "id": "CHK-037",
     "area": "checkout-lifecycle",
     "keyword": "MUST NOT",
     "testability": "manual",
     "status": "exempt",
     "requirement": "status, currency, totals, messages, links, expires_at, continue_url, order are response-only (ucp_request: omit) and omitted on requests",
     "source": "ucp:source/schemas/shopping/checkout.json#L76",
     "covered_by": [],
     "exempt_reason": "'status, currency, totals, messages, links, expires_at, continue_url, order are response-only (ucp_request: omit) and omitted on requests' (checkout.json L76-127) constrains the REQUEST author (the platform) to omit response-only fields; no business-side rejection duty exists, so nothing the suite elicits from the merchant reflects it and the tester omits them. Request-author-bound; same subject-binding as DISC-006/CART-024. [Scoped: at 01-era this id is an unrelated needs-receiver requirement (deterministic checkout-session logic).]"
    },
    {
     "id": "CHK-038",
     "area": "checkout-lifecycle",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "line_items is required on create and update requests and omitted on complete requests",
     "source": "ucp:source/schemas/shopping/checkout.json#L34",
     "covered_by": [
      "merchant_checks.py"
     ]
    },
    {
     "id": "CHK-039",
     "area": "checkout-lifecycle",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "payment is optional on create and update, and required on complete requests",
     "source": "ucp:source/schemas/shopping/checkout.json#L118",
     "covered_by": [
      "schema_check_04_08_lifecycle.py"
     ]
    },
    {
     "id": "CHK-043",
     "area": "checkout-lifecycle",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "continue_url MUST be provided when status is requires_escalation (schema field description)",
     "source": "ucp:source/schemas/shopping/checkout.json#L113",
     "covered_by": [
      "merchant_checks_04_08_receiver.py"
     ]
    },
    {
     "id": "CHK-044",
     "area": "checkout-lifecycle",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "All REST request and response bodies MUST be valid JSON per RFC 8259",
     "source": "ucp:docs/specification/checkout-rest.md#L36",
     "covered_by": [
      "merchant_checks_04_08_lifecycle.py"
     ]
    },
    {
     "id": "CHK-045",
     "area": "checkout-lifecycle",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "All REST endpoints MUST be served over HTTPS with minimum TLS version 1.3",
     "source": "ucp:docs/specification/checkout-rest.md#L41",
     "covered_by": [
      "merchant_checks_04_08_lifecycle.py"
     ]
    },
    {
     "id": "CHK-046",
     "area": "checkout-lifecycle",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "All requests MUST include the UCP-Agent header containing the platform profile URI (RFC 8941 dictionary syntax)",
     "source": "ucp:docs/specification/checkout-rest.md#L1237",
     "covered_by": [],
     "exempt_reason": "Binds the REQUEST AUTHOR: 'All requests MUST include the UCP-Agent header' places the include-duty on the platform, and the suite's own probe traffic satisfies it. The business-side ENFORCEMENT duty exists SEPARATELY \u2014 overview.md L696 maps a malformed/MISSING/unresolvable profile URL to a 400 invalid_profile_url rejection \u2014 and is covered by register row NEG-005 with its own check, so exempting this row hides no observable obligation (rewritten 2026-07-03, wave-2 review F4: the earlier reason wrongly claimed no business-side duty exists)."
    },
    {
     "id": "CHK-048",
     "area": "checkout-lifecycle",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "When an Idempotency-Key is provided, server MUST store the key+result >=24h, return cached result for duplicates, and return 409 if reused with different parameters",
     "source": "ucp:docs/specification/checkout-rest.md#L1242",
     "covered_by": [
      "merchant_checks_04_08_receiver.py"
     ]
    },
    {
     "id": "CHK-050",
     "area": "checkout-lifecycle",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "gap",
     "requirement": "Protocol errors return appropriate HTTP status (401/403/409/429/503) with JSON body containing code and content",
     "source": "ucp:docs/specification/checkout-rest.md#L1273",
     "covered_by": []
    },
    {
     "id": "CHK-051",
     "area": "checkout-lifecycle",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "gap",
     "requirement": "Business outcomes (including unavailable merchandise) return HTTP 200 with the UCP envelope and messages array",
     "source": "ucp:docs/specification/checkout-rest.md#L1275",
     "covered_by": []
    },
    {
     "id": "CHK-053",
     "area": "checkout-lifecycle",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "gap",
     "requirement": "On create with all items unavailable, response carries ucp.status:error with messages and no resource body",
     "source": "ucp:docs/specification/checkout.md#L141",
     "covered_by": []
    },
    {
     "id": "CHK-055",
     "area": "checkout-lifecycle",
     "keyword": "MUST NOT",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Platforms MUST NOT autonomously complete a checkout with mismatched totals; SHOULD reject or escalate for buyer review via continue_url",
     "source": "ucp:docs/specification/checkout.md#L811",
     "covered_by": [],
     "exempt_reason": "Prohibits the platform from AUTONOMOUSLY completing a checkout with mismatched totals (checkout.md L811): the business only ever sees a complete request, which carries no evidence of whether the platform verified totals or escalated for buyer review first."
    },
    {
     "id": "CHK-056",
     "area": "checkout-lifecycle",
     "keyword": "MUST NOT",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "Businesses MUST NOT complete a transaction with unresolved eligibility claims; accepted claims MUST be resolved before completion",
     "source": "ucp:docs/specification/checkout.md#L287",
     "covered_by": [
      "merchant_checks_04_08_receiver.py"
     ]
    },
    {
     "id": "DISC-001",
     "area": "discovery-and-profile",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Business profile MUST be served over HTTPS.",
     "source": "ucp:docs/specification/overview.md#L1053",
     "covered_by": [
      "merchant_checks_04_08_discovery.py"
     ]
    },
    {
     "id": "DISC-002",
     "area": "discovery-and-profile",
     "keyword": "MUST NOT",
     "testability": "testable",
     "status": "check",
     "requirement": "Profile endpoints MUST NOT use 3xx redirects; verifiers MUST NOT follow them.",
     "source": "ucp:docs/specification/overview.md#L1054",
     "covered_by": [
      "merchant_checks_04_08_discovery.py"
     ]
    },
    {
     "id": "DISC-003",
     "area": "discovery-and-profile",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Profile responses MUST send Cache-Control with public and max-age>=60, and MUST NOT use private/no-store/no-cache.",
     "source": "ucp:docs/specification/overview.md#L1055-L1057",
     "covered_by": [
      "merchant_checks_04_08_discovery.py"
     ]
    },
    {
     "id": "DISC-004",
     "area": "discovery-and-profile",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Implementations MUST reject profile URLs not served over HTTPS.",
     "source": "ucp:docs/specification/overview.md#L1098",
     "covered_by": [
      "merchant_checks_04_08_discovery.py"
     ]
    },
    {
     "id": "DISC-005",
     "area": "discovery-and-profile",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "service endpoint MUST be a valid HTTPS URL.",
     "source": "ucp:docs/specification/overview.md#L147",
     "covered_by": [
      "merchant_checks_04_08_discovery.py"
     ]
    },
    {
     "id": "DISC-006",
     "area": "discovery-and-profile",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Platforms MUST include their profile URI in every request.",
     "source": "ucp:docs/specification/overview.md#L622-L623",
     "covered_by": [],
     "exempt_reason": "'Platforms MUST include their profile URI in every request' (overview.md L622-623) binds the PLATFORM (the UCP-Agent header request author), not the merchant; the spec mandates no business-side rejection of requests lacking the profile URI, so a merchant check would grade something never required of merchants, and the tester itself sends UCP-Agent profile=... on every request (merchant_checks._hdr). Same subject-binding as CAT-008/CAT-034."
    },
    {
     "id": "DSC-003",
     "area": "discounts-consent",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Submitting discounts.codes replaces any previously submitted codes (replacement semantics)",
     "source": "ucp:docs/specification/discount.md#L133",
     "covered_by": [
      "merchant_checks_04_08.py"
     ]
    },
    {
     "id": "DSC-004",
     "area": "discounts-consent",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Sending an empty array codes:[] removes all discount codes",
     "source": "ucp:docs/specification/discount.md#L134",
     "covered_by": [
      "merchant_checks.py"
     ]
    },
    {
     "id": "DSC-005",
     "area": "discounts-consent",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Discount codes are matched case-insensitively by the business",
     "source": "ucp:docs/specification/discount.md#L135",
     "covered_by": [
      "merchant_checks_04_08.py"
     ]
    },
    {
     "id": "DSC-006",
     "area": "discounts-consent",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "discounts.applied contains all active discounts (code-based + automatic)",
     "source": "ucp:docs/specification/discount.md#L139",
     "covered_by": [
      "merchant_checks.py"
     ]
    },
    {
     "id": "DSC-007",
     "area": "discounts-consent",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Rejected codes are communicated via the messages[] array",
     "source": "ucp:docs/specification/discount.md#L140",
     "covered_by": [
      "merchant_checks.py"
     ]
    },
    {
     "id": "DSC-008",
     "area": "discounts-consent",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Discount amounts are reflected in totals[] and line_items[].totals[]",
     "source": "ucp:docs/specification/discount.md#L141",
     "covered_by": [
      "merchant_checks_04_08.py"
     ]
    },
    {
     "id": "DSC-009",
     "area": "discounts-consent",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "When a cart is converted to a checkout via cart_id, businesses MUST carry forward any discount codes that were applied to the cart",
     "source": "ucp:docs/specification/discount.md#L143",
     "covered_by": [
      "merchant_checks_04_08_receiver.py"
     ]
    },
    {
     "id": "DSC-012",
     "area": "discounts-consent",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Automatic discounts appear in discounts.applied with automatic:true and no code field",
     "source": "ucp:docs/specification/discount.md#L189",
     "covered_by": [
      "merchant_checks_04_08.py"
     ]
    },
    {
     "id": "DSC-013",
     "area": "discounts-consent",
     "keyword": "MUST NOT",
     "testability": "needs-receiver",
     "status": "gap",
     "requirement": "Automatic discounts cannot be removed by the platform",
     "source": "ucp:docs/specification/discount.md#L191",
     "covered_by": []
    },
    {
     "id": "DSC-014",
     "area": "discounts-consent",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "When the discount extension is active, businesses that accept eligibility claims MUST surface their effect on pricing as provisional discounts in the applied array",
     "source": "ucp:docs/specification/discount.md#L198",
     "covered_by": [
      "merchant_checks_04_08_receiver.py"
     ]
    },
    {
     "id": "DSC-015",
     "area": "discounts-consent",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Platforms MUST display provisional discounts to the buyer",
     "source": "ucp:docs/specification/discount.md#L201",
     "covered_by": [],
     "exempt_reason": "'Platforms MUST display provisional discounts to the buyer' (discount.md L200-201) is a platform UI rendering duty: whether the provisional entries in the applied array were actually painted for a human happens after the protocol exchange ends and leaves no wire trace the suite could observe from the business side \u2014 same family as the exempt ERR-011/TOT-001 display duties. [Scoped: at 2026-01-23 this id is the testable discounts-omitted-on-complete request annotation, which stays in the 01-23 testable grind.]"
    },
    {
     "id": "DSC-016",
     "area": "discounts-consent",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "When a business recognizes an eligibility claim that affects pricing, it MUST surface a corresponding provisional discount in discounts.applied",
     "source": "ucp:docs/specification/discount.md#L207",
     "covered_by": [
      "merchant_checks_04_08_receiver.py"
     ]
    },
    {
     "id": "DSC-017",
     "area": "discounts-consent",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "At checkout completion, all remaining provisional claims MUST be resolved",
     "source": "ucp:docs/specification/discount.md#L227",
     "covered_by": [
      "merchant_checks_04_08_receiver.py"
     ]
    },
    {
     "id": "DSC-018",
     "area": "discounts-consent",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "If a discount has allocations pointing to line items it contributes to items_discount; otherwise (no allocations, or shipping/fees allocations) it contributes to discount",
     "source": "ucp:docs/specification/discount.md#L298",
     "covered_by": [
      "merchant_checks_04_08.py"
     ]
    },
    {
     "id": "DSC-019",
     "area": "discounts-consent",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Invariant: totals[type=items_discount].amount equals sum of line_items[].totals[type=items_discount].amount",
     "source": "ucp:docs/specification/discount.md#L307",
     "covered_by": [
      "merchant_checks_04_08.py"
     ]
    },
    {
     "id": "DSC-020",
     "area": "discounts-consent",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Discount amounts in discounts.applied are positive integers (value of the discount)",
     "source": "ucp:docs/specification/discount.md#L313",
     "covered_by": [
      "merchant_checks_04_08.py"
     ]
    },
    {
     "id": "DSC-021",
     "area": "discounts-consent",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Discount entries in totals[] are negative (strictly < 0); the sign is schema-enforced",
     "source": "ucp:docs/specification/discount.md#L314",
     "covered_by": [
      "area_04_08_discount.py"
     ]
    },
    {
     "id": "DSC-022",
     "area": "discounts-consent",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Invariant: sum of allocations[].amount equals applied_discount.amount",
     "source": "ucp:docs/specification/discount.md#L124",
     "covered_by": [
      "merchant_checks_04_08.py"
     ]
    },
    {
     "id": "DSC-023",
     "area": "discounts-consent",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "applied_discount MUST include title and amount",
     "source": "ucp:source/schemas/shopping/discount.json#L29",
     "covered_by": [
      "area_04_08_discount.py",
      "merchant_checks.py"
     ]
    },
    {
     "id": "DSC-024",
     "area": "discounts-consent",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "applied_discount.method MUST be one of 'each' or 'across' when present",
     "source": "ucp:source/schemas/shopping/discount.json#L51",
     "covered_by": [
      "area_04_08_discount.py"
     ]
    },
    {
     "id": "DSC-025",
     "area": "discounts-consent",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "applied_discount.priority MUST be an integer >= 1 (lower applied first)",
     "source": "ucp:source/schemas/shopping/discount.json#L59",
     "covered_by": [
      "area_04_08_discount.py"
     ]
    },
    {
     "id": "DSC-026",
     "area": "discounts-consent",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "allocation MUST include path and amount",
     "source": "ucp:source/schemas/shopping/discount.json#L11",
     "covered_by": [
      "area_04_08_discount.py"
     ]
    },
    {
     "id": "DSC-027",
     "area": "discounts-consent",
     "keyword": "MUST NOT",
     "testability": "testable",
     "status": "check",
     "requirement": "discounts.applied is response-only and MUST be omitted from requests",
     "source": "ucp:source/schemas/shopping/discount.json#L99",
     "covered_by": [
      "schema_check_04_08.py"
     ]
    },
    {
     "id": "DSC-028",
     "area": "discounts-consent",
     "keyword": "MUST NOT",
     "testability": "testable",
     "status": "check",
     "requirement": "On checkout complete, the discounts object MUST be omitted from the request",
     "source": "ucp:source/schemas/shopping/discount.json#L139",
     "covered_by": [
      "schema_check_04_08.py"
     ]
    },
    {
     "id": "DSC-029",
     "area": "discounts-consent",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Multiple valid discount codes are both applied (accept-both)",
     "source": "ucp:docs/specification/discount.md#L139",
     "covered_by": [
      "merchant_checks.py"
     ]
    },
    {
     "id": "DSC-030",
     "area": "discounts-consent",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "When multiple codes are submitted and one is invalid, only the valid discount is applied (accept-one-reject-one); rejected appears in messages[] not applied",
     "source": "ucp:docs/specification/discount.md#L490",
     "covered_by": [
      "merchant_checks.py"
     ]
    },
    {
     "id": "DSC-032",
     "area": "discounts-consent",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "When the consent extension is supported, the buyer object in checkout is extended with a consent field containing boolean consent states",
     "source": "ucp:docs/specification/buyer-consent.md#L27",
     "covered_by": [
      "area_04_08_discount.py"
     ]
    },
    {
     "id": "DSC-033",
     "area": "discounts-consent",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "consent properties (analytics, preferences, marketing, sale_of_data) MUST be booleans when present",
     "source": "ucp:source/schemas/shopping/buyer_consent.json#L12",
     "covered_by": [
      "area_04_08_discount.py"
     ]
    },
    {
     "id": "DSC-034",
     "area": "discounts-consent",
     "keyword": "MUST NOT",
     "testability": "testable",
     "status": "check",
     "requirement": "On checkout complete, the buyer object (carrying consent) MUST be omitted from the request",
     "source": "ucp:source/schemas/shopping/buyer_consent.json#L64",
     "covered_by": [
      "schema_check_04_08.py"
     ]
    },
    {
     "id": "ERR-001",
     "area": "error-envelope",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Error message MUST include type, code, content, and severity fields.",
     "source": "ucp:source/schemas/shopping/types/message_error.json#L6",
     "covered_by": [
      "area_04_08_error.py"
     ]
    },
    {
     "id": "ERR-002",
     "area": "error-envelope",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Error message type discriminator MUST be the constant \"error\".",
     "source": "ucp:source/schemas/shopping/types/message_error.json#L13",
     "covered_by": [
      "schema_check_04_08_discovery.py"
     ]
    },
    {
     "id": "ERR-003",
     "area": "error-envelope",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Error message severity MUST be one of recoverable, requires_buyer_input, requires_buyer_review, unrecoverable.",
     "source": "ucp:source/schemas/shopping/types/message_error.json#L40",
     "covered_by": [
      "area_04_08_error.py"
     ]
    },
    {
     "id": "ERR-004",
     "area": "error-envelope",
     "keyword": "MUST",
     "testability": "manual",
     "status": "check",
     "requirement": "Errors with requires_* severity contribute to status: requires_escalation.",
     "source": "ucp:source/schemas/shopping/types/message_error.json#L46",
     "covered_by": [
      "merchant_checks_04_08_receiver.py"
     ]
    },
    {
     "id": "ERR-005",
     "area": "error-envelope",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Error message content_type, when present, MUST be one of plain or markdown (default plain).",
     "source": "ucp:source/schemas/shopping/types/message_error.json#L25",
     "covered_by": [
      "schema_check_04_08_discovery.py"
     ]
    },
    {
     "id": "ERR-006",
     "area": "error-envelope",
     "keyword": "MUST",
     "testability": "manual",
     "status": "gap",
     "requirement": "Error message path, when present, MUST be an RFC 9535 JSONPath to the referenced component.",
     "source": "ucp:source/schemas/shopping/types/message_error.json#L21",
     "covered_by": []
    },
    {
     "id": "ERR-009",
     "area": "error-envelope",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Warning message MUST include type, code, and content fields.",
     "source": "ucp:source/schemas/shopping/types/message_warning.json#L6",
     "covered_by": [
      "schema_check_04_08_discovery.py"
     ]
    },
    {
     "id": "ERR-010",
     "area": "error-envelope",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Warning message type discriminator MUST be the constant \"warning\".",
     "source": "ucp:source/schemas/shopping/types/message_warning.json#L12",
     "covered_by": [
      "schema_check_04_08_discovery.py"
     ]
    },
    {
     "id": "ERR-011",
     "area": "error-envelope",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Warning content is a human-readable message that MUST be displayed.",
     "source": "ucp:source/schemas/shopping/types/message_warning.json#L24",
     "covered_by": [],
     "exempt_reason": "The MUST is a platform display duty (message_warning.json content: 'message that MUST be displayed'); rendering content to a human buyer happens in the platform UI, outside every message the suite can elicit from the business under test."
    },
    {
     "id": "ERR-012",
     "area": "error-envelope",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Warning presentation is the rendering contract; 'notice' (default) platform MUST display, MAY dismiss; 'disclosure' platform MUST display in proximity to path, MUST NOT hide or auto-dismiss.",
     "source": "ucp:source/schemas/shopping/types/message_warning.json#L37",
     "covered_by": [],
     "exempt_reason": "The presentation field's rendering contract (message_warning.json L37-41: display, proximity, no auto-dismiss) binds the platform's UI; no protocol message reflects how a warning was actually rendered."
    },
    {
     "id": "ERR-013",
     "area": "error-envelope",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "For all warnings, platforms MUST display the warning content to the buyer (both notice and disclosure).",
     "source": "ucp:docs/specification/checkout.md#L337",
     "covered_by": [],
     "exempt_reason": "Platform display duty from the Warning Presentation contract table (checkout.md L337, 'Display content: MUST / MUST'); whether content reached the buyer is a UI fact with no protocol-observable counterpart."
    },
    {
     "id": "ERR-014",
     "area": "error-envelope",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "For disclosure warnings, platforms MUST display the warning content to the buyer.",
     "source": "ucp:docs/specification/checkout.md#L358",
     "covered_by": [],
     "exempt_reason": "Platform display duty for disclosure warnings (checkout.md L358); the rendering happens in the platform UI, and the business \u2014 the only party the suite can probe \u2014 never observes it."
    },
    {
     "id": "ERR-015",
     "area": "error-envelope",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "For disclosure warnings, platforms MUST display the warning in proximity to the path-referenced component; when path is omitted the disclosure applies to the response as a whole.",
     "source": "ucp:docs/specification/checkout.md#L359",
     "covered_by": [],
     "exempt_reason": "Proximity-to-path (checkout.md L359-362) is a UI LAYOUT obligation on the platform: the spatial association between a disclosure and its path-referenced component exists only in the rendered interface, never in a protocol message."
    },
    {
     "id": "ERR-016",
     "area": "error-envelope",
     "keyword": "MUST NOT",
     "testability": "manual",
     "status": "exempt",
     "requirement": "For disclosure warnings, platforms MUST NOT hide, collapse, or auto-dismiss the warning.",
     "source": "ucp:docs/specification/checkout.md#L363",
     "covered_by": [],
     "exempt_reason": "Prohibits platform UI actions (hide/collapse/auto-dismiss, checkout.md L363); these occur after the protocol exchange ends and leave no trace observable from the business side."
    },
    {
     "id": "ERR-017",
     "area": "error-envelope",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "For disclosure warnings, platforms MUST render image_url when present.",
     "source": "ucp:docs/specification/checkout.md#L364",
     "covered_by": [],
     "exempt_reason": "Platform rendering duty for image_url (checkout.md L364-365); whether the image was actually rendered next to the warning is a UI fact invisible to the business under test."
    },
    {
     "id": "ERR-018",
     "area": "error-envelope",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Platforms that cannot honor the disclosure rendering contract MUST escalate to merchant UI via continue_url rather than silently downgrading to a notice.",
     "source": "ucp:docs/specification/checkout.md#L371",
     "covered_by": [],
     "exempt_reason": "Binds platforms whose UI cannot honor the disclosure contract (checkout.md L371-373): both the 'cannot honor' capability judgment and the escalate-vs-silently-downgrade choice are platform-internal; the business merely serves continue_url either way."
    },
    {
     "id": "ERR-021",
     "area": "error-envelope",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "For notice warnings, platforms MUST display the warning content to the buyer.",
     "source": "ucp:docs/specification/checkout.md#L346",
     "covered_by": [],
     "exempt_reason": "Platform display duty for notice warnings (checkout.md L346-348); rendering in a banner, tray, or toast is UI behavior on the platform side, outside the protocol surface a merchant conformance run can observe."
    },
    {
     "id": "ERR-022",
     "area": "error-envelope",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Businesses MUST set presentation: disclosure when warning content must be displayed alongside a specific component and must not be hidden or auto-dismissed.",
     "source": "ucp:docs/specification/checkout.md#L377",
     "covered_by": [],
     "exempt_reason": "The business-side trigger (checkout.md L377-379) \u2014 warning content that 'must be displayed alongside a specific component and must not be hidden' (safety, allergen, compliance material) \u2014 is a judgment about the content's regulatory character, not a mechanical property of the payload; no predicate can decide which warnings OUGHT to carry presentation: disclosure."
    },
    {
     "id": "ERR-025",
     "area": "error-envelope",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Info message MUST include type and content fields.",
     "source": "ucp:source/schemas/shopping/types/message_info.json#L6",
     "covered_by": [
      "schema_check_04_08_discovery.py"
     ]
    },
    {
     "id": "ERR-026",
     "area": "error-envelope",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Info message type discriminator MUST be the constant \"info\".",
     "source": "ucp:source/schemas/shopping/types/message_info.json#L11",
     "covered_by": [
      "schema_check_04_08_discovery.py"
     ]
    },
    {
     "id": "ERR-028",
     "area": "error-envelope",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Error response MUST include ucp and messages.",
     "source": "ucp:source/schemas/shopping/types/error_response.json#L7",
     "covered_by": [
      "area_04_08_error.py"
     ]
    },
    {
     "id": "ERR-029",
     "area": "error-envelope",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Error response ucp.status MUST be 'error'.",
     "source": "ucp:source/schemas/shopping/types/error_response.json#L10",
     "covered_by": [
      "area_04_08_error.py"
     ]
    },
    {
     "id": "ERR-030",
     "area": "error-envelope",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Error response messages array MUST contain at least one message (minItems 1).",
     "source": "ucp:source/schemas/shopping/types/error_response.json#L14",
     "covered_by": [
      "area_04_08_error.py"
     ]
    },
    {
     "id": "ERR-031",
     "area": "error-envelope",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "A Message MUST be exactly one of error, warning, or info (oneOf discriminated container).",
     "source": "ucp:source/schemas/shopping/types/message.json#L7",
     "covered_by": [
      "schema_check_04_08_discovery.py"
     ]
    },
    {
     "id": "ERR-034",
     "area": "error-envelope",
     "keyword": "MUST",
     "testability": "manual",
     "status": "gap",
     "requirement": "ucp.status is the shape discriminator: 'success' carries the expected payload, 'error' carries error information instead.",
     "source": "ucp:docs/specification/checkout.md#L120",
     "covered_by": []
    },
    {
     "id": "FUL-003",
     "area": "fulfillment",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "A fulfillment_method MUST include id, type, and line_item_ids",
     "source": "ucp:source/schemas/shopping/types/fulfillment_method.json#L7",
     "covered_by": [
      "area_04_08_fulfillment.py",
      "area_fulfillment.py",
      "merchant_checks.py"
     ]
    },
    {
     "id": "FUL-004",
     "area": "fulfillment",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "fulfillment_method.type MUST be one of shipping or pickup",
     "source": "ucp:source/schemas/shopping/types/fulfillment_method.json#L17",
     "covered_by": [
      "area_04_08_fulfillment.py",
      "area_fulfillment.py"
     ]
    },
    {
     "id": "FUL-007",
     "area": "fulfillment",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "A fulfillment_group MUST include id and line_item_ids",
     "source": "ucp:source/schemas/shopping/types/fulfillment_group.json#L7",
     "covered_by": [
      "area_04_08_fulfillment.py",
      "area_fulfillment.py"
     ]
    },
    {
     "id": "FUL-008",
     "area": "fulfillment",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "A fulfillment_option MUST include id, title, and totals",
     "source": "ucp:source/schemas/shopping/types/fulfillment_option.json#L8",
     "covered_by": [
      "area_04_08_fulfillment.py",
      "area_fulfillment.py",
      "merchant_checks.py"
     ]
    },
    {
     "id": "FUL-009",
     "area": "fulfillment",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "options[].title is the required primary label that distinguishes the option from its siblings",
     "source": "ucp:docs/specification/fulfillment.md#L168",
     "covered_by": [],
     "exempt_reason": "The schema-enforceable half (title present) is already validated by FUL-008; FUL-009's DISTINCT requirement \u2014 the title 'distinguishes the option from its siblings' \u2014 is human content judgment: two options each with a non-empty, schema-valid title but indistinguishable labels violate it, yet no protocol message reveals that. Present at 04-08 (fulfillment_option required[]); at 01-era the same subjective clause holds."
    },
    {
     "id": "FUL-010",
     "area": "fulfillment",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "options[].title MUST distinguish this option from its siblings",
     "source": "ucp:docs/specification/fulfillment.md#L177",
     "covered_by": [],
     "exempt_reason": "Whether a title lets a human buyer tell one option from its siblings (fulfillment.md Business Responsibilities) is a comprehension judgment: string uniqueness neither proves it (trivially-differing strings do not distinguish) nor is literally required by it."
    },
    {
     "id": "FUL-012",
     "area": "fulfillment",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "options[].title MUST be sufficient for buyer decision if description is absent",
     "source": "ucp:docs/specification/fulfillment.md#L179",
     "covered_by": [],
     "exempt_reason": "'Sufficient for buyer decision if description is absent' is a human-adequacy judgment about label content; no mechanical predicate separates sufficient titles from insufficient ones."
    },
    {
     "id": "FUL-013",
     "area": "fulfillment",
     "keyword": "MUST NOT",
     "testability": "manual",
     "status": "exempt",
     "requirement": "options[].description MUST NOT repeat title or total; provides supplementary context only",
     "source": "ucp:docs/specification/fulfillment.md#L183",
     "covered_by": [],
     "exempt_reason": "'Repeat' covers paraphrase, not just string equality: deciding whether a description merely restates title/total versus supplying supplementary context is linguistic judgment beyond any exact-match test."
    },
    {
     "id": "FUL-017",
     "area": "fulfillment",
     "keyword": "MUST",
     "testability": "manual",
     "status": "gap",
     "requirement": "options[].total is the required price in minor units and may be null if not yet available",
     "source": "ucp:docs/specification/fulfillment.md#L170",
     "covered_by": []
    },
    {
     "id": "FUL-018",
     "area": "fulfillment",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "available_methods[].description MUST be a standalone sentence explaining what, when, and where",
     "source": "ucp:docs/specification/fulfillment.md#L190",
     "covered_by": [],
     "exempt_reason": "Whether a description is 'a standalone sentence explaining what, when, and where' is a natural-language completeness judgment (does it actually explain all three?) that a parser cannot arbitrate."
    },
    {
     "id": "FUL-026",
     "area": "fulfillment",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "When supports_multi_group is false (default), the business MUST consolidate all items into a single group per method",
     "source": "ucp:docs/specification/fulfillment.md#L323",
     "covered_by": [
      "merchant_checks.py"
     ]
    },
    {
     "id": "FUL-029",
     "area": "fulfillment",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Extensions that add new fulfillment method types MUST add an extension schema that adds the method to the fulfillment_method type enum and adds corresponding business config options",
     "source": "ucp:docs/specification/fulfillment.md#L336",
     "covered_by": [],
     "exempt_reason": "Binds authors of FUTURE fulfillment extensions (fulfillment.md 'Adding New Methods'): the obligation attaches to a hypothetical extension schema document, not to any behavior of the business or platform under test."
    },
    {
     "id": "FUL-030",
     "area": "fulfillment",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "A shipping_destination MUST include an id (it is a postal_address plus a required id)",
     "source": "ucp:source/schemas/shopping/types/shipping_destination.json#L21",
     "covered_by": [
      "area_04_08_fulfillment.py",
      "area_fulfillment.py"
     ]
    },
    {
     "id": "IDL-001",
     "area": "identity-linking",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Identity linking uses OAuth 2.0 as the v1 auth mechanism.",
     "source": "ucp:docs/specification/identity-linking.md#L34-L35",
     "covered_by": [
      "merchant_checks_04_08_oauth.py"
     ]
    },
    {
     "id": "IDL-002",
     "area": "identity-linking",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Platforms MUST authenticate token-endpoint requests using a method advertised in the business's token_endpoint_auth_methods_supported metadata.",
     "source": "ucp:docs/specification/identity-linking.md#L98-L100",
     "covered_by": [],
     "exempt_reason": "'Platforms MUST authenticate token-endpoint requests using a method advertised in the business's token_endpoint_auth_methods_supported metadata' (identity-linking.md L98-100, RFC 8414) binds the PLATFORM's token-request authoring; token-endpoint auth-method selection is the platform's request behavior, not a merchant obligation. Requester-bound per policy F2; precedent CAT-034."
    },
    {
     "id": "IDL-004",
     "area": "identity-linking",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Public clients MUST use token-endpoint auth method 'none' and rely on PKCE with S256 as proof-of-possession of the authorization code.",
     "source": "ucp:docs/specification/identity-linking.md#L111-L116",
     "covered_by": [],
     "exempt_reason": "'Public clients MUST use token-endpoint auth method none and rely on PKCE with S256 as proof-of-possession of the authorization code' (identity-linking.md L111-116) binds the PLATFORM's client type and request behavior, not the business; no merchant response reflects a client's method choice or PKCE use. Requester-bound per policy F2; precedent CAT-034."
    },
    {
     "id": "IDL-005",
     "area": "identity-linking",
     "keyword": "MUST NOT",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Public clients MUST NOT embed a client_secret.",
     "source": "ucp:docs/specification/identity-linking.md#L116-L117",
     "covered_by": [],
     "exempt_reason": "'Public clients MUST NOT embed a client_secret' (identity-linking.md L116-117; Security Considerations L581) is a property of the PLATFORM's distributed binary, not any protocol message the business sees. Requester-bound per policy F2; precedent CAT-034."
    },
    {
     "id": "IDL-006",
     "area": "identity-linking",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Platforms MUST select the strongest token-endpoint auth method offered by the business compatible with the platform's deployment model.",
     "source": "ucp:docs/specification/identity-linking.md#L119-L120",
     "covered_by": [],
     "exempt_reason": "'Platforms MUST select the strongest token-endpoint auth method offered by the business compatible with the platform's deployment model' (identity-linking.md L119-120) binds the PLATFORM's negotiation behavior; method-strength selection is internal client logic, not a merchant obligation. Requester-bound per policy F2; precedent CAT-034."
    },
    {
     "id": "IDL-007",
     "area": "identity-linking",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Platforms MUST include user identity tokens in the HTTP Authorization header using the Bearer scheme.",
     "source": "ucp:docs/specification/identity-linking.md#L121-L122",
     "covered_by": [],
     "exempt_reason": "'Platforms MUST include user identity tokens in the HTTP Authorization header using the Bearer scheme' (identity-linking.md L121-122, RFC 6750 2.1) binds the PLATFORM as the request author; the Authorization: Bearer header is authored by the platform. Requester-bound per policy F2; precedent CAT-034."
    },
    {
     "id": "IDL-008",
     "area": "identity-linking",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Platforms MUST process WWW-Authenticate: Bearer challenges on 401 and 403 responses to user-authenticated operations.",
     "source": "ucp:docs/specification/identity-linking.md#L124-L126",
     "covered_by": [],
     "exempt_reason": "'Platforms MUST process WWW-Authenticate: Bearer challenges on 401 and 403 responses to user-authenticated operations' (identity-linking.md L124-126) binds the PLATFORM's response handling, not the business; challenge processing is internal client conduct with no merchant-observable signal. Requester-bound per policy F2; precedent CAT-034."
    },
    {
     "id": "IDL-009",
     "area": "identity-linking",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Platforms MUST extract the scope parameter (when present) from the WWW-Authenticate challenge to construct subsequent authorization requests.",
     "source": "ucp:docs/specification/identity-linking.md#L127-L128",
     "covered_by": [],
     "exempt_reason": "'Platforms MUST extract the scope parameter (when present) from the WWW-Authenticate challenge to construct subsequent authorization requests' (identity-linking.md L127-128) binds the PLATFORM's response handling; extracting the scope param is internal client conduct. Requester-bound per policy F2; precedent CAT-034."
    },
    {
     "id": "IDL-010",
     "area": "identity-linking",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Platforms MUST implement the OAuth 2.0 Authorization Code flow as the account linking mechanism.",
     "source": "ucp:docs/specification/identity-linking.md#L132-L134",
     "covered_by": [],
     "exempt_reason": "'Platforms MUST implement the OAuth 2.0 Authorization Code flow as the account linking mechanism' (identity-linking.md L132-134) binds the PLATFORM's client implementation \u2014 a flow choice, not a merchant obligation. Requester-bound per policy F2; precedent CAT-034."
    },
    {
     "id": "IDL-011",
     "area": "identity-linking",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Platforms MUST use PKCE with code_challenge_method=S256 for all authorization code exchanges.",
     "source": "ucp:docs/specification/identity-linking.md#L135-L137",
     "covered_by": [],
     "exempt_reason": "'Platforms MUST use PKCE with code_challenge_method=S256 for all authorization code exchanges' (identity-linking.md L135-137) binds the PLATFORM's request side; the business-side PKCE enforcement is IDL-019/023/036/049 (checked). Requester-bound per policy F2; precedent CAT-034."
    },
    {
     "id": "IDL-012",
     "area": "identity-linking",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Platforms MUST validate the iss parameter in the authorization response to prevent Mix-Up Attacks, verifying it matches the AS issuer URI; if it does not match they MUST abort and discard the authorization response.",
     "source": "ucp:docs/specification/identity-linking.md#L138-L144",
     "covered_by": [],
     "exempt_reason": "'Platforms MUST validate the iss parameter in the authorization response ... if it does not match they MUST abort and discard the authorization response' (identity-linking.md L138-144, Mix-Up defense) binds the PLATFORM's response handling; the business-side iss emission is IDL-018 (checked). Requester-bound per policy F2; precedent CAT-034."
    },
    {
     "id": "IDL-014",
     "area": "identity-linking",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Platforms MUST call the business's token revocation endpoint when a user initiates an unlink action on the platform side.",
     "source": "ucp:docs/specification/identity-linking.md#L149-L151",
     "covered_by": [],
     "exempt_reason": "'Platforms MUST call the business's token revocation endpoint when a user initiates an unlink action on the platform side' (identity-linking.md L149-151; Security Considerations L617-618) binds the PLATFORM's action; the business-side revocation endpoint is IDL-027/028 (checked). Requester-bound per policy F2; precedent CAT-034."
    },
    {
     "id": "IDL-015",
     "area": "identity-linking",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Businesses MUST implement OAuth 2.0.",
     "source": "ucp:docs/specification/identity-linking.md#L159-L160",
     "covered_by": [
      "merchant_checks_04_08_oauth.py"
     ]
    },
    {
     "id": "IDL-016",
     "area": "identity-linking",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Businesses MUST publish authorization server metadata via RFC 8414 at /.well-known/oauth-authorization-server.",
     "source": "ucp:docs/specification/identity-linking.md#L161-L163",
     "covered_by": [
      "merchant_checks_04_08_identity.py"
     ]
    },
    {
     "id": "IDL-017",
     "area": "identity-linking",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Businesses MUST populate scopes_supported in RFC 8414 metadata so platforms can detect scope mismatches before initiating an authorization flow.",
     "source": "ucp:docs/specification/identity-linking.md#L164-L167",
     "covered_by": [
      "merchant_checks_04_08_identity.py"
     ]
    },
    {
     "id": "IDL-018",
     "area": "identity-linking",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Businesses MUST return the iss parameter in the authorization response.",
     "source": "ucp:docs/specification/identity-linking.md#L168-L169",
     "covered_by": [
      "merchant_checks_04_08_oauth.py"
     ]
    },
    {
     "id": "IDL-019",
     "area": "identity-linking",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Businesses MUST enforce PKCE validation at the token endpoint for all authorization code exchanges; requests without a valid code_verifier MUST be rejected.",
     "source": "ucp:docs/specification/identity-linking.md#L170-L173",
     "covered_by": [
      "merchant_checks_04_08_oauth.py"
     ]
    },
    {
     "id": "IDL-020",
     "area": "identity-linking",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Businesses MUST enforce exact string matching for the redirect_uri parameter during authorization requests; the token-request redirect_uri MUST be identical to the authorization-request one.",
     "source": "ucp:docs/specification/identity-linking.md#L174-L177",
     "covered_by": [
      "merchant_checks_04_08_oauth.py"
     ]
    },
    {
     "id": "IDL-021",
     "area": "identity-linking",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "For loopback redirect URIs (127.0.0.1 or [::1]) businesses MUST ignore the port component and match on scheme, host, and path only.",
     "source": "ucp:docs/specification/identity-linking.md#L177-L181",
     "covered_by": [
      "merchant_checks_04_08_oauth.py"
     ]
    },
    {
     "id": "IDL-022",
     "area": "identity-linking",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Businesses MUST declare supported client authentication methods in token_endpoint_auth_methods_supported and enforce one of the declared methods at the token endpoint.",
     "source": "ucp:docs/specification/identity-linking.md#L183-L185",
     "covered_by": [
      "merchant_checks_04_08_identity.py"
     ]
    },
    {
     "id": "IDL-023",
     "area": "identity-linking",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "When 'none' is advertised, businesses MUST require PKCE with S256 and MUST reject any authorization code redemption lacking a valid code_verifier.",
     "source": "ucp:docs/specification/identity-linking.md#L190-L192",
     "covered_by": [
      "merchant_checks_04_08_oauth.py"
     ]
    },
    {
     "id": "IDL-024",
     "area": "identity-linking",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Requests that fail the negotiated authentication method MUST be rejected with invalid_client; requests that fail PKCE MUST be rejected with invalid_grant.",
     "source": "ucp:docs/specification/identity-linking.md#L192-L194",
     "covered_by": [
      "merchant_checks_04_08_oauth.py"
     ]
    },
    {
     "id": "IDL-025",
     "area": "identity-linking",
     "keyword": "MUST",
     "testability": "needs-oauth",
     "status": "gap",
     "requirement": "Businesses MUST validate user identity tokens on every user-authenticated request (iss, aud, exp, scopes, and client_id/azp).",
     "source": "ucp:docs/specification/identity-linking.md#L195-L199",
     "covered_by": []
    },
    {
     "id": "IDL-026",
     "area": "identity-linking",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Businesses MUST emit a WWW-Authenticate: Bearer challenge on 401 Unauthorized (identity_required) and 403 Forbidden (insufficient_scope) responses to user-authenticated operations.",
     "source": "ucp:docs/specification/identity-linking.md#L200-L203",
     "covered_by": [
      "merchant_checks_04_08_oauth.py"
     ]
    },
    {
     "id": "IDL-027",
     "area": "identity-linking",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Businesses MUST implement token revocation; revoking a refresh_token MUST also immediately invalidate all access_tokens issued from it.",
     "source": "ucp:docs/specification/identity-linking.md#L206-L209",
     "covered_by": [
      "merchant_checks_04_08_oauth.py"
     ]
    },
    {
     "id": "IDL-028",
     "area": "identity-linking",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Businesses MUST support revocation requests authenticated with the same client credentials used at the token endpoint.",
     "source": "ucp:docs/specification/identity-linking.md#L210-L211",
     "covered_by": [
      "merchant_checks_04_08_oauth.py"
     ]
    },
    {
     "id": "IDL-029",
     "area": "identity-linking",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Businesses MUST support standard UCP scopes as defined in the Scopes section.",
     "source": "ucp:docs/specification/identity-linking.md#L216-L217",
     "covered_by": [
      "merchant_checks_04_08_oauth.py"
     ]
    },
    {
     "id": "IDL-031",
     "area": "identity-linking",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "On RFC 8414 discovery, any non-2xx response other than 404 (network error or timeout) MUST abort and MUST NOT proceed to OIDC fallback.",
     "source": "ucp:docs/specification/identity-linking.md#L240-L241",
     "covered_by": [],
     "exempt_reason": "'On RFC 8414 discovery, any non-2xx response other than 404 (network error or timeout) MUST abort and MUST NOT proceed to OIDC fallback' (identity-linking.md L240-241) binds the PLATFORM's discovery fetch behavior, not the business. Requester-bound per policy F2; precedent CAT-034."
    },
    {
     "id": "IDL-032",
     "area": "identity-linking",
     "keyword": "MUST NOT",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Platforms MUST NOT silently fall through on any error other than 404 in RFC 8414 discovery.",
     "source": "ucp:docs/specification/identity-linking.md#L249-L251",
     "covered_by": [],
     "exempt_reason": "'Platforms MUST NOT silently fall through on any error other than 404 in RFC 8414 discovery' (identity-linking.md L249-251) binds the PLATFORM's discovery fetch behavior. Requester-bound per policy F2; precedent CAT-034."
    },
    {
     "id": "IDL-033",
     "area": "identity-linking",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "The issuer value in discovered metadata MUST match the discovery base URI exactly; platforms MUST NOT normalize before comparison (byte-for-byte match).",
     "source": "ucp:docs/specification/identity-linking.md#L253-L257",
     "covered_by": [
      "merchant_checks_04_08_oauth.py"
     ]
    },
    {
     "id": "IDL-034",
     "area": "identity-linking",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Platforms MUST request only the derived scope set (from config.scopes) and not a superset.",
     "source": "ucp:docs/specification/identity-linking.md#L297-L299",
     "covered_by": [],
     "exempt_reason": "'Platforms MUST request only the derived scope set (from config.scopes) and not a superset' (identity-linking.md L297-299) binds the PLATFORM's request authoring; the scope set it requests is not a merchant obligation. Requester-bound per policy F2; precedent CAT-034."
    },
    {
     "id": "IDL-035",
     "area": "identity-linking",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "On the authorization response, the platform MUST verify state matches the sent value and iss matches the discovered issuer URI; if either fails it MUST discard the response.",
     "source": "ucp:docs/specification/identity-linking.md#L301-L305",
     "covered_by": [],
     "exempt_reason": "'On the authorization response, the platform MUST verify state matches the sent value and iss matches the discovered issuer URI; if either fails it MUST discard the response' (identity-linking.md L301-305) binds the PLATFORM's response handling. Requester-bound per policy F2; precedent CAT-034."
    },
    {
     "id": "IDL-036",
     "area": "identity-linking",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "The token-request code_verifier MUST correspond to the code_challenge sent in the authorization request; businesses MUST reject token requests where code_verifier is absent or does not verify.",
     "source": "ucp:docs/specification/identity-linking.md#L307-L310",
     "covered_by": [
      "merchant_checks_04_08_oauth.py"
     ]
    },
    {
     "id": "IDL-037",
     "area": "identity-linking",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Scope names MUST match the pattern ^[a-z][a-z0-9_]*$.",
     "source": "ucp:docs/specification/identity-linking.md#L339-L340",
     "covered_by": [
      "schema_check_04_08_identity.py"
     ]
    },
    {
     "id": "IDL-038",
     "area": "identity-linking",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Platforms MUST treat any scope listed in config.scopes (well-known or custom) as gating its operations behind user authentication.",
     "source": "ucp:docs/specification/identity-linking.md#L348-L350",
     "covered_by": [],
     "exempt_reason": "'Platforms MUST treat any scope listed in config.scopes (well-known or custom) as gating its operations behind user authentication' (identity-linking.md L348-350) binds the PLATFORM's interpretation duty. Requester-bound per policy F2; precedent CAT-034."
    },
    {
     "id": "IDL-039",
     "area": "identity-linking",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Platforms MUST ignore unrecognized fields in a per-scope policy object.",
     "source": "ucp:docs/specification/identity-linking.md#L358-L359",
     "covered_by": [],
     "exempt_reason": "'Platforms MUST ignore unrecognized fields in a per-scope policy object' (identity-linking.md L358-359) binds the PLATFORM's parsing duty. Requester-bound per policy F2; precedent CAT-034."
    },
    {
     "id": "IDL-040",
     "area": "identity-linking",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "On identity_required the business MUST return 401 Unauthorized, a WWW-Authenticate: Bearer challenge, and a UCP error body with code 'identity_required'.",
     "source": "ucp:docs/specification/identity-linking.md#L414-L422",
     "covered_by": [
      "merchant_checks_04_08_oauth.py"
     ]
    },
    {
     "id": "IDL-041",
     "area": "identity-linking",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "The identity_required WWW-Authenticate header MUST include a realm parameter set to the business's issuer URI from RFC 8414 metadata.",
     "source": "ucp:docs/specification/identity-linking.md#L424-L426",
     "covered_by": [
      "merchant_checks_04_08_oauth.py"
     ]
    },
    {
     "id": "IDL-042",
     "area": "identity-linking",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "When the request included a token that was invalid/expired/unverifiable, the identity_required header MUST also include error=\"invalid_token\".",
     "source": "ucp:docs/specification/identity-linking.md#L427-L429",
     "covered_by": [
      "merchant_checks_04_08_oauth.py"
     ]
    },
    {
     "id": "IDL-044",
     "area": "identity-linking",
     "keyword": "MUST NOT",
     "testability": "testable",
     "status": "check",
     "requirement": "continue_url MUST NOT be used to convey a pre-baked OAuth authorization request; the platform constructs its own authorization request.",
     "source": "ucp:docs/specification/identity-linking.md#L440-L443",
     "covered_by": [
      "merchant_checks_04_08_oauth.py"
     ]
    },
    {
     "id": "IDL-045",
     "area": "identity-linking",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "On insufficient_scope the business MUST return 403 Forbidden, a WWW-Authenticate: Bearer challenge, and a UCP error body with code 'insufficient_scope'.",
     "source": "ucp:docs/specification/identity-linking.md#L489-L496",
     "covered_by": [
      "merchant_checks_04_08_oauth.py"
     ]
    },
    {
     "id": "IDL-046",
     "area": "identity-linking",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "The insufficient_scope WWW-Authenticate header MUST include realm, error=\"insufficient_scope\", and scope=\"<space-separated full required scope set>\".",
     "source": "ucp:docs/specification/identity-linking.md#L498-L502",
     "covered_by": [
      "merchant_checks_04_08_oauth.py"
     ]
    },
    {
     "id": "IDL-047",
     "area": "identity-linking",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "The insufficient_scope scope parameter MUST list the full set of scopes required for the operation, not just the missing ones.",
     "source": "ucp:docs/specification/identity-linking.md#L507-L509",
     "covered_by": [
      "merchant_checks_04_08_oauth.py"
     ]
    },
    {
     "id": "IDL-048",
     "area": "identity-linking",
     "keyword": "MUST NOT",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Platforms MUST NOT retry an insufficient_scope response by re-initiating a fresh account linking flow; they MUST instead request only the missing scope(s) via incremental authorization.",
     "source": "ucp:docs/specification/identity-linking.md#L535-L538",
     "covered_by": [],
     "exempt_reason": "'Platforms MUST NOT retry an insufficient_scope response by re-initiating a fresh account linking flow; they MUST instead request only the missing scope(s) via incremental authorization' (identity-linking.md L535-538) binds the PLATFORM's flow control. Requester-bound per policy F2; precedent CAT-034."
    },
    {
     "id": "IDL-049",
     "area": "identity-linking",
     "keyword": "MUST NOT",
     "testability": "testable",
     "status": "check",
     "requirement": "Plain PKCE (plain) MUST NOT be used; PKCE S256 is REQUIRED for all authorization code flows, and businesses MUST reject exchanges without a valid code_verifier.",
     "source": "ucp:docs/specification/identity-linking.md#L570-L572",
     "covered_by": [
      "merchant_checks_04_08_oauth.py"
     ]
    },
    {
     "id": "IDL-050",
     "area": "identity-linking",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Public clients cannot keep a client_secret confidential and MUST use 'none'; businesses MUST NOT require client_secret_basic as the only method when serving native or agent platforms.",
     "source": "ucp:docs/specification/identity-linking.md#L578-L585",
     "covered_by": [
      "merchant_checks_04_08_oauth.py"
     ]
    },
    {
     "id": "IDL-051",
     "area": "identity-linking",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Platforms MUST process the structured scope and error parameters to drive authorization flow decisions; error_description MUST NOT be used for control-flow decisions.",
     "source": "ucp:docs/specification/identity-linking.md#L596-L599",
     "covered_by": [],
     "exempt_reason": "'Platforms MUST process the structured scope and error parameters to drive authorization flow decisions; error_description MUST NOT be used for control-flow decisions' (identity-linking.md L596-599) binds the PLATFORM's flow logic. Requester-bound per policy F2; precedent CAT-034."
    },
    {
     "id": "IDL-052",
     "area": "identity-linking",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "The realm parameter MUST match the business's issuer URI so platforms can correlate the challenge with the correct authorization server.",
     "source": "ucp:docs/specification/identity-linking.md#L599-L601",
     "covered_by": [
      "merchant_checks_04_08_oauth.py"
     ]
    },
    {
     "id": "IDL-053",
     "area": "identity-linking",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "All communication between platform and business MUST use HTTPS with a minimum of TLS 1.2.",
     "source": "ucp:docs/specification/identity-linking.md#L610-L612",
     "covered_by": [
      "merchant_checks_04_08_tls.py"
     ]
    },
    {
     "id": "IDL-055",
     "area": "identity-linking",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Platforms MUST revoke user identity tokens at the business's revocation endpoint when a user unlinks; businesses MUST reject subsequent requests that present revoked tokens.",
     "source": "ucp:docs/specification/identity-linking.md#L617-L619",
     "covered_by": [
      "merchant_checks_04_08_oauth.py"
     ]
    },
    {
     "id": "IDL-056",
     "area": "identity-linking",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "When config.providers is absent (v1), platforms MUST fall back to direct OAuth 2.0 against the business domain via RFC 8414 discovery.",
     "source": "ucp:docs/specification/identity-linking.md#L646-L649",
     "covered_by": [],
     "exempt_reason": "'When config.providers is absent (v1), platforms MUST fall back to direct OAuth 2.0 against the business domain via RFC 8414 discovery' (identity-linking.md L646-649) binds the PLATFORM's flow selection. Requester-bound per policy F2; precedent CAT-034."
    },
    {
     "id": "IDL-057",
     "area": "identity-linking",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "When config contains fields not defined in this version, platforms MUST ignore those fields and proceed using OAuth 2.0 with RFC 8414 discovery on the business domain.",
     "source": "ucp:docs/specification/identity-linking.md#L651-L654",
     "covered_by": [],
     "exempt_reason": "'When config contains fields not defined in this version, platforms MUST ignore those fields and proceed using OAuth 2.0 with RFC 8414 discovery on the business domain' (identity-linking.md L651-654) binds the PLATFORM's parsing duty. Requester-bound per policy F2; precedent CAT-034."
    },
    {
     "id": "IDL-058",
     "area": "identity-linking",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "authorization_response_iss_parameter_supported and code_challenge_methods_supported:[\"S256\"] MUST both be present in UCP-compliant RFC 8414 metadata.",
     "source": "ucp:docs/specification/identity-linking.md#L689-L691",
     "covered_by": [
      "merchant_checks_04_08_identity.py"
     ]
    },
    {
     "id": "IDL-059",
     "area": "identity-linking",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "config.scopes property names MUST conform to the scope_token pattern {capability}:{scope}.",
     "source": "ucp:source/schemas/common/identity_linking.json#L26",
     "covered_by": [
      "merchant_checks_04_08_identity.py",
      "schema_check_04_08_identity.py"
     ]
    },
    {
     "id": "IDL-060",
     "area": "identity-linking",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Business identity_linking config object is required and MUST contain a 'scopes' property.",
     "source": "ucp:source/schemas/common/identity_linking.json#L44-L49",
     "covered_by": [
      "merchant_checks_04_08_identity.py",
      "schema_check_04_08_identity.py"
     ]
    },
    {
     "id": "IDL-061",
     "area": "identity-linking",
     "keyword": "MUST",
     "testability": "needs-oauth",
     "status": "gap",
     "requirement": "The `issuer` value in RFC 8414 metadata and the `iss` parameter returned in authorization responses MUST be identical byte-for-byte; platforms MUST NOT normalize (e.g. strip trailing slashes) before comparison.",
     "source": "ucp:docs/specification/identity-linking.md#L604-L607",
     "covered_by": []
    },
    {
     "id": "IDL-062",
     "area": "identity-linking",
     "keyword": "MUST",
     "testability": "manual",
     "status": "gap",
     "requirement": "In the OIDC Discovery fallback (step 2, reached after RFC 8414 discovery in step 1 returns 404), if the {business-domain}/.well-known/openid-configuration fetch returns any non-2xx response, network error, or timeout, the platform MUST abort the identity linking process.",
     "source": "ucp:docs/specification/identity-linking.md#L246",
     "covered_by": []
    },
    {
     "id": "MCP-001",
     "area": "transports",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "When the catalog Lookup capability is advertised, the MCP endpoint MUST expose both `lookup_catalog` and `get_product` tools",
     "source": "ucp:docs/specification/catalog/mcp.md#L693",
     "covered_by": [
      "merchant_checks.py"
     ]
    },
    {
     "id": "MCP-002",
     "area": "transports",
     "keyword": "MUST NOT",
     "testability": "manual",
     "status": "exempt",
     "requirement": "MCP request payloads MUST NOT carry an `id` inside the `checkout` object; the target resource is identified by a top-level `id` parameter",
     "source": "ucp:docs/specification/checkout-mcp.md#L120",
     "covered_by": [],
     "exempt_reason": "Client/platform-bound obligation surfaced by the register-completeness pass: MCP request payloads MUST NOT carry an `id` inside the `checkout` object; the target resource is identified by a top-level `id` pa \u2014 binds the platform/agent (or is a spec-authoring rule), not the merchant server under test; a merchant check would be fake coverage. Same treatment as the existing client-bound PAY exemptions. Version-scoped so it never collides with a version where this id names a checked requirement."
    },
    {
     "id": "MCP-003",
     "area": "transports",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "A conforming MCP checkout endpoint MUST implement JSON-RPC 2.0, provide all core checkout tools, return errors per core spec, return business outcomes as JSON-RPC result with UCP envelope and messages array, validate tool inputs against schemas, and support HTTP streaming",
     "source": "ucp:docs/specification/checkout-mcp.md#L783",
     "covered_by": [
      "merchant_checks.py"
     ]
    },
    {
     "id": "MCP-004",
     "area": "transports",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "MCP transport MUST expose UCP OpenRPC operations via JSON-RPC tools/call, mapping method->params.name and params->params.arguments",
     "source": "ucp:docs/specification/checkout-mcp.md#L804",
     "covered_by": [
      "merchant_checks.py"
     ]
    },
    {
     "id": "MCP-005",
     "area": "transports",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "A business implementing the MCP order binding MUST implement the `get_order` tool per the OpenRPC schema",
     "source": "ucp:docs/specification/order-mcp.md#L292",
     "covered_by": [
      "merchant_checks.py"
     ]
    },
    {
     "id": "MCP-006",
     "area": "transports",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "A conforming catalog MCP transport implementation MUST implement JSON-RPC 2.0 correctly, validate tool inputs against UCP schemas, support cursor-based pagination with a default limit of 10, and return -32602 (Invalid params) for requests exceeding batch size limits (the catalog MCP conformance list beyond the Lookup-tool exposure that MCP-001 covers).",
     "source": "ucp:docs/specification/catalog/mcp.md#L690",
     "covered_by": [
      "merchant_checks.py"
     ]
    },
    {
     "id": "NEG-001",
     "area": "version-negotiation-and-protocol-errors",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "When the platform's protocol version is unsupported, the business MUST return a version_unsupported error, mapped to HTTP 422 (REST).",
     "source": "ucp:docs/specification/overview.md#L699,L1912",
     "covered_by": [
      "merchant_checks_04_08_discovery.py"
     ]
    },
    {
     "id": "NEG-002",
     "area": "version-negotiation-and-protocol-errors",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "When no compatible capabilities exist in the intersection, the business MUST return a capabilities_incompatible error, mapped to HTTP 200 with the error in the UCP body (NOT a 4xx).",
     "source": "ucp:docs/specification/overview.md#L700,L1916",
     "covered_by": [
      "merchant_checks_04_08_discovery.py"
     ]
    },
    {
     "id": "NEG-003",
     "area": "version-negotiation-and-protocol-errors",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "A resolved-but-unfetchable profile (timeout/non-2xx) maps to profile_unreachable, HTTP 424.",
     "source": "ucp:docs/specification/overview.md#L697",
     "covered_by": [
      "merchant_checks_04_08_discovery.py"
     ]
    },
    {
     "id": "NEG-004",
     "area": "version-negotiation-and-protocol-errors",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "A fetched profile that is not valid JSON or violates schema maps to profile_malformed, HTTP 422.",
     "source": "ucp:docs/specification/overview.md#L698",
     "covered_by": [
      "merchant_checks_04_08_discovery.py"
     ]
    },
    {
     "id": "NEG-005",
     "area": "version-negotiation-and-protocol-errors",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "A request whose platform profile URL is malformed, MISSING, or unresolvable is rejected with invalid_profile_url (REST 400 / MCP -32001)",
     "source": "ucp:docs/specification/overview.md#L696",
     "covered_by": [
      "merchant_checks_04_08_discovery.py"
     ]
    },
    {
     "id": "ORD-001",
     "area": "order",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Businesses MUST return the full order entity (current-state snapshot) on every response, not deltas",
     "source": "ucp:docs/specification/order.md#L290",
     "covered_by": [
      "merchant_checks.py"
     ]
    },
    {
     "id": "ORD-002",
     "area": "order",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Line items MUST include all line items that ever existed on the order regardless of edits or alterations",
     "source": "ucp:docs/specification/order.md#L34",
     "covered_by": [
      "merchant_checks_04_08_order.py"
     ]
    },
    {
     "id": "ORD-003",
     "area": "order",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "order.currency is REQUIRED on the order entity",
     "source": "ucp:source/schemas/shopping/order.json#L25",
     "covered_by": [
      "area_04_08_order.py"
     ]
    },
    {
     "id": "ORD-004",
     "area": "order",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "line_items is REQUIRED on the order entity",
     "source": "ucp:source/schemas/shopping/order.json#L25",
     "covered_by": [
      "area_04_08_order.py"
     ]
    },
    {
     "id": "ORD-005",
     "area": "order",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Each order line item requires id, item, quantity, totals, and status",
     "source": "ucp:source/schemas/shopping/types/order_line_item.json#L6",
     "covered_by": [
      "area_04_08_order.py",
      "merchant_checks.py"
     ]
    },
    {
     "id": "ORD-006",
     "area": "order",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Line item quantity requires total and fulfilled (non-negative integers)",
     "source": "ucp:source/schemas/shopping/types/order_line_item.json#L24",
     "covered_by": [
      "schema_check_04_08_order.py"
     ]
    },
    {
     "id": "ORD-007",
     "area": "order",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Adjustment quantities and amounts are signed \u2014 negative for reductions (returns, refunds), positive for additions (exchanges)",
     "source": "ucp:docs/specification/order.md#L107",
     "covered_by": [
      "merchant_checks_04_08_order.py"
     ]
    },
    {
     "id": "ORD-008",
     "area": "order",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Each adjustment requires id, type, occurred_at, and status",
     "source": "ucp:source/schemas/shopping/types/adjustment.json#L7",
     "covered_by": [
      "schema_check_04_08_order.py"
     ]
    },
    {
     "id": "ORD-009",
     "area": "order",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Adjustment totals are signed \u2014 negative for money returned to buyer (refunds, credits), positive for additional charges (exchanges)",
     "source": "ucp:source/schemas/shopping/types/adjustment.json#L59",
     "covered_by": [
      "merchant_checks_04_08_order.py"
     ]
    },
    {
     "id": "ORD-012",
     "area": "order",
     "keyword": "MUST",
     "testability": "needs-oauth",
     "status": "check",
     "requirement": "The business MUST authenticate requests to order data before returning a response (API key, OAuth 2.0, mutual TLS, or HTTP Message Signatures)",
     "source": "ucp:docs/specification/order.md#L313",
     "covered_by": [
      "merchant_checks_04_08_order.py"
     ]
    },
    {
     "id": "ORD-015",
     "area": "order",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Platforms MUST include the UCP-Agent header with profile URL on all order requests",
     "source": "ucp:docs/specification/order.md#L395",
     "covered_by": [],
     "exempt_reason": "'Platforms MUST include the UCP-Agent header with profile URL on all order requests' (order.md L395; order-rest.md L252) binds the PLATFORM's outbound requests; the order spec places no duty on the business to reject requests lacking UCP-Agent, and the tester sends it on all order requests. Same subject-binding as CAT-008/CAT-034."
    },
    {
     "id": "ORD-018",
     "area": "order",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Each expectation requires id, line_items, method_type, and destination",
     "source": "ucp:source/schemas/shopping/types/expectation.json#L7",
     "covered_by": [
      "schema_check_04_08_order.py"
     ]
    },
    {
     "id": "ORD-019",
     "area": "order",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Each fulfillment event requires id, occurred_at, type, and line_items",
     "source": "ucp:source/schemas/shopping/types/fulfillment_event.json#L7",
     "covered_by": [
      "schema_check_04_08_order.py"
     ]
    },
    {
     "id": "ORD-020",
     "area": "order",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Order confirmation requires id and permalink_url",
     "source": "ucp:source/schemas/shopping/types/order_confirmation.json#L7",
     "covered_by": [
      "schema_check_04_08_order.py"
     ]
    },
    {
     "id": "ORD-021",
     "area": "order",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "All REST response bodies MUST be valid JSON (RFC 8259)",
     "source": "ucp:docs/specification/order-rest.md#L66",
     "covered_by": [
      "merchant_checks_04_08_order.py"
     ]
    },
    {
     "id": "ORD-022",
     "area": "order",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "All REST endpoints MUST be served over HTTPS with minimum TLS version 1.3",
     "source": "ucp:docs/specification/order-rest.md#L71",
     "covered_by": [
      "merchant_checks_04_08_order.py"
     ]
    },
    {
     "id": "ORD-023",
     "area": "order",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Platforms MUST check the messages array in responses before accessing order data",
     "source": "ucp:docs/specification/order-rest.md#L253",
     "covered_by": [],
     "exempt_reason": "'Platforms MUST check the messages array in responses before accessing order data' (order-rest.md L253) binds the PLATFORM's client-side consumption order \u2014 an internal read-ordering behavior with no wire-observable merchant obligation. Same subject-binding as CAT-008/CAT-034."
    },
    {
     "id": "ORD-026",
     "area": "order",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "Webhook payloads MUST be signed by the business and verified by the platform",
     "source": "ucp:docs/specification/order.md#L459",
     "covered_by": [
      "merchant_checks_04_08_events.py"
     ]
    },
    {
     "id": "ORD-027",
     "area": "order",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "Businesses MUST sign all webhook payloads per Message Signatures using RFC 9421 headers (Signature, Signature-Input, Content-Digest)",
     "source": "ucp:docs/specification/order.md#L537",
     "covered_by": [
      "merchant_checks_04_08_events.py"
     ]
    },
    {
     "id": "ORD-028",
     "area": "order",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "Businesses MUST include the UCP-Agent header with profile URL for signer identification on webhooks",
     "source": "ucp:docs/specification/order.md#L536",
     "covered_by": [
      "merchant_checks_04_08_events.py"
     ]
    },
    {
     "id": "ORD-029",
     "area": "order",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "Businesses MUST send the 'Order created' event with a fully populated order entity",
     "source": "ucp:docs/specification/order.md#L540",
     "covered_by": [
      "merchant_checks_04_08_events.py"
     ]
    },
    {
     "id": "ORD-030",
     "area": "order",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "Businesses MUST send the full order entity on updates (not incremental deltas)",
     "source": "ucp:docs/specification/order.md#L541",
     "covered_by": [
      "merchant_checks_04_08_events.py"
     ]
    },
    {
     "id": "ORD-031",
     "area": "order",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "Businesses MUST retry failed webhook deliveries",
     "source": "ucp:docs/specification/order.md#L542",
     "covered_by": [
      "merchant_checks_04_08_events.py"
     ]
    },
    {
     "id": "ORD-032",
     "area": "order",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Platforms MUST respond quickly with a 2xx HTTP status code to acknowledge webhook receipt",
     "source": "ucp:docs/specification/order.md#L531",
     "covered_by": [],
     "exempt_reason": "'Platforms MUST respond quickly with a 2xx HTTP status code to acknowledge webhook receipt' (order.md L531, Events Guidelines) binds the webhook RECEIVER (the platform); no check on a merchant can prove a third-party platform's ack behavior. Same subject-binding as ORD-015/ORD-023; the 01-era twin is ORD-018."
    },
    {
     "id": "ORD-033",
     "area": "order",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "After verifying the signature, the platform MUST confirm the signer is authorized to send events for the referenced order (profile matches order's business; reject otherwise)",
     "source": "ucp:docs/specification/order.md#L512",
     "covered_by": [],
     "exempt_reason": "'After verifying the signature, the platform MUST confirm the signer is authorized to send events for the referenced order (profile matches order's business; reject otherwise)' (order.md L512, Webhook Signature Verification) is receiver-side authorization conduct, not a business obligation observable on the merchant. Same subject-binding as ORD-032; the 01-era twin is ORD-019."
    },
    {
     "id": "OVR-001",
     "area": "overview",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "gap",
     "requirement": "All capability and service names advertised in a profile MUST use the reverse-domain format {reverse-domain}.{service}.{capability}",
     "source": "ucp:docs/specification/overview.md#L59",
     "covered_by": []
    },
    {
     "id": "OVR-003",
     "area": "overview",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "gap",
     "requirement": "The origin of each capability's spec/schema URL MUST match the namespace authority derived from its reverse-domain name",
     "source": "ucp:docs/specification/overview.md#L83",
     "covered_by": []
    },
    {
     "id": "OVR-004",
     "area": "overview",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "gap",
     "requirement": "Businesses MUST include the ucp field in every response, containing the processing version and the array of active capabilities",
     "source": "ucp:docs/specification/overview.md#L641",
     "covered_by": []
    },
    {
     "id": "OVR-005",
     "area": "overview",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "gap",
     "requirement": "In ucp.capabilities the business MUST include only capabilities that are (1) in the negotiated intersection and (2) relevant to this response's operation type",
     "source": "ucp:docs/specification/overview.md#L950",
     "covered_by": []
    },
    {
     "id": "OVR-006",
     "area": "overview",
     "keyword": "MUST",
     "testability": "needs-oauth",
     "status": "gap",
     "requirement": "Verifiers MUST ensure the authenticated identity is consistent with the UCP-Agent profile, and (for API key/OAuth/mTLS) MUST confirm the authenticated principal is authorized to act on behalf of that profile, rejecting requests where identity and claimed profile conflict",
     "source": "ucp:docs/specification/overview.md#L1014",
     "covered_by": []
    },
    {
     "id": "OVR-007",
     "area": "overview",
     "keyword": "MUST NOT",
     "testability": "manual",
     "status": "exempt",
     "requirement": "On signature-verification failure with an unknown kid, an implementation that force-refreshes the cached platform profile MUST NOT do so more than once per TTL floor per origin",
     "source": "ucp:docs/specification/overview.md#L1108",
     "covered_by": [],
     "exempt_reason": "Client/platform-bound obligation surfaced by the register-completeness pass: On signature-verification failure with an unknown kid, an implementation that force-refreshes the cached platform profile MUST NOT \u2014 binds the platform/agent (or is a spec-authoring rule), not the merchant server under test; a merchant check would be fake coverage. Same treatment as the existing client-bound PAY exemptions. Version-scoped so it never collides with a version where this id names a checked requirement."
    },
    {
     "id": "OVR-008",
     "area": "overview",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "gap",
     "requirement": "REST requests and responses MUST use Content-Type application/json",
     "source": "ucp:docs/specification/overview.md#L1584",
     "covered_by": []
    },
    {
     "id": "OVR-009",
     "area": "overview",
     "keyword": "MUST NOT",
     "testability": "needs-receiver",
     "status": "gap",
     "requirement": "A version-specific (leaf) profile served from a supported_versions URI MUST NOT contain a supported_versions field",
     "source": "ucp:docs/specification/overview.md#L1897",
     "covered_by": []
    },
    {
     "id": "OVR-010",
     "area": "overview",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "gap",
     "requirement": "The protocol version MUST be a dated YYYY-MM-DD release; businesses MUST NOT advertise a non-date version string (e.g. 'draft') in their profile version field or in supported_versions",
     "source": "ucp:docs/specification/overview.md#L1953",
     "covered_by": []
    },
    {
     "id": "OVR-011",
     "area": "overview",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Businesses MUST include only capabilities compatible with the negotiated protocol version in their response; a capability depending on features introduced in a newer protocol version MUST NOT be included when processing at an older protocol version",
     "source": "ucp:docs/specification/overview.md#L1974",
     "covered_by": [],
     "exempt_reason": "Client/platform-bound obligation surfaced by the register-completeness pass: Businesses MUST include only capabilities compatible with the negotiated protocol version in their response; a capability dependin \u2014 binds the platform/agent (or is a spec-authoring rule), not the merchant server under test; a merchant check would be fake coverage. Same treatment as the existing client-bound PAY exemptions. Version-scoped so it never collides with a version where this id names a checked requirement."
    },
    {
     "id": "OVR-012",
     "area": "overview",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "gap",
     "requirement": "During capability negotiation (step 3, Extension Validation), businesses MUST exclude any extension whose parent capability is not present in the platform/business capability intersection.",
     "source": "ucp:docs/specification/overview.md#L640",
     "covered_by": []
    },
    {
     "id": "OVR-013",
     "area": "overview",
     "keyword": "MUST",
     "testability": "manual",
     "status": "gap",
     "requirement": "In the REST transport (core), implementations MUST use standard HTTP verbs (e.g., POST for creation, GET for retrieval).",
     "source": "ucp:docs/specification/overview.md#L1585",
     "covered_by": []
    },
    {
     "id": "OVR-014",
     "area": "overview",
     "keyword": "MUST",
     "testability": "manual",
     "status": "gap",
     "requirement": "In the REST transport (core), implementations MUST use standard HTTP status codes (e.g., 200, 201, 400, 401, 500).",
     "source": "ucp:docs/specification/overview.md#L1587",
     "covered_by": []
    },
    {
     "id": "PAY-001",
     "area": "payment",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "A business/platform/response profile advertises payment handlers in the payment_handlers registry, keyed by reverse-domain name",
     "source": "ucp:source/schemas/ucp.json#L150",
     "covered_by": [
      "merchant_checks_04_08_payment.py",
      "schema_check_04_08_payment.py"
     ]
    },
    {
     "id": "PAY-002",
     "area": "payment",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Every payment handler declaration MUST include an id (handler instance identifier)",
     "source": "ucp:source/schemas/payment_handler.json#L13",
     "covered_by": [
      "area_payment.py",
      "merchant_checks_04_08_payment.py",
      "schema_check_04_08_payment.py"
     ]
    },
    {
     "id": "PAY-003",
     "area": "payment",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Checkout/Order responses echo handlers in ucp.payment_handlers; the response payment_handlers array is required and each entry conforms to the handler response_schema (which requires id)",
     "source": "ucp:source/schemas/ucp.json#L219",
     "covered_by": [
      "merchant_checks_04_08_payment.py",
      "schema_check_04_08_payment.py"
     ]
    },
    {
     "id": "PAY-004",
     "area": "payment",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "A platform-schema payment handler declaration MUST additionally include spec and schema URLs",
     "source": "ucp:source/schemas/payment_handler.json#L32",
     "covered_by": [
      "schema_check_04_08_payment.py"
     ]
    },
    {
     "id": "PAY-005",
     "area": "payment",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "When a handler declares available_instruments, the array MUST contain at least one instrument type (minItems 1)",
     "source": "ucp:source/schemas/payment_handler.json#L22",
     "covered_by": [
      "schema_check_04_08_payment.py"
     ]
    },
    {
     "id": "PAY-007",
     "area": "payment",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "An available_payment_instrument MUST include a type (instrument type identifier)",
     "source": "ucp:source/schemas/shopping/types/available_payment_instrument.json#L7",
     "covered_by": [
      "schema_check_04_08_payment.py"
     ]
    },
    {
     "id": "PAY-008",
     "area": "payment",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Platforms MUST treat the response_schema available_instruments / handler config as authoritative for the checkout",
     "source": "ucp:docs/specification/payment-handler-guide.md#L212",
     "covered_by": [],
     "exempt_reason": "Platform-bound MUST (@04-08 only; a business MUST at 01-era, covered there): platforms treat available_instruments/handler config as authoritative. The business duty to RETURN the authoritative set is checked by PAY-005/007."
    },
    {
     "id": "PAY-009",
     "area": "payment",
     "keyword": "MUST NOT",
     "testability": "manual",
     "status": "exempt",
     "requirement": "The available_instruments returned in the response_schema is authoritative; platforms MUST treat it as authoritative and MUST NOT use instrument types or constraints that contradict it",
     "source": "ucp:docs/specification/payment-handler-guide.md#L303",
     "covered_by": [],
     "exempt_reason": "Platform-bound MUST/MUST NOT (@04-08 only; covered at 01-era): platforms treat available_instruments as authoritative and MUST NOT use contradicting types. Business emission is checked by PAY-005/007."
    },
    {
     "id": "PAY-010",
     "area": "payment",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Platforms MUST treat the available_instruments in the checkout response as authoritative for that checkout",
     "source": "ucp:docs/specification/overview.md#L1203",
     "covered_by": [],
     "exempt_reason": "Platform-bound MUST (@04-08 only; covered at 01-era): platforms treat available_instruments as authoritative. Business emission checked by PAY-005/007."
    },
    {
     "id": "PAY-011",
     "area": "payment",
     "keyword": "MUST NOT",
     "testability": "testable",
     "status": "check",
     "requirement": "Credentials flow Platform -> Business only; businesses MUST NOT echo credentials back in responses",
     "source": "ucp:docs/specification/overview.md#L1151",
     "covered_by": [
      "merchant_checks.py"
     ]
    },
    {
     "id": "PAY-012",
     "area": "payment",
     "keyword": "MUST NOT",
     "testability": "testable",
     "status": "check",
     "requirement": "Token credential value MUST NOT be echoed in responses (token field is omit-on-response)",
     "source": "ucp:source/schemas/shopping/types/token_credential.json#L21",
     "covered_by": [
      "schema_check_04_08_payment.py"
     ]
    },
    {
     "id": "PAY-014",
     "area": "payment",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "gap",
     "requirement": "Businesses MUST validate handler_id before processing (ensure handler is in the advertised set)",
     "source": "ucp:docs/specification/overview.md#L1513",
     "covered_by": []
    },
    {
     "id": "PAY-015",
     "area": "payment",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "gap",
     "requirement": "Businesses MUST dynamically filter the handlers list based on the context of the cart (e.g., removing BNPL for subscription items, filtering regional methods by shipping address)",
     "source": "ucp:docs/specification/overview.md#L1193",
     "covered_by": []
    },
    {
     "id": "PAY-017",
     "area": "payment",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "On 3DS/SCA soft-decline the business returns status requires_escalation with a continue_url; the platform MUST open continue_url for the buyer to complete the bank check then retry completion",
     "source": "ucp:docs/specification/overview.md#L1409",
     "covered_by": [],
     "exempt_reason": "Platform-bound MUST (@04-08 only; covered at 01-era): the PLATFORM opens continue_url after a 3DS/SCA soft-decline. The business soft-decline duty (return requires_escalation + continue_url) is checked by CHK-001/004/014."
    },
    {
     "id": "PAY-018",
     "area": "payment",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "continue_url MUST be provided when checkout status is requires_escalation",
     "source": "ucp:source/schemas/shopping/checkout.json#L113",
     "covered_by": [
      "merchant_checks_04_08_payment.py"
     ]
    },
    {
     "id": "PAY-019",
     "area": "payment",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "A payment instrument MUST include id, handler_id, and type",
     "source": "ucp:source/schemas/shopping/types/payment_instrument.json#L7",
     "covered_by": [
      "schema_check_04_08_payment.py"
     ]
    },
    {
     "id": "PAY-020",
     "area": "payment",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "A payment credential MUST include a type discriminator",
     "source": "ucp:source/schemas/shopping/types/payment_credential.json#L7",
     "covered_by": [
      "schema_check_04_08_payment.py"
     ]
    },
    {
     "id": "PAY-021",
     "area": "payment",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "A card credential MUST include type and card_number_type",
     "source": "ucp:source/schemas/shopping/types/card_credential.json#L12",
     "covered_by": [
      "schema_check_04_08_payment.py"
     ]
    },
    {
     "id": "PAY-022",
     "area": "payment",
     "keyword": "MUST NOT",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Card credential (raw PAN) MUST NOT be used for checkout, only with payment handlers that tokenize or encrypt credentials",
     "source": "ucp:source/schemas/shopping/types/card_credential.json#L5",
     "covered_by": [],
     "exempt_reason": "'This credential type MUST NOT be used for checkout, only with payment handlers that tokenize or encrypt credentials' (card_credential.json L5 description) prohibits a USAGE by the credential SENDER \u2014 the platform submitting raw PANs at checkout. The spec places no rejection duty on the receiving business (the official sample suite itself accepts a raw card credential at checkout with 200), so no merchant response can prove or refute the prohibition \u2014 the 04-08 twin of the exempt PAY-013@01-era. [Scoped: at 2026-01-11/2026-01-23 this id is the ap2 mandate-required completion rule, a needs-receiver row left to its own accounting.]"
    },
    {
     "id": "PAY-023",
     "area": "payment",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "A card payment instrument MUST have type const 'card'",
     "source": "ucp:source/schemas/shopping/types/card_payment_instrument.json#L41",
     "covered_by": [
      "schema_check_04_08_payment.py"
     ]
    },
    {
     "id": "PAY-024",
     "area": "payment",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "A token credential MUST include type and token",
     "source": "ucp:source/schemas/shopping/types/token_credential.json#L12",
     "covered_by": [
      "schema_check_04_08_payment.py"
     ]
    },
    {
     "id": "PAY-025",
     "area": "payment",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "If using token credentials, the handler schema MUST include an expiration field (expiry, ttl, or similar) so platforms know when to refresh credentials",
     "source": "ucp:docs/specification/payment-handler-guide.md#L626",
     "covered_by": [],
     "exempt_reason": "'If using token credentials, the schema MUST include an expiration field (expiry, ttl, or similar)' (payment-handler-guide.md L626-628) binds authors of payment-handler credential schemas: a design requirement on a third-party specification document with an open-ended naming clause ('or similar') that no fixed predicate can arbitrate without false-flagging conforming handlers \u2014 the 04-08 twin of the exempt PAY-006@01-era. [Scoped: at 2026-01-11/2026-01-23 this id is the ap2 unresolvable-key/invalid-signature error duty, a needs-receiver row left to its own accounting.]"
    },
    {
     "id": "PAY-026",
     "area": "payment",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "A token binding MUST include the checkout_id the token is bound to (prevents token reuse across checkouts/participants)",
     "source": "ucp:source/schemas/shopping/types/binding.json#L7",
     "covered_by": [
      "schema_check_04_08_payment.py"
     ]
    },
    {
     "id": "PAY-027",
     "area": "payment",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "A payment identity MUST include an access_token uniquely identifying the participant tokens are bound to",
     "source": "ucp:source/schemas/shopping/types/payment_identity.json#L7",
     "covered_by": [
      "schema_check_04_08_payment.py"
     ]
    },
    {
     "id": "PAY-028",
     "area": "payment",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Every payment handler specification MUST define the core elements (identity, participants, prerequisites, declaration, instrument, credential, acquisition, processing, error mapping)",
     "source": "ucp:docs/specification/payment-handler-guide.md#L50",
     "covered_by": [],
     "exempt_reason": "'Every payment handler specification MUST define the core elements below' (payment-handler-guide.md L50) binds AUTHORS of payment-handler specification documents \u2014 identity, participants, prerequisites, declaration, instrument, credential, acquisition, processing and error mapping are properties of a third-party document established by review, with no runtime counterpart the suite can elicit from the business under test \u2014 the 04-08 twin of the exempt PAY-001@01-era. [Scoped: at 2026-01-11/2026-01-23 this id is the AP2 signature-coverage rule, a needs-receiver row left to its own accounting.]"
    },
    {
     "id": "PAY-029",
     "area": "payment",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "A payment handler specification MUST define which credential types are accepted by the business",
     "source": "ucp:docs/specification/payment-handler-guide.md#L623",
     "covered_by": [],
     "exempt_reason": "'The specification MUST define which credential types are accepted by the handler' (payment-handler-guide.md L623-624) binds handler-spec AUTHORS: whether a third-party handler document declares its accepted credential types is decided by reading that document, not by any protocol exchange \u2014 the 04-08 twin of the exempt PAY-005@01-era. [Scoped: at 2026-01-11/2026-01-23 this id is the two-mandate-artifacts AP2 completion rule, which stays in the 01-era grind.]"
    },
    {
     "id": "PAY-030",
     "area": "payment",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Sensitive data such as Payment Credentials or PII MUST be handled according to PCI-DSS and GDPR guidelines",
     "source": "ucp:docs/specification/overview.md#L1682",
     "covered_by": [],
     "exempt_reason": "'Sensitive data (such as Payment Credentials or PII) MUST be handled according to PCI-DSS and GDPR guidelines' (overview.md Data Privacy, L1682-1684): compliance with PCI-DSS and GDPR is established by out-of-band audit and regulation \u2014 data-handling practices inside the business leave no trace in any protocol message the suite can observe. [Scoped: at 2026-01-11/2026-01-23 this id is the checkout-mandate-content rule, a needs-receiver row left to its own accounting.]"
    },
    {
     "id": "PAY-031",
     "area": "payment",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "When AP2 is active, the business must embed a cryptographic signature in checkout responses proving the terms are authentic.",
     "source": "ucp:docs/specification/ap2-mandates.md#L29",
     "covered_by": [
      "area_04_08_ap2.py"
     ]
    },
    {
     "id": "PAY-032",
     "area": "payment",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Platforms must provide cryptographically signed mandates during the complete operation proving user authorization.",
     "source": "ucp:docs/specification/ap2-mandates.md#L31",
     "covered_by": [],
     "exempt_reason": "Client/platform-bound obligation surfaced by the register-completeness pass: Platforms must provide cryptographically signed mandates during the complete operation proving user authorization. \u2014 binds the platform/agent (or is a spec-authoring rule), not the merchant server under test; a merchant check would be fake coverage. Same treatment as the existing client-bound PAY exemptions. Version-scoped so it never collides with a version where this id names a checked requirement."
    },
    {
     "id": "PAY-033",
     "area": "payment",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "A platform in the trusted-platform-provider model must publish at least one key in the top-level signing_keys array of its profile.",
     "source": "ucp:docs/specification/ap2-mandates.md#L98",
     "covered_by": [],
     "exempt_reason": "Client/platform-bound obligation surfaced by the register-completeness pass: A platform in the trusted-platform-provider model must publish at least one key in the top-level signing_keys array of its profile \u2014 binds the platform/agent (or is a spec-authoring rule), not the merchant server under test; a merchant check would be fake coverage. Same treatment as the existing client-bound PAY exemptions. Version-scoped so it never collides with a version where this id names a checked requirement."
    },
    {
     "id": "PAY-034",
     "area": "payment",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "When AP2 is negotiated, the business must include ap2.merchant_authorization in all checkout responses.",
     "source": "ucp:docs/specification/ap2-mandates.md#L106",
     "covered_by": [
      "area_04_08_ap2.py"
     ]
    },
    {
     "id": "PAY-035",
     "area": "payment",
     "keyword": "MUST NOT",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "The business must not accept a complete_checkout request that lacks ap2.checkout_mandate.",
     "source": "ucp:docs/specification/ap2-mandates.md#L108",
     "covered_by": [
      "merchant_checks_04_08_ap2.py"
     ]
    },
    {
     "id": "PAY-036",
     "area": "payment",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "The platform must verify the business's signature before presenting the checkout to the user.",
     "source": "ucp:docs/specification/ap2-mandates.md#L110",
     "covered_by": [],
     "exempt_reason": "Client/platform-bound obligation surfaced by the register-completeness pass: The platform must verify the business's signature before presenting the checkout to the user. \u2014 binds the platform/agent (or is a spec-authoring rule), not the merchant server under test; a merchant check would be fake coverage. Same treatment as the existing client-bound PAY exemptions. Version-scoped so it never collides with a version where this id names a checked requirement."
    },
    {
     "id": "PAY-037",
     "area": "payment",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "A public signing key must be available for the business to verify the mandate's signature.",
     "source": "ucp:docs/specification/ap2-mandates.md#L115",
     "covered_by": [
      "merchant_checks_04_08_ap2.py"
     ]
    },
    {
     "id": "PAY-038",
     "area": "payment",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "If a public key cannot be resolved or the signature is invalid, the business must return an error.",
     "source": "ucp:docs/specification/ap2-mandates.md#L122",
     "covered_by": [
      "merchant_checks_04_08_ap2.py"
     ]
    },
    {
     "id": "PAY-039",
     "area": "payment",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "Businesses must embed their signature in the checkout response body under ap2.merchant_authorization using JWS detached content.",
     "source": "ucp:docs/specification/ap2-mandates.md#L140",
     "covered_by": [
      "area_04_08_ap2.py"
     ]
    },
    {
     "id": "PAY-040",
     "area": "payment",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "The business's signature must cover both the JWS header and the checkout payload (preventing alg-substitution attacks).",
     "source": "ucp:docs/specification/ap2-mandates.md#L172",
     "covered_by": [
      "area_04_08_ap2.py"
     ]
    },
    {
     "id": "PAY-041",
     "area": "payment",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "gap",
     "requirement": "The platform must produce two distinct mandate artifacts (checkout_mandate and payment_mandate).",
     "source": "ucp:docs/specification/ap2-mandates.md#L200",
     "covered_by": []
    },
    {
     "id": "PAY-042",
     "area": "payment",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "The checkout mandate must contain the full checkout response including the ap2.merchant_authorization field.",
     "source": "ucp:docs/specification/ap2-mandates.md#L207",
     "covered_by": [
      "area_04_08_ap2.py"
     ]
    },
    {
     "id": "PAY-043",
     "area": "payment",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "All JSON payloads must be canonicalized using JSON Canonicalization Scheme (JCS) per RFC 8785.",
     "source": "ucp:docs/specification/ap2-mandates.md#L218",
     "covered_by": [
      "area_04_08_ap2.py"
     ]
    },
    {
     "id": "PAY-044",
     "area": "payment",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "Once AP2 is negotiated, both parties must follow the defined mandate flow steps to ensure cryptographic integrity.",
     "source": "ucp:docs/specification/ap2-mandates.md#L242",
     "covered_by": [
      "merchant_checks_04_08_ap2.py"
     ]
    },
    {
     "id": "PAY-045",
     "area": "payment",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "A completion request without mandates must result in a session failure.",
     "source": "ucp:docs/specification/ap2-mandates.md#L244",
     "covered_by": [
      "merchant_checks_04_08_ap2.py"
     ]
    },
    {
     "id": "PAY-046",
     "area": "payment",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "The trusted platform provider must ensure that mandates are not created without explicit user consent from trusted, deterministic channels.",
     "source": "ucp:docs/specification/ap2-mandates.md#L314",
     "covered_by": [],
     "exempt_reason": "Client/platform-bound obligation surfaced by the register-completeness pass: The trusted platform provider must ensure that mandates are not created without explicit user consent from trusted, deterministic  \u2014 binds the platform/agent (or is a spec-authoring rule), not the merchant server under test; a merchant check would be fake coverage. Same treatment as the existing client-bound PAY exemptions. Version-scoped so it never collides with a version where this id names a checked requirement."
    },
    {
     "id": "PAY-047",
     "area": "payment",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "Upon receiving the complete request, the business must perform the defined negotiation-enforcement and mandate/authorization verification steps.",
     "source": "ucp:docs/specification/ap2-mandates.md#L380",
     "covered_by": [
      "merchant_checks_04_08_ap2.py"
     ]
    },
    {
     "id": "SAE-001",
     "area": "signals-attribution-eligibility",
     "keyword": "MUST NOT",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Signal values MUST NOT be buyer-asserted claims; platforms provide signals from direct observation or independently verifiable third-party attestations",
     "source": "ucp:docs/specification/overview.md#L1689",
     "covered_by": [],
     "exempt_reason": "Platform-bound MUST NOT: signal values are platform-provided (direct observation/independent attestation); the business under test never asserts them, so provenance is unobservable business-side."
    },
    {
     "id": "SAE-002",
     "area": "signals-attribution-eligibility",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "All signal keys MUST use reverse-domain naming to ensure provenance and prevent collisions in the shared namespace",
     "source": "ucp:docs/specification/overview.md#L1696",
     "covered_by": [
      "schema_check_04_08_identity.py"
     ]
    },
    {
     "id": "SAE-003",
     "area": "signals-attribution-eligibility",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Signal keys must match the reverse-domain identifier pattern (well-known dev.ucp namespace; extensions use their own)",
     "source": "ucp:source/schemas/shopping/types/signals.json#L8",
     "covered_by": [
      "schema_check_04_08_identity.py"
     ]
    },
    {
     "id": "SAE-007",
     "area": "signals-attribution-eligibility",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Numeric or boolean attribution values MUST be string-encoded as they would be in a URL query string",
     "source": "ucp:source/schemas/shopping/types/attribution.json#L9",
     "covered_by": [
      "schema_check_04_08_identity.py"
     ]
    },
    {
     "id": "SAE-008",
     "area": "signals-attribution-eligibility",
     "keyword": "MUST NOT",
     "testability": "needs-receiver",
     "status": "gap",
     "requirement": "The presence or absence of the attribution field MUST NOT affect the response or negotiation",
     "source": "ucp:docs/specification/overview.md#L1771",
     "covered_by": []
    },
    {
     "id": "SAE-010",
     "area": "signals-attribution-eligibility",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Eligibility values MUST use reverse-domain naming and MUST be non-identifying",
     "source": "ucp:source/schemas/shopping/types/context.json#L35",
     "covered_by": [
      "schema_check_04_08_identity.py"
     ]
    },
    {
     "id": "SAE-011",
     "area": "signals-attribution-eligibility",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "Businesses MUST ignore unrecognized eligibility values without error",
     "source": "ucp:source/schemas/shopping/types/context.json#L35",
     "covered_by": [
      "merchant_checks_04_08_receiver.py"
     ]
    },
    {
     "id": "SAE-012",
     "area": "signals-attribution-eligibility",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "gap",
     "requirement": "Eligibility and policy enforcement MUST occur at checkout time using binding transaction data (context is non-authoritative)",
     "source": "ucp:source/schemas/shopping/types/context.json#L5",
     "covered_by": []
    },
    {
     "id": "SAE-013",
     "area": "signals-attribution-eligibility",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "All accepted eligibility claims MUST be resolved before the transaction can complete",
     "source": "ucp:docs/specification/checkout.md#L259",
     "covered_by": [
      "merchant_checks_04_08_receiver.py"
     ]
    },
    {
     "id": "SAE-014",
     "area": "signals-attribution-eligibility",
     "keyword": "MUST NOT",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "Unrecognized or inapplicable eligibility claims MUST NOT block the checkout",
     "source": "ucp:docs/specification/checkout.md#L262",
     "covered_by": [
      "merchant_checks_04_08_receiver.py"
     ]
    },
    {
     "id": "SAE-017",
     "area": "signals-attribution-eligibility",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "At completion, accepted claims that remain unverified MUST result in type 'error' with code 'eligibility_invalid'",
     "source": "ucp:docs/specification/checkout.md#L265",
     "covered_by": [
      "merchant_checks_04_08_receiver.py"
     ]
    },
    {
     "id": "SAE-018",
     "area": "signals-attribution-eligibility",
     "keyword": "MUST NOT",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "Businesses MUST NOT complete a transaction with unresolved eligibility claims",
     "source": "ucp:docs/specification/checkout.md#L287",
     "covered_by": [
      "merchant_checks_04_08_receiver.py"
     ]
    },
    {
     "id": "SAE-019",
     "area": "signals-attribution-eligibility",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "Eligibility verification failure MUST only affect the messages array",
     "source": "ucp:docs/specification/checkout.md#L293",
     "covered_by": [
      "merchant_checks_04_08_receiver.py"
     ]
    },
    {
     "id": "SAE-020",
     "area": "signals-attribution-eligibility",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "On eligibility verification failure the Business MUST return an error in messages with code 'eligibility_invalid' and severity 'recoverable'",
     "source": "ucp:docs/specification/checkout.md#L294",
     "covered_by": [
      "merchant_checks_04_08_receiver.py"
     ]
    },
    {
     "id": "SIG-001",
     "area": "signatures",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "UCP signs and verifies HTTP messages using RFC 9421 HTTP Message Signatures across all HTTP-based transports.",
     "source": "ucp:docs/specification/signatures.md#L42",
     "covered_by": [
      "merchant_checks_04_08_signatures.py"
     ]
    },
    {
     "id": "SIG-002",
     "area": "signatures",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "All implementations MUST support verifying P-256 (ES256) signatures.",
     "source": "ucp:docs/specification/signatures.md#L90",
     "covered_by": [
      "merchant_checks_04_08_signatures.py"
     ]
    },
    {
     "id": "SIG-006",
     "area": "signatures",
     "keyword": "MUST NOT",
     "testability": "testable",
     "status": "check",
     "requirement": "The algorithm is derived from the key's crv field in the JWK; alg is NOT included in Signature-Input parameters.",
     "source": "ucp:docs/specification/signatures.md#L97",
     "covered_by": [
      "merchant_checks_04_08_signatures.py"
     ]
    },
    {
     "id": "SIG-007",
     "area": "signatures",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Public keys MUST be represented using JSON Web Key (JWK) format as defined in RFC 7517.",
     "source": "ucp:docs/specification/signatures.md#L102",
     "covered_by": [
      "merchant_checks_04_08_signatures.py"
     ]
    },
    {
     "id": "SIG-008",
     "area": "signatures",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Public keys are published in the signing_keys array of the party's UCP profile at /.well-known/ucp.",
     "source": "ucp:docs/specification/signatures.md#L135",
     "covered_by": [
      "merchant_checks_04_08_signatures.py"
     ]
    },
    {
     "id": "SIG-009",
     "area": "signatures",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "gap",
     "requirement": "During key rotation, continue accepting signatures from old keys for a grace period of minimum 7 days.",
     "source": "ucp:docs/specification/signatures.md#L146",
     "covered_by": []
    },
    {
     "id": "SIG-010",
     "area": "signatures",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Content-Digest follows RFC 9530 and hashes the raw body bytes; implementations MUST use sha-256.",
     "source": "ucp:docs/specification/signatures.md#L176",
     "covered_by": [
      "merchant_checks_04_08_signatures.py"
     ]
    },
    {
     "id": "SIG-011",
     "area": "signatures",
     "keyword": "MUST NOT",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Proxies, API gateways, and other intermediaries MUST NOT re-serialize JSON bodies, as this would invalidate the signature.",
     "source": "ucp:docs/specification/signatures.md#L182",
     "covered_by": [],
     "exempt_reason": "Intermediary-bound MUST NOT: proxies/API gateways/other intermediaries MUST NOT re-serialize bodies; neither the business nor the platform endpoint under test is the bound party, and no intermediary sits under test."
    },
    {
     "id": "SIG-012",
     "area": "signatures",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Content-Digest header is required when a request/response has a body.",
     "source": "ucp:docs/specification/signatures.md#L172",
     "covered_by": [
      "merchant_checks_04_08_signatures.py"
     ]
    },
    {
     "id": "SIG-013",
     "area": "signatures",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Signature-Input and Signature headers are required on requests and responses.",
     "source": "ucp:docs/specification/signatures.md#L170",
     "covered_by": [
      "merchant_checks_04_08_signatures.py"
     ]
    },
    {
     "id": "SIG-014",
     "area": "signatures",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "REST request signed components MUST include @method, @authority, and @path (all Required).",
     "source": "ucp:docs/specification/signatures.md#L193",
     "covered_by": [
      "merchant_checks_04_08_events.py"
     ]
    },
    {
     "id": "SIG-015",
     "area": "signatures",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "REST request signed components MUST include content-digest and content-type when the request has a body.",
     "source": "ucp:docs/specification/signatures.md#L199",
     "covered_by": [
      "merchant_checks_04_08_events.py"
     ]
    },
    {
     "id": "SIG-016",
     "area": "signatures",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "gap",
     "requirement": "idempotency-key MUST be a signed component for POST, PUT, DELETE, PATCH requests.",
     "source": "ucp:docs/specification/signatures.md#L198",
     "covered_by": []
    },
    {
     "id": "SIG-017",
     "area": "signatures",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "@query MUST be a signed component if the request has query parameters.",
     "source": "ucp:docs/specification/signatures.md#L196",
     "covered_by": [
      "merchant_checks_04_08_events.py"
     ]
    },
    {
     "id": "SIG-018",
     "area": "signatures",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "gap",
     "requirement": "ucp-agent MUST be a signed component if the UCP-Agent header is present.",
     "source": "ucp:docs/specification/signatures.md#L197",
     "covered_by": []
    },
    {
     "id": "SIG-019",
     "area": "signatures",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "ECDSA signatures MUST use fixed-width raw r||s encoding per RFC 9421, not ASN.1/DER (64 bytes P-256, 96 bytes P-384).",
     "source": "ucp:docs/specification/signatures.md#L252",
     "covered_by": [
      "merchant_checks_04_08_signatures.py"
     ]
    },
    {
     "id": "SIG-020",
     "area": "signatures",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "REST response signed components use @status instead of @method (Required).",
     "source": "ucp:docs/specification/signatures.md#L285",
     "covered_by": [
      "merchant_checks_04_08_signatures.py"
     ]
    },
    {
     "id": "SIG-021",
     "area": "signatures",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "REST response signed components MUST include content-digest and content-type if the response has a body.",
     "source": "ucp:docs/specification/signatures.md#L292",
     "covered_by": [
      "merchant_checks_04_08_events.py"
     ]
    },
    {
     "id": "SIG-022",
     "area": "signatures",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Idempotency-Key must have minimum 128 bits of entropy (e.g., UUID v4, 22+ char alphanumeric).",
     "source": "ucp:docs/specification/signatures.md#L482",
     "covered_by": [],
     "exempt_reason": "Client/signer-bound MUST: the client generating the Idempotency-Key chooses >=128-bit entropy; entropy of one observed key is unmeasurable on a business response."
    },
    {
     "id": "SIG-023",
     "area": "signatures",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "gap",
     "requirement": "Server storage of idempotency keys must be a minimum of 24 hours, recommended 48 hours.",
     "source": "ucp:docs/specification/signatures.md#L484",
     "covered_by": []
    },
    {
     "id": "SIG-024",
     "area": "signatures",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "gap",
     "requirement": "On idempotency storage failure, fail closed (reject request with 503).",
     "source": "ucp:docs/specification/signatures.md#L486",
     "covered_by": []
    },
    {
     "id": "SIG-025",
     "area": "signatures",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "On duplicate idempotency key, return cached response and do not re-execute.",
     "source": "ucp:docs/specification/signatures.md#L485",
     "covered_by": [
      "merchant_checks_04_08_events.py"
     ]
    },
    {
     "id": "SIG-027",
     "area": "signatures",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "Webhook notifications MUST be signed.",
     "source": "ucp:docs/specification/signatures.md#L499",
     "covered_by": [
      "merchant_checks_04_08_events.py"
     ]
    },
    {
     "id": "SIG-031",
     "area": "signatures",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "signature_missing maps to HTTP 401 (required signature header/field not present).",
     "source": "ucp:docs/specification/signatures.md#L551",
     "covered_by": [
      "merchant_checks_04_08_events.py"
     ]
    },
    {
     "id": "SIG-032",
     "area": "signatures",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "signature_invalid maps to HTTP 401 (signature verification failed).",
     "source": "ucp:docs/specification/signatures.md#L552",
     "covered_by": [
      "merchant_checks_04_08_events.py"
     ]
    },
    {
     "id": "SIG-033",
     "area": "signatures",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "key_not_found maps to HTTP 401 (key ID not found in signer's signing_keys).",
     "source": "ucp:docs/specification/signatures.md#L553",
     "covered_by": [
      "merchant_checks_04_08_events.py"
     ]
    },
    {
     "id": "SIG-034",
     "area": "signatures",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "digest_mismatch maps to HTTP 400 (body digest doesn't match Content-Digest header).",
     "source": "ucp:docs/specification/signatures.md#L554",
     "covered_by": [
      "merchant_checks_04_08_events.py"
     ]
    },
    {
     "id": "SIG-035",
     "area": "signatures",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "gap",
     "requirement": "algorithm_unsupported maps to HTTP 400 (signature algorithm not supported).",
     "source": "ucp:docs/specification/signatures.md#L555",
     "covered_by": []
    },
    {
     "id": "SIG-036",
     "area": "signatures",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "Verification must reconstruct the signature base and reject with signature_invalid if ECDSA verification fails.",
     "source": "ucp:docs/specification/signatures.md#L404",
     "covered_by": [
      "merchant_checks_04_08_events.py"
     ]
    },
    {
     "id": "SIG-037",
     "area": "signatures",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "Verification must return key_not_found when the kid is not present in the signer's signing_keys.",
     "source": "ucp:docs/specification/signatures.md#L387",
     "covered_by": [
      "merchant_checks_04_08_events.py"
     ]
    },
    {
     "id": "SIG-038",
     "area": "signatures",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "check",
     "requirement": "Verification must return digest_mismatch when Content-Digest does not match the recomputed sha-256 of the body bytes.",
     "source": "ucp:docs/specification/signatures.md#L393",
     "covered_by": [
      "merchant_checks_04_08_events.py"
     ]
    },
    {
     "id": "SIG-040",
     "area": "signatures",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "gap",
     "requirement": "The `profile` value in the `UCP-Agent` header MUST be a quoted string containing an HTTPS URL (non-HTTPS URLs must be rejected).",
     "source": "ucp:docs/specification/signatures.md#L355",
     "covered_by": []
    },
    {
     "id": "SIG-041",
     "area": "signatures",
     "keyword": "MUST",
     "testability": "needs-receiver",
     "status": "gap",
     "requirement": "For business profiles, the `UCP-Agent` profile URL MUST point to `/.well-known/ucp`; platform profile URLs are not path-constrained.",
     "source": "ucp:docs/specification/signatures.md#L356",
     "covered_by": []
    },
    {
     "id": "TOT-001",
     "area": "totals",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Platforms MUST render all top-level totals entries in the order provided",
     "source": "ucp:docs/specification/checkout.md#L770",
     "covered_by": [],
     "exempt_reason": "Platform rendering-order duty (checkout.md L770): the order in which totals lines are painted exists only in the platform UI; the business cannot observe how its authoritative ordering was displayed."
    },
    {
     "id": "TOT-002",
     "area": "totals",
     "keyword": "MUST NOT",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Platforms MUST NOT interpret, filter, reorder, aggregate, or apply their own display logic to totals",
     "source": "ucp:docs/specification/checkout.md#L787",
     "covered_by": [],
     "exempt_reason": "Prohibits platform DISPLAY logic (interpret/filter/reorder/aggregate, checkout.md L787-788); display-side processing happens after the response is received and leaves no trace in any message the business sees."
    },
    {
     "id": "TOT-004",
     "area": "totals",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Unknown (non-well-known) totals types MUST include display_text",
     "source": "ucp:docs/specification/checkout.md#L793",
     "covered_by": [
      "schema_check_04_08_lifecycle.py"
     ]
    },
    {
     "id": "TOT-005",
     "area": "totals",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Exactly one totals entry of type subtotal MUST be present",
     "source": "ucp:docs/specification/checkout.md#L797",
     "covered_by": [
      "area_04_08_checkout.py",
      "merchant_checks.py"
     ]
    },
    {
     "id": "TOT-006",
     "area": "totals",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Exactly one totals entry of type total MUST be present",
     "source": "ucp:docs/specification/checkout.md#L798",
     "covered_by": [
      "area_04_08_checkout.py",
      "merchant_checks.py"
     ]
    },
    {
     "id": "TOT-007",
     "area": "totals",
     "keyword": "MUST NOT",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Platforms MUST NOT substitute their own computed totals for the business values",
     "source": "ucp:docs/specification/checkout.md#L802",
     "covered_by": [],
     "exempt_reason": "Prohibits substituting platform-computed totals in the RENDERED output (checkout.md L802-803): what the platform displays to the buyer is not observable from the business side of the protocol."
    },
    {
     "id": "TOT-010",
     "area": "totals",
     "keyword": "MUST NOT",
     "testability": "manual",
     "status": "exempt",
     "requirement": "Platforms MUST NOT autonomously complete a checkout with mismatched totals",
     "source": "ucp:docs/specification/checkout.md#L811",
     "covered_by": [],
     "exempt_reason": "Binds the platform's autonomous-completion decision (checkout.md L811, same prohibition as CHK-055): the business cannot tell a totals-verified complete request from an unverified one \u2014 the payloads are identical."
    },
    {
     "id": "TOT-012",
     "area": "totals",
     "keyword": "MUST",
     "testability": "manual",
     "status": "exempt",
     "requirement": "When display_text is provided, platforms MUST use it",
     "source": "ucp:docs/specification/checkout.md#L827",
     "covered_by": [],
     "exempt_reason": "Platform display duty (checkout.md L827): using display_text verbatim instead of a default label is a rendering choice made in the platform UI, invisible to the business under test."
    },
    {
     "id": "TOT-014",
     "area": "totals",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Subtractive well-known types (discount, items_discount) MUST have negative amounts",
     "source": "ucp:docs/specification/checkout.md#L829",
     "covered_by": [
      "area_04_08_checkout.py"
     ]
    },
    {
     "id": "TOT-015",
     "area": "totals",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Additive well-known types (subtotal, fulfillment, tax, fee) MUST have non-negative amounts",
     "source": "ucp:docs/specification/checkout.md#L830",
     "covered_by": [
      "area_04_08_checkout.py",
      "merchant_checks.py"
     ]
    },
    {
     "id": "TOT-017",
     "area": "totals",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Sum of a parent entry lines[] amounts MUST equal the parent entry amount",
     "source": "ucp:docs/specification/checkout.md#L848",
     "covered_by": [
      "merchant_checks_04_08_lifecycle.py"
     ]
    },
    {
     "id": "TOT-019",
     "area": "totals",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Each sub-line entry requires both display_text and amount",
     "source": "ucp:source/schemas/shopping/types/totals.json#L19",
     "covered_by": [
      "schema_check_04_08_lifecycle.py"
     ]
    },
    {
     "id": "TOT-020",
     "area": "totals",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Each top-level totals entry requires both type and amount",
     "source": "ucp:source/schemas/shopping/types/total.json#L7",
     "covered_by": [
      "schema_check_04_08_lifecycle.py"
     ]
    },
    {
     "id": "TOT-021",
     "area": "totals",
     "keyword": "MUST",
     "testability": "testable",
     "status": "check",
     "requirement": "Price requires a non-negative integer amount and an ISO 4217 currency code",
     "source": "ucp:source/schemas/shopping/types/price.json#L7",
     "covered_by": [
      "schema_check_04_08_lifecycle.py"
     ]
    }
   ]
  }
 }
}
