1. Concepts
1.1 Actors
- Exchange (
EXCHANGE_ID) โ one Xchange deployment is one exchange. It owns its own MongoDB database (exchange_{EXCHANGE_ID}, see ยง2.4) and its own set of subjects. There is no cross-exchange query surface โ "all subjects in the exchange" always means "everyExchangeSubjectsdocument in this deployment's database". - Task (
ExchangeTasks) โ a unit of work submitted to the exchange. Itstask_assignment_typepicks one of four orchestration strategies (ยง1.2); itstask_metadata.task_assignmentsub-object carries the parameters that strategy needs (which subject, which function, which bid-job fields, ...). - Subject (
ExchangeSubjects) โ a registered agent that can receive tasks. Subjects are plain CRUD records (ยง3.3) โ there is no separate "join the exchange" workflow beyond creating the record. A subject'stask_evaluation_functionfield is only consulted by theopenassignment mode. - Selector function (
functionmode only) โ an external function, resolved byfunction_id, invoked once with the task and the full subject list; it must return aselected_subject_id. See ยง5.1. - Task evaluation function (
openmode only) โ each subject carries its own, viaExchangeSubjects.task_evaluation_function. Called per-subject, in order, until one accepts. See ยง5.2. - Delegate system (
SUBJECT_DELEGATE_URL) โ the same external service Orcade uses to reach a live subject and get a response back (SubjectTask.submit_and_wait, see ยง6). Xchange never talks to a subject directly. - Function registry (
FUNCTION_REGISTRY_URL) โ the same external function-hosting service Orcade uses. Xchange resolves selector / task-evaluationfunction_ids against it viaFunctionCaller(see ยง5), reusing the exact sameagents_functionsclient code as Orcade. - Orcade / bidding system (
BIDDING_SERVICE_URL) โ used only by thebiddingassignment mode. Xchange acts as a creator against Orcade's own/bid-jobsAPI โ see ยง7.
1.2 The four assignment modes
Set on submission via task_assignment_type:
| Mode | Selection | Assignment timing | Submission timing |
|---|---|---|---|
direct |
Caller names the subject_id up front. No selection logic runs at all. |
N/A (fixed) | Background thread |
function |
A single selector function picks one subject from the full subject list. | Synchronous (blocks the POST /tasks call) |
Background thread |
open |
Each subject's own task_evaluation_function is asked in turn; first "accepted" wins. |
Entirely on a background thread | Same background thread (sequential, not parallel) |
bidding |
Delegated wholesale to Orcade: a bid job is opened for the subject set and Orcade's own evaluator picks the winner. | Entirely on a background thread | Handled by Orcade, not Xchange; Xchange only polls for the outcome |
direct: POST /tasks โโโบ persist task โโฌโโบ return 201 to caller
โโโบ [thread] submit_and_wait(subject_id) โโโบ save task_output
function: POST /tasks โโโบ persist task โโโบ call selector function (blocking)
โโโบ return 201 to caller (status="assigned")
โโโบ [thread] submit_and_wait(selected_subject_id) โโโบ save task_output
open: POST /tasks โโโบ persist task โโโบ return 201 to caller
โโโบ [thread] for each subject:
call subject.task_evaluation_function
if "accepted" โ submit_and_wait(subject_id) โโโบ save task_output; stop
if none accept โ status="rejected"
bidding: POST /tasks โโโบ persist task โโโบ return 201 to caller
โโโบ [thread] create Orcade bid job
poll Orcade for a "bid_winner" task-result
save it as task_output
Key behavioral notes:
POST /tasksalways returns as soon as the task is persisted and (forfunctionmode only) a subject has been synchronously selected. It never waits for the subject's actual response โ that always happens on a background thread, for all four modes. PollGET /tasks/{task_id}orGET /tasks/{task_id}/output(ยง3.4) to find out what happened.functionmode is the only mode with a synchronous step. Selecting the subject happens inline inside the request handler; if the selector function raises or doesn't returnselected_subject_id,POST /tasksitself fails (400) and the task is markedfailedโ nothing is submitted to any subject.openmode evaluates subjects sequentially, not in parallel, and stops at the first acceptance. Subject order is whateverExchangeSubjectsCRUD.list()returns (MongoDB's natural document order โ not otherwise guaranteed). If a subject'stask_evaluation_functioncall raises, that subject is treated as a non-accept (logged and skipped) rather than failing the whole task.biddingmode has no timeout. It polls Orcade's/bid-jobs/{id}/task-resultseverybid_poll_intervalseconds (default 5s) forever, until a"bid_winner"entry shows up. If Orcade's own bidding never resolves (e.g. a required subject never bids), the Xchange task sits intask_assignment_status="bidding"indefinitely.task_assignment_statusvalues you'll observe:pending(just submitted, only briefly before a mode-specific handler updates it),assigned(function/open mode has picked a subject, submission thread not yet finished),bidding(bid job created, waiting on Orcade),completed(subject responded, seetask_output),failed(an exception occurred โ seetask_output.error),rejected(openmode exhausted every subject with no acceptance).