For platform engineers & builders
Implement two contracts. Pick a tier. Everything else is derived.
RetailAgentOS is a reasoning layer on top of UCP (Universal Commerce Protocol). You write a MerchantCatalogAdapter and a BuyerContextResolver; the engine derives your manifest, JSON-LD, product feed, decision pipeline, quotes, and traces. You never re-implement commerce reasoning.
This page renders ADOPTION-GUIDE.md as a product page. That file in the repo is canonical — this is the same content, laid out for reading without cloning anything. See the spec catalog for the exact contracts.
The mental model, 60 seconds
UCP gives commerce its rails: discovery, catalog, cart, checkout handoff. RetailAgentOS adds the layer UCP doesn't carry — the merchant's reasoning: who may see an item, who may buy it, at what price, in what stock, shipped where — moved from checkout-time to catalog-time, as deterministic, machine-readable contracts with a reason code on every decision.
VISIBILITY → ELIGIBILITY → FEASIBILITY → PRICE → FULFILLMENT → QUOTE
Same inputs, byte-identical output, always. No model in the loop, no clock reads inside the engine. One axis to remember: the conformance tier (0–4)describes your store's maturity — a buyer's gold/silver/guest loyalty standing is a separate, orthogonal claim, never a tier.
What you implement: two contracts, nothing else
The engine package is @retailagentos/engine. Zero runtime dependencies; importing it self-registers all evaluators.
import { evaluateOffer, buildManifest, toSchemaOrgProduct } from '@retailagentos/engine';
// 1. Map YOUR catalog to canonical variants
interface MerchantCatalogAdapter<TSource> {
merchantProfile(): MerchantProfile; // tier + capabilities[] + endpoints + servesRegions
toVariants(source: TSource): Variant[]; // one of your products -> 1+ RAOS variants
listVariants(): Variant[]; // your whole normalized catalog
}
// 2. Resolve who is shopping (region, fulfillment mode, claims)
interface BuyerContextResolver {
resolve(input: { region?: string; fulfillmentMode?: string }): PartialBuyerContext;
}Two rules you must not break:
- Never call the system clock inside evaluation — read
Date.now()once at your server boundary and pass it in asnow. - Never fork the reasoning. If a spec doesn't fit your store, raise it as an Open Question on the spec — don't quietly re-implement a decision outside the engine.
Everything else is derived
From the two contracts, the engine derives every agent-facing surface.
| You call | You get | Serve it at |
|---|---|---|
buildManifest(profile) | UCP discovery manifest | GET /.well-known/ucp |
toSchemaOrgProduct(variant) | Product + Offer JSON-LD | each product URL (server-rendered) |
toProductFeed(variants) | Google-format feed rows | wherever you publish feeds |
evaluateOffer({merchant, variant, quantity, context, now}) | full DecisionRecord with reasons | your API / MCP tools |
issueQuote(record, now) / validateQuote(...) | price-lock tokens | quote + checkout handoff |
buildDecisionTrace(record) + 3 renderers | buyer / merchant-ops / developer views of any decision | UI, support tooling, logs |
The trace renderer (developer / merchant-ops / buyer views) is backed by RAOS-0013.
The adoption ladder — pick your tier, ship in steps
Each tier is cumulative and independently valuable. A one-person boutique can stop at Tier 1 and already be agent-safe. You declare a headline tier plus an authoritative capabilities[]list in your manifest; agents negotiate against it and degrade gracefully for anything you don't support — partial adoption is first-class.
- 0
Tier 0 · Discoverable
“an agent can find and correctly read my catalog”
Typical effort: ~1 day once your adapter maps the catalog.
Implement MerchantCatalogAdapter over your existing product data
Serve buildManifest() at /.well-known/ucp
Serve toSchemaOrgProduct() JSON-LD on product pages (prerender or edge function if you’re an SPA)
Acceptance
- curl https://yourstore.com/.well-known/ucp returns tier + capabilities
- curl on a product URL returns valid JSON-LD without JavaScript
- 1
Tier 1 · Qualified
“no dead-end carts”
Only eligible, in-stock items surface, with a reason for everything hidden or blocked.
Populate eligibilityRules on variants (visibility, region, qualification)
Populate inventory state (+ freshness TTL, optional soft-hold reservation)
Implement BuyerContextResolver; call evaluateOffer per product view / cart line
Acceptance
- An out-of-region or out-of-stock request returns a ReasonEntry (e.g. REGION_RESTRICTED, OUT_OF_STOCK) with severity and resolution path — not a silent miss or a checkout failure
- 2
Tier 2 · Priced
“the right price per buyer, honored at checkout”
Configure member / bulk pricing (MOQ, increments, tiers, teaser prices, purchase limits)
Issue and validate quote tokens; declare your honor policy (grace / requote / reject)
Attach the trust/freshness envelope (simulated signing is fine to start — it’s labeled)
Acceptance
- The price an agent shows is bound in a TTL’d QuoteToken
- A stale or context-changed quote is re-quoted per your declared policy, never silently repriced
- 3
Tier 3 · Member-aware
specs planned · don't build ahead“supports member/loyalty-aware pricing and earn preview”
Loyalty earn/burn preview
RAOS-0009
Subscriptions & recurring
RAOS-0010
Promo stacking
RAOS-0006
Catalogued and designed but not yet published or built. Track status on the specs index. Don't build ahead of a published spec — the contracts may still move.
- 4
Tier 4 · Assisted
specs planned · don't build ahead“full commerce: fulfillment, handoff, intent, returns”
Fulfillment feasibility
RAOS-0003
Cart bridge & checkout handoff
RAOS-0012
Intent capture routing (part 2)
RAOS-0013 pt 2
Returns & post-purchase policy
RAOS-0014
Catalogued and designed but not yet published or built. Track status on the specs index. Don't build ahead of a published spec — the contracts may still move.
How agents actually reach you
Minimum (Tier 0)
Static surfaces — manifest + JSON-LD + feed. Crawlable agents work with zero live endpoints.
Interactive
Expose evaluateOffer and issueQuote as MCP tools on a long-lived server. The MCP layer is a thin adapter: authenticate, inject now, call the engine, return its output unmodified.
Checkout
Hand off to your existing checkout (e.g. Stripe session), carrying quote tokens. RAOS carries reasoning + locked prices; checkout owns payment and tax.
Worked example: TheCustomHub
pilot in progressA real made-to-order apparel merchant (Vite/React SPA on Firebase, Stripe checkout, ~59-product Shopify-shaped products.json) adopting the ladder as a pilot. This is in-progress integration work, not a completed live deployment — nothing here claims a completed real purchase or a live MCP endpoint.
| Ladder step | What it looked like there |
|---|---|
| Adapter | CustomHubAdapter: products.json → variants; compareAtPrice → applied offer; inventoryQty → inventory state; null variant rows stripped |
| Buyer context | region (US/CA allowlist via checkServesRegion) + fulfillment mode |
| Tier 0 | manifest via Cloud Function; JSON-LD via prerender (the SPA is invisible to agents otherwise) |
| Tier 1–2 | eligibility + inventory + quote via evaluateOffer/issueQuote behind a Cloud Run MCP server |
| Differentiator | callForPrice: true custom/bulk SKUs route to structured intent capture instead of dead-ending at a contact form |
Full detail lives in the repo: specs/reference-implementation/thecustomhub/(discovery → spine design → implementation plan → the self-contained brief you can hand to a coding agent for any new merchant integration).
For platforms
Build it once, every merchant inherits it
A single merchant integrating this ladder gets one agent-ready store. A commerce platform that implements the adapter and resolver once — against its own multi-tenant catalog and buyer-context model — makes every merchant on that platform agent-ready with zero additional work per store. The spec is the leverage: the hard part (deterministic reasoning, reason codes, quote integrity) lives in the engine and the specs, not in each integration.
Ground rules the whole architecture depends on
From RAOS-0000, applying to every tier.
- Determinism: same (BuyerContext, manifest, catalog, now) → identical output.
- Most-restrictive default: unknown/missing/untrusted context degrades to guest. Asserted (unsigned) privilege claims are downgraded for transaction-gating stages.
- Fail-degraded, never crash: a failing safety-critical evaluator blocks; a failing advisory evaluator is omitted.
- Additive-only evolution: reason codes never change meaning; semver per namespace; deprecation via supersededBy.
- Namespace: com.os.retailagent.shopping.*, vendor-neutral, written as UCP upstream candidates.
- v1 seams: USD-only (currency field exists as a seam), single-merchant cart.
Where to go next