Fulfillment Feasibility
com.os.retailagent.shopping.fulfillment_constraints
Layer: RetailAgentOS extension on top of UCP (Universal Commerce Protocol) · Reference implementation in Playground · See it in the late-night pizza demo
MerchantProfile.serviceSchedule), per-item preparation time (preparationTimeMinutes), and an exact deadline (BuyerContext.needByAt) — four new reason codes below, all additive, all optional, byte-identical v1.0 behavior when unset. See the late-night pizza demo — and see specs/work-packages/RAOS-0003-quick-commerce-provider-signals.md for the live courier/routing extension point this deliberately does NOT build.Promoted from Tier 4 (Assisted) on 2026-08-12 — see §2 below for why "can this reach this buyer at all" is a Tier 1, not Tier 4, question.
Abstract
This spec defines a machine-readable way for a merchant to declare whether an item can actually reach a given buyer — evaluated at catalog time, before a cart is ever built. It produces one computed output an AI agent can act on: ComputedFulfillmentFeasibility — feasible, or blocked with a structured, non-resolvable reason.
Feasibility is deliberately narrower than "fulfillment" in general: mode, region, carrier restrictions, lead time, and same-day cutoff — everything answerable deterministically, from data already on hand. It does not answer "is there capacity right now" — see Scope below for why that line is drawn where it is.
The problem this solves
An agent confirms shipping to Hawaii for a local-delivery-only item, or promises same-day pickup after the cutoff has passed — because nothing at catalog time carries fulfillment feasibility, only fulfillment intent.
This spec originally shipped as Tier 4 ("Assisted"), alongside cart handoff and returns — a late-stage nicety. For a last-mile network, reachability is the opposite of a nicety: it is not an enhancement to commerce, it is a precondition for it. An item can be perfectly eligible (RAOS-0001) and perfectly in stock (RAOS-0005) and still be a dead-end cart if it cannot reach the buyer. Tier 1 — "no dead-end carts" — now names all three failure modes explicitly: eligible, in stock, reachable.
What this covers, and what it deliberately doesn't
In scope — deterministic, from injected inputs
- Mode feasibility
- Variant-level region reach
- Hazmat / oversize carrier restrictions
- Lead time vs. need-by
- Same-day cutoff
Explicitly OUT for v1
- Live slot capacity (DELIVERY_WINDOW_FULL) — not deterministic, cannot pass "same inputs → same answer."
- Split-shipment planning — a cart-level problem (RAOS-0012), not a single-variant one.
- Pickup/delivery-window selection UI — presentation, not feasibility.
Do not ship a reason code you cannot test. The two codes that can't be produced from the static mock catalog (LEAD_TIME_EXCEEDS_NEED_BY, CUTOFF_PASSED) have dedicated synthetic-input tests instead.
What goes in
Merchant declares on the product
Modes, regions it can't reach, hazmat/oversize flags, lead time, and same-day cutoff hour — attached to each variant.
Agent supplies buyer context
Fulfillment mode, region, and (optionally) a need-by date — the agent knows when and where the buyer needs it.
What comes out
ComputedFulfillmentFeasibility
FEASIBLE — proceed freely.
BLOCKED — cannot reach this buyer. No resolution path — explain and stop.
Reason code registry
Two systems agree on codes, not on prose. Messages are localizable; codes are stable.
| Code | Meaning | Resolvable |
|---|---|---|
| FULFILLMENT_MODE_UNAVAILABLE | Not available for the requested fulfillment mode | No |
| REGION_NOT_SERVED | This specific item can't reach the buyer's region, though the merchant generally serves it | No |
| HAZMAT_RESTRICTION | Hazmat-classified item, not shippable by parcel carrier | No |
| OVERSIZE_RESTRICTION | Exceeds standard parcel dimensions, not shippable by parcel carrier | No |
| LEAD_TIME_EXCEEDS_NEED_BY | Can't be fulfilled by the buyer's stated deadline given the declared lead time | No |
| CUTOFF_PASSED | The merchant-local same-day cutoff has passed for a same-day-capable mode | No |
| STORE_CLOSED | v1.1 — the store is not open at the requested time (weekly hours + exceptions) | No |
| ORDER_ACCEPTANCE_ENDED | v1.1 — still open, but past the declared order-acceptance buffer before close | No |
| PREPARATION_EXCEEDS_NEED_BY | v1.1 — item preparation time would finish after the buyer's exact need-by timestamp | No |
| INSUFFICIENT_TIME_BEFORE_CLOSE | v1.1 — item preparation time would finish after the store closes | No |
FULFILLMENT_MODE_UNAVAILABLE is a rename, not a new concept — it replaces RAOS-0001's deprecated FULFILLMENT_UNAVAILABLE (not re-emitted; see the 0001 changelog).
REGION_NOT_SERVED is a deliberately distinct code from RAOS-0001's REGION_RESTRICTED, not a rename: "we don't do business here at all" (merchant-level) vs. "this one item can't reach you, everything else in your cart can" (item-level) are different buyer conversations.
How it decides
Deterministic. Same (variant, context, now, merchantTimezone) → same result, every time. No model in the loop. Every configured check runs and accumulates — an agent sees every blocker in one pass, not one at a time.
Worked examples
Fresh Corner Market — mode, region, lead time, cutoff (grocery)
- Bananas, buyer requests
shipping→BLOCKED [FULFILLMENT_MODE_UNAVAILABLE]. - Bananas, buyer in HI →
BLOCKED [REGION_NOT_SERVED]— even though the merchant generally serves HI. - Custom cake (3-day lead time), buyer needs it in 2 days →
BLOCKED [LEAD_TIME_EXCEEDS_NEED_BY]. No stated deadline → never blocks. - Hot deli tray (2pm cutoff), ordered at 3pm local for pickup →
BLOCKED [CUTOFF_PASSED].
Atlas Wholesale — carrier restrictions
- Industrial solvent (hazmat), shipping request →
BLOCKED [HAZMAT_RESTRICTION]; pickup → FEASIBLE. - Commercial oven (oversize), shipping request →
BLOCKED [OVERSIZE_RESTRICTION]; pickup → FEASIBLE.
Sara's Boutique — discovery-led, open DTC
No fulfillmentConstraints on any variant. Every buyer, every mode → FEASIBLE, no reasons.
Open questions
These are genuine forks — §1 especially is written for fulfillment-network operators. Tell me I'm wrong.
#1The provider-delegation extension point — designed, not built
v1 ships merchant-owned feasibility only. For a real fulfillment network (Instacart, DoorDash, Uber), feasibility is not merchant data — the merchant says "I stock milk," the network says "I can deliver it in two hours." RAOS has no third-party fulfillment-provider actor today.
Designed in §9.1 of the spec: a manifest field naming which provider asserts feasibility for a mode, a requirement that the assertion be signed (trustMode: 'signed', not 'asserted' — the stakes are higher than a buyer's own claim), a shorter provider-declared freshness TTL riding in the same RAOS-0000 §9 envelope shape, and a rule that a merchant's own declared restriction always wins over a provider's optimism (never the reverse). Nothing here is built. If you operate a fulfillment network, this is written for you — tell me I'm wrong.
#2Should HAZMAT_RESTRICTION / OVERSIZE_RESTRICTION ever be resolvable?
v1 treats both as unconditionally non-resolvable in shipping mode. Does a merchant sometimes offer a hazmat-rated carrier upcharge that would make shipping possible for a fee? RAOS-0000's Requirement.type registry has no entry for "pay a surcharge."
#3Is calendar-date lead time granular enough?
leadTimeDays compares whole merchant-local calendar dates, not hours — an order at 11:58pm and one two minutes later on the same lead-time-day get the same earliest date. Should this compose with cutoffHourLocal (treat "past cutoff" as consuming a lead-time day)? Not composed in v1.
#4Precedence when eligibility AND feasibility both blockRESOLVED 2026-08-12
When an item is both ineligible (RAOS-0001) and unreachable (RAOS-0003) for the same buyer, which reason governs the buyer-facing explanation?
RESOLVED: first-blocking-stage governs. STAGE_ORDER runs ELIGIBILITY before the new FEASIBILITY stage, so "who is allowed to buy this at all" governs over "can we physically get it to this buyer" when both are true — the more fundamental blocker, surfaced first. Both reasons still fire and both appear in the trace; only which one is the headline explanation changes.
Run a fulfillment network? Have a view on any of these?
§9.1 (provider delegation) is written to get a real reaction from someone who operates real fulfillment logistics. Email me or reply on the build-log post.
rikbanerjee007@gmail.comWhy this matters
Fulfillment feasibility is the other half of "no dead-end carts." RAOS-0001 proved a buyer can be ineligible; RAOS-0005 proved an item can be out of stock; this spec proves an item can be unreachable — three independent, composable ways the same cart line can be a dead end, each with its own reason code an agent can explain rather than silently drop.
The day a fulfillment network is willing to sign a feasibility assertion, §9.1 has a documented seam ready for it.