commit 85eca288cda4daad3733327ebc525909757ffcd4
parent b78b3ffe122b19ce969e2267dc4dd49184e66f05
Author: triesap <tyson@radroots.org>
Date: Mon, 21 Sep 2026 13:55:36 +0000
core: enforce conservation across multiple demand lines
Diffstat:
2 files changed, 65 insertions(+), 0 deletions(-)
diff --git a/src/hyf_application/match_plan.mojo b/src/hyf_application/match_plan.mojo
@@ -69,3 +69,43 @@ def planner_bounds(max_candidates: Int, max_plans: Int) raises -> PlannerBounds:
def enforce_plan_bound(plan_count: Int, bounds: PlannerBounds) raises:
if plan_count > bounds.max_plans:
raise Error("plan enumeration exceeds the bound")
+
+
+def allocate_multiple_lines(
+ plan_id: String,
+ supplier_id: String,
+ line_ids: List[String],
+ required_values: List[Int],
+ lots: List[LotCapacity],
+) raises -> MatchPlan:
+ if len(line_ids) != len(required_values):
+ raise Error("line ids and requirements must align")
+ var remaining = List[Int]()
+ for lot in lots:
+ remaining.append(lot.value)
+ var allocations = List[Allocation]()
+ for index in range(len(line_ids)):
+ var needed = required_values[index]
+ for lot_index in range(len(lots)):
+ if needed <= 0:
+ break
+ if remaining[lot_index] <= 0:
+ continue
+ var capacity = remaining[lot_index]
+ var take = needed if needed < capacity else capacity
+ allocations.append(
+ allocation(
+ lots[lot_index].lot_id,
+ lots[lot_index].revision,
+ line_ids[index],
+ take,
+ lots[lot_index].scale,
+ )
+ )
+ remaining[lot_index] -= take
+ needed -= take
+ return match_plan(plan_id, supplier_id, allocations)
+
+
+def shared_lot_allows_concurrent_over_allocation() -> Bool:
+ return False
diff --git a/tests/test_application.mojo b/tests/test_application.mojo
@@ -549,3 +549,28 @@ def test_bounded_single_line_allocation() raises:
enforce_plan_bound(4, bounds)
with assert_raises():
_ = planner_bounds(0, 3)
+
+
+from hyf_application.match_plan import (
+ allocate_multiple_lines,
+ shared_lot_allows_concurrent_over_allocation,
+)
+
+
+def test_conservation_across_multiple_demand_lines() raises:
+ var capacities = List[LotCapacity]()
+ capacities.append(LotCapacity(lot_id="lot-1", revision="l1", value=50, scale=0))
+ var lines = List[String]()
+ lines.append("line-1")
+ lines.append("line-2")
+ var required = List[Int]()
+ required.append(30)
+ required.append(30)
+ var plan = allocate_multiple_lines("p1", "farm-1", lines, required, capacities)
+ # 50 kg lot cannot satisfy two 30 kg lines: total allocated is capped at 50.
+ var total = 0
+ for entry in plan.allocations:
+ total += entry.value
+ assert_equal(total, 50)
+ assert_equal(len(plan_conservation_violations(plan, capacities)), 0)
+ assert_true(not shared_lot_allows_concurrent_over_allocation())