Markets
Competitive routing with broadcast claims and durable market settlement records.
Market strategies are the clearing-native family: instead of the engine pushing Work to a chosen Worker, workers compete for it, and the winner is chosen by market rules rather than a fixed score. That competition can be a race to claim or a market session that records Bids and settlement outcomes — the same primitives the whole clearing lifecycle is built on, exposed as strategies.
Two market primitives ship today: shark_tank, the broadcast-and-claim strategy
behind speed-to-lead, and the market-session assignment layer for recording
winners, prices, and Settlement receipts on a routing decision.
The same machinery carries Agent RFQ: typed bid terms, buyer-side clearing,
authenticated bid submission, the task-spec front door, and award execution all
ship today behind the agent_rfq.* gates — see
Agent procurement for the call sequence.
| Strategy | Output | Status |
|---|---|---|
| Shark tank | session-based | Available |
| Market sessions & settlement | market-clearing | Available |
Shark tank
Capabilities: stateful, explain · Aliases: broadcast_claim
Broadcasts an opportunity to N candidates simultaneously and assigns it to the first one to claim it. It is the claim primitive behind speed-to-lead and ping/post-style competition, driven by the ping/post service.
Why use it. When responsiveness is the selection criterion — you want the recipient who is ready right now, not the one who scores best on paper. Instead of guessing who will act fastest, you let them race. Classic uses: lead distribution where the first agent to grab a lead works it, on-call escalation, and any "whoever's available first" assignment.
How it works. It is stateful — it tracks the open broadcast and the claim
race — and produces a session-based output rather than an instant winner:
the decision resolves when a claim arrives (or the session times out and falls
back). It advertises explain, so the claim outcome is auditable. It is
registered by builtin.RegisterBuiltins with a fallback strategy (default
smooth_weighted_round_robin) for the no-claim case.
Where it fits. The Select stage, but unlike single-shot strategies its
result is a durable claim session. See
idempotency for how claims commit exactly once.
Market sessions & settlement
Output: market-clearing
The assignment and settlement layer supports a multi-winner session:
participants submit Bids, prices are set, and
winners are committed with a settlement receipt — and the outcome is recorded on
the Route (Decision) as a MarketDecisionRef.
The reference captures the whole settlement, not just the winner:
| Field | Meaning |
|---|---|
SessionID | The market session that produced the decision. |
ResultID | The clearing result. |
PolicyHash | Hash of the clearing/disclosure policy in force. |
WinnerCount | How many participants won (multi-winner markets). |
SettlementReceiptID | Proof the route, connector, and ledger commits happened together. |
Prices | Per-participant MarketPriceRef — RecipientID, BidID, Amount, Currency, Rule. |
BlockerReasons | Redacted reasons a session was blocked, if any. |
Lifecycle. Close the market to stop accepting bids, then commit the winner
set and settle. Validation
on the ref rejects a missing SessionID, a negative WinnerCount, or a price with
a negative amount or missing RecipientID.
Where it fits. This is the durable record and commit path a market strategy resolves to.
The market session API
RoutingMarketService drives a session from open to commit. All routes are
under /api/routing-markets, scoped by environment_id and session_id.
| Call | Does |
|---|---|
POST /api/routing-markets:preview | Dry-runs an open request and returns named blockers instead of a bare 400. Use it first. |
POST /api/routing-markets | Opens a session: idempotency_key, routable_id, source, disclosure_policy, timeout_policy, clearing_policy, participants[]. |
GET /api/routing-markets/{env}/{session} | Session, participants, bids, result, receipt. |
POST .../{session}:solicit | Records a solicitation attempt on a channel. |
POST .../{session}/bids | Submits or amends a bid. Recipient bids carry amount; worker bids carry worker_id plus typed terms. |
POST .../{session}:close | Freezes the bid set and clears it deterministically. |
POST .../{session}:commit | Commits the winner set; commit_mode: hold escrows for an award, the default pays now. |
POST .../{session}:cancel | Cancels an uncommitted session. |
POST .../{session}:replay | Re-clears from the stored deterministic_seed and policy hash. |
source is a closed set: route, dag_step, batch_allocation_plan,
ranked_slate, headless, api, pingpost, simulation,
connector_event. Scripted clients use api.
clearing_policy.algorithm is one of the seller-side algorithms
(first_price_highest_bid, top_n, threshold, all_above_min,
vickrey_second_price, posted_price_acceptance, waterfall_fallback) or a
buyer-side profile (reverse_lowest_ask, best_value). Buyer-side policies
read budget_micros, currency, weight_price, weight_quality,
weight_sla, sla_headroom_ref_ms, and reverse_second_price.
disclosure_policy.scope is digest_only, partial, or full; with
partial, allowed_fields lists the task-spec fields a participant may see.
timeout_policy.bid_window is a duration ("900s"); bids after the
derived deadline are refused, and expire_on_timeout decides whether the
session expires or waits for an explicit close.
Every participant is either a recipient_id or a worker_id, never
both. Worker bids additionally require the caller to own the worker.