Allocation
Batch-level assignment — durable allocation plans, capacity-weighted greedy planning, and portfolio quota enforcement optimized across a whole batch.
Allocation strategies decide assignments for a whole batch at once, against a global objective and hard/soft constraints — not one item at a time. Where a single-winner strategy greedily picks the best recipient for this item, an allocator reasons about the batch: it can spread a scarce recipient's capacity across items, honor per-recipient caps globally, and leave items unassigned when constraints require.
Allocation strategies return an AllocationPlan that records assignments,
unassigned items, scores, and constraint outcomes. Ductor includes
capacity-weighted and portfolio-quota allocators for this batch-oriented path.
Each plan carries an execution status:
| Strategy | Output | Availability |
|---|---|---|
| Allocation plan | batch-allocation | Supported contract |
| Capacity-weighted allocator | batch-allocation | Built in |
| Portfolio quota allocator | batch-allocation | Built in |
Allocation vs. per-item selection
Per-item selection is greedy: item 1 takes the best recipient, and item 2 sees whatever's left. Batch allocation optimizes the set — it can deliberately give item 1 a second-best recipient so item 2, which has no other option, still gets served. Reach for allocation when items compete for the same scarce capacity.
Allocation plan
Output: batch-allocation
A durable batch contract that optimizes a whole batch against a global objective and hard/soft constraints — not per-item greedy selection. It records per-item rank, score, and reason, lists unassigned items explicitly, reports a global objective value, and is deterministic for identical inputs.
Why it matters. It is the authoritative shape all allocators produce. Because it is durable and deterministic, an allocation plan can be persisted, replayed, and audited: you can prove why item 7 went unassigned and what the batch-level objective value was.
How it's shaped. A strategy implements AllocationStrategy.Allocate, which
takes an AllocationRequest (a batch of selection requests) and returns an
AllocationPlan whose assignments are addressed by
AllocationAssignment.RequestIndex. Hard-constraint violations must be explicit
in the plan or surfaced as an error — an allocator never silently breaks a hard
cap. The capability is advertised as allocation_plan, and a contract that
declares an allocation output must carry that capability.
Where it fits. A batch execution shape (batch_route) rather than the
single-item Select path.
Capacity-weighted allocator
Output: batch-allocation · Built in
A greedy batch optimizer that deducts capacity as it assigns. It orders items by priority and deadline, scores candidates by weight plus remaining capacity, and — critically — deducts the planned units as it goes, so later items see true remaining capacity rather than the batch's starting capacity.
Why use it. A practical allocator for fulfillment and dispatch. Naive per-item routing over-assigns to a high-weight recipient because each decision sees full capacity; this allocator remembers what it has already planned, so it stops assigning to a recipient once its capacity is spent and moves to the next best.
How it works. Items are processed priority- and deadline-first; each assignment reduces the chosen recipient's remaining capacity for subsequent items.
Where it fits. Batch allocation. It is the greedy, capacity-aware default; for share/concentration limits use the quota allocator below.
Portfolio quota allocator
Output: batch-allocation · Built in
Enforces per-recipient and per-group caps across an entire batch. Hard caps are never violated; soft caps are penalized; items are left unassigned when the constraints require it. It is concentration control at batch scale — the batch counterpart of the per-decision portfolio balance scorer.
Why use it. When a batch must respect global limits — no recipient gets more than X items, no group (region, vendor, team) gets more than Y share — and you'd rather leave an item unassigned than blow a hard cap. Compliance-driven distribution and fair-share fulfillment are the typical cases.
How it works. It tracks per-recipient and per-group counts/shares as it
allocates, refusing assignments that would breach a hard cap and penalizing those
that breach a soft cap. Unfulfillable items are returned as explicitly
unassigned. It implements AllocationStrategy and is registered by
builtin.RegisterBuiltins.
Where it fits. Batch allocation, when constraints are about distribution limits rather than capacity.
Related
Ranked
Ordered routing slates — a durable list of selected, alternate, eliminated, pending, and failed candidates — and an adapter to produce them from any scorer.
Eligibility
Recipient eligibility traits — a governed qualification contract evaluated before strategy selection, with hard/preferred/disallowed requirements, redaction, and fail-closed expiry.